mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-07 07:04:09 +00:00
Bug 1505915, move view source components to use JSWindowActor. This allows view frame source to work in out of process child frames, r=mconley
Differential Revision: https://phabricator.services.mozilla.com/D60253 --HG-- rename : toolkit/components/viewsource/content/viewSource-content.js => toolkit/actors/ViewSourceChild.jsm rename : toolkit/components/viewsource/content/viewSource-content.js => toolkit/actors/ViewSourcePageChild.jsm rename : toolkit/components/viewsource/ViewSourceBrowser.jsm => toolkit/actors/ViewSourcePageParent.jsm extra : moz-landing-system : lando
This commit is contained in:
parent
6dbc210f59
commit
0355257f83
@ -1174,7 +1174,10 @@ class nsContextMenu {
|
||||
return viewSourceBrowser;
|
||||
};
|
||||
|
||||
top.gViewSourceUtils.viewPartialSourceInBrowser(browser, openSelectionFn);
|
||||
top.gViewSourceUtils.viewPartialSourceInBrowser(
|
||||
this.actor.browsingContext,
|
||||
openSelectionFn
|
||||
);
|
||||
}
|
||||
|
||||
// Open new "view source" window with the frame's URL.
|
||||
|
@ -208,7 +208,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
TabStateCache: "resource:///modules/sessionstore/TabStateCache.jsm",
|
||||
TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.jsm",
|
||||
Utils: "resource://gre/modules/sessionstore/Utils.jsm",
|
||||
ViewSourceBrowser: "resource://gre/modules/ViewSourceBrowser.jsm",
|
||||
setTimeout: "resource://gre/modules/Timer.jsm",
|
||||
});
|
||||
|
||||
@ -4710,12 +4709,6 @@ var SessionStoreInternal = {
|
||||
});
|
||||
}
|
||||
|
||||
// If the restored browser wants to show view source content, start up a
|
||||
// view source browser that will load the required frame script.
|
||||
if (uri && ViewSourceBrowser.isViewSource(uri)) {
|
||||
new ViewSourceBrowser(browser);
|
||||
}
|
||||
|
||||
browser.messageManager.sendAsyncMessage("SessionStore:restoreTabContent", {
|
||||
loadArguments,
|
||||
isRemotenessUpdate,
|
||||
|
@ -3,28 +3,183 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var EXPORTED_SYMBOLS = ["SelectionSourceChild"];
|
||||
var EXPORTED_SYMBOLS = ["ViewSourceChild"];
|
||||
|
||||
const { ActorChild } = ChromeUtils.import(
|
||||
"resource://gre/modules/ActorChild.jsm"
|
||||
);
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
class SelectionSourceChild extends ActorChild {
|
||||
receiveMessage(message) {
|
||||
const global = message.target;
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"ViewSourcePageChild",
|
||||
"resource://gre/actors/ViewSourcePageChild.jsm"
|
||||
);
|
||||
|
||||
if (message.name == "ViewSource:GetSelection") {
|
||||
let selectionDetails;
|
||||
try {
|
||||
selectionDetails = this.getSelection(global);
|
||||
} finally {
|
||||
global.sendAsyncMessage(
|
||||
"ViewSource:GetSelectionDone",
|
||||
selectionDetails
|
||||
class ViewSourceChild extends JSWindowActorChild {
|
||||
receiveMessage(message) {
|
||||
let data = message.data;
|
||||
switch (message.name) {
|
||||
case "ViewSource:LoadSource":
|
||||
this.viewSource(
|
||||
data.URL,
|
||||
data.outerWindowID,
|
||||
data.lineNumber,
|
||||
data.shouldWrap
|
||||
);
|
||||
break;
|
||||
case "ViewSource:LoadSourceWithSelection":
|
||||
this.viewSourceWithSelection(
|
||||
data.URL,
|
||||
data.drawSelection,
|
||||
data.baseURI
|
||||
);
|
||||
break;
|
||||
case "ViewSource:GetSelection":
|
||||
let selectionDetails;
|
||||
try {
|
||||
selectionDetails = this.getSelection(this.document.ownerGlobal);
|
||||
} catch (e) {}
|
||||
return selectionDetails;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the parent sends a message to view some source code.
|
||||
*
|
||||
* @param URL (required)
|
||||
* The URL string of the source to be shown.
|
||||
* @param outerWindowID (optional)
|
||||
* The outerWindowID of the content window that has hosted
|
||||
* the document, in case we want to retrieve it from the network
|
||||
* cache.
|
||||
* @param lineNumber (optional)
|
||||
* The line number to focus as soon as the source has finished
|
||||
* loading.
|
||||
*/
|
||||
viewSource(URL, outerWindowID, lineNumber) {
|
||||
let pageDescriptor, forcedCharSet;
|
||||
|
||||
if (outerWindowID) {
|
||||
let contentWindow = Services.wm.getOuterWindowWithId(outerWindowID);
|
||||
if (contentWindow) {
|
||||
let otherDocShell = contentWindow.docShell;
|
||||
|
||||
try {
|
||||
pageDescriptor = otherDocShell.QueryInterface(Ci.nsIWebPageDescriptor)
|
||||
.currentDescriptor;
|
||||
} catch (e) {
|
||||
// We couldn't get the page descriptor, so we'll probably end up re-retrieving
|
||||
// this document off of the network.
|
||||
}
|
||||
|
||||
let utils = contentWindow.windowUtils;
|
||||
let doc = contentWindow.document;
|
||||
forcedCharSet = utils.docCharsetIsForced ? doc.characterSet : null;
|
||||
}
|
||||
}
|
||||
|
||||
this.loadSource(URL, pageDescriptor, lineNumber, forcedCharSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a view source selection showing the given view-source url and
|
||||
* highlight the selection.
|
||||
*
|
||||
* @param uri view-source uri to show
|
||||
* @param drawSelection true to highlight the selection
|
||||
* @param baseURI base URI of the original document
|
||||
*/
|
||||
viewSourceWithSelection(uri, drawSelection, baseURI) {
|
||||
// This isn't ideal, but set a global in the view source page actor
|
||||
// that indicates that a selection should be drawn. It will be read
|
||||
// when by the page's pageshow listener. This should work as the
|
||||
// view source page is always loaded in the same process.
|
||||
ViewSourcePageChild.setNeedsDrawSelection(drawSelection);
|
||||
|
||||
// all our content is held by the data:URI and URIs are internally stored as utf-8 (see nsIURI.idl)
|
||||
let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
||||
let webNav = this.docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let loadURIOptions = {
|
||||
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
loadFlags,
|
||||
baseURI: Services.io.newURI(baseURI),
|
||||
};
|
||||
webNav.loadURI(uri, loadURIOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common utility function used by both the current and deprecated APIs
|
||||
* for loading source.
|
||||
*
|
||||
* @param URL (required)
|
||||
* The URL string of the source to be shown.
|
||||
* @param pageDescriptor (optional)
|
||||
* The currentDescriptor off of an nsIWebPageDescriptor, in the
|
||||
* event that the caller wants to try to load the source out of
|
||||
* the network cache.
|
||||
* @param lineNumber (optional)
|
||||
* The line number to focus as soon as the source has finished
|
||||
* loading.
|
||||
* @param forcedCharSet (optional)
|
||||
* The document character set to use instead of the default one.
|
||||
*/
|
||||
loadSource(URL, pageDescriptor, lineNumber, forcedCharSet) {
|
||||
const viewSrcURL = "view-source:" + URL;
|
||||
|
||||
if (forcedCharSet) {
|
||||
try {
|
||||
this.docShell.charset = forcedCharSet;
|
||||
} catch (e) {
|
||||
/* invalid charset */
|
||||
}
|
||||
}
|
||||
|
||||
ViewSourcePageChild.setInitialLineNumber(lineNumber);
|
||||
|
||||
if (!pageDescriptor) {
|
||||
this.loadSourceFromURL(viewSrcURL);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let pageLoader = this.docShell.QueryInterface(Ci.nsIWebPageDescriptor);
|
||||
pageLoader.loadPage(
|
||||
pageDescriptor,
|
||||
Ci.nsIWebPageDescriptor.DISPLAY_AS_SOURCE
|
||||
);
|
||||
} catch (e) {
|
||||
// We were not able to load the source from the network cache.
|
||||
this.loadSourceFromURL(viewSrcURL);
|
||||
return;
|
||||
}
|
||||
|
||||
let shEntrySource = pageDescriptor.QueryInterface(Ci.nsISHEntry);
|
||||
let shistory = this.docShell.QueryInterface(Ci.nsIWebNavigation)
|
||||
.sessionHistory.legacySHistory;
|
||||
let shEntry = shistory.createEntry();
|
||||
shEntry.URI = Services.io.newURI(viewSrcURL);
|
||||
shEntry.title = viewSrcURL;
|
||||
let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
shEntry.triggeringPrincipal = systemPrincipal;
|
||||
shEntry.setLoadTypeAsHistory();
|
||||
shEntry.cacheKey = shEntrySource.cacheKey;
|
||||
shistory.addEntry(shEntry, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load some URL in the browser.
|
||||
*
|
||||
* @param URL
|
||||
* The URL string to load.
|
||||
*/
|
||||
loadSourceFromURL(URL) {
|
||||
let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
||||
let webNav = this.docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let loadURIOptions = {
|
||||
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
loadFlags,
|
||||
};
|
||||
webNav.loadURI(URL, loadURIOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -213,7 +368,7 @@ class SelectionSourceChild extends ActorChild {
|
||||
tmpNode.appendChild(ancestorContainer);
|
||||
|
||||
return {
|
||||
uri:
|
||||
URL:
|
||||
(isHTML
|
||||
? "view-source:data:text/html;charset=utf-8,"
|
||||
: "view-source:data:application/xml;charset=utf-8,") +
|
@ -9,11 +9,7 @@ const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"DeferredTask",
|
||||
"resource://gre/modules/DeferredTask.jsm"
|
||||
);
|
||||
var EXPORTED_SYMBOLS = ["ViewSourcePageChild"];
|
||||
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, ["NodeFilter"]);
|
||||
|
||||
@ -28,280 +24,105 @@ const BUNDLE_URL = "chrome://global/locale/viewSource.properties";
|
||||
const MARK_SELECTION_START = "\uFDD0";
|
||||
const MARK_SELECTION_END = "\uFDEF";
|
||||
|
||||
var global = this;
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
let gNeedsDrawSelection = false;
|
||||
|
||||
/**
|
||||
* ViewSourceContent should be loaded in the <xul:browser> of the
|
||||
* view source window, and initialized as soon as it has loaded.
|
||||
* Start at a specific line number.
|
||||
*/
|
||||
var ViewSourceContent = {
|
||||
/**
|
||||
* These are the messages that ViewSourceContent is prepared to listen
|
||||
* for. If you need ViewSourceContent to handle more messages, add them
|
||||
* here.
|
||||
*/
|
||||
messages: [
|
||||
"ViewSource:LoadSource",
|
||||
"ViewSource:LoadSourceWithSelection",
|
||||
"ViewSource:GoToLine",
|
||||
],
|
||||
let gInitialLineNumber = -1;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
|
||||
get isViewSource() {
|
||||
let uri = content.document.documentURI;
|
||||
return uri.startsWith("view-source:");
|
||||
/**
|
||||
* In-page context menu items that are injected after page load.
|
||||
*/
|
||||
let gContextMenuItems = [
|
||||
{
|
||||
id: "goToLine",
|
||||
accesskey: true,
|
||||
handler(actor) {
|
||||
actor.sendAsyncMessage("ViewSource:PromptAndGoToLine");
|
||||
},
|
||||
},
|
||||
|
||||
get isAboutBlank() {
|
||||
let uri = content.document.documentURI;
|
||||
return uri == "about:blank";
|
||||
{
|
||||
id: "wrapLongLines",
|
||||
get checked() {
|
||||
return Services.prefs.getBoolPref("view_source.wrap_long_lines");
|
||||
},
|
||||
handler(actor) {
|
||||
actor.toggleWrapping();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "highlightSyntax",
|
||||
get checked() {
|
||||
return Services.prefs.getBoolPref("view_source.syntax_highlight");
|
||||
},
|
||||
handler(actor) {
|
||||
actor.toggleSyntaxHighlighting();
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* This should be called as soon as this frame script has loaded.
|
||||
*/
|
||||
init() {
|
||||
this.messages.forEach(msgName => {
|
||||
addMessageListener(msgName, this);
|
||||
class ViewSourcePageChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "bundle", function() {
|
||||
return Services.strings.createBundle(BUNDLE_URL);
|
||||
});
|
||||
}
|
||||
|
||||
addEventListener("pagehide", this, true);
|
||||
addEventListener("pageshow", this, true);
|
||||
addEventListener("click", this);
|
||||
addEventListener("unload", this);
|
||||
Services.els.addSystemEventListener(global, "contextmenu", this, false);
|
||||
},
|
||||
static setNeedsDrawSelection(value) {
|
||||
gNeedsDrawSelection = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be called when the frame script is being unloaded,
|
||||
* and the browser is tearing down.
|
||||
*/
|
||||
uninit() {
|
||||
this.messages.forEach(msgName => {
|
||||
removeMessageListener(msgName, this);
|
||||
});
|
||||
static setInitialLineNumber(value) {
|
||||
gInitialLineNumber = value;
|
||||
}
|
||||
|
||||
removeEventListener("pagehide", this, true);
|
||||
removeEventListener("pageshow", this, true);
|
||||
removeEventListener("click", this);
|
||||
removeEventListener("unload", this);
|
||||
|
||||
Services.els.removeSystemEventListener(global, "contextmenu", this, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Anything added to the messages array will get handled here, and should
|
||||
* get dispatched to a specific function for the message name.
|
||||
*/
|
||||
receiveMessage(msg) {
|
||||
if (!this.isViewSource && !this.isAboutBlank) {
|
||||
return;
|
||||
if (msg.name == "ViewSource:GoToLine") {
|
||||
this.goToLine(msg.data.lineNumber);
|
||||
}
|
||||
let data = msg.data;
|
||||
switch (msg.name) {
|
||||
case "ViewSource:LoadSource":
|
||||
this.viewSource(
|
||||
data.URL,
|
||||
data.outerWindowID,
|
||||
data.lineNumber,
|
||||
data.shouldWrap
|
||||
);
|
||||
break;
|
||||
case "ViewSource:LoadSourceWithSelection":
|
||||
this.viewSourceWithSelection(
|
||||
data.URL,
|
||||
data.drawSelection,
|
||||
data.baseURI
|
||||
);
|
||||
break;
|
||||
case "ViewSource:GoToLine":
|
||||
this.goToLine(data.lineNumber);
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Any events should get handled here, and should get dispatched to
|
||||
* a specific function for the event type.
|
||||
*/
|
||||
handleEvent(event) {
|
||||
if (!this.isViewSource) {
|
||||
return;
|
||||
}
|
||||
switch (event.type) {
|
||||
case "pagehide":
|
||||
this.onPageHide(event);
|
||||
break;
|
||||
case "pageshow":
|
||||
this.onPageShow(event);
|
||||
break;
|
||||
case "click":
|
||||
this.onClick(event);
|
||||
break;
|
||||
case "unload":
|
||||
this.uninit();
|
||||
break;
|
||||
case "contextmenu":
|
||||
this.onContextMenu(event);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A getter for the view source string bundle.
|
||||
*/
|
||||
get bundle() {
|
||||
delete this.bundle;
|
||||
this.bundle = Services.strings.createBundle(BUNDLE_URL);
|
||||
return this.bundle;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A shortcut to the nsISelectionController for the content.
|
||||
*/
|
||||
get selectionController() {
|
||||
return docShell
|
||||
return this.docShell
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsISelectionDisplay)
|
||||
.QueryInterface(Ci.nsISelectionController);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A shortcut to the nsIWebBrowserFind for the content.
|
||||
*/
|
||||
get webBrowserFind() {
|
||||
return docShell
|
||||
return this.docShell
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebBrowserFind);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the parent sends a message to view some source code.
|
||||
*
|
||||
* @param URL (required)
|
||||
* The URL string of the source to be shown.
|
||||
* @param outerWindowID (optional)
|
||||
* The outerWindowID of the content window that has hosted
|
||||
* the document, in case we want to retrieve it from the network
|
||||
* cache.
|
||||
* @param lineNumber (optional)
|
||||
* The line number to focus as soon as the source has finished
|
||||
* loading.
|
||||
*/
|
||||
viewSource(URL, outerWindowID, lineNumber) {
|
||||
let pageDescriptor, forcedCharSet;
|
||||
|
||||
if (outerWindowID) {
|
||||
let contentWindow = Services.wm.getOuterWindowWithId(outerWindowID);
|
||||
let otherDocShell = contentWindow.docShell;
|
||||
|
||||
try {
|
||||
pageDescriptor = otherDocShell.QueryInterface(Ci.nsIWebPageDescriptor)
|
||||
.currentDescriptor;
|
||||
} catch (e) {
|
||||
// We couldn't get the page descriptor, so we'll probably end up re-retrieving
|
||||
// this document off of the network.
|
||||
}
|
||||
|
||||
let utils = contentWindow.windowUtils;
|
||||
let doc = contentWindow.document;
|
||||
forcedCharSet = utils.docCharsetIsForced ? doc.characterSet : null;
|
||||
}
|
||||
|
||||
this.loadSource(URL, pageDescriptor, lineNumber, forcedCharSet);
|
||||
},
|
||||
|
||||
/**
|
||||
* Common utility function used by both the current and deprecated APIs
|
||||
* for loading source.
|
||||
*
|
||||
* @param URL (required)
|
||||
* The URL string of the source to be shown.
|
||||
* @param pageDescriptor (optional)
|
||||
* The currentDescriptor off of an nsIWebPageDescriptor, in the
|
||||
* event that the caller wants to try to load the source out of
|
||||
* the network cache.
|
||||
* @param lineNumber (optional)
|
||||
* The line number to focus as soon as the source has finished
|
||||
* loading.
|
||||
* @param forcedCharSet (optional)
|
||||
* The document character set to use instead of the default one.
|
||||
*/
|
||||
loadSource(URL, pageDescriptor, lineNumber, forcedCharSet) {
|
||||
const viewSrcURL = "view-source:" + URL;
|
||||
|
||||
if (forcedCharSet) {
|
||||
try {
|
||||
docShell.charset = forcedCharSet;
|
||||
} catch (e) {
|
||||
/* invalid charset */
|
||||
}
|
||||
}
|
||||
|
||||
if (lineNumber && lineNumber > 0) {
|
||||
let doneLoading = event => {
|
||||
// Ignore possible initial load of about:blank
|
||||
if (this.isAboutBlank || !content.document.body) {
|
||||
return;
|
||||
}
|
||||
this.goToLine(lineNumber);
|
||||
removeEventListener("pageshow", doneLoading);
|
||||
};
|
||||
|
||||
addEventListener("pageshow", doneLoading);
|
||||
}
|
||||
|
||||
if (!pageDescriptor) {
|
||||
this.loadSourceFromURL(viewSrcURL);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let pageLoader = docShell.QueryInterface(Ci.nsIWebPageDescriptor);
|
||||
pageLoader.loadPage(
|
||||
pageDescriptor,
|
||||
Ci.nsIWebPageDescriptor.DISPLAY_AS_SOURCE
|
||||
);
|
||||
} catch (e) {
|
||||
// We were not able to load the source from the network cache.
|
||||
this.loadSourceFromURL(viewSrcURL);
|
||||
return;
|
||||
}
|
||||
|
||||
let shEntrySource = pageDescriptor.QueryInterface(Ci.nsISHEntry);
|
||||
let shistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory
|
||||
.legacySHistory;
|
||||
let shEntry = shistory.createEntry();
|
||||
shEntry.URI = Services.io.newURI(viewSrcURL);
|
||||
shEntry.title = viewSrcURL;
|
||||
let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
shEntry.triggeringPrincipal = systemPrincipal;
|
||||
shEntry.setLoadTypeAsHistory();
|
||||
shEntry.cacheKey = shEntrySource.cacheKey;
|
||||
shistory.addEntry(shEntry, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load some URL in the browser.
|
||||
*
|
||||
* @param URL
|
||||
* The URL string to load.
|
||||
*/
|
||||
loadSourceFromURL(URL) {
|
||||
let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
||||
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let loadURIOptions = {
|
||||
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
loadFlags,
|
||||
};
|
||||
webNav.loadURI(URL, loadURIOptions);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* This handler is for click events from:
|
||||
@ -313,11 +134,11 @@ var ViewSourceContent = {
|
||||
let target = event.originalTarget;
|
||||
// Check for content menu actions
|
||||
if (target.id) {
|
||||
this.contextMenuItems.forEach(itemSpec => {
|
||||
gContextMenuItems.forEach(itemSpec => {
|
||||
if (itemSpec.id !== target.id) {
|
||||
return;
|
||||
}
|
||||
itemSpec.handler.call(this, event);
|
||||
itemSpec.handler(this);
|
||||
event.stopPropagation();
|
||||
});
|
||||
}
|
||||
@ -334,10 +155,10 @@ var ViewSourceContent = {
|
||||
|
||||
if (target == errorDoc.getElementById("goBackButton")) {
|
||||
// Instead of loading some safe page, just close the window
|
||||
sendAsyncMessage("ViewSource:Close");
|
||||
this.sendAsyncMessage("ViewSource:Close");
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the pageshow event.
|
||||
@ -346,57 +167,27 @@ var ViewSourceContent = {
|
||||
* The pageshow event being handled.
|
||||
*/
|
||||
onPageShow(event) {
|
||||
content.focus();
|
||||
this.contentWindow.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:")
|
||||
gNeedsDrawSelection &&
|
||||
this.document.documentURI.startsWith("view-source:")
|
||||
) {
|
||||
this.needsDrawSelection = false;
|
||||
gNeedsDrawSelection = false;
|
||||
this.drawSelection();
|
||||
}
|
||||
|
||||
if (content.document.body) {
|
||||
if (gInitialLineNumber >= 0) {
|
||||
this.goToLine(gInitialLineNumber);
|
||||
gInitialLineNumber = -1;
|
||||
}
|
||||
|
||||
if (this.document.body) {
|
||||
this.injectContextMenu();
|
||||
}
|
||||
|
||||
sendAsyncMessage("ViewSource:SourceLoaded");
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the pagehide event.
|
||||
*
|
||||
* @param event
|
||||
* The pagehide event being handled.
|
||||
*/
|
||||
onPageHide(event) {
|
||||
sendAsyncMessage("ViewSource:SourceUnloaded");
|
||||
},
|
||||
|
||||
onContextMenu(event) {
|
||||
let node = event.target;
|
||||
|
||||
let result = {
|
||||
isEmail: false,
|
||||
isLink: false,
|
||||
href: "",
|
||||
// We have to pass these in the event that we're running in
|
||||
// a remote browser, so that ViewSourceChrome knows where to
|
||||
// open the context menu.
|
||||
screenX: event.screenX,
|
||||
screenY: event.screenY,
|
||||
};
|
||||
|
||||
if (node && node.localName == "a") {
|
||||
result.isLink = node.href.startsWith("view-source:");
|
||||
result.isEmail = node.href.startsWith("mailto:");
|
||||
result.href = node.href.substring(node.href.indexOf(":") + 1);
|
||||
}
|
||||
|
||||
sendSyncMessage("ViewSource:ContextMenuOpening", result);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to go to a particular line in the source code being
|
||||
@ -409,7 +200,7 @@ var ViewSourceContent = {
|
||||
* The line number to attempt to go to.
|
||||
*/
|
||||
goToLine(lineNumber) {
|
||||
let body = content.document.body;
|
||||
let body = this.document.body;
|
||||
|
||||
// The source document is made up of a number of pre elements with
|
||||
// id attributes in the format <pre id="line123">, meaning that
|
||||
@ -439,11 +230,11 @@ var ViewSourceContent = {
|
||||
let found = this.findLocation(pre, lineNumber, null, -1, false, result);
|
||||
|
||||
if (!found) {
|
||||
sendAsyncMessage("ViewSource:GoToLine:Failed");
|
||||
this.sendAsyncMessage("ViewSource:GoToLine:Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
let selection = content.getSelection();
|
||||
let selection = this.document.defaultView.getSelection();
|
||||
selection.removeAllRanges();
|
||||
|
||||
// In our case, the range's startOffset is after "\n" on the previous line.
|
||||
@ -484,8 +275,8 @@ var ViewSourceContent = {
|
||||
true
|
||||
);
|
||||
|
||||
sendAsyncMessage("ViewSource:GoToLine:Success", { lineNumber });
|
||||
},
|
||||
this.sendAsyncMessage("ViewSource:GoToLine:Success", { lineNumber });
|
||||
}
|
||||
|
||||
/**
|
||||
* Some old code from the original view source implementation. Original
|
||||
@ -514,7 +305,7 @@ var ViewSourceContent = {
|
||||
let curLine = pre.id ? parseInt(pre.id.substring(4)) : 1;
|
||||
|
||||
// Walk through each of the text nodes and count newlines.
|
||||
let treewalker = content.document.createTreeWalker(
|
||||
let treewalker = this.document.createTreeWalker(
|
||||
pre,
|
||||
NodeFilter.SHOW_TEXT,
|
||||
null
|
||||
@ -573,7 +364,7 @@ var ViewSourceContent = {
|
||||
break;
|
||||
}
|
||||
} else if (curLine == lineNumber && !("range" in result)) {
|
||||
result.range = content.document.createRange();
|
||||
result.range = this.document.createRange();
|
||||
result.range.setStart(textNode, curPos);
|
||||
|
||||
// This will always be overridden later, except when we look for
|
||||
@ -589,17 +380,17 @@ var ViewSourceContent = {
|
||||
}
|
||||
|
||||
return found || "range" in result;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the "wrap" class on the document body, which sets whether
|
||||
* or not long lines are wrapped. Notifies parent to update the pref.
|
||||
*/
|
||||
toggleWrapping() {
|
||||
let body = content.document.body;
|
||||
let body = this.document.body;
|
||||
let state = body.classList.toggle("wrap");
|
||||
sendAsyncMessage("ViewSource:StoreWrapping", { state });
|
||||
},
|
||||
this.sendAsyncMessage("ViewSource:StoreWrapping", { state });
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the "highlight" class on the document body, which sets whether
|
||||
@ -607,32 +398,10 @@ var ViewSourceContent = {
|
||||
* pref.
|
||||
*/
|
||||
toggleSyntaxHighlighting() {
|
||||
let body = content.document.body;
|
||||
let body = this.document.body;
|
||||
let state = body.classList.toggle("highlight");
|
||||
sendAsyncMessage("ViewSource:StoreSyntaxHighlighting", { state });
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads a view source selection showing the given view-source url and
|
||||
* highlight the selection.
|
||||
*
|
||||
* @param uri view-source uri to show
|
||||
* @param drawSelection true to highlight the selection
|
||||
* @param baseURI base URI of the original document
|
||||
*/
|
||||
viewSourceWithSelection(uri, drawSelection, baseURI) {
|
||||
this.needsDrawSelection = drawSelection;
|
||||
|
||||
// all our content is held by the data:URI and URIs are internally stored as utf-8 (see nsIURI.idl)
|
||||
let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
||||
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let loadURIOptions = {
|
||||
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
loadFlags,
|
||||
baseURI: Services.io.newURI(baseURI),
|
||||
};
|
||||
webNav.loadURI(uri, loadURIOptions);
|
||||
},
|
||||
this.sendAsyncMessage("ViewSource:StoreSyntaxHighlighting", { state });
|
||||
}
|
||||
|
||||
/**
|
||||
* Using special markers left in the serialized source, this helper makes the
|
||||
@ -640,7 +409,7 @@ var ViewSourceContent = {
|
||||
* selected on the inflated view-source DOM.
|
||||
*/
|
||||
drawSelection() {
|
||||
content.document.title = this.bundle.GetStringFromName(
|
||||
this.document.title = this.bundle.GetStringFromName(
|
||||
"viewSelectionSourceTitle"
|
||||
);
|
||||
|
||||
@ -677,7 +446,7 @@ var ViewSourceContent = {
|
||||
var startLength = MARK_SELECTION_START.length;
|
||||
findInst.findNext();
|
||||
|
||||
var selection = content.getSelection();
|
||||
var selection = this.document.defaultView.getSelection();
|
||||
if (!selection.rangeCount) {
|
||||
return;
|
||||
}
|
||||
@ -732,44 +501,13 @@ var ViewSourceContent = {
|
||||
findInst.wrapFind = wrapFind;
|
||||
findInst.findBackwards = findBackwards;
|
||||
findInst.searchString = searchString;
|
||||
},
|
||||
|
||||
/**
|
||||
* In-page context menu items that are injected after page load.
|
||||
*/
|
||||
contextMenuItems: [
|
||||
{
|
||||
id: "goToLine",
|
||||
accesskey: true,
|
||||
handler() {
|
||||
sendAsyncMessage("ViewSource:PromptAndGoToLine");
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "wrapLongLines",
|
||||
get checked() {
|
||||
return Services.prefs.getBoolPref("view_source.wrap_long_lines");
|
||||
},
|
||||
handler() {
|
||||
this.toggleWrapping();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "highlightSyntax",
|
||||
get checked() {
|
||||
return Services.prefs.getBoolPref("view_source.syntax_highlight");
|
||||
},
|
||||
handler() {
|
||||
this.toggleSyntaxHighlighting();
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* Add context menu items for view source specific actions.
|
||||
*/
|
||||
injectContextMenu() {
|
||||
let doc = content.document;
|
||||
let doc = this.document;
|
||||
|
||||
let menu = doc.createElementNS(NS_XHTML, "menu");
|
||||
menu.setAttribute("type", "context");
|
||||
@ -777,7 +515,7 @@ var ViewSourceContent = {
|
||||
doc.body.appendChild(menu);
|
||||
doc.body.setAttribute("contextmenu", "actions");
|
||||
|
||||
this.contextMenuItems.forEach(itemSpec => {
|
||||
gContextMenuItems.forEach(itemSpec => {
|
||||
let item = doc.createElementNS(NS_XHTML, "menuitem");
|
||||
item.setAttribute("id", itemSpec.id);
|
||||
let labelName = `context_${itemSpec.id}_label`;
|
||||
@ -797,14 +535,14 @@ var ViewSourceContent = {
|
||||
});
|
||||
|
||||
this.updateContextMenu();
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Update state of checkbox-style context menu items.
|
||||
*/
|
||||
updateContextMenu() {
|
||||
let doc = content.document;
|
||||
this.contextMenuItems.forEach(itemSpec => {
|
||||
let doc = this.document;
|
||||
gContextMenuItems.forEach(itemSpec => {
|
||||
if (!("checked" in itemSpec)) {
|
||||
return;
|
||||
}
|
||||
@ -815,6 +553,5 @@ var ViewSourceContent = {
|
||||
item.removeAttribute("checked");
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
ViewSourceContent.init();
|
||||
}
|
||||
}
|
159
toolkit/actors/ViewSourcePageParent.jsm
Normal file
159
toolkit/actors/ViewSourcePageParent.jsm
Normal file
@ -0,0 +1,159 @@
|
||||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"Services",
|
||||
"resource://gre/modules/Services.jsm"
|
||||
);
|
||||
|
||||
const BUNDLE_URL = "chrome://global/locale/viewSource.properties";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["ViewSourcePageParent"];
|
||||
|
||||
/**
|
||||
* ViewSourcePageParent manages the view source <browser> from the chrome side.
|
||||
*/
|
||||
class ViewSourcePageParent extends JSWindowActorParent {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
/**
|
||||
* Holds the value of the last line found via the "Go to line"
|
||||
* command, to pre-populate the prompt the next time it is
|
||||
* opened.
|
||||
*/
|
||||
this.lastLineFound = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Anything added to the messages array will get handled here, and should
|
||||
* get dispatched to a specific function for the message name.
|
||||
*/
|
||||
receiveMessage(message) {
|
||||
let data = message.data;
|
||||
|
||||
switch (message.name) {
|
||||
case "ViewSource:PromptAndGoToLine":
|
||||
this.promptAndGoToLine();
|
||||
break;
|
||||
case "ViewSource:GoToLine:Success":
|
||||
this.onGoToLineSuccess(data.lineNumber);
|
||||
break;
|
||||
case "ViewSource:GoToLine:Failed":
|
||||
this.onGoToLineFailed();
|
||||
break;
|
||||
case "ViewSource:StoreWrapping":
|
||||
this.storeWrapping(data.state);
|
||||
break;
|
||||
case "ViewSource:StoreSyntaxHighlighting":
|
||||
this.storeSyntaxHighlighting(data.state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A getter for the view source string bundle.
|
||||
*/
|
||||
get bundle() {
|
||||
if (this._bundle) {
|
||||
return this._bundle;
|
||||
}
|
||||
return (this._bundle = Services.strings.createBundle(BUNDLE_URL));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the "Go to line" prompt for a user to hop to a particular line
|
||||
* of the source code they're viewing. This will keep prompting until the
|
||||
* user either cancels out of the prompt, or enters a valid line number.
|
||||
*/
|
||||
promptAndGoToLine() {
|
||||
let input = { value: this.lastLineFound };
|
||||
let window = Services.wm.getMostRecentWindow(null);
|
||||
|
||||
let ok = Services.prompt.prompt(
|
||||
window,
|
||||
this.bundle.GetStringFromName("goToLineTitle"),
|
||||
this.bundle.GetStringFromName("goToLineText"),
|
||||
input,
|
||||
null,
|
||||
{ value: 0 }
|
||||
);
|
||||
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
let line = parseInt(input.value, 10);
|
||||
|
||||
if (!(line > 0)) {
|
||||
Services.prompt.alert(
|
||||
window,
|
||||
this.bundle.GetStringFromName("invalidInputTitle"),
|
||||
this.bundle.GetStringFromName("invalidInputText")
|
||||
);
|
||||
this.promptAndGoToLine();
|
||||
} else {
|
||||
this.goToLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to a particular line of the source code. This act is asynchronous.
|
||||
*
|
||||
* @param lineNumber
|
||||
* The line number to try to go to to.
|
||||
*/
|
||||
goToLine(lineNumber) {
|
||||
this.sendAsyncMessage("ViewSource:GoToLine", { lineNumber });
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the frame script reports that a line was successfully gotten
|
||||
* to.
|
||||
*
|
||||
* @param lineNumber
|
||||
* The line number that we successfully got to.
|
||||
*/
|
||||
onGoToLineSuccess(lineNumber) {
|
||||
// We'll pre-populate the "Go to line" prompt with this value the next
|
||||
// time it comes up.
|
||||
this.lastLineFound = lineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the child reports that we failed to go to a particular
|
||||
* line. This informs the user that their selection was likely out of range,
|
||||
* and then reprompts the user to try again.
|
||||
*/
|
||||
onGoToLineFailed() {
|
||||
let window = Services.wm.getMostRecentWindow(null);
|
||||
Services.prompt.alert(
|
||||
window,
|
||||
this.bundle.GetStringFromName("outOfRangeTitle"),
|
||||
this.bundle.GetStringFromName("outOfRangeText")
|
||||
);
|
||||
this.promptAndGoToLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the wrapping pref based on the child's current state.
|
||||
* @param state
|
||||
* Whether wrapping is currently enabled in the child.
|
||||
*/
|
||||
storeWrapping(state) {
|
||||
Services.prefs.setBoolPref("view_source.wrap_long_lines", state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the syntax highlighting pref based on the child's current state.
|
||||
* @param state
|
||||
* Whether syntax highlighting is currently enabled in the child.
|
||||
*/
|
||||
storeSyntaxHighlighting(state) {
|
||||
Services.prefs.setBoolPref("view_source.syntax_highlight", state);
|
||||
}
|
||||
}
|
@ -48,11 +48,13 @@ FINAL_TARGET_FILES.actors += [
|
||||
'PrintingChild.jsm',
|
||||
'PurgeSessionHistoryChild.jsm',
|
||||
'SelectChild.jsm',
|
||||
'SelectionSourceChild.jsm',
|
||||
'SelectParent.jsm',
|
||||
'ThumbnailsChild.jsm',
|
||||
'UAWidgetsChild.jsm',
|
||||
'UnselectedTabHoverChild.jsm',
|
||||
'ViewSourceChild.jsm',
|
||||
'ViewSourcePageChild.jsm',
|
||||
'ViewSourcePageParent.jsm',
|
||||
'WebChannelChild.jsm',
|
||||
'WebChannelParent.jsm',
|
||||
'WebNavigationChild.jsm',
|
||||
|
@ -1,335 +0,0 @@
|
||||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"Services",
|
||||
"resource://gre/modules/Services.jsm"
|
||||
);
|
||||
|
||||
const BUNDLE_URL = "chrome://global/locale/viewSource.properties";
|
||||
|
||||
const FRAME_SCRIPT = "chrome://global/content/viewSource-content.js";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["ViewSourceBrowser"];
|
||||
|
||||
// Keep a set of browsers we've seen before, so we can load our frame script as
|
||||
// needed into any new ones.
|
||||
var gKnownBrowsers = new WeakSet();
|
||||
|
||||
/**
|
||||
* ViewSourceBrowser manages the view source <browser> from the chrome side.
|
||||
* It's companion frame script, viewSource-content.js, needs to be loaded as a
|
||||
* frame script into the browser being managed.
|
||||
*
|
||||
* For a view source tab (or some other non-window case), an instance of this is
|
||||
* created by viewSourceUtils.js to wrap the <browser>. The frame script will
|
||||
* be loaded by this module at construction time.
|
||||
*/
|
||||
function ViewSourceBrowser(aBrowser) {
|
||||
this._browser = aBrowser;
|
||||
this.init();
|
||||
}
|
||||
|
||||
ViewSourceBrowser.prototype = {
|
||||
/**
|
||||
* The <browser> that will be displaying the view source content.
|
||||
*/
|
||||
get browser() {
|
||||
return this._browser;
|
||||
},
|
||||
|
||||
/**
|
||||
* Holds the value of the last line found via the "Go to line"
|
||||
* command, to pre-populate the prompt the next time it is
|
||||
* opened.
|
||||
*/
|
||||
lastLineFound: null,
|
||||
|
||||
/**
|
||||
* These are the messages that ViewSourceBrowser will listen for
|
||||
* from the frame script it injects. Any message names added here
|
||||
* will automatically have ViewSourceBrowser listen for those messages,
|
||||
* and remove the listeners on teardown.
|
||||
*/
|
||||
messages: [
|
||||
"ViewSource:PromptAndGoToLine",
|
||||
"ViewSource:GoToLine:Success",
|
||||
"ViewSource:GoToLine:Failed",
|
||||
"ViewSource:StoreWrapping",
|
||||
"ViewSource:StoreSyntaxHighlighting",
|
||||
],
|
||||
|
||||
/**
|
||||
* This should be called as soon as the script loads. When this function
|
||||
* executes, we can assume the DOM content has not yet loaded.
|
||||
*/
|
||||
init() {
|
||||
this.messages.forEach(msgName => {
|
||||
this.mm.addMessageListener(msgName, this);
|
||||
});
|
||||
|
||||
this.loadFrameScript();
|
||||
},
|
||||
|
||||
/**
|
||||
* This should be called when the window is closing. This function should
|
||||
* clean up event and message listeners.
|
||||
*/
|
||||
uninit() {
|
||||
this.messages.forEach(msgName => {
|
||||
this.mm.removeMessageListener(msgName, this);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* For a new browser we've not seen before, load the frame script.
|
||||
*/
|
||||
loadFrameScript() {
|
||||
// Check for a browser first. There won't be one for the window case
|
||||
// (still used by other applications like Thunderbird), as the element
|
||||
// does not exist until the XUL document loads.
|
||||
if (!this.browser) {
|
||||
return;
|
||||
}
|
||||
if (!gKnownBrowsers.has(this.browser)) {
|
||||
gKnownBrowsers.add(this.browser);
|
||||
this.mm.loadFrameScript(FRAME_SCRIPT, false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Anything added to the messages array will get handled here, and should
|
||||
* get dispatched to a specific function for the message name.
|
||||
*/
|
||||
receiveMessage(message) {
|
||||
let data = message.data;
|
||||
|
||||
switch (message.name) {
|
||||
case "ViewSource:PromptAndGoToLine":
|
||||
this.promptAndGoToLine();
|
||||
break;
|
||||
case "ViewSource:GoToLine:Success":
|
||||
this.onGoToLineSuccess(data.lineNumber);
|
||||
break;
|
||||
case "ViewSource:GoToLine:Failed":
|
||||
this.onGoToLineFailed();
|
||||
break;
|
||||
case "ViewSource:StoreWrapping":
|
||||
this.storeWrapping(data.state);
|
||||
break;
|
||||
case "ViewSource:StoreSyntaxHighlighting":
|
||||
this.storeSyntaxHighlighting(data.state);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the message manager of the view source browser.
|
||||
*/
|
||||
get mm() {
|
||||
return this.browser.messageManager;
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a message to the view source browser.
|
||||
*/
|
||||
sendAsyncMessage(...args) {
|
||||
this.browser.messageManager.sendAsyncMessage(...args);
|
||||
},
|
||||
|
||||
/**
|
||||
* A getter for the view source string bundle.
|
||||
*/
|
||||
get bundle() {
|
||||
if (this._bundle) {
|
||||
return this._bundle;
|
||||
}
|
||||
return (this._bundle = Services.strings.createBundle(BUNDLE_URL));
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads the source for a URL while applying some optional features if
|
||||
* enabled.
|
||||
*
|
||||
* For view source in a specific browser, this is manually called after
|
||||
* this object is constructed.
|
||||
*
|
||||
* This takes a single object argument containing:
|
||||
*
|
||||
* URL (required):
|
||||
* A string URL for the page we'd like to view the source of.
|
||||
* browser:
|
||||
* The browser containing the document that we would like to view the
|
||||
* source of. This argument is optional if outerWindowID is not passed.
|
||||
* outerWindowID (optional):
|
||||
* The outerWindowID of the content window containing the document that
|
||||
* we want to view the source of. This is the only way of attempting to
|
||||
* load the source out of the network cache.
|
||||
* lineNumber (optional):
|
||||
* The line number to focus on once the source is loaded.
|
||||
*/
|
||||
loadViewSource({ URL, browser, outerWindowID, lineNumber }) {
|
||||
if (!URL) {
|
||||
throw new Error("Must supply a URL when opening view source.");
|
||||
}
|
||||
|
||||
if (browser) {
|
||||
this.browser.sameProcessAsFrameLoader = browser.frameLoader;
|
||||
|
||||
// If we're dealing with a remote browser, then the browser
|
||||
// for view source needs to be remote as well.
|
||||
this.updateBrowserRemoteness(browser.remoteType);
|
||||
} else if (outerWindowID) {
|
||||
throw new Error("Must supply the browser if passing the outerWindowID");
|
||||
}
|
||||
|
||||
this.sendAsyncMessage("ViewSource:LoadSource", {
|
||||
URL,
|
||||
outerWindowID,
|
||||
lineNumber,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads a view source selection showing the given view-source url and
|
||||
* highlight the selection.
|
||||
*
|
||||
* @param uri view-source uri to show
|
||||
* @param drawSelection true to highlight the selection
|
||||
* @param baseURI base URI of the original document
|
||||
*/
|
||||
loadViewSourceFromSelection(URL, drawSelection, baseURI) {
|
||||
this.sendAsyncMessage("ViewSource:LoadSourceWithSelection", {
|
||||
URL,
|
||||
drawSelection,
|
||||
baseURI,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the "remote" attribute of the view source browser. This
|
||||
* will remove the browser from the DOM, and then re-add it in the
|
||||
* same place it was taken from.
|
||||
*
|
||||
* @param shouldBeRemote
|
||||
* True if the browser should be made remote. If the browsers
|
||||
* remoteness already matches this value, this function does
|
||||
* nothing.
|
||||
* @param remoteType
|
||||
* The type of remote browser process.
|
||||
*/
|
||||
updateBrowserRemoteness(remoteType) {
|
||||
if (this.browser.remoteType != remoteType) {
|
||||
// In this base case, where we are handed a <browser> someone else is
|
||||
// managing, we don't know for sure that it's safe to toggle remoteness.
|
||||
// For view source in a window, this is overridden to actually do the
|
||||
// flip if needed.
|
||||
throw new Error("View source browser's remoteness mismatch");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens the "Go to line" prompt for a user to hop to a particular line
|
||||
* of the source code they're viewing. This will keep prompting until the
|
||||
* user either cancels out of the prompt, or enters a valid line number.
|
||||
*/
|
||||
promptAndGoToLine() {
|
||||
let input = { value: this.lastLineFound };
|
||||
let window = Services.wm.getMostRecentWindow(null);
|
||||
|
||||
let ok = Services.prompt.prompt(
|
||||
window,
|
||||
this.bundle.GetStringFromName("goToLineTitle"),
|
||||
this.bundle.GetStringFromName("goToLineText"),
|
||||
input,
|
||||
null,
|
||||
{ value: 0 }
|
||||
);
|
||||
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
let line = parseInt(input.value, 10);
|
||||
|
||||
if (!(line > 0)) {
|
||||
Services.prompt.alert(
|
||||
window,
|
||||
this.bundle.GetStringFromName("invalidInputTitle"),
|
||||
this.bundle.GetStringFromName("invalidInputText")
|
||||
);
|
||||
this.promptAndGoToLine();
|
||||
} else {
|
||||
this.goToLine(line);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Go to a particular line of the source code. This act is asynchronous.
|
||||
*
|
||||
* @param lineNumber
|
||||
* The line number to try to go to to.
|
||||
*/
|
||||
goToLine(lineNumber) {
|
||||
this.sendAsyncMessage("ViewSource:GoToLine", { lineNumber });
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the frame script reports that a line was successfully gotten
|
||||
* to.
|
||||
*
|
||||
* @param lineNumber
|
||||
* The line number that we successfully got to.
|
||||
*/
|
||||
onGoToLineSuccess(lineNumber) {
|
||||
// We'll pre-populate the "Go to line" prompt with this value the next
|
||||
// time it comes up.
|
||||
this.lastLineFound = lineNumber;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the frame script reports that we failed to go to a particular
|
||||
* line. This informs the user that their selection was likely out of range,
|
||||
* and then reprompts the user to try again.
|
||||
*/
|
||||
onGoToLineFailed() {
|
||||
let window = Services.wm.getMostRecentWindow(null);
|
||||
Services.prompt.alert(
|
||||
window,
|
||||
this.bundle.GetStringFromName("outOfRangeTitle"),
|
||||
this.bundle.GetStringFromName("outOfRangeText")
|
||||
);
|
||||
this.promptAndGoToLine();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the wrapping pref based on the child's current state.
|
||||
* @param state
|
||||
* Whether wrapping is currently enabled in the child.
|
||||
*/
|
||||
storeWrapping(state) {
|
||||
Services.prefs.setBoolPref("view_source.wrap_long_lines", state);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the syntax highlighting pref based on the child's current state.
|
||||
* @param state
|
||||
* Whether syntax highlighting is currently enabled in the child.
|
||||
*/
|
||||
storeSyntaxHighlighting(state) {
|
||||
Services.prefs.setBoolPref("view_source.syntax_highlight", state);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to decide if a URI maps to view source content.
|
||||
* @param uri
|
||||
* String containing the URI
|
||||
*/
|
||||
ViewSourceBrowser.isViewSource = function(uri) {
|
||||
return uri.startsWith("view-source:");
|
||||
};
|
@ -14,11 +14,6 @@
|
||||
|
||||
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"ViewSourceBrowser",
|
||||
"resource://gre/modules/ViewSourceBrowser.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"PrivateBrowsingUtils",
|
||||
@ -30,6 +25,11 @@ var gViewSourceUtils = {
|
||||
mnsIWebProgress: Ci.nsIWebProgress,
|
||||
mnsIWebPageDescriptor: Ci.nsIWebPageDescriptor,
|
||||
|
||||
// Get the ViewSource actor for a browsing context.
|
||||
getViewSourceActor(aBrowsingContext) {
|
||||
return aBrowsingContext.currentWindowGlobal.getActor("ViewSource");
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens the view source window.
|
||||
*
|
||||
@ -106,9 +106,41 @@ var gViewSourceUtils = {
|
||||
* lineNumber (optional):
|
||||
* The line number to focus on once the source is loaded.
|
||||
*/
|
||||
viewSourceInBrowser(aArgs) {
|
||||
let viewSourceBrowser = new ViewSourceBrowser(aArgs.viewSourceBrowser);
|
||||
viewSourceBrowser.loadViewSource(aArgs);
|
||||
viewSourceInBrowser({
|
||||
URL,
|
||||
viewSourceBrowser,
|
||||
browser,
|
||||
outerWindowID,
|
||||
lineNumber,
|
||||
}) {
|
||||
if (!URL) {
|
||||
throw new Error("Must supply a URL when opening view source.");
|
||||
}
|
||||
|
||||
if (browser) {
|
||||
viewSourceBrowser.sameProcessAsFrameLoader = browser.frameLoader;
|
||||
|
||||
// If we're dealing with a remote browser, then the browser
|
||||
// for view source needs to be remote as well.
|
||||
if (viewSourceBrowser.remoteType != browser.remoteType) {
|
||||
// In this base case, where we are handed a <browser> someone else is
|
||||
// managing, we don't know for sure that it's safe to toggle remoteness.
|
||||
// For view source in a window, this is overridden to actually do the
|
||||
// flip if needed.
|
||||
throw new Error("View source browser's remoteness mismatch");
|
||||
}
|
||||
} else if (outerWindowID) {
|
||||
throw new Error("Must supply the browser if passing the outerWindowID");
|
||||
}
|
||||
|
||||
let viewSourceActor = this.getViewSourceActor(
|
||||
viewSourceBrowser.browsingContext
|
||||
);
|
||||
viewSourceActor.sendAsyncMessage("ViewSource:LoadSource", {
|
||||
URL,
|
||||
outerWindowID,
|
||||
lineNumber,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -116,31 +148,21 @@ var gViewSourceUtils = {
|
||||
* <browser>. This allows for non-window display methods, such as a tab from
|
||||
* Firefox.
|
||||
*
|
||||
* @param aViewSourceInBrowser
|
||||
* The browser containing the page to view the source of.
|
||||
* @param aBrowsingContext:
|
||||
* The child browsing context containing the document to view the source of.
|
||||
* @param aGetBrowserFn
|
||||
* A function that will return a browser to open the source in.
|
||||
*/
|
||||
viewPartialSourceInBrowser(aViewSourceInBrowser, aGetBrowserFn) {
|
||||
let mm = aViewSourceInBrowser.messageManager;
|
||||
mm.addMessageListener("ViewSource:GetSelectionDone", function gotSelection(
|
||||
message
|
||||
) {
|
||||
mm.removeMessageListener("ViewSource:GetSelectionDone", gotSelection);
|
||||
async viewPartialSourceInBrowser(aBrowsingContext, aGetBrowserFn) {
|
||||
let sourceActor = this.getViewSourceActor(aBrowsingContext);
|
||||
if (sourceActor) {
|
||||
let data = await sourceActor.sendQuery("ViewSource:GetSelection", {});
|
||||
|
||||
if (!message.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
let viewSourceBrowser = new ViewSourceBrowser(aGetBrowserFn());
|
||||
viewSourceBrowser.loadViewSourceFromSelection(
|
||||
message.data.uri,
|
||||
message.data.drawSelection,
|
||||
message.data.baseURI
|
||||
let targetActor = this.getViewSourceActor(
|
||||
aGetBrowserFn().browsingContext
|
||||
);
|
||||
});
|
||||
|
||||
mm.sendAsyncMessage("ViewSource:GetSelection");
|
||||
targetActor.sendAsyncMessage("ViewSource:LoadSourceWithSelection", data);
|
||||
}
|
||||
},
|
||||
|
||||
buildEditorArgs(aPath, aLineNumber) {
|
||||
|
@ -4,4 +4,3 @@
|
||||
|
||||
toolkit.jar:
|
||||
content/global/viewSourceUtils.js (content/viewSourceUtils.js)
|
||||
content/global/viewSource-content.js (content/viewSource-content.js)
|
||||
|
@ -9,9 +9,5 @@ MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'ViewSourceBrowser.jsm',
|
||||
]
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Toolkit', 'View Source')
|
||||
|
@ -9,5 +9,6 @@ support-files = head.js
|
||||
skip-if = (os == "win" && processor == "aarch64") # disabled on aarch64 due to 1531590
|
||||
[browser_gotoline.js]
|
||||
[browser_open_docgroup.js]
|
||||
[browser_partialsource.js]
|
||||
[browser_srcdoc.js]
|
||||
[browser_viewsourceprefs.js]
|
||||
|
@ -24,9 +24,13 @@ var checkViewSource = async function(aTab) {
|
||||
});
|
||||
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
browser.messageManager.sendAsyncMessage("ViewSource:GoToLine", {
|
||||
lineNumber: i,
|
||||
});
|
||||
browser.sendMessageToActor(
|
||||
"ViewSource:GoToLine",
|
||||
{
|
||||
lineNumber: i,
|
||||
},
|
||||
"ViewSourcePage"
|
||||
);
|
||||
await SpecialPowers.spawn(browser, [i], async function(i) {
|
||||
let selection = content.getSelection();
|
||||
Assert.equal(selection.toString(), "line " + i, "Correct text selected");
|
||||
|
@ -0,0 +1,46 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
const frameSource =
|
||||
"<a href='about:mozilla'>some text</a><a id='other' href='about:about'>other text</a>";
|
||||
const sources = [
|
||||
`<html><iframe id="f" srcdoc="${frameSource}"></iframe></html>`,
|
||||
`<html><iframe id="f" src="https://example.com/document-builder.sjs?html=${frameSource}"></iframe></html>`,
|
||||
];
|
||||
|
||||
add_task(async function partial_source() {
|
||||
for (let source of sources) {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(
|
||||
gBrowser,
|
||||
"data:text/html," + source
|
||||
);
|
||||
|
||||
let frameBC = gBrowser.selectedBrowser.browsingContext.getChildren()[0];
|
||||
|
||||
await SpecialPowers.spawn(frameBC, [], () => {
|
||||
let element = content.document.getElementById("other");
|
||||
content.focus();
|
||||
content.getSelection().selectAllChildren(element);
|
||||
});
|
||||
|
||||
let sourceTab = await openViewPartialSource("#other", frameBC);
|
||||
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
let textContent = await SpecialPowers.spawn(browser, [], async function() {
|
||||
return content.document.body.textContent;
|
||||
});
|
||||
is(
|
||||
textContent,
|
||||
'<a id="other" href="about:about">other text</a>',
|
||||
"Correct content loaded"
|
||||
);
|
||||
let selection = await SpecialPowers.spawn(browser, [], async function() {
|
||||
return String(content.getSelection());
|
||||
});
|
||||
is(selection, "other text", "Correct text selected");
|
||||
|
||||
gBrowser.removeTab(sourceTab);
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
});
|
@ -87,9 +87,13 @@ async function openViewSource() {
|
||||
* @param aCSSSelector - used to specify a node within the selection to
|
||||
* view the source of. It is expected that this node is
|
||||
* within an existing selection.
|
||||
* @param aBrowsingContext - browsing context containing a subframe (optional).
|
||||
* @returns the new tab which shows the source.
|
||||
*/
|
||||
async function openViewPartialSource(aCSSSelector) {
|
||||
async function openViewPartialSource(
|
||||
aCSSSelector,
|
||||
aBrowsingContext = gBrowser.selectedBrowser
|
||||
) {
|
||||
let contentAreaContextMenuPopup = document.getElementById(
|
||||
"contentAreaContextMenu"
|
||||
);
|
||||
@ -100,7 +104,7 @@ async function openViewPartialSource(aCSSSelector) {
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
aCSSSelector,
|
||||
{ type: "contextmenu", button: 2 },
|
||||
gBrowser.selectedBrowser
|
||||
aBrowsingContext
|
||||
);
|
||||
await popupShownPromise;
|
||||
|
||||
@ -161,13 +165,12 @@ async function openViewFrameSourceTab(aCSSSelector) {
|
||||
* complete.
|
||||
*/
|
||||
function waitForSourceLoaded(tab) {
|
||||
return new Promise(resolve => {
|
||||
let mm = tab.linkedBrowser.messageManager;
|
||||
mm.addMessageListener("ViewSource:SourceLoaded", function sourceLoaded() {
|
||||
mm.removeMessageListener("ViewSource:SourceLoaded", sourceLoaded);
|
||||
setTimeout(resolve, 0);
|
||||
});
|
||||
});
|
||||
return BrowserTestUtils.waitForContentEvent(
|
||||
tab.linkedBrowser,
|
||||
"pageshow",
|
||||
false,
|
||||
event => String(event.target.location).startsWith("view-source")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -300,6 +300,35 @@ let ACTORS = {
|
||||
allFrames: true,
|
||||
},
|
||||
|
||||
// This actor is available for all pages that one can
|
||||
// view the source of, however it won't be created until a
|
||||
// request to view the source is made via the message
|
||||
// 'ViewSource:LoadSource' or 'ViewSource:LoadSourceWithSelection'.
|
||||
ViewSource: {
|
||||
child: {
|
||||
moduleURI: "resource://gre/actors/ViewSourceChild.jsm",
|
||||
},
|
||||
|
||||
allFrames: true,
|
||||
},
|
||||
|
||||
// This actor is for the view-source page itself.
|
||||
ViewSourcePage: {
|
||||
parent: {
|
||||
moduleURI: "resource://gre/actors/ViewSourcePageParent.jsm",
|
||||
},
|
||||
child: {
|
||||
moduleURI: "resource://gre/actors/ViewSourcePageChild.jsm",
|
||||
events: {
|
||||
pageshow: { capture: true },
|
||||
click: {},
|
||||
},
|
||||
},
|
||||
|
||||
matches: ["view-source:*"],
|
||||
allFrames: true,
|
||||
},
|
||||
|
||||
WebChannel: {
|
||||
parent: {
|
||||
moduleURI: "resource://gre/actors/WebChannelParent.jsm",
|
||||
@ -485,13 +514,6 @@ let LEGACY_ACTORS = {
|
||||
},
|
||||
},
|
||||
|
||||
SelectionSource: {
|
||||
child: {
|
||||
module: "resource://gre/actors/SelectionSourceChild.jsm",
|
||||
messages: ["ViewSource:GetSelection"],
|
||||
},
|
||||
},
|
||||
|
||||
UnselectedTabHover: {
|
||||
child: {
|
||||
module: "resource://gre/actors/UnselectedTabHoverChild.jsm",
|
||||
|
Loading…
x
Reference in New Issue
Block a user