mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1067325 - Selection source in tab via frame scripts. r=mconley
This commit is contained in:
parent
3522b6e88a
commit
3882a3547c
@ -1005,11 +1005,22 @@ nsContextMenu.prototype = {
|
||||
else
|
||||
throw "not reached";
|
||||
|
||||
// unused (and play nice for fragments generated via XSLT too)
|
||||
var docUrl = null;
|
||||
window.openDialog("chrome://global/content/viewPartialSource.xul",
|
||||
"_blank", "scrollbars,resizable,chrome,dialog=no",
|
||||
docUrl, docCharset, reference, aContext);
|
||||
let inTab = Services.prefs.getBoolPref("view_source.tab");
|
||||
if (inTab) {
|
||||
let tab = gBrowser.loadOneTab("about:blank", {
|
||||
relatedToCurrent: true,
|
||||
inBackground: false
|
||||
});
|
||||
let viewSourceBrowser = gBrowser.getBrowserForTab(tab);
|
||||
top.gViewSourceUtils.viewSourceFromSelectionInBrowser(reference,
|
||||
viewSourceBrowser);
|
||||
} else {
|
||||
// unused (and play nice for fragments generated via XSLT too)
|
||||
var docUrl = null;
|
||||
window.openDialog("chrome://global/content/viewPartialSource.xul",
|
||||
"_blank", "scrollbars,resizable,chrome,dialog=no",
|
||||
docUrl, docCharset, reference, aContext);
|
||||
}
|
||||
},
|
||||
|
||||
// Open new "view source" window with the frame's URL.
|
||||
|
@ -13,6 +13,16 @@ XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
|
||||
"resource://gre/modules/Deprecated.jsm");
|
||||
|
||||
const NS_XHTML = 'http://www.w3.org/1999/xhtml';
|
||||
|
||||
// These are markers used to delimit the selection during processing. They
|
||||
// are removed from the final rendering.
|
||||
// We use noncharacter Unicode codepoints to minimize the risk of clashing
|
||||
// with anything that might legitimately be present in the document.
|
||||
// U+FDD0..FDEF <noncharacters>
|
||||
const MARK_SELECTION_START = '\uFDD0';
|
||||
const MARK_SELECTION_END = '\uFDEF';
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["ViewSourceBrowser"];
|
||||
|
||||
/**
|
||||
@ -98,6 +108,13 @@ ViewSourceBrowser.prototype = {
|
||||
this.browser.messageManager.sendAsyncMessage(...args);
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the nsIWebNavigation of the view source browser.
|
||||
*/
|
||||
get webNav() {
|
||||
return this.browser.webNavigation;
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads the source for a URL while applying some optional features if
|
||||
* enabled.
|
||||
@ -158,4 +175,168 @@ ViewSourceBrowser.prototype = {
|
||||
throw new Error("View source browser's remoteness mismatch");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the view source browser from a selection in some document.
|
||||
*
|
||||
* @param selection
|
||||
* A Selection object for the content of interest.
|
||||
*/
|
||||
loadViewSourceFromSelection(selection) {
|
||||
var range = selection.getRangeAt(0);
|
||||
var ancestorContainer = range.commonAncestorContainer;
|
||||
var doc = ancestorContainer.ownerDocument;
|
||||
|
||||
var startContainer = range.startContainer;
|
||||
var endContainer = range.endContainer;
|
||||
var startOffset = range.startOffset;
|
||||
var endOffset = range.endOffset;
|
||||
|
||||
// let the ancestor be an element
|
||||
var Node = doc.defaultView.Node;
|
||||
if (ancestorContainer.nodeType == Node.TEXT_NODE ||
|
||||
ancestorContainer.nodeType == Node.CDATA_SECTION_NODE)
|
||||
ancestorContainer = ancestorContainer.parentNode;
|
||||
|
||||
// for selectAll, let's use the entire document, including <html>...</html>
|
||||
// @see nsDocumentViewer::SelectAll() for how selectAll is implemented
|
||||
try {
|
||||
if (ancestorContainer == doc.body)
|
||||
ancestorContainer = doc.documentElement;
|
||||
} catch (e) { }
|
||||
|
||||
// each path is a "child sequence" (a.k.a. "tumbler") that
|
||||
// descends from the ancestor down to the boundary point
|
||||
var startPath = this._getPath(ancestorContainer, startContainer);
|
||||
var endPath = this._getPath(ancestorContainer, endContainer);
|
||||
|
||||
// clone the fragment of interest and reset everything to be relative to it
|
||||
// note: it is with the clone that we operate/munge from now on. Also note
|
||||
// that we clone into a data document to prevent images in the fragment from
|
||||
// loading and the like. The use of importNode here, as opposed to adoptNode,
|
||||
// is _very_ important.
|
||||
// XXXbz wish there were a less hacky way to create an untrusted document here
|
||||
var isHTML = (doc.createElement("div").tagName == "DIV");
|
||||
var dataDoc = isHTML ?
|
||||
ancestorContainer.ownerDocument.implementation.createHTMLDocument("") :
|
||||
ancestorContainer.ownerDocument.implementation.createDocument("", "", null);
|
||||
ancestorContainer = dataDoc.importNode(ancestorContainer, true);
|
||||
startContainer = ancestorContainer;
|
||||
endContainer = ancestorContainer;
|
||||
|
||||
// Only bother with the selection if it can be remapped. Don't mess with
|
||||
// leaf elements (such as <isindex>) that secretly use anynomous content
|
||||
// for their display appearance.
|
||||
var canDrawSelection = ancestorContainer.hasChildNodes();
|
||||
var tmpNode;
|
||||
if (canDrawSelection) {
|
||||
var i;
|
||||
for (i = startPath ? startPath.length-1 : -1; i >= 0; i--) {
|
||||
startContainer = startContainer.childNodes.item(startPath[i]);
|
||||
}
|
||||
for (i = endPath ? endPath.length-1 : -1; i >= 0; i--) {
|
||||
endContainer = endContainer.childNodes.item(endPath[i]);
|
||||
}
|
||||
|
||||
// add special markers to record the extent of the selection
|
||||
// note: |startOffset| and |endOffset| are interpreted either as
|
||||
// offsets in the text data or as child indices (see the Range spec)
|
||||
// (here, munging the end point first to keep the start point safe...)
|
||||
if (endContainer.nodeType == Node.TEXT_NODE ||
|
||||
endContainer.nodeType == Node.CDATA_SECTION_NODE) {
|
||||
// do some extra tweaks to try to avoid the view-source output to look like
|
||||
// ...<tag>]... or ...]</tag>... (where ']' marks the end of the selection).
|
||||
// To get a neat output, the idea here is to remap the end point from:
|
||||
// 1. ...<tag>]... to ...]<tag>...
|
||||
// 2. ...]</tag>... to ...</tag>]...
|
||||
if ((endOffset > 0 && endOffset < endContainer.data.length) ||
|
||||
!endContainer.parentNode || !endContainer.parentNode.parentNode)
|
||||
endContainer.insertData(endOffset, MARK_SELECTION_END);
|
||||
else {
|
||||
tmpNode = dataDoc.createTextNode(MARK_SELECTION_END);
|
||||
endContainer = endContainer.parentNode;
|
||||
if (endOffset === 0)
|
||||
endContainer.parentNode.insertBefore(tmpNode, endContainer);
|
||||
else
|
||||
endContainer.parentNode.insertBefore(tmpNode, endContainer.nextSibling);
|
||||
}
|
||||
}
|
||||
else {
|
||||
tmpNode = dataDoc.createTextNode(MARK_SELECTION_END);
|
||||
endContainer.insertBefore(tmpNode, endContainer.childNodes.item(endOffset));
|
||||
}
|
||||
|
||||
if (startContainer.nodeType == Node.TEXT_NODE ||
|
||||
startContainer.nodeType == Node.CDATA_SECTION_NODE) {
|
||||
// do some extra tweaks to try to avoid the view-source output to look like
|
||||
// ...<tag>[... or ...[</tag>... (where '[' marks the start of the selection).
|
||||
// To get a neat output, the idea here is to remap the start point from:
|
||||
// 1. ...<tag>[... to ...[<tag>...
|
||||
// 2. ...[</tag>... to ...</tag>[...
|
||||
if ((startOffset > 0 && startOffset < startContainer.data.length) ||
|
||||
!startContainer.parentNode || !startContainer.parentNode.parentNode ||
|
||||
startContainer != startContainer.parentNode.lastChild)
|
||||
startContainer.insertData(startOffset, MARK_SELECTION_START);
|
||||
else {
|
||||
tmpNode = dataDoc.createTextNode(MARK_SELECTION_START);
|
||||
startContainer = startContainer.parentNode;
|
||||
if (startOffset === 0)
|
||||
startContainer.parentNode.insertBefore(tmpNode, startContainer);
|
||||
else
|
||||
startContainer.parentNode.insertBefore(tmpNode, startContainer.nextSibling);
|
||||
}
|
||||
}
|
||||
else {
|
||||
tmpNode = dataDoc.createTextNode(MARK_SELECTION_START);
|
||||
startContainer.insertBefore(tmpNode, startContainer.childNodes.item(startOffset));
|
||||
}
|
||||
}
|
||||
|
||||
// now extract and display the syntax highlighted source
|
||||
tmpNode = dataDoc.createElementNS(NS_XHTML, "div");
|
||||
tmpNode.appendChild(ancestorContainer);
|
||||
|
||||
// Tell content to draw a selection after the load below
|
||||
if (canDrawSelection) {
|
||||
this.sendAsyncMessage("ViewSource:ScheduleDrawSelection");
|
||||
}
|
||||
|
||||
// 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_NONE;
|
||||
var referrerPolicy = Components.interfaces.nsIHttpChannel.REFERRER_POLICY_DEFAULT;
|
||||
this.webNav.loadURIWithOptions((isHTML ?
|
||||
"view-source:data:text/html;charset=utf-8," :
|
||||
"view-source:data:application/xml;charset=utf-8,")
|
||||
+ encodeURIComponent(tmpNode.innerHTML),
|
||||
loadFlags,
|
||||
null, referrerPolicy, // referrer
|
||||
null, null, // postData, headers
|
||||
Services.io.newURI(doc.baseURI, null, null));
|
||||
},
|
||||
|
||||
/**
|
||||
* A helper to get a path like FIXptr, but with an array instead of the
|
||||
* "tumbler" notation.
|
||||
* See FIXptr: http://lists.w3.org/Archives/Public/www-xml-linking-comments/2001AprJun/att-0074/01-NOTE-FIXptr-20010425.htm
|
||||
*/
|
||||
_getPath(ancestor, node) {
|
||||
var n = node;
|
||||
var p = n.parentNode;
|
||||
if (n == ancestor || !p)
|
||||
return null;
|
||||
var path = new Array();
|
||||
if (!path)
|
||||
return null;
|
||||
do {
|
||||
for (var i = 0; i < p.childNodes.length; i++) {
|
||||
if (p.childNodes.item(i) == n) {
|
||||
path.push(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
n = p;
|
||||
p = n.parentNode;
|
||||
} while (n != ancestor && p);
|
||||
return path;
|
||||
},
|
||||
};
|
||||
|
@ -15,15 +15,6 @@ var gTargetNode = null;
|
||||
var gEntityConverter = null;
|
||||
var gWrapLongLines = false;
|
||||
const gViewSourceCSS = 'resource://gre-resources/viewsource.css';
|
||||
const NS_XHTML = 'http://www.w3.org/1999/xhtml';
|
||||
|
||||
// These are markers used to delimit the selection during processing. They
|
||||
// are removed from the final rendering.
|
||||
// We use noncharacter Unicode codepoints to minimize the risk of clashing
|
||||
// with anything that might legitimately be present in the document.
|
||||
// U+FDD0..FDEF <noncharacters>
|
||||
const MARK_SELECTION_START = '\uFDD0';
|
||||
const MARK_SELECTION_END = '\uFDEF';
|
||||
|
||||
function onLoadViewPartialSource()
|
||||
{
|
||||
@ -36,269 +27,13 @@ function onLoadViewPartialSource()
|
||||
Services.prefs.getBoolPref("view_source.syntax_highlight"));
|
||||
|
||||
if (window.arguments[3] == 'selection')
|
||||
viewPartialSourceForSelection(window.arguments[2]);
|
||||
viewSourceChrome.loadViewSourceFromSelection(window.arguments[2]);
|
||||
else
|
||||
viewPartialSourceForFragment(window.arguments[2], window.arguments[3]);
|
||||
|
||||
window.content.focus();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// view-source of a selection with the special effect of remapping the selection
|
||||
// to the underlying view-source output
|
||||
function viewPartialSourceForSelection(selection)
|
||||
{
|
||||
var range = selection.getRangeAt(0);
|
||||
var ancestorContainer = range.commonAncestorContainer;
|
||||
var doc = ancestorContainer.ownerDocument;
|
||||
|
||||
var startContainer = range.startContainer;
|
||||
var endContainer = range.endContainer;
|
||||
var startOffset = range.startOffset;
|
||||
var endOffset = range.endOffset;
|
||||
|
||||
// let the ancestor be an element
|
||||
if (ancestorContainer.nodeType == Node.TEXT_NODE ||
|
||||
ancestorContainer.nodeType == Node.CDATA_SECTION_NODE)
|
||||
ancestorContainer = ancestorContainer.parentNode;
|
||||
|
||||
// for selectAll, let's use the entire document, including <html>...</html>
|
||||
// @see nsDocumentViewer::SelectAll() for how selectAll is implemented
|
||||
try {
|
||||
if (ancestorContainer == doc.body)
|
||||
ancestorContainer = doc.documentElement;
|
||||
} catch (e) { }
|
||||
|
||||
// each path is a "child sequence" (a.k.a. "tumbler") that
|
||||
// descends from the ancestor down to the boundary point
|
||||
var startPath = getPath(ancestorContainer, startContainer);
|
||||
var endPath = getPath(ancestorContainer, endContainer);
|
||||
|
||||
// clone the fragment of interest and reset everything to be relative to it
|
||||
// note: it is with the clone that we operate/munge from now on. Also note
|
||||
// that we clone into a data document to prevent images in the fragment from
|
||||
// loading and the like. The use of importNode here, as opposed to adoptNode,
|
||||
// is _very_ important.
|
||||
// XXXbz wish there were a less hacky way to create an untrusted document here
|
||||
var isHTML = (doc.createElement("div").tagName == "DIV");
|
||||
var dataDoc = isHTML ?
|
||||
ancestorContainer.ownerDocument.implementation.createHTMLDocument("") :
|
||||
ancestorContainer.ownerDocument.implementation.createDocument("", "", null);
|
||||
ancestorContainer = dataDoc.importNode(ancestorContainer, true);
|
||||
startContainer = ancestorContainer;
|
||||
endContainer = ancestorContainer;
|
||||
|
||||
// Only bother with the selection if it can be remapped. Don't mess with
|
||||
// leaf elements (such as <isindex>) that secretly use anynomous content
|
||||
// for their display appearance.
|
||||
var canDrawSelection = ancestorContainer.hasChildNodes();
|
||||
if (canDrawSelection) {
|
||||
var i;
|
||||
for (i = startPath ? startPath.length-1 : -1; i >= 0; i--) {
|
||||
startContainer = startContainer.childNodes.item(startPath[i]);
|
||||
}
|
||||
for (i = endPath ? endPath.length-1 : -1; i >= 0; i--) {
|
||||
endContainer = endContainer.childNodes.item(endPath[i]);
|
||||
}
|
||||
|
||||
// add special markers to record the extent of the selection
|
||||
// note: |startOffset| and |endOffset| are interpreted either as
|
||||
// offsets in the text data or as child indices (see the Range spec)
|
||||
// (here, munging the end point first to keep the start point safe...)
|
||||
var tmpNode;
|
||||
if (endContainer.nodeType == Node.TEXT_NODE ||
|
||||
endContainer.nodeType == Node.CDATA_SECTION_NODE) {
|
||||
// do some extra tweaks to try to avoid the view-source output to look like
|
||||
// ...<tag>]... or ...]</tag>... (where ']' marks the end of the selection).
|
||||
// To get a neat output, the idea here is to remap the end point from:
|
||||
// 1. ...<tag>]... to ...]<tag>...
|
||||
// 2. ...]</tag>... to ...</tag>]...
|
||||
if ((endOffset > 0 && endOffset < endContainer.data.length) ||
|
||||
!endContainer.parentNode || !endContainer.parentNode.parentNode)
|
||||
endContainer.insertData(endOffset, MARK_SELECTION_END);
|
||||
else {
|
||||
tmpNode = dataDoc.createTextNode(MARK_SELECTION_END);
|
||||
endContainer = endContainer.parentNode;
|
||||
if (endOffset == 0)
|
||||
endContainer.parentNode.insertBefore(tmpNode, endContainer);
|
||||
else
|
||||
endContainer.parentNode.insertBefore(tmpNode, endContainer.nextSibling);
|
||||
}
|
||||
}
|
||||
else {
|
||||
tmpNode = dataDoc.createTextNode(MARK_SELECTION_END);
|
||||
endContainer.insertBefore(tmpNode, endContainer.childNodes.item(endOffset));
|
||||
}
|
||||
|
||||
if (startContainer.nodeType == Node.TEXT_NODE ||
|
||||
startContainer.nodeType == Node.CDATA_SECTION_NODE) {
|
||||
// do some extra tweaks to try to avoid the view-source output to look like
|
||||
// ...<tag>[... or ...[</tag>... (where '[' marks the start of the selection).
|
||||
// To get a neat output, the idea here is to remap the start point from:
|
||||
// 1. ...<tag>[... to ...[<tag>...
|
||||
// 2. ...[</tag>... to ...</tag>[...
|
||||
if ((startOffset > 0 && startOffset < startContainer.data.length) ||
|
||||
!startContainer.parentNode || !startContainer.parentNode.parentNode ||
|
||||
startContainer != startContainer.parentNode.lastChild)
|
||||
startContainer.insertData(startOffset, MARK_SELECTION_START);
|
||||
else {
|
||||
tmpNode = dataDoc.createTextNode(MARK_SELECTION_START);
|
||||
startContainer = startContainer.parentNode;
|
||||
if (startOffset == 0)
|
||||
startContainer.parentNode.insertBefore(tmpNode, startContainer);
|
||||
else
|
||||
startContainer.parentNode.insertBefore(tmpNode, startContainer.nextSibling);
|
||||
}
|
||||
}
|
||||
else {
|
||||
tmpNode = dataDoc.createTextNode(MARK_SELECTION_START);
|
||||
startContainer.insertBefore(tmpNode, startContainer.childNodes.item(startOffset));
|
||||
}
|
||||
}
|
||||
|
||||
// now extract and display the syntax highlighted source
|
||||
tmpNode = dataDoc.createElementNS(NS_XHTML, 'div');
|
||||
tmpNode.appendChild(ancestorContainer);
|
||||
|
||||
// the load is aynchronous and so we will wait until the view-source DOM is done
|
||||
// before drawing the selection.
|
||||
if (canDrawSelection) {
|
||||
window.document.getElementById("content").addEventListener("load", drawSelection, true);
|
||||
}
|
||||
|
||||
// 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_NONE;
|
||||
var referrerPolicy = Components.interfaces.nsIHttpChannel.REFERRER_POLICY_DEFAULT;
|
||||
viewSourceChrome.webNav.loadURIWithOptions((isHTML ?
|
||||
"view-source:data:text/html;charset=utf-8," :
|
||||
"view-source:data:application/xml;charset=utf-8,")
|
||||
+ encodeURIComponent(tmpNode.innerHTML),
|
||||
loadFlags,
|
||||
null, referrerPolicy, // referrer
|
||||
null, null, // postData, headers
|
||||
Services.io.newURI(doc.baseURI, null, null));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// helper to get a path like FIXptr, but with an array instead of the "tumbler" notation
|
||||
// see FIXptr: http://lists.w3.org/Archives/Public/www-xml-linking-comments/2001AprJun/att-0074/01-NOTE-FIXptr-20010425.htm
|
||||
function getPath(ancestor, node)
|
||||
{
|
||||
var n = node;
|
||||
var p = n.parentNode;
|
||||
if (n == ancestor || !p)
|
||||
return null;
|
||||
var path = new Array();
|
||||
if (!path)
|
||||
return null;
|
||||
do {
|
||||
for (var i = 0; i < p.childNodes.length; i++) {
|
||||
if (p.childNodes.item(i) == n) {
|
||||
path.push(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
n = p;
|
||||
p = n.parentNode;
|
||||
} while (n != ancestor && p);
|
||||
return path;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// using special markers left in the serialized source, this helper makes the
|
||||
// underlying markup of the selected fragment to automatically appear as selected
|
||||
// on the inflated view-source DOM
|
||||
function drawSelection()
|
||||
{
|
||||
gBrowser.contentDocument.title =
|
||||
gViewSourceBundle.getString("viewSelectionSourceTitle");
|
||||
|
||||
// find the special selection markers that we added earlier, and
|
||||
// draw the selection between the two...
|
||||
var findService = null;
|
||||
try {
|
||||
// get the find service which stores the global find state
|
||||
findService = Components.classes["@mozilla.org/find/find_service;1"]
|
||||
.getService(Components.interfaces.nsIFindService);
|
||||
} catch(e) { }
|
||||
if (!findService)
|
||||
return;
|
||||
|
||||
// cache the current global find state
|
||||
var matchCase = findService.matchCase;
|
||||
var entireWord = findService.entireWord;
|
||||
var wrapFind = findService.wrapFind;
|
||||
var findBackwards = findService.findBackwards;
|
||||
var searchString = findService.searchString;
|
||||
var replaceString = findService.replaceString;
|
||||
|
||||
// setup our find instance
|
||||
var findInst = gBrowser.webBrowserFind;
|
||||
findInst.matchCase = true;
|
||||
findInst.entireWord = false;
|
||||
findInst.wrapFind = true;
|
||||
findInst.findBackwards = false;
|
||||
|
||||
// ...lookup the start mark
|
||||
findInst.searchString = MARK_SELECTION_START;
|
||||
var startLength = MARK_SELECTION_START.length;
|
||||
findInst.findNext();
|
||||
|
||||
var selection = content.getSelection();
|
||||
if (!selection.rangeCount)
|
||||
return;
|
||||
|
||||
var range = selection.getRangeAt(0);
|
||||
|
||||
var startContainer = range.startContainer;
|
||||
var startOffset = range.startOffset;
|
||||
|
||||
// ...lookup the end mark
|
||||
findInst.searchString = MARK_SELECTION_END;
|
||||
var endLength = MARK_SELECTION_END.length;
|
||||
findInst.findNext();
|
||||
|
||||
var endContainer = selection.anchorNode;
|
||||
var endOffset = selection.anchorOffset;
|
||||
|
||||
// reset the selection that find has left
|
||||
selection.removeAllRanges();
|
||||
|
||||
// delete the special markers now...
|
||||
endContainer.deleteData(endOffset, endLength);
|
||||
startContainer.deleteData(startOffset, startLength);
|
||||
if (startContainer == endContainer)
|
||||
endOffset -= startLength; // has shrunk if on same text node...
|
||||
range.setEnd(endContainer, endOffset);
|
||||
|
||||
// show the selection and scroll it into view
|
||||
selection.addRange(range);
|
||||
// the default behavior of the selection is to scroll at the end of
|
||||
// the selection, whereas in this situation, it is more user-friendly
|
||||
// to scroll at the beginning. So we override the default behavior here
|
||||
try {
|
||||
getSelectionController().scrollSelectionIntoView(
|
||||
Ci.nsISelectionController.SELECTION_NORMAL,
|
||||
Ci.nsISelectionController.SELECTION_ANCHOR_REGION,
|
||||
true);
|
||||
}
|
||||
catch(e) { }
|
||||
|
||||
// restore the current find state
|
||||
findService.matchCase = matchCase;
|
||||
findService.entireWord = entireWord;
|
||||
findService.wrapFind = wrapFind;
|
||||
findService.findBackwards = findBackwards;
|
||||
findService.searchString = searchString;
|
||||
findService.replaceString = replaceString;
|
||||
|
||||
findInst.matchCase = matchCase;
|
||||
findInst.entireWord = entireWord;
|
||||
findInst.wrapFind = wrapFind;
|
||||
findInst.findBackwards = findBackwards;
|
||||
findInst.searchString = searchString;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// special handler for markups such as MathML where reformatting the output is
|
||||
// helpful
|
||||
|
@ -14,6 +14,14 @@ XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
|
||||
|
||||
const BUNDLE_URL = "chrome://global/locale/viewSource.properties";
|
||||
|
||||
// These are markers used to delimit the selection during processing. They
|
||||
// are removed from the final rendering.
|
||||
// We use noncharacter Unicode codepoints to minimize the risk of clashing
|
||||
// with anything that might legitimately be present in the document.
|
||||
// U+FDD0..FDEF <noncharacters>
|
||||
const MARK_SELECTION_START = '\uFDD0';
|
||||
const MARK_SELECTION_END = '\uFDEF';
|
||||
|
||||
let global = this;
|
||||
|
||||
/**
|
||||
@ -39,8 +47,16 @@ let ViewSourceContent = {
|
||||
"ViewSource:ToggleWrapping",
|
||||
"ViewSource:ToggleSyntaxHighlighting",
|
||||
"ViewSource:SetCharacterSet",
|
||||
"ViewSource:ScheduleDrawSelection",
|
||||
],
|
||||
|
||||
/**
|
||||
* When showing selection source, chrome will construct a page fragment to
|
||||
* show, and then instruct content to draw a selection after load. This is
|
||||
* set true when there is a pending request to draw selection.
|
||||
*/
|
||||
needsDrawSelection: false,
|
||||
|
||||
/**
|
||||
* ViewSourceContent is attached as an nsISelectionListener on pageshow,
|
||||
* and removed on pagehide. When the initial about:blank is transitioned
|
||||
@ -129,6 +145,9 @@ let ViewSourceContent = {
|
||||
case "ViewSource:SetCharacterSet":
|
||||
this.setCharacterSet(data.charset, data.doPageLoad);
|
||||
break;
|
||||
case "ViewSource:ScheduleDrawSelection":
|
||||
this.scheduleDrawSelection();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@ -177,6 +196,14 @@ let ViewSourceContent = {
|
||||
.QueryInterface(Ci.nsISelectionController);
|
||||
},
|
||||
|
||||
/**
|
||||
* A shortcut to the nsIWebBrowserFind for the content.
|
||||
*/
|
||||
get webBrowserFind() {
|
||||
return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebBrowserFind);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the parent sends a message to view some source code.
|
||||
*
|
||||
@ -369,6 +396,15 @@ let ViewSourceContent = {
|
||||
this.selectionListenerAttached = true;
|
||||
}
|
||||
content.focus();
|
||||
|
||||
// If we need to draw the selection, wait until an actual view source page
|
||||
// has loaded, instead of about:blank.
|
||||
if (this.needsDrawSelection &&
|
||||
content.document.documentURI.startsWith("view-source:")) {
|
||||
this.needsDrawSelection = false;
|
||||
this.drawSelection();
|
||||
}
|
||||
|
||||
sendAsyncMessage("ViewSource:SourceLoaded");
|
||||
},
|
||||
|
||||
@ -721,5 +757,109 @@ let ViewSourceContent = {
|
||||
|
||||
this.updateStatusTask.arm();
|
||||
},
|
||||
|
||||
/**
|
||||
* Chrome has requested that we draw a selection once the content loads.
|
||||
* We set a flag, and wait for the load event, where drawSelection() will be
|
||||
* called to do the real work.
|
||||
*/
|
||||
scheduleDrawSelection() {
|
||||
this.needsDrawSelection = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Using special markers left in the serialized source, this helper makes the
|
||||
* underlying markup of the selected fragment to automatically appear as
|
||||
* selected on the inflated view-source DOM.
|
||||
*/
|
||||
drawSelection() {
|
||||
content.document.title =
|
||||
this.bundle.GetStringFromName("viewSelectionSourceTitle");
|
||||
|
||||
// find the special selection markers that we added earlier, and
|
||||
// draw the selection between the two...
|
||||
var findService = null;
|
||||
try {
|
||||
// get the find service which stores the global find state
|
||||
findService = Cc["@mozilla.org/find/find_service;1"]
|
||||
.getService(Ci.nsIFindService);
|
||||
} catch(e) { }
|
||||
if (!findService)
|
||||
return;
|
||||
|
||||
// cache the current global find state
|
||||
var matchCase = findService.matchCase;
|
||||
var entireWord = findService.entireWord;
|
||||
var wrapFind = findService.wrapFind;
|
||||
var findBackwards = findService.findBackwards;
|
||||
var searchString = findService.searchString;
|
||||
var replaceString = findService.replaceString;
|
||||
|
||||
// setup our find instance
|
||||
var findInst = this.webBrowserFind;
|
||||
findInst.matchCase = true;
|
||||
findInst.entireWord = false;
|
||||
findInst.wrapFind = true;
|
||||
findInst.findBackwards = false;
|
||||
|
||||
// ...lookup the start mark
|
||||
findInst.searchString = MARK_SELECTION_START;
|
||||
var startLength = MARK_SELECTION_START.length;
|
||||
findInst.findNext();
|
||||
|
||||
var selection = content.getSelection();
|
||||
if (!selection.rangeCount)
|
||||
return;
|
||||
|
||||
var range = selection.getRangeAt(0);
|
||||
|
||||
var startContainer = range.startContainer;
|
||||
var startOffset = range.startOffset;
|
||||
|
||||
// ...lookup the end mark
|
||||
findInst.searchString = MARK_SELECTION_END;
|
||||
var endLength = MARK_SELECTION_END.length;
|
||||
findInst.findNext();
|
||||
|
||||
var endContainer = selection.anchorNode;
|
||||
var endOffset = selection.anchorOffset;
|
||||
|
||||
// reset the selection that find has left
|
||||
selection.removeAllRanges();
|
||||
|
||||
// delete the special markers now...
|
||||
endContainer.deleteData(endOffset, endLength);
|
||||
startContainer.deleteData(startOffset, startLength);
|
||||
if (startContainer == endContainer)
|
||||
endOffset -= startLength; // has shrunk if on same text node...
|
||||
range.setEnd(endContainer, endOffset);
|
||||
|
||||
// show the selection and scroll it into view
|
||||
selection.addRange(range);
|
||||
// the default behavior of the selection is to scroll at the end of
|
||||
// the selection, whereas in this situation, it is more user-friendly
|
||||
// to scroll at the beginning. So we override the default behavior here
|
||||
try {
|
||||
this.selectionController.scrollSelectionIntoView(
|
||||
Ci.nsISelectionController.SELECTION_NORMAL,
|
||||
Ci.nsISelectionController.SELECTION_ANCHOR_REGION,
|
||||
true);
|
||||
}
|
||||
catch(e) { }
|
||||
|
||||
// restore the current find state
|
||||
findService.matchCase = matchCase;
|
||||
findService.entireWord = entireWord;
|
||||
findService.wrapFind = wrapFind;
|
||||
findService.findBackwards = findBackwards;
|
||||
findService.searchString = searchString;
|
||||
findService.replaceString = replaceString;
|
||||
|
||||
findInst.matchCase = matchCase;
|
||||
findInst.entireWord = entireWord;
|
||||
findInst.wrapFind = wrapFind;
|
||||
findInst.findBackwards = findBackwards;
|
||||
findInst.searchString = searchString;
|
||||
},
|
||||
};
|
||||
ViewSourceContent.init();
|
||||
|
@ -215,13 +215,6 @@ ViewSourceChrome.prototype = {
|
||||
return window.messageManager;
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the nsIWebNavigation of the view source browser.
|
||||
*/
|
||||
get webNav() {
|
||||
return this.browser.webNavigation;
|
||||
},
|
||||
|
||||
/**
|
||||
* Send the browser forward in its history.
|
||||
*/
|
||||
|
@ -92,6 +92,23 @@ var gViewSourceUtils = {
|
||||
viewSourceBrowser.loadViewSource(aArgs);
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays view source for a selection from some document in the provided
|
||||
* <browser>. This allows for non-window display methods, such as a tab from
|
||||
* Firefox. The caller that manages the <browser> is responsible for ensuring
|
||||
* the companion frame script, viewSource-content.js, has been loaded for the
|
||||
* <browser>.
|
||||
*
|
||||
* @param aSelection
|
||||
* A Selection object for the content of interest.
|
||||
* @param aViewSourceInBrowser
|
||||
* The browser to display the view source in.
|
||||
*/
|
||||
viewSourceFromSelectionInBrowser: function(aSelection, aViewSourceInBrowser) {
|
||||
let viewSourceBrowser = new ViewSourceBrowser(aViewSourceInBrowser);
|
||||
viewSourceBrowser.loadViewSourceFromSelection(aSelection);
|
||||
},
|
||||
|
||||
// Opens the interval view source viewer
|
||||
_openInInternalViewer: function(aArgsOrURL, aPageDescriptor, aDocument, aLineNumber)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user