From f8fea9e83bf9ccab95e14f41ae8b89383105b883 Mon Sep 17 00:00:00 2001 From: "chanial%noos.fr" Date: Wed, 22 Oct 2003 08:02:23 +0000 Subject: [PATCH] sync the source viewer with the suite git-svn-id: svn://10.0.0.236/trunk@148275 18797224-902f-48f8-a5cc-f745e15eee43 --- .../viewsource/content/viewPartialSource.js | 28 +- .../viewsource/content/viewPartialSource.xul | 5 +- .../viewsource/content/viewSource.js | 395 +++++++++++++++++- .../viewsource/content/viewSource.xul | 10 +- mozilla/toolkit/components/viewsource/jar.mn | 11 +- .../viewsource/locale/viewSource.dtd | 22 +- 6 files changed, 417 insertions(+), 54 deletions(-) diff --git a/mozilla/toolkit/components/viewsource/content/viewPartialSource.js b/mozilla/toolkit/components/viewsource/content/viewPartialSource.js index 5758b846671..5422ce12ca9 100644 --- a/mozilla/toolkit/components/viewsource/content/viewPartialSource.js +++ b/mozilla/toolkit/components/viewsource/content/viewPartialSource.js @@ -1,3 +1,4 @@ +# -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- # **** BEGIN LICENSE BLOCK **** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # @@ -44,7 +45,7 @@ var gTargetNode = null; var gEntityConverter = null; var gWrapLongLines = false; -const gViewSourceCSS = 'resource:///res/viewsource.css'; +const gViewSourceCSS = 'resource://gre/res/viewsource.css'; const NS_XHTML = 'http://www.w3.org/1999/xhtml'; // These are markers used to delimit the selection during processing. They @@ -56,8 +57,7 @@ const MARK_SELECTION_START = '\u200B\u200B\u200B\u200B\u200B'; const MARK_SELECTION_END = '\u200B\u200B\u200B\u200B\u200B'; function onLoadViewPartialSource() -{ - gBrowser = document.getElementById("content"); +{ // check the view_source.wrap_long_lines pref and set the menuitem's checked attribute accordingly if (gPrefs) { try { @@ -78,7 +78,7 @@ function onLoadViewPartialSource() // disable menu items that don't work since the selection is munged and // the editor doesn't work for MathML document.getElementById('cmd_savePage').setAttribute('disabled', 'true'); - // we don't support yet external editor + // we don't support external editors //document.getElementById('cmd_editPage').setAttribute('disabled', 'true'); if (window.arguments[3] == 'selection') @@ -197,8 +197,8 @@ function viewPartialSourceForSelection(selection) // all our content is held by the data:URI and URIs are internally stored as utf-8 (see nsIURI.idl) var loadFlags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE; - gBrowser.webNavigation - .loadURI("view-source:data:text/html;charset=utf-8," + escape(tmpNode.innerHTML), + getBrowser().webNavigation + .loadURI("view-source:data:text/html;charset=utf-8," + escape(tmpNode.innerHTML), loadFlags, null, null, null); } @@ -253,7 +253,7 @@ function drawSelection() var replaceString = findService.replaceString; // setup our find instance - var findInst = gBrowser.webBrowserFind; + var findInst = getBrowser().webBrowserFind; findInst.matchCase = true; findInst.entireWord = false; findInst.wrapFind = true; @@ -264,7 +264,7 @@ function drawSelection() var startLength = MARK_SELECTION_START.length; findInst.findNext(); - var contentWindow = gBrowser.contentDocument.defaultView; + var contentWindow = getBrowser().contentDocument.defaultView; var selection = contentWindow.getSelection(); var range = selection.getRangeAt(0); @@ -295,11 +295,11 @@ function drawSelection() // the selection, whereas in this situation, it is more user-friendly // to scroll at the beginning. So we override the default behavior here try { - gBrowser.docShell - .QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsISelectionDisplay) - .QueryInterface(Components.interfaces.nsISelectionController) - .scrollSelectionIntoView(Components.interfaces.nsISelectionController.SELECTION_NORMAL, + getBrowser().docShell + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsISelectionDisplay) + .QueryInterface(Components.interfaces.nsISelectionController) + .scrollSelectionIntoView(Components.interfaces.nsISelectionController.SELECTION_NORMAL, Components.interfaces.nsISelectionController.SELECTION_ANCHOR_REGION, true); } @@ -359,7 +359,7 @@ function viewPartialSourceForFragment(node, context) ; // end // display - var doc = gBrowser.contentDocument; + var doc = getBrowser().contentDocument; doc.open("text/html", "replace"); doc.write(source); doc.close(); diff --git a/mozilla/toolkit/components/viewsource/content/viewPartialSource.xul b/mozilla/toolkit/components/viewsource/content/viewPartialSource.xul index d41d8a00d8c..0aa3061978c 100644 --- a/mozilla/toolkit/components/viewsource/content/viewPartialSource.xul +++ b/mozilla/toolkit/components/viewsource/content/viewPartialSource.xul @@ -65,11 +65,12 @@ - - + + + diff --git a/mozilla/toolkit/components/viewsource/content/viewSource.js b/mozilla/toolkit/components/viewsource/content/viewSource.js index 264fa98707a..310988eca17 100644 --- a/mozilla/toolkit/components/viewsource/content/viewSource.js +++ b/mozilla/toolkit/components/viewsource/content/viewSource.js @@ -22,9 +22,15 @@ # const pageLoaderIface = Components.interfaces.nsIWebPageDescriptor; +const nsISelectionPrivate = Components.interfaces.nsISelectionPrivate; +const nsISelectionController = Components.interfaces.nsISelectionController; var gBrowser = null; +var gViewSourceBundle = null; var gPrefs = null; +var gLastLineFound = ''; +var gGoToLine = 0; + try { var prefService = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefService); @@ -32,24 +38,60 @@ try { } catch (ex) { } +var gSelectionListener = { + timeout: 0, + notifySelectionChanged: function(doc, sel, reason) + { + // Coalesce notifications within 100ms intervals. + if (!this.timeout) + this.timeout = setTimeout(updateStatusBar, 100); + } +} + function onLoadViewSource() { - gBrowser = document.getElementById("content"); viewSource(window.arguments[0]); document.commandDispatcher.focusedWindow = content; } +function getBrowser() +{ + if (!gBrowser) + gBrowser = document.getElementById("content"); + return gBrowser; +} + +function getSelectionController() +{ + return getBrowser().docShell + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsISelectionDisplay) + .QueryInterface(nsISelectionController); + +} + +function getViewSourceBundle() +{ + if (!gViewSourceBundle) + gViewSourceBundle = document.getElementById("viewSourceBundle"); + return gViewSourceBundle; +} + function viewSource(url) { if (!url) return false; // throw Components.results.NS_ERROR_FAILURE; + getBrowser().addEventListener("unload", onUnloadContent, true); + getBrowser().addEventListener("load", onLoadContent, true); + var loadFromURL = true; // // Parse the 'arguments' supplied with the dialog. // arg[0] - URL string. // arg[1] - Charset value in the form 'charset=xxx'. // arg[2] - Page descriptor used to load content from the cache. + // arg[3] - Line number to go to. // if ("arguments" in window) { var arg; @@ -72,6 +114,13 @@ function viewSource(url) } } // + // Get any specified line to jump to. + // + if (window.arguments.length >= 4) { + arg = window.arguments[3]; + gGoToLine = parseInt(arg); + } + // // Use the page descriptor to load the content from the cache (if // available). // @@ -80,7 +129,7 @@ function viewSource(url) try { if (typeof(arg) == "object" && arg != null) { - var PageLoader = gBrowser.webNavigation.QueryInterface(pageLoaderIface); + var PageLoader = getBrowser().webNavigation.QueryInterface(pageLoaderIface); // // Load the page using the page descriptor rather than the URL. @@ -104,7 +153,7 @@ function viewSource(url) // var loadFlags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE; var viewSrcUrl = "view-source:" + url; - gBrowser.webNavigation.loadURI(viewSrcUrl, loadFlags, null, null, null); + getBrowser().webNavigation.loadURI(viewSrcUrl, loadFlags, null, null, null); } //check the view_source.wrap_long_lines pref and set the menuitem's checked attribute accordingly @@ -125,37 +174,346 @@ function viewSource(url) } window._content.focus(); + return true; } +function onLoadContent() +{ + // + // If the view source was opened with a "go to line" argument. + // + if (gGoToLine > 0) { + goToLine(gGoToLine); + gGoToLine = 0; + } + document.getElementById('cmd_goToLine').removeAttribute('disabled'); + + // Register a listener so that we can show the caret position on the status bar. + window._content.getSelection() + .QueryInterface(nsISelectionPrivate) + .addSelectionListener(gSelectionListener); +} + +function onUnloadContent() +{ + // + // Disable "go to line" while reloading due to e.g. change of charset + // or toggling of syntax highlighting. + // + document.getElementById('cmd_goToLine').setAttribute('disabled', 'true'); +} + function ViewSourceClose() { window.close(); } +function BrowserReload() +{ + // Reload will always reload from cache which is probably not what's wanted + BrowserReloadSkipCache(); +} + +function BrowserReloadSkipCache() +{ + const webNavigation = getBrowser().webNavigation; + webNavigation.reload(webNavigation.LOAD_FLAGS_BYPASS_PROXY | webNavigation.LOAD_FLAGS_BYPASS_CACHE); +} + // Strips the |view-source:| for editPage() function ViewSourceEditPage() { - var url = window._content.location.href; - url = url.substring(12,url.length); - editPage(url,window, false); + editPage(window.content.location.href.substring(12), window, false); } // Strips the |view-source:| for saveURL() function ViewSourceSavePage() { - var url = window._content.document.location.href; - url = url.substring(12,url.length); + saveURL(window.content.location.href.substring(12), null, "SaveLinkTitle"); +} - saveURL(url, null, "SaveLinkTitle"); +function ViewSourceGoToLine() +{ + var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + var viewSourceBundle = getViewSourceBundle(); + + var input = {value:gLastLineFound}; + for (;;) { + var ok = promptService.prompt( + window, + viewSourceBundle.getString("goToLineTitle"), + viewSourceBundle.getString("goToLineText"), + input, + null, + {value:0}); + + if (!ok) return; + + var line = parseInt(input.value); + + if (!(line > 0)) { + promptService.alert(window, + viewSourceBundle.getString("invalidInputTitle"), + viewSourceBundle.getString("invalidInputText")); + + continue; + } + + var found = goToLine(line); + + if (found) { + break; + } + + promptService.alert(window, + viewSourceBundle.getString("outOfRangeTitle"), + viewSourceBundle.getString("outOfRangeText")); + } +} + +function goToLine(line) +{ + var viewsource = window._content.document.body; + + // + // The source document is made up of a number of pre elements with + // id attributes in the format
, meaning that
+  // the first line in the pre element is number 123.
+  // Do binary search to find the pre element containing the line.
+  //
+  var pre;
+  for (var lbound = 0, ubound = viewsource.childNodes.length; ; ) {
+    var middle = (lbound + ubound) >> 1;
+    pre = viewsource.childNodes[middle];
+
+    var firstLine = parseInt(pre.id.substring(4));
+
+    if (lbound == ubound - 1) {
+      break;
+    }
+
+    if (line >= firstLine) {
+      lbound = middle;
+    } else {
+      ubound = middle;
+    }
+  }
+
+  var result = {};
+  var found = findLocation(pre, line, null, -1, false, result);
+
+  if (!found) {
+    return false;
+  }
+
+  var selection = window._content.getSelection();
+  selection.removeAllRanges();
+
+  // In our case, the range's startOffset is after "\n" on the previous line.
+  // Tune the selection at the beginning of the next line and do some tweaking
+  // to position the focusNode and the caret at the beginning of the line.
+
+  selection.QueryInterface(nsISelectionPrivate)
+    .interlinePosition = true;	
+
+  selection.addRange(result.range);
+
+  if (!selection.isCollapsed) {
+    selection.collapseToEnd();
+
+    var offset = result.range.startOffset;
+    var node = result.range.startContainer;
+    if (offset < node.data.length) {
+      // The same text node spans across the "\n", just focus where we were.
+      selection.extend(node, offset);
+    }
+    else {
+      // There is another tag just after the "\n", hook there. We need
+      // to focus a safe point because there are edgy cases such as
+      // ...\n... vs.
+      // ...\n......
+      node = node.nextSibling ? node.nextSibling : node.parentNode.nextSibling;
+      selection.extend(node, 0);
+    }
+  }
+
+  var selCon = getSelectionController();
+  selCon.setDisplaySelection(nsISelectionController.SELECTION_ON);
+  selCon.setCaretEnabled(true);
+  selCon.setCaretVisibilityDuringSelection(true);
+
+  // Scroll the beginning of the line into view.
+  selCon.scrollSelectionIntoView(
+    nsISelectionController.SELECTION_NORMAL,
+    nsISelectionController.SELECTION_FOCUS_REGION,
+    true);
+
+  gLastLineFound = line;
+
+  //pch: don't update the status bar for now
+  //document.getElementById("statusbar-line-col").label = getViewSourceBundle()
+  //    .getFormattedString("statusBarLineCol", [line, 1]);
+
+  return true;
+}
+
+function updateStatusBar()
+{
+  // Reset the coalesce flag.
+  gSelectionListener.timeout = 0;
+
+  var statusBarField = document.getElementById("statusbar-line-col");
+
+  var selection = window._content.getSelection();
+  if (!selection.focusNode) {
+    statusBarField.label = '';
+    return;
+  }
+  if (selection.focusNode.nodeType != Node.TEXT_NODE) {
+    return;
+  }
+
+  var selCon = getSelectionController();
+  selCon.setDisplaySelection(nsISelectionController.SELECTION_ON);
+  selCon.setCaretEnabled(true);
+  selCon.setCaretVisibilityDuringSelection(true);
+
+  var interlinePosition = selection
+      .QueryInterface(nsISelectionPrivate).interlinePosition;
+
+  var result = {};
+  findLocation(null, -1, 
+      selection.focusNode, selection.focusOffset, interlinePosition, result);
+
+  statusBarField.label = getViewSourceBundle()
+      .getFormattedString("statusBarLineCol", [result.line, result.col]);
+}
+
+//
+// Loops through the text lines in the pre element. The arguments are either
+// (pre, line) or (node, offset, interlinePosition). result is an out
+// argument. If (pre, line) are specified (and node == null), result.range is
+// a range spanning the specified line. If the (node, offset,
+// interlinePosition) are specified, result.line and result.col are the line
+// and column number of the specified offset in the specified node relative to
+// the whole file.
+//
+function findLocation(pre, line, node, offset, interlinePosition, result)
+{
+  if (node && !pre) {
+    //
+    // Look upwards to find the current pre element.
+    //
+    for (pre = node;
+         pre.nodeName != "PRE";
+         pre = pre.parentNode);
+  }
+
+  //
+  // The source document is made up of a number of pre elements with
+  // id attributes in the format 
, meaning that
+  // the first line in the pre element is number 123.
+  //
+  var curLine = parseInt(pre.id.substring(4));
+
+  //
+  // Walk through each of the text nodes and count newlines.
+  //
+  var treewalker = window._content.document
+      .createTreeWalker(pre, NodeFilter.SHOW_TEXT, null, false);
+
+  //
+  // The column number of the first character in the current text node.
+  //
+  var firstCol = 1;
+
+  var found = false;
+  for (var textNode = treewalker.firstChild();
+       textNode && !found;
+       textNode = treewalker.nextNode()) {
+
+    //
+    // \r is not a valid character in the DOM, so we only check for \n.
+    //
+    var lineArray = textNode.data.split(/\n/);
+    var lastLineInNode = curLine + lineArray.length - 1;
+
+    //
+    // Check if we can skip the text node without further inspection.
+    //
+    if (node ? (textNode != node) : (lastLineInNode < line)) {
+      if (lineArray.length > 1) {
+        firstCol = 1;
+      }
+      firstCol += lineArray[lineArray.length - 1].length;
+      curLine = lastLineInNode;
+      continue;
+    }
+
+    //
+    // curPos is the offset within the current text node of the first
+    // character in the current line.
+    //
+    for (var i = 0, curPos = 0;
+         i < lineArray.length;
+         curPos += lineArray[i++].length + 1) {
+
+      if (i > 0) {
+        curLine++;
+      }
+
+      if (node) {
+        if (offset >= curPos && offset <= curPos + lineArray[i].length) {
+          //
+          // If we are right after the \n of a line and interlinePosition is
+          // false, the caret looks as if it were at the end of the previous
+          // line, so we display that line and column instead.
+          //
+          if (i > 0 && offset == curPos && !interlinePosition) {
+            result.line = curLine - 1;
+            var prevPos = curPos - lineArray[i - 1].length;
+            result.col = (i == 1 ? firstCol : 1) + offset - prevPos;
+
+          } else {
+            result.line = curLine;
+            result.col = (i == 0 ? firstCol : 1) + offset - curPos;
+          }
+          found = true;
+
+          break;
+        }
+
+      } else {
+        if (curLine == line && !("range" in result)) {
+          result.range = document.createRange();
+          result.range.setStart(textNode, curPos);
+
+          //
+          // This will always be overridden later, except when we look for
+          // the very last line in the file (this is the only line that does
+          // not end with \n).
+          //
+          result.range.setEndAfter(pre.lastChild);
+
+        } else if (curLine == line + 1) {
+          result.range.setEnd(textNode, curPos - 1);
+          found = true;
+          break;
+        }
+      }
+    }
+  }
+
+  return found;
 }
 
 //function to toggle long-line wrapping and set the view_source.wrap_long_lines 
 //pref to persist the last state
 function wrapLongLines()
 {
-  //get the first pre tag which surrounds the entire viewsource content
-  var myWrap = window._content.document.getElementById('viewsource');
+  var myWrap = window._content.document.body;
 
   if (myWrap.className == '')
     myWrap.className = 'wrap';
@@ -185,20 +543,17 @@ function highlightSyntax()
   var highlightSyntax = (highlightSyntaxMenu.getAttribute("checked") == "true");
   gPrefs.setBoolPref("view_source.syntax_highlight", highlightSyntax);
 
-  var PageLoader = gBrowser.webNavigation.QueryInterface(pageLoaderIface);
+  var PageLoader = getBrowser().webNavigation.QueryInterface(pageLoaderIface);
   PageLoader.LoadPage(PageLoader.currentDescriptor, pageLoaderIface.DISPLAY_NORMAL);
 }
 
