diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index a9093ff3e571..38ca7475a1a5 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -2365,27 +2365,43 @@ function BrowserViewSourceOfDocument(aArgsOrDocument) { args = aArgsOrDocument; } - let inTab = Services.prefs.getBoolPref("view_source.tab"); - if (inTab) { - let viewSourceURL = `view-source:${args.URL}`; - let tabBrowser = gBrowser; - // In the case of sidebars and chat windows, gBrowser is defined but null, - // because no #content element exists. For these cases, we need to find - // the most recent browser window. - // In the case of popups, we need to find a non-popup browser window. - if (!tabBrowser || !window.toolbar.visible) { - // This returns only non-popup browser windows by default. - let browserWindow = RecentWindow.getMostRecentBrowserWindow(); - tabBrowser = browserWindow.gBrowser; + let viewInternal = () => { + let inTab = Services.prefs.getBoolPref("view_source.tab"); + if (inTab) { + let viewSourceURL = `view-source:${args.URL}`; + let tabBrowser = gBrowser; + // In the case of sidebars and chat windows, gBrowser is defined but null, + // because no #content element exists. For these cases, we need to find + // the most recent browser window. + // In the case of popups, we need to find a non-popup browser window. + if (!tabBrowser || !window.toolbar.visible) { + // This returns only non-popup browser windows by default. + let browserWindow = RecentWindow.getMostRecentBrowserWindow(); + tabBrowser = browserWindow.gBrowser; + } + let tab = tabBrowser.loadOneTab(viewSourceURL, { + relatedToCurrent: true, + inBackground: false + }); + args.viewSourceBrowser = tabBrowser.getBrowserForTab(tab); + top.gViewSourceUtils.viewSourceInBrowser(args); + } else { + top.gViewSourceUtils.viewSource(args); } - let tab = tabBrowser.loadOneTab(viewSourceURL, { - relatedToCurrent: true, - inBackground: false + } + + // Check if external view source is enabled. If so, try it. If it fails, + // fallback to internal view source. + if (Services.prefs.getBoolPref("view_source.editor.external")) { + top.gViewSourceUtils + .openInExternalEditor(args, null, null, null, result => { + if (!result) { + viewInternal(); + } }); - args.viewSourceBrowser = tabBrowser.getBrowserForTab(tab); - top.gViewSourceUtils.viewSourceInBrowser(args); } else { - top.gViewSourceUtils.viewSource(args); + // Display using internal view source + viewInternal(); } } diff --git a/toolkit/components/viewsource/content/viewSourceUtils.js b/toolkit/components/viewsource/content/viewSourceUtils.js index 8dc3111064a9..2a4a98971297 100644 --- a/toolkit/components/viewsource/content/viewSourceUtils.js +++ b/toolkit/components/viewsource/content/viewSourceUtils.js @@ -15,6 +15,8 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ViewSourceBrowser", "resource://gre/modules/ViewSourceBrowser.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Deprecated", + "resource://gre/modules/Deprecated.jsm"); var gViewSourceUtils = { @@ -179,12 +181,64 @@ var gViewSourceUtils = { return editorArgs; }, - // aCallBack is a function accepting two arguments - result (true=success) and a data object - // It defaults to openInInternalViewer if undefined. - openInExternalEditor: function(aURL, aPageDescriptor, aDocument, aLineNumber, aCallBack) - { - var data = {url: aURL, pageDescriptor: aPageDescriptor, doc: aDocument, - lineNumber: aLineNumber}; + /** + * Opens an external editor with the view source content. + * + * @param aArgsOrURL (required) + * This is either an Object containing parameters, or a string + * URL for the page we want to view the source of. In the latter + * case we will be paying attention to the other parameters, as + * we will be supporting the old API for this method. + * If aArgsOrURL is an Object, the other parameters will be ignored. + * aArgsOrURL as an Object can include the following properties: + * + * 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. 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. + * + * @param aPageDescriptor (deprecated, optional) + * Accepted for compatibility reasons, but is otherwise ignored. + * @param aDocument (deprecated, optional) + * The content document we would like to view the source of. This + * function will throw if aDocument is a CPOW. + * @param aLineNumber (deprecated, optional) + * The line number to focus on once the source is loaded. + * @param aCallBack + * A function accepting two arguments: + * * result (true = success) + * * data object + * The function defaults to opening an internal viewer if external + * viewing fails. + */ + openInExternalEditor: function(aArgsOrURL, aPageDescriptor, aDocument, + aLineNumber, aCallBack) { + let data; + if (typeof aArgsOrURL == "string") { + Deprecated.warning("The arguments you're passing to " + + "openInExternalEditor are using an out-of-date API.", + "https://developer.mozilla.org/en-US/Add-ons/" + + "Code_snippets/View_Source_for_XUL_Applications"); + data = { + url: aArgsOrURL, + pageDescriptor: aPageDescriptor, + doc: aDocument, + lineNumber: aLineNumber + }; + } else { + let { URL, outerWindowID, lineNumber } = aArgsOrURL; + data = { + url: URL, + lineNumber + }; + } try { var editor = this.getExternalViewSourceEditor(); @@ -197,7 +251,7 @@ var gViewSourceUtils = { var ios = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); var charset = aDocument ? aDocument.characterSet : null; - var uri = ios.newURI(aURL, charset, null); + var uri = ios.newURI(data.url, charset, null); data.uri = uri; var path; @@ -211,6 +265,7 @@ var gViewSourceUtils = { this.handleCallBack(aCallBack, true, data); } else { // set up the progress listener with what we know so far + this.viewSourceProgressListener.contentLoaded = false; this.viewSourceProgressListener.editor = editor; this.viewSourceProgressListener.callBack = aCallBack; this.viewSourceProgressListener.data = data; @@ -363,6 +418,11 @@ var gViewSourceUtils = { }, onContentLoaded: function() { + // The progress listener may call this multiple times, so be sure we only + // run once. + if (this.contentLoaded) { + return; + } try { if (!this.file) { // it's not saved to file yet, it's in the webshell @@ -410,6 +470,7 @@ var gViewSourceUtils = { this.data.lineNumber); this.editor.runw(false, editorArgs, editorArgs.length); + this.contentLoaded = true; gViewSourceUtils.handleCallBack(this.callBack, true, this.data); } catch (ex) { // we failed loading it with the external editor.