Bug 1439021 - restore 'view source' by forking M-C code. r=me (fork-onky)
authorJorg K <jorgk@jorgk.com>
Mon, 26 Feb 2018 16:10:00 +0100
changeset 23386 2caecab6e8c041a4379cb706ae921d083b7456ed
parent 23385 a6f508bc10533ed82dc3bc36aec36dbb1c2b6868
child 23387 172a4b9b497481c018f47c6cc5a5f9721e928474
push id14131
push usermozilla@jorgk.com
push dateTue, 27 Feb 2018 23:09:44 +0000
treeherdercomm-central@2caecab6e8c0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme
bugs1439021
Bug 1439021 - restore 'view source' by forking M-C code. r=me (fork-onky)
common/src/viewSource.css
common/src/viewSource.js
common/src/viewSource.xul
mail/base/content/mailCommands.js
mail/base/jar.mn
mail/locales/en-US/chrome/messenger/viewSource.dtd
mail/locales/en-US/chrome/messenger/viewSource.properties
mail/locales/jar.mn
new file mode 100644
--- /dev/null
+++ b/common/src/viewSource.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+toolbar[printpreview="true"] {
+  -moz-binding: url("chrome://global/content/printPreviewBindings.xml#printpreviewtoolbar");
+}
new file mode 100644
--- /dev/null
+++ b/common/src/viewSource.js
@@ -0,0 +1,866 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+
+/* import-globals-from ../../../content/globalOverlay.js */
+/* import-globals-from ../../printing/content/printUtils.js */
+/* import-globals-from ../../../content/viewZoomOverlay.js */
+/* import-globals-from ../../../content/contentAreaUtils.js */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/ViewSourceBrowser.jsm");
+
+ChromeUtils.defineModuleGetter(this, "Services",
+  "resource://gre/modules/Services.jsm");
+ChromeUtils.defineModuleGetter(this, "CharsetMenu",
+  "resource://gre/modules/CharsetMenu.jsm");
+ChromeUtils.defineModuleGetter(this, "Deprecated",
+  "resource://gre/modules/Deprecated.jsm");
+
+/* global gBrowser, gViewSourceBundle, gContextMenu */
+[
+  ["gBrowser",          "content"],
+  ["gViewSourceBundle", "viewSourceBundle"],
+  ["gContextMenu",      "viewSourceContextMenu"]
+].forEach(function([name, id]) {
+  Object.defineProperty(window, name, {
+    configurable: true,
+    enumerable: true,
+    get() {
+      var element = document.getElementById(id);
+      if (!element)
+        return null;
+      delete window[name];
+      return window[name] = element;
+    },
+  });
+});
+
+/**
+ * ViewSourceChrome is the primary interface for interacting with
+ * 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.
+ */
+function ViewSourceChrome() {
+  ViewSourceBrowser.call(this);
+}
+
+ViewSourceChrome.prototype = {
+  __proto__: ViewSourceBrowser.prototype,
+
+  /**
+   * The <browser> that will be displaying the view source content.
+   */
+  get browser() {
+    return gBrowser;
+  },
+
+  /**
+   * The context menu, when opened from the content process, sends
+   * up a chunk of serialized data describing the items that the
+   * context menu is being opened on. This allows us to avoid using
+   * CPOWs.
+   */
+  contextMenuData: {},
+
+  /**
+   * These are the messages that ViewSourceChrome will listen for
+   * from the frame script it injects. Any message names added here
+   * will automatically have ViewSourceChrome listen for those messages,
+   * and remove the listeners on teardown.
+   */
+  messages: ViewSourceBrowser.prototype.messages.concat([
+    "ViewSource:SourceLoaded",
+    "ViewSource:SourceUnloaded",
+    "ViewSource:Close",
+    "ViewSource:OpenURL",
+    "ViewSource:ContextMenuOpening",
+  ]),
+
+  /**
+   * 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() {
+    this.mm.loadFrameScript("chrome://global/content/viewSource-content.js", true);
+
+    this.shouldWrap = Services.prefs.getBoolPref("view_source.wrap_long_lines");
+    this.shouldHighlight =
+      Services.prefs.getBoolPref("view_source.syntax_highlight");
+
+    addEventListener("load", this);
+    addEventListener("unload", this);
+    addEventListener("AppCommand", this, true);
+    addEventListener("MozSwipeGesture", this, true);
+
+    ViewSourceBrowser.prototype.init.call(this);
+  },
+
+  /**
+   * This should be called when the window is closing. This function should
+   * clean up event and message listeners.
+   */
+  uninit() {
+    ViewSourceBrowser.prototype.uninit.call(this);
+
+    // "load" event listener is removed in its handler, to
+    // ensure we only fire it once.
+    removeEventListener("unload", this);
+    removeEventListener("AppCommand", this, true);
+    removeEventListener("MozSwipeGesture", this, true);
+    gContextMenu.removeEventListener("popupshowing", this);
+    gContextMenu.removeEventListener("popuphidden", this);
+    Services.els.removeSystemEventListener(this.browser, "dragover", this,
+                                           true);
+    Services.els.removeSystemEventListener(this.browser, "drop", this, true);
+  },
+
+  /**
+   * 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) {
+      // 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;
+      case "ViewSource:StoreWrapping":
+        this.storeWrapping(data.state);
+        break;
+      case "ViewSource:StoreSyntaxHighlighting":
+        this.storeSyntaxHighlighting(data.state);
+        break;
+      // End messages from super class
+      case "ViewSource:SourceLoaded":
+        this.onSourceLoaded();
+        break;
+      case "ViewSource:SourceUnloaded":
+        this.onSourceUnloaded();
+        break;
+      case "ViewSource:Close":
+        this.close();
+        break;
+      case "ViewSource:OpenURL":
+        this.openURL(data.URL);
+        break;
+      case "ViewSource:ContextMenuOpening":
+        this.onContextMenuOpening(data.isLink, data.isEmail, data.href);
+        if (this.browser.isRemoteBrowser) {
+          this.openContextMenu(data.screenX, data.screenY);
+        }
+        break;
+    }
+  },
+
+  /**
+   * Any events should get handled here, and should get dispatched to
+   * a specific function for the event type.
+   */
+  handleEvent(event) {
+    switch (event.type) {
+      case "unload":
+        this.uninit();
+        break;
+      case "load":
+        this.onXULLoaded();
+        break;
+      case "AppCommand":
+        this.onAppCommand(event);
+        break;
+      case "MozSwipeGesture":
+        this.onSwipeGesture(event);
+        break;
+      case "popupshowing":
+        this.onContextMenuShowing(event);
+        break;
+      case "popuphidden":
+        this.onContextMenuHidden(event);
+        break;
+      case "dragover":
+        this.onDragOver(event);
+        break;
+      case "drop":
+        this.onDrop(event);
+        break;
+    }
+  },
+
+  /**
+   * Getter that returns whether or not the view source browser
+   * has history enabled on it.
+   */
+  get historyEnabled() {
+    return !this.browser.hasAttribute("disablehistory");
+  },
+
+  /**
+   * 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 window.messageManager;
+  },
+
+  /**
+   * Getter for the nsIWebNavigation of the view source browser.
+   */
+  get webNav() {
+    return this.browser.webNavigation;
+  },
+
+  /**
+   * Send the browser forward in its history.
+   */
+  goForward() {
+    this.browser.goForward();
+  },
+
+  /**
+   * Send the browser backward in its history.
+   */
+  goBack() {
+    this.browser.goBack();
+  },
+
+  /**
+   * This should be called once when the DOM has finished loading. Here we
+   * set the state of various menu items, and add event listeners to
+   * DOM nodes.
+   *
+   * This is also the place where we handle any arguments that have been
+   * passed to viewSource.xul.
+   *
+   * Modern consumers should pass a single object argument to viewSource.xul:
+   *
+   *   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.
+   *
+   * The deprecated API has the opener pass in a number of arguments:
+   *
+   * arg[0] - URL string.
+   * arg[1] - Charset value string in the form 'charset=xxx'.
+   * arg[2] - Page descriptor from nsIWebPageDescriptor used to load content
+   *          from the cache.
+   * arg[3] - Line number to go to.
+   * arg[4] - Boolean for whether charset was forced by the user
+   */
+  onXULLoaded() {
+    // This handler should only ever run the first time the XUL is loaded.
+    removeEventListener("load", this);
+
+    let wrapMenuItem = document.getElementById("menu_wrapLongLines");
+    if (this.shouldWrap) {
+      wrapMenuItem.setAttribute("checked", "true");
+    }
+
+    let highlightMenuItem = document.getElementById("menu_highlightSyntax");
+    if (this.shouldHighlight) {
+      highlightMenuItem.setAttribute("checked", "true");
+    }
+
+    gContextMenu.addEventListener("popupshowing", this);
+    gContextMenu.addEventListener("popuphidden", this);
+
+    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.
+      let viewSourceNavigation = document.getElementById("viewSourceNavigation");
+      if (viewSourceNavigation) {
+        viewSourceNavigation.setAttribute("disabled", "true");
+        viewSourceNavigation.setAttribute("hidden", "true");
+      }
+    }
+
+    // We require the first argument to do any loading of source.
+    // otherwise, we're done.
+    if (!window.arguments[0]) {
+      return undefined;
+    }
+
+    if (typeof window.arguments[0] == "string") {
+      // We're using the deprecated API
+      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];
+
+    // viewPartialSource.js will take care of loading the content in partial mode.
+    if (!args.partial) {
+      this.loadViewSource(args);
+    }
+
+    return undefined;
+  },
+
+  /**
+   * This is the deprecated API for viewSource.xul, for old-timer consumers.
+   * This API might eventually go away.
+   */
+  _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");
+    // Parse the 'arguments' supplied with the dialog.
+    //    arg[0] - URL string.
+    //    arg[1] - Charset value in the form 'charset=xxx'.
+    //    arg[2] - Page descriptor used to load content from the cache.
+    //    arg[3] - Line number to go to.
+    //    arg[4] - Whether charset was forced by the user
+
+    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 (this.browser.isRemoteBrowser) {
+      throw new Error("Deprecated view source API should not use a remote browser.");
+    }
+
+    let forcedCharSet;
+    if (aArguments[4] && aArguments[1].startsWith("charset=")) {
+      forcedCharSet = aArguments[1].split("=")[1];
+    }
+
+    this.sendAsyncMessage("ViewSource:LoadSourceDeprecated", {
+      URL: aArguments[0],
+      lineNumber: aArguments[3],
+      forcedCharSet,
+    }, {
+      pageDescriptor: aArguments[2],
+    });
+  },
+
+  /**
+   * Handler for the AppCommand event.
+   *
+   * @param event
+   *        The AppCommand event being handled.
+   */
+  onAppCommand(event) {
+    event.stopPropagation();
+    switch (event.command) {
+      case "Back":
+        this.goBack();
+        break;
+      case "Forward":
+        this.goForward();
+        break;
+    }
+  },
+
+  /**
+   * Handler for the MozSwipeGesture event.
+   *
+   * @param event
+   *        The MozSwipeGesture event being handled.
+   */
+  onSwipeGesture(event) {
+    event.stopPropagation();
+    switch (event.direction) {
+      case SimpleGestureEvent.DIRECTION_LEFT:
+        this.goBack();
+        break;
+      case SimpleGestureEvent.DIRECTION_RIGHT:
+        this.goForward();
+        break;
+      case SimpleGestureEvent.DIRECTION_UP:
+        goDoCommand("cmd_scrollTop");
+        break;
+      case SimpleGestureEvent.DIRECTION_DOWN:
+        goDoCommand("cmd_scrollBottom");
+        break;
+    }
+  },
+
+  /**
+   * Called as soon as the frame script reports that some source
+   * code has been loaded in the browser.
+   */
+  onSourceLoaded() {
+    document.getElementById("cmd_goToLine").removeAttribute("disabled");
+
+    if (this.historyEnabled) {
+      this.updateCommands();
+    }
+
+    this.browser.focus();
+  },
+
+  /**
+   * Called as soon as the frame script reports that some source
+   * code has been unloaded from the browser.
+   */
+  onSourceUnloaded() {
+    // Disable "go to line" while reloading due to e.g. change of charset
+    // or toggling of syntax highlighting.
+    document.getElementById("cmd_goToLine").setAttribute("disabled", "true");
+  },
+
+  /**
+   * Called by clicks on a menu populated by CharsetMenu.jsm to
+   * change the selected character set.
+   *
+   * @param event
+   *        The click event on a character set menuitem.
+   */
+  onSetCharacterSet(event) {
+    if (event.target.hasAttribute("charset")) {
+      let charset = event.target.getAttribute("charset");
+
+      // 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.sendAsyncMessage("ViewSource:SetCharacterSet", {
+        charset,
+        doPageLoad: this.historyEnabled,
+      });
+
+      if (!this.historyEnabled) {
+        this.browser
+            .reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
+      }
+    }
+  },
+
+  /**
+   * Called from the frame script when the context menu is about to
+   * open. This tells ViewSourceChrome things about the item that
+   * the context menu is being opened on. This should be called before
+   * the popupshowing event handler fires.
+   */
+  onContextMenuOpening(isLink, isEmail, href) {
+    this.contextMenuData = { isLink, isEmail, href, isOpen: true };
+  },
+
+  /**
+   * Event handler for the popupshowing event on the context menu.
+   * This handler is responsible for setting the state on various
+   * menu items in the context menu, and reads values that were sent
+   * up from the frame script and stashed into this.contextMenuData.
+   *
+   * @param event
+   *        The popupshowing event for the context menu.
+   */
+  onContextMenuShowing(event) {
+    let copyLinkMenuItem = document.getElementById("context-copyLink");
+    copyLinkMenuItem.hidden = !this.contextMenuData.isLink;
+
+    let copyEmailMenuItem = document.getElementById("context-copyEmail");
+    copyEmailMenuItem.hidden = !this.contextMenuData.isEmail;
+  },
+
+  /**
+   * Called when the user chooses the "Copy Link" or "Copy Email"
+   * menu items in the context menu. Copies the relevant selection
+   * into the system clipboard.
+   */
+  onContextMenuCopyLinkOrEmail() {
+    // It doesn't make any sense to call this if the context menu
+    // isn't open...
+    if (!this.contextMenuData.isOpen) {
+      return;
+    }
+
+    let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
+                      .getService(Ci.nsIClipboardHelper);
+    clipboard.copyString(this.contextMenuData.href);
+  },
+
+  /**
+   * Called when the context menu closes, and invalidates any data
+   * that the frame script might have sent up about what the context
+   * menu was opened on.
+   */
+  onContextMenuHidden(event) {
+    this.contextMenuData = {
+      isOpen: false,
+    };
+  },
+
+  /**
+   * Called when the user drags something over the content browser.
+   */
+  onDragOver(event) {
+    // For drags that appear to be internal text (for example, tab drags),
+    // set the dropEffect to 'none'. This prevents the drop even if some
+    // other listener cancelled the event.
+    let types = event.dataTransfer.types;
+    if (types.includes("text/x-moz-text-internal") && !types.includes("text/plain")) {
+        event.dataTransfer.dropEffect = "none";
+        event.stopPropagation();
+        event.preventDefault();
+    }
+
+    if (Services.droppedLinkHandler.canDropLink(event, false)) {
+      event.preventDefault();
+    }
+  },
+
+  /**
+   * Called twhen the user drops something onto the content browser.
+   */
+  onDrop(event) {
+    if (event.defaultPrevented)
+      return;
+
+    let name = { };
+    let uri;
+    try {
+      // Pass true to prevent the dropping of javascript:/data: URIs
+      uri = Services.droppedLinkHandler.dropLink(event, name, true);
+    } catch (e) {
+      return;
+    }
+
+    if (uri) {
+      this.loadURL(uri);
+    }
+  },
+
+  /**
+   * For remote browsers, the contextmenu event is received in the
+   * content process, and a message is sent up from the frame script
+   * to ViewSourceChrome, but then it stops. The event does not bubble
+   * up to the point that the popup is opened in the parent process.
+   * ViewSourceChrome is responsible for opening up the context menu in
+   * that case. This is called when we receive the contextmenu message
+   * from the child, and we know that the browser is currently remote.
+   *
+   * @param screenX
+   *        The screenX coordinate to open the popup at.
+   * @param screenY
+   *        The screenY coordinate to open the popup at.
+   */
+  openContextMenu(screenX, screenY) {
+    gContextMenu.openPopupAtScreen(screenX, screenY, true);
+  },
+
+  /**
+   * Loads the source of a URL. This will probably end up hitting the
+   * network.
+   *
+   * @param URL
+   *        A URL string to be opened in the view source browser.
+   */
+  loadURL(URL) {
+    this.sendAsyncMessage("ViewSource:LoadSource", { URL });
+  },
+
+  /**
+   * Updates any commands that are dependant on command broadcasters.
+   */
+  updateCommands() {
+    let backBroadcaster = document.getElementById("Browser:Back");
+    let forwardBroadcaster = document.getElementById("Browser:Forward");
+
+    if (this.webNav.canGoBack) {
+      backBroadcaster.removeAttribute("disabled");
+    } else {
+      backBroadcaster.setAttribute("disabled", "true");
+    }
+    if (this.webNav.canGoForward) {
+      forwardBroadcaster.removeAttribute("disabled");
+    } else {
+      forwardBroadcaster.setAttribute("disabled", "true");
+    }
+  },
+
+  /**
+   * Reloads the browser, bypassing the network cache.
+   */
+  reload() {
+    this.browser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY |
+                                 Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
+  },
+
+  /**
+   * Closes the view source window.
+   */
+  close() {
+    window.close();
+  },
+
+  /**
+   * Called when the user clicks on the "Wrap Long Lines" menu item.
+   */
+  toggleWrapping() {
+    this.shouldWrap = !this.shouldWrap;
+    this.sendAsyncMessage("ViewSource:ToggleWrapping");
+  },
+
+  /**
+   * Called when the user clicks on the "Syntax Highlighting" menu item.
+   */
+  toggleSyntaxHighlighting() {
+    this.shouldHighlight = !this.shouldHighlight;
+    this.sendAsyncMessage("ViewSource:ToggleSyntaxHighlighting");
+  },
+
+  /**
+   * Updates the "remote" attribute of the view source browser. This
+   * will remove the browser from the DOM, and then re-add it in the
+   * same place it was taken from.
+   *
+   * @param shouldBeRemote
+   *        True if the browser should be made remote. If the browsers
+   *        remoteness already matches this value, this function does
+   *        nothing.
+   * @param remoteType
+   *        The type of remote browser process.
+   */
+  updateBrowserRemoteness(shouldBeRemote, remoteType) {
+    if (this.browser.isRemoteBrowser == shouldBeRemote &&
+        this.browser.remoteType == remoteType) {
+      return;
+    }
+
+    let parentNode = this.browser.parentNode;
+    let nextSibling = this.browser.nextSibling;
+
+    // Removing and re-adding the browser from and to the DOM strips its XBL
+    // properties. Save and restore sameProcessAsFrameLoader. Note that when we
+    // restore sameProcessAsFrameLoader, there won't yet be a binding or
+    // setter. This works in conjunction with the hack in <xul:browser>'s
+    // constructor to re-get the weak reference to it.
+    let sameProcessAsFrameLoader = this.browser.sameProcessAsFrameLoader;
+
+    this.browser.remove();
+    if (shouldBeRemote) {
+      this.browser.setAttribute("remote", "true");
+      this.browser.setAttribute("remoteType", remoteType);
+    } else {
+      this.browser.removeAttribute("remote");
+      this.browser.removeAttribute("remoteType");
+    }
+
+    this.browser.sameProcessAsFrameLoader = sameProcessAsFrameLoader;
+
+    // If nextSibling was null, this will put the browser at
+    // the end of the list.
+    parentNode.insertBefore(this.browser, nextSibling);
+
+    if (shouldBeRemote) {
+      // We're going to send a message down to the remote browser
+      // to load the source content - however, in order for the
+      // contentWindowAsCPOW and contentDocumentAsCPOW values on
+      // the remote browser to be set, we must set up the
+      // 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 this.browser to
+      // PrintUtils.
+      this.browser.webProgress;
+    }
+  },
+};
+
+var viewSourceChrome = new ViewSourceChrome();
+
+/**
+ * PrintUtils uses this to make Print Preview work.
+ */
+var PrintPreviewListener = {
+  _ppBrowser: null,
+
+  getPrintPreviewBrowser() {
+    if (!this._ppBrowser) {
+      this._ppBrowser = document.createElement("browser");
+      this._ppBrowser.setAttribute("flex", "1");
+      this._ppBrowser.setAttribute("type", "content");
+    }
+
+    if (gBrowser.isRemoteBrowser) {
+      this._ppBrowser.setAttribute("remote", "true");
+    } else {
+      this._ppBrowser.removeAttribute("remote");
+    }
+
+    let findBar = document.getElementById("FindToolbar");
+    document.getElementById("appcontent")
+            .insertBefore(this._ppBrowser, findBar);
+
+    return this._ppBrowser;
+  },
+
+  getSourceBrowser() {
+    return gBrowser;
+  },
+
+  getNavToolbox() {
+    return document.getElementById("appcontent");
+  },
+
+  onEnter() {
+    let toolbox = document.getElementById("viewSource-toolbox");
+    toolbox.hidden = true;
+    gBrowser.collapsed = true;
+  },
+
+  onExit() {
+    this._ppBrowser.remove();
+    gBrowser.collapsed = false;
+    document.getElementById("viewSource-toolbox").hidden = false;
+  },
+
+  activateBrowser(browser) {
+    browser.docShellIsActive = true;
+  },
+};
+
+// viewZoomOverlay.js uses this
+function getBrowser() {
+  return gBrowser;
+}
+
+Object.defineProperty(this, "gPageLoader", {
+  configurable: true,
+  enumerable: true,
+  get() {
+    var webnav = viewSourceChrome.webNav;
+    if (!webnav)
+      return null;
+    delete this.gPageLoader;
+    this.gPageLoader = (webnav instanceof Ci.nsIWebPageDescriptor) ? webnav
+                                                                   : null;
+    return this.gPageLoader;
+  },
+});
+
+// Strips the |view-source:| for internalSave()
+function ViewSourceSavePage() {
+  internalSave(gBrowser.currentURI.spec.replace(/^view-source:/i, ""),
+               null, null, null, null, null, "SaveLinkTitle",
+               null, null, gBrowser.contentDocumentAsCPOW, null,
+               gPageLoader);
+}
+
+// Below are old deprecated functions and variables left behind for
+// compatibility reasons. These will be removed soon via bug 1159293.
+
+Object.defineProperty(this, "gLastLineFound", {
+  configurable: true,
+  enumerable: true,
+  get() {
+    Deprecated.warning("gLastLineFound is deprecated - please use " +
+                       "viewSourceChrome.lastLineFound instead.",
+                       "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
+    return viewSourceChrome.lastLineFound;
+  },
+});
+
+function onLoadViewSource() {
+  Deprecated.warning("onLoadViewSource() is deprecated - please use " +
+                     "viewSourceChrome.onXULLoaded() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
+  viewSourceChrome.onXULLoaded();
+}
+
+function isHistoryEnabled() {
+  Deprecated.warning("isHistoryEnabled() is deprecated - please use " +
+                     "viewSourceChrome.historyEnabled instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
+  return viewSourceChrome.historyEnabled;
+}
+
+function ViewSourceClose() {
+  Deprecated.warning("ViewSourceClose() is deprecated - please use " +
+                     "viewSourceChrome.close() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
+  viewSourceChrome.close();
+}
+
+function ViewSourceReload() {
+  Deprecated.warning("ViewSourceReload() is deprecated - please use " +
+                     "viewSourceChrome.reload() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
+  viewSourceChrome.reload();
+}
+
+function getWebNavigation() {
+  Deprecated.warning("getWebNavigation() is deprecated - please use " +
+                     "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;
+  } catch (e) {
+    return null;
+  }
+}
+
+function viewSource(url) {
+  Deprecated.warning("viewSource() is deprecated - please use " +
+                     "viewSourceChrome.loadURL() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
+  viewSourceChrome.loadURL(url);
+}
+
+function ViewSourceGoToLine() {
+  Deprecated.warning("ViewSourceGoToLine() is deprecated - please use " +
+                     "viewSourceChrome.promptAndGoToLine() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
+  viewSourceChrome.promptAndGoToLine();
+}
+
+function goToLine(line) {
+  Deprecated.warning("goToLine() is deprecated - please use " +
+                     "viewSourceChrome.goToLine() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
+  viewSourceChrome.goToLine(line);
+}
+
+function BrowserForward(aEvent) {
+  Deprecated.warning("BrowserForward() is deprecated - please use " +
+                     "viewSourceChrome.goForward() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
+  viewSourceChrome.goForward();
+}
+
+function BrowserBack(aEvent) {
+  Deprecated.warning("BrowserBack() is deprecated - please use " +
+                     "viewSourceChrome.goBack() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
+  viewSourceChrome.goBack();
+}
+
+function UpdateBackForwardCommands() {
+  Deprecated.warning("UpdateBackForwardCommands() is deprecated - please use " +
+                     "viewSourceChrome.updateCommands() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/View_Source_for_XUL_Applications");
+  viewSourceChrome.updateCommands();
+}
new file mode 100644
--- /dev/null
+++ b/common/src/viewSource.xul
@@ -0,0 +1,229 @@
+<?xml version="1.0"?>
+# -*- Mode: HTML -*-
+# 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/.
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/content/viewSource.css" type="text/css"?>
+<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % sourceDTD SYSTEM "chrome://messenger/locale/viewSource.dtd" >
+%sourceDTD;
+]>
+
+<window id="viewSource"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        contenttitlesetting="true"
+        title="&mainWindow.title;"
+        titlemodifier="&mainWindow.titlemodifier;"
+        titlepreface="&mainWindow.preface;"
+        titlemenuseparator ="&mainWindow.titlemodifierseparator;"
+        windowtype="navigator:view-source"
+        width="640" height="480"
+        screenX="10" screenY="10"
+        persist="screenX screenY width height sizemode">
+
+  <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
+  <script type="application/javascript" src="chrome://global/content/printUtils.js"/>
+  <script type="application/javascript" src="chrome://messenger/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://messenger/locale/viewSource.properties"/>
+
+  <command id="cmd_savePage" oncommand="ViewSourceSavePage();"/>
+  <command id="cmd_print" oncommand="PrintUtils.printWindow(gBrowser.outerWindowID, gBrowser);"/>
+  <command id="cmd_printpreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
+  <command id="cmd_pagesetup" oncommand="PrintUtils.showPageSetup();"/>
+  <command id="cmd_close" oncommand="window.close();"/>
+  <commandset id="editMenuCommands"/>
+  <command id="cmd_find"
+           oncommand="document.getElementById('FindToolbar').onFindCommand();"/>
+  <command id="cmd_findAgain"
+           oncommand="document.getElementById('FindToolbar').onFindAgainCommand(false);"/>
+  <command id="cmd_findPrevious"
+           oncommand="document.getElementById('FindToolbar').onFindAgainCommand(true);"/>
+#ifdef XP_MACOSX
+  <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_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"/>
+
+  <broadcaster id="viewSourceNavigation"/>
+
+  <keyset id="editMenuKeys"/>
+  <keyset id="viewSourceKeys">
+    <key id="key_savePage" key="&savePageCmd.commandkey;" modifiers="accel" command="cmd_savePage"/>
+    <key id="key_print" key="&printCmd.commandkey;" modifiers="accel" command="cmd_print"/>
+    <key id="key_close" key="&closeCmd.commandkey;" modifiers="accel" command="cmd_close"/>
+    <key id="key_goToLine"     key="&goToLineCmd.commandkey;"  command="cmd_goToLine"  modifiers="accel"/>
+
+    <key id="key_textZoomEnlarge" key="&textEnlarge.commandkey;" command="cmd_textZoomEnlarge" modifiers="accel"/>
+    <key id="key_textZoomEnlarge2" key="&textEnlarge.commandkey2;" command="cmd_textZoomEnlarge" modifiers="accel"/>
+    <key id="key_textZoomEnlarge3" key="&textEnlarge.commandkey3;" command="cmd_textZoomEnlarge" modifiers="accel"/>
+    <key id="key_textZoomReduce"  key="&textReduce.commandkey;" command="cmd_textZoomReduce" modifiers="accel"/>
+    <key id="key_textZoomReduce2"  key="&textReduce.commandkey2;" command="cmd_textZoomReduce" modifiers="accel"/>
+    <key id="key_textZoomReset" key="&textReset.commandkey;" command="cmd_textZoomReset" modifiers="accel"/>
+    <key id="key_textZoomReset2" key="&textReset.commandkey2;" command="cmd_textZoomReset" modifiers="accel"/>
+
+    <key id="key_reload" key="&reloadCmd.commandkey;" command="cmd_reload" modifiers="accel"/>
+    <key key="&reloadCmd.commandkey;" command="cmd_reload" modifiers="accel,shift"/>
+    <key keycode="VK_F5" command="cmd_reload"/>
+    <key keycode="VK_F5" command="cmd_reload" modifiers="accel"/>
+    <key id="key_find" key="&findOnCmd.commandkey;" command="cmd_find" modifiers="accel"/>
+    <key id="key_findAgain" key="&findAgainCmd.commandkey;" command="cmd_findAgain" modifiers="accel"/>
+    <key id="key_findPrevious" key="&findAgainCmd.commandkey;" command="cmd_findPrevious" modifiers="accel,shift"/>
+#ifdef XP_MACOSX
+    <key id="key_findSelection" key="&findSelectionCmd.commandkey;" command="cmd_findSelection" modifiers="accel"/>
+#endif
+    <key keycode="&findAgainCmd.commandkey2;" command="cmd_findAgain"/>
+    <key keycode="&findAgainCmd.commandkey2;"  command="cmd_findPrevious" modifiers="shift"/>
+
+    <key keycode="VK_BACK" command="Browser:Back"/>
+    <key keycode="VK_BACK" command="Browser:Forward" modifiers="shift"/>
+#ifndef XP_MACOSX
+    <key id="goBackKb" keycode="VK_LEFT" command="Browser:Back" modifiers="alt"/>
+    <key id="goForwardKb" keycode="VK_RIGHT" command="Browser:Forward" modifiers="alt"/>
+#else
+    <key id="goBackKb" keycode="VK_LEFT" command="Browser:Back" modifiers="accel" />
+    <key id="goForwardKb" keycode="VK_RIGHT" command="Browser:Forward" modifiers="accel" />
+#endif
+#ifdef XP_UNIX
+    <key id="goBackKb2" key="&goBackCmd.commandKey;" command="Browser:Back" modifiers="accel"/>
+    <key id="goForwardKb2" key="&goForwardCmd.commandKey;" command="Browser:Forward" modifiers="accel"/>
+#endif
+
+  </keyset>
+
+  <tooltip id="aHTMLTooltip" page="true"/>
+
+  <menupopup id="viewSourceContextMenu">
+    <menuitem id="context-back"
+              label="&backCmd.label;"
+              accesskey="&backCmd.accesskey;"
+              command="Browser:Back"
+              observes="viewSourceNavigation"/>
+    <menuitem id="context-forward"
+              label="&forwardCmd.label;"
+              accesskey="&forwardCmd.accesskey;"
+              command="Browser:Forward"
+              observes="viewSourceNavigation"/>
+    <menuseparator observes="viewSourceNavigation"/>
+    <menuitem id="cMenu_findAgain"/>
+    <menuseparator/>
+    <menuitem id="cMenu_copy"/>
+    <menuitem id="context-copyLink"
+              label="&copyLinkCmd.label;"
+              accesskey="&copyLinkCmd.accesskey;"
+              oncommand="viewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
+    <menuitem id="context-copyEmail"
+              label="&copyEmailCmd.label;"
+              accesskey="&copyEmailCmd.accesskey;"
+              oncommand="viewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
+    <menuseparator/>
+    <menuitem id="cMenu_selectAll"/>
+  </menupopup>
+
+  <!-- Menu -->
+  <toolbox id="viewSource-toolbox">
+    <menubar id="viewSource-main-menubar">
+
+      <menu id="menu_file" label="&fileMenu.label;" accesskey="&fileMenu.accesskey;">
+        <menupopup id="menu_FilePopup">
+          <menuitem key="key_savePage" command="cmd_savePage" id="menu_savePage"
+                    label="&savePageCmd.label;" accesskey="&savePageCmd.accesskey;"/>
+          <menuitem command="cmd_pagesetup" id="menu_pageSetup"
+                    label="&pageSetupCmd.label;" accesskey="&pageSetupCmd.accesskey;"/>
+#ifndef XP_MACOSX
+          <menuitem command="cmd_printpreview" id="menu_printPreview"
+                    label="&printPreviewCmd.label;" accesskey="&printPreviewCmd.accesskey;"/>
+#endif
+          <menuitem key="key_print" command="cmd_print" id="menu_print"
+                    label="&printCmd.label;" accesskey="&printCmd.accesskey;"/>
+          <menuseparator/>
+          <menuitem key="key_close" command="cmd_close" id="menu_close"
+                    label="&closeCmd.label;" accesskey="&closeCmd.accesskey;"/>
+        </menupopup>
+      </menu>
+
+      <menu id="menu_edit">
+        <menupopup id="editmenu-popup">
+          <menuitem id="menu_undo"/>
+          <menuitem id="menu_redo"/>
+          <menuseparator/>
+          <menuitem id="menu_cut"/>
+          <menuitem id="menu_copy"/>
+          <menuitem id="menu_paste"/>
+          <menuitem id="menu_delete"/>
+          <menuseparator/>
+          <menuitem id="menu_selectAll"/>
+          <menuseparator/>
+          <menuitem id="menu_find"/>
+          <menuitem id="menu_findAgain"/>
+          <menuseparator/>
+          <menuitem id="menu_goToLine" key="key_goToLine" command="cmd_goToLine"
+                    label="&goToLineCmd.label;" accesskey="&goToLineCmd.accesskey;"/>
+        </menupopup>
+      </menu>
+
+      <menu id="menu_view" label="&viewMenu.label;" accesskey="&viewMenu.accesskey;">
+        <menupopup id="viewmenu-popup">
+          <menuitem id="menu_reload" command="cmd_reload" accesskey="&reloadCmd.accesskey;"
+                    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"
+                        label="&menu_textEnlarge.label;" accesskey="&menu_textEnlarge.accesskey;"
+                        key="key_textZoomEnlarge"/>
+              <menuitem id="menu_textReduce" command="cmd_textZoomReduce"
+                        label="&menu_textReduce.label;" accesskey="&menu_textReduce.accesskey;"
+                        key="key_textZoomReduce"/>
+              <menuseparator/>
+              <menuitem id="menu_textReset" command="cmd_textZoomReset"
+                        label="&menu_textReset.label;" accesskey="&menu_textReset.accesskey;"
+                        key="key_textZoomReset"/>
+            </menupopup>
+          </menu>
+
+          <!-- Charset Menu -->
+          <menu id="charsetMenu"
+                label="&charsetMenu2.label;"
+                accesskey="&charsetMenu2.accesskey;"
+                oncommand="viewSourceChrome.onSetCharacterSet(event);"
+                onpopupshowing="CharsetMenu.build(event.target);
+                                CharsetMenu.update(event.target, content.document.characterSet);">
+            <menupopup/>
+          </menu>
+          <menuseparator/>
+          <menuitem id="menu_wrapLongLines" type="checkbox" command="cmd_wrapLongLines"
+                    label="&menu_wrapLongLines.title;" accesskey="&menu_wrapLongLines.accesskey;"/>
+          <menuitem type="checkbox" id="menu_highlightSyntax" command="cmd_highlightSyntax"
+                    label="&menu_highlightSyntax.label;" accesskey="&menu_highlightSyntax.accesskey;"/>
+        </menupopup>
+      </menu>
+    </menubar>
+  </toolbox>
+
+  <vbox id="appcontent" flex="1">
+
+    <browser id="content" type="content" name="content" src="about:blank" flex="1"
+             primary="true"
+             context="viewSourceContextMenu" showcaret="true" tooltip="aHTMLTooltip" />
+    <findbar id="FindToolbar" browserid="content"/>
+  </vbox>
+
+</window>
--- a/mail/base/content/mailCommands.js
+++ b/mail/base/content/mailCommands.js
@@ -503,17 +503,17 @@ function ViewPageSource(messages)
     for (var i = 0; i < numMessages; i++)
     {
       // Now, we need to get a URL from a URI
       var url = MailServices.mailSession.ConvertMsgURIToMsgURL(messages[i], msgWindow);
 
       // Strip out the message-display parameter to ensure that attached emails
       // display the message source, not the processed HTML.
       url = url.replace(/type=application\/x-message-display&/, "");
-      window.openDialog("chrome://global/content/viewSource.xul",
+      window.openDialog("chrome://messenger/content/viewSource.xul",
                         "_blank", "all,dialog=no",
                         {URL: url, browser: browser,
                          outerWindowID: browser.outerWindowID});
     }
     return true;
   } catch (e) {
     // Couldn't get mail session
     return false;
--- a/mail/base/jar.mn
+++ b/mail/base/jar.mn
@@ -1,16 +1,16 @@
 #filter substitution
 
 messenger.jar:
 % content messagebody %content/messagebody/ contentaccessible=yes
 % content messenger %content/messenger/
 % override chrome://global/content/nsDragAndDrop.js chrome://messenger/content/nsDragAndDrop.js
 % override chrome://messagebody/skin/messageBody.css chrome://messenger/skin/messageBody.css
-% overlay chrome://global/content/viewSource.xul chrome://messenger/content/viewSourceOverlay.xul
+% overlay chrome://messenger/content/viewSource.xul chrome://messenger/content/viewSourceOverlay.xul
 % overlay chrome://global/content/config.xul chrome://messenger/content/configEditorOverlay.xul
 % overlay chrome://editor/content/EdSpellCheck.xul chrome://messenger/content/EdSpellCheckOverlay.xul
 % overlay about:addons chrome://messenger/content/extensionsOverlay.xul
 % style about:addons chrome://messenger/content/extensionsOverlay.css
 % style chrome://messenger/content/customizeToolbar.xul chrome://messenger/content/messenger.css
 % style chrome://messenger/content/customizeToolbar.xul chrome://messenger/skin/
 % style chrome://messenger/content/customizeToolbar.xul chrome://messenger/skin/addressbook/addressbook.css
 % style chrome://messenger/content/customizeToolbar.xul chrome://messenger/skin/messengercompose/messengercompose.css
@@ -35,16 +35,19 @@ messenger.jar:
     content/messenger/hiddenWindow.js               (content/hiddenWindow.js)
     content/messenger/msgHdrViewOverlay.js          (content/msgHdrViewOverlay.js)
     content/messenger/msgHdrViewOverlay.xul         (content/msgHdrViewOverlay.xul)
     content/messenger/msgViewNavigation.js          (content/msgViewNavigation.js)
     content/messenger/mailWidgets.xml               (content/mailWidgets.xml)
     content/messenger/customizeToolbar.css          (../../common/src/customizeToolbar.css)
     content/messenger/customizeToolbar.js           (../../common/src/customizeToolbar.js)
 *   content/messenger/customizeToolbar.xul          (../../common/src/customizeToolbar.xul)
+    content/messenger/viewSource.css                (../../common/src/viewSource.css)
+    content/messenger/viewSource.js                 (../../common/src/viewSource.js)
+*   content/messenger/viewSource.xul                (../../common/src/viewSource.xul)
 *   content/messenger/datetimepicker.xml            (../../common/bindings/datetimepicker.xml)
     content/messenger/generalBindings.xml           (../../common/bindings/generalBindings.xml)
 *   content/messenger/toolbar.xml                   (../../common/bindings/toolbar.xml)
 *   content/messenger/bindings.css                  (content/bindings.css)
     content/messenger/nsDragAndDrop.js              (content/nsDragAndDrop.js)
     content/messenger/editContactOverlay.js         (content/editContactOverlay.js)
 *   content/messenger/editContactOverlay.xul        (content/editContactOverlay.xul)
     content/messenger/msgMail3PaneWindow.js         (content/msgMail3PaneWindow.js)
new file mode 100644
--- /dev/null
+++ b/mail/locales/en-US/chrome/messenger/viewSource.dtd
@@ -0,0 +1,91 @@
+<!-- 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/. -->
+
+<!-- extracted from content/viewSource.xul -->
+
+<!-- LOCALIZATION NOTE (mainWindow.title) : DONT_TRANSLATE --> 
+<!ENTITY mainWindow.title "&brandFullName;">
+<!-- LOCALIZATION NOTE (mainWindow.titlemodifier) : DONT_TRANSLATE --> 
+<!ENTITY mainWindow.titlemodifier "&brandFullName;">
+<!-- LOCALIZATION NOTE (mainWindow.titlemodifierseparator) : DONT_TRANSLATE -->
+<!ENTITY mainWindow.titlemodifierseparator " - ">
+<!ENTITY mainWindow.preface "Source of: ">
+
+<!ENTITY fileMenu.label "File">
+<!ENTITY fileMenu.accesskey "F">
+<!ENTITY savePageCmd.label "Save Page As…">
+<!ENTITY savePageCmd.accesskey "A">
+<!ENTITY savePageCmd.commandkey "S">
+<!ENTITY pageSetupCmd.label "Page Setup…">
+<!ENTITY pageSetupCmd.accesskey "u">
+<!ENTITY printPreviewCmd.label "Print Preview">
+<!ENTITY printPreviewCmd.accesskey "v">
+<!ENTITY printCmd.label "Print…">
+<!ENTITY printCmd.accesskey "P">
+<!ENTITY printCmd.commandkey "P">
+<!ENTITY closeCmd.label "Close">
+<!ENTITY closeCmd.accesskey "C">
+<!ENTITY closeCmd.commandkey "W">
+
+<!-- LOCALIZATION NOTE :
+textEnlarge.commandkey3, textReduce.commandkey2 and
+textReset.commandkey2 are alternative acceleration keys for zoom.
+If shift key is needed with your locale popular keyboard for them,
+you can use these alternative items. Otherwise, their values should be empty.  -->
+
+<!ENTITY textEnlarge.commandkey "+">
+<!ENTITY textEnlarge.commandkey2 "=">
+<!ENTITY textEnlarge.commandkey3 "">
+<!ENTITY textReduce.commandkey "-">
+<!ENTITY textReduce.commandkey2 "">
+<!ENTITY textReset.commandkey "0">
+<!ENTITY textReset.commandkey2 "">
+
+<!ENTITY goToLineCmd.label "Go to Line…">
+<!ENTITY goToLineCmd.accesskey "G">
+<!ENTITY goToLineCmd.commandkey "l">
+
+<!ENTITY viewMenu.label           "View">
+<!ENTITY viewMenu.accesskey       "V">
+<!ENTITY reloadCmd.label "Reload">
+<!ENTITY reloadCmd.accesskey "R">
+<!ENTITY reloadCmd.commandkey "r">
+<!ENTITY menu_wrapLongLines.title "Wrap Long Lines"> 
+<!ENTITY menu_wrapLongLines.accesskey "W">
+<!ENTITY menu_highlightSyntax.label "Syntax Highlighting">
+<!ENTITY menu_highlightSyntax.accesskey "H">
+<!ENTITY menu_textSize.label "Text Size">
+<!ENTITY menu_textSize.accesskey "Z">
+<!ENTITY menu_textEnlarge.label "Increase">
+<!ENTITY menu_textEnlarge.accesskey "I">
+<!ENTITY menu_textReduce.label "Decrease">
+<!ENTITY menu_textReduce.accesskey "D">
+<!ENTITY menu_textReset.label "Normal">
+<!ENTITY menu_textReset.accesskey "N">
+
+<!ENTITY findOnCmd.label     "Find in This Page…">
+<!ENTITY findOnCmd.accesskey "F">
+<!ENTITY findOnCmd.commandkey "f">
+<!ENTITY findAgainCmd.label  "Find Again">
+<!ENTITY findAgainCmd.accesskey "g">
+<!ENTITY findAgainCmd.commandkey "g">
+<!ENTITY findAgainCmd.commandkey2 "VK_F3">
+<!ENTITY findSelectionCmd.commandkey "e">
+
+<!ENTITY backCmd.label "Back">
+<!ENTITY backCmd.accesskey "B">
+<!ENTITY forwardCmd.label "Forward">
+<!ENTITY forwardCmd.accesskey "F">
+<!ENTITY goBackCmd.commandKey "[">
+<!ENTITY goForwardCmd.commandKey "]">
+
+<!ENTITY copyLinkCmd.label "Copy Link Location">
+<!ENTITY copyLinkCmd.accesskey "L">
+<!ENTITY copyEmailCmd.label "Copy Email Address">
+<!ENTITY copyEmailCmd.accesskey "E">
+
+<!-- Copied from charsetMenu.dtd -->
+<!ENTITY charsetMenu2.label            "Text Encoding">
+<!ENTITY charsetMenu2.accesskey        "c">
+
new file mode 100644
--- /dev/null
+++ b/mail/locales/en-US/chrome/messenger/viewSource.properties
@@ -0,0 +1,17 @@
+# 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/.
+
+goToLineTitle     = Go to line
+goToLineText      = Enter line number
+invalidInputTitle = Invalid input
+invalidInputText  = The line number entered is invalid.
+outOfRangeTitle   = Line not found
+outOfRangeText    = The specified line was not found.
+viewSelectionSourceTitle = DOM Source of Selection
+viewMathMLSourceTitle    = DOM Source of MathML
+
+context_goToLine_label        = Go to Line…
+context_goToLine_accesskey    = L
+context_wrapLongLines_label   = Wrap Long Lines
+context_highlightSyntax_label = Syntax Highlighting
--- a/mail/locales/jar.mn
+++ b/mail/locales/jar.mn
@@ -19,16 +19,18 @@
   locale/@AB_CD@/messenger/telemetry.properties                         (%chrome/messenger/telemetry.properties)
   locale/@AB_CD@/messenger/accountCreation.dtd                          (%chrome/messenger/accountCreation.dtd)
   locale/@AB_CD@/messenger/accountCreation.properties                   (%chrome/messenger/accountCreation.properties)
   locale/@AB_CD@/messenger/accountCreationModel.properties              (%chrome/messenger/accountCreationModel.properties)
   locale/@AB_CD@/messenger/accountCreationUtil.properties               (%chrome/messenger/accountCreationUtil.properties)
   locale/@AB_CD@/messenger/charsetTitles.properties                     (%chrome/messenger/charsetTitles.properties)
   locale/@AB_CD@/messenger/customizeToolbar.dtd                         (%chrome/messenger/customizeToolbar.dtd)
   locale/@AB_CD@/messenger/customizeToolbar.properties                  (%chrome/messenger/customizeToolbar.properties)
+  locale/@AB_CD@/messenger/viewSource.dtd                               (%chrome/messenger/viewSource.dtd)
+  locale/@AB_CD@/messenger/viewSource.properties                        (%chrome/messenger/viewSource.properties)
   locale/@AB_CD@/messenger/datetimepicker.dtd                           (%chrome/messenger/datetimepicker.dtd)
   locale/@AB_CD@/messenger/systemIntegrationDialog.dtd                  (%chrome/messenger/systemIntegrationDialog.dtd)
   locale/@AB_CD@/messenger/virtualFolderProperties.dtd                  (%chrome/messenger/virtualFolderProperties.dtd)
   locale/@AB_CD@/messenger/virtualFolderListDialog.dtd                  (%chrome/messenger/virtualFolderListDialog.dtd)
   locale/@AB_CD@/messenger/multimessageview.properties                  (%chrome/messenger/multimessageview.properties)
   locale/@AB_CD@/messenger/multimessageview.dtd                         (%chrome/messenger/multimessageview.dtd)
   locale/@AB_CD@/messenger/mailOverlay.dtd                              (%chrome/messenger/mailOverlay.dtd)
   locale/@AB_CD@/messenger/messenger.dtd                                (%chrome/messenger/messenger.dtd)