diff --git a/mozilla/browser/app/profile/firefox.js b/mozilla/browser/app/profile/firefox.js
index b0950354b1d..bdc4317735c 100644
--- a/mozilla/browser/app/profile/firefox.js
+++ b/mozilla/browser/app/profile/firefox.js
@@ -434,3 +434,6 @@ pref("bidi.browser.ui", false);
// 1 act like PgUp/PgDown
// 2 and other values, nothing
pref("browser.backspace_action", 0);
+
+pref("view_source.editor.path", "");
+pref("view_source.editor.external", false);
diff --git a/mozilla/browser/base/content/browser.js b/mozilla/browser/base/content/browser.js
index 166aa94192f..6f6e966c311 100644
--- a/mozilla/browser/base/content/browser.js
+++ b/mozilla/browser/base/content/browser.js
@@ -1932,12 +1932,11 @@ function readFromClipboard()
function BrowserViewSourceOfDocument(aDocument)
{
- var docCharset;
var pageCookie;
var webNav;
// Get the document charset
- docCharset = "charset=" + aDocument.characterSet;
+ var docCharset = "charset=" + aDocument.characterSet;
// Get the nsIWebNavigation associated with the document
try {
@@ -1973,16 +1972,17 @@ function BrowserViewSourceOfDocument(aDocument)
// If no page descriptor is available, just use the view-source URL...
}
- BrowserViewSourceOfURL(webNav.currentURI.spec, docCharset, pageCookie);
+ ViewSourceOfURL(webNav.currentURI.spec, pageCookie, aDocument);
}
-function BrowserViewSourceOfURL(url, charset, pageCookie)
+function ViewSourceOfURL(aURL, aPageDescriptor, aDocument)
{
- // try to open a view-source window while inheriting the charset (if any)
- openDialog("chrome://global/content/viewSource.xul",
- "_blank",
- "scrollbars,resizable,chrome,dialog=no",
- url, charset, pageCookie);
+ if (getBoolPref("view_source.editor.external", false)) {
+ gViewSourceUtils.openInExternalEditor(aURL, aPageDescriptor, aDocument);
+ }
+ else {
+ gViewSourceUtils.openInInternalViewer(aURL, aPageDescriptor, aDocument);
+ }
}
// doc - document to use for source, or null for this window's document
diff --git a/mozilla/browser/base/content/global-scripts.inc b/mozilla/browser/base/content/global-scripts.inc
index c34cb30d152..4057e7f2eef 100644
--- a/mozilla/browser/base/content/global-scripts.inc
+++ b/mozilla/browser/base/content/global-scripts.inc
@@ -46,3 +46,4 @@
+
diff --git a/mozilla/toolkit/components/viewsource/content/viewSourceUtils.js b/mozilla/toolkit/components/viewsource/content/viewSourceUtils.js
new file mode 100644
index 00000000000..5d47fbd1dd9
--- /dev/null
+++ b/mozilla/toolkit/components/viewsource/content/viewSourceUtils.js
@@ -0,0 +1,259 @@
+# -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is View Source Utilities.
+#
+# The Initial Developer of the Original Code is
+# Jason Barnabe.
+# Portions created by the Initial Developer are Copyright (C) 2005
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+/*
+ * To keep the global namespace safe, don't define global variables and
+ * functions in this file.
+ *
+ * This file requires contentAreaUtils.js
+*/
+
+var gViewSourceUtils = {
+
+ mnsIWebBrowserPersist: Components.interfaces.nsIWebBrowserPersist,
+ mnsIWebProgress: Components.interfaces.nsIWebProgress,
+ mnsIWebPageDescriptor: Components.interfaces.nsIWebPageDescriptor,
+
+ // Opens the interval view source viewer
+ openInInternalViewer: function(aURL, aPageDescriptor, aDocument)
+ {
+ // try to open a view-source window while inheriting the charset (if any)
+ var charset = null;
+ if (aDocument)
+ charset = "charset=" + aDocument.characterSet;
+ openDialog("chrome://global/content/viewSource.xul",
+ "_blank",
+ "scrollbars,resizable,chrome,dialog=no",
+ aURL, charset, aPageDescriptor);
+ },
+
+ // 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, aCallBack)
+ {
+ var data = {url: aURL, pageDescriptor: aPageDescriptor, doc: aDocument};
+
+ try {
+ var editor = this.getExternalViewSourceEditor();
+ if (!editor) {
+ this.handleCallBack(aCallBack, false, data);
+ return;
+ }
+
+ // make a uri
+ 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);
+ data.uri = uri;
+
+ var path;
+ var contentType = aDocument ? aDocument.contentType : null;
+ if (uri.scheme == "file") {
+ // it's a local file; we can open it directly
+ path = uri.QueryInterface(Components.interfaces.nsIFileURL).file.path;
+ editor.run(false, [path], 1);
+ this.handleCallBack(aCallBack, true, data);
+ } else {
+ // set up the progress listener with what we know so far
+ this.viewSourceProgressListener.editor = editor;
+ this.viewSourceProgressListener.callBack = aCallBack;
+ 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);
+ this.viewSourceProgressListener.file = file;
+
+ var webBrowserPersist = Components
+ .classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(this.mnsIWebBrowserPersist);
+ // the default setting is to not decode. we need to decode.
+ webBrowserPersist.persistFlags = this.mnsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
+ webBrowserPersist.progressListener = this.viewSourceProgressListener;
+ webBrowserPersist.saveURI(uri, null, null, null, null, file);
+ } else {
+ // we'll use nsIWebPageDescriptor to get the source because it may not have to refetch
+ // the file from the server
+ var webShell = Components.classes["@mozilla.org/webshell;1"].createInstance();
+ this.viewSourceProgressListener.webShell = webShell;
+ var progress = webShell.QueryInterface(this.mnsIWebProgress);
+ progress.addProgressListener(this.viewSourceProgressListener,
+ this.mnsIWebProgress.NOTIFY_STATE_DOCUMENT);
+ var pageLoader = webShell.QueryInterface(this.mnsIWebPageDescriptor);
+ pageLoader.loadPage(aPageDescriptor, this.mnsIWebPageDescriptor.DISPLAY_AS_SOURCE);
+ }
+ }
+ } catch (ex) {
+ // we failed loading it with the external editor.
+ this.handleCallBack(aCallBack, false, data);
+ return;
+ }
+ },
+
+ // Default callback - opens the internal viewer if the external editor failed
+ internalViewerFallback: function(result, data)
+ {
+ if (!result) {
+ this.openInInternalViewer(data.url, data.pageDescriptor, data.doc);
+ }
+ },
+
+ // Calls the callback, keeping in mind undefined or null values.
+ handleCallBack: function(aCallBack, result, data)
+ {
+ // ifcallback is undefined, default to the internal viewer
+ if (aCallBack === undefined) {
+ this.internalViewerFallback(result, data);
+ } else if (aCallBack) {
+ aCallBack(result, data);
+ }
+ },
+
+ // Returns nsIProcess of the external view source editor or null
+ getExternalViewSourceEditor: function()
+ {
+ var editor = null;
+ var viewSourceAppPath = null;
+ try {
+ var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+ var prefPath = prefs.getCharPref("view_source.editor.path");
+ if (prefPath.length > 0) {
+ viewSourceAppPath = Components.classes["@mozilla.org/file/local;1"]
+ .createInstance(Components.interfaces.nsILocalFile);
+ viewSourceAppPath.initWithPath(prefPath);
+ // it's gotta be executable
+ if (viewSourceAppPath.exists() && viewSourceAppPath.isExecutable()) {
+ editor = Components.classes['@mozilla.org/process/util;1']
+ .getService(Components.interfaces.nsIProcess);
+ editor.init(viewSourceAppPath);
+ }
+ }
+ }
+ catch (ex) {
+ dump(ex);
+ }
+ return editor;
+ },
+
+ viewSourceProgressListener: {
+
+ mnsIWebProgressListener: Components.interfaces.nsIWebProgressListener,
+
+ QueryInterface: function(aIID) {
+ if (aIID.equals(this.mnsIWebProgressListener) ||
+ aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
+ aIID.equals(Components.interfaces.nsISupports))
+ return this;
+ throw Components.results.NS_NOINTERFACE;
+ },
+
+ destroy: function() {
+ this.webShell = null;
+ this.editor = null;
+ this.callBack = null;
+ this.data = null;
+ this.file = null;
+ },
+
+ onStateChange: function(aProgress, aRequest, aFlag, aStatus) {
+ // once it's done loading...
+ if ((aFlag & this.mnsIWebProgressListener.STATE_STOP) && aStatus == 0) {
+ try {
+ if (!this.file) {
+ // it's not saved to file yet, it's in the webshell
+
+ // 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.data.doc.contentType);
+
+ // we have to convert from the source charset.
+ var webNavigation = this.webShell.QueryInterface(Components.interfaces.nsIWebNavigation);
+ var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Components.interfaces.nsIFileOutputStream);
+ foStream.init(this.file, 0x02 | 0x08 | 0x20, 0664, 0); // write | create | truncate
+ var coStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
+ .createInstance(Components.interfaces.nsIConverterOutputStream);
+ coStream.init(foStream, this.data.doc.characterSet, 0, null);
+
+ // write the source to the file
+ coStream.writeString(webNavigation.document.body.textContent);
+
+ // clean up
+ coStream.close();
+ foStream.close();
+ }
+ // fire up the editor
+ this.editor.run(false, [this.file.path], 1);
+
+ gViewSourceUtils.handleCallBack(this.callBack, true, this.data);
+ } catch (ex) {
+ // we failed loading it with the external editor.
+ this.handleCallBack(this.callBack, false, this.data);
+ } finally {
+ this.destroy();
+ }
+ }
+ return 0;
+ },
+
+ onLocationChange: function() {return 0;},
+ onProgressChange: function() {return 0;},
+ onStatusChange: function() {return 0;},
+ onSecurityChange: function() {return 0;},
+ onLinkIconAvailable: function() {return 0;},
+
+ webShell: null,
+ editor: null,
+ callBack: null,
+ data: null,
+ file: null
+ },
+
+ // returns an nsIFile for the passed document in the system temp directory
+ getTemporaryFile: function(aURI, aDocument, aContentType) {
+ var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties);
+ var tempFile = fileLocator.get("TmpD", Components.interfaces.nsIFile);
+ var fileName = getDefaultFileName(null, aURI, aDocument, aContentType);
+ var extension = getDefaultExtension(fileName, aURI, aContentType);
+ var leafName = getNormalizedLeafName(fileName, extension);
+ tempFile.append(leafName);
+ return tempFile;
+ }
+}
diff --git a/mozilla/toolkit/components/viewsource/jar.mn b/mozilla/toolkit/components/viewsource/jar.mn
index b03f70a9336..fdc66b9f841 100644
--- a/mozilla/toolkit/components/viewsource/jar.mn
+++ b/mozilla/toolkit/components/viewsource/jar.mn
@@ -4,3 +4,4 @@ toolkit.jar:
* content/global/viewSource.xul (content/viewSource.xul)
* content/global/viewPartialSource.js (content/viewPartialSource.js)
* content/global/viewPartialSource.xul (content/viewPartialSource.xul)
+* content/global/viewSourceUtils.js (content/viewSourceUtils.js)