mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge f-t to m-c, a=merge
This commit is contained in:
commit
c94954680c
@ -198,7 +198,7 @@ pref("app.update.url", "https://aus4.mozilla.org/update/3/%PRODUCT%/%VERSION%/%B
|
||||
pref("app.update.idletime", 60);
|
||||
|
||||
// Whether or not we show a dialog box informing the user that the update was
|
||||
// successfully applied. This is off in Firefox by default since we show a
|
||||
// successfully applied. This is off in Firefox by default since we show a
|
||||
// upgrade start page instead! Other apps may wish to show this UI, and supply
|
||||
// a whatsNewURL field in their brand.properties that contains a link to a page
|
||||
// which tells users what's new in this new update.
|
||||
@ -225,10 +225,10 @@ pref("app.update.service.enabled", true);
|
||||
pref("extensions.update.enabled", true);
|
||||
pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%¤tAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
|
||||
pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%¤tAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
|
||||
pref("extensions.update.interval", 86400); // Check for updates to Extensions and
|
||||
pref("extensions.update.interval", 86400); // Check for updates to Extensions and
|
||||
// Themes every day
|
||||
// Non-symmetric (not shared by extensions) extension-specific [update] preferences
|
||||
pref("extensions.dss.enabled", false); // Dynamic Skin Switching
|
||||
pref("extensions.dss.enabled", false); // Dynamic Skin Switching
|
||||
pref("extensions.dss.switchPending", false); // Non-dynamic switch pending after next
|
||||
// restart.
|
||||
|
||||
@ -359,7 +359,7 @@ pref("browser.altClickSave", false);
|
||||
pref("browser.download.debug", false);
|
||||
|
||||
// Number of milliseconds to wait for the http headers (and thus
|
||||
// the Content-Disposition filename) before giving up and falling back to
|
||||
// the Content-Disposition filename) before giving up and falling back to
|
||||
// picking a filename without that info in hand so that the user sees some
|
||||
// feedback from their action.
|
||||
pref("browser.download.saveLinkAsFilenameTimeout", 4000);
|
||||
@ -390,7 +390,7 @@ pref("browser.search.jarURIs", "chrome://browser/locale/searchplugins/");
|
||||
// pointer to the default engine name
|
||||
pref("browser.search.defaultenginename", "chrome://browser-region/locale/region.properties");
|
||||
|
||||
// Ordering of Search Engines in the Engine list.
|
||||
// Ordering of Search Engines in the Engine list.
|
||||
pref("browser.search.order.1", "chrome://browser-region/locale/region.properties");
|
||||
pref("browser.search.order.2", "chrome://browser-region/locale/region.properties");
|
||||
pref("browser.search.order.3", "chrome://browser-region/locale/region.properties");
|
||||
@ -462,7 +462,7 @@ pref("browser.tabs.drawInTitlebar", false);
|
||||
pref("browser.tabs.drawInTitlebar", true);
|
||||
#endif
|
||||
|
||||
// When tabs opened by links in other tabs via a combination of
|
||||
// When tabs opened by links in other tabs via a combination of
|
||||
// browser.link.open_newwindow being set to 3 and target="_blank" etc are
|
||||
// closed:
|
||||
// true return to the tab that opened this tab (its owner)
|
||||
@ -476,7 +476,7 @@ pref("browser.ctrlTab.previews", false);
|
||||
// be exported as HTML to the bookmarks.html file.
|
||||
pref("browser.bookmarks.autoExportHTML", false);
|
||||
|
||||
// The maximum number of daily bookmark backups to
|
||||
// The maximum number of daily bookmark backups to
|
||||
// keep in {PROFILEDIR}/bookmarkbackups. Special values:
|
||||
// -1: unlimited
|
||||
// 0: no backups created (and deletes all existing backups)
|
||||
@ -489,7 +489,7 @@ pref("javascript.options.showInConsole", true);
|
||||
pref("general.warnOnAboutConfig", false);
|
||||
#endif
|
||||
|
||||
// This is the pref to control the location bar, change this to true to
|
||||
// This is the pref to control the location bar, change this to true to
|
||||
// force this - this makes the origin of popup windows more obvious to avoid
|
||||
// spoofing. We would rather not do it by default because it affects UE for web
|
||||
// applications, but without it there isn't a really good way to prevent chrome
|
||||
@ -917,7 +917,7 @@ pref("browser.audioFeeds.handler", "ask");
|
||||
// At startup, if the handler service notices that the version number in the
|
||||
// region.properties file is newer than the version number in the handler
|
||||
// service datastore, it will add any new handlers it finds in the prefs (as
|
||||
// seeded by this file) to its datastore.
|
||||
// seeded by this file) to its datastore.
|
||||
pref("gecko.handlerService.defaultHandlersVersion", "chrome://browser-region/locale/region.properties");
|
||||
|
||||
// The default set of web-based protocol handlers shown in the application
|
||||
@ -1921,3 +1921,5 @@ pref("browser.pocket.site", "getpocket.com");
|
||||
pref("browser.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
|
||||
pref("browser.pocket.useLocaleList", true);
|
||||
pref("browser.pocket.enabledLocales", "en-US de es-ES ja ja-JP-mac ru");
|
||||
|
||||
pref("view_source.tab", true);
|
||||
|
@ -986,6 +986,7 @@ var gBrowserInit = {
|
||||
mm.loadFrameScript("chrome://browser/content/content.js", true);
|
||||
mm.loadFrameScript("chrome://browser/content/content-UITour.js", true);
|
||||
mm.loadFrameScript("chrome://global/content/manifestMessages.js", true);
|
||||
mm.loadFrameScript("chrome://global/content/viewSource-content.js", true);
|
||||
|
||||
window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad);
|
||||
|
||||
@ -2322,11 +2323,11 @@ function readFromClipboard()
|
||||
* If aArgsOrDocument is an object, that object can take the
|
||||
* following properties:
|
||||
*
|
||||
* browser:
|
||||
* The browser containing the document that we would like to view the
|
||||
* source of.
|
||||
* URL:
|
||||
* URL (required):
|
||||
* A string URL for the page we'd like to view the source of.
|
||||
* browser (optional):
|
||||
* The browser containing the document that we would like to view the
|
||||
* source of. This is required if outerWindowID is passed.
|
||||
* outerWindowID (optional):
|
||||
* The outerWindowID of the content window containing the document that
|
||||
* we want to view the source of. You only need to provide this if you
|
||||
@ -2359,7 +2360,18 @@ function BrowserViewSourceOfDocument(aArgsOrDocument) {
|
||||
args = aArgsOrDocument;
|
||||
}
|
||||
|
||||
top.gViewSourceUtils.viewSource(args);
|
||||
let inTab = Services.prefs.getBoolPref("view_source.tab");
|
||||
if (inTab) {
|
||||
let viewSourceURL = `view-source:${args.URL}`;
|
||||
let tab = gBrowser.loadOneTab(viewSourceURL, {
|
||||
relatedToCurrent: true,
|
||||
inBackground: false
|
||||
});
|
||||
args.viewSourceBrowser = gBrowser.getBrowserForTab(tab);
|
||||
top.gViewSourceUtils.viewSourceInBrowser(args);
|
||||
} else {
|
||||
top.gViewSourceUtils.viewSource(args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -5560,7 +5572,7 @@ function middleMousePaste(event) {
|
||||
|
||||
clipboard = stripUnsafeProtocolOnPaste(clipboard);
|
||||
|
||||
// if it's not the current tab, we don't need to do anything because the
|
||||
// if it's not the current tab, we don't need to do anything because the
|
||||
// browser doesn't exist.
|
||||
let where = whereToOpenLink(event, true, false);
|
||||
let lastLocationChange;
|
||||
@ -6278,10 +6290,10 @@ function warnAboutClosingWindow() {
|
||||
otherPBWindowExists = true;
|
||||
if (win.toolbar.visible)
|
||||
nonPopupPresent = true;
|
||||
// If the current window is not in private browsing mode we don't need to
|
||||
// look for other pb windows, we can leave the loop when finding the
|
||||
// first non-popup window. If however the current window is in private
|
||||
// browsing mode then we need at least one other pb and one non-popup
|
||||
// If the current window is not in private browsing mode we don't need to
|
||||
// look for other pb windows, we can leave the loop when finding the
|
||||
// first non-popup window. If however the current window is in private
|
||||
// browsing mode then we need at least one other pb and one non-popup
|
||||
// window to break out early.
|
||||
if ((!isPBWindow || otherPBWindowExists) && nonPopupPresent)
|
||||
break;
|
||||
@ -7099,7 +7111,7 @@ var gIdentityHandler = {
|
||||
dt.setData("text/html", htmlString);
|
||||
dt.setDragImage(gProxyFavIcon, 16, 16);
|
||||
},
|
||||
|
||||
|
||||
handleEvent: function (event) {
|
||||
switch (event.type) {
|
||||
case "blur":
|
||||
|
@ -1005,11 +1005,28 @@ 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);
|
||||
if (aContext == "selection") {
|
||||
top.gViewSourceUtils
|
||||
.viewSourceFromSelectionInBrowser(reference, viewSourceBrowser);
|
||||
} else {
|
||||
top.gViewSourceUtils
|
||||
.viewSourceFromFragmentInBrowser(reference, aContext,
|
||||
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.
|
||||
@ -1153,7 +1170,7 @@ nsContextMenu.prototype = {
|
||||
urlSecurityCheck(this.target.currentURI.spec, this.principal);
|
||||
|
||||
// Confirm since it's annoying if you hit this accidentally.
|
||||
const kDesktopBackgroundURL =
|
||||
const kDesktopBackgroundURL =
|
||||
"chrome://browser/content/setDesktopBackground.xul";
|
||||
#ifdef XP_MACOSX
|
||||
// On Mac, the Set Desktop Background window is not modal.
|
||||
@ -1196,7 +1213,7 @@ nsContextMenu.prototype = {
|
||||
// file picker
|
||||
function saveAsListener() {}
|
||||
saveAsListener.prototype = {
|
||||
extListener: null,
|
||||
extListener: null,
|
||||
|
||||
onStartRequest: function saveLinkAs_onStartRequest(aRequest, aContext) {
|
||||
|
||||
@ -1229,17 +1246,17 @@ nsContextMenu.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let extHelperAppSvc =
|
||||
let extHelperAppSvc =
|
||||
Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
|
||||
getService(Ci.nsIExternalHelperAppService);
|
||||
let channel = aRequest.QueryInterface(Ci.nsIChannel);
|
||||
this.extListener =
|
||||
extHelperAppSvc.doContent(channel.contentType, aRequest,
|
||||
extHelperAppSvc.doContent(channel.contentType, aRequest,
|
||||
null, true, window);
|
||||
this.extListener.onStartRequest(aRequest, aContext);
|
||||
},
|
||||
},
|
||||
|
||||
onStopRequest: function saveLinkAs_onStopRequest(aRequest, aContext,
|
||||
onStopRequest: function saveLinkAs_onStopRequest(aRequest, aContext,
|
||||
aStatusCode) {
|
||||
if (aStatusCode == NS_ERROR_SAVE_LINK_AS_TIMEOUT) {
|
||||
// do it the old fashioned way, which will pick the best filename
|
||||
@ -1272,12 +1289,12 @@ nsContextMenu.prototype = {
|
||||
channel.cancel(NS_ERROR_SAVE_LINK_AS_TIMEOUT);
|
||||
}
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if it we don't have the headers after a short time, the user
|
||||
// if it we don't have the headers after a short time, the user
|
||||
// won't have received any feedback from their click. that's bad. so
|
||||
// we give up waiting for the filename.
|
||||
// we give up waiting for the filename.
|
||||
function timerCallback() {}
|
||||
timerCallback.prototype = {
|
||||
notify: function sLA_timer_notify(aTimer) {
|
||||
@ -1319,8 +1336,8 @@ nsContextMenu.prototype = {
|
||||
channel.forceAllowThirdPartyCookie = true;
|
||||
}
|
||||
|
||||
// fallback to the old way if we don't see the headers quickly
|
||||
var timeToWait =
|
||||
// fallback to the old way if we don't see the headers quickly
|
||||
var timeToWait =
|
||||
gPrefService.getIntPref("browser.download.saveLinkAsFilenameTimeout");
|
||||
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback(new timerCallback(), timeToWait,
|
||||
@ -1499,7 +1516,7 @@ nsContextMenu.prototype = {
|
||||
|
||||
// Generate fully qualified URL for clicked-on link.
|
||||
getLinkURL: function() {
|
||||
var href = this.link.href;
|
||||
var href = this.link.href;
|
||||
if (href)
|
||||
return href;
|
||||
|
||||
|
@ -29,7 +29,7 @@ function test() {
|
||||
function runTests1(aTab) {
|
||||
let toolDefinition = {
|
||||
id: toolId1,
|
||||
isTargetSupported: function() true,
|
||||
isTargetSupported: () => true,
|
||||
visibilityswitch: "devtools.test-tool.enabled",
|
||||
url: "about:blank",
|
||||
label: "someLabel",
|
||||
@ -90,7 +90,7 @@ function runTests1(aTab) {
|
||||
function runTests2() {
|
||||
let toolDefinition = {
|
||||
id: toolId2,
|
||||
isTargetSupported: function() true,
|
||||
isTargetSupported: () => true,
|
||||
visibilityswitch: "devtools.test-tool.enabled",
|
||||
url: "about:blank",
|
||||
label: "someLabel",
|
||||
@ -229,11 +229,17 @@ DevToolPanel.prototype = {
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
get target() this._toolbox.target,
|
||||
get target() {
|
||||
return this._toolbox.target;
|
||||
},
|
||||
|
||||
get toolbox() this._toolbox,
|
||||
get toolbox() {
|
||||
return this._toolbox;
|
||||
},
|
||||
|
||||
get isReady() this._isReady,
|
||||
get isReady() {
|
||||
return this._isReady;
|
||||
},
|
||||
|
||||
_isReady: false,
|
||||
|
||||
|
@ -13,7 +13,7 @@ function runTests(aTab) {
|
||||
let toolDefinition = {
|
||||
id: "testTool",
|
||||
visibilityswitch: "devtools.testTool.enabled",
|
||||
isTargetSupported: function() true,
|
||||
isTargetSupported: () => true,
|
||||
url: "about:blank",
|
||||
label: "someLabel",
|
||||
build: function(iframeWindow, toolbox) {
|
||||
|
@ -26,7 +26,7 @@ function testRegister(aToolbox)
|
||||
id: "test-tool",
|
||||
label: "Test Tool",
|
||||
inMenu: true,
|
||||
isTargetSupported: function() true,
|
||||
isTargetSupported: () => true,
|
||||
build: function() {}
|
||||
});
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ add_task(function*() {
|
||||
function registerNewTool() {
|
||||
let toolDefinition = {
|
||||
id: "test-tool",
|
||||
isTargetSupported: function() true,
|
||||
isTargetSupported: () => true,
|
||||
visibilityswitch: "devtools.test-tool.enabled",
|
||||
url: "about:blank",
|
||||
label: "someLabel"
|
||||
|
@ -27,7 +27,7 @@ function test() {
|
||||
visibilityswitch: "devtools.fakeTool4242.enabled",
|
||||
url: toolURL,
|
||||
label: "FAKE TOOL!!!",
|
||||
isTargetSupported: function() true,
|
||||
isTargetSupported: () => true,
|
||||
build: function(iframeWindow, toolbox) {
|
||||
let deferred = promise.defer();
|
||||
executeSoon(() => {
|
||||
@ -35,7 +35,7 @@ function test() {
|
||||
target: toolbox.target,
|
||||
toolbox: toolbox,
|
||||
isReady: true,
|
||||
destroy: function(){},
|
||||
destroy: function() {},
|
||||
panelDoc: iframeWindow.document,
|
||||
});
|
||||
});
|
||||
|
@ -22,7 +22,7 @@ function test() {
|
||||
visibilityswitch: "devtools.testTool1072208.enabled",
|
||||
url: toolURL,
|
||||
label: "Test tool",
|
||||
isTargetSupported: function() true,
|
||||
isTargetSupported: () => true,
|
||||
build: function(iframeWindow, toolbox) {
|
||||
let deferred = promise.defer();
|
||||
executeSoon(() => {
|
||||
@ -30,7 +30,7 @@ function test() {
|
||||
target: toolbox.target,
|
||||
toolbox: toolbox,
|
||||
isReady: true,
|
||||
destroy: function(){},
|
||||
destroy: function() {},
|
||||
panelDoc: iframeWindow.document,
|
||||
});
|
||||
});
|
||||
|
@ -4,12 +4,12 @@
|
||||
///////////////////
|
||||
//
|
||||
// Whitelisting this test.
|
||||
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
|
||||
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
|
||||
//
|
||||
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Shader Editor is still waiting for a WebGL context to be created.");
|
||||
|
||||
function performChecks(target) {
|
||||
return Task.spawn(function() {
|
||||
return Task.spawn(function*() {
|
||||
let toolIds = gDevTools.getToolDefinitionArray()
|
||||
.filter(def => def.isTargetSupported(target))
|
||||
.map(def => def.id);
|
||||
@ -32,7 +32,7 @@ function performChecks(target) {
|
||||
}
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
Task.spawn(function*() {
|
||||
toggleAllTools(true);
|
||||
let tab = yield addTab("about:blank");
|
||||
let target = TargetFactory.forTab(tab);
|
||||
|
@ -4,7 +4,7 @@
|
||||
///////////////////
|
||||
//
|
||||
// Whitelisting this test.
|
||||
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
|
||||
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
|
||||
//
|
||||
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Shader Editor is still waiting for a WebGL context to be created.");
|
||||
|
||||
@ -40,7 +40,7 @@ const { DebuggerClient } =
|
||||
*/
|
||||
|
||||
function runTools(target) {
|
||||
return Task.spawn(function() {
|
||||
return Task.spawn(function*() {
|
||||
let toolIds = gDevTools.getToolDefinitionArray()
|
||||
.filter(def => def.isTargetSupported(target))
|
||||
.map(def => def.id);
|
||||
@ -96,7 +96,7 @@ function getTarget(client) {
|
||||
}
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
Task.spawn(function*() {
|
||||
toggleAllTools(true);
|
||||
yield addTab("about:blank");
|
||||
|
||||
|
@ -23,20 +23,20 @@ function test() {
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.showToolbox(target, null, Toolbox.HostType.BOTTOM)
|
||||
.then(function (aToolbox) { toolbox = aToolbox; })
|
||||
.then(function () toolbox.selectTool(TOOL_ID_1))
|
||||
.then(() => toolbox.selectTool(TOOL_ID_1))
|
||||
|
||||
// undock toolbox and check title
|
||||
.then(function () toolbox.switchHost(Toolbox.HostType.WINDOW))
|
||||
.then(() => toolbox.switchHost(Toolbox.HostType.WINDOW))
|
||||
.then(checkTitle.bind(null, LABEL_1, URL_1, "toolbox undocked"))
|
||||
|
||||
// switch to different tool and check title
|
||||
.then(function () toolbox.selectTool(TOOL_ID_2))
|
||||
.then(() => toolbox.selectTool(TOOL_ID_2))
|
||||
.then(checkTitle.bind(null, LABEL_2, URL_1, "tool changed"))
|
||||
|
||||
// navigate to different url and check title
|
||||
.then(function () {
|
||||
let deferred = promise.defer();
|
||||
target.once("navigate", function () deferred.resolve());
|
||||
target.once("navigate", () => deferred.resolve());
|
||||
gBrowser.loadURI(URL_2);
|
||||
return deferred.promise;
|
||||
})
|
||||
@ -55,12 +55,12 @@ function test() {
|
||||
return gDevTools.showToolbox(target, null, Toolbox.HostType.WINDOW);
|
||||
})
|
||||
.then(function (aToolbox) { toolbox = aToolbox; })
|
||||
.then(function () toolbox.selectTool(TOOL_ID_1))
|
||||
.then(() => toolbox.selectTool(TOOL_ID_1))
|
||||
.then(checkTitle.bind(null, LABEL_1, URL_2,
|
||||
"toolbox destroyed and recreated"))
|
||||
|
||||
// clean up
|
||||
.then(function () toolbox.destroy())
|
||||
.then(() => toolbox.destroy())
|
||||
.then(function () {
|
||||
toolbox = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
|
@ -181,4 +181,94 @@ const DOM = exports.DOM = {
|
||||
hbox.appendChild(labelValue);
|
||||
return hbox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Builds a stack trace in an element.
|
||||
*
|
||||
* @param {Document} doc
|
||||
* @param object params
|
||||
* An options object with the following members:
|
||||
* string type - String identifier for type of stack ("stack", "startStack" or "endStack")
|
||||
* number frameIndex - The index of the topmost stack frame.
|
||||
* array frames - Array of stack frames.
|
||||
*/
|
||||
buildStackTrace: function(doc, { type, frameIndex, frames }) {
|
||||
let container = doc.createElement("vbox");
|
||||
let labelName = doc.createElement("label");
|
||||
labelName.className = "plain marker-details-labelname";
|
||||
labelName.setAttribute("value", L10N.getStr(`timeline.markerDetail.${type}`));
|
||||
container.appendChild(labelName);
|
||||
|
||||
let wasAsyncParent = false;
|
||||
while (frameIndex > 0) {
|
||||
let frame = frames[frameIndex];
|
||||
let url = frame.source;
|
||||
let displayName = frame.functionDisplayName;
|
||||
let line = frame.line;
|
||||
|
||||
// If the previous frame had an async parent, then the async
|
||||
// cause is in this frame and should be displayed.
|
||||
if (wasAsyncParent) {
|
||||
let asyncBox = doc.createElement("hbox");
|
||||
let asyncLabel = doc.createElement("label");
|
||||
asyncLabel.className = "devtools-monospace";
|
||||
asyncLabel.setAttribute("value", L10N.getFormatStr("timeline.markerDetail.asyncStack",
|
||||
frame.asyncCause));
|
||||
asyncBox.appendChild(asyncLabel);
|
||||
container.appendChild(asyncBox);
|
||||
wasAsyncParent = false;
|
||||
}
|
||||
|
||||
let hbox = doc.createElement("hbox");
|
||||
|
||||
if (displayName) {
|
||||
let functionLabel = doc.createElement("label");
|
||||
functionLabel.className = "devtools-monospace";
|
||||
functionLabel.setAttribute("value", displayName);
|
||||
hbox.appendChild(functionLabel);
|
||||
}
|
||||
|
||||
if (url) {
|
||||
let aNode = doc.createElement("a");
|
||||
aNode.className = "waterfall-marker-location devtools-source-link";
|
||||
aNode.href = url;
|
||||
aNode.draggable = false;
|
||||
aNode.setAttribute("title", url);
|
||||
|
||||
let urlNode = doc.createElement("label");
|
||||
urlNode.className = "filename";
|
||||
urlNode.setAttribute("value", WebConsoleUtils.Utils.abbreviateSourceURL(url));
|
||||
let lineNode = doc.createElement("label");
|
||||
lineNode.className = "line-number";
|
||||
lineNode.setAttribute("value", `:${line}`);
|
||||
|
||||
aNode.appendChild(urlNode);
|
||||
aNode.appendChild(lineNode);
|
||||
hbox.appendChild(aNode);
|
||||
|
||||
// Clicking here will bubble up to the parent,
|
||||
// which handles the view source.
|
||||
aNode.setAttribute("data-action", JSON.stringify({
|
||||
url, line, action: "view-source"
|
||||
}));
|
||||
}
|
||||
|
||||
if (!displayName && !url) {
|
||||
let label = doc.createElement("label");
|
||||
label.setAttribute("value", L10N.getStr("timeline.markerDetail.unknownFrame"));
|
||||
hbox.appendChild(label);
|
||||
}
|
||||
|
||||
container.appendChild(hbox);
|
||||
|
||||
if (frame.asyncParent) {
|
||||
frameIndex = frame.asyncParent;
|
||||
wasAsyncParent = true;
|
||||
} else {
|
||||
frameIndex = frame.parent;
|
||||
}
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
};
|
||||
|
@ -29,10 +29,12 @@ loader.lazyRequireGetter(this, "MarkerUtils",
|
||||
*/
|
||||
function MarkerDetails(parent, splitter) {
|
||||
EventEmitter.decorate(this);
|
||||
this._onClick = this._onClick.bind(this);
|
||||
this._document = parent.ownerDocument;
|
||||
this._parent = parent;
|
||||
this._splitter = splitter;
|
||||
this._splitter.addEventListener("mouseup", () => this.emit("resize"));
|
||||
this._parent.addEventListener("click", this._onClick);
|
||||
}
|
||||
|
||||
MarkerDetails.prototype = {
|
||||
@ -41,6 +43,7 @@ MarkerDetails.prototype = {
|
||||
*/
|
||||
destroy: function() {
|
||||
this.empty();
|
||||
this._parent.removeEventListener("click", this._onClick);
|
||||
this._parent = null;
|
||||
this._splitter = null;
|
||||
},
|
||||
@ -57,121 +60,66 @@ MarkerDetails.prototype = {
|
||||
*
|
||||
* @param object params
|
||||
* An options object holding:
|
||||
* toolbox - The toolbox.
|
||||
* marker - The marker to display.
|
||||
* frames - Array of stack frame information; see stack.js.
|
||||
*/
|
||||
render: function({toolbox: toolbox, marker: marker, frames: frames}) {
|
||||
render: function({ marker, frames }) {
|
||||
this.empty();
|
||||
|
||||
// UI for any marker
|
||||
|
||||
let title = MarkerUtils.DOM.buildTitle(this._document, marker);
|
||||
let duration = MarkerUtils.DOM.buildDuration(this._document, marker);
|
||||
let fields = MarkerUtils.DOM.buildFields(this._document, marker);
|
||||
|
||||
this._parent.appendChild(title);
|
||||
this._parent.appendChild(duration);
|
||||
fields.forEach(field => this._parent.appendChild(field));
|
||||
let elements = [];
|
||||
elements.push(MarkerUtils.DOM.buildTitle(this._document, marker));
|
||||
elements.push(MarkerUtils.DOM.buildDuration(this._document, marker));
|
||||
MarkerUtils.DOM.buildFields(this._document, marker).forEach(field => elements.push(field));
|
||||
|
||||
// Build a stack element -- and use the "startStack" label if
|
||||
// we have both a star and endStack.
|
||||
if (marker.stack) {
|
||||
let property = "timeline.markerDetail.stack";
|
||||
if (marker.endStack) {
|
||||
property = "timeline.markerDetail.startStack";
|
||||
}
|
||||
this.renderStackTrace({toolbox: toolbox, parent: this._parent, property: property,
|
||||
frameIndex: marker.stack, frames: frames});
|
||||
let type = marker.endStack ? "startStack" : "stack";
|
||||
elements.push(MarkerUtils.DOM.buildStackTrace(this._document, {
|
||||
frameIndex: marker.stack, frames, type
|
||||
}));
|
||||
}
|
||||
|
||||
if (marker.endStack) {
|
||||
this.renderStackTrace({toolbox: toolbox, parent: this._parent, property: "timeline.markerDetail.endStack",
|
||||
frameIndex: marker.endStack, frames: frames});
|
||||
}
|
||||
elements.forEach(el => this._parent.appendChild(el));
|
||||
},
|
||||
|
||||
/**
|
||||
* Render a stack trace.
|
||||
*
|
||||
* @param object params
|
||||
* An options object with the following members:
|
||||
* object toolbox - The toolbox.
|
||||
* nsIDOMNode parent - The parent node holding the view.
|
||||
* string property - String identifier for label's name.
|
||||
* integer frameIndex - The index of the topmost stack frame.
|
||||
* array frames - Array of stack frames.
|
||||
* Handles click in the marker details view. Based on the target,
|
||||
* can handle different actions -- only supporting view source links
|
||||
* for the moment.
|
||||
*/
|
||||
renderStackTrace: function({toolbox: toolbox, parent: parent,
|
||||
property: property, frameIndex: frameIndex,
|
||||
frames: frames}) {
|
||||
let labelName = this._document.createElement("label");
|
||||
labelName.className = "plain marker-details-labelname";
|
||||
labelName.setAttribute("value", L10N.getStr(property));
|
||||
parent.appendChild(labelName);
|
||||
_onClick: function (e) {
|
||||
let data = findActionFromEvent(e.target);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
let wasAsyncParent = false;
|
||||
while (frameIndex > 0) {
|
||||
let frame = frames[frameIndex];
|
||||
let url = frame.source;
|
||||
let displayName = frame.functionDisplayName;
|
||||
let line = frame.line;
|
||||
|
||||
// If the previous frame had an async parent, then the async
|
||||
// cause is in this frame and should be displayed.
|
||||
if (wasAsyncParent) {
|
||||
let asyncBox = this._document.createElement("hbox");
|
||||
let asyncLabel = this._document.createElement("label");
|
||||
asyncLabel.className = "devtools-monospace";
|
||||
asyncLabel.setAttribute("value", L10N.getFormatStr("timeline.markerDetail.asyncStack",
|
||||
frame.asyncCause));
|
||||
asyncBox.appendChild(asyncLabel);
|
||||
parent.appendChild(asyncBox);
|
||||
wasAsyncParent = false;
|
||||
}
|
||||
|
||||
let hbox = this._document.createElement("hbox");
|
||||
|
||||
if (displayName) {
|
||||
let functionLabel = this._document.createElement("label");
|
||||
functionLabel.className = "devtools-monospace";
|
||||
functionLabel.setAttribute("value", displayName);
|
||||
hbox.appendChild(functionLabel);
|
||||
}
|
||||
|
||||
if (url) {
|
||||
let aNode = this._document.createElement("a");
|
||||
aNode.className = "waterfall-marker-location theme-link devtools-monospace";
|
||||
aNode.href = url;
|
||||
aNode.draggable = false;
|
||||
aNode.setAttribute("title", url);
|
||||
|
||||
let text = WebConsoleUtils.abbreviateSourceURL(url) + ":" + line;
|
||||
let label = this._document.createElement("label");
|
||||
label.setAttribute("value", text);
|
||||
aNode.appendChild(label);
|
||||
hbox.appendChild(aNode);
|
||||
|
||||
aNode.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
this.emit("view-source", url, line);
|
||||
});
|
||||
}
|
||||
|
||||
if (!displayName && !url) {
|
||||
let label = this._document.createElement("label");
|
||||
label.setAttribute("value", L10N.getStr("timeline.markerDetail.unknownFrame"));
|
||||
hbox.appendChild(label);
|
||||
}
|
||||
|
||||
parent.appendChild(hbox);
|
||||
|
||||
if (frame.asyncParent) {
|
||||
frameIndex = frame.asyncParent;
|
||||
wasAsyncParent = true;
|
||||
} else {
|
||||
frameIndex = frame.parent;
|
||||
}
|
||||
if (data.action === "view-source") {
|
||||
this.emit("view-source", data.url, data.line);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
exports.MarkerDetails = MarkerDetails;
|
||||
|
||||
/**
|
||||
* Take an element from an event `target`, and asend through
|
||||
* the DOM, looking for an element with a `data-action` attribute. Return
|
||||
* the parsed `data-action` value found, or null if none found before
|
||||
* reaching the parent `container`.
|
||||
*
|
||||
* @param {Element} target
|
||||
* @param {Element} container
|
||||
* @return {?object}
|
||||
*/
|
||||
function findActionFromEvent (target, container) {
|
||||
let el = target;
|
||||
let action;
|
||||
while (el !== container) {
|
||||
if (action = el.getAttribute("data-action")) {
|
||||
return JSON.parse(action);
|
||||
}
|
||||
el = el.parentNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const MARKER_DETAILS_WIDTH = 300;
|
||||
|
||||
/**
|
||||
* Waterfall view containing the timeline markers, controlled by DetailsView.
|
||||
*/
|
||||
@ -24,6 +26,9 @@ let WaterfallView = Heritage.extend(DetailsSubview, {
|
||||
initialize: function () {
|
||||
DetailsSubview.initialize.call(this);
|
||||
|
||||
// TODO bug 1167093 save the previously set width, and ensure minimum width
|
||||
$("#waterfall-details").setAttribute("width", MARKER_DETAILS_WIDTH);
|
||||
|
||||
this.waterfall = new Waterfall($("#waterfall-breakdown"), $("#waterfall-view"));
|
||||
this.details = new MarkerDetails($("#waterfall-details"), $("#waterfall-view > splitter"));
|
||||
|
||||
|
@ -123,6 +123,15 @@ exports.viewSourceInScratchpad = Task.async(function *(sourceURL, sourceLine) {
|
||||
* @return {Promise}
|
||||
*/
|
||||
exports.viewSource = Task.async(function *(toolbox, sourceURL, sourceLine) {
|
||||
// Attempt to access view source via a browser first, which may display it in
|
||||
// a tab, if enabled.
|
||||
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (browserWin) {
|
||||
return browserWin.BrowserViewSourceOfDocument({
|
||||
URL: sourceURL,
|
||||
lineNumber: sourceLine
|
||||
});
|
||||
}
|
||||
let utils = toolbox.gViewSourceUtils;
|
||||
utils.viewSource(sourceURL, null, toolbox.doc, sourceLine || 0);
|
||||
});
|
||||
|
@ -111,7 +111,7 @@ function testAdvanceCharsFunction(doc) {
|
||||
return aText.length > 0;
|
||||
},
|
||||
start: function(editor) {
|
||||
for each (let ch in ":Test:") {
|
||||
for (let ch of ":Test:") {
|
||||
EventUtils.sendChar(ch);
|
||||
}
|
||||
},
|
||||
|
@ -44,7 +44,7 @@ function promiseTab(aURL) {
|
||||
addTab(aURL, resolve));
|
||||
}
|
||||
|
||||
registerCleanupFunction(function tearDown() {
|
||||
registerCleanupFunction(function* tearDown() {
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
yield gDevTools.closeToolbox(target);
|
||||
|
||||
@ -126,7 +126,9 @@ function waitForValue(aOptions)
|
||||
successFn(aOptions, lastValue);
|
||||
}
|
||||
else {
|
||||
setTimeout(function() wait(validatorFn, successFn, failureFn), 100);
|
||||
setTimeout(() => {
|
||||
wait(validatorFn, successFn, failureFn);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
///////////////////
|
||||
//
|
||||
// Whitelisting this test.
|
||||
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
|
||||
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
|
||||
//
|
||||
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
|
||||
|
||||
@ -70,16 +70,16 @@ function* testInlineStyle(view, inspector) {
|
||||
|
||||
yield expandComputedViewPropertyByIndex(view, 0);
|
||||
|
||||
let onWindow = waitForWindow();
|
||||
let onTab = waitForTab();
|
||||
info("Clicking on the first rule-link in the computed-view");
|
||||
clickLinkByIndex(view, 0);
|
||||
|
||||
let win = yield onWindow;
|
||||
let tab = yield onTab;
|
||||
|
||||
let windowType = win.document.documentElement.getAttribute("windowtype");
|
||||
is(windowType, "navigator:view-source", "View source window is open");
|
||||
info("Closing window");
|
||||
win.close();
|
||||
let tabURI = tab.linkedBrowser.documentURI.spec;
|
||||
ok(tabURI.startsWith("view-source:"), "View source tab is open");
|
||||
info("Closing tab");
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
|
||||
function* testFirstInlineStyleSheet(view, toolbox) {
|
||||
|
@ -7,7 +7,7 @@
|
||||
///////////////////
|
||||
//
|
||||
// Whitelisting this test.
|
||||
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
|
||||
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
|
||||
//
|
||||
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
|
||||
|
||||
@ -70,16 +70,16 @@ add_task(function*() {
|
||||
function* testInlineStyle(view, inspector) {
|
||||
info("Testing inline style");
|
||||
|
||||
let onWindow = waitForWindow();
|
||||
let onTab = waitForTab();
|
||||
info("Clicking on the first link in the rule-view");
|
||||
clickLinkByIndex(view, 0);
|
||||
|
||||
let win = yield onWindow;
|
||||
let tab = yield onTab;
|
||||
|
||||
let windowType = win.document.documentElement.getAttribute("windowtype");
|
||||
is(windowType, "navigator:view-source", "View source window is open");
|
||||
info("Closing window");
|
||||
win.close();
|
||||
let tabURI = tab.linkedBrowser.documentURI.spec;
|
||||
ok(tabURI.startsWith("view-source:"), "View source tab is open");
|
||||
info("Closing tab");
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
|
||||
function* testFirstInlineStyleSheet(view, toolbox) {
|
||||
|
@ -491,6 +491,21 @@ function waitForWindow() {
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for a new tab to open and return a promise that resolves when one
|
||||
* does and completes the load event.
|
||||
* @return a promise that resolves to the tab object
|
||||
*/
|
||||
let waitForTab = Task.async(function*() {
|
||||
info("Waiting for a tab to open");
|
||||
yield once(gBrowser.tabContainer, "TabOpen");
|
||||
let tab = gBrowser.selectedTab;
|
||||
let browser = tab.linkedBrowser;
|
||||
yield once(browser, "load", true);
|
||||
info("The tab load completed");
|
||||
return tab;
|
||||
});
|
||||
|
||||
/**
|
||||
* @see SimpleTest.waitForClipboard
|
||||
* @param {Function} setup Function to execute before checking for the
|
||||
|
@ -434,6 +434,15 @@ WebConsole.prototype = {
|
||||
* The line number which should be highlighted.
|
||||
*/
|
||||
viewSource: function WC_viewSource(aSourceURL, aSourceLine) {
|
||||
// Attempt to access view source via a browser first, which may display it in
|
||||
// a tab, if enabled.
|
||||
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (browserWin) {
|
||||
return browserWin.BrowserViewSourceOfDocument({
|
||||
URL: aSourceURL,
|
||||
lineNumber: aSourceLine
|
||||
});
|
||||
}
|
||||
this.gViewSourceUtils.viewSource(aSourceURL, null, this.iframeWindow.document, aSourceLine || 0);
|
||||
},
|
||||
|
||||
|
@ -10,13 +10,9 @@ let getItemForAttachment;
|
||||
let Sources;
|
||||
let getItemInvoked = false;
|
||||
|
||||
function test() {
|
||||
loadTab(TEST_URI).then(() => {
|
||||
openConsole(null).then(testViewSource);
|
||||
});
|
||||
}
|
||||
|
||||
function testViewSource(hud) {
|
||||
add_task(function*() {
|
||||
yield loadTab(TEST_URI);
|
||||
let hud = yield openConsole(null);
|
||||
info("console opened");
|
||||
|
||||
let button = content.document.querySelector("button");
|
||||
@ -25,58 +21,42 @@ function testViewSource(hud) {
|
||||
expectUncaughtException();
|
||||
EventUtils.sendMouseEvent({ type: "click" }, button, content);
|
||||
|
||||
openDebugger().then(({panelWin: { DebuggerView }}) => {
|
||||
info("debugger opened");
|
||||
Sources = DebuggerView.Sources;
|
||||
openConsole().then((hud) => {
|
||||
info("console opened again");
|
||||
let { panelWin: { DebuggerView } } = yield openDebugger();
|
||||
info("debugger opened");
|
||||
Sources = DebuggerView.Sources;
|
||||
hud = yield openConsole();
|
||||
info("console opened again");
|
||||
|
||||
waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [{
|
||||
text: "fooBazBaz is not defined",
|
||||
category: CATEGORY_JS,
|
||||
severity: SEVERITY_ERROR,
|
||||
}],
|
||||
}).then(onMessage);
|
||||
});
|
||||
let [result] = yield waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [{
|
||||
text: "fooBazBaz is not defined",
|
||||
category: CATEGORY_JS,
|
||||
severity: SEVERITY_ERROR,
|
||||
}],
|
||||
});
|
||||
|
||||
function onMessage([result]) {
|
||||
let msg = [...result.matched][0];
|
||||
ok(msg, "error message");
|
||||
let locationNode = msg.querySelector(".message-location");
|
||||
ok(locationNode, "location node");
|
||||
let msg = [...result.matched][0];
|
||||
ok(msg, "error message");
|
||||
let locationNode = msg.querySelector(".message-location");
|
||||
ok(locationNode, "location node");
|
||||
|
||||
Services.ww.registerNotification(observer);
|
||||
let onTabOpen = waitForTab();
|
||||
|
||||
getItemForAttachment = Sources.getItemForAttachment;
|
||||
Sources.getItemForAttachment = () => {
|
||||
getItemInvoked = true;
|
||||
return false;
|
||||
};
|
||||
getItemForAttachment = Sources.getItemForAttachment;
|
||||
Sources.getItemForAttachment = () => {
|
||||
getItemInvoked = true;
|
||||
return false;
|
||||
};
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "click" }, locationNode);
|
||||
}
|
||||
}
|
||||
EventUtils.sendMouseEvent({ type: "click" }, locationNode);
|
||||
|
||||
let observer = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic != "domwindowopened") {
|
||||
return;
|
||||
}
|
||||
let tab = yield onTabOpen;
|
||||
ok(true, "the view source tab was opened in response to clicking " +
|
||||
"the location node");
|
||||
gBrowser.removeTab(tab);
|
||||
|
||||
ok(true, "the view source window was opened in response to clicking " +
|
||||
"the location node");
|
||||
|
||||
aSubject.close();
|
||||
ok(getItemInvoked, "custom getItemForAttachment() was invoked");
|
||||
Sources.getItemForAttachment = getItemForAttachment;
|
||||
Sources = getItemForAttachment = null;
|
||||
finishTest();
|
||||
}
|
||||
};
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.ww.unregisterNotification(observer);
|
||||
ok(getItemInvoked, "custom getItemForAttachment() was invoked");
|
||||
Sources.getItemForAttachment = getItemForAttachment;
|
||||
Sources = getItemForAttachment = null;
|
||||
});
|
||||
|
@ -255,6 +255,21 @@ function waitForContextMenu(aPopup, aButton, aOnShown, aOnHidden)
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for a new tab to open and return a promise that resolves when one
|
||||
* does and completes the load event.
|
||||
* @return a promise that resolves to the tab object
|
||||
*/
|
||||
let waitForTab = Task.async(function*() {
|
||||
info("Waiting for a tab to open");
|
||||
yield once(gBrowser.tabContainer, "TabOpen");
|
||||
let tab = gBrowser.selectedTab;
|
||||
let browser = tab.linkedBrowser;
|
||||
yield once(browser, "load", true);
|
||||
info("The tab load completed");
|
||||
return tab;
|
||||
});
|
||||
|
||||
/**
|
||||
* Dump the output of all open Web Consoles - used only for debugging purposes.
|
||||
*/
|
||||
|
@ -1136,10 +1136,10 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
|
||||
|
||||
%include ../shared/notification-icons.inc.css
|
||||
|
||||
.popup-notification-description[popupid="addon-progress"],
|
||||
.popup-notification-description[popupid="addon-install-confirmation"] {
|
||||
width: 27em;
|
||||
max-width: 27em;
|
||||
.popup-notification-body[popupid="addon-progress"],
|
||||
.popup-notification-body[popupid="addon-install-confirmation"] {
|
||||
width: 28em;
|
||||
max-width: 28em;
|
||||
}
|
||||
|
||||
.addon-install-confirmation-name {
|
||||
|
@ -3687,10 +3687,10 @@ notification[value="loop-sharing-notification"] .messageImage {
|
||||
}
|
||||
}
|
||||
|
||||
.popup-notification-description[popupid="addon-progress"],
|
||||
.popup-notification-description[popupid="addon-install-confirmation"] {
|
||||
width: 27em;
|
||||
max-width: 27em;
|
||||
.popup-notification-body[popupid="addon-progress"],
|
||||
.popup-notification-body[popupid="addon-install-confirmation"] {
|
||||
width: 28em;
|
||||
max-width: 28em;
|
||||
}
|
||||
|
||||
#addon-progress-notification-progresstext,
|
||||
|
@ -6,16 +6,17 @@
|
||||
|
||||
:root {
|
||||
font: message-box;
|
||||
%ifdef XP_MACOSX
|
||||
--monospace-font-family: Menlo, monospace;
|
||||
%elifdef XP_WIN
|
||||
--monospace-font-family: Consolas, monospace;
|
||||
%else
|
||||
--monospace-font-family: monospace;
|
||||
%endif
|
||||
}
|
||||
|
||||
.devtools-monospace {
|
||||
%ifdef XP_MACOSX
|
||||
font-family: Menlo, monospace;
|
||||
%elifdef XP_WIN
|
||||
font-family: Consolas, monospace;
|
||||
%else
|
||||
font-family: monospace;
|
||||
%endif
|
||||
font-family: var(--monospace-font-family);
|
||||
%if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
|
||||
font-size: 80%;
|
||||
%endif
|
||||
@ -247,3 +248,34 @@
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* links to source code, like displaying `myfile.js:45` */
|
||||
|
||||
.devtools-source-link {
|
||||
font-family: var(--monospace-font-family);
|
||||
color: var(--theme-highlight-blue);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
font-size: 11px;
|
||||
width: 12em; /* probably should be changed for each tool */
|
||||
}
|
||||
|
||||
.devtools-source-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.devtools-source-link > .filename {
|
||||
text-overflow: ellipsis;
|
||||
text-align: end;
|
||||
overflow: hidden;
|
||||
margin: 2px 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.devtools-source-link > .line-number {
|
||||
flex: none;
|
||||
margin: 2px 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -457,15 +457,6 @@
|
||||
color: var(--theme-selection-color);
|
||||
}
|
||||
|
||||
.waterfall-marker-location {
|
||||
color: -moz-nativehyperlinktext;
|
||||
}
|
||||
|
||||
.waterfall-marker-location:hover,
|
||||
.waterfall-marker-location:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#waterfall-details {
|
||||
-moz-padding-start: 8px;
|
||||
-moz-padding-end: 8px;
|
||||
|
@ -2153,10 +2153,10 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
|
||||
|
||||
%include ../shared/notification-icons.inc.css
|
||||
|
||||
.popup-notification-description[popupid="addon-progress"],
|
||||
.popup-notification-description[popupid="addon-install-confirmation"] {
|
||||
width: 27em;
|
||||
max-width: 27em;
|
||||
.popup-notification-body[popupid="addon-progress"],
|
||||
.popup-notification-body[popupid="addon-install-confirmation"] {
|
||||
width: 28em;
|
||||
max-width: 28em;
|
||||
}
|
||||
|
||||
.addon-install-confirmation-name {
|
||||
|
@ -40,45 +40,45 @@ span[id]:before {
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
.start-tag {
|
||||
.highlight .start-tag {
|
||||
color: purple;
|
||||
font-weight: bold;
|
||||
}
|
||||
.end-tag {
|
||||
.highlight .end-tag {
|
||||
color: purple;
|
||||
font-weight: bold;
|
||||
}
|
||||
.comment {
|
||||
.highlight .comment {
|
||||
color: green;
|
||||
font-style: italic;
|
||||
}
|
||||
.cdata {
|
||||
.highlight .cdata {
|
||||
color: #CC0066;
|
||||
}
|
||||
.doctype {
|
||||
.highlight .doctype {
|
||||
color: steelblue;
|
||||
font-style: italic;
|
||||
}
|
||||
.pi {
|
||||
.highlight .pi {
|
||||
color: orchid;
|
||||
font-style: italic;
|
||||
}
|
||||
.entity {
|
||||
color:#FF4500;
|
||||
.highlight .entity {
|
||||
color: #FF4500;
|
||||
font-weight: normal;
|
||||
}
|
||||
.text {
|
||||
.highlight .text {
|
||||
font-weight: normal;
|
||||
}
|
||||
.attribute-name {
|
||||
.highlight .attribute-name {
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
.attribute-value {
|
||||
.highlight .attribute-value {
|
||||
color: blue;
|
||||
font-weight: normal;
|
||||
}
|
||||
.markupdeclaration {
|
||||
.highlight .markupdeclaration {
|
||||
color: steelblue;
|
||||
font-style: italic;
|
||||
}
|
||||
@ -88,9 +88,9 @@ span:not(.error), a:not(.error) {
|
||||
span[id] {
|
||||
unicode-bidi: -moz-isolate;
|
||||
}
|
||||
.error,
|
||||
.error > :-moz-any(.start-tag, .end-tag, .comment, .cdata, .doctype, .pi,
|
||||
.entity, .attribute-name, .attribute-value) {
|
||||
.highlight .error,
|
||||
.highlight .error > :-moz-any(.start-tag, .end-tag, .comment, .cdata, .doctype,
|
||||
.pi, .entity, .attribute-name, .attribute-value) {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@ -53,8 +53,6 @@ nsHtml5Highlighter::nsHtml5Highlighter(nsAHtml5TreeOpSink* aOpSink)
|
||||
, mInlinesOpen(0)
|
||||
, mInCharacters(false)
|
||||
, mBuffer(nullptr)
|
||||
, mSyntaxHighlight(Preferences::GetBool("view_source.syntax_highlight",
|
||||
true))
|
||||
, mOpSink(aOpSink)
|
||||
, mCurrentRun(nullptr)
|
||||
, mAmpersand(nullptr)
|
||||
@ -713,9 +711,6 @@ nsHtml5Highlighter::AppendCharacters(const char16_t* aBuffer,
|
||||
void
|
||||
nsHtml5Highlighter::AddClass(const char16_t* aClass)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
mOpQueue.AppendElement()->InitAddClass(CurrentNode(), aClass);
|
||||
}
|
||||
|
||||
@ -751,9 +746,6 @@ nsHtml5Highlighter::AddBase(const nsString& aValue)
|
||||
void
|
||||
nsHtml5Highlighter::AddErrorToCurrentNode(const char* aMsgId)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
treeOp->Init(CurrentNode(), aMsgId);
|
||||
@ -762,9 +754,6 @@ nsHtml5Highlighter::AddErrorToCurrentNode(const char* aMsgId)
|
||||
void
|
||||
nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
@ -775,9 +764,6 @@ void
|
||||
nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
|
||||
nsIAtom* aName)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
@ -789,9 +775,6 @@ nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
|
||||
nsIAtom* aName,
|
||||
nsIAtom* aOther)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
NS_PRECONDITION(mCurrentRun, "Adding error to run without one!");
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
@ -801,9 +784,6 @@ nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId,
|
||||
void
|
||||
nsHtml5Highlighter::AddErrorToCurrentAmpersand(const char* aMsgId)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
NS_PRECONDITION(mAmpersand, "Adding error to ampersand without one!");
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
@ -813,9 +793,6 @@ nsHtml5Highlighter::AddErrorToCurrentAmpersand(const char* aMsgId)
|
||||
void
|
||||
nsHtml5Highlighter::AddErrorToCurrentSlash(const char* aMsgId)
|
||||
{
|
||||
if (!mSyntaxHighlight) {
|
||||
return;
|
||||
}
|
||||
NS_PRECONDITION(mSlash, "Adding error to slash without one!");
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
NS_ASSERTION(treeOp, "Tree op allocation failed.");
|
||||
|
@ -313,11 +313,6 @@ class nsHtml5Highlighter
|
||||
*/
|
||||
nsHtml5UTF16Buffer* mBuffer;
|
||||
|
||||
/**
|
||||
* Whether to highlight syntax visibly initially.
|
||||
*/
|
||||
bool mSyntaxHighlight;
|
||||
|
||||
/**
|
||||
* The outgoing tree op queue.
|
||||
*/
|
||||
|
@ -15,8 +15,14 @@ nsHtml5ViewSourceUtils::NewBodyAttributes()
|
||||
nsString* id = new nsString(NS_LITERAL_STRING("viewsource"));
|
||||
bodyAttrs->addAttribute(nsHtml5AttributeName::ATTR_ID, id);
|
||||
|
||||
nsString* klass = new nsString();
|
||||
if (mozilla::Preferences::GetBool("view_source.wrap_long_lines", true)) {
|
||||
nsString* klass = new nsString(NS_LITERAL_STRING("wrap"));
|
||||
klass->Append(NS_LITERAL_STRING("wrap "));
|
||||
}
|
||||
if (mozilla::Preferences::GetBool("view_source.syntax_highlight", true)) {
|
||||
klass->Append(NS_LITERAL_STRING("highlight"));
|
||||
}
|
||||
if (!klass->IsEmpty()) {
|
||||
bodyAttrs->addAttribute(nsHtml5AttributeName::ATTR_CLASS, klass);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap"><pre id><span class="doctype"><!DOCTYPE html></span>
|
||||
<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap highlight"><pre id><span class="doctype"><!DOCTYPE html></span>
|
||||
<span id></span><span><<span class="start-tag">html</span>></span>
|
||||
<span id></span><span><<span class="start-tag">head</span>></span>
|
||||
<span id></span><span><<span class="start-tag">title</span>></span><span>Title</span><span></<span class="end-tag">title</span>></span>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap"><pre id><span class="pi"><?xml version="1.0" encoding="utf-8"?></span>
|
||||
<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap highlight"><pre id><span class="pi"><?xml version="1.0" encoding="utf-8"?></span>
|
||||
<span id></span><span class="pi"><?foo bar?></span>
|
||||
<span id></span><span><<span class="start-tag">html</span>></span>
|
||||
<span id></span><span><<span class="start-tag">head</span>></span>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap"><pre id><span class="doctype"><!DOCTYPE html></span>
|
||||
<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap highlight"><pre id><span class="doctype"><!DOCTYPE html></span>
|
||||
<span id></span>XX<span class="error">&</span>XX
|
||||
<span id></span>XX<span class="error">&</span>nXX
|
||||
<span id></span>XX<span class="error">&</span>noXX
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap"><pre id><span class="error comment"><!--></span> <span class="error comment"><!X></span>
|
||||
<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap highlight"><pre id><span class="error comment"><!--></span> <span class="error comment"><!X></span>
|
||||
<span id></span>
|
||||
</pre>
|
||||
<!-- View source CSS matches the <pre id> and <span id> elements and produces line numbers. -->
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap"><pre id><span class="doctype"><!DOCTYPE html></span><span>
|
||||
<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap highlight"><pre id><span class="doctype"><!DOCTYPE html></span><span>
|
||||
<span id></span></span><span><<span class="start-tag">body</span>></span><span>
|
||||
<span id></span></span><span><<span class="start-tag">script</span>></span><span></span><span></<span class="end-tag">script</span>></span><span>X
|
||||
<span id></span></span><span><<span class="start-tag">script</span>></span><span></span><span></<span class="end-tag">script</span> ></span><span>X
|
||||
|
@ -1,2 +1,2 @@
|
||||
<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" style="-moz-tab-size: 4"><pre id="line1"><span></span><span class="doctype"><!DOCTYPE html></span><span></span><span><<span class="start-tag">table</span>></span><span></span><span title="Start tag “input” seen in “table”." class="error"><<span class="start-tag">input</span> <span class="attribute-name">type</span>=<a class="attribute-value">hidden</a>></span><span></span><span></<span class="end-tag">table</span>></span><span>
|
||||
<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="highlight" style="-moz-tab-size: 4"><pre id="line1"><span></span><span class="doctype"><!DOCTYPE html></span><span></span><span><<span class="start-tag">table</span>></span><span></span><span title="Start tag “input” seen in “table”." class="error"><<span class="start-tag">input</span> <span class="attribute-name">type</span>=<a class="attribute-value">hidden</a>></span><span></span><span></<span class="end-tag">table</span>></span><span>
|
||||
<span id="line2"></span></span></pre></body></html>
|
||||
|
627
toolkit/components/viewsource/ViewSourceBrowser.jsm
Normal file
627
toolkit/components/viewsource/ViewSourceBrowser.jsm
Normal file
@ -0,0 +1,627 @@
|
||||
// -*- 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/. */
|
||||
|
||||
const { utils: Cu, interfaces: Ci, classes: Cc } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
|
||||
"resource://gre/modules/Deprecated.jsm");
|
||||
|
||||
const NS_XHTML = "http://www.w3.org/1999/xhtml";
|
||||
const VIEW_SOURCE_CSS = "resource://gre-resources/viewsource.css";
|
||||
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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["ViewSourceBrowser"];
|
||||
|
||||
/**
|
||||
* 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 window using viewSource.xul, the script viewSource.js in
|
||||
* the window extends an instance of this with more window specific functions.
|
||||
* The page script takes care of loading the companion frame script.
|
||||
*
|
||||
* 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 caller that manages
|
||||
* the <browser> is responsible for ensuring the companion frame script has been
|
||||
* loaded.
|
||||
*/
|
||||
this.ViewSourceBrowser = 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",
|
||||
],
|
||||
|
||||
/**
|
||||
* 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 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);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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);
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the nsIWebNavigation of the view source browser.
|
||||
*/
|
||||
get webNav() {
|
||||
return this.browser.webNavigation;
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for whether long lines should be wrapped.
|
||||
*/
|
||||
get wrapLongLines() {
|
||||
return Services.prefs.getBoolPref("view_source.wrap_long_lines");
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 the viewSource.xul window, this is called by onXULLoaded above.
|
||||
* 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) {
|
||||
// If we're dealing with a remote browser, then the browser
|
||||
// for view source needs to be remote as well.
|
||||
this.updateBrowserRemoteness(browser.isRemoteBrowser);
|
||||
} else {
|
||||
if (outerWindowID) {
|
||||
throw new Error("Must supply the browser if passing the outerWindowID");
|
||||
}
|
||||
}
|
||||
|
||||
this.sendAsyncMessage("ViewSource:LoadSource",
|
||||
{ URL, outerWindowID, lineNumber });
|
||||
},
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
updateBrowserRemoteness(shouldBeRemote) {
|
||||
if (this.browser.isRemoteBrowser != shouldBeRemote) {
|
||||
// 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");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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;
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the view source browser from a fragment of some document, as in
|
||||
* markups such as MathML where reformatting the output is helpful.
|
||||
*
|
||||
* @param aNode
|
||||
* Some element within the fragment of interest.
|
||||
* @param aContext
|
||||
* A string denoting the type of fragment. Currently, "mathml" is the
|
||||
* only accepted value.
|
||||
*/
|
||||
loadViewSourceFromFragment(node, context) {
|
||||
var Node = node.ownerDocument.defaultView.Node;
|
||||
this._lineCount = 0;
|
||||
this._startTargetLine = 0;
|
||||
this._endTargetLine = 0;
|
||||
this._targetNode = node;
|
||||
if (this._targetNode && this._targetNode.nodeType == Node.TEXT_NODE)
|
||||
this._targetNode = this._targetNode.parentNode;
|
||||
|
||||
// walk up the tree to the top-level element (e.g., <math>, <svg>)
|
||||
var topTag;
|
||||
if (context == "mathml")
|
||||
topTag = "math";
|
||||
else
|
||||
throw "not reached";
|
||||
var topNode = this._targetNode;
|
||||
while (topNode && topNode.localName != topTag)
|
||||
topNode = topNode.parentNode;
|
||||
if (!topNode)
|
||||
return;
|
||||
|
||||
// serialize
|
||||
var title = this.bundle.GetStringFromName("viewMathMLSourceTitle");
|
||||
var wrapClass = this.wrapLongLines ? ' class="wrap"' : '';
|
||||
var source =
|
||||
'<!DOCTYPE html>'
|
||||
+ '<html>'
|
||||
+ '<head><title>' + title + '</title>'
|
||||
+ '<link rel="stylesheet" type="text/css" href="' + VIEW_SOURCE_CSS + '">'
|
||||
+ '<style type="text/css">'
|
||||
+ '#target { border: dashed 1px; background-color: lightyellow; }'
|
||||
+ '</style>'
|
||||
+ '</head>'
|
||||
+ '<body id="viewsource"' + wrapClass
|
||||
+ ' onload="document.title=\''+title+'\'; document.getElementById(\'target\').scrollIntoView(true)">'
|
||||
+ '<pre>'
|
||||
+ this._getOuterMarkup(topNode, 0)
|
||||
+ '</pre></body></html>'
|
||||
; // end
|
||||
|
||||
// display
|
||||
this.browser.loadURI("data:text/html;charset=utf-8," +
|
||||
encodeURIComponent(source));
|
||||
},
|
||||
|
||||
_getInnerMarkup(node, indent) {
|
||||
var str = '';
|
||||
for (var i = 0; i < node.childNodes.length; i++) {
|
||||
str += this._getOuterMarkup(node.childNodes.item(i), indent);
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
_getOuterMarkup(node, indent) {
|
||||
var Node = node.ownerDocument.defaultView.Node;
|
||||
var newline = "";
|
||||
var padding = "";
|
||||
var str = "";
|
||||
if (node == this._targetNode) {
|
||||
this._startTargetLine = this._lineCount;
|
||||
str += '</pre><pre id="target">';
|
||||
}
|
||||
|
||||
switch (node.nodeType) {
|
||||
case Node.ELEMENT_NODE: // Element
|
||||
// to avoid the wide gap problem, '\n' is not emitted on the first
|
||||
// line and the lines before & after the <pre id="target">...</pre>
|
||||
if (this._lineCount > 0 &&
|
||||
this._lineCount != this._startTargetLine &&
|
||||
this._lineCount != this._endTargetLine) {
|
||||
newline = "\n";
|
||||
}
|
||||
this._lineCount++;
|
||||
for (var k = 0; k < indent; k++) {
|
||||
padding += " ";
|
||||
}
|
||||
str += newline + padding
|
||||
+ '<<span class="start-tag">' + node.nodeName + '</span>';
|
||||
for (var i = 0; i < node.attributes.length; i++) {
|
||||
var attr = node.attributes.item(i);
|
||||
if (attr.nodeName.match(/^[-_]moz/)) {
|
||||
continue;
|
||||
}
|
||||
str += ' <span class="attribute-name">'
|
||||
+ attr.nodeName
|
||||
+ '</span>=<span class="attribute-value">"'
|
||||
+ this._unicodeToEntity(attr.nodeValue)
|
||||
+ '"</span>';
|
||||
}
|
||||
if (!node.hasChildNodes()) {
|
||||
str += "/>";
|
||||
}
|
||||
else {
|
||||
str += ">";
|
||||
var oldLine = this._lineCount;
|
||||
str += this._getInnerMarkup(node, indent + 2);
|
||||
if (oldLine == this._lineCount) {
|
||||
newline = "";
|
||||
padding = "";
|
||||
}
|
||||
else {
|
||||
newline = (this._lineCount == this._endTargetLine) ? "" : "\n";
|
||||
this._lineCount++;
|
||||
}
|
||||
str += newline + padding
|
||||
+ '</<span class="end-tag">' + node.nodeName + '</span>>';
|
||||
}
|
||||
break;
|
||||
case Node.TEXT_NODE: // Text
|
||||
var tmp = node.nodeValue;
|
||||
tmp = tmp.replace(/(\n|\r|\t)+/g, " ");
|
||||
tmp = tmp.replace(/^ +/, "");
|
||||
tmp = tmp.replace(/ +$/, "");
|
||||
if (tmp.length != 0) {
|
||||
str += '<span class="text">' + this._unicodeToEntity(tmp) + '</span>';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (node == this._targetNode) {
|
||||
this._endTargetLine = this._lineCount;
|
||||
str += '</pre><pre>';
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
_unicodeToEntity(text) {
|
||||
const charTable = {
|
||||
'&': '&<span class="entity">amp;</span>',
|
||||
'<': '&<span class="entity">lt;</span>',
|
||||
'>': '&<span class="entity">gt;</span>',
|
||||
'"': '&<span class="entity">quot;</span>'
|
||||
};
|
||||
|
||||
function charTableLookup(letter) {
|
||||
return charTable[letter];
|
||||
}
|
||||
|
||||
function convertEntity(letter) {
|
||||
try {
|
||||
var unichar = this._entityConverter
|
||||
.ConvertToEntity(letter, entityVersion);
|
||||
var entity = unichar.substring(1); // extract '&'
|
||||
return '&<span class="entity">' + entity + '</span>';
|
||||
} catch (ex) {
|
||||
return letter;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._entityConverter) {
|
||||
try {
|
||||
this._entityConverter = Cc["@mozilla.org/intl/entityconverter;1"]
|
||||
.createInstance(Ci.nsIEntityConverter);
|
||||
} catch(e) { }
|
||||
}
|
||||
|
||||
const entityVersion = Ci.nsIEntityConverter.entityW3C;
|
||||
|
||||
var str = text;
|
||||
|
||||
// replace chars in our charTable
|
||||
str = str.replace(/[<>&"]/g, charTableLookup);
|
||||
|
||||
// replace chars > 0x7f via nsIEntityConverter
|
||||
str = str.replace(/[^\0-\u007f]/g, convertEntity);
|
||||
|
||||
return str;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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();
|
||||
},
|
||||
};
|
@ -6,479 +6,20 @@
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var gDebug = 0;
|
||||
var gLineCount = 0;
|
||||
var gStartTargetLine = 0;
|
||||
var gEndTargetLine = 0;
|
||||
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()
|
||||
{
|
||||
function onLoadViewPartialSource() {
|
||||
// check the view_source.wrap_long_lines pref
|
||||
// and set the menuitem's checked attribute accordingly
|
||||
gWrapLongLines = Services.prefs.getBoolPref("view_source.wrap_long_lines");
|
||||
document.getElementById("menu_wrapLongLines").setAttribute("checked", gWrapLongLines);
|
||||
let wrapLongLines = Services.prefs.getBoolPref("view_source.wrap_long_lines");
|
||||
document.getElementById("menu_wrapLongLines")
|
||||
.setAttribute("checked", wrapLongLines);
|
||||
document.getElementById("menu_highlightSyntax")
|
||||
.setAttribute("checked",
|
||||
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]);
|
||||
|
||||
gBrowser.droppedLinkHandler = function (event, url, name) {
|
||||
viewSource(url)
|
||||
event.preventDefault();
|
||||
}
|
||||
viewSourceChrome.loadViewSourceFromFragment(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
|
||||
function viewPartialSourceForFragment(node, context)
|
||||
{
|
||||
gTargetNode = node;
|
||||
if (gTargetNode && gTargetNode.nodeType == Node.TEXT_NODE)
|
||||
gTargetNode = gTargetNode.parentNode;
|
||||
|
||||
// walk up the tree to the top-level element (e.g., <math>, <svg>)
|
||||
var topTag;
|
||||
if (context == 'mathml')
|
||||
topTag = 'math';
|
||||
else
|
||||
throw 'not reached';
|
||||
var topNode = gTargetNode;
|
||||
while (topNode && topNode.localName != topTag)
|
||||
topNode = topNode.parentNode;
|
||||
if (!topNode)
|
||||
return;
|
||||
|
||||
// serialize
|
||||
var title = gViewSourceBundle.getString("viewMathMLSourceTitle");
|
||||
var wrapClass = gWrapLongLines ? ' class="wrap"' : '';
|
||||
var source =
|
||||
'<!DOCTYPE html>'
|
||||
+ '<html>'
|
||||
+ '<head><title>' + title + '</title>'
|
||||
+ '<link rel="stylesheet" type="text/css" href="' + gViewSourceCSS + '">'
|
||||
+ '<style type="text/css">'
|
||||
+ '#target { border: dashed 1px; background-color: lightyellow; }'
|
||||
+ '</style>'
|
||||
+ '</head>'
|
||||
+ '<body id="viewsource"' + wrapClass
|
||||
+ ' onload="document.title=\''+title+'\';document.getElementById(\'target\').scrollIntoView(true)">'
|
||||
+ '<pre>'
|
||||
+ getOuterMarkup(topNode, 0)
|
||||
+ '</pre></body></html>'
|
||||
; // end
|
||||
|
||||
// display
|
||||
gBrowser.loadURI("data:text/html;charset=utf-8," + encodeURIComponent(source));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
function getInnerMarkup(node, indent) {
|
||||
var str = '';
|
||||
for (var i = 0; i < node.childNodes.length; i++) {
|
||||
str += getOuterMarkup(node.childNodes.item(i), indent);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
function getOuterMarkup(node, indent) {
|
||||
var newline = '';
|
||||
var padding = '';
|
||||
var str = '';
|
||||
if (node == gTargetNode) {
|
||||
gStartTargetLine = gLineCount;
|
||||
str += '</pre><pre id="target">';
|
||||
}
|
||||
|
||||
switch (node.nodeType) {
|
||||
case Node.ELEMENT_NODE: // Element
|
||||
// to avoid the wide gap problem, '\n' is not emitted on the first
|
||||
// line and the lines before & after the <pre id="target">...</pre>
|
||||
if (gLineCount > 0 &&
|
||||
gLineCount != gStartTargetLine &&
|
||||
gLineCount != gEndTargetLine) {
|
||||
newline = '\n';
|
||||
}
|
||||
gLineCount++;
|
||||
if (gDebug) {
|
||||
newline += gLineCount;
|
||||
}
|
||||
for (var k = 0; k < indent; k++) {
|
||||
padding += ' ';
|
||||
}
|
||||
str += newline + padding
|
||||
+ '<<span class="start-tag">' + node.nodeName + '</span>';
|
||||
for (var i = 0; i < node.attributes.length; i++) {
|
||||
var attr = node.attributes.item(i);
|
||||
if (!gDebug && attr.nodeName.match(/^[-_]moz/)) {
|
||||
continue;
|
||||
}
|
||||
str += ' <span class="attribute-name">'
|
||||
+ attr.nodeName
|
||||
+ '</span>=<span class="attribute-value">"'
|
||||
+ unicodeTOentity(attr.nodeValue)
|
||||
+ '"</span>';
|
||||
}
|
||||
if (!node.hasChildNodes()) {
|
||||
str += '/>';
|
||||
}
|
||||
else {
|
||||
str += '>';
|
||||
var oldLine = gLineCount;
|
||||
str += getInnerMarkup(node, indent + 2);
|
||||
if (oldLine == gLineCount) {
|
||||
newline = '';
|
||||
padding = '';
|
||||
}
|
||||
else {
|
||||
newline = (gLineCount == gEndTargetLine) ? '' : '\n';
|
||||
gLineCount++;
|
||||
if (gDebug) {
|
||||
newline += gLineCount;
|
||||
}
|
||||
}
|
||||
str += newline + padding
|
||||
+ '</<span class="end-tag">' + node.nodeName + '</span>>';
|
||||
}
|
||||
break;
|
||||
case Node.TEXT_NODE: // Text
|
||||
var tmp = node.nodeValue;
|
||||
tmp = tmp.replace(/(\n|\r|\t)+/g, " ");
|
||||
tmp = tmp.replace(/^ +/, "");
|
||||
tmp = tmp.replace(/ +$/, "");
|
||||
if (tmp.length != 0) {
|
||||
str += '<span class="text">' + unicodeTOentity(tmp) + '</span>';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (node == gTargetNode) {
|
||||
gEndTargetLine = gLineCount;
|
||||
str += '</pre><pre>';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
function unicodeTOentity(text)
|
||||
{
|
||||
const charTable = {
|
||||
'&': '&<span class="entity">amp;</span>',
|
||||
'<': '&<span class="entity">lt;</span>',
|
||||
'>': '&<span class="entity">gt;</span>',
|
||||
'"': '&<span class="entity">quot;</span>'
|
||||
};
|
||||
|
||||
function charTableLookup(letter) {
|
||||
return charTable[letter];
|
||||
}
|
||||
|
||||
function convertEntity(letter) {
|
||||
try {
|
||||
var unichar = gEntityConverter.ConvertToEntity(letter, entityVersion);
|
||||
var entity = unichar.substring(1); // extract '&'
|
||||
return '&<span class="entity">' + entity + '</span>';
|
||||
} catch (ex) {
|
||||
return letter;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gEntityConverter) {
|
||||
try {
|
||||
gEntityConverter =
|
||||
Components.classes["@mozilla.org/intl/entityconverter;1"]
|
||||
.createInstance(Components.interfaces.nsIEntityConverter);
|
||||
} catch(e) { }
|
||||
}
|
||||
|
||||
const entityVersion = Components.interfaces.nsIEntityConverter.entityW3C;
|
||||
|
||||
var str = text;
|
||||
|
||||
// replace chars in our charTable
|
||||
str = str.replace(/[<>&"]/g, charTableLookup);
|
||||
|
||||
// replace chars > 0x7f via nsIEntityConverter
|
||||
str = str.replace(/[^\0-\u007f]/g, convertEntity);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
@ -4,8 +4,8 @@
|
||||
# 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/.
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://global/content/viewSource.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://global/content/viewSource.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mozapps/skin/viewsource/viewsource.css" type="text/css"?>
|
||||
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
|
||||
|
||||
@ -20,10 +20,10 @@
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="onLoadViewPartialSource();"
|
||||
contenttitlesetting="true"
|
||||
title="&mainWindow.title;"
|
||||
titlemodifier="&mainWindow.titlemodifier;"
|
||||
title="&mainWindow.title;"
|
||||
titlemodifier="&mainWindow.titlemodifier;"
|
||||
titlepreface=""
|
||||
titlemenuseparator ="&mainWindow.titlemodifierseparator;"
|
||||
titlemenuseparator ="&mainWindow.titlemodifierseparator;"
|
||||
windowtype="navigator:view-source"
|
||||
width="500" height="300"
|
||||
screenX="10" screenY="10"
|
||||
@ -50,9 +50,9 @@
|
||||
oncommand="document.getElementById('FindToolbar').onFindAgainCommand(false);"/>
|
||||
<command id="cmd_findPrevious"
|
||||
oncommand="document.getElementById('FindToolbar').onFindAgainCommand(true);"/>
|
||||
<command id="cmd_goToLine" oncommand="ViewSourceChrome.promptAndGoToLine();" disabled="true"/>
|
||||
<command id="cmd_highlightSyntax" oncommand="ViewSourceChrome.toggleSyntaxHighlighting();"/>
|
||||
<command id="cmd_wrapLongLines" oncommand="ViewSourceChrome.toggleWrapping();"/>
|
||||
<command id="cmd_goToLine" oncommand="viewSourceChrome.promptAndGoToLine();" disabled="true"/>
|
||||
<command id="cmd_highlightSyntax" oncommand="viewSourceChrome.toggleSyntaxHighlighting();"/>
|
||||
<command id="cmd_wrapLongLines" oncommand="viewSourceChrome.toggleWrapping();"/>
|
||||
<command id="cmd_textZoomReduce" oncommand="ZoomManager.reduce();"/>
|
||||
<command id="cmd_textZoomEnlarge" oncommand="ZoomManager.enlarge();"/>
|
||||
<command id="cmd_textZoomReset" oncommand="ZoomManager.reset();"/>
|
||||
@ -80,16 +80,16 @@
|
||||
<menuitem id="context-copyLink"
|
||||
label="©LinkCmd.label;"
|
||||
accesskey="©LinkCmd.accesskey;"
|
||||
oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
|
||||
oncommand="viewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
|
||||
<menuitem id="context-copyEmail"
|
||||
label="©EmailCmd.label;"
|
||||
accesskey="©EmailCmd.accesskey;"
|
||||
oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
|
||||
oncommand="viewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="cMenu_selectAll"/>
|
||||
</menupopup>
|
||||
|
||||
<!-- Menu -->
|
||||
<!-- Menu -->
|
||||
<toolbox id="viewSource-toolbox">
|
||||
<menubar id="viewSource-main-menubar">
|
||||
|
||||
@ -131,11 +131,11 @@
|
||||
<menu id="menu_view" label="&viewMenu.label;" accesskey="&viewMenu.accesskey;">
|
||||
<menupopup id="viewmenu-popup">
|
||||
<menu id="viewTextZoomMenu" label="&menu_textSize.label;" accesskey="&menu_textSize.accesskey;">
|
||||
<menupopup>
|
||||
<menuitem id="menu_textEnlarge" command="cmd_textZoomEnlarge"
|
||||
<menupopup>
|
||||
<menuitem id="menu_textEnlarge" command="cmd_textZoomEnlarge"
|
||||
label="&menu_textEnlarge.label;" accesskey="&menu_textEnlarge.accesskey;"
|
||||
key="key_textZoomEnlarge"/>
|
||||
<menuitem id="menu_textReduce" command="cmd_textZoomReduce"
|
||||
<menuitem id="menu_textReduce" command="cmd_textZoomReduce"
|
||||
label="&menu_textReduce.label;" accesskey="&menu_textReduce.accesskey;"
|
||||
key="key_textZoomReduce"/>
|
||||
<menuseparator/>
|
||||
@ -151,7 +151,7 @@
|
||||
label="&menu_highlightSyntax.label;" accesskey="&menu_highlightSyntax.accesskey;"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
</menubar>
|
||||
</menubar>
|
||||
</toolbox>
|
||||
|
||||
<vbox id="appcontent" flex="1">
|
||||
|
@ -12,8 +12,17 @@ XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
|
||||
"resource://gre/modules/DeferredTask.jsm");
|
||||
|
||||
const NS_XHTML = "http://www.w3.org/1999/xhtml";
|
||||
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 +48,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
|
||||
@ -51,6 +68,17 @@ let ViewSourceContent = {
|
||||
*/
|
||||
selectionListenerAttached: false,
|
||||
|
||||
get isViewSource() {
|
||||
let uri = content.document.documentURI;
|
||||
return uri.startsWith("view-source:") ||
|
||||
(uri.startsWith("data:") && uri.includes("MathML"));
|
||||
},
|
||||
|
||||
get isAboutBlank() {
|
||||
let uri = content.document.documentURI;
|
||||
return uri == "about:blank";
|
||||
},
|
||||
|
||||
/**
|
||||
* This should be called as soon as this frame script has loaded.
|
||||
*/
|
||||
@ -93,6 +121,9 @@ let ViewSourceContent = {
|
||||
* get dispatched to a specific function for the message name.
|
||||
*/
|
||||
receiveMessage(msg) {
|
||||
if (!this.isViewSource && !this.isAboutBlank) {
|
||||
return;
|
||||
}
|
||||
let data = msg.data;
|
||||
let objects = msg.objects;
|
||||
switch(msg.name) {
|
||||
@ -116,6 +147,9 @@ let ViewSourceContent = {
|
||||
case "ViewSource:SetCharacterSet":
|
||||
this.setCharacterSet(data.charset, data.doPageLoad);
|
||||
break;
|
||||
case "ViewSource:ScheduleDrawSelection":
|
||||
this.scheduleDrawSelection();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@ -124,6 +158,9 @@ let ViewSourceContent = {
|
||||
* a specific function for the event type.
|
||||
*/
|
||||
handleEvent(event) {
|
||||
if (!this.isViewSource) {
|
||||
return;
|
||||
}
|
||||
switch(event.type) {
|
||||
case "pagehide":
|
||||
this.onPageHide(event);
|
||||
@ -161,6 +198,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.
|
||||
*
|
||||
@ -250,8 +295,13 @@ let ViewSourceContent = {
|
||||
docShell.charset = forcedCharSet;
|
||||
}
|
||||
|
||||
if (lineNumber) {
|
||||
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);
|
||||
};
|
||||
@ -300,16 +350,28 @@ let ViewSourceContent = {
|
||||
},
|
||||
|
||||
/**
|
||||
* This handler is specifically for click events bubbling up from
|
||||
* error page content, which can show up if the user attempts to
|
||||
* view the source of an attack page.
|
||||
* This handler is for click events from:
|
||||
* * error page content, which can show up if the user attempts to view the
|
||||
* source of an attack page.
|
||||
* * in-page context menu actions
|
||||
*/
|
||||
onClick(event) {
|
||||
let target = event.originalTarget;
|
||||
// Check for content menu actions
|
||||
if (target.id) {
|
||||
this.contextMenuItems.forEach(itemSpec => {
|
||||
if (itemSpec.id !== target.id) {
|
||||
return;
|
||||
}
|
||||
itemSpec.handler.call(this, event);
|
||||
event.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
// Don't trust synthetic events
|
||||
if (!event.isTrusted || event.target.localName != "button")
|
||||
return;
|
||||
|
||||
let target = event.originalTarget;
|
||||
let errorDoc = target.ownerDocument;
|
||||
|
||||
if (/^about:blocked/.test(errorDoc.documentURI)) {
|
||||
@ -341,12 +403,26 @@ let ViewSourceContent = {
|
||||
* The pageshow event being handled.
|
||||
*/
|
||||
onPageShow(event) {
|
||||
content.getSelection()
|
||||
.QueryInterface(Ci.nsISelectionPrivate)
|
||||
.addSelectionListener(this);
|
||||
this.selectionListenerAttached = true;
|
||||
|
||||
let selection = content.getSelection();
|
||||
if (selection) {
|
||||
selection.QueryInterface(Ci.nsISelectionPrivate)
|
||||
.addSelectionListener(this);
|
||||
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();
|
||||
}
|
||||
|
||||
if (content.document.body) {
|
||||
this.injectContextMenu();
|
||||
}
|
||||
|
||||
sendAsyncMessage("ViewSource:SourceLoaded");
|
||||
},
|
||||
|
||||
@ -604,13 +680,12 @@ let ViewSourceContent = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the parent has changed the syntax highlighting pref.
|
||||
* Toggles the "highlight" class on the document body, which sets whether
|
||||
* or not syntax highlighting is displayed.
|
||||
*/
|
||||
toggleSyntaxHighlighting() {
|
||||
// The parent process should have set the view_source.syntax_highlight
|
||||
// pref to the desired value. The reload brings that setting into
|
||||
// effect.
|
||||
this.reload();
|
||||
let body = content.document.body;
|
||||
body.classList.toggle("highlight");
|
||||
},
|
||||
|
||||
/**
|
||||
@ -699,5 +774,184 @@ 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;
|
||||
},
|
||||
|
||||
/**
|
||||
* In-page context menu items that are injected after page load.
|
||||
*/
|
||||
contextMenuItems: [
|
||||
{
|
||||
id: "goToLine",
|
||||
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 menu = doc.createElementNS(NS_XHTML, "menu");
|
||||
menu.setAttribute("type", "context");
|
||||
menu.setAttribute("id", "actions");
|
||||
doc.body.appendChild(menu);
|
||||
doc.body.setAttribute("contextmenu", "actions");
|
||||
|
||||
this.contextMenuItems.forEach(itemSpec => {
|
||||
let item = doc.createElementNS(NS_XHTML, "menuitem");
|
||||
item.setAttribute("id", itemSpec.id);
|
||||
let labelName = `context_${itemSpec.id}_label`;
|
||||
let label = this.bundle.GetStringFromName(labelName);
|
||||
item.setAttribute("label", label);
|
||||
if ("checked" in itemSpec) {
|
||||
item.setAttribute("type", "checkbox");
|
||||
}
|
||||
menu.appendChild(item);
|
||||
});
|
||||
|
||||
this.updateContextMenu();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update state of checkbox-style context menu items.
|
||||
*/
|
||||
updateContextMenu() {
|
||||
let doc = content.document;
|
||||
this.contextMenuItems.forEach(itemSpec => {
|
||||
if (!("checked" in itemSpec)) {
|
||||
return;
|
||||
}
|
||||
let item = doc.getElementById(itemSpec.id);
|
||||
if (itemSpec.checked) {
|
||||
item.setAttribute("checked", true);
|
||||
} else {
|
||||
item.removeAttribute("checked");
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
ViewSourceContent.init();
|
||||
|
@ -7,6 +7,7 @@
|
||||
const { utils: Cu, interfaces: Ci, classes: Cc } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/ViewSourceBrowser.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
@ -31,15 +32,24 @@ XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
|
||||
|
||||
/**
|
||||
* ViewSourceChrome is the primary interface for interacting with
|
||||
* the view source browser. It initializes itself on script load.
|
||||
* the view source browser from a self-contained window. It extends
|
||||
* ViewSourceBrowser with additional things needed inside the special window.
|
||||
*
|
||||
* It initializes itself on script load.
|
||||
*/
|
||||
let ViewSourceChrome = {
|
||||
function ViewSourceChrome() {
|
||||
ViewSourceBrowser.call(this);
|
||||
}
|
||||
|
||||
ViewSourceChrome.prototype = {
|
||||
__proto__: ViewSourceBrowser.prototype,
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* The <browser> that will be displaying the view source content.
|
||||
*/
|
||||
lastLineFound: null,
|
||||
get browser() {
|
||||
return gBrowser;
|
||||
},
|
||||
|
||||
/**
|
||||
* The context menu, when opened from the content process, sends
|
||||
@ -55,31 +65,22 @@ let ViewSourceChrome = {
|
||||
* will automatically have ViewSourceChrome listen for those messages,
|
||||
* and remove the listeners on teardown.
|
||||
*/
|
||||
messages: [
|
||||
messages: ViewSourceBrowser.prototype.messages.concat([
|
||||
"ViewSource:SourceLoaded",
|
||||
"ViewSource:SourceUnloaded",
|
||||
"ViewSource:Close",
|
||||
"ViewSource:OpenURL",
|
||||
"ViewSource:GoToLine:Success",
|
||||
"ViewSource:GoToLine:Failed",
|
||||
"ViewSource:UpdateStatus",
|
||||
"ViewSource:ContextMenuOpening",
|
||||
],
|
||||
]),
|
||||
|
||||
/**
|
||||
* This should be called as soon as the script loads. When this function
|
||||
* executes, we can assume the DOM content has not yet loaded.
|
||||
* This called via ViewSourceBrowser's constructor. 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() {
|
||||
// We use the window message manager so that if we switch remoteness of the
|
||||
// browser (which we might do if we're attempting to load the document
|
||||
// source out of the network cache), we automatically re-load the frame
|
||||
// script.
|
||||
let wMM = window.messageManager;
|
||||
wMM.loadFrameScript("chrome://global/content/viewSource-content.js", true);
|
||||
this.messages.forEach((msgName) => {
|
||||
wMM.addMessageListener(msgName, this);
|
||||
});
|
||||
this.mm.loadFrameScript("chrome://global/content/viewSource-content.js", true);
|
||||
|
||||
this.shouldWrap = Services.prefs.getBoolPref("view_source.wrap_long_lines");
|
||||
this.shouldHighlight =
|
||||
@ -89,6 +90,8 @@ let ViewSourceChrome = {
|
||||
addEventListener("unload", this);
|
||||
addEventListener("AppCommand", this, true);
|
||||
addEventListener("MozSwipeGesture", this, true);
|
||||
|
||||
ViewSourceBrowser.prototype.init.call(this);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -96,10 +99,7 @@ let ViewSourceChrome = {
|
||||
* clean up event and message listeners.
|
||||
*/
|
||||
uninit() {
|
||||
let wMM = window.messageManager;
|
||||
this.messages.forEach((msgName) => {
|
||||
wMM.removeMessageListener(msgName, this);
|
||||
});
|
||||
ViewSourceBrowser.prototype.uninit.call(this);
|
||||
|
||||
// "load" event listener is removed in its handler, to
|
||||
// ensure we only fire it once.
|
||||
@ -108,8 +108,9 @@ let ViewSourceChrome = {
|
||||
removeEventListener("MozSwipeGesture", this, true);
|
||||
gContextMenu.removeEventListener("popupshowing", this);
|
||||
gContextMenu.removeEventListener("popuphidden", this);
|
||||
Services.els.removeSystemEventListener(gBrowser, "dragover", this, true);
|
||||
Services.els.removeSystemEventListener(gBrowser, "drop", this, true);
|
||||
Services.els.removeSystemEventListener(this.browser, "dragover", this,
|
||||
true);
|
||||
Services.els.removeSystemEventListener(this.browser, "drop", this, true);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -120,6 +121,17 @@ let ViewSourceChrome = {
|
||||
let data = message.data;
|
||||
|
||||
switch(message.name) {
|
||||
// Begin messages from super class
|
||||
case "ViewSource:PromptAndGoToLine":
|
||||
this.promptAndGoToLine();
|
||||
break;
|
||||
case "ViewSource:GoToLine:Success":
|
||||
this.onGoToLineSuccess(data.lineNumber);
|
||||
break;
|
||||
case "ViewSource:GoToLine:Failed":
|
||||
this.onGoToLineFailed();
|
||||
break;
|
||||
// End messages from super class
|
||||
case "ViewSource:SourceLoaded":
|
||||
this.onSourceLoaded();
|
||||
break;
|
||||
@ -132,18 +144,12 @@ let ViewSourceChrome = {
|
||||
case "ViewSource:OpenURL":
|
||||
this.openURL(data.URL);
|
||||
break;
|
||||
case "ViewSource:GoToLine:Failed":
|
||||
this.onGoToLineFailed();
|
||||
break;
|
||||
case "ViewSource:GoToLine:Success":
|
||||
this.onGoToLineSuccess(data.lineNumber);
|
||||
break;
|
||||
case "ViewSource:UpdateStatus":
|
||||
this.updateStatus(data.label);
|
||||
break;
|
||||
case "ViewSource:ContextMenuOpening":
|
||||
this.onContextMenuOpening(data.isLink, data.isEmail, data.href);
|
||||
if (gBrowser.isRemoteBrowser) {
|
||||
if (this.browser.isRemoteBrowser) {
|
||||
this.openContextMenu(data.screenX, data.screenY);
|
||||
}
|
||||
break;
|
||||
@ -188,35 +194,35 @@ let ViewSourceChrome = {
|
||||
* has history enabled on it.
|
||||
*/
|
||||
get historyEnabled() {
|
||||
return !gBrowser.hasAttribute("disablehistory");
|
||||
return !this.browser.hasAttribute("disablehistory");
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the message manager of the view source browser.
|
||||
* Getter for the message manager used to communicate with the view source
|
||||
* browser.
|
||||
*
|
||||
* In this window version of view source, we use the window message manager
|
||||
* for loading scripts and listening for messages so that if we switch
|
||||
* remoteness of the browser (which we might do if we're attempting to load
|
||||
* the document source out of the network cache), we automatically re-load
|
||||
* the frame script.
|
||||
*/
|
||||
get mm() {
|
||||
return gBrowser.messageManager;
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the nsIWebNavigation of the view source browser.
|
||||
*/
|
||||
get webNav() {
|
||||
return gBrowser.webNavigation;
|
||||
return window.messageManager;
|
||||
},
|
||||
|
||||
/**
|
||||
* Send the browser forward in its history.
|
||||
*/
|
||||
goForward() {
|
||||
gBrowser.goForward();
|
||||
this.browser.goForward();
|
||||
},
|
||||
|
||||
/**
|
||||
* Send the browser backward in its history.
|
||||
*/
|
||||
goBack() {
|
||||
gBrowser.goBack();
|
||||
this.browser.goBack();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -267,8 +273,8 @@ let ViewSourceChrome = {
|
||||
gContextMenu.addEventListener("popupshowing", this);
|
||||
gContextMenu.addEventListener("popuphidden", this);
|
||||
|
||||
Services.els.addSystemEventListener(gBrowser, "dragover", this, true);
|
||||
Services.els.addSystemEventListener(gBrowser, "drop", this, true);
|
||||
Services.els.addSystemEventListener(this.browser, "dragover", this, true);
|
||||
Services.els.addSystemEventListener(this.browser, "drop", this, true);
|
||||
|
||||
if (!this.historyEnabled) {
|
||||
// Disable the BACK and FORWARD commands and hide the related menu items.
|
||||
@ -279,12 +285,6 @@ let ViewSourceChrome = {
|
||||
}
|
||||
}
|
||||
|
||||
// This will only work with non-remote browsers. See bug 1158377.
|
||||
gBrowser.droppedLinkHandler = function (event, url, name) {
|
||||
ViewSourceChrome.loadURL(url);
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
// We require the first argument to do any loading of source.
|
||||
// otherwise, we're done.
|
||||
if (!window.arguments[0]) {
|
||||
@ -293,39 +293,20 @@ let ViewSourceChrome = {
|
||||
|
||||
if (typeof window.arguments[0] == "string") {
|
||||
// We're using the deprecated API
|
||||
return ViewSourceChrome._loadViewSourceDeprecated();
|
||||
return this._loadViewSourceDeprecated(window.arguments);
|
||||
}
|
||||
|
||||
// We're using the modern API, which allows us to view the
|
||||
// source of documents from out of process browsers.
|
||||
let args = window.arguments[0];
|
||||
|
||||
if (!args.URL) {
|
||||
throw new Error("Must supply a URL when opening view source.");
|
||||
}
|
||||
|
||||
if (args.browser) {
|
||||
// If we're dealing with a remote browser, then the browser
|
||||
// for view source needs to be remote as well.
|
||||
this.updateBrowserRemoteness(args.browser.isRemoteBrowser);
|
||||
} else {
|
||||
if (args.outerWindowID) {
|
||||
throw new Error("Must supply the browser if passing the outerWindowID");
|
||||
}
|
||||
}
|
||||
|
||||
this.mm.sendAsyncMessage("ViewSource:LoadSource", {
|
||||
URL: args.URL,
|
||||
outerWindowID: args.outerWindowID,
|
||||
lineNumber: args.lineNumber,
|
||||
});
|
||||
this.loadViewSource(args);
|
||||
},
|
||||
|
||||
/**
|
||||
* This is the deprecated API for viewSource.xul, for old-timer consumers.
|
||||
* This API might eventually go away.
|
||||
*/
|
||||
_loadViewSourceDeprecated() {
|
||||
_loadViewSourceDeprecated(aArguments) {
|
||||
Deprecated.warning("The arguments you're passing to viewSource.xul " +
|
||||
"are using an out-of-date API.",
|
||||
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
|
||||
@ -336,34 +317,34 @@ let ViewSourceChrome = {
|
||||
// arg[3] - Line number to go to.
|
||||
// arg[4] - Whether charset was forced by the user
|
||||
|
||||
if (window.arguments[3] == "selection" ||
|
||||
window.arguments[3] == "mathml") {
|
||||
if (aArguments[3] == "selection" ||
|
||||
aArguments[3] == "mathml") {
|
||||
// viewPartialSource.js will take care of loading the content.
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.arguments[2]) {
|
||||
let pageDescriptor = window.arguments[2];
|
||||
if (aArguments[2]) {
|
||||
let pageDescriptor = aArguments[2];
|
||||
if (Cu.isCrossProcessWrapper(pageDescriptor)) {
|
||||
throw new Error("Cannot pass a CPOW as the page descriptor to viewSource.xul.");
|
||||
}
|
||||
}
|
||||
|
||||
if (gBrowser.isRemoteBrowser) {
|
||||
if (this.browser.isRemoteBrowser) {
|
||||
throw new Error("Deprecated view source API should not use a remote browser.");
|
||||
}
|
||||
|
||||
let forcedCharSet;
|
||||
if (window.arguments[4] && window.arguments[1].startsWith("charset=")) {
|
||||
forcedCharSet = window.arguments[1].split("=")[1];
|
||||
if (aArguments[4] && aArguments[1].startsWith("charset=")) {
|
||||
forcedCharSet = aArguments[1].split("=")[1];
|
||||
}
|
||||
|
||||
gBrowser.messageManager.sendAsyncMessage("ViewSource:LoadSourceDeprecated", {
|
||||
URL: window.arguments[0],
|
||||
lineNumber: window.arguments[3],
|
||||
this.sendAsyncMessage("ViewSource:LoadSourceDeprecated", {
|
||||
URL: aArguments[0],
|
||||
lineNumber: aArguments[3],
|
||||
forcedCharSet,
|
||||
}, {
|
||||
pageDescriptor: window.arguments[2],
|
||||
pageDescriptor: aArguments[2],
|
||||
});
|
||||
},
|
||||
|
||||
@ -420,7 +401,7 @@ let ViewSourceChrome = {
|
||||
this.updateCommands();
|
||||
}
|
||||
|
||||
gBrowser.focus();
|
||||
this.browser.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -446,13 +427,14 @@ let ViewSourceChrome = {
|
||||
|
||||
// If we don't have history enabled, we have to do a reload in order to
|
||||
// show the character set change. See bug 136322.
|
||||
this.mm.sendAsyncMessage("ViewSource:SetCharacterSet", {
|
||||
this.sendAsyncMessage("ViewSource:SetCharacterSet", {
|
||||
charset: charset,
|
||||
doPageLoad: this.historyEnabled,
|
||||
});
|
||||
|
||||
if (this.historyEnabled) {
|
||||
gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
|
||||
if (!this.historyEnabled) {
|
||||
this.browser
|
||||
.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -583,7 +565,7 @@ let ViewSourceChrome = {
|
||||
* A URL string to be opened in the view source browser.
|
||||
*/
|
||||
loadURL(URL) {
|
||||
this.mm.sendAsyncMessage("ViewSource:LoadSource", { URL });
|
||||
this.sendAsyncMessage("ViewSource:LoadSource", { URL });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -618,47 +600,6 @@ let ViewSourceChrome = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 ok = Services.prompt.prompt(
|
||||
window,
|
||||
gViewSourceBundle.getString("goToLineTitle"),
|
||||
gViewSourceBundle.getString("goToLineText"),
|
||||
input,
|
||||
null,
|
||||
{value:0});
|
||||
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
let line = parseInt(input.value, 10);
|
||||
|
||||
if (!(line > 0)) {
|
||||
Services.prompt.alert(window,
|
||||
gViewSourceBundle.getString("invalidInputTitle"),
|
||||
gViewSourceBundle.getString("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.mm.sendAsyncMessage("ViewSource:GoToLine", { lineNumber });
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the frame script reports that a line was successfully gotten
|
||||
* to.
|
||||
@ -667,31 +608,17 @@ let ViewSourceChrome = {
|
||||
* 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;
|
||||
ViewSourceBrowser.prototype.onGoToLineSuccess.call(this, lineNumber);
|
||||
document.getElementById("statusbar-line-col").label =
|
||||
gViewSourceBundle.getFormattedString("statusBarLineCol", [lineNumber, 1]);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
Services.prompt.alert(window,
|
||||
gViewSourceBundle.getString("outOfRangeTitle"),
|
||||
gViewSourceBundle.getString("outOfRangeText"));
|
||||
this.promptAndGoToLine();
|
||||
},
|
||||
|
||||
/**
|
||||
* Reloads the browser, bypassing the network cache.
|
||||
*/
|
||||
reload() {
|
||||
gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY |
|
||||
Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
|
||||
this.browser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY |
|
||||
Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -710,7 +637,7 @@ let ViewSourceChrome = {
|
||||
this.shouldWrap = !this.shouldWrap;
|
||||
Services.prefs.setBoolPref("view_source.wrap_long_lines",
|
||||
this.shouldWrap);
|
||||
this.mm.sendAsyncMessage("ViewSource:ToggleWrapping");
|
||||
this.sendAsyncMessage("ViewSource:ToggleWrapping");
|
||||
},
|
||||
|
||||
/**
|
||||
@ -721,11 +648,10 @@ let ViewSourceChrome = {
|
||||
toggleSyntaxHighlighting() {
|
||||
this.shouldHighlight = !this.shouldHighlight;
|
||||
// We can't flip this value in the child, since prefs are read-only there.
|
||||
// We flip it here, and then cause a reload in the child to make the change
|
||||
// occur.
|
||||
// We flip it here, and then toggle a class in the child.
|
||||
Services.prefs.setBoolPref("view_source.syntax_highlight",
|
||||
this.shouldHighlight);
|
||||
this.mm.sendAsyncMessage("ViewSource:ToggleSyntaxHighlighting");
|
||||
this.sendAsyncMessage("ViewSource:ToggleSyntaxHighlighting");
|
||||
},
|
||||
|
||||
/**
|
||||
@ -739,22 +665,22 @@ let ViewSourceChrome = {
|
||||
* nothing.
|
||||
*/
|
||||
updateBrowserRemoteness(shouldBeRemote) {
|
||||
if (gBrowser.isRemoteBrowser == shouldBeRemote) {
|
||||
if (this.browser.isRemoteBrowser == shouldBeRemote) {
|
||||
return;
|
||||
}
|
||||
|
||||
let parentNode = gBrowser.parentNode;
|
||||
let nextSibling = gBrowser.nextSibling;
|
||||
let parentNode = this.browser.parentNode;
|
||||
let nextSibling = this.browser.nextSibling;
|
||||
|
||||
gBrowser.remove();
|
||||
this.browser.remove();
|
||||
if (shouldBeRemote) {
|
||||
gBrowser.setAttribute("remote", "true");
|
||||
this.browser.setAttribute("remote", "true");
|
||||
} else {
|
||||
gBrowser.removeAttribute("remote");
|
||||
this.browser.removeAttribute("remote");
|
||||
}
|
||||
// If nextSibling was null, this will put the browser at
|
||||
// the end of the list.
|
||||
parentNode.insertBefore(gBrowser, nextSibling);
|
||||
parentNode.insertBefore(this.browser, nextSibling);
|
||||
|
||||
if (shouldBeRemote) {
|
||||
// We're going to send a message down to the remote browser
|
||||
@ -764,14 +690,14 @@ let ViewSourceChrome = {
|
||||
// RemoteWebProgress, which is lazily loaded. We only need
|
||||
// contentWindowAsCPOW for the printing support, and this
|
||||
// should go away once bug 1146454 is fixed, since we can
|
||||
// then just pass the outerWindowID of the gBrowser to
|
||||
// then just pass the outerWindowID of the this.browser to
|
||||
// PrintUtils.
|
||||
gBrowser.webProgress;
|
||||
this.browser.webProgress;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
ViewSourceChrome.init();
|
||||
let viewSourceChrome = new ViewSourceChrome();
|
||||
|
||||
/**
|
||||
* PrintUtils uses this to make Print Preview work.
|
||||
@ -820,7 +746,7 @@ function getBrowser() {
|
||||
}
|
||||
|
||||
this.__defineGetter__("gPageLoader", function () {
|
||||
var webnav = ViewSourceChrome.webNav;
|
||||
var webnav = viewSourceChrome.webNav;
|
||||
if (!webnav)
|
||||
return null;
|
||||
delete this.gPageLoader;
|
||||
@ -843,48 +769,48 @@ function ViewSourceSavePage()
|
||||
|
||||
this.__defineGetter__("gLastLineFound", function () {
|
||||
Deprecated.warning("gLastLineFound is deprecated - please use " +
|
||||
"ViewSourceChrome.lastLineFound instead.",
|
||||
"viewSourceChrome.lastLineFound instead.",
|
||||
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
|
||||
return ViewSourceChrome.lastLineFound;
|
||||
return viewSourceChrome.lastLineFound;
|
||||
});
|
||||
|
||||
function onLoadViewSource() {
|
||||
Deprecated.warning("onLoadViewSource() is deprecated - please use " +
|
||||
"ViewSourceChrome.onXULLoaded() instead.",
|
||||
"viewSourceChrome.onXULLoaded() instead.",
|
||||
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
|
||||
ViewSourceChrome.onXULLoaded();
|
||||
viewSourceChrome.onXULLoaded();
|
||||
}
|
||||
|
||||
function isHistoryEnabled() {
|
||||
Deprecated.warning("isHistoryEnabled() is deprecated - please use " +
|
||||
"ViewSourceChrome.historyEnabled instead.",
|
||||
"viewSourceChrome.historyEnabled instead.",
|
||||
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
|
||||
return ViewSourceChrome.historyEnabled;
|
||||
return viewSourceChrome.historyEnabled;
|
||||
}
|
||||
|
||||
function ViewSourceClose() {
|
||||
Deprecated.warning("ViewSourceClose() is deprecated - please use " +
|
||||
"ViewSourceChrome.close() instead.",
|
||||
"viewSourceChrome.close() instead.",
|
||||
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
|
||||
ViewSourceChrome.close();
|
||||
viewSourceChrome.close();
|
||||
}
|
||||
|
||||
function ViewSourceReload() {
|
||||
Deprecated.warning("ViewSourceReload() is deprecated - please use " +
|
||||
"ViewSourceChrome.reload() instead.",
|
||||
"viewSourceChrome.reload() instead.",
|
||||
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
|
||||
ViewSourceChrome.reload();
|
||||
viewSourceChrome.reload();
|
||||
}
|
||||
|
||||
function getWebNavigation()
|
||||
{
|
||||
Deprecated.warning("getWebNavigation() is deprecated - please use " +
|
||||
"ViewSourceChrome.webNav instead.",
|
||||
"viewSourceChrome.webNav instead.",
|
||||
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
|
||||
// The original implementation returned null if anything threw during
|
||||
// the getting of the webNavigation.
|
||||
try {
|
||||
return ViewSourceChrome.webNav;
|
||||
return viewSourceChrome.webNav;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
@ -892,44 +818,44 @@ function getWebNavigation()
|
||||
|
||||
function viewSource(url) {
|
||||
Deprecated.warning("viewSource() is deprecated - please use " +
|
||||
"ViewSourceChrome.loadURL() instead.",
|
||||
"viewSourceChrome.loadURL() instead.",
|
||||
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
|
||||
ViewSourceChrome.loadURL(url);
|
||||
viewSourceChrome.loadURL(url);
|
||||
}
|
||||
|
||||
function ViewSourceGoToLine()
|
||||
{
|
||||
Deprecated.warning("ViewSourceGoToLine() is deprecated - please use " +
|
||||
"ViewSourceChrome.promptAndGoToLine() instead.",
|
||||
"viewSourceChrome.promptAndGoToLine() instead.",
|
||||
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
|
||||
ViewSourceChrome.promptAndGoToLine();
|
||||
viewSourceChrome.promptAndGoToLine();
|
||||
}
|
||||
|
||||
function goToLine(line)
|
||||
{
|
||||
Deprecated.warning("goToLine() is deprecated - please use " +
|
||||
"ViewSourceChrome.goToLine() instead.",
|
||||
"viewSourceChrome.goToLine() instead.",
|
||||
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
|
||||
ViewSourceChrome.goToLine(line);
|
||||
viewSourceChrome.goToLine(line);
|
||||
}
|
||||
|
||||
function BrowserForward(aEvent) {
|
||||
Deprecated.warning("BrowserForward() is deprecated - please use " +
|
||||
"ViewSourceChrome.goForward() instead.",
|
||||
"viewSourceChrome.goForward() instead.",
|
||||
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
|
||||
ViewSourceChrome.goForward();
|
||||
viewSourceChrome.goForward();
|
||||
}
|
||||
|
||||
function BrowserBack(aEvent) {
|
||||
Deprecated.warning("BrowserBack() is deprecated - please use " +
|
||||
"ViewSourceChrome.goBack() instead.",
|
||||
"viewSourceChrome.goBack() instead.",
|
||||
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
|
||||
ViewSourceChrome.goBack();
|
||||
viewSourceChrome.goBack();
|
||||
}
|
||||
|
||||
function UpdateBackForwardCommands() {
|
||||
Deprecated.warning("UpdateBackForwardCommands() is deprecated - please use " +
|
||||
"ViewSourceChrome.updateCommands() instead.",
|
||||
"viewSourceChrome.updateCommands() instead.",
|
||||
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
|
||||
ViewSourceChrome.updateCommands();
|
||||
viewSourceChrome.updateCommands();
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
# 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/.
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://global/content/viewSource.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mozapps/skin/viewsource/viewsource.css" type="text/css"?>
|
||||
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
|
||||
@ -21,10 +21,10 @@
|
||||
<window id="viewSource"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
contenttitlesetting="true"
|
||||
title="&mainWindow.title;"
|
||||
titlemodifier="&mainWindow.titlemodifier;"
|
||||
title="&mainWindow.title;"
|
||||
titlemodifier="&mainWindow.titlemodifier;"
|
||||
titlepreface="&mainWindow.preface;"
|
||||
titlemenuseparator ="&mainWindow.titlemodifierseparator;"
|
||||
titlemenuseparator ="&mainWindow.titlemodifierseparator;"
|
||||
windowtype="navigator:view-source"
|
||||
width="640" height="480"
|
||||
screenX="10" screenY="10"
|
||||
@ -35,7 +35,7 @@
|
||||
<script type="application/javascript" src="chrome://global/content/viewSource.js"/>
|
||||
<script type="application/javascript" src="chrome://global/content/viewZoomOverlay.js"/>
|
||||
<script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
|
||||
|
||||
|
||||
<stringbundle id="viewSourceBundle" src="chrome://global/locale/viewSource.properties"/>
|
||||
|
||||
<command id="cmd_savePage" oncommand="ViewSourceSavePage();"/>
|
||||
@ -54,16 +54,16 @@
|
||||
<command id="cmd_findSelection"
|
||||
oncommand="document.getElementById('FindToolbar').onFindSelectionCommand();"/>
|
||||
#endif
|
||||
<command id="cmd_reload" oncommand="ViewSourceChrome.reload();"/>
|
||||
<command id="cmd_goToLine" oncommand="ViewSourceChrome.promptAndGoToLine();" disabled="true"/>
|
||||
<command id="cmd_highlightSyntax" oncommand="ViewSourceChrome.toggleSyntaxHighlighting();"/>
|
||||
<command id="cmd_wrapLongLines" oncommand="ViewSourceChrome.toggleWrapping();"/>
|
||||
<command id="cmd_reload" oncommand="viewSourceChrome.reload();"/>
|
||||
<command id="cmd_goToLine" oncommand="viewSourceChrome.promptAndGoToLine();" disabled="true"/>
|
||||
<command id="cmd_highlightSyntax" oncommand="viewSourceChrome.toggleSyntaxHighlighting();"/>
|
||||
<command id="cmd_wrapLongLines" oncommand="viewSourceChrome.toggleWrapping();"/>
|
||||
<command id="cmd_textZoomReduce" oncommand="ZoomManager.reduce();"/>
|
||||
<command id="cmd_textZoomEnlarge" oncommand="ZoomManager.enlarge();"/>
|
||||
<command id="cmd_textZoomReset" oncommand="ZoomManager.reset();"/>
|
||||
|
||||
<command id="Browser:Back" oncommand="ViewSourceChrome.goBack()" observes="viewSourceNavigation"/>
|
||||
<command id="Browser:Forward" oncommand="ViewSourceChrome.goForward()" observes="viewSourceNavigation"/>
|
||||
<command id="Browser:Back" oncommand="viewSourceChrome.goBack()" observes="viewSourceNavigation"/>
|
||||
<command id="Browser:Forward" oncommand="viewSourceChrome.goForward()" observes="viewSourceNavigation"/>
|
||||
|
||||
<broadcaster id="viewSourceNavigation"/>
|
||||
|
||||
@ -110,7 +110,7 @@
|
||||
#endif
|
||||
|
||||
</keyset>
|
||||
|
||||
|
||||
<tooltip id="aHTMLTooltip" page="true"/>
|
||||
|
||||
<menupopup id="viewSourceContextMenu">
|
||||
@ -131,16 +131,16 @@
|
||||
<menuitem id="context-copyLink"
|
||||
label="©LinkCmd.label;"
|
||||
accesskey="©LinkCmd.accesskey;"
|
||||
oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
|
||||
oncommand="viewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
|
||||
<menuitem id="context-copyEmail"
|
||||
label="©EmailCmd.label;"
|
||||
accesskey="©EmailCmd.accesskey;"
|
||||
oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
|
||||
oncommand="viewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="cMenu_selectAll"/>
|
||||
</menupopup>
|
||||
|
||||
<!-- Menu -->
|
||||
<!-- Menu -->
|
||||
<toolbox id="viewSource-toolbox">
|
||||
<menubar id="viewSource-main-menubar">
|
||||
|
||||
@ -188,11 +188,11 @@
|
||||
label="&reloadCmd.label;" key="key_reload"/>
|
||||
<menuseparator />
|
||||
<menu id="viewTextZoomMenu" label="&menu_textSize.label;" accesskey="&menu_textSize.accesskey;">
|
||||
<menupopup>
|
||||
<menuitem id="menu_textEnlarge" command="cmd_textZoomEnlarge"
|
||||
<menupopup>
|
||||
<menuitem id="menu_textEnlarge" command="cmd_textZoomEnlarge"
|
||||
label="&menu_textEnlarge.label;" accesskey="&menu_textEnlarge.accesskey;"
|
||||
key="key_textZoomEnlarge"/>
|
||||
<menuitem id="menu_textReduce" command="cmd_textZoomReduce"
|
||||
<menuitem id="menu_textReduce" command="cmd_textZoomReduce"
|
||||
label="&menu_textReduce.label;" accesskey="&menu_textReduce.accesskey;"
|
||||
key="key_textZoomReduce"/>
|
||||
<menuseparator/>
|
||||
@ -206,7 +206,7 @@
|
||||
<menu id="charsetMenu"
|
||||
label="&charsetMenu2.label;"
|
||||
accesskey="&charsetMenu2.accesskey;"
|
||||
oncommand="ViewSourceChrome.onSetCharacterSet(event);"
|
||||
oncommand="viewSourceChrome.onSetCharacterSet(event);"
|
||||
onpopupshowing="CharsetMenu.build(event.target);"
|
||||
onpopupshown="CharsetMenu.update(event.target, content.document.characterSet);">
|
||||
<menupopup/>
|
||||
@ -218,7 +218,7 @@
|
||||
label="&menu_highlightSyntax.label;" accesskey="&menu_highlightSyntax.accesskey;"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
</menubar>
|
||||
</menubar>
|
||||
</toolbox>
|
||||
|
||||
<vbox id="appcontent" flex="1">
|
||||
@ -226,7 +226,7 @@
|
||||
<browser id="content" type="content-primary" name="content" src="about:blank" flex="1"
|
||||
context="viewSourceContextMenu" showcaret="true" tooltip="aHTMLTooltip" />
|
||||
<findbar id="FindToolbar" browserid="content"/>
|
||||
</vbox>
|
||||
</vbox>
|
||||
|
||||
<statusbar id="status-bar" class="chromeclass-status">
|
||||
<statusbarpanel id="statusbar-line-col" label="" flex="1"/>
|
||||
|
@ -12,6 +12,10 @@
|
||||
* getDefaultFileName, getNormalizedLeafName and getDefaultExtension
|
||||
*/
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ViewSourceBrowser",
|
||||
"resource://gre/modules/ViewSourceBrowser.jsm");
|
||||
|
||||
var gViewSourceUtils = {
|
||||
|
||||
mnsIWebBrowserPersist: Components.interfaces.nsIWebBrowserPersist,
|
||||
@ -60,6 +64,72 @@ var gViewSourceUtils = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays view source 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 aArgs
|
||||
* An object with the following properties:
|
||||
*
|
||||
* URL (required):
|
||||
* A string URL for the page we'd like to view the source of.
|
||||
* viewSourceBrowser (required):
|
||||
* The browser to display the view source in.
|
||||
* browser (optional):
|
||||
* The browser containing the document that we would like to view the
|
||||
* source of. This is required if outerWindowID is passed.
|
||||
* outerWindowID (optional):
|
||||
* The outerWindowID of the content window containing the document that
|
||||
* we want to view the source of. Pass this if you want to attempt to
|
||||
* load the document source out of the network cache.
|
||||
* lineNumber (optional):
|
||||
* The line number to focus on once the source is loaded.
|
||||
*/
|
||||
viewSourceInBrowser: function(aArgs) {
|
||||
let viewSourceBrowser = new ViewSourceBrowser(aArgs.viewSourceBrowser);
|
||||
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);
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays view source for a MathML fragment 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 aNode
|
||||
* Some element within the fragment of interest.
|
||||
* @param aContext
|
||||
* A string denoting the type of fragment. Currently, "mathml" is the
|
||||
* only accepted value.
|
||||
* @param aViewSourceInBrowser
|
||||
* The browser to display the view source in.
|
||||
*/
|
||||
viewSourceFromFragmentInBrowser: function(aNode, aContext,
|
||||
aViewSourceInBrowser) {
|
||||
let viewSourceBrowser = new ViewSourceBrowser(aViewSourceInBrowser);
|
||||
viewSourceBrowser.loadViewSourceFromFragment(aNode, aContext);
|
||||
},
|
||||
|
||||
// Opens the interval view source viewer
|
||||
_openInInternalViewer: function(aArgsOrURL, aPageDescriptor, aDocument, aLineNumber)
|
||||
{
|
||||
@ -114,7 +184,7 @@ var gViewSourceUtils = {
|
||||
lineNumber: aLineNumber};
|
||||
|
||||
try {
|
||||
var editor = this.getExternalViewSourceEditor();
|
||||
var editor = this.getExternalViewSourceEditor();
|
||||
if (!editor) {
|
||||
this.handleCallBack(aCallBack, false, data);
|
||||
return;
|
||||
@ -129,7 +199,7 @@ var gViewSourceUtils = {
|
||||
|
||||
var path;
|
||||
var contentType = aDocument ? aDocument.contentType : null;
|
||||
if (uri.scheme == "file") {
|
||||
if (uri.scheme == "file") {
|
||||
// it's a local file; we can open it directly
|
||||
path = uri.QueryInterface(Components.interfaces.nsIFileURL).file.path;
|
||||
|
||||
@ -140,7 +210,7 @@ var gViewSourceUtils = {
|
||||
// set up the progress listener with what we know so far
|
||||
this.viewSourceProgressListener.editor = editor;
|
||||
this.viewSourceProgressListener.callBack = aCallBack;
|
||||
this.viewSourceProgressListener.data = data;
|
||||
this.viewSourceProgressListener.data = data;
|
||||
if (!aPageDescriptor) {
|
||||
// without a page descriptor, loadPage has no chance of working. download the file.
|
||||
var file = this.getTemporaryFile(uri, aDocument, contentType);
|
||||
@ -189,7 +259,7 @@ var gViewSourceUtils = {
|
||||
var progress = webShell.QueryInterface(this.mnsIWebProgress);
|
||||
progress.addProgressListener(this.viewSourceProgressListener,
|
||||
this.mnsIWebProgress.NOTIFY_STATE_DOCUMENT);
|
||||
var pageLoader = webShell.QueryInterface(this.mnsIWebPageDescriptor);
|
||||
var pageLoader = webShell.QueryInterface(this.mnsIWebPageDescriptor);
|
||||
pageLoader.loadPage(aPageDescriptor, this.mnsIWebPageDescriptor.DISPLAY_AS_SOURCE);
|
||||
}
|
||||
}
|
||||
@ -296,7 +366,7 @@ var gViewSourceUtils = {
|
||||
|
||||
// get a temporary filename using the attributes from the data object that
|
||||
// openInExternalEditor gave us
|
||||
this.file = gViewSourceUtils.getTemporaryFile(this.data.uri, this.data.doc,
|
||||
this.file = gViewSourceUtils.getTemporaryFile(this.data.uri, this.data.doc,
|
||||
this.data.doc.contentType);
|
||||
|
||||
// we have to convert from the source charset.
|
||||
@ -310,7 +380,7 @@ var gViewSourceUtils = {
|
||||
|
||||
// write the source to the file
|
||||
coStream.writeString(webNavigation.document.body.textContent);
|
||||
|
||||
|
||||
// clean up
|
||||
coStream.close();
|
||||
foStream.close();
|
||||
|
@ -9,4 +9,4 @@ toolkit.jar:
|
||||
content/global/viewPartialSource.js (content/viewPartialSource.js)
|
||||
* content/global/viewPartialSource.xul (content/viewPartialSource.xul)
|
||||
content/global/viewSourceUtils.js (content/viewSourceUtils.js)
|
||||
content/global/viewSource-content.js (content/viewSource-content.js)
|
||||
content/global/viewSource-content.js (content/viewSource-content.js)
|
||||
|
@ -9,5 +9,9 @@ MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'ViewSourceBrowser.jsm',
|
||||
]
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Toolkit', 'View Source')
|
||||
|
@ -24,7 +24,7 @@ let checkViewSource = Task.async(function* (aWindow) {
|
||||
is(statusPanel.getAttribute("label"), "", "Correct status bar text");
|
||||
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
aWindow.ViewSourceChrome.goToLine(i);
|
||||
aWindow.viewSourceChrome.goToLine(i);
|
||||
let result = yield ContentTask.spawn(aWindow.gBrowser, i, function*(i) {
|
||||
let selection = content.getSelection();
|
||||
return (selection.toString() == "line " + i);
|
||||
|
@ -53,21 +53,14 @@ let exercisePrefs = Task.async(function* (source, highlightable) {
|
||||
yield checkStyle(win, "white-space", "pre");
|
||||
|
||||
// Check that the Syntax Highlighting menu item works.
|
||||
let pageShowPromise = BrowserTestUtils.waitForEvent(win.gBrowser, "pageshow");
|
||||
simulateClick(syntaxMenuItem);
|
||||
yield pageShowPromise;
|
||||
|
||||
is(syntaxMenuItem.hasAttribute("checked"), false, "Syntax menu item unchecked");
|
||||
is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), false, "Syntax highlighting pref set");
|
||||
yield checkHighlight(win, false);
|
||||
|
||||
pageShowPromise = BrowserTestUtils.waitForEvent(win.gBrowser, "pageshow");
|
||||
simulateClick(syntaxMenuItem);
|
||||
yield pageShowPromise;
|
||||
|
||||
is(syntaxMenuItem.hasAttribute("checked"), true, "Syntax menu item checked");
|
||||
is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), true, "Syntax highlighting pref set");
|
||||
|
||||
yield checkHighlight(win, highlightable);
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
|
||||
@ -127,7 +120,8 @@ let checkHighlight = Task.async(function* (win, expected) {
|
||||
let highlighted = yield ContentTask.spawn(browser, {}, function* () {
|
||||
let spans = content.document.getElementsByTagName("span");
|
||||
return Array.some(spans, (span) => {
|
||||
return span.className != "";
|
||||
let style = content.getComputedStyle(span, null);
|
||||
return style.getPropertyValue("color") !== "rgb(0, 0, 0)";
|
||||
});
|
||||
});
|
||||
is(highlighted, expected, "Syntax highlighting " + (expected ? "on" : "off"));
|
||||
|
@ -475,7 +475,7 @@
|
||||
<xul:image class="popup-notification-icon"
|
||||
xbl:inherits="popupid,src=icon"/>
|
||||
</xul:vbox>
|
||||
<xul:vbox>
|
||||
<xul:vbox class="popup-notification-body" xbl:inherits="popupid">
|
||||
<xul:hbox align="start">
|
||||
<xul:vbox flex="1">
|
||||
<xul:label class="popup-notification-origin header"
|
||||
|
@ -978,7 +978,6 @@ TabActor.prototype = {
|
||||
});
|
||||
|
||||
this._extraActors = null;
|
||||
this._styleSheetActors.clear();
|
||||
|
||||
this._exited = true;
|
||||
},
|
||||
@ -1325,6 +1324,10 @@ TabActor.prototype = {
|
||||
this._popContext();
|
||||
|
||||
// Shut down actors that belong to this tab's pool.
|
||||
for (let sheetActor of this._styleSheetActors.values()) {
|
||||
this._tabPool.removeActor(sheetActor);
|
||||
}
|
||||
this._styleSheetActors.clear();
|
||||
this.conn.removeActorPool(this._tabPool);
|
||||
this._tabPool = null;
|
||||
if (this._tabActorPool) {
|
||||
@ -1642,12 +1645,6 @@ TabActor.prototype = {
|
||||
threadActor.global = window;
|
||||
}
|
||||
|
||||
for (let sheetActor of this._styleSheetActors.values()) {
|
||||
this._tabPool.removeActor(sheetActor);
|
||||
}
|
||||
this._styleSheetActors.clear();
|
||||
|
||||
|
||||
// Refresh the debuggee list when a new window object appears (top window or
|
||||
// iframe).
|
||||
if (threadActor.attached) {
|
||||
|
@ -11,3 +11,7 @@ outOfRangeText = The specified line was not found.
|
||||
statusBarLineCol = Line %1$S, Col %2$S
|
||||
viewSelectionSourceTitle = DOM Source of Selection
|
||||
viewMathMLSourceTitle = DOM Source of MathML
|
||||
|
||||
context_goToLine_label = Go to Line…
|
||||
context_wrapLongLines_label = Wrap Long Lines
|
||||
context_highlightSyntax_label = Syntax Highlighting
|
||||
|
@ -63,8 +63,8 @@ notification[type="critical"] {
|
||||
|
||||
/* Popup notification */
|
||||
|
||||
.popup-notification-description {
|
||||
max-width: 24em;
|
||||
.popup-notification-body {
|
||||
max-width: 25em;
|
||||
}
|
||||
|
||||
.popup-notification-origin:not([value]),
|
||||
|
@ -101,8 +101,8 @@ notification[type="info"]:not([value="translation"]) .close-icon:not(:hover) {
|
||||
|
||||
/* Popup notification */
|
||||
|
||||
.popup-notification-description {
|
||||
max-width: 24em;
|
||||
.popup-notification-body {
|
||||
max-width: 25em;
|
||||
}
|
||||
|
||||
.popup-notification-origin:not([value]),
|
||||
|
@ -58,8 +58,8 @@ notification[type="critical"] {
|
||||
|
||||
/* Popup notification */
|
||||
|
||||
.popup-notification-description {
|
||||
max-width: 24em;
|
||||
.popup-notification-body {
|
||||
max-width: 25em;
|
||||
}
|
||||
|
||||
.popup-notification-origin:not([value]),
|
||||
|
Loading…
Reference in New Issue
Block a user