+// Fix for bug 136322: this function overrides the function in
+// browser.js to call PageLoader.LoadPage() instead of BrowserReloadWithFlags()
 function BrowserSetForcedCharacterSet(aCharset)
 {
-  var docCharset = gBrowser.docShell
-                           .QueryInterface(Components.interfaces.nsIDocCharset);
+  var docCharset = getBrowser().docShell.QueryInterface(
+                            Components.interfaces.nsIDocCharset);
   docCharset.charset = aCharset;
-  var PageLoader = gBrowser.webNavigation.QueryInterface(pageLoaderIface);
+  var PageLoader = getBrowser().webNavigation.QueryInterface(pageLoaderIface);
   PageLoader.LoadPage(PageLoader.currentDescriptor, pageLoaderIface.DISPLAY_NORMAL);
 }
-
-function BrowserFind()
-{
-  findInPage(gBrowser, window._content, window._content)
-}
diff --git a/mozilla/toolkit/components/viewsource/content/viewSource.xul b/mozilla/toolkit/components/viewsource/content/viewSource.xul
index b7f295ae28f..fec505b7e81 100644
--- a/mozilla/toolkit/components/viewsource/content/viewSource.xul
+++ b/mozilla/toolkit/components/viewsource/content/viewSource.xul
@@ -64,13 +64,15 @@