//NB: make sure the <base> is set correctly, 'cause you don't know where this method will //save the file before navigating to it. public void Navigate(XmlDocument dom, XmlDocument editDom = null) { if (InvokeRequired) { Invoke(new Action <XmlDocument, XmlDocument>(Navigate), dom, editDom); return; } _rootDom = dom; //.CloneNode(true); //clone because we want to modify it a bit _pageEditDom = editDom ?? dom; /* This doesn't work for the 1st book shown, or when you change book sizes. * But it's still worth doing, becuase without it, we have this annoying re-zoom every time we look at different page. */ XmlElement body = (XmlElement)_rootDom.GetElementsByTagName("body")[0]; if (ScaleToFullWidthOfPage) { var scale = GetScaleToShowWholeWidthOfPage(); if (scale > 0f) { body.SetAttribute("style", GetZoomCSS(scale)); } } XmlHtmlConverter.MakeXmlishTagsSafeForInterpretationAsHtml(dom); SetNewTempFile(TempFileUtils.CreateHtm5FromXml(dom)); _url = _tempHtmlFile.Path.ToLocalhost(); UpdateDisplay(); }
public void CanRetrieveContentOfFakeTempFile_ButOnlyUntilDisposed() { using (var server = CreateImageServer()) { var html = @"<html ><head></head><body>here it is</body></html>"; var dom = new HtmlDom(html); dom.BaseForRelativePaths = _folder.Path.ToLocalhost(); string url; using (var fakeTempFile = EnhancedImageServer.MakeSimulatedPageFileInBookFolder(dom)) { url = fakeTempFile.Key; var transaction = new PretendRequestInfo(url); // Execute server.MakeReply(transaction); // Verify // Whitespace inserted by CreateHtml5StringFromXml seems to vary across versions and platforms. // I would rather verify the actual output, but don't want this test to be fragile, and the // main point is that we get a file with the DOM content. Assert.That(transaction.ReplyContents, Is.EqualTo(TempFileUtils.CreateHtml5StringFromXml(dom.RawDom))); } var transactionFail = new PretendRequestInfo(url); // Execute server.MakeReply(transactionFail); // Verify Assert.That(transactionFail.StatusCode, Is.EqualTo(404)); } }
public void StartMeasuring() { CurrentlyMeasuring = true; if (_stream != null) { _stream.Close(); _stream.Dispose(); // no, leave it and its contents around: _folder.Dispose(); } _csvFilePath = TempFileUtils.GetTempFilepathWithExtension(".csv"); _stream = RobustFile.CreateText(_csvFilePath); _stream.AutoFlush = true; try { _stream.WriteLine(Form.ActiveForm.Text); } catch (Exception) { // swallow. This happens when we call from firefox, while debugging. } using (Measure("Initial Memory Reading")) { } }
public static string MakeToolboxContent(Book.Book book) { var path = BloomFileLocator.GetBrowserFile("bookEdit/toolbox", "toolbox.html"); var toolboxFolder = Path.GetDirectoryName(path); var domForToolbox = new HtmlDom(XmlHtmlConverter.GetXmlDomFromHtmlFile(path)); var toolsToDisplay = GetToolsToDisplay(book, IdsOfToolsThisVersionKnowsAbout); // get additional tools to load var checkedBoxes = new List <string>(); foreach (var tool in toolsToDisplay) { LoadPanelIntoToolbox(domForToolbox, tool, checkedBoxes, toolboxFolder); } // Load settings into the toolbox panel AppendToolboxPanel(domForToolbox, BloomFileLocator.GetFileDistributedWithApplication(Path.Combine(toolboxFolder, "settings", "Settings.html"))); // check the appropriate boxes foreach (var checkBoxId in checkedBoxes) { // Review: really? we have to use a special character to make a check? can't we add "checked" attribute? var node = domForToolbox.Body.SelectSingleNode("//div[@id='" + checkBoxId + "']"); if (node != null) { node.InnerXml = "✔"; } } XmlHtmlConverter.MakeXmlishTagsSafeForInterpretationAsHtml(domForToolbox.RawDom); return(TempFileUtils.CreateHtml5StringFromXml(domForToolbox.RawDom)); }
public static string MakeToolboxContent(Book.Book book) { var path = BloomFileLocator.GetBrowserFile(false, "bookEdit/toolbox", "toolbox.html"); var domForToolbox = new HtmlDom(XmlHtmlConverter.GetXmlDomFromHtmlFile(path)); XmlHtmlConverter.MakeXmlishTagsSafeForInterpretationAsHtml(domForToolbox.RawDom); return(TempFileUtils.CreateHtml5StringFromXml(domForToolbox.RawDom)); }
public void SetupServerWithCurrentPageIframeContents() { _domForCurrentPage = _bookSelection.CurrentSelection.GetEditableHtmlDomForPage(_pageSelection.CurrentSelection); XmlHtmlConverter.MakeXmlishTagsSafeForInterpretationAsHtml(_domForCurrentPage.RawDom); _server.CurrentPageContent = TempFileUtils.CreateHtml5StringFromXml(_domForCurrentPage.RawDom); _server.AccordionContent = MakeAccordionContent(); _server.CurrentBook = _currentlyDisplayedBook; }
public void CanRetrieveContentOfFakeTempFile_WhenFolderContainsAmpersand_NotViaJavaScript() { var dom = SetupDomWithAmpersandInTitle(); var transaction = CreateServerMakeSimPageMakeReply(dom); // Verify // Whitespace inserted by CreateHtml5StringFromXml seems to vary across versions and platforms. // I would rather verify the actual output, but don't want this test to be fragile, and the // main point is that we get a file with the DOM content. Assert.That(transaction.ReplyContents, Is.EqualTo(TempFileUtils.CreateHtml5StringFromXml(dom.RawDom))); }
/// <summary> /// This code sets things up so that we can edit (or make a thumbnail of, etc.) one page of a book. /// This is tricky because we have to satisfy several constraints: /// - We need to make this page content the 'src' of an iframe in a browser. So it has to be /// locatable by url. /// - It needs to appear to the browser to be a document in the book's folder. This allows local /// hrefs (e.g., src of images) that are normally relative to the whole-book file to locate /// the images. (We previously did this by making a file elsewhere and setting the 'base' /// for interpreting urls. But this fails for internal hrefs (starting with #)). /// - We don't want to risk leaving junk page files in the real book folder if anything goes wrong. /// - There may be several of these simulated pages around at the same time (e.g., when the thumbnailer is /// working on several threads). /// - The simulated files need to hang around for an unpredictable time (until the browser is done /// with them). /// The solution we have adopted is to make this server simulate files in the book folder. /// That is, the src for the page iframe is set to a localhost: url which maps to a file in the /// book folder. This means that any local hrefs (e.g., to images) will become server requests /// for the right file in the right folder. However, the page file never exists as a real file /// system file; instead, a request for the page file itself will be intercepted, and this server /// simply returns the content it has remembered. /// To manage the lifetime of the page data, we use a SimulatedPageFile object, which the Browser /// disposes of when it is no longer looking at that URL. Its dispose method tells this class /// to discard the simulated page data. /// To handle the need for multiple simulated page files and quickly check whether a particular /// url is one of them, we have a dictionary in which the urls are keys. /// A marker is inserted into the generated urls if the input HtmlDom wants to use original images. /// </summary> /// <param name="dom"></param> /// <param name="isCurrentPageContent">If this is true, the url will be inserted by JavaScript into /// a src attr for an IFrame. We need to account for this because un-escaped quotation marks in the /// URL can cause errors in JavaScript strings. Also, we want to use the same name each time /// for current page content, so Open Page in Browser works even after changing pages.</param> /// <returns></returns> public static SimulatedPageFile MakeSimulatedPageFileInBookFolder(HtmlDom dom, bool isCurrentPageContent = false, bool setAsCurrentPageForDebugging = false, string source = "") { var simulatedPageFileName = Path.ChangeExtension((isCurrentPageContent ? "currentPage" : Guid.NewGuid().ToString()) + SimulatedFileUrlMarker + source, ".html"); var pathToSimulatedPageFile = simulatedPageFileName; // a default, if there is no special folder if (dom.BaseForRelativePaths != null) { pathToSimulatedPageFile = Path.Combine(dom.BaseForRelativePaths, simulatedPageFileName).Replace('\\', '/'); } if (File.Exists(pathToSimulatedPageFile)) { // Just in case someone perversely calls a book "currentPage" we will use another name. // (We want one that does NOT conflict with anything really in the folder.) // We only allow one HTML file per folder so we shouldn't need multiple attempts. pathToSimulatedPageFile = Path.Combine(dom.BaseForRelativePaths, "X" + simulatedPageFileName).Replace('\\', '/'); } // FromLocalHost is smart about doing nothing if it is not a localhost url. In case it is, we // want the OriginalImageMarker (if any) after the localhost stuff. pathToSimulatedPageFile = pathToSimulatedPageFile.FromLocalhost(); if (dom.UseOriginalImages) { pathToSimulatedPageFile = OriginalImageMarker + "/" + pathToSimulatedPageFile; } var url = pathToSimulatedPageFile.ToLocalhost(); var key = pathToSimulatedPageFile.Replace('\\', '/'); if (isCurrentPageContent) { // We need to UrlEncode the single and double quote characters, and the space character, // so they will play nicely with HTML. var urlPath = UrlPathString.CreateFromUnencodedString(url); url = urlPath.UrlEncodedForHttpPath; } if (setAsCurrentPageForDebugging) { _keyToCurrentPage = key; } var html5String = TempFileUtils.CreateHtml5StringFromXml(dom.RawDom); lock (_urlToSimulatedPageContent) { _urlToSimulatedPageContent[key] = html5String; } return(new SimulatedPageFile() { Key = url }); }
/// <summary> /// This code sets things up so that we can edit (or make a thumbnail of, etc.) one page of a book. /// This is tricky because we have to satisfy several constraints: /// - We need to make this page content the 'src' of an iframe in a browser. So it has to be /// locatable by url. /// - It needs to appear to the browser to be a document in the book's folder. This allows local /// hrefs (e.g., src of images) that are normally relative to the whole-book file to locate /// the images. (We previously did this by making a file elsewhere and setting the 'base' /// for interpreting urls. But this fails for internal hrefs (starting with #)). /// - We don't want to risk leaving junk page files in the real book folder if anything goes wrong. /// - There may be several of these simulated pages around at the same time (e.g., when the thumbnailer is /// working on several threads). /// - The simulated files need to hang around for an unpredictable time (until the browser is done /// with them). /// The solution we have adopted is to make this server simulate files in the book folder. /// That is, the src for the page iframe is set to a localhost: url which maps to a file in the /// book folder. This means that any local hrefs (e.g., to images) will become server requests /// for the right file in the right folder. However, the page file never exists as a real file /// system file; instead, a request for the page file itself will be intercepted, and this server /// simply returns the content it has remembered. /// To manage the lifetime of the page data, we use a SimulatedPageFile object, which the Browser /// disposes of when it is no longer looking at that URL. Its dispose method tells this class /// to discard the simulated page data. /// To handle the need for multiple simulated page files and quickly check whether a particular /// url is one of them, we have a dictionary in which the urls are keys. /// A marker is inserted into the generated urls if the input HtmlDom wants to use original images. /// </summary> /// <param name="dom"></param> /// <param name="escapeUrlAsNeededForSrcAttribute">If this is true, the url will be inserted by JavaScript into /// a src attr for an IFrame. We need to account for this because un-escaped quotation marks in the /// URL can cause errors in JavaScript strings.</param> /// <returns></returns> public static SimulatedPageFile MakeSimulatedPageFileInBookFolder(HtmlDom dom, bool escapeUrlAsNeededForSrcAttribute = false, bool setAsCurrentPageForDebugging = false) { var simulatedPageFileName = Path.ChangeExtension(Guid.NewGuid().ToString(), ".html"); var pathToSimulatedPageFile = simulatedPageFileName; // a default, if there is no special folder if (dom.BaseForRelativePaths != null) { pathToSimulatedPageFile = Path.Combine(dom.BaseForRelativePaths, simulatedPageFileName).Replace('\\', '/'); } // FromLocalHost is smart about doing nothing if it is not a localhost url. In case it is, we // want the OriginalImageMarker (if any) after the localhost stuff. pathToSimulatedPageFile = pathToSimulatedPageFile.FromLocalhost(); if (dom.UseOriginalImages) { pathToSimulatedPageFile = OriginalImageMarker + "/" + pathToSimulatedPageFile; } var url = pathToSimulatedPageFile.ToLocalhost(); var key = pathToSimulatedPageFile.Replace('\\', '/'); if (escapeUrlAsNeededForSrcAttribute) { // We need to UrlEncode the single and double quote characters so they will play nicely with JavaScript. url = EscapeUrlQuotes(url); // When JavaScript inserts our path into the html it replaces the three magic html characters with these substitutes. // We need to modify our key so that when the JavaScript comes looking for the page its modified url will // generate the right key. key = SimulateJavaScriptHandlingOfHtml(key); } if (setAsCurrentPageForDebugging) { _keyToCurrentPage = key; } var html5String = TempFileUtils.CreateHtml5StringFromXml(dom.RawDom); lock (_urlToSimulatedPageContent) { _urlToSimulatedPageContent[key] = html5String; } return(new SimulatedPageFile() { Key = url }); }
/// <summary> /// Returns true if it make some attempt at an image, false if navigation is currently suppressed. /// </summary> /// <param name="order"></param> /// <param name="browser"></param> /// <param name="thumbnail"></param> /// <returns></returns> private bool CreateThumbNail(ThumbnailOrder order, GeckoWebBrowser browser, out Image thumbnail) { // runs on threadpool thread thumbnail = null; using (var temp = TempFileUtils.CreateHtm5FromXml(order.Document)) { order.Done = false; browser.Tag = order; if (!OpenTempFileInBrowser(browser, temp.Path)) { return(false); } var browserSize = SetWidthAndHeight(browser); if (browserSize.Height == 0) //happens when we run into the as-yet-unreproduced-or-fixed bl-254 { return(false); // will try again later } try { Logger.WriteMinorEvent("HtmlThumNailer ({2}): browser.GetBitmap({0},{1})", browserSize.Width, (uint)browserSize.Height, Thread.CurrentThread.ManagedThreadId); //BUG (April 2013) found that the initial call to GetBitMap always had a zero width, leading to an exception which //the user doesn't see and then all is well. So at the moment, we avoid the exception, and just leave with //the placeholder thumbnail. if (browserSize.Width == 0 || browserSize.Height == 0) { var paperSizeName = GetPaperSizeName(order.Document); throw new ApplicationException("Problem getting thumbnail browser for document with Paper Size: " + paperSizeName); } using (Image fullsizeImage = CreateImage(browser)) { if (_disposed) { return(false); } thumbnail = MakeThumbNail(fullsizeImage, order.Options); return(true); } } catch (Exception error) { Logger.WriteEvent("HtmlThumbNailer ({0}) got {1}", Thread.CurrentThread.ManagedThreadId, error.Message); Logger.WriteEvent("Disposing of all browsers in hopes of getting a fresh start on life"); foreach (var browserCacheForDifferentPaperSize in _browserCacheForDifferentPaperSizes) { try { Logger.WriteEvent("Disposing of browser {0}", browserCacheForDifferentPaperSize.Key); browserCacheForDifferentPaperSize.Value.Dispose(); } catch (Exception e2) { Logger.WriteEvent( "While trying to dispose of thumbnailer browsers as a result of an exception, go another: " + e2.Message); } } _browserCacheForDifferentPaperSizes.Clear(); #if DEBUG _syncControl.Invoke((Action)(() => Debug.Fail(error.Message))); #endif } } return(false); }
private string MakeAccordionContent() { var path = FileLocator.GetFileDistributedWithApplication("BloomBrowserUI/bookEdit/accordion", "Accordion.htm"); _accordionFolder = Path.GetDirectoryName(path); var domForAccordion = new HtmlDom(XmlHtmlConverter.GetXmlDomFromHtmlFile(path)); // embed settings on the page var tools = _currentlyDisplayedBook.BookInfo.Tools.Where(t => t.Enabled == true).ToList(); var settings = new Dictionary <string, object> { { "current", AccordionToolNameToDirectoryName(_currentlyDisplayedBook.BookInfo.CurrentTool) } }; var decodableTool = tools.FirstOrDefault(t => t.Name == "decodableReader"); if (decodableTool != null && !string.IsNullOrEmpty(decodableTool.State)) { settings.Add("decodableState", decodableTool.State); } var leveledTool = tools.FirstOrDefault(t => t.Name == "leveledReader"); if (leveledTool != null && !string.IsNullOrEmpty(leveledTool.State)) { settings.Add("leveledState", leveledTool.State); } var settingsStr = JsonConvert.SerializeObject(settings); settingsStr = String.Format("function GetAccordionSettings() {{ return {0};}}", settingsStr) + "\n$(document).ready(function() { restoreAccordionSettings(GetAccordionSettings()); });"; var scriptElement = domForAccordion.RawDom.CreateElement("script"); scriptElement.SetAttribute("type", "text/javascript"); scriptElement.SetAttribute("id", "ui-accordionSettings"); scriptElement.InnerText = settingsStr; domForAccordion.Head.InsertAfter(scriptElement, domForAccordion.Head.LastChild); // get additional tabs to load var checkedBoxes = new List <string>(); if (tools.Any(t => t.Name == "decodableReader")) { AppendAccordionPanel(domForAccordion, FileLocator.GetFileDistributedWithApplication(Path.Combine(_accordionFolder, "DecodableRT", "DecodableRT.htm"))); checkedBoxes.Add("showDRT"); } if (tools.Any(t => t.Name == "leveledReader")) { AppendAccordionPanel(domForAccordion, FileLocator.GetFileDistributedWithApplication(Path.Combine(_accordionFolder, "LeveledRT", "LeveledRT.htm"))); checkedBoxes.Add("showLRT"); } // Load settings into the accordion panel AppendAccordionPanel(domForAccordion, FileLocator.GetFileDistributedWithApplication(Path.Combine(_accordionFolder, "settings", "Settings.htm"))); // check the appropriate boxes foreach (var checkBoxId in checkedBoxes) { domForAccordion.Body.SelectSingleNode("//div[@id='" + checkBoxId + "']").InnerXml = "✔"; } XmlHtmlConverter.MakeXmlishTagsSafeForInterpretationAsHtml(domForAccordion.RawDom); return(TempFileUtils.CreateHtml5StringFromXml(domForAccordion.RawDom)); }