Merge fx-team to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 28 Oct 2014 19:02:36 -0700
changeset 237103 7e3c85754d32544625f785703d98e0e7d0ee4daf
parent 237088 e7e1418113ccc4b02b71fcdcb223c6dd16464929 (current diff)
parent 237102 99d578d3c8a1ca10e16f84067daa5d99d5b7c8f5 (diff)
child 237104 366d06412304c16c2c8cdafed6afecb5576c92cd
child 237119 6bc7eedcee7f1b1255193808186d2c23597cb826
child 237154 35870244d187f49ad5c50cb8cf08caf27df432e6
child 237161 d2e06eb128bdb262747420f09db0b002cd4ba019
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone36.0a1
first release with
nightly linux32
7e3c85754d32 / 36.0a1 / 20141029030207 / files
nightly linux64
7e3c85754d32 / 36.0a1 / 20141029030207 / files
nightly mac
7e3c85754d32 / 36.0a1 / 20141029030207 / files
nightly win32
7e3c85754d32 / 36.0a1 / 20141029030207 / files
nightly win64
7e3c85754d32 / 36.0a1 / 20141029030207 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c a=merge
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1761,8 +1761,15 @@ pref("experiments.supported", true);
 
 // Enable the OpenH264 plugin support in the addon manager.
 pref("media.gmp-gmpopenh264.provider.enabled", true);
 
 pref("browser.apps.URL", "https://marketplace.firefox.com/discovery/");
 
 pref("browser.polaris.enabled", false);
 pref("privacy.trackingprotection.ui.enabled", false);
+
+// Temporary pref to allow printing in e10s windows on some platforms.
+#ifdef UNIX_BUT_NOT_MAC
+pref("print.enable_e10s_testing", false);
+#else
+pref("print.enable_e10s_testing", true);
+#endif
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -23,17 +23,17 @@
     <command id="cmd_newNavigatorTab" oncommand="BrowserOpenNewTabOrWindow(event);"/>
     <command id="Browser:OpenFile"  oncommand="BrowserOpenFileWindow();"/>
     <command id="Browser:SavePage" oncommand="saveDocument(window.content.document);"/>
 
     <command id="Browser:SendLink"
              oncommand="MailIntegration.sendLinkForWindow(window.content);"/>
 
     <command id="cmd_pageSetup" oncommand="PrintUtils.showPageSetup();"/>
-    <command id="cmd_print" oncommand="PrintUtils.print();"/>
+    <command id="cmd_print" oncommand="PrintUtils.print(window.gBrowser.selectedBrowser.contentWindowAsCPOW, window.gBrowser.selectedBrowser);"/>
     <command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
     <command id="cmd_close" oncommand="BrowserCloseTabOrWindow()"/>
     <command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()"/>
     <command id="cmd_CustomizeToolbars" oncommand="BrowserCustomizeToolbar()"/>
     <command id="cmd_quitApplication" oncommand="goQuitApplication()"/>
 
 
     <commandset id="editMenuCommands"/>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1658,17 +1658,18 @@ function HandleAppCommandEvent(evt) {
     break;
   case "Help":
     openHelpLink('firefox-help');
     break;
   case "Open":
     BrowserOpenFileWindow();
     break;
   case "Print":
-    PrintUtils.print();
+    PrintUtils.print(gBrowser.selectedBrowser.contentWindowAsCPOW,
+                     gBrowser.selectedBrowser);
     break;
   case "Save":
     saveDocument(window.content.document);
     break;
   case "SendMail":
     MailIntegration.sendLinkForWindow(window.content);
     break;
   default:
--- a/browser/components/loop/LoopCalls.jsm
+++ b/browser/components/loop/LoopCalls.jsm
@@ -289,17 +289,18 @@ let LoopCallsInternal = {
    */
   startDirectCall: function(contact, callType) {
     if (this.callsData.inUse)
       return false;
 
     var callData = {
       contact: contact,
       callType: callType,
-      callId: Math.floor((Math.random() * 10))
+      // XXX Really we shouldn't be using random numbers, bug 1090209 will fix this.
+      callId: Math.floor((Math.random() * 100000000))
     };
 
     this._startCall(callData, "outgoing");
     return true;
   },
 
    /**
    * Open call progress websocket and terminate with a reason of busy
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -566,28 +566,27 @@
   background-position: center;
   border-radius: 50%;
 }
 
   .fx-embedded-tiny-video-icon.muted {
     background-color: rgba(0,0,0,.2)
   }
 
-@media screen and (min-width:640px) {
+/* Force full height on all parents up to the video elements
+ * this way we can ensure the aspect ratio and use height 100%
+ * on the video element
+ * */
+html, .fx-embedded, #main,
+.video-layout-wrapper,
+.conversation {
+  height: 100%;
+}
 
-  /* Force full height on all parents up to the video elements
-   * this way we can ensure the aspect ratio and use height 100%
-   * on the video element
-   * */
-  html, body, #main,
-  .video-layout-wrapper,
-  .conversation {
-    height: 100%;
-  }
-
+@media screen and (min-width:640px) {
   .standalone .conversation-toolbar {
     position: absolute;
     bottom: 0;
     left: 0;
     right: 0;
   }
 
   .fx-embedded .local-stream {
--- a/browser/devtools/main.js
+++ b/browser/devtools/main.js
@@ -112,17 +112,17 @@ Tools.inspector = {
   ],
 
   preventClosingOnKey: true,
   onkey: function(panel) {
     panel.toolbox.highlighterUtils.togglePicker();
   },
 
   isTargetSupported: function(target) {
-    return !target.isAddon;
+    return !target.isAddon && target.hasActor("inspector");
   },
 
   build: function(iframeWindow, toolbox) {
     return new InspectorPanel(iframeWindow, toolbox);
   }
 };
 
 Tools.webConsole = {
@@ -194,17 +194,18 @@ Tools.styleEditor = {
   url: "chrome://browser/content/devtools/styleeditor.xul",
   label: l10n("ToolboxStyleEditor.label", styleEditorStrings),
   panelLabel: l10n("ToolboxStyleEditor.panelLabel", styleEditorStrings),
   tooltip: l10n("ToolboxStyleEditor.tooltip2", styleEditorStrings),
   inMenu: true,
   commands: "devtools/styleeditor/styleeditor-commands",
 
   isTargetSupported: function(target) {
-    return !target.isAddon;
+    return !target.isAddon &&
+      (target.hasActor("styleEditor") || target.hasActor("styleSheets"));
   },
 
   build: function(iframeWindow, toolbox) {
     return new StyleEditorPanel(iframeWindow, toolbox);
   }
 };
 
 Tools.shaderEditor = {
@@ -262,17 +263,17 @@ Tools.jsprofiler = {
   label: l10n("profiler.label2", profilerStrings),
   panelLabel: l10n("profiler.panelLabel2", profilerStrings),
   tooltip: l10n("profiler.tooltip2", profilerStrings),
   inMenu: true,
 
   isTargetSupported: function (target) {
     // Hide the profiler when debugging devices pre bug 1046394,
     // that don't expose profiler actor in content processes.
-    return !target.isAddon && (!target.isApp || target.form.profilerActor);
+    return !target.isAddon && target.hasActor("profiler");
   },
 
   build: function (frame, target) {
     return new ProfilerPanel(frame, target);
   }
 };
 
 Tools.performance = {
@@ -286,19 +287,17 @@ Tools.performance = {
   panelLabel: "Performance++", //l10n("profiler.panelLabel2", profilerStrings),
   tooltip: l10n("profiler.tooltip2", profilerStrings),
   accesskey: l10n("profiler.accesskey", profilerStrings),
   key: l10n("profiler.commandkey2", profilerStrings),
   modifiers: "shift",
   inMenu: true,
 
   isTargetSupported: function (target) {
-    // Hide the profiler when debugging devices pre bug 1046394,
-    // that don't expose profiler actor in content processes.
-    return !target.isAddon && (!target.isApp || target.form.profilerActor);
+    return !target.isAddon && target.hasActor("profiler");
   },
 
   build: function (frame, target) {
     return new PerformancePanel(frame, target);
   }
 };
 
 Tools.timeline = {
@@ -333,18 +332,17 @@ Tools.netMonitor = {
   invertIconForLightTheme: true,
   url: "chrome://browser/content/devtools/netmonitor.xul",
   label: l10n("netmonitor.label", netMonitorStrings),
   panelLabel: l10n("netmonitor.panelLabel", netMonitorStrings),
   tooltip: l10n("netmonitor.tooltip", netMonitorStrings),
   inMenu: true,
 
   isTargetSupported: function(target) {
-    let root = target.client.mainRoot;
-    return !target.isAddon && (root.traits.networkMonitor || !target.isApp);
+    return !target.isAddon && target.getTrait("networkMonitor");
   },
 
   build: function(iframeWindow, toolbox) {
     return new NetMonitorPanel(iframeWindow, toolbox);
   }
 };
 
 Tools.storage = {
--- a/browser/devtools/profiler/test/browser_profiler_tree-view-01.js
+++ b/browser/devtools/profiler/test/browser_profiler_tree-view-01.js
@@ -24,17 +24,17 @@ function test() {
 
   is(container.childNodes[0].childNodes.length, 6,
     "The root node in the tree has the correct number of children.");
   is(container.childNodes[0].querySelectorAll(".call-tree-cell").length, 6,
     "The root node in the tree has only 'call-tree-cell' children.");
 
   is(container.childNodes[0].childNodes[0].getAttribute("type"), "duration",
     "The root node in the tree has a duration cell.");
-  is(container.childNodes[0].childNodes[0].getAttribute("value"), "18",
+  is(container.childNodes[0].childNodes[0].getAttribute("value"), "15",
     "The root node in the tree has the correct duration cell value.");
 
   is(container.childNodes[0].childNodes[1].getAttribute("type"), "percentage",
     "The root node in the tree has a percentage cell.");
   is(container.childNodes[0].childNodes[1].getAttribute("value"), "100%",
     "The root node in the tree has the correct percentage cell value.");
 
   is(container.childNodes[0].childNodes[2].getAttribute("type"), "self-duration",
@@ -44,17 +44,17 @@ function test() {
 
   is(container.childNodes[0].childNodes[3].getAttribute("type"), "self-percentage",
     "The root node in the tree has a self-percentage cell.");
   is(container.childNodes[0].childNodes[3].getAttribute("value"), "0%",
     "The root node in the tree has the correct self-percentage cell value.");
 
   is(container.childNodes[0].childNodes[4].getAttribute("type"), "samples",
     "The root node in the tree has an samples cell.");
-  is(container.childNodes[0].childNodes[4].getAttribute("value"), "3",
+  is(container.childNodes[0].childNodes[4].getAttribute("value"), "4",
     "The root node in the tree has the correct samples cell value.");
 
   is(container.childNodes[0].childNodes[5].getAttribute("type"), "function",
     "The root node in the tree has a function cell.");
   is(container.childNodes[0].childNodes[5].style.MozMarginStart, "0px",
     "The root node in the tree has the correct indentation.");
 
   finish();
@@ -64,24 +64,32 @@ let gSamples = [{
   time: 5,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 32, location: "C (http://foo/bar/baz:56)" }
   ]
 }, {
-  time: 5 + 6,
+  time: 5 + 1,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 64, location: "D (http://foo/bar/baz:78)" }
   ]
 }, {
-  time: 5 + 6 + 7,
+  time: 5 + 1 + 2,
+  frames: [
+    { category: 8,  location: "(root)" },
+    { category: 8,  location: "A (http://foo/bar/baz:12)" },
+    { category: 16, location: "B (http://foo/bar/baz:34)" },
+    { category: 64, location: "D (http://foo/bar/baz:78)" }
+  ]
+}, {
+  time: 5 + 1 + 2 + 7,
   frames: [
     { category: 8,   location: "(root)" },
     { category: 8,   location: "A (http://foo/bar/baz:12)" },
     { category: 128, location: "E (http://foo/bar/baz:90)" },
     { category: 256, location: "F (http://foo/bar/baz:99)" }
   ]
 }];
--- a/browser/devtools/profiler/test/browser_profiler_tree-view-02.js
+++ b/browser/devtools/profiler/test/browser_profiler_tree-view-02.js
@@ -22,21 +22,21 @@ function test() {
   let $$perc = i => container.querySelectorAll(".call-tree-cell[type=percentage]")[i];
   let $$sampl = i => container.querySelectorAll(".call-tree-cell[type=samples]")[i];
 
   is(container.childNodes.length, 1,
     "The container node should have one child available.");
   is(container.childNodes[0].className, "call-tree-item",
     "The root node in the tree has the correct class name.");
 
-  is($$dur(0).getAttribute("value"), "18",
+  is($$dur(0).getAttribute("value"), "15",
     "The root's duration cell displays the correct value.");
   is($$perc(0).getAttribute("value"), "100%",
     "The root's percentage cell displays the correct value.");
-  is($$sampl(0).getAttribute("value"), "3",
+  is($$sampl(0).getAttribute("value"), "4",
     "The root's samples cell displays the correct value.");
   is($$fun(".call-tree-name")[0].getAttribute("value"), "(root)",
     "The root's function cell displays the correct name.");
   is($$fun(".call-tree-url")[0].getAttribute("value"), "",
     "The root's function cell displays the correct url.");
   is($$fun(".call-tree-line")[0].getAttribute("value"), "",
     "The root's function cell displays the correct line.");
   is($$fun(".call-tree-host")[0].getAttribute("value"), "",
@@ -48,21 +48,21 @@ function test() {
 
   is(container.childNodes.length, 2,
     "The container node should have two children available.");
   is(container.childNodes[0].className, "call-tree-item",
     "The root node in the tree has the correct class name.");
   is(container.childNodes[1].className, "call-tree-item",
     "The .A node in the tree has the correct class name.");
 
-  is($$dur(1).getAttribute("value"), "18",
+  is($$dur(1).getAttribute("value"), "15",
     "The .A node's duration cell displays the correct value.");
   is($$perc(1).getAttribute("value"), "100%",
     "The .A node's percentage cell displays the correct value.");
-  is($$sampl(1).getAttribute("value"), "3",
+  is($$sampl(1).getAttribute("value"), "4",
     "The .A node's samples cell displays the correct value.");
   is($$fun(".call-tree-name")[1].getAttribute("value"), "A",
     "The .A node's function cell displays the correct name.");
   is($$fun(".call-tree-url")[1].getAttribute("value"), "baz",
     "The .A node's function cell displays the correct url.");
   ok($$fun(".call-tree-url")[1].getAttribute("tooltiptext").contains("http://foo/bar/baz"),
     "The .A node's function cell displays the correct url tooltiptext.");
   is($$fun(".call-tree-line")[1].getAttribute("value"), ":12",
@@ -77,38 +77,38 @@ function test() {
 
   is(container.childNodes.length, 4,
     "The container node should have four children available.");
   is(container.childNodes[2].className, "call-tree-item",
     "The .B node in the tree has the correct class name.");
   is(container.childNodes[3].className, "call-tree-item",
     "The .E node in the tree has the correct class name.");
 
-  is($$dur(2).getAttribute("value"), "11",
+  is($$dur(2).getAttribute("value"), "8",
     "The .A.B node's duration cell displays the correct value.");
-  is($$perc(2).getAttribute("value"), "66.66%",
+  is($$perc(2).getAttribute("value"), "75%",
     "The .A.B node's percentage cell displays the correct value.");
-  is($$sampl(2).getAttribute("value"), "2",
+  is($$sampl(2).getAttribute("value"), "3",
     "The .A.B node's samples cell displays the correct value.");
   is($$fun(".call-tree-name")[2].getAttribute("value"), "B",
     "The .A.B node's function cell displays the correct name.");
   is($$fun(".call-tree-url")[2].getAttribute("value"), "baz",
     "The .A.B node's function cell displays the correct url.");
   ok($$fun(".call-tree-url")[2].getAttribute("tooltiptext").contains("http://foo/bar/baz"),
     "The .A.B node's function cell displays the correct url tooltiptext.");
   is($$fun(".call-tree-line")[2].getAttribute("value"), ":34",
     "The .A.B node's function cell displays the correct line.");
   is($$fun(".call-tree-host")[2].getAttribute("value"), "foo",
     "The .A.B node's function cell displays the correct host.");
   is($$fun(".call-tree-category")[2].getAttribute("value"), "Styles",
     "The .A.B node's function cell displays the correct category.");
 
   is($$dur(3).getAttribute("value"), "7",
     "The .A.E node's duration cell displays the correct value.");
-  is($$perc(3).getAttribute("value"), "33.33%",
+  is($$perc(3).getAttribute("value"), "25%",
     "The .A.E node's percentage cell displays the correct value.");
   is($$sampl(3).getAttribute("value"), "1",
     "The .A.E node's samples cell displays the correct value.");
   is($$fun(".call-tree-name")[3].getAttribute("value"), "E",
     "The .A.E node's function cell displays the correct name.");
   is($$fun(".call-tree-url")[3].getAttribute("value"), "baz",
     "The .A.E node's function cell displays the correct url.");
   ok($$fun(".call-tree-url")[3].getAttribute("tooltiptext").contains("http://foo/bar/baz"),
@@ -127,24 +127,32 @@ let gSamples = [{
   time: 5,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 32, location: "C (http://foo/bar/baz:56)" }
   ]
 }, {
-  time: 5 + 6,
+  time: 5 + 1,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 64, location: "D (http://foo/bar/baz:78)" }
   ]
 }, {
-  time: 5 + 6 + 7,
+  time: 5 + 1 + 2,
+  frames: [
+    { category: 8,  location: "(root)" },
+    { category: 8,  location: "A (http://foo/bar/baz:12)" },
+    { category: 16, location: "B (http://foo/bar/baz:34)" },
+    { category: 64, location: "D (http://foo/bar/baz:78)" }
+  ]
+}, {
+  time: 5 + 1 + 2 + 7,
   frames: [
     { category: 8,   location: "(root)" },
     { category: 8,   location: "A (http://foo/bar/baz:12)" },
     { category: 128, location: "E (http://foo/bar/baz:90)" },
     { category: 256, location: "F (http://foo/bar/baz:99)" }
   ]
 }];
--- a/browser/devtools/profiler/test/browser_profiler_tree-view-03.js
+++ b/browser/devtools/profiler/test/browser_profiler_tree-view-03.js
@@ -50,23 +50,23 @@ function test() {
     "The .A.B.D node's function cell displays the correct name.");
   is($$name(4).getAttribute("value"), "C",
     "The .A.B.C node's function cell displays the correct name.");
   is($$name(5).getAttribute("value"), "E",
     "The .A.E node's function cell displays the correct name.");
   is($$name(6).getAttribute("value"), "F",
     "The .A.E.F node's function cell displays the correct name.");
 
-  is($$duration(0).getAttribute("value"), "18",
+  is($$duration(0).getAttribute("value"), "15",
     "The root node's function cell displays the correct duration.");
-  is($$duration(1).getAttribute("value"), "18",
+  is($$duration(1).getAttribute("value"), "15",
     "The .A node's function cell displays the correct duration.");
-  is($$duration(2).getAttribute("value"), "11",
+  is($$duration(2).getAttribute("value"), "8",
     "The .A.B node's function cell displays the correct duration.");
-  is($$duration(3).getAttribute("value"), "6",
+  is($$duration(3).getAttribute("value"), "3",
     "The .A.B.D node's function cell displays the correct duration.");
   is($$duration(4).getAttribute("value"), "5",
     "The .A.B.C node's function cell displays the correct duration.");
   is($$duration(5).getAttribute("value"), "7",
     "The .A.E node's function cell displays the correct duration.");
   is($$duration(6).getAttribute("value"), "7",
     "The .A.E.F node's function cell displays the correct duration.");
 
@@ -77,25 +77,33 @@ let gSamples = [{
   time: 5,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 32, location: "C (http://foo/bar/baz:56)" }
   ]
 }, {
-  time: 5 + 6,
+  time: 5 + 1,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 64, location: "D (http://foo/bar/baz:78)" }
   ]
 }, {
-  time: 5 + 6 + 7,
+  time: 5 + 1 + 2,
+  frames: [
+    { category: 8,  location: "(root)" },
+    { category: 8,  location: "A (http://foo/bar/baz:12)" },
+    { category: 16, location: "B (http://foo/bar/baz:34)" },
+    { category: 64, location: "D (http://foo/bar/baz:78)" }
+  ]
+}, {
+  time: 5 + 1 + 2 + 7,
   frames: [
     { category: 8,   location: "(root)" },
     { category: 8,   location: "A (http://foo/bar/baz:12)" },
     { category: 128, location: "E (http://foo/bar/baz:90)" },
     { category: 256, location: "F (http://foo/bar/baz:99)" }
   ]
 }];
 
--- a/browser/devtools/profiler/test/browser_profiler_tree-view-04.js
+++ b/browser/devtools/profiler/test/browser_profiler_tree-view-04.js
@@ -24,45 +24,45 @@ function test() {
     "The root node's 'tooltiptext' attribute is correct.");
   ok(treeRoot.target.querySelector(".call-tree-zoom").hidden,
     "The root node's zoom button cell should be hidden.");
   ok(treeRoot.target.querySelector(".call-tree-category").hidden,
     "The root node's category label cell should be hidden.");
 
   let A = treeRoot.getChild();
   let B = A.getChild();
-  let C = B.getChild();
+  let D = B.getChild();
 
-  is(C.target.getAttribute("origin"), "chrome",
-    "The .A.B.C node's 'origin' attribute is correct.");
-  is(C.target.getAttribute("category"), "gc",
-    "The .A.B.C node's 'category' attribute is correct.");
-  is(C.target.getAttribute("tooltiptext"), "D (http://foo/bar/baz:78)",
-    "The .A.B.C node's 'tooltiptext' attribute is correct.");
+  is(D.target.getAttribute("origin"), "chrome",
+    "The .A.B.D node's 'origin' attribute is correct.");
+  is(D.target.getAttribute("category"), "gc",
+    "The .A.B.D node's 'category' attribute is correct.");
+  is(D.target.getAttribute("tooltiptext"), "D (http://foo/bar/baz:78)",
+    "The .A.B.D node's 'tooltiptext' attribute is correct.");
   ok(!A.target.querySelector(".call-tree-zoom").hidden,
-    "The .A.B.C node's zoom button cell should not be hidden.");
+    "The .A.B.D node's zoom button cell should not be hidden.");
   ok(!A.target.querySelector(".call-tree-category").hidden,
-    "The .A.B.C node's category label cell should not be hidden.");
+    "The .A.B.D node's category label cell should not be hidden.");
 
-  is(C.target.childNodes.length, 6,
+  is(D.target.childNodes.length, 6,
     "The number of columns displayed for tree items is correct.");
-  is(C.target.childNodes[0].getAttribute("type"), "duration",
+  is(D.target.childNodes[0].getAttribute("type"), "duration",
     "The first column displayed for tree items is correct.");
-  is(C.target.childNodes[1].getAttribute("type"), "percentage",
+  is(D.target.childNodes[1].getAttribute("type"), "percentage",
     "The third column displayed for tree items is correct.");
-  is(C.target.childNodes[2].getAttribute("type"), "self-duration",
+  is(D.target.childNodes[2].getAttribute("type"), "self-duration",
     "The second column displayed for tree items is correct.");
-  is(C.target.childNodes[3].getAttribute("type"), "self-percentage",
+  is(D.target.childNodes[3].getAttribute("type"), "self-percentage",
     "The fourth column displayed for tree items is correct.");
-  is(C.target.childNodes[4].getAttribute("type"), "samples",
+  is(D.target.childNodes[4].getAttribute("type"), "samples",
     "The fifth column displayed for tree items is correct.");
-  is(C.target.childNodes[5].getAttribute("type"), "function",
+  is(D.target.childNodes[5].getAttribute("type"), "function",
     "The sixth column displayed for tree items is correct.");
 
-  let functionCell = C.target.childNodes[5];
+  let functionCell = D.target.childNodes[5];
 
   is(functionCell.childNodes.length, 8,
     "The number of columns displayed for function cells is correct.");
   is(functionCell.childNodes[0].className, "arrow theme-twisty",
     "The first node displayed for function cells is correct.");
   is(functionCell.childNodes[1].className, "plain call-tree-name",
     "The second node displayed for function cells is correct.");
   is(functionCell.childNodes[2].className, "plain call-tree-url",
@@ -85,25 +85,33 @@ let gSamples = [{
   time: 5,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 32, location: "C (http://foo/bar/baz:56)" }
   ]
 }, {
-  time: 5 + 6,
+  time: 5 + 1,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 64, location: "D (http://foo/bar/baz:78)" }
   ]
 }, {
-  time: 5 + 6 + 7,
+  time: 5 + 1 + 2,
+  frames: [
+    { category: 8,  location: "(root)" },
+    { category: 8,  location: "A (http://foo/bar/baz:12)" },
+    { category: 16, location: "B (http://foo/bar/baz:34)" },
+    { category: 64, location: "D (http://foo/bar/baz:78)" }
+  ]
+}, {
+  time: 5 + 1 + 2 + 7,
   frames: [
     { category: 8,   location: "(root)" },
     { category: 8,   location: "A (http://foo/bar/baz:12)" },
     { category: 128, location: "E (http://foo/bar/baz:90)" },
     { category: 256, location: "F (http://foo/bar/baz:99)" }
   ]
 }];
 
--- a/browser/devtools/profiler/test/browser_profiler_tree-view-05.js
+++ b/browser/devtools/profiler/test/browser_profiler_tree-view-05.js
@@ -35,24 +35,32 @@ let gSamples = [{
   time: 5,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 32, location: "C (http://foo/bar/baz:56)" }
   ]
 }, {
-  time: 5 + 6,
+  time: 5 + 1,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 64, location: "D (http://foo/bar/baz:78)" }
   ]
 }, {
-  time: 5 + 6 + 7,
+  time: 5 + 1 + 2,
+  frames: [
+    { category: 8,  location: "(root)" },
+    { category: 8,  location: "A (http://foo/bar/baz:12)" },
+    { category: 16, location: "B (http://foo/bar/baz:34)" },
+    { category: 64, location: "D (http://foo/bar/baz:78)" }
+  ]
+}, {
+  time: 5 + 1 + 2 + 7,
   frames: [
     { category: 8,   location: "(root)" },
     { category: 8,   location: "A (http://foo/bar/baz:12)" },
     { category: 128, location: "E (http://foo/bar/baz:90)" },
     { category: 256, location: "F (http://foo/bar/baz:99)" }
   ]
 }];
--- a/browser/devtools/profiler/test/browser_profiler_tree-view-06.js
+++ b/browser/devtools/profiler/test/browser_profiler_tree-view-06.js
@@ -13,50 +13,58 @@ let test = Task.async(function*() {
   let threadNode = new ThreadNode(gSamples);
   let treeRoot = new CallView({ frame: threadNode });
 
   let container = document.createElement("vbox");
   treeRoot.attachTo(container);
 
   let A = treeRoot.getChild();
   let B = A.getChild();
-  let C = B.getChild();
+  let D = B.getChild();
 
   let receivedLinkEvent = treeRoot.once("link");
-  EventUtils.sendMouseEvent({ type: "mousedown" }, C.target.querySelector(".call-tree-url"));
+  EventUtils.sendMouseEvent({ type: "mousedown" }, D.target.querySelector(".call-tree-url"));
 
   let eventItem = yield receivedLinkEvent;
-  is(eventItem, C, "The 'link' event target is correct.");
+  is(eventItem, D, "The 'link' event target is correct.");
 
   let receivedZoomEvent = treeRoot.once("zoom");
-  EventUtils.sendMouseEvent({ type: "mousedown" }, C.target.querySelector(".call-tree-zoom"));
+  EventUtils.sendMouseEvent({ type: "mousedown" }, D.target.querySelector(".call-tree-zoom"));
 
   eventItem = yield receivedZoomEvent;
-  is(eventItem, C, "The 'zoom' event target is correct.");
+  is(eventItem, D, "The 'zoom' event target is correct.");
 
   finish();
 });
 
 let gSamples = [{
   time: 5,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 32, location: "C (http://foo/bar/baz:56)" }
   ]
 }, {
-  time: 5 + 6,
+  time: 5 + 1,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 64, location: "D (http://foo/bar/baz:78)" }
   ]
 }, {
-  time: 5 + 6 + 7,
+  time: 5 + 1 + 2,
+  frames: [
+    { category: 8,  location: "(root)" },
+    { category: 8,  location: "A (http://foo/bar/baz:12)" },
+    { category: 16, location: "B (http://foo/bar/baz:34)" },
+    { category: 64, location: "D (http://foo/bar/baz:78)" }
+  ]
+}, {
+  time: 5 + 1 + 2 + 7,
   frames: [
     { category: 8,   location: "(root)" },
     { category: 8,   location: "A (http://foo/bar/baz:12)" },
     { category: 128, location: "E (http://foo/bar/baz:90)" },
     { category: 256, location: "F (http://foo/bar/baz:99)" }
   ]
 }];
--- a/browser/devtools/profiler/test/browser_profiler_tree-view-07.js
+++ b/browser/devtools/profiler/test/browser_profiler_tree-view-07.js
@@ -14,49 +14,57 @@ function test() {
   let treeRoot = new CallView({ frame: threadNode });
 
   let container = document.createElement("vbox");
   container.id = "call-tree-container";
   treeRoot.attachTo(container);
 
   let A = treeRoot.getChild();
   let B = A.getChild();
-  let C = B.getChild();
+  let D = B.getChild();
 
-  is(C.root, treeRoot,
-    "The .A.B.C node has the correct root.");
-  is(C.parent, B,
-    "The .A.B.C node has the correct parent.");
-  is(C.level, 3,
-    "The .A.B.C node has the correct level.");
-  is(C.target.className, "call-tree-item",
-    "The .A.B.C node has the correct target node.");
-  is(C.container.id, "call-tree-container",
-    "The .A.B.C node has the correct container node.");
+  is(D.root, treeRoot,
+    "The .A.B.D node has the correct root.");
+  is(D.parent, B,
+    "The .A.B.D node has the correct parent.");
+  is(D.level, 3,
+    "The .A.B.D node has the correct level.");
+  is(D.target.className, "call-tree-item",
+    "The .A.B.D node has the correct target node.");
+  is(D.container.id, "call-tree-container",
+    "The .A.B.D node has the correct container node.");
 
   finish();
 }
 
 let gSamples = [{
   time: 5,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 32, location: "C (http://foo/bar/baz:56)" }
   ]
 }, {
-  time: 5 + 6,
+  time: 5 + 1,
   frames: [
     { category: 8,  location: "(root)" },
     { category: 8,  location: "A (http://foo/bar/baz:12)" },
     { category: 16, location: "B (http://foo/bar/baz:34)" },
     { category: 64, location: "D (http://foo/bar/baz:78)" }
   ]
 }, {
-  time: 5 + 6 + 7,
+  time: 5 + 1 + 2,
+  frames: [
+    { category: 8,  location: "(root)" },
+    { category: 8,  location: "A (http://foo/bar/baz:12)" },
+    { category: 16, location: "B (http://foo/bar/baz:34)" },
+    { category: 64, location: "D (http://foo/bar/baz:78)" }
+  ]
+}, {
+  time: 5 + 1 + 2 + 7,
   frames: [
     { category: 8,   location: "(root)" },
     { category: 8,   location: "A (http://foo/bar/baz:12)" },
     { category: 128, location: "E (http://foo/bar/baz:90)" },
     { category: 256, location: "F (http://foo/bar/baz:99)" }
   ]
 }];
--- a/browser/devtools/profiler/utils/tree-view.js
+++ b/browser/devtools/profiler/utils/tree-view.js
@@ -169,18 +169,18 @@ CallView.prototype = Heritage.extend(Abs
       children.push(new CallView({
         caller: this,
         frame: newFrame,
         level: newLevel,
         inverted: this.inverted
       }));
     }
 
-    // Sort the "callees" asc. by duration, before inserting them in the tree.
-    children.sort((a, b) => a.frame.duration < b.frame.duration ? 1 : -1);
+    // Sort the "callees" asc. by samples, before inserting them in the tree.
+    children.sort((a, b) => a.frame.samples < b.frame.samples ? 1 : -1);
   },
 
   /**
    * Functions creating each cell in this call view.
    * Invoked by `_displaySelf`.
    */
   _createTimeCell: function(duration, isSelf = false) {
     let cell = this.document.createElement("label");
--- a/browser/modules/ContentSearch.jsm
+++ b/browser/modules/ContentSearch.jsm
@@ -268,22 +268,24 @@ this.ContentSearch = {
       engineName: data.engineName,
       searchString: suggestions.term,
       formHistory: suggestions.local,
       remote: suggestions.remote,
     });
   }),
 
   _onMessageAddFormHistoryEntry: function (msg, entry) {
-    // There are some tests that use about:home and newtab that trigger a search
-    // and then immediately close the tab.  In those cases, the browser may have
-    // been destroyed by the time we receive this message, and as a result
-    // contentWindow is undefined.
-    if (!msg.target.contentWindow ||
-        PrivateBrowsingUtils.isBrowserPrivate(msg.target)) {
+    let isPrivate = true;
+    try {
+      // isBrowserPrivate assumes that the passed-in browser has all the normal
+      // properties, which won't be true if the browser has been destroyed.
+      // That may be the case here due to the asynchronous nature of messaging.
+      isPrivate = PrivateBrowsingUtils.isBrowserPrivate(msg.target);
+    } catch (err) {}
+    if (isPrivate || entry === "") {
       return Promise.resolve();
     }
     let browserData = this._suggestionDataForBrowser(msg.target, true);
     FormHistory.update({
       op: "bump",
       fieldname: browserData.controller.formHistoryParam,
       value: entry,
     }, {
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -170,16 +170,17 @@ using namespace mozilla::docshell;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::cellbroadcast;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::ipc;
 using namespace mozilla::dom::mobileconnection;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using namespace mozilla::dom::voicemail;
+using namespace mozilla::embedding;
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
 #if defined(MOZ_WIDGET_GONK)
 using namespace mozilla::system;
 #endif
@@ -1386,16 +1387,34 @@ ContentChild::AllocPNeckoChild()
 
 bool
 ContentChild::DeallocPNeckoChild(PNeckoChild* necko)
 {
     delete necko;
     return true;
 }
 
+PPrintingChild*
+ContentChild::AllocPPrintingChild()
+{
+    // The ContentParent should never attempt to allocate the
+    // nsPrintingPromptServiceProxy, which implements PPrintingChild. Instead,
+    // the nsPrintingPromptServiceProxy service is requested and instantiated
+    // via XPCOM, and the constructor of nsPrintingPromptServiceProxy sets up
+    // the IPC connection.
+    NS_NOTREACHED("Should never get here!");
+    return nullptr;
+}
+
+bool
+ContentChild::DeallocPPrintingChild(PPrintingChild* printing)
+{
+    return true;
+}
+
 PScreenManagerChild*
 ContentChild::AllocPScreenManagerChild(uint32_t* aNumberOfScreens,
                                        float* aSystemDefaultScale,
                                        bool* aSuccess)
 {
     // The ContentParent should never attempt to allocate the
     // nsScreenManagerProxy. Instead, the nsScreenManagerProxy
     // service is requested and instantiated via XPCOM, and the
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -202,16 +202,19 @@ public:
     virtual PMobileConnectionChild*
     AllocPMobileConnectionChild(const uint32_t& aClientId) MOZ_OVERRIDE;
     virtual bool
     DeallocPMobileConnectionChild(PMobileConnectionChild* aActor) MOZ_OVERRIDE;
 
     virtual PNeckoChild* AllocPNeckoChild() MOZ_OVERRIDE;
     virtual bool DeallocPNeckoChild(PNeckoChild*) MOZ_OVERRIDE;
 
+    virtual PPrintingChild* AllocPPrintingChild() MOZ_OVERRIDE;
+    virtual bool DeallocPPrintingChild(PPrintingChild*) MOZ_OVERRIDE;
+
     virtual PScreenManagerChild*
     AllocPScreenManagerChild(uint32_t* aNumberOfScreens,
                              float* aSystemDefaultScale,
                              bool* aSuccess) MOZ_OVERRIDE;
     virtual bool DeallocPScreenManagerChild(PScreenManagerChild*) MOZ_OVERRIDE;
 
     virtual PExternalHelperAppChild *AllocPExternalHelperAppChild(
             const OptionalURIParams& uri,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -52,16 +52,17 @@
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
 #include "mozilla/dom/mobileconnection/MobileConnectionParent.h"
 #include "mozilla/dom/mobilemessage/SmsParent.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/telephony/TelephonyParent.h"
 #include "mozilla/dom/time/DateCacheCleaner.h"
 #include "mozilla/dom/voicemail/VoicemailParent.h"
+#include "mozilla/embedding/printingui/PrintingParent.h"
 #include "mozilla/hal_sandbox/PHalParent.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
@@ -203,16 +204,17 @@ using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::cellbroadcast;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::dom::power;
 using namespace mozilla::dom::mobileconnection;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using namespace mozilla::dom::voicemail;
+using namespace mozilla::embedding;
 using namespace mozilla::hal;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
 using namespace mozilla::widget;
 
 #ifdef ENABLE_TESTS
@@ -3111,16 +3113,35 @@ ContentParent::AllocPNeckoParent()
 
 bool
 ContentParent::DeallocPNeckoParent(PNeckoParent* necko)
 {
     delete necko;
     return true;
 }
 
+PPrintingParent*
+ContentParent::AllocPPrintingParent()
+{
+    return new PrintingParent();
+}
+
+bool
+ContentParent::RecvPPrintingConstructor(PPrintingParent* aActor)
+{
+    return true;
+}
+
+bool
+ContentParent::DeallocPPrintingParent(PPrintingParent* printing)
+{
+    delete printing;
+    return true;
+}
+
 PScreenManagerParent*
 ContentParent::AllocPScreenManagerParent(uint32_t* aNumberOfScreens,
                                          float* aSystemDefaultScale,
                                          bool* aSuccess)
 {
     return new ScreenManagerParent(aNumberOfScreens, aSystemDefaultScale, aSuccess);
 }
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -240,16 +240,20 @@ public:
                                   const NativeThreadId& tid,
                                   const uint32_t& processType) MOZ_OVERRIDE;
 
     virtual PNeckoParent* AllocPNeckoParent() MOZ_OVERRIDE;
     virtual bool RecvPNeckoConstructor(PNeckoParent* aActor) MOZ_OVERRIDE {
         return PContentParent::RecvPNeckoConstructor(aActor);
     }
 
+    virtual PPrintingParent* AllocPPrintingParent() MOZ_OVERRIDE;
+    virtual bool RecvPPrintingConstructor(PPrintingParent* aActor) MOZ_OVERRIDE;
+    virtual bool DeallocPPrintingParent(PPrintingParent* aActor) MOZ_OVERRIDE;
+
     virtual PScreenManagerParent*
     AllocPScreenManagerParent(uint32_t* aNumberOfScreens,
                               float* aSystemDefaultScale,
                               bool* aSuccess) MOZ_OVERRIDE;
     virtual bool DeallocPScreenManagerParent(PScreenManagerParent* aActor) MOZ_OVERRIDE;
 
     virtual PHalParent* AllocPHalParent() MOZ_OVERRIDE;
     virtual bool RecvPHalConstructor(PHalParent* aActor) MOZ_OVERRIDE {
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -20,16 +20,17 @@ include protocol PDeviceStorageRequest;
 include protocol PFileDescriptorSet;
 include protocol PFMRadio;
 include protocol PFileSystemRequest;
 include protocol PHal;
 include protocol PImageBridge;
 include protocol PMemoryReportRequest;
 include protocol PMobileConnection;
 include protocol PNecko;
+include protocol PPrinting;
 include protocol PScreenManager;
 include protocol PSharedBufferManager;
 include protocol PSms;
 include protocol PSpeechSynthesis;
 include protocol PStorage;
 include protocol PTelephony;
 include protocol PTestShell;
 include protocol PVoicemail;
@@ -344,16 +345,17 @@ prio(normal upto high) intr protocol PCo
     manages PFileSystemRequest;
     manages PExternalHelperApp;
     manages PFileDescriptorSet;
     manages PFMRadio;
     manages PHal;
     manages PMemoryReportRequest;
     manages PMobileConnection;
     manages PNecko;
+    manages PPrinting;
     manages PScreenManager;
     manages PSms;
     manages PSpeechSynthesis;
     manages PStorage;
     manages PTelephony;
     manages PTestShell;
     manages PVoicemail;
     manages PJavaScript;
@@ -548,16 +550,18 @@ parent:
         returns (bool isSecureURI);
 
     PHal();
 
     PMobileConnection(uint32_t clientId);
 
     PNecko();
 
+    PPrinting();
+
     prio(high) sync PScreenManager()
         returns (uint32_t numberOfScreens,
                  float systemDefaultScale,
                  bool success);
 
     PCellBroadcast();
 
     PSms();
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -105,16 +105,17 @@ LOCAL_INCLUDES += [
     '/dom/devicestorage',
     '/dom/filesystem',
     '/dom/fmradio/ipc',
     '/dom/geolocation',
     '/dom/media/webspeech/synth/ipc',
     '/dom/mobilemessage/ipc',
     '/dom/storage',
     '/editor/libeditor',
+    '/embedding/components/printingui/ipc',
     '/extensions/cookie',
     '/extensions/spellcheck/src',
     '/hal/sandbox',
     '/js/ipc',
     '/layout/base',
     '/netwerk/base/src',
     '/toolkit/xre',
     '/uriloader/exthandler',
--- a/embedding/components/build/moz.build
+++ b/embedding/components/build/moz.build
@@ -10,26 +10,31 @@ SOURCES += [
 
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '../appstartup',
     '../commandhandler',
     '../find',
+    '../printingui/ipc',
     '../webbrowserpersist',
     '../windowwatcher',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+    DEFINES['PROXY_PRINTING'] = 1
     LOCAL_INCLUDES += [
         '../printingui/win',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     LOCAL_INCLUDES += [
         '../printingui/mac',
     ]
 
 if CONFIG['MOZ_PDF_PRINTING']:
+    DEFINES['PROXY_PRINTING'] = 1
     LOCAL_INCLUDES += [
         '../printingui/unixshared',
     ]
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
--- a/embedding/components/build/nsEmbeddingModule.cpp
+++ b/embedding/components/build/nsEmbeddingModule.cpp
@@ -15,16 +15,17 @@
 #include "nsCommandParams.h"
 #include "nsCommandGroup.h"
 #include "nsBaseCommandController.h"
 #include "nsNetCID.h"
 #include "nsEmbedCID.h"
 
 #ifdef NS_PRINTING
 #include "nsPrintingPromptService.h"
+#include "nsPrintingPromptServiceProxy.h"
 #endif
 
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowWatcher, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsAppStartupNotifier)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFind)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebBrowserFind)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebBrowserPersist)
@@ -33,16 +34,19 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsCommand
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsCommandParams, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsControllerCommandGroup)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBaseCommandController)
 
 #ifdef MOZ_XUL
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDialogParamBlock)
 #ifdef NS_PRINTING
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintingPromptService, Init)
+#ifdef PROXY_PRINTING
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintingPromptServiceProxy, Init)
+#endif
 #endif
 #endif
 
 #ifdef MOZ_XUL
 NS_DEFINE_NAMED_CID(NS_DIALOGPARAMBLOCK_CID);
 #ifdef NS_PRINTING
 NS_DEFINE_NAMED_CID(NS_PRINTINGPROMPTSERVICE_CID);
 #endif
@@ -57,19 +61,27 @@ NS_DEFINE_NAMED_CID(NS_COMMAND_MANAGER_C
 NS_DEFINE_NAMED_CID(NS_COMMAND_PARAMS_CID);
 NS_DEFINE_NAMED_CID(NS_CONTROLLER_COMMAND_GROUP_CID);
 NS_DEFINE_NAMED_CID(NS_BASECOMMANDCONTROLLER_CID);
 
 static const mozilla::Module::CIDEntry kEmbeddingCIDs[] = {
 #ifdef MOZ_XUL
     { &kNS_DIALOGPARAMBLOCK_CID, false, nullptr, nsDialogParamBlockConstructor },
 #ifdef NS_PRINTING
+
+#ifdef PROXY_PRINTING
+    { &kNS_PRINTINGPROMPTSERVICE_CID, false, nullptr, nsPrintingPromptServiceConstructor,
+      mozilla::Module::MAIN_PROCESS_ONLY },
+    { &kNS_PRINTINGPROMPTSERVICE_CID, false, nullptr, nsPrintingPromptServiceProxyConstructor,
+      mozilla::Module::CONTENT_PROCESS_ONLY },
+#else
     { &kNS_PRINTINGPROMPTSERVICE_CID, false, nullptr, nsPrintingPromptServiceConstructor },
 #endif
 #endif
+#endif
     { &kNS_WINDOWWATCHER_CID, false, nullptr, nsWindowWatcherConstructor },
     { &kNS_FIND_CID, false, nullptr, nsFindConstructor },
     { &kNS_WEB_BROWSER_FIND_CID, false, nullptr, nsWebBrowserFindConstructor },
     { &kNS_APPSTARTUPNOTIFIER_CID, false, nullptr, nsAppStartupNotifierConstructor },
     { &kNS_WEBBROWSERPERSIST_CID, false, nullptr, nsWebBrowserPersistConstructor },
     { &kNS_CONTROLLERCOMMANDTABLE_CID, false, nullptr, nsControllerCommandTableConstructor },
     { &kNS_COMMAND_MANAGER_CID, false, nullptr, nsCommandManagerConstructor },
     { &kNS_COMMAND_PARAMS_CID, false, nullptr, nsCommandParamsConstructor },
new file mode 100644
--- /dev/null
+++ b/embedding/components/printingui/ipc/PPrinting.ipdl
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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/. */
+
+include protocol PContent;
+include protocol PBrowser;
+
+namespace mozilla {
+namespace embedding {
+
+struct PrintData {
+  int32_t startPageRange;
+  int32_t endPageRange;
+  double edgeTop;
+  double edgeLeft;
+  double edgeBottom;
+  double edgeRight;
+  double marginTop;
+  double marginLeft;
+  double marginBottom;
+  double marginRight;
+  double unwriteableMarginTop;
+  double unwriteableMarginLeft;
+  double unwriteableMarginBottom;
+  double unwriteableMarginRight;
+  double scaling;
+  bool printBGColors;
+  bool printBGImages;
+  short printRange;
+  nsString title;
+  nsString docURL;
+  nsString headerStrLeft;
+  nsString headerStrCenter;
+  nsString headerStrRight;
+  nsString footerStrLeft;
+  nsString footerStrCenter;
+  nsString footerStrRight;
+
+  short  howToEnableFrameUI;
+  bool isCancelled;
+  short printFrameTypeUsage;
+  short  printFrameType;
+  bool printSilent;
+  bool shrinkToFit;
+  bool showPrintProgress;
+
+  nsString paperName;
+  short paperSizeType;
+  short paperData;
+  double paperWidth;
+  double paperHeight;
+  short paperSizeUnit;
+  nsString plexName;
+  nsString colorspace;
+  nsString resolutionName;
+  bool downloadFonts;
+  bool printReversed;
+  bool printInColor;
+  int32_t orientation;
+  nsString printCommand;
+  int32_t numCopies;
+  nsString printerName;
+  bool printToFile;
+  nsString toFileName;
+  short outputFormat;
+  int32_t printPageDelay;
+  int32_t resolution;
+  int32_t duplex;
+  bool isInitializedFromPrinter;
+  bool isInitializedFromPrefs;
+  bool persistMarginBoxSettings;
+
+  /* Windows-specific things */
+  nsString driverName;
+  nsString deviceName;
+  bool isFramesetDocument;
+  bool isFramesetFrameSelected;
+  bool isIFrameSelected;
+  bool isRangeSelection;
+
+  /* TODO: OS X specific things - specifically, an array of names for the
+   * document to be supplied by nsIWebBrowserPrint::enumerateDocumentNames
+   */
+};
+
+sync protocol PPrinting
+{
+  manager PContent;
+
+parent:
+  sync ShowProgress(PBrowser browser, bool isForPrinting);
+  sync ShowPrintDialog(PBrowser browser, PrintData settings)
+    returns(PrintData modifiedSettings, bool success);
+
+child:
+  __delete__();
+};
+
+} // namespace embedding
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/embedding/components/printingui/ipc/PrintDataUtils.cpp
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* 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/. */
+
+#include "PrintDataUtils.h"
+#include "nsIPrintOptions.h"
+#include "nsIPrintSettings.h"
+#include "nsIServiceManager.h"
+#include "nsIWebBrowserPrint.h"
+#include "nsXPIDLString.h"
+
+namespace mozilla {
+namespace embedding {
+
+/**
+ * MockWebBrowserPrint is a mostly useless implementation of nsIWebBrowserPrint,
+ * but wraps a PrintData so that it's able to return information to print
+ * settings dialogs that need an nsIWebBrowserPrint to interrogate.
+ */
+
+NS_IMPL_ISUPPORTS(MockWebBrowserPrint, nsIWebBrowserPrint);
+
+MockWebBrowserPrint::MockWebBrowserPrint(PrintData aData)
+  : mData(aData)
+{
+  MOZ_COUNT_CTOR(MockWebBrowserPrint);
+}
+
+MockWebBrowserPrint::~MockWebBrowserPrint()
+{
+  MOZ_COUNT_DTOR(MockWebBrowserPrint);
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::GetGlobalPrintSettings(nsIPrintSettings **aGlobalPrintSettings)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::GetCurrentPrintSettings(nsIPrintSettings **aCurrentPrintSettings)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::GetCurrentChildDOMWindow(nsIDOMWindow **aCurrentPrintSettings)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::GetDoingPrint(bool *aDoingPrint)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::GetDoingPrintPreview(bool *aDoingPrintPreview)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::GetIsFramesetDocument(bool *aIsFramesetDocument)
+{
+  *aIsFramesetDocument = mData.isFramesetDocument();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::GetIsFramesetFrameSelected(bool *aIsFramesetFrameSelected)
+{
+  *aIsFramesetFrameSelected = mData.isFramesetFrameSelected();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::GetIsIFrameSelected(bool *aIsIFrameSelected)
+{
+  *aIsIFrameSelected = mData.isIFrameSelected();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::GetIsRangeSelection(bool *aIsRangeSelection)
+{
+  *aIsRangeSelection = mData.isRangeSelection();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::GetPrintPreviewNumPages(int32_t *aPrintPreviewNumPages)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::Print(nsIPrintSettings* aThePrintSettings,
+                           nsIWebProgressListener* aWPListener)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::PrintPreview(nsIPrintSettings* aThePrintSettings,
+                                  nsIDOMWindow* aChildDOMWin,
+                                  nsIWebProgressListener* aWPListener)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::PrintPreviewNavigate(int16_t aNavType,
+                                          int32_t aPageNum)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::Cancel()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::EnumerateDocumentNames(uint32_t* aCount,
+                                            char16_t*** aResult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MockWebBrowserPrint::ExitPrintPreview()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+} // namespace embedding
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/embedding/components/printingui/ipc/PrintDataUtils.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* 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/. */
+
+#ifndef mozilla_embedding_PrintDataUtils_h
+#define mozilla_embedding_PrintDataUtils_h
+
+#include "mozilla/embedding/PPrinting.h"
+#include "nsIWebBrowserPrint.h"
+
+/**
+ * nsIPrintSettings and nsIWebBrowserPrint information is sent back and forth
+ * across PPrinting via the PrintData struct. These are utilities for
+ * manipulating PrintData that can be used on either side of the communications
+ * channel.
+ */
+
+namespace mozilla {
+namespace embedding {
+
+class MockWebBrowserPrint MOZ_FINAL : public nsIWebBrowserPrint
+{
+public:
+  MockWebBrowserPrint(PrintData aData);
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIWEBBROWSERPRINT
+
+private:
+  ~MockWebBrowserPrint();
+  PrintData mData;
+};
+
+} // namespace embedding
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/embedding/components/printingui/ipc/PrintingParent.cpp
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* 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/. */
+
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/TabParent.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+#include "nsIDOMWindow.h"
+#include "nsIPrintingPromptService.h"
+#include "nsIPrintProgressParams.h"
+#include "nsIServiceManager.h"
+#include "nsIWebProgressListener.h"
+#include "PrintingParent.h"
+#include "nsIPrintOptions.h"
+#include "PrintDataUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+namespace mozilla {
+namespace embedding {
+bool
+PrintingParent::RecvShowProgress(PBrowserParent* parent,
+                                 const bool& isForPrinting)
+{
+  TabParent* tabParent = static_cast<TabParent*>(parent);
+  if (!tabParent) {
+    return true;
+  }
+
+  nsCOMPtr<Element> frameElement = tabParent->GetOwnerElement();
+  if (!frameElement) {
+    return true;
+  }
+
+  nsCOMPtr<nsIContent> frame(do_QueryInterface(frameElement));
+  if (!frame) {
+    return true;
+  }
+
+  nsCOMPtr<nsIDOMWindow> parentWin = do_QueryInterface(frame->OwnerDoc()->GetWindow());
+  if (!parentWin) {
+    return true;
+  }
+
+  nsCOMPtr<nsIPrintingPromptService> pps(do_GetService("@mozilla.org/embedcomp/printingprompt-service;1"));
+
+  if (!pps) {
+    return true;
+  }
+
+  nsCOMPtr<nsIWebProgressListener> printProgressListener;
+  nsCOMPtr<nsIPrintProgressParams> printProgressParams;
+
+  // TODO: What do I do with this thing?
+  bool doNotify = false;
+
+  pps->ShowProgress(parentWin, nullptr, nullptr, nullptr,
+                    isForPrinting,
+                    getter_AddRefs(printProgressListener),
+                    getter_AddRefs(printProgressParams),
+                    &doNotify);
+
+  return true;
+}
+
+bool
+PrintingParent::RecvShowPrintDialog(PBrowserParent* parent,
+                                    const PrintData& data,
+                                    PrintData* retVal,
+                                    bool* success)
+{
+  *success = false;
+
+  TabParent* tabParent = static_cast<TabParent*>(parent);
+  if (!tabParent) {
+    return true;
+  }
+
+  nsCOMPtr<Element> frameElement = tabParent->GetOwnerElement();
+  if (!frameElement) {
+    return true;
+  }
+
+  nsCOMPtr<nsIContent> frame(do_QueryInterface(frameElement));
+  if (!frame) {
+    return true;
+  }
+
+  nsCOMPtr<nsIDOMWindow> parentWin = do_QueryInterface(frame->OwnerDoc()->GetWindow());
+  if (!parentWin) {
+    return true;
+  }
+
+  nsCOMPtr<nsIPrintingPromptService> pps(do_GetService("@mozilla.org/embedcomp/printingprompt-service;1"));
+
+  if (!pps) {
+    return true;
+  }
+
+  // The initSettings we got can be wrapped using
+  // PrintDataUtils' MockWebBrowserPrint, which implements enough of
+  // nsIWebBrowserPrint to keep the dialogs happy.
+  nsCOMPtr<nsIWebBrowserPrint> wbp = new MockWebBrowserPrint(data);
+
+  nsresult rv;
+  nsCOMPtr<nsIPrintOptions> po = do_GetService("@mozilla.org/gfx/printsettings-service;1", &rv);
+  NS_ENSURE_SUCCESS(rv, true);
+
+  nsCOMPtr<nsIPrintSettings> settings;
+  rv = po->CreatePrintSettings(getter_AddRefs(settings));
+  NS_ENSURE_SUCCESS(rv, true);
+
+  rv = po->DeserializeToPrintSettings(data, settings);
+  NS_ENSURE_SUCCESS(rv, true);
+
+  rv = pps->ShowPrintDialog(parentWin, wbp, settings);
+  NS_ENSURE_SUCCESS(rv, true);
+
+  // And send it back.
+  PrintData result;
+  rv = po->SerializeToPrintData(settings, nullptr, &result);
+  NS_ENSURE_SUCCESS(rv, true);
+
+  *retVal = result;
+  *success = true;
+  return true;
+}
+
+void
+PrintingParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+}
+
+MOZ_IMPLICIT PrintingParent::PrintingParent()
+{
+    MOZ_COUNT_CTOR(PrintingParent);
+}
+
+MOZ_IMPLICIT PrintingParent::~PrintingParent()
+{
+    MOZ_COUNT_DTOR(PrintingParent);
+}
+
+} // namespace embedding
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/embedding/components/printingui/ipc/PrintingParent.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* 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/. */
+
+#ifndef mozilla_embedding_PrintingParent_h
+#define mozilla_embedding_PrintingParent_h
+
+#include "mozilla/embedding/PPrintingParent.h"
+#include "mozilla/dom/PBrowserParent.h"
+
+namespace mozilla {
+namespace embedding {
+class PrintingParent : public PPrintingParent
+{
+public:
+    virtual bool
+    RecvShowProgress(PBrowserParent* parent,
+                     const bool& isForPrinting);
+    virtual bool
+    RecvShowPrintDialog(PBrowserParent* parent,
+                        const PrintData& initSettings,
+                        PrintData* retVal,
+                        bool* success);
+
+    virtual void
+    ActorDestroy(ActorDestroyReason aWhy);
+
+    MOZ_IMPLICIT PrintingParent();
+    virtual ~PrintingParent();
+};
+} // namespace embedding
+} // namespace mozilla
+
+#endif
+
new file mode 100644
--- /dev/null
+++ b/embedding/components/printingui/ipc/moz.build
@@ -0,0 +1,25 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.embedding.printingui += [
+    'PrintingParent.h',
+]
+
+UNIFIED_SOURCES += [
+    'nsPrintingPromptServiceProxy.cpp',
+    'PrintDataUtils.cpp',
+    'PrintingParent.cpp',
+]
+
+IPDL_SOURCES += [
+    'PPrinting.ipdl',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FAIL_ON_WARNINGS = True
+
+FINAL_LIBRARY = 'xul'
new file mode 100644
--- /dev/null
+++ b/embedding/components/printingui/ipc/nsPrintingPromptServiceProxy.cpp
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/unused.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsPIDOMWindow.h"
+#include "nsPrintingPromptServiceProxy.h"
+#include "nsIPrintingPromptService.h"
+#include "PrintDataUtils.h"
+#include "nsPrintOptionsImpl.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::embedding;
+
+NS_IMPL_ISUPPORTS(nsPrintingPromptServiceProxy, nsIPrintingPromptService)
+
+nsPrintingPromptServiceProxy::nsPrintingPromptServiceProxy()
+{
+}
+
+nsPrintingPromptServiceProxy::~nsPrintingPromptServiceProxy()
+{
+}
+
+nsresult
+nsPrintingPromptServiceProxy::Init()
+{
+  mozilla::unused << ContentChild::GetSingleton()->SendPPrintingConstructor(this);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrintingPromptServiceProxy::ShowPrintDialog(nsIDOMWindow *parent,
+                                              nsIWebBrowserPrint *webBrowserPrint,
+                                              nsIPrintSettings *printSettings)
+{
+  NS_ENSURE_ARG(parent);
+  NS_ENSURE_ARG(webBrowserPrint);
+  NS_ENSURE_ARG(printSettings);
+
+  // Get the root docshell owner of this nsIDOMWindow, which
+  // should map to a TabChild, which we can then pass up to
+  // the parent.
+  nsCOMPtr<nsPIDOMWindow> pwin = do_QueryInterface(parent);
+  NS_ENSURE_STATE(pwin);
+  nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
+  NS_ENSURE_STATE(docShell);
+  nsCOMPtr<nsIDocShellTreeOwner> owner;
+  nsresult rv = docShell->GetTreeOwner(getter_AddRefs(owner));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsITabChild> tabchild = do_GetInterface(owner);
+  NS_ENSURE_STATE(tabchild);
+
+  TabChild* pBrowser = static_cast<TabChild*>(tabchild.get());
+
+  // Next, serialize the nsIWebBrowserPrint and nsIPrintSettings we were given.
+  nsCOMPtr<nsIPrintOptions> po =
+    do_GetService("@mozilla.org/gfx/printsettings-service;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PrintData inSettings;
+  rv = po->SerializeToPrintData(printSettings, webBrowserPrint, &inSettings);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PrintData modifiedSettings;
+  bool success;
+
+  mozilla::unused << SendShowPrintDialog(pBrowser, inSettings, &modifiedSettings, &success);
+
+  if (!success) {
+    // Something failed in the parent.
+    return NS_ERROR_FAILURE;
+  }
+
+  rv = po->DeserializeToPrintSettings(modifiedSettings, printSettings);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrintingPromptServiceProxy::ShowProgress(nsIDOMWindow*            parent,
+                                           nsIWebBrowserPrint*      webBrowserPrint,    // ok to be null
+                                           nsIPrintSettings*        printSettings,      // ok to be null
+                                           nsIObserver*             openDialogObserver, // ok to be null
+                                           bool                     isForPrinting,
+                                           nsIWebProgressListener** webProgressListener,
+                                           nsIPrintProgressParams** printProgressParams,
+                                           bool*                  notifyOnOpen)
+{
+  NS_ENSURE_ARG(parent);
+  NS_ENSURE_ARG(webProgressListener);
+  NS_ENSURE_ARG(printProgressParams);
+  NS_ENSURE_ARG(notifyOnOpen);
+
+  // Get the root docshell owner of this nsIDOMWindow, which
+  // should map to a TabChild, which we can then pass up to
+  // the parent.
+  nsCOMPtr<nsPIDOMWindow> pwin = do_QueryInterface(parent);
+  NS_ENSURE_STATE(pwin);
+  nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
+  NS_ENSURE_STATE(docShell);
+  nsCOMPtr<nsIDocShellTreeOwner> owner;
+  nsresult rv = docShell->GetTreeOwner(getter_AddRefs(owner));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsITabChild> tabchild = do_GetInterface(owner);
+  TabChild* pBrowser = static_cast<TabChild*>(tabchild.get());
+
+  mozilla::unused << SendShowProgress(pBrowser, isForPrinting);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrintingPromptServiceProxy::ShowPageSetup(nsIDOMWindow *parent,
+                                            nsIPrintSettings *printSettings,
+                                            nsIObserver *aObs)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPrintingPromptServiceProxy::ShowPrinterProperties(nsIDOMWindow *parent,
+                                                    const char16_t *printerName,
+                                                    nsIPrintSettings *printSettings)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
new file mode 100644
--- /dev/null
+++ b/embedding/components/printingui/ipc/nsPrintingPromptServiceProxy.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
+
+#ifndef __nsPrintingPromptServiceProxy_h
+#define __nsPrintingPromptServiceProxy_h
+
+#include "nsIPrintingPromptService.h"
+#include "mozilla/embedding/PPrintingChild.h"
+
+class nsPrintingPromptServiceProxy: public nsIPrintingPromptService,
+                                    public mozilla::embedding::PPrintingChild
+{
+    virtual ~nsPrintingPromptServiceProxy();
+
+public:
+    nsPrintingPromptServiceProxy();
+
+    nsresult Init();
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIPRINTINGPROMPTSERVICE
+};
+
+#endif
+
--- a/embedding/components/printingui/moz.build
+++ b/embedding/components/printingui/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
 
+DIRS += ['ipc']
+
 if toolkit == 'windows':
     DIRS += ['win']
 elif toolkit == 'cocoa':
     DIRS += ['mac']
 elif CONFIG['MOZ_PDF_PRINTING']:
     DIRS += ['unixshared']
--- a/embedding/components/printingui/win/moz.build
+++ b/embedding/components/printingui/win/moz.build
@@ -6,11 +6,15 @@
 
 UNIFIED_SOURCES += [
     'nsPrintDialogUtil.cpp',
     'nsPrintingPromptService.cpp',
     'nsPrintProgress.cpp',
     'nsPrintProgressParams.cpp',
 ]
 
+EXPORTS += [
+    'nsPrintDialogUtil.h',
+]
+
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'xul'
--- a/embedding/components/printingui/win/nsPrintDialogUtil.cpp
+++ b/embedding/components/printingui/win/nsPrintDialogUtil.cpp
@@ -677,17 +677,17 @@ static UINT CALLBACK PrintHookProc(HWND 
 //----------------------------------------------------------------------------------
 // Returns a Global Moveable Memory Handle to a DevMode
 // from the Printer by the name of aPrintName
 //
 // NOTE:
 //   This function assumes that aPrintName has already been converted from 
 //   unicode
 //
-static HGLOBAL CreateGlobalDevModeAndInit(const nsXPIDLString& aPrintName, nsIPrintSettings* aPS)
+HGLOBAL CreateGlobalDevModeAndInit(const nsXPIDLString& aPrintName, nsIPrintSettings* aPS)
 {
   HGLOBAL hGlobalDevMode = nullptr;
 
   HANDLE hPrinter = nullptr;
   // const cast kludge for silly Win32 api's
   LPWSTR printName = const_cast<wchar_t*>(static_cast<const wchar_t*>(aPrintName.get()));
   BOOL status = ::OpenPrinterW(printName, &hPrinter, nullptr);
   if (status) {
--- a/embedding/components/printingui/win/nsPrintDialogUtil.h
+++ b/embedding/components/printingui/win/nsPrintDialogUtil.h
@@ -4,9 +4,11 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef nsFlyOwnDialog_h___
 #define nsFlyOwnDialog_h___
 
 nsresult NativeShowPrintDialog(HWND                aHWnd,
                                nsIWebBrowserPrint* aWebBrowserPrint,
                                nsIPrintSettings*   aPrintSettings);
 
+HGLOBAL CreateGlobalDevModeAndInit(const nsXPIDLString& aPrintName, nsIPrintSettings* aPS);
+
 #endif /* nsFlyOwnDialog_h___ */
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -16,17 +16,16 @@ import java.util.Locale;
 import java.util.Vector;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.DynamicToolbar.PinReason;
 import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
 import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
-import org.mozilla.gecko.ReadingListHelper;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserContract.SearchHistory;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.SuggestedSites;
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.favicons.Favicons;
@@ -47,16 +46,17 @@ import org.mozilla.gecko.home.BrowserSea
 import org.mozilla.gecko.home.HomeBanner;
 import org.mozilla.gecko.home.HomePager;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenInBackgroundListener;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.HomePanelsManager;
 import org.mozilla.gecko.home.SearchEngine;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuItem;
+import org.mozilla.gecko.mozglue.ContextUtils;
 import org.mozilla.gecko.preferences.ClearOnShutdownPref;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.prompts.Prompt;
 import org.mozilla.gecko.prompts.PromptListItem;
 import org.mozilla.gecko.sync.setup.SyncAccounts;
 import org.mozilla.gecko.tabs.TabsPanel;
 import org.mozilla.gecko.toolbar.AutocompleteHandler;
 import org.mozilla.gecko.toolbar.BrowserToolbar;
@@ -679,17 +679,17 @@ public class BrowserApp extends GeckoApp
         // We can't show Onboarding until Gecko has finished initialization (bug 1077583).
         checkStartPane(this, getIntent().getAction());
     }
 
     @Override
     public void onResume() {
         super.onResume();
 
-        final String args = StringUtils.getStringExtra(getIntent(), "args");
+        final String args = ContextUtils.getStringExtra(getIntent(), "args");
         // If an external intent tries to start Fennec in guest mode, and it's not already
         // in guest mode, this will change modes before opening the url.
         // NOTE: OnResume is called twice sometimes when showing on the lock screen.
         final boolean enableGuestSession = GuestSession.shouldUse(this, args);
         final boolean inGuestSession = GeckoProfile.get(this).inGuestMode();
         if (enableGuestSession != inGuestSession) {
             doRestart(getIntent());
             GeckoAppShell.systemExit();
@@ -2661,22 +2661,22 @@ public class BrowserApp extends GeckoApp
         if (AboutPages.isAboutReader(url)) {
             String urlFromReader = ReaderModeUtils.getUrlFromAboutReader(url);
             if (urlFromReader != null) {
                 url = urlFromReader;
             }
         }
 
         // Disable share menuitem for about:, chrome:, file:, and resource: URIs
-        final boolean shareEnabled = RestrictedProfiles.isAllowed(RestrictedProfiles.Restriction.DISALLOW_SHARE);
+        final boolean shareEnabled = RestrictedProfiles.isAllowed(this, RestrictedProfiles.Restriction.DISALLOW_SHARE);
         share.setVisible(shareEnabled);
         share.setEnabled(StringUtils.isShareableUrl(url) && shareEnabled);
-        MenuUtils.safeSetEnabled(aMenu, R.id.apps, RestrictedProfiles.isAllowed(RestrictedProfiles.Restriction.DISALLOW_INSTALL_APPS));
-        MenuUtils.safeSetEnabled(aMenu, R.id.addons, RestrictedProfiles.isAllowed(RestrictedProfiles.Restriction.DISALLOW_INSTALL_EXTENSION));
-        MenuUtils.safeSetEnabled(aMenu, R.id.downloads, RestrictedProfiles.isAllowed(RestrictedProfiles.Restriction.DISALLOW_DOWNLOADS));
+        MenuUtils.safeSetEnabled(aMenu, R.id.apps, RestrictedProfiles.isAllowed(this, RestrictedProfiles.Restriction.DISALLOW_INSTALL_APPS));
+        MenuUtils.safeSetEnabled(aMenu, R.id.addons, RestrictedProfiles.isAllowed(this, RestrictedProfiles.Restriction.DISALLOW_INSTALL_EXTENSION));
+        MenuUtils.safeSetEnabled(aMenu, R.id.downloads, RestrictedProfiles.isAllowed(this, RestrictedProfiles.Restriction.DISALLOW_DOWNLOADS));
 
         // NOTE: Use MenuUtils.safeSetEnabled because some actions might
         // be on the BrowserToolbar context menu.
         if (Versions.feature11Plus) {
             MenuUtils.safeSetEnabled(aMenu, R.id.page, !isAboutHome(tab));
         }
         MenuUtils.safeSetEnabled(aMenu, R.id.subscribe, tab.hasFeeds());
         MenuUtils.safeSetEnabled(aMenu, R.id.add_search_engine, tab.hasOpenSearch());
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -35,32 +35,33 @@ import org.mozilla.gecko.gfx.Layer;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PluginLayer;
 import org.mozilla.gecko.health.HealthRecorder;
 import org.mozilla.gecko.health.SessionInformation;
 import org.mozilla.gecko.health.StubbedHealthRecorder;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuInflater;
 import org.mozilla.gecko.menu.MenuPanel;
+import org.mozilla.gecko.mozglue.ContextUtils;
+import org.mozilla.gecko.mozglue.ContextUtils.SafeIntent;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.preferences.ClearOnShutdownPref;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.prompts.PromptService;
 import org.mozilla.gecko.updater.UpdateService;
 import org.mozilla.gecko.updater.UpdateServiceHelper;
 import org.mozilla.gecko.util.ActivityResultHandler;
 import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.FileUtils;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.NativeEventListener;
 import org.mozilla.gecko.util.NativeJSObject;
 import org.mozilla.gecko.util.PrefUtils;
-import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.webapp.EventListener;
 import org.mozilla.gecko.webapp.UninstallListener;
 import org.mozilla.gecko.widget.ButtonToast;
 
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -1097,18 +1098,18 @@ public abstract class GeckoApp
                 ActivityUtils.setFullScreen(GeckoApp.this, fullscreen);
             }
         });
     }
 
     /**
      * Check and start the Java profiler if MOZ_PROFILER_STARTUP env var is specified.
      **/
-    protected static void earlyStartJavaSampler(Intent intent) {
-        String env = StringUtils.getStringExtra(intent, "env0");
+    protected static void earlyStartJavaSampler(SafeIntent intent) {
+        String env = intent.getStringExtra("env0");
         for (int i = 1; env != null; i++) {
             if (env.startsWith("MOZ_PROFILER_STARTUP=")) {
                 if (!env.endsWith("=")) {
                     GeckoJavaSampler.start(10, 1000);
                     Log.d(LOGTAG, "Profiling Java on startup");
                 }
                 break;
             }
@@ -1118,32 +1119,31 @@ public abstract class GeckoApp
 
     /**
      * Called when the activity is first created.
      *
      * Here we initialize all of our profile settings, Firefox Health Report,
      * and other one-shot constructions.
      **/
     @Override
-    public void onCreate(Bundle savedInstanceState)
-    {
+    public void onCreate(Bundle savedInstanceState) {
         GeckoAppShell.ensureCrashHandling();
 
         // Enable Android Strict Mode for developers' local builds (the "default" channel).
         if ("default".equals(AppConstants.MOZ_UPDATE_CHANNEL)) {
             enableStrictMode();
         }
 
         // The clock starts...now. Better hurry!
         mJavaUiStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_JAVAUI");
         mGeckoReadyStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_GECKOREADY");
 
-        final Intent intent = getIntent();
+        final SafeIntent intent = new SafeIntent(getIntent());
         final String action = intent.getAction();
-        final String args = StringUtils.getStringExtra(intent, "args");
+        final String args = intent.getStringExtra("args");
 
         earlyStartJavaSampler(intent);
 
         // GeckoLoader wants to dig some environment variables out of the
         // incoming intent, so pass it in here. GeckoLoader will do its
         // business later and dispose of the reference.
         GeckoLoader.setLastIntent(intent);
 
@@ -1249,17 +1249,17 @@ public abstract class GeckoApp
                 @Override
                 public void run() {
                     GeckoThread.setLaunchState(GeckoThread.LaunchState.Launched);
                     GeckoThread.createAndStart();
                 }
             }, 1000 * 5 /* 5 seconds */);
         }
 
-        Bundle stateBundle = getIntent().getBundleExtra(EXTRA_STATE_BUNDLE);
+        Bundle stateBundle = ContextUtils.getBundleExtra(getIntent(), EXTRA_STATE_BUNDLE);
         if (stateBundle != null) {
             // Use the state bundle if it was given as an intent extra. This is
             // only intended to be used internally via Robocop, so a boolean
             // is read from a private shared pref to prevent other apps from
             // injecting states.
             final SharedPreferences prefs = getSharedPreferences();
             if (prefs.getBoolean(PREFS_ALLOW_STATE_BUNDLE, false)) {
                 prefs.edit().remove(PREFS_ALLOW_STATE_BUNDLE).apply();
@@ -1453,28 +1453,31 @@ public abstract class GeckoApp
             int flags = Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_USER_ENTERED | Tabs.LOADURL_EXTERNAL;
             Tabs.getInstance().loadUrl(url, flags);
         }
     }
 
     private void initialize() {
         mInitialized = true;
 
-        Intent intent = getIntent();
-        String action = intent.getAction();
-
-        String passedUri = null;
+        final SafeIntent intent = new SafeIntent(getIntent());
+        final String action = intent.getAction();
+
         final String uri = getURIFromIntent(intent);
+
+        final String passedUri;
         if (!TextUtils.isEmpty(uri)) {
             passedUri = uri;
+        } else {
+            passedUri = null;
         }
 
         final boolean isExternalURL = passedUri != null &&
                                       !AboutPages.isAboutHome(passedUri);
-        StartupAction startupAction;
+        final StartupAction startupAction;
         if (isExternalURL) {
             startupAction = StartupAction.URL;
         } else {
             startupAction = StartupAction.NORMAL;
         }
 
         // Start migrating as early as possible, can do this in
         // parallel with Gecko load.
@@ -1527,17 +1530,17 @@ public abstract class GeckoApp
         }
 
         Telemetry.HistogramAdd("FENNEC_STARTUP_GECKOAPP_ACTION", startupAction.ordinal());
 
         // Check if launched from data reporting notification.
         if (ACTION_LAUNCH_SETTINGS.equals(action)) {
             Intent settingsIntent = new Intent(GeckoApp.this, GeckoPreferences.class);
             // Copy extras.
-            settingsIntent.putExtras(intent);
+            settingsIntent.putExtras(intent.getUnsafe());
             startActivity(settingsIntent);
         }
 
         //app state callbacks
         mAppStateListeners = new LinkedList<GeckoAppShell.AppStateListener>();
 
         //register for events
         EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this,
@@ -1727,17 +1730,17 @@ public abstract class GeckoApp
         return shouldRestore;
     }
 
     private String getSessionRestorePreference() {
         return getSharedPreferences().getString(GeckoPreferences.PREFS_RESTORE_SESSION, "quit");
     }
 
     private boolean getRestartFromIntent() {
-        return getIntent().getBooleanExtra("didRestart", false);
+        return ContextUtils.getBooleanExtra(getIntent(), "didRestart", false);
     }
 
     /**
      * Enable Android StrictMode checks (for supported OS versions).
      * http://developer.android.com/reference/android/os/StrictMode.html
      */
     private void enableStrictMode() {
         Log.d(LOGTAG, "Enabling Android StrictMode");
@@ -1793,44 +1796,46 @@ public abstract class GeckoApp
     }
 
     @Override
     public String getDefaultUAString() {
         return HardwareUtils.isTablet() ? AppConstants.USER_AGENT_FENNEC_TABLET :
                                           AppConstants.USER_AGENT_FENNEC_MOBILE;
     }
 
-    private void processAlertCallback(Intent intent) {
+    private void processAlertCallback(SafeIntent intent) {
         String alertName = "";
         String alertCookie = "";
         Uri data = intent.getData();
         if (data != null) {
             alertName = data.getQueryParameter("name");
             if (alertName == null)
                 alertName = "";
             alertCookie = data.getQueryParameter("cookie");
             if (alertCookie == null)
                 alertCookie = "";
         }
         handleNotification(ACTION_ALERT_CALLBACK, alertName, alertCookie);
     }
 
     @Override
-    protected void onNewIntent(Intent intent) {
+    protected void onNewIntent(Intent externalIntent) {
+        final SafeIntent intent = new SafeIntent(externalIntent);
+
         if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoExiting)) {
             // We're exiting and shouldn't try to do anything else. In the case
             // where we are hung while exiting, we should force the process to exit.
             GeckoAppShell.systemExit();
             return;
         }
 
         // if we were previously OOM killed, we can end up here when launching
         // from external shortcuts, so set this as the intent for initialization
         if (!mInitialized) {
-            setIntent(intent);
+            setIntent(externalIntent);
             return;
         }
 
         final String action = intent.getAction();
 
         if (ACTION_LOAD.equals(action)) {
             String uri = intent.getDataString();
             Tabs.getInstance().loadUrl(uri);
@@ -1854,26 +1859,26 @@ public abstract class GeckoApp
         } else if (ACTION_ALERT_CALLBACK.equals(action)) {
             processAlertCallback(intent);
         } else if (NotificationHelper.HELPER_BROADCAST_ACTION.equals(action)) {
             NotificationHelper.getInstance(getApplicationContext()).handleNotificationIntent(intent);
         } else if (ACTION_LAUNCH_SETTINGS.equals(action)) {
             // Check if launched from data reporting notification.
             Intent settingsIntent = new Intent(GeckoApp.this, GeckoPreferences.class);
             // Copy extras.
-            settingsIntent.putExtras(intent);
+            settingsIntent.putExtras(intent.getUnsafe());
             startActivity(settingsIntent);
         }
     }
 
     /**
      * Handles getting a URI from an intent in a way that is backwards-
      * compatible with our previous implementations.
      */
-    protected String getURIFromIntent(Intent intent) {
+    protected String getURIFromIntent(SafeIntent intent) {
         final String action = intent.getAction();
         if (ACTION_ALERT_CALLBACK.equals(action) || NotificationHelper.HELPER_BROADCAST_ACTION.equals(action)) {
             return null;
         }
 
         return intent.getDataString();
     }
 
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -20,45 +20,41 @@ import java.net.URLConnection;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Queue;
-import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
-import org.json.JSONException;
-import org.json.JSONObject;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
 import org.mozilla.gecko.favicons.decoders.FaviconDecoder;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PanZoomController;
+import org.mozilla.gecko.mozglue.ContextUtils;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.mozglue.JNITarget;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 import org.mozilla.gecko.mozglue.generatorannotations.OptionalGeneratedParameter;
 import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 import org.mozilla.gecko.prompts.PromptService;
-import org.mozilla.gecko.SmsManager;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoRequest;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.NativeEventListener;
 import org.mozilla.gecko.util.NativeJSContainer;
 import org.mozilla.gecko.util.NativeJSObject;
 import org.mozilla.gecko.util.ProxySelector;
-import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
@@ -2514,17 +2510,17 @@ public class GeckoAppShell
         }
 
         return "DIRECT";
     }
 
     /* Downloads the URI pointed to by a share intent, and alters the intent to point to the locally stored file.
      */
     public static void downloadImageForIntent(final Intent intent) {
-        final String src = StringUtils.getStringExtra(intent, Intent.EXTRA_TEXT);
+        final String src = ContextUtils.getStringExtra(intent, Intent.EXTRA_TEXT);
         if (src == null) {
             showImageShareFailureToast();
             return;
         }
 
         final File dir = GeckoApp.getTempDirectory();
 
         if (dir == null) {
--- a/mobile/android/base/NotificationHelper.java
+++ b/mobile/android/base/NotificationHelper.java
@@ -1,36 +1,33 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko;
 
-import org.mozilla.gecko.gfx.BitmapUtils;
-import org.mozilla.gecko.util.GeckoEventListener;
-import org.mozilla.gecko.util.StringUtils;
+import java.util.HashMap;
+import java.util.Iterator;
+
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
+import org.mozilla.gecko.gfx.BitmapUtils;
+import org.mozilla.gecko.mozglue.ContextUtils.SafeIntent;
+import org.mozilla.gecko.util.GeckoEventListener;
 
-import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.IntentFilter;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.support.v4.app.NotificationCompat;
 import android.util.Log;
 
-import java.util.Iterator;
-import java.util.HashMap;
-
 public final class NotificationHelper implements GeckoEventListener {
     public static final String HELPER_BROADCAST_ACTION = AppConstants.ANDROID_PACKAGE_NAME + ".helperBroadcastAction";
 
     public static final String NOTIFICATION_ID = "NotificationHelper_ID";
     private static final String LOGTAG = "GeckoNotificationHelper";
     private static final String HELPER_NOTIFICATION = "helperNotif";
 
     // Attributes mandatory to be used while sending a notification from js.
@@ -105,17 +102,17 @@ public final class NotificationHelper im
             hideNotification(message);
         }
     }
 
     public boolean isHelperIntent(Intent i) {
         return i.getBooleanExtra(HELPER_NOTIFICATION, false);
     }
 
-    public void handleNotificationIntent(Intent i) {
+    public void handleNotificationIntent(SafeIntent i) {
         final Uri data = i.getData();
         if (data == null) {
             Log.e(LOGTAG, "handleNotificationEvent: empty data");
             return;
         }
         final String id = data.getQueryParameter(ID_ATTR);
         final String notificationType = data.getQueryParameter(EVENT_TYPE_ATTR);
         if (id == null || notificationType == null) {
@@ -132,17 +129,17 @@ public final class NotificationHelper im
                 return;
             }
         }
 
         JSONObject args = new JSONObject();
 
         // The handler and cookie parameters are optional.
         final String handler = data.getQueryParameter(HANDLER_ATTR);
-        final String cookie = StringUtils.getStringExtra(i, COOKIE_ATTR);
+        final String cookie = i.getStringExtra(COOKIE_ATTR);
 
         try {
             args.put(ID_ATTR, id);
             args.put(EVENT_TYPE_ATTR, notificationType);
             args.put(HANDLER_ATTR, handler);
             args.put(COOKIE_ATTR, cookie);
 
             if (BUTTON_EVENT.equals(notificationType)) {
--- a/mobile/android/base/RestrictedProfiles.java
+++ b/mobile/android/base/RestrictedProfiles.java
@@ -20,27 +20,37 @@ import android.os.Build;
 import android.os.Bundle;
 import android.os.UserManager;
 import android.util.Log;
 
 @RobocopTarget
 public class RestrictedProfiles {
     private static final String LOGTAG = "GeckoRestrictedProfiles";
 
-    private static Boolean inGuest = null;
+    private static volatile Boolean inGuest = null;
 
     @SuppressWarnings("serial")
     private static final List<String> BANNED_SCHEMES = new ArrayList<String>() {{
         add("file");
         add("chrome");
         add("resource");
         add("jar");
         add("wyciwyg");
     }};
 
+    /**
+     * This is a hack to allow non-GeckoApp activities to safely call into
+     * RestrictedProfiles without reworking this class or GeckoProfile.
+     *
+     * It can be removed after Bug 1077590 lands.
+     */
+    public static void initWithProfile(GeckoProfile profile) {
+        inGuest = profile.inGuestMode();
+    }
+
     private static boolean getInGuest() {
         if (inGuest == null) {
             inGuest = GeckoAppShell.getGeckoInterface().getProfile().inGuestMode();
         }
 
         return inGuest;
     }
 
@@ -81,51 +91,50 @@ public class RestrictedProfiles {
                 return rest;
             }
         }
 
         throw new IllegalArgumentException("Unknown action " + action);
     }
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    @RobocopTarget
-    private static Bundle getRestrictions() {
-        final UserManager mgr = (UserManager) GeckoAppShell.getContext().getSystemService(Context.USER_SERVICE);
+    private static Bundle getRestrictions(final Context context) {
+        final UserManager mgr = (UserManager) context.getSystemService(Context.USER_SERVICE);
         return mgr.getUserRestrictions();
     }
 
     /**
      * This method does the system version check for you.
      *
      * Returns false if the system doesn't support restrictions,
      * or the provided value is not present in the set of user
      * restrictions.
      *
      * Returns true otherwise.
      */
-    private static boolean getRestriction(final String name) {
+    private static boolean getRestriction(final Context context, final String name) {
         // Early versions don't support restrictions at all,
         // so no action can be restricted.
         if (Versions.preJBMR2) {
             return false;
         }
 
-        return getRestrictions().getBoolean(name, false);
+        return getRestrictions(context).getBoolean(name, false);
     }
 
-    private static boolean canLoadUrl(final String url) {
+    private static boolean canLoadUrl(final Context context, final String url) {
         // Null URLs are always permitted.
         if (url == null) {
             return true;
         }
 
         try {
             // If we're not in guest mode, and the system restriction isn't in place, everything is allowed.
             if (!getInGuest() &&
-                !getRestriction(Restriction.DISALLOW_BROWSE_FILES.name)) {
+                !getRestriction(context, Restriction.DISALLOW_BROWSE_FILES.name)) {
                 return true;
             }
         } catch (IllegalArgumentException ex) {
             Log.i(LOGTAG, "Invalid action", ex);
         }
 
         final Uri u = Uri.parse(url);
         final String scheme = u.getScheme();
@@ -140,59 +149,71 @@ public class RestrictedProfiles {
         }
 
         // TODO: The UserManager should support blacklisting URLs by the device owner.
         return true;
     }
 
     @WrapElementForJNI
     public static boolean isUserRestricted() {
+        return isUserRestricted(GeckoAppShell.getContext());
+    }
+
+    private static boolean isUserRestricted(final Context context) {
         // Guest mode is supported in all Android versions.
         if (getInGuest()) {
             return true;
         }
 
         if (Versions.preJBMR2) {
             return false;
         }
 
-        return !getRestrictions().isEmpty();
+        return !getRestrictions(context).isEmpty();
     }
 
-    public static boolean isAllowed(Restriction action) {
-        return isAllowed(action.id, null);
+    public static boolean isAllowed(final Context context, final Restriction action) {
+        return isAllowed(context, action.id, null);
     }
 
     @WrapElementForJNI
     public static boolean isAllowed(int action, String url) {
+        return isAllowed(GeckoAppShell.getContext(), action, url);
+    }
+
+    private static boolean isAllowed(final Context context, int action, String url) {
         final Restriction restriction;
         try {
             restriction = geckoActionToRestriction(action);
         } catch (IllegalArgumentException ex) {
             // Unknown actions represent a coding error, so we
             // refuse the action and log.
             Log.e(LOGTAG, "Unknown action " + action + "; check calling code.");
             return false;
         }
 
         if (getInGuest()) {
             if (Restriction.DISALLOW_BROWSE_FILES == restriction) {
-                return canLoadUrl(url);
+                return canLoadUrl(context, url);
             }
 
             // Guest users can't do anything.
             return false;
         }
 
         // NOTE: Restrictions hold the opposite intention, so we need to flip it.
-        return !getRestriction(restriction.name);
+        return !getRestriction(context, restriction.name);
     }
 
     @WrapElementForJNI
     public static String getUserRestrictions() {
+        return getUserRestrictions(GeckoAppShell.getContext());
+    }
+
+    private static String getUserRestrictions(final Context context) {
         // Guest mode is supported in all Android versions
         if (getInGuest()) {
             StringBuilder builder = new StringBuilder("{ ");
 
             for (Restriction restriction : Restriction.values()) {
                 builder.append("\"" + restriction.name + "\": true, ");
             }
 
@@ -200,17 +221,17 @@ public class RestrictedProfiles {
             return builder.toString();
         }
 
         if (Versions.preJBMR2) {
             return "{}";
         }
 
         final JSONObject json = new JSONObject();
-        final Bundle restrictions = getRestrictions();
+        final Bundle restrictions = getRestrictions(context);
         final Set<String> keys = restrictions.keySet();
 
         for (String key : keys) {
             try {
                 json.put(key, restrictions.get(key));
             } catch (JSONException e) {
             }
         }
--- a/mobile/android/base/favicons/LoadFaviconTask.java
+++ b/mobile/android/base/favicons/LoadFaviconTask.java
@@ -333,27 +333,30 @@ public class LoadFaviconTask {
     Bitmap doInBackground() {
         if (isCancelled()) {
             return null;
         }
 
         // Attempt to decode the favicon URL as a data URL. We don't bother storing such URIs in
         // the database: the cost of decoding them here probably doesn't exceed the cost of mucking
         // about with the DB.
-        LoadFaviconResult uriBitmaps = FaviconDecoder.decodeDataURI(faviconURL);
-        if (uriBitmaps != null) {
-            return pushToCacheAndGetResult(uriBitmaps);
+        final boolean isEmpty = TextUtils.isEmpty(faviconURL);
+        if (!isEmpty) {
+            LoadFaviconResult uriBitmaps = FaviconDecoder.decodeDataURI(faviconURL);
+            if (uriBitmaps != null) {
+                return pushToCacheAndGetResult(uriBitmaps);
+            }
         }
 
         String storedFaviconUrl;
         boolean isUsingDefaultURL = false;
 
         // Handle the case of malformed favicon URL.
         // If favicon is empty, fall back to the stored one.
-        if (TextUtils.isEmpty(faviconURL)) {
+        if (isEmpty) {
             // Try to get the favicon URL from the memory cache.
             storedFaviconUrl = Favicons.getFaviconURLForPageURLFromCache(pageUrl);
 
             // If that failed, try to get the URL from the database.
             if (storedFaviconUrl == null) {
                 storedFaviconUrl = Favicons.getFaviconURLForPageURL(context, pageUrl);
                 if (storedFaviconUrl != null) {
                     // If that succeeded, cache the URL loaded from the database in memory.
--- a/mobile/android/base/home/HomeConfigPrefsBackend.java
+++ b/mobile/android/base/home/HomeConfigPrefsBackend.java
@@ -80,17 +80,17 @@ class HomeConfigPrefsBackend implements 
             panelConfigs.add(createBuiltinPanelConfig(mContext, PanelType.READING_LIST));
         }
 
         final PanelConfig historyEntry = createBuiltinPanelConfig(mContext, PanelType.HISTORY);
         final PanelConfig recentTabsEntry = createBuiltinPanelConfig(mContext, PanelType.RECENT_TABS);
 
         // We disable Synced Tabs for guest mode profiles.
         final PanelConfig remoteTabsEntry;
-        if (RestrictedProfiles.isAllowed(RestrictedProfiles.Restriction.DISALLOW_MODIFY_ACCOUNTS)) {
+        if (RestrictedProfiles.isAllowed(mContext, RestrictedProfiles.Restriction.DISALLOW_MODIFY_ACCOUNTS)) {
             remoteTabsEntry = createBuiltinPanelConfig(mContext, PanelType.REMOTE_TABS);
         } else {
             remoteTabsEntry = null;
         }
 
         // On tablets, we go [...|History|Recent Tabs|Synced Tabs].
         // On phones, we go [Synced Tabs|Recent Tabs|History|...].
         if (HardwareUtils.isTablet()) {
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -22,16 +22,17 @@ if CONFIG['MOZ_NATIVE_DEVICES']:
     resjar.generated_sources += ['android/support/v7/appcompat/R.java']
     resjar.generated_sources += ['android/support/v7/mediarouter/R.java']
 
 resjar.javac_flags += ['-Xlint:all']
 
 mgjar = add_java_jar('gecko-mozglue')
 mgjar.sources += [
     'mozglue/ByteBufferInputStream.java',
+    'mozglue/ContextUtils.java',
     'mozglue/DirectBufferAllocator.java',
     'mozglue/generatorannotations/OptionalGeneratedParameter.java',
     'mozglue/generatorannotations/WrapElementForJNI.java',
     'mozglue/generatorannotations/WrapEntireClassForJNI.java',
     'mozglue/JNITarget.java',
     'mozglue/NativeReference.java',
     'mozglue/NativeZip.java',
     'mozglue/RobocopTarget.java',
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/mozglue/ContextUtils.java
@@ -0,0 +1,102 @@
+/* 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/. */
+
+package org.mozilla.gecko.mozglue;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+public class ContextUtils {
+    private static final String LOGTAG = "GeckoContextUtils";
+
+    public static Bundle getBundleExtra(final Intent intent, final String name) {
+        return new SafeIntent(intent).getBundleExtra(name);
+    }
+
+    public static String getStringExtra(final Intent intent, final String name) {
+        return new SafeIntent(intent).getStringExtra(name);
+    }
+
+    public static boolean getBooleanExtra(Intent intent, String name, boolean defaultValue) {
+        return new SafeIntent(intent).getBooleanExtra(name, defaultValue);
+    }
+
+    public static class SafeIntent {
+        private final Intent intent;
+
+        public SafeIntent(final Intent intent) {
+            this.intent = intent;
+        }
+
+        public boolean getBooleanExtra(final String name, final boolean defaultValue) {
+            try {
+                return intent.getBooleanExtra(name, defaultValue);
+            } catch (OutOfMemoryError e) {
+                Log.w(LOGTAG, "Couldn't get intent extras: OOM. Malformed?");
+                return defaultValue;
+            } catch (RuntimeException e) {
+                Log.w(LOGTAG, "Couldn't get intent extras.", e);
+                return defaultValue;
+            }
+        }
+
+        public String getStringExtra(final String name) {
+            try {
+                return intent.getStringExtra(name);
+            } catch (OutOfMemoryError e) {
+                Log.w(LOGTAG, "Couldn't get intent extras: OOM. Malformed?");
+                return null;
+            } catch (RuntimeException e) {
+                Log.w(LOGTAG, "Couldn't get intent extras.", e);
+                return null;
+            }
+        }
+
+        public Bundle getBundleExtra(final String name) {
+            try {
+                return intent.getBundleExtra(name);
+            } catch (OutOfMemoryError e) {
+                Log.w(LOGTAG, "Couldn't get intent extras: OOM. Malformed?");
+                return null;
+            } catch (RuntimeException e) {
+                Log.w(LOGTAG, "Couldn't get intent extras.", e);
+                return null;
+            }
+        }
+
+        public String getAction() {
+            return intent.getAction();
+        }
+
+        public String getDataString() {
+            try {
+                return intent.getDataString();
+            } catch (OutOfMemoryError e) {
+                Log.w(LOGTAG, "Couldn't get intent data string: OOM. Malformed?");
+                return null;
+            } catch (RuntimeException e) {
+                Log.w(LOGTAG, "Couldn't get intent data string.", e);
+                return null;
+            }
+        }
+
+        public Uri getData() {
+            try {
+                return intent.getData();
+            } catch (OutOfMemoryError e) {
+                Log.w(LOGTAG, "Couldn't get intent data: OOM. Malformed?");
+                return null;
+            } catch (RuntimeException e) {
+                Log.w(LOGTAG, "Couldn't get intent data.", e);
+                return null;
+            }
+        }
+
+        public Intent getUnsafe() {
+            return intent;
+        }
+    }
+}
--- a/mobile/android/base/mozglue/GeckoLoader.java.in
+++ b/mobile/android/base/mozglue/GeckoLoader.java.in
@@ -17,24 +17,26 @@ import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
 import android.os.Environment;
 import android.util.Log;
 
+import org.mozilla.gecko.mozglue.ContextUtils.SafeIntent;
+
 public final class GeckoLoader {
     private static final String LOGTAG = "GeckoLoader";
 
     // These match AppConstants, but we're built earlier.
     private static final String ANDROID_PACKAGE_NAME = "@ANDROID_PACKAGE_NAME@";
     private static final String MOZ_APP_ABI = "@MOZ_APP_ABI@";
 
-    private static volatile Intent sIntent;
+    private static volatile SafeIntent sIntent;
     private static File sCacheFile;
     private static File sGREDir;
 
     private static final Object sLibLoadingLock = new Object();
     // Must hold sLibLoadingLock while accessing the following boolean variables.
     private static boolean sSQLiteLibsLoaded;
     private static boolean sNSSLibsLoaded;
     private static boolean sMozGlueLoaded;
@@ -118,24 +120,24 @@ public final class GeckoLoader {
         // check if the old tmp dir is there
         File oldDir = new File(tmpDir.getParentFile(), "app_tmp");
         if (oldDir.exists()) {
             delTree(oldDir);
         }
         return tmpDir;
     }
 
-    public static void setLastIntent(Intent intent) {
+    public static void setLastIntent(SafeIntent intent) {
         sIntent = intent;
     }
 
     public static void setupGeckoEnvironment(Context context, String[] pluginDirs, String profilePath) {
         // if we have an intent (we're being launched by an activity)
         // read in any environmental variables from it here
-        final Intent intent = sIntent;
+        final SafeIntent intent = sIntent;
         if (intent != null) {
             String env = intent.getStringExtra("env0");
             Log.d(LOGTAG, "Gecko environment env0: " + env);
             for (int c = 1; env != null; c++) {
                 putenv(env);
                 env = intent.getStringExtra("env" + c);
                 Log.d(LOGTAG, "env" + c + ": " + env);
             }
--- a/mobile/android/base/overlays/ui/ShareDialog.java
+++ b/mobile/android/base/overlays/ui/ShareDialog.java
@@ -14,18 +14,18 @@ import org.mozilla.gecko.LocaleAware;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.LocalBrowserDB;
 import org.mozilla.gecko.overlays.OverlayConstants;
 import org.mozilla.gecko.overlays.service.OverlayActionService;
 import org.mozilla.gecko.overlays.service.sharemethods.ParcelableClientRecord;
 import org.mozilla.gecko.overlays.service.sharemethods.SendTab;
 import org.mozilla.gecko.overlays.service.sharemethods.ShareMethod;
 import org.mozilla.gecko.sync.setup.activities.WebURLFinder;
+import org.mozilla.gecko.mozglue.ContextUtils;
 import org.mozilla.gecko.util.HardwareUtils;
-import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.UIAsyncTask;
 
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -116,17 +116,17 @@ public class ShareDialog extends LocaleA
         super.onCreate(savedInstanceState);
 
         getWindow().setWindowAnimations(0);
 
         Intent intent = getIntent();
         final Resources resources = getResources();
 
         // The URL is usually hiding somewhere in the extra text. Extract it.
-        final String extraText = StringUtils.getStringExtra(intent, Intent.EXTRA_TEXT);
+        final String extraText = ContextUtils.getStringExtra(intent, Intent.EXTRA_TEXT);
         if (TextUtils.isEmpty(extraText)) {
             abortDueToNoURL();
             return;
         }
 
         final String pageUrl = new WebURLFinder(extraText).bestWebURL();
         if (TextUtils.isEmpty(pageUrl)) {
             abortDueToNoURL();
--- a/mobile/android/base/preferences/AndroidImportPreference.java
+++ b/mobile/android/base/preferences/AndroidImportPreference.java
@@ -21,17 +21,17 @@ import android.util.Log;
 class AndroidImportPreference extends MultiPrefMultiChoicePreference {
     private static final String LOGTAG = "AndroidImport";
     public static final String PREF_KEY = "android.not_a_preference.import_android";
     private static final String PREF_KEY_PREFIX = "import_android.data.";
     private final Context mContext;
 
     public static class Handler implements GeckoPreferences.PrefHandler {
         public boolean setupPref(Context context, Preference pref) {
-            return RestrictedProfiles.isAllowed(Restriction.DISALLOW_IMPORT_SETTINGS);
+            return RestrictedProfiles.isAllowed(context, Restriction.DISALLOW_IMPORT_SETTINGS);
         }
 
         public void onChange(Context context, Preference pref, Object newValue) { }
     }
 
     public AndroidImportPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
--- a/mobile/android/base/preferences/GeckoPreferences.java
+++ b/mobile/android/base/preferences/GeckoPreferences.java
@@ -291,16 +291,19 @@ OnSharedPreferenceChangeListener
             return;
         }
 
         onLocaleChanged(currentLocale);
     }
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
+        // Make sure RestrictedProfiles is ready.
+        RestrictedProfiles.initWithProfile(GeckoProfile.get(this));
+
         if (GeckoProfile.get(this).inGuestMode()) {
             GuestSession.configureWindow(getWindow());
         }
 
         // Apply the current user-selected locale, if necessary.
         checkLocale();
 
         // Track this so we can decide whether to show locale options.
@@ -695,17 +698,17 @@ OnSharedPreferenceChangeListener
                     continue;
                 } else if (!AppConstants.MOZ_STUMBLER_BUILD_TIME_ENABLED &&
                            (PREFS_GEO_REPORTING.equals(key) ||
                             PREFS_GEO_LEARN_MORE.equals(key))) {
                     preferences.removePreference(pref);
                     i--;
                     continue;
                 } else if (PREFS_DEVTOOLS_REMOTE_ENABLED.equals(key)) {
-                    if (!RestrictedProfiles.isAllowed(RestrictedProfiles.Restriction.DISALLOW_REMOTE_DEBUGGING)) {
+                    if (!RestrictedProfiles.isAllowed(this, RestrictedProfiles.Restriction.DISALLOW_REMOTE_DEBUGGING)) {
                         preferences.removePreference(pref);
                         i--;
                         continue;
                     }
 
                     final Context thisContext = this;
                     pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                         @Override
@@ -723,17 +726,17 @@ OnSharedPreferenceChangeListener
                     // for other list prefs will be set in the PrefsHelper
                     // callback, but since this pref doesn't live in Gecko, we
                     // need to handle it separately.
                     ListPreference listPref = (ListPreference) pref;
                     CharSequence selectedEntry = listPref.getEntry();
                     listPref.setSummary(selectedEntry);
                     continue;
                 } else if (PREFS_SYNC.equals(key) &&
-                           !RestrictedProfiles.isAllowed(RestrictedProfiles.Restriction.DISALLOW_MODIFY_ACCOUNTS)) {
+                           !RestrictedProfiles.isAllowed(this, RestrictedProfiles.Restriction.DISALLOW_MODIFY_ACCOUNTS)) {
                     // Don't show sync prefs while in guest mode.
                     preferences.removePreference(pref);
                     i--;
                     continue;
                 } else if (PREFS_SEARCH_RESTORE_DEFAULTS.equals(key)) {
                     pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                         @Override
                         public boolean onPreferenceClick(Preference preference) {
--- a/mobile/android/base/tabs/TabsPanel.java
+++ b/mobile/android/base/tabs/TabsPanel.java
@@ -178,17 +178,17 @@ public class TabsPanel extends LinearLay
             }
         });
 
         mTabWidget = (IconTabWidget) findViewById(R.id.tab_widget);
 
         mTabWidget.addTab(R.drawable.tabs_normal, R.string.tabs_normal);
         mTabWidget.addTab(R.drawable.tabs_private, R.string.tabs_private);
 
-        if (RestrictedProfiles.isAllowed(RestrictedProfiles.Restriction.DISALLOW_MODIFY_ACCOUNTS)) {
+        if (RestrictedProfiles.isAllowed(mContext, RestrictedProfiles.Restriction.DISALLOW_MODIFY_ACCOUNTS)) {
             // The initial icon is not the animated icon, because on Android
             // 4.4.2, the animation starts immediately (and can start at other
             // unpredictable times). See Bug 1015974.
             mTabWidget.addTab(R.drawable.tabs_synced, R.string.tabs_synced);
         }
 
         mTabWidget.setTabSelectionListener(this);
 
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -99,16 +99,17 @@ skip-if = android_version == "10"
 # [testVkbOverlap] # see bug 907274
 
 # Using JavascriptTest
 [testAccounts]
 [testAndroidLog]
 [testBrowserDiscovery]
 [testDebuggerServer]
 [testDeviceSearchEngine]
+[testFilePicker]
 [testJNI]
 # [testMozPay] # see bug 945675
 [testNetworkManager]
 [testOfflinePage]
 [testOrderedBroadcast]
 [testOSLocale]
 [testResourceSubstitutions]
 [testRestrictedProfiles]
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testFilePicker.java
@@ -0,0 +1,52 @@
+/* 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/. */
+
+package org.mozilla.gecko.tests;
+
+import static org.mozilla.gecko.tests.helpers.AssertionHelper.fFail;
+
+import org.mozilla.gecko.EventDispatcher;
+import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoEvent;
+import org.mozilla.gecko.util.GeckoEventListener;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class testFilePicker extends JavascriptTest implements GeckoEventListener {
+    private static final String TEST_FILENAME = "/mnt/sdcard/my-favorite-martian.png";
+
+    public testFilePicker() {
+        super("testFilePicker.js");
+    }
+
+    @Override
+    public void handleMessage(String event, final JSONObject message) {
+        // We handle the FilePicker message here so we can send back hard coded file information. We
+        // don't want to try to emulate "picking" a file using the Android intent chooser.
+        if (event.equals("FilePicker:Show")) {
+            try {
+                message.put("file", TEST_FILENAME);
+            } catch (JSONException ex) {
+                fFail("Can't add filename to message " + TEST_FILENAME);
+            }
+
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FilePicker:Result", message.toString()));
+        }
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        EventDispatcher.getInstance().registerGeckoThreadListener(this, "FilePicker:Show");
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "FilePicker:Show");
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testFilePicker.js
@@ -0,0 +1,80 @@
+// -*- 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/. */
+
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+function ok(passed, text) {
+  do_report_result(passed, text, Components.stack.caller, false);
+}
+
+function is(lhs, rhs, text) {
+  do_report_result(lhs === rhs, text, Components.stack.caller, false);
+}
+
+add_test(function filepicker_open() {
+  let chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
+
+  do_test_pending();
+
+  let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+  fp.appendFilter("Martian files", "*.martian");
+  fp.appendFilters(Ci.nsIFilePicker.filterAll);
+  fp.filterIndex = 0;
+
+  let fpCallback = function(result) {
+    if (result == Ci.nsIFilePicker.returnOK || result == Ci.nsIFilePicker.returnReplace) {
+      do_print("File: " + fp.file.path);
+      is(fp.file.path, "/mnt/sdcard/my-favorite-martian.png", "Retrieve the right martian file!");
+
+      let files = fp.files;
+      while (files.hasMoreElements()) {
+        let file = files.getNext().QueryInterface(Ci.nsIFile);
+        do_print("File: " + file.path);
+        is(file.path, "/mnt/sdcard/my-favorite-martian.png", "Retrieve the right martian file from array!");
+      }
+
+      do_print("DOMFile: " + fp.domfile.mozFullPath);
+      is(fp.domfile.mozFullPath, "/mnt/sdcard/my-favorite-martian.png", "Retrieve the right martian domfile!");
+
+      let domfiles = fp.domfiles;
+      while (domfiles.hasMoreElements()) {
+        let domfile = domfiles.getNext();
+        do_print("DOMFile: " + domfile.mozFullPath);
+        is(domfile.mozFullPath, "/mnt/sdcard/my-favorite-martian.png", "Retrieve the right martian file from domfile array!");
+      }
+
+      do_test_finished();
+
+      run_next_test();
+    }
+  };
+
+  try {
+    fp.init(chromeWin, "Open", Ci.nsIFilePicker.modeOpen);
+  } catch(ex) {
+    ok(false, "Android should support FilePicker.modeOpen: " + ex);
+  }
+  fp.open(fpCallback);
+});
+
+add_test(function filepicker_save() {
+  let failed = false;
+  let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+  try {
+    fp.init(null, "Save", Ci.nsIFilePicker.modeSave);
+  } catch(ex) {
+    failed = true;
+  }
+  ok(failed, "Android does not support FilePicker.modeSave");
+
+  run_next_test();
+});
+
+run_next_test();
+
--- a/mobile/android/base/util/StringUtils.java
+++ b/mobile/android/base/util/StringUtils.java
@@ -1,19 +1,17 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.util;
 
-import android.content.Intent;
 import android.net.Uri;
 import android.text.TextUtils;
-import android.util.Log;
 
 public class StringUtils {
     private static final String LOGTAG = "GeckoStringUtils";
 
     private static final String FILTER_URL_PREFIX = "filter://";
     private static final String USER_ENTERED_URL_PREFIX = "user-entered:";
 
     /*
@@ -184,21 +182,9 @@ public class StringUtils {
             return uri.getSchemeSpecificPart();
         }
         return url;
     }
 
     public static String encodeUserEnteredUrl(String url) {
         return Uri.fromParts("user-entered", url, null).toString();
     }
-
-    public static String getStringExtra(Intent intent, String name) {
-        try {
-            return intent.getStringExtra(name);
-        } catch (android.os.BadParcelableException ex) {
-            Log.w(LOGTAG, "Couldn't get string extra: malformed intent.");
-            return null;
-        } catch (RuntimeException re) {
-            Log.w(LOGTAG, "Couldn't get string extra.", re);
-            return null;
-        }
-    }
 }
--- a/mobile/android/base/webapp/WebappImpl.java
+++ b/mobile/android/base/webapp/WebappImpl.java
@@ -13,16 +13,17 @@ import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.GeckoApp;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoThread;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.mozglue.ContextUtils.SafeIntent;
 import org.mozilla.gecko.util.NativeJSObject;
 import org.mozilla.gecko.webapp.InstallHelper.InstallCallback;
 
 import android.content.Intent;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
@@ -152,17 +153,17 @@ public class WebappImpl extends GeckoApp
         }
 
         launchWebapp(origin);
 
         setTitle(mAppName);
     }
 
     @Override
-    protected String getURIFromIntent(Intent intent) {
+    protected String getURIFromIntent(SafeIntent intent) {
         String uri = super.getURIFromIntent(intent);
         if (uri != null) {
             return uri;
         }
         // This is where we construct the URL from the Intent from the
         // the synthesized APK.
 
         // TODO Translate AndroidIntents into WebActivities here.
--- a/mobile/android/chrome/content/WebcompatReporter.js
+++ b/mobile/android/chrome/content/WebcompatReporter.js
@@ -71,17 +71,17 @@ var WebcompatReporter = {
         label: this.strings.GetStringFromName("webcompat.reportDesktopModeYes.label"),
         callback: () => this.reportIssue(currentURI)
       }
     };
     NativeWindow.toast.show(message, "long", options);
   },
 
   reportIssue: function(url) {
-    let webcompatURL = new URL("http://webcompat.com/");
+    let webcompatURL = new URL("https://webcompat.com/");
     webcompatURL.searchParams.append("open", "1");
     webcompatURL.searchParams.append("url", url);
     if (PrivateBrowsingUtils.isBrowserPrivate(BrowserApp.selectedTab.browser)) {
       BrowserApp.addTab(webcompatURL.href, {parentId: BrowserApp.selectedTab.id, isPrivate: true});
     } else {
       BrowserApp.addTab(webcompatURL.href);
     }
   }
--- a/mobile/android/components/FilePicker.js
+++ b/mobile/android/components/FilePicker.js
@@ -141,18 +141,20 @@ FilePicker.prototype = {
   },
 
   get domfile() {
     let f = this.file;
     if (!f) {
         return null;
     }
 
-    if (this._domWin) {
-      return new this._domWin.File(f);
+    let win = this._domWin;
+    if (win) {
+      let utils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
+      return utils.wrapDOMFile(f);
     }
 
     return new File(f);
   },
 
   get domfiles() {
     let win = this._domWin;
     return this.getEnumerator([this.file], function(file) {
@@ -208,17 +210,16 @@ FilePicker.prototype = {
     this._callback = callback;
     this._sendMessage();
   },
 
   _sendMessage: function() {
     let msg = {
       type: "FilePicker:Show",
       guid: this.guid,
-      guid: this.guid,
       title: this._title,
     };
 
     // Knowing the window lets us destroy any temp files when the tab is closed
     // Other consumers of the file picker may have to either wait for Android
     // to clean up the temp dir (not guaranteed) or clean up after themselves.
     let win = Services.wm.getMostRecentWindow('navigator:browser');
     let tab = win.BrowserApp.getTabForWindow(this._domWin.top)
--- a/toolkit/components/printing/content/printPreviewBindings.xml
+++ b/toolkit/components/printing/content/printPreviewBindings.xml
@@ -18,17 +18,17 @@
   <binding id="printpreviewtoolbar" 
            extends="chrome://global/content/bindings/toolbar.xml#toolbar">
     <resources>
       <stylesheet src="chrome://global/skin/printPreview.css"/>
     </resources>
 
     <content>
       <xul:button label="&print.label;" accesskey="&print.accesskey;"
-        oncommand="PrintUtils.print();" icon="print"/>
+        oncommand="this.parentNode.print();" icon="print"/>
 
       <xul:button label="&pageSetup.label;" accesskey="&pageSetup.accesskey;"
         oncommand="this.parentNode.doPageSetup();"/>
 
       <xul:vbox align="center" pack="center">
         <xul:label value="&page.label;" accesskey="&page.accesskey;" control="pageNumber"/>
       </xul:vbox>
       <xul:toolbarbutton class="home-arrow tabbable"
@@ -88,17 +88,17 @@
       </xul:hbox>
 
       <xul:toolbarseparator class="toolbarseparator-primary"/>
       <xul:button label="&close.label;" accesskey="&close.accesskey;"
         oncommand="PrintUtils.exitPrintPreview();" icon="close"/>
       <xul:data value="&customPrompt.title;"/>
     </content>
 
-    <implementation>
+    <implementation implements="nsIMessageListener">
       <field name="mPrintButton">
         document.getAnonymousNodes(this)[0]
       </field>
       <field name="mPageTextBox">
         document.getAnonymousNodes(this)[5].childNodes[0]
       </field>
       <field name="mTotalPages">
         document.getAnonymousNodes(this)[5].childNodes[2]
@@ -120,26 +120,34 @@
       </field>
       <field name="mCustomTitle">
         document.getAnonymousNodes(this)[15].firstChild
       </field>
       <field name="mPrintPreviewObs">
       </field>
       <field name="mWebProgress">
       </field>
-     
-      <constructor>
-      <![CDATA[
-        var print = PrintUtils.getPrintPreview();
-        this.mTotalPages.value = print.printPreviewNumPages;
-        this.mPageTextBox.max = print.printPreviewNumPages;
+      <field name="mPPBrowser">
+        null
+      </field>
+      <field name="mMessageManager">
+        null
+      </field>
 
-        this.updateToolbar();
-      ]]>
-      </constructor>
+      <method name="initialize">
+        <parameter name="aPPBrowser"/>
+        <body>
+        <![CDATA[
+          this.mPPBrowser = aPPBrowser;
+          this.mMessageManager = aPPBrowser.messageManager;
+          this.mMessageManager.addMessageListener("Printing:Preview:UpdatePageCount", this);
+          this.updateToolbar();
+        ]]>
+        </body>
+      </method>
 
       <method name="doPageSetup">
         <body>
         <![CDATA[
           var didOK = PrintUtils.showPageSetup();
           if (didOK) {
             // the changes that effect the UI
             this.updateToolbar();
@@ -151,48 +159,57 @@
         </body>
       </method>
 
       <method name="navigate">
         <parameter name="aDirection"/>
         <parameter name="aPageNum"/>
         <parameter name="aHomeOrEnd"/>
         <body>
-        <![CDATA[          
-          var print = PrintUtils.getPrintPreview();
+        <![CDATA[
+          const nsIWebBrowserPrint = Components.interfaces.nsIWebBrowserPrint;
+          let navType, pageNum;
 
           // we use only one of aHomeOrEnd, aDirection, or aPageNum
-          if (aHomeOrEnd)
-          {
-            var homeOrEnd;
-            if (aHomeOrEnd == "home")
-            {
-              homeOrEnd = print.PRINTPREVIEW_HOME;
-              this.mPageTextBox.value = 1;  
-            }
-            else
-            {
-              homeOrEnd = print.PRINTPREVIEW_END;
-              this.mPageTextBox.value = print.printPreviewNumPages;
+          if (aHomeOrEnd) {
+            // We're going to either the very first page ("home"), or the
+            // very last page ("end").
+            if (aHomeOrEnd == "home") {
+              navType = nsIWebBrowserPrint.PRINTPREVIEW_HOME;
+              this.mPageTextBox.value = 1;
+            } else {
+              navType = nsIWebBrowserPrint.PRINTPREVIEW_END;
+              this.mPageTextBox.value = this.mPageTextBox.max;
             }
-            
-            print.printPreviewNavigate(homeOrEnd, 0);
-          }
-          else if (aDirection)
-          {
+            pageNum = 0;
+          } else if (aDirection) {
+            // aDirection is either +1 or -1, and allows us to increment
+            // or decrement our currently viewed page.
             this.mPageTextBox.valueNumber += aDirection;
-            print.printPreviewNavigate(
-              print.PRINTPREVIEW_GOTO_PAGENUM,
-              this.mPageTextBox.valueNumber);
+            navType = nsIWebBrowserPrint.PRINTPREVIEW_GOTO_PAGENUM;
+            pageNum = this.mPageTextBox.value;  // TODO: back to valueNumber?
+          } else {
+            // We're going to a specific page (aPageNum)
+            navType = nsIWebBrowserPrint.PRINTPREVIEW_GOTO_PAGENUM;
+            pageNum = aPageNum;
           }
-          else 
-          {
-            print.printPreviewNavigate(
-              print.PRINTPREVIEW_GOTO_PAGENUM, aPageNum);
-          }
+
+          this.mMessageManager.sendAsyncMessage("Printing:Preview:Navigate", {
+            navType: navType,
+            pageNum: pageNum,
+          });
+        ]]>
+        </body>
+      </method>
+
+      <method name="print">
+        <body>
+        <![CDATA[
+          let contentWindow = this.mPPBrowser.contentWindowAsCPOW;
+          PrintUtils.print(contentWindow, this.mPPBrowser);
         ]]>
         </body>
       </method>
 
       <method name="promptForScaleValue">
         <parameter name="aValue"/>
         <body>
         <![CDATA[
@@ -285,42 +302,55 @@
           }
         ]]>
         </body>
       </method>
 
       <method name="updateToolbar">
         <body>
         <![CDATA[
-          var print = PrintUtils.getPrintPreview();
           var settings = PrintUtils.getPrintSettings();
 
           var isPortrait = settings.orientation == Components.interfaces.nsIPrintSettings.kPortraitOrientation;
 
           this.mPortaitButton.checked = isPortrait;
           this.mLandscapeButton.checked = !isPortrait;
 
           if (settings.shrinkToFit) {
             this.mScaleCombobox.value = "ShrinkToFit";
           } else {
             this.setScaleCombobox(settings.scaling);
           }
 
-          this.mTotalPages.value = print.printPreviewNumPages;
-          this.mPageTextBox.max = print.printPreviewNumPages;
           this.mPageTextBox.value = 1;
+
+          this.mMessageManager.sendAsyncMessage("Printing:Preview:UpdatePageCount");
         ]]>
         </body>
       </method>
 
       <method name="savePrintSettings">
         <parameter name="settings"/>
         <parameter name="flags"/>
         <body><![CDATA[
           var PSSVC = Components.classes["@mozilla.org/gfx/printsettings-service;1"]
                                 .getService(Components.interfaces.nsIPrintSettingsService);
           PSSVC.savePrintSettingsToPrefs(settings, true, flags);
         ]]></body>
       </method>
+
+      <!-- nsIMessageListener -->
+      <method name="receiveMessage">
+        <parameter name="message"/>
+        <body>
+        <![CDATA[
+          if (message.name == "Printing:Preview:UpdatePageCount") {
+            let numPages = message.data.numPages;
+            this.mTotalPages.value = numPages;
+            this.mPageTextBox.max = numPages;
+          }
+        ]]>
+        </body>
+      </method>
     </implementation>
   </binding>
 
 </bindings>
--- a/toolkit/components/printing/content/printUtils.js
+++ b/toolkit/components/printing/content/printUtils.js
@@ -1,34 +1,98 @@
-
 // -*- tab-width: 2; 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/. */
 
+/**
+ * PrintUtils is a utility for front-end code to trigger common print
+ * operations (printing, show print preview, show page settings).
+ *
+ * Unfortunately, likely due to inconsistencies in how different operating
+ * systems do printing natively, our XPCOM-level printing interfaces
+ * are a bit confusing and the method by which we do something basic
+ * like printing a page is quite circuitous.
+ *
+ * To compound that, we need to support remote browsers, and that means
+ * kicking off the print jobs in the content process. This means we send
+ * messages back and forth to that process. browser-content.js contains
+ * the object that listens and responds to the messages that PrintUtils
+ * sends.
+ *
+ * PrintUtils sends messages at different points in its implementation, but
+ * their documentation is consolidated here for ease-of-access.
+ *
+ *
+ * Messages sent:
+ *
+ *   Printing:Print
+ *     This message is sent to kick off a print job for a particular content
+ *     window (which is passed along with the message). We also pass print
+ *     settings with this message - though bug 1088070 will have us gather
+ *     those settings from the content process instead.
+ *
+ *   Printing:Preview:Enter
+ *     This message is sent to put content into print preview mode. We pass
+ *     the content window of the browser we're showing the preview of, and
+ *     the target of the message is the browser that we'll be showing the
+ *     preview in. We also pass print settings in this message, but
+ *     bug 1088070 will have us gather those settings from the content process
+ *     instead.
+ *
+ *   Printing:Preview:Exit
+ *     This message is sent to take content out of print preview mode.
+ *
+ *
+ * Messages Received
+ *
+ *   Printing:Preview:Entered
+ *     This message is sent by the content process once it has completed
+ *     putting the content into print preview mode. We must wait for that to
+ *     to complete before switching the chrome UI to print preview mode,
+ *     otherwise we have layout issues.
+ *
+ *   Printing:Preview:StateChange, Printing:Preview:ProgressChange
+ *     Due to a timing issue resulting in a main-process crash, we have to
+ *     manually open the progress dialog for print preview. The progress
+ *     dialog is opened here in PrintUtils, and then we listen for update
+ *     messages from the child. Bug 1088061 has been filed to investigate
+ *     other solutions.
+ *
+ */
+
 var gPrintSettingsAreGlobal = false;
 var gSavePrintSettings = false;
 var gFocusedElement = null;
 
 var PrintUtils = {
   bailOut: function () {
-    let remote = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-            .getInterface(Components.interfaces.nsIWebNavigation)
-            .QueryInterface(Components.interfaces.nsILoadContext)
-            .useRemoteTabs;
-    if (remote) {
+    let pref = Components.classes["@mozilla.org/preferences-service;1"]
+                         .getService(Components.interfaces.nsIPrefBranch);
+    let allow_for_testing = false;
+    try {
+      allow_for_testing = pref.getBoolPref("print.enable_e10s_testing");
+    } catch(e) {
+      // The pref wasn't set, so I guess we're not overriding.
+    }
+    if (this.usingRemoteTabs && !allow_for_testing) {
       alert("e10s printing is not implemented yet. Bug 927188.");
       return true;
     }
     return false;
   },
 
-  showPageSetup: function ()
-  {
+  /**
+   * Shows the page setup dialog, and saves any settings changed in
+   * that dialog if print.save_print_settings is set to true.
+   *
+   * @return true on success, false on failure
+   */
+  showPageSetup: function () {
     if (this.bailOut()) {
       return;
     }
     try {
       var printSettings = this.getPrintSettings();
       var PRINTPROMPTSVC = Components.classes["@mozilla.org/embedcomp/printingprompt-service;1"]
                                      .getService(Components.interfaces.nsIPrintingPromptService);
       PRINTPROMPTSVC.showPageSetup(window, printSettings, null);
@@ -40,112 +104,271 @@ var PrintUtils = {
       }
     } catch (e) {
       dump("showPageSetup "+e+"\n");
       return false;
     }
     return true;
   },
 
-  print: function (aWindow)
+  /**
+   * Starts printing the contents of aWindow.
+   *
+   * @param aWindow
+   *        An nsIDOMWindow to initiate the printing of. If the chrome window
+   *        is not running with remote tabs, this defaults to window.content if
+   *        omitted. If running with remote tabs, the caller must pass in the
+   *        content window to be printed. This function throws if that invariant
+   *        is violated.
+   * @param aBrowser (optional for non-remote browsers)
+   *        The remote <xul:browser> that contains aWindow. This argument is
+   *        not necessary if aWindow came from a non-remote browser, but is
+   *        strictly required otherwise. This function will throw if aWindow
+   *        comes from a remote browser and aBrowser is not provided.
+   */
+  print: function (aWindow, aBrowser)
   {
     if (this.bailOut()) {
       return;
     }
-    var webBrowserPrint = this.getWebBrowserPrint(aWindow);
-    var printSettings = this.getPrintSettings();
-    try {
-      webBrowserPrint.print(printSettings, null);
-      if (gPrintSettingsAreGlobal && gSavePrintSettings) {
-        var PSSVC = Components.classes["@mozilla.org/gfx/printsettings-service;1"]
-                              .getService(Components.interfaces.nsIPrintSettingsService);
-        PSSVC.savePrintSettingsToPrefs(printSettings, true,
-                                       printSettings.kInitSaveAll);
-        PSSVC.savePrintSettingsToPrefs(printSettings, false,
-                                       printSettings.kInitSavePrinterName);
+
+    if (!aWindow) {
+      // If we're using remote browsers, chances are that window.content will
+      // not be defined.
+      if (this.usingRemoteTabs) {
+        throw new Error("Windows running with remote tabs must explicitly pass " +
+                        "a content window to PrintUtils.print.");
+      }
+      // Otherwise, we should have access to window.content.
+      aWindow = window.content;
+    }
+
+    if (Cu.isCrossProcessWrapper(aWindow)) {
+      if (!aBrowser) {
+        throw new Error("PrintUtils.print expects a remote browser passed as " +
+                        "an argument if the content window is a CPOW.");
       }
-    } catch (e) {
-      // Pressing cancel is expressed as an NS_ERROR_ABORT return value,
-      // causing an exception to be thrown which we catch here.
-      // Unfortunately this will also consume helpful failures, so add a
-      // dump("print: "+e+"\n"); // if you need to debug
+    } else {
+      // For content windows coming from non-remote browsers, the browser can
+      // be resolved as the chromeEventHandler.
+      aBrowser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Ci.nsIWebNavigation)
+                        .QueryInterface(Ci.nsIDocShell)
+                        .chromeEventHandler;
     }
+
+    if (!aBrowser) {
+      throw new Error("PrintUtils.print could not resolve content window " +
+                      "to a browser.");
+    }
+
+    let printSettings = this.getPrintSettings();
+
+    let mm = aBrowser.messageManager;
+    mm.sendAsyncMessage("Printing:Print", null, {
+      printSettings: printSettings,
+      contentWindow: aWindow,
+    });
   },
 
-  // If aCallback is not null, it must be an object which has the following methods:
-  // getPrintPreviewBrowser(), getSourceBrowser(),
-  // getNavToolbox(), onEnter() and onExit().
-  // If aCallback is null, then printPreview must previously have been called with
-  // non-null aCallback and that object will be reused.
-  printPreview: function (aCallback)
+  /**
+   * Initializes print preview.
+   *
+   * @param aListenerObj
+   *        An object that defines the following functions:
+   *
+   *        getPrintPreviewBrowser:
+   *          Returns the <xul:browser> to display the print preview in.
+   *
+   *        getSourceBrowser:
+   *          Returns the <xul:browser> that contains the document being
+   *          printed.
+   *
+   *        getNavToolbox:
+   *          Returns the primary toolbox for this window.
+   *
+   *        onEnter:
+   *          Called upon entering print preview.
+   *
+   *        onExit:
+   *          Called upon exiting print preview.
+   *
+   *        These methods must be defined. printPreview can be called
+   *        with aListenerObj as null iff this window is already displaying
+   *        print preview (in which case, the previous aListenerObj passed
+   *        to it will be used).
+   */
+  printPreview: function (aListenerObj)
   {
     if (this.bailOut()) {
       return;
     }
-    // if we're already in PP mode, don't set the callback; chances
+    // if we're already in PP mode, don't set the listener; chances
     // are it is null because someone is calling printPreview() to
     // get us to refresh the display.
-    if (!document.getElementById("print-preview-toolbar")) {
-      this._callback = aCallback;
-      this._sourceBrowser = aCallback.getSourceBrowser();
-      this._originalTitle = this._sourceBrowser.contentDocument.title;
+    if (!this.inPrintPreview) {
+      this._listener = aListenerObj;
+      this._sourceBrowser = aListenerObj.getSourceBrowser();
+      this._originalTitle = this._sourceBrowser.contentTitle;
       this._originalURL = this._sourceBrowser.currentURI.spec;
     } else {
       // collapse the browser here -- it will be shown in
       // enterPrintPreview; this forces a reflow which fixes display
       // issues in bug 267422.
-      this._sourceBrowser = this._callback.getPrintPreviewBrowser();
+      this._sourceBrowser = this._listener.getPrintPreviewBrowser();
       this._sourceBrowser.collapsed = true;
     }
 
     this._webProgressPP = {};
-    var ppParams        = {};
-    var notifyOnOpen    = {};
-    var webBrowserPrint = this.getWebBrowserPrint();
-    var printSettings   = this.getPrintSettings();
+    let ppParams        = {};
+    let notifyOnOpen    = {};
+    let printSettings   = this.getPrintSettings();
     // Here we get the PrintingPromptService so we can display the PP Progress from script
     // For the browser implemented via XUL with the PP toolbar we cannot let it be
     // automatically opened from the print engine because the XUL scrollbars in the PP window
     // will layout before the content window and a crash will occur.
     // Doing it all from script, means it lays out before hand and we can let printing do its own thing
-    var PPROMPTSVC = Components.classes["@mozilla.org/embedcomp/printingprompt-service;1"]
+    let PPROMPTSVC = Components.classes["@mozilla.org/embedcomp/printingprompt-service;1"]
                                .getService(Components.interfaces.nsIPrintingPromptService);
-    // just in case we are already printing, 
-    // an error code could be returned if the Prgress Dialog is already displayed
+    // just in case we are already printing,
+    // an error code could be returned if the Progress Dialog is already displayed
     try {
-      PPROMPTSVC.showProgress(window, webBrowserPrint, printSettings, this._obsPP, false,
+      PPROMPTSVC.showProgress(window, null, printSettings, this._obsPP, false,
                               this._webProgressPP, ppParams, notifyOnOpen);
       if (ppParams.value) {
         ppParams.value.docTitle = this._originalTitle;
         ppParams.value.docURL   = this._originalURL;
       }
 
-      // this tells us whether we should continue on with PP or 
+      // this tells us whether we should continue on with PP or
       // wait for the callback via the observer
-      if (!notifyOnOpen.value.valueOf() || this._webProgressPP.value == null)
+      if (!notifyOnOpen.value.valueOf() || this._webProgressPP.value == null) {
         this.enterPrintPreview();
+      }
     } catch (e) {
       this.enterPrintPreview();
     }
   },
 
+  /**
+   * Returns the nsIWebBrowserPrint associated with some content window.
+   * This method is being kept here for compatibility reasons, but should not
+   * be called by code hoping to support e10s / remote browsers.
+   *
+   * @param aWindow
+   *        The window from which to get the nsIWebBrowserPrint from.
+   * @return nsIWebBrowserPrint
+   */
   getWebBrowserPrint: function (aWindow)
   {
+    let Deprecated = Components.utils.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated;
+    let text = "getWebBrowserPrint is now deprecated, and fully unsupported for " +
+               "multi-process browsers. Please use a frame script to get " +
+               "access to nsIWebBrowserPrint from content.";
+    let url = "https://developer.mozilla.org/en-US/docs/Printing_from_a_XUL_App";
+    Deprecated.warning(text, url);
+
+    if (this.usingRemoteTabs) {
+      return {};
+    }
+
     var contentWindow = aWindow || window.content;
     return contentWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                         .getInterface(Components.interfaces.nsIWebBrowserPrint);
   },
 
+  /**
+   * Returns the nsIWebBrowserPrint from the print preview browser's docShell.
+   * This method is being kept here for compatibility reasons, but should not
+   * be called by code hoping to support e10s / remote browsers.
+   *
+   * @return nsIWebBrowserPrint
+   */
   getPrintPreview: function() {
-    return this._callback.getPrintPreviewBrowser().docShell.printPreview;
+    let Deprecated = Components.utils.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated;
+    let text = "getPrintPreview is now deprecated, and fully unsupported for " +
+               "multi-process browsers. Please use a frame script to get " +
+               "access to nsIWebBrowserPrint from content.";
+    let url = "https://developer.mozilla.org/en-US/docs/Printing_from_a_XUL_App";
+    Deprecated.warning(text, url);
+
+    if (this.usingRemoteTabs) {
+      return {};
+    }
+
+    return this._listener.getPrintPreviewBrowser().docShell.printPreview;
+  },
+
+  get inPrintPreview() {
+    return document.getElementById("print-preview-toolbar") != null;
   },
 
-  ////////////////////////////////////////
-  // "private" methods. Don't use them. //
-  ////////////////////////////////////////
+  ////////////////////////////////////////////////////
+  // "private" methods and members. Don't use them. //
+  ///////////////////////////////////////////////////
+
+  _listener: null,
+  _closeHandlerPP: null,
+  _webProgressPP: null,
+  _sourceBrowser: null,
+  _originalTitle: "",
+  _originalURL: "",
+
+  get usingRemoteTabs() {
+    // We memoize this, since it's highly unlikely to change over the lifetime
+    // of the window.
+    let usingRemoteTabs =
+      window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+            .getInterface(Components.interfaces.nsIWebNavigation)
+            .QueryInterface(Components.interfaces.nsILoadContext)
+            .useRemoteTabs;
+    delete this.usingRemoteTabs;
+    return this.usingRemoteTabs = usingRemoteTabs;
+  },
+
+  receiveMessage(aMessage) {
+    if (!this._webProgressPP.value) {
+      // We somehow didn't get a nsIWebProgressListener to be updated...
+      // I guess there's nothing to do.
+      return;
+    }
+
+    let listener = this._webProgressPP.value;
+    let mm = aMessage.target.messageManager;
+    let data = aMessage.data;
+
+    switch (aMessage.name) {
+      case "Printing:Preview:ProgressChange": {
+        return listener.onProgressChange(null, null,
+                                         data.curSelfProgress,
+                                         data.maxSelfProgress,
+                                         data.curTotalProgress,
+                                         data.maxTotalProgress);
+        break;
+      }
+
+      case "Printing:Preview:StateChange": {
+        if (data.stateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP) {
+          // Strangely, the printing engine sends 2 STATE_STOP messages when
+          // print preview is finishing. One has the STATE_IS_DOCUMENT flag,
+          // the other has the STATE_IS_NETWORK flag. However, the webProgressPP
+          // listener stops listening once the first STATE_STOP is sent.
+          // Any subsequent messages result in NS_ERROR_FAILURE errors getting
+          // thrown. This should all get torn out once bug 1088061 is fixed.
+          mm.removeMessageListener("Printing:Preview:StateChange", this);
+          mm.removeMessageListener("Printing:Preview:ProgressChange", this);
+        }
+
+        return listener.onStateChange(null, null,
+                                      data.stateFlags,
+                                      data.status);
+        break;
+      }
+    }
+  },
 
   setPrinterDefaultsForSelectedPrinter: function (aPSSVC, aPrintSettings)
   {
     if (!aPrintSettings.printerName)
       aPrintSettings.printerName = aPSSVC.defaultPrinterName;
 
     // First get any defaults from the printer 
     aPSSVC.initPrintSettingsFromPrinter(aPrintSettings.printerName, aPrintSettings);
@@ -173,138 +396,131 @@ var PrintUtils = {
         printSettings = PSSVC.newPrintSettings;
       }
     } catch (e) {
       dump("getPrintSettings: "+e+"\n");
     }
     return printSettings;
   },
 
-  _closeHandlerPP: null,
-  _webProgressPP: null,
-  _callback: null,
-  _sourceBrowser: null,
-  _originalTitle: "",
-  _originalURL: "",
-
   // This observer is called once the progress dialog has been "opened"
-  _obsPP: 
+  _obsPP:
   {
     observe: function(aSubject, aTopic, aData)
     {
       // delay the print preview to show the content of the progress dialog
       setTimeout(function () { PrintUtils.enterPrintPreview(); }, 0);
     },
 
     QueryInterface : function(iid)
     {
       if (iid.equals(Components.interfaces.nsIObserver) ||
           iid.equals(Components.interfaces.nsISupportsWeakReference) ||
           iid.equals(Components.interfaces.nsISupports))
-        return this;   
+        return this;
       throw Components.results.NS_NOINTERFACE;
     }
   },
 
   enterPrintPreview: function ()
   {
-    gFocusedElement = document.commandDispatcher.focusedElement;
-
-    var webBrowserPrint;
-    var printSettings  = this.getPrintSettings();
-    var originalWindow = this._sourceBrowser.contentWindow;
+    // Send a message to the print preview browser to initialize
+    // print preview. If we happen to have gotten a print preview
+    // progress listener from nsIPrintingPromptService.showProgress
+    // in printPreview, we add listeners to feed that progress
+    // listener.
+    let ppBrowser = this._listener.getPrintPreviewBrowser();
+    let mm = ppBrowser.messageManager;
+    let printSettings = this.getPrintSettings();
+    mm.sendAsyncMessage("Printing:Preview:Enter", null, {
+      printSettings: printSettings,
+      contentWindow: this._sourceBrowser.contentWindowAsCPOW,
+    });
 
-    try {
-      webBrowserPrint = this.getPrintPreview();
-      webBrowserPrint.printPreview(printSettings, originalWindow,
-                                   this._webProgressPP.value);
-    } catch (e) {
-      // Pressing cancel is expressed as an NS_ERROR_ABORT return value,
-      // causing an exception to be thrown which we catch here.
-      // Unfortunately this will also consume helpful failures, so add a
-      // dump(e); // if you need to debug
-
-      // Need to call enter and exit so that UI gets back to normal.
-      this._callback.onEnter();
-      this._callback.onExit();
-      return;
-    }
-
-    var printPreviewTB = document.getElementById("print-preview-toolbar");
-    if (printPreviewTB) {
-      printPreviewTB.updateToolbar();
-      var browser = this._callback.getPrintPreviewBrowser();
-      browser.collapsed = false;
-      browser.contentWindow.focus();
-      return;
+    if (this._webProgressPP.value) {
+      mm.addMessageListener("Printing:Preview:StateChange", this);
+      mm.addMessageListener("Printing:Preview:ProgressChange", this);
     }
 
-    // Set the original window as an active window so any mozPrintCallbacks can
-    // run without delayed setTimeouts.
-    var docShell = originalWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-                                 .getInterface(Components.interfaces.nsIWebNavigation)
-                                 .QueryInterface(Components.interfaces.nsIDocShell);
-    docShell.isActive = true;
+    let onEntered = (message) => {
+      mm.removeMessageListener("Printing:PrintPreview:Entered", onEntered);
+      // Stash the focused element so that we can return to it after exiting
+      // print preview.
+      gFocusedElement = document.commandDispatcher.focusedElement;
 
-    // show the toolbar after we go into print preview mode so
-    // that we can initialize the toolbar with total num pages
-    var XUL_NS =
-      "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-    printPreviewTB = document.createElementNS(XUL_NS, "toolbar");
-    printPreviewTB.setAttribute("printpreview", true);
-    printPreviewTB.id = "print-preview-toolbar";
-    printPreviewTB.className = "toolbar-primary";
+      let printPreviewTB = document.getElementById("print-preview-toolbar");
+      if (printPreviewTB) {
+        printPreviewTB.updateToolbar();
+        ppBrowser.collapsed = false;
+        ppBrowser.focus();
+        return;
+      }
+
+      // Set the original window as an active window so any mozPrintCallbacks can
+      // run without delayed setTimeouts.
+      this._sourceBrowser.docShellIsActive = true;
 
-    var navToolbox = this._callback.getNavToolbox();
-    navToolbox.parentNode.insertBefore(printPreviewTB, navToolbox);
+      // show the toolbar after we go into print preview mode so
+      // that we can initialize the toolbar with total num pages
+      const XUL_NS =
+        "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+      printPreviewTB = document.createElementNS(XUL_NS, "toolbar");
+      printPreviewTB.setAttribute("printpreview", true);
+      printPreviewTB.id = "print-preview-toolbar";
+      printPreviewTB.className = "toolbar-primary";
 
-    // copy the window close handler
-    if (document.documentElement.hasAttribute("onclose"))
-      this._closeHandlerPP = document.documentElement.getAttribute("onclose");
-    else
-      this._closeHandlerPP = null;
-    document.documentElement.setAttribute("onclose", "PrintUtils.exitPrintPreview(); return false;");
+      let navToolbox = this._listener.getNavToolbox();
+      navToolbox.parentNode.insertBefore(printPreviewTB, navToolbox);
+      printPreviewTB.initialize(ppBrowser);
 
-    // disable chrome shortcuts...
-    window.addEventListener("keydown", this.onKeyDownPP, true);
-    window.addEventListener("keypress", this.onKeyPressPP, true);
+      // copy the window close handler
+      if (document.documentElement.hasAttribute("onclose"))
+        this._closeHandlerPP = document.documentElement.getAttribute("onclose");
+      else
+        this._closeHandlerPP = null;
+      document.documentElement.setAttribute("onclose", "PrintUtils.exitPrintPreview(); return false;");
 
-    var browser = this._callback.getPrintPreviewBrowser();
-    browser.collapsed = false;
-    browser.contentWindow.focus();
+      // disable chrome shortcuts...
+      window.addEventListener("keydown", this.onKeyDownPP, true);
+      window.addEventListener("keypress", this.onKeyPressPP, true);
 
-    // on Enter PP Call back
-    this._callback.onEnter();
+      ppBrowser.collapsed = false;
+      ppBrowser.focus();
+      // on Enter PP Call back
+      this._listener.onEnter();
+    };
+
+    mm.addMessageListener("Printing:Preview:Entered", onEntered);
   },
 
   exitPrintPreview: function ()
   {
+    let ppBrowser = this._listener.getPrintPreviewBrowser();
+    let browserMM = ppBrowser.messageManager;
+    browserMM.sendAsyncMessage("Printing:Preview:Exit");
     window.removeEventListener("keydown", this.onKeyDownPP, true);
     window.removeEventListener("keypress", this.onKeyPressPP, true);
 
     // restore the old close handler
     document.documentElement.setAttribute("onclose", this._closeHandlerPP);
     this._closeHandlerPP = null;
 
-    var webBrowserPrint = this.getPrintPreview();
-    webBrowserPrint.exitPrintPreview();
+    // remove the print preview toolbar
+    let printPreviewTB = document.getElementById("print-preview-toolbar");
+    this._listener.getNavToolbox().parentNode.removeChild(printPreviewTB);
 
-    // remove the print preview toolbar
-    var printPreviewTB = document.getElementById("print-preview-toolbar");
-    this._callback.getNavToolbox().parentNode.removeChild(printPreviewTB);
-
-    var fm = Components.classes["@mozilla.org/focus-manager;1"]
+    let fm = Components.classes["@mozilla.org/focus-manager;1"]
                        .getService(Components.interfaces.nsIFocusManager);
     if (gFocusedElement)
       fm.setFocus(gFocusedElement, fm.FLAG_NOSCROLL);
     else
       window.content.focus();
     gFocusedElement = null;
 
-    this._callback.onExit();
+    this._listener.onExit();
   },
 
   onKeyDownPP: function (aEvent)
   {
     // Esc exits the PP
     if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
       PrintUtils.exitPrintPreview();
     }
@@ -324,17 +540,17 @@ var PrintUtils = {
         (aEvent.charCode == closeKey || aEvent.charCode == closeKey + 32)) {
       PrintUtils.exitPrintPreview();
     }
     else if (isModif) {
       var printPreviewTB = document.getElementById("print-preview-toolbar");
       var printKey = document.getElementById("printKb").getAttribute("key").toUpperCase();
       var pressedKey = String.fromCharCode(aEvent.charCode).toUpperCase();
       if (printKey == pressedKey) {
-	  PrintUtils.print();
+        printPreviewTB.print();
       }
     }
     // cancel shortkeys
     if (isModif) {
       aEvent.preventDefault();
       aEvent.stopPropagation();
     }
   }
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -1,18 +1,20 @@
 /* -*- 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/. */
 
 let Cc = Components.classes;
 let Ci = Components.interfaces;
 let Cu = Components.utils;
+let Cr = Components.results;
 
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 var global = this;
 
 let ClickEventHandler = {
   init: function init() {
     this._scrollable = null;
     this._scrolldir = "";
     this._startX = null;
@@ -348,8 +350,135 @@ let PopupBlocking = {
                      {blockedPopups: this.popupData, freshPopup: freshPopup});
   },
 };
 PopupBlocking.init();
 
 // Set up console.* for frame scripts.
 let Console = Components.utils.import("resource://gre/modules/devtools/Console.jsm", {});
 this.console = new Console.ConsoleAPI();
+
+let Printing = {
+  // Bug 1088061: nsPrintEngine's DoCommonPrint currently expects the
+  // progress listener passed to it to QI to an nsIPrintingPromptService
+  // in order to know that a printing progress dialog has been shown. That's
+  // really all the interface is used for, hence the fact that I don't actually
+  // implement the interface here. Bug 1088061 has been filed to remove
+  // this hackery.
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+                                         Ci.nsIPrintingPromptService]),
+
+  MESSAGES: [
+    "Printing:Preview:Enter",
+    "Printing:Preview:Exit",
+    "Printing:Preview:Navigate",
+    "Printing:Preview:UpdatePageCount",
+    "Printing:Print",
+  ],
+
+  init() {
+    this.MESSAGES.forEach(msgName => addMessageListener(msgName, this));
+  },
+
+  receiveMessage(message) {
+    let objects = message.objects;
+    let data = message.data;
+    switch(message.name) {
+      case "Printing:Preview:Enter": {
+        this.enterPrintPreview(objects.printSettings, objects.contentWindow);
+        break;
+      }
+
+      case "Printing:Preview:Exit": {
+        this.exitPrintPreview();
+        break;
+      }
+
+      case "Printing:Preview:Navigate": {
+        this.navigate(data.navType, data.pageNum);
+        break;
+      }
+
+      case "Printing:Preview:UpdatePageCount": {
+        this.updatePageCount();
+        break;
+      }
+
+      case "Printing:Print": {
+        this.print(objects.printSettings, objects.contentWindow);
+        break;
+      }
+    }
+  },
+
+  enterPrintPreview(printSettings, contentWindow) {
+    // Bug 1088070 - we should instantiate nsIPrintSettings here in the
+    // content script instead of passing it down as a CPOW.
+    if (Cu.isCrossProcessWrapper(printSettings)) {
+      printSettings = null;
+    }
+
+    // We have to wait for the print engine to finish reflowing all of the
+    // documents and subdocuments before we can tell the parent to flip to
+    // the print preview UI - otherwise, the print preview UI might ask for
+    // information (like the number of pages in the document) before we have
+    // our PresShells set up.
+    addEventListener("printPreviewUpdate", function onPrintPreviewReady() {
+      removeEventListener("printPreviewUpdate", onPrintPreviewReady);
+      sendAsyncMessage("Printing:Preview:Entered");
+    });
+
+    docShell.printPreview.printPreview(printSettings, contentWindow, this);
+  },
+
+  exitPrintPreview() {
+    docShell.printPreview.exitPrintPreview();
+  },
+
+  print(printSettings, contentWindow) {
+    // Bug 1088070 - we should instantiate nsIPrintSettings here in the
+    // content script instead of passing it down as a CPOW.
+    if (Cu.isCrossProcessWrapper(printSettings)) {
+      printSettings = null;
+    }
+
+    let print = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIWebBrowserPrint);
+    print.print(printSettings, null);
+  },
+
+  updatePageCount() {
+    let numPages = docShell.printPreview.printPreviewNumPages;
+    sendAsyncMessage("Printing:Preview:UpdatePageCount", {
+      numPages: numPages,
+    });
+  },
+
+  navigate(navType, pageNum) {
+    docShell.printPreview.printPreviewNavigate(navType, pageNum);
+  },
+
+  /* nsIWebProgressListener for print preview */
+
+  onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
+    sendAsyncMessage("Printing:Preview:StateChange", {
+      stateFlags: aStateFlags,
+      status: aStatus,
+    });
+  },
+
+  onProgressChange(aWebProgress, aRequest, aCurSelfProgress,
+                   aMaxSelfProgress, aCurTotalProgress,
+                   aMaxTotalProgress) {
+    sendAsyncMessage("Printing:Preview:ProgressChange", {
+      curSelfProgress: aCurSelfProgress,
+      maxSelfProgress: aMaxSelfProgress,
+      curTotalProgress: aCurTotalProgress,
+      maxTotalProgress: aMaxTotalProgress,
+    });
+  },
+
+  onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {},
+  onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {},
+  onSecurityChange(aWebProgress, aRequest, aState) {},
+}
+Printing.init();
+
--- a/toolkit/devtools/touch-events.js
+++ b/toolkit/devtools/touch-events.js
@@ -32,17 +32,17 @@ function TouchEventHandler (window) {
 
   let delay = 500;
   try {
     delay = Services.prefs.getIntPref('ui.click_hold_context_menus.delay');
   } catch(e) {}
 
   let TouchEventHandler = {
     enabled: false,
-    events: ['mousedown', 'mousemove', 'mouseup'],
+    events: ['mousedown', 'mousemove', 'mouseup', 'touchstart', 'touchend'],
     start: function teh_start() {
       if (this.enabled)
         return false;
       this.enabled = true;
       let isReloadNeeded = Services.prefs.getIntPref('dom.w3c_touch_events.enabled') != 1;
       Services.prefs.setIntPref('dom.w3c_touch_events.enabled', 1);
       this.events.forEach((function(evt) {
         // Only listen trusted events to prevent messing with
@@ -56,28 +56,53 @@ function TouchEventHandler (window) {
         return;
       this.enabled = false;
       Services.prefs.setIntPref('dom.w3c_touch_events.enabled', orig_w3c_touch_events);
       this.events.forEach((function(evt) {
         window.removeEventListener(evt, this, true);
       }).bind(this));
     },
     handleEvent: function teh_handleEvent(evt) {
+      // The gaia system window use an hybrid system even on the device which is
+      // a mix of mouse/touch events. So let's not cancel *all* mouse events
+      // if it is the current target.
+      let content = this.getContent(evt.target);
+      let isSystemWindow = content.location.toString().indexOf("system.gaiamobile.org") != -1;
+
+      // App touchstart & touchend should also be dispatched on the system app
+      // to match on-device behavior.
+      if (evt.type.startsWith('touch') && !isSystemWindow) {
+        let sysFrame = content.realFrameElement;
+        let sysDocument = sysFrame.ownerDocument;
+        let sysWindow = sysDocument.defaultView;
+
+        let touchEvent = sysDocument.createEvent('touchevent');
+        let touch = evt.touches[0] || evt.changedTouches[0];
+        let point = sysDocument.createTouch(sysWindow, sysFrame, 0,
+                                            touch.pageX, touch.pageY,
+                                            touch.screenX, touch.screenY,
+                                            touch.clientX, touch.clientY,
+                                            1, 1, 0, 0);
+
+        let touches = sysDocument.createTouchList(point);
+        let targetTouches = touches;
+        let changedTouches = touches;
+        touchEvent.initTouchEvent(evt.type, true, true, sysWindow, 0,
+                                  false, false, false, false,
+                                  touches, targetTouches, changedTouches);
+        sysFrame.dispatchEvent(touchEvent);
+        return;
+      }
+
       // Ignore all but real mouse event coming from physical mouse
       // (especially ignore mouse event being dispatched from a touch event)
       if (evt.button || evt.mozInputSource != Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE || evt.isSynthesized) {
         return;
       }
 
-      // The gaia system window use an hybrid system even on the device which is
-      // a mix of mouse/touch events. So let's not cancel *all* mouse events
-      // if it is the current target.
-      let content = this.getContent(evt.target);
-      let isSystemWindow = content.location.toString().indexOf("system.gaiamobile.org") != -1;
-
       let eventTarget = this.target;
       let type = '';
       switch (evt.type) {
         case 'mousedown':
           this.target = evt.target;
 
           contextMenuTimeout =
             this.sendContextMenu(evt.target, evt.pageX, evt.pageY, delay);
--- a/view/nsViewManager.cpp
+++ b/view/nsViewManager.cpp
@@ -1061,23 +1061,24 @@ nsViewManager::IsPainting(bool& aIsPaint
 void
 nsViewManager::ProcessPendingUpdates()
 {
   if (!IsRootVM()) {
     RootViewManager()->ProcessPendingUpdates();
     return;
   }
 
-  mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
-
   // Flush things like reflows by calling WillPaint on observer presShells.
   if (mPresShell) {
+    mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
+
     CallWillPaintOnObservers();
+
+    ProcessPendingUpdatesForView(mRootView, true);
   }
-  ProcessPendingUpdatesForView(mRootView, true);
 }
 
 void
 nsViewManager::UpdateWidgetGeometry()
 {
   if (!IsRootVM()) {
     RootViewManager()->UpdateWidgetGeometry();
     return;
--- a/widget/cocoa/nsPrintOptionsX.mm
+++ b/widget/cocoa/nsPrintOptionsX.mm
@@ -24,17 +24,17 @@ nsPrintOptionsX::ReadPrefs(nsIPrintSetti
   rv = nsPrintOptions::ReadPrefs(aPS, aPrinterName, aFlags);
   NS_ASSERTION(NS_SUCCEEDED(rv), "nsPrintOptions::ReadPrefs() failed");
   
   nsRefPtr<nsPrintSettingsX> printSettingsX(do_QueryObject(aPS));
   if (!printSettingsX)
     return NS_ERROR_NO_INTERFACE;
   rv = printSettingsX->ReadPageFormatFromPrefs();
   
-  return rv;
+  return NS_OK;
 }
 
 nsresult nsPrintOptionsX::_CreatePrintSettings(nsIPrintSettings **_retval)
 {
   nsresult rv;
   *_retval = nullptr;
 
   nsPrintSettingsX* printSettings = new nsPrintSettingsX; // does not initially ref count
--- a/widget/cocoa/nsWidgetFactory.mm
+++ b/widget/cocoa/nsWidgetFactory.mm
@@ -135,23 +135,20 @@ static const mozilla::Module::CIDEntry k
     mozilla::Module::MAIN_PROCESS_ONLY },
   { &kNS_CLIPBOARDHELPER_CID, false, NULL, nsClipboardHelperConstructor },
   { &kNS_DRAGSERVICE_CID, false, NULL, nsDragServiceConstructor,
     mozilla::Module::MAIN_PROCESS_ONLY },
   { &kNS_BIDIKEYBOARD_CID, false, NULL, nsBidiKeyboardConstructor },
   { &kNS_THEMERENDERER_CID, false, NULL, nsNativeThemeCocoaConstructor },
   { &kNS_SCREENMANAGER_CID, false, NULL, nsScreenManagerCocoaConstructor,
     mozilla::Module::MAIN_PROCESS_ONLY },
-  { &kNS_DEVICE_CONTEXT_SPEC_CID, false, NULL, nsDeviceContextSpecXConstructor,
-    mozilla::Module::MAIN_PROCESS_ONLY },
-  { &kNS_PRINTSESSION_CID, false, NULL, nsPrintSessionConstructor,
-    mozilla::Module::MAIN_PROCESS_ONLY },
+  { &kNS_DEVICE_CONTEXT_SPEC_CID, false, NULL, nsDeviceContextSpecXConstructor },
+  { &kNS_PRINTSESSION_CID, false, NULL, nsPrintSessionConstructor },
   { &kNS_PRINTSETTINGSSERVICE_CID, false, NULL, nsPrintOptionsXConstructor },
-  { &kNS_PRINTDIALOGSERVICE_CID, false, NULL, nsPrintDialogServiceXConstructor,
-    mozilla::Module::MAIN_PROCESS_ONLY },
+  { &kNS_PRINTDIALOGSERVICE_CID, false, NULL, nsPrintDialogServiceXConstructor },
   { &kNS_IDLE_SERVICE_CID, false, NULL, nsIdleServiceXConstructor },
   { &kNS_SYSTEMALERTSSERVICE_CID, false, NULL, OSXNotificationCenterConstructor },
   { &kNS_NATIVEMENUSERVICE_CID, false, NULL, nsNativeMenuServiceXConstructor },
   { &kNS_MACDOCKSUPPORT_CID, false, NULL, nsMacDockSupportConstructor },
   { &kNS_MACWEBAPPUTILS_CID, false, NULL, nsMacWebAppUtilsConstructor },
   { &kNS_STANDALONENATIVEMENU_CID, false, NULL, nsStandaloneNativeMenuConstructor },
   { &kNS_MACSYSTEMSTATUSBAR_CID, false, NULL, nsSystemStatusBarCocoaConstructor },
   { &kNS_GFXINFO_CID, false, NULL, mozilla::widget::GfxInfoConstructor },
@@ -175,23 +172,20 @@ static const mozilla::Module::ContractID
     mozilla::Module::MAIN_PROCESS_ONLY },
   { "@mozilla.org/widget/clipboardhelper;1", &kNS_CLIPBOARDHELPER_CID },
   { "@mozilla.org/widget/dragservice;1", &kNS_DRAGSERVICE_CID,
     mozilla::Module::MAIN_PROCESS_ONLY },
   { "@mozilla.org/widget/bidikeyboard;1", &kNS_BIDIKEYBOARD_CID },
   { "@mozilla.org/chrome/chrome-native-theme;1", &kNS_THEMERENDERER_CID },
   { "@mozilla.org/gfx/screenmanager;1", &kNS_SCREENMANAGER_CID,
     mozilla::Module::MAIN_PROCESS_ONLY },
-  { "@mozilla.org/gfx/devicecontextspec;1", &kNS_DEVICE_CONTEXT_SPEC_CID,
-    mozilla::Module::MAIN_PROCESS_ONLY },
-  { "@mozilla.org/gfx/printsession;1", &kNS_PRINTSESSION_CID,
-    mozilla::Module::MAIN_PROCESS_ONLY },
+  { "@mozilla.org/gfx/devicecontextspec;1", &kNS_DEVICE_CONTEXT_SPEC_CID },
+  { "@mozilla.org/gfx/printsession;1", &kNS_PRINTSESSION_CID },
   { "@mozilla.org/gfx/printsettings-service;1", &kNS_PRINTSETTINGSSERVICE_CID },
-  { NS_PRINTDIALOGSERVICE_CONTRACTID, &kNS_PRINTDIALOGSERVICE_CID,
-    mozilla::Module::MAIN_PROCESS_ONLY },
+  { NS_PRINTDIALOGSERVICE_CONTRACTID, &kNS_PRINTDIALOGSERVICE_CID },
   { "@mozilla.org/widget/idleservice;1", &kNS_IDLE_SERVICE_CID },
   { "@mozilla.org/system-alerts-service;1", &kNS_SYSTEMALERTSSERVICE_CID },
   { "@mozilla.org/widget/nativemenuservice;1", &kNS_NATIVEMENUSERVICE_CID },
   { "@mozilla.org/widget/macdocksupport;1", &kNS_MACDOCKSUPPORT_CID },
   { "@mozilla.org/widget/mac-web-app-utils;1", &kNS_MACWEBAPPUTILS_CID },
   { "@mozilla.org/widget/standalonenativemenu;1", &kNS_STANDALONENATIVEMENU_CID },
   { "@mozilla.org/widget/macsystemstatusbar;1", &kNS_MACSYSTEMSTATUSBAR_CID },
   { "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID },
--- a/widget/gtk/nsWidgetFactory.cpp
+++ b/widget/gtk/nsWidgetFactory.cpp
@@ -214,24 +214,20 @@ static const mozilla::Module::CIDEntry k
 #endif
     { &kNS_HTMLFORMATCONVERTER_CID, false, nullptr, nsHTMLFormatConverterConstructor },
     { &kNS_BIDIKEYBOARD_CID, false, nullptr, nsBidiKeyboardConstructor },
     { &kNS_SCREENMANAGER_CID, false, nullptr, nsScreenManagerGtkConstructor,
       Module::MAIN_PROCESS_ONLY },
     { &kNS_THEMERENDERER_CID, false, nullptr, nsNativeThemeGTKConstructor },
 #ifdef NS_PRINTING
     { &kNS_PRINTSETTINGSSERVICE_CID, false, nullptr, nsPrintOptionsGTKConstructor },
-    { &kNS_PRINTER_ENUMERATOR_CID, false, nullptr, nsPrinterEnumeratorGTKConstructor,
-      Module::MAIN_PROCESS_ONLY },
-    { &kNS_PRINTSESSION_CID, false, nullptr, nsPrintSessionConstructor,
-      Module::MAIN_PROCESS_ONLY },
-    { &kNS_DEVICE_CONTEXT_SPEC_CID, false, nullptr, nsDeviceContextSpecGTKConstructor,
-      Module::MAIN_PROCESS_ONLY },
-    { &kNS_PRINTDIALOGSERVICE_CID, false, nullptr, nsPrintDialogServiceGTKConstructor,
-      Module::MAIN_PROCESS_ONLY },
+    { &kNS_PRINTER_ENUMERATOR_CID, false, nullptr, nsPrinterEnumeratorGTKConstructor },
+    { &kNS_PRINTSESSION_CID, false, nullptr, nsPrintSessionConstructor },
+    { &kNS_DEVICE_CONTEXT_SPEC_CID, false, nullptr, nsDeviceContextSpecGTKConstructor },
+    { &kNS_PRINTDIALOGSERVICE_CID, false, nullptr, nsPrintDialogServiceGTKConstructor },
 #endif
     { &kNS_IMAGE_TO_PIXBUF_CID, false, nullptr, nsImageToPixbufConstructor },
 #if defined(MOZ_X11)
     { &kNS_IDLE_SERVICE_CID, false, nullptr, nsIdleServiceGTKConstructor },
     { &kNS_GFXINFO_CID, false, nullptr, mozilla::widget::GfxInfoConstructor },
 #endif
     { nullptr }
 };
@@ -251,24 +247,20 @@ static const mozilla::Module::ContractID
 #endif
     { "@mozilla.org/widget/htmlformatconverter;1", &kNS_HTMLFORMATCONVERTER_CID },
     { "@mozilla.org/widget/bidikeyboard;1", &kNS_BIDIKEYBOARD_CID },
     { "@mozilla.org/gfx/screenmanager;1", &kNS_SCREENMANAGER_CID,
       Module::MAIN_PROCESS_ONLY },
     { "@mozilla.org/chrome/chrome-native-theme;1", &kNS_THEMERENDERER_CID },
 #ifdef NS_PRINTING
     { "@mozilla.org/gfx/printsettings-service;1", &kNS_PRINTSETTINGSSERVICE_CID },
-    { "@mozilla.org/gfx/printerenumerator;1", &kNS_PRINTER_ENUMERATOR_CID,
-      Module::MAIN_PROCESS_ONLY },
-    { "@mozilla.org/gfx/printsession;1", &kNS_PRINTSESSION_CID,
-      Module::MAIN_PROCESS_ONLY },
-    { "@mozilla.org/gfx/devicecontextspec;1", &kNS_DEVICE_CONTEXT_SPEC_CID,
-      Module::MAIN_PROCESS_ONLY },
-    { NS_PRINTDIALOGSERVICE_CONTRACTID, &kNS_PRINTDIALOGSERVICE_CID,
-      Module::MAIN_PROCESS_ONLY },
+    { "@mozilla.org/gfx/printerenumerator;1", &kNS_PRINTER_ENUMERATOR_CID },
+    { "@mozilla.org/gfx/printsession;1", &kNS_PRINTSESSION_CID },
+    { "@mozilla.org/gfx/devicecontextspec;1", &kNS_DEVICE_CONTEXT_SPEC_CID },
+    { NS_PRINTDIALOGSERVICE_CONTRACTID, &kNS_PRINTDIALOGSERVICE_CID },
 #endif
     { "@mozilla.org/widget/image-to-gdk-pixbuf;1", &kNS_IMAGE_TO_PIXBUF_CID },
 #if defined(MOZ_X11)
     { "@mozilla.org/widget/idleservice;1", &kNS_IDLE_SERVICE_CID },
     { "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID },
 #endif
     { nullptr }
 };
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -97,16 +97,17 @@ EXPORTS += [
     'GfxInfoBase.h',
     'GfxInfoCollector.h',
     'InputData.h',
     'nsIDeviceContextSpec.h',
     'nsIPluginWidget.h',
     'nsIRollupListener.h',
     'nsIWidget.h',
     'nsIWidgetListener.h',
+    'nsPrintOptionsImpl.h',
     'nsWidgetInitData.h',
     'nsWidgetsCID.h',
 ]
 
 EXPORTS.mozilla += [
     'BasicEvents.h',
     'CommandList.h',
     'ContentEvents.h',
--- a/widget/nsIPrintOptions.idl
+++ b/widget/nsIPrintOptions.idl
@@ -3,33 +3,42 @@
  * 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/. */
 
 #include "nsISupports.idl"
 #include "nsIPrintSettings.idl"
 
 %{ C++
 struct nsFont;
+
+namespace mozilla {
+namespace embedding {
+  class PrintData;
+}
+}
 %}
 
 interface nsIStringEnumerator;
+interface nsIWebBrowserPrint;
 
 /**
  * Native types
  */
 [ref] native nsNativeFontRef(nsFont);
+[ref] native PrintDataRef(const mozilla::embedding::PrintData);
+[ptr] native PrintDataPtr(mozilla::embedding::PrintData);
 
 /**
  * Print options interface
  *
  * Do not attempt to freeze this API - it still needs lots of work. Consult
  * John Keiser <jkeiser@netscape.com> and Roland Mainz
  * <roland.mainz@informatik.med.uni-giessen.de> for futher details.
  */
-[scriptable, uuid(92597c2b-109b-40bb-8f93-9b9acfa31de8)]
+[scriptable, uuid(2ac74034-700e-40fd-8059-81d33223af58)]
 
 interface nsIPrintOptions : nsISupports
 {
   /**
    * Show Native Print Options dialog, this may not be supported on all platforms
    */
   void ShowPrintSetupDialog(in nsIPrintSettings aThePrintSettings);
 
@@ -52,16 +61,49 @@ interface nsIPrintOptions : nsISupports
                              aPrintSettings, out boolean aDisplayed);
 
   /**
    * Native data constants
    */
   const short kNativeDataPrintRecord        = 0;
 
   [noscript] voidPtr GetNativeData(in short aDataType);
+
+  /**
+   * Given some nsIPrintSettings and (optionally) an nsIWebBrowserPrint, populates
+   * a PrintData representing them which can be sent over IPC. Values are only
+   * ever read from aSettings and aWBP.
+   *
+   * @param aSettings
+   *        An nsIPrintSettings for a print job.
+   * @param aWBP (optional)
+   *        The nsIWebBrowserPrint for the print job.
+   * @param data
+   *        Pointer to a pre-existing PrintData to populate.
+   *
+   * @return nsresult
+   */
+  [noscript] void SerializeToPrintData(in nsIPrintSettings aPrintSettings,
+                                       in nsIWebBrowserPrint aWebBrowserPrint,
+                                       in PrintDataPtr data);
+
+  /**
+   * This function is the opposite of SerializeToPrintData, in that it takes
+   * a PrintData, and populates a pre-existing nsIPrintSettings with the data
+   * from PrintData.
+   *
+   * @param PrintData
+   *        Printing information sent through IPC.
+   * @param settings
+   *        A pre-existing nsIPrintSettings to populate with the PrintData.
+   *
+   * @return nsresult
+   */
+  [noscript] void DeserializeToPrintSettings(in PrintDataRef data,
+                                             in nsIPrintSettings aPrintSettings);
 };
 
 [scriptable, uuid(5e738fff-404c-4c94-9189-e8f2cce93e94)]
 
 interface nsIPrinterEnumerator : nsISupports
 {
   /**
    * The name of the system default printer. This name should also be
--- a/widget/nsPrintOptionsImpl.cpp
+++ b/widget/nsPrintOptionsImpl.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
 
+#include "mozilla/embedding/PPrinting.h"
 #include "nsPrintOptionsImpl.h"
 #include "nsReadableUtils.h"
 #include "nsPrintSettingsImpl.h"
 
 #include "nsIDOMWindow.h"
 #include "nsIServiceManager.h"
 #include "nsIDialogParamBlock.h"
 #include "nsXPCOM.h"
@@ -17,18 +18,20 @@
 #include "prprf.h"
 
 #include "nsIStringEnumerator.h"
 #include "nsISupportsPrimitives.h"
 #include "stdlib.h"
 #include "nsAutoPtr.h"
 #include "mozilla/Preferences.h"
 #include "nsPrintfCString.h"
+#include "nsIWebBrowserPrint.h"
 
 using namespace mozilla;
+using namespace mozilla::embedding;
 
 NS_IMPL_ISUPPORTS(nsPrintOptions, nsIPrintOptions, nsIPrintSettingsService)
 
 // Pref Constants
 static const char kMarginTop[]       = "print_margin_top";
 static const char kMarginLeft[]      = "print_margin_left";
 static const char kMarginBottom[]    = "print_margin_bottom";
 static const char kMarginRight[]     = "print_margin_right";
@@ -93,16 +96,231 @@ nsPrintOptions::~nsPrintOptions()
 
 nsresult
 nsPrintOptions::Init()
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsPrintOptions::SerializeToPrintData(nsIPrintSettings* aSettings,
+                                     nsIWebBrowserPrint* aWBP,
+                                     PrintData* data)
+{
+  aSettings->GetStartPageRange(&data->startPageRange());
+  aSettings->GetEndPageRange(&data->endPageRange());
+
+  aSettings->GetEdgeTop(&data->edgeTop());
+  aSettings->GetEdgeLeft(&data->edgeLeft());
+  aSettings->GetEdgeBottom(&data->edgeBottom());
+  aSettings->GetEdgeRight(&data->edgeRight());
+
+  aSettings->GetMarginTop(&data->marginTop());
+  aSettings->GetMarginLeft(&data->marginLeft());
+  aSettings->GetMarginBottom(&data->marginBottom());
+  aSettings->GetMarginRight(&data->marginRight());
+  aSettings->GetUnwriteableMarginTop(&data->unwriteableMarginTop());
+  aSettings->GetUnwriteableMarginLeft(&data->unwriteableMarginLeft());
+  aSettings->GetUnwriteableMarginBottom(&data->unwriteableMarginBottom());
+  aSettings->GetUnwriteableMarginRight(&data->unwriteableMarginRight());
+
+  aSettings->GetScaling(&data->scaling());
+
+  aSettings->GetPrintBGColors(&data->printBGColors());
+  aSettings->GetPrintBGImages(&data->printBGImages());
+  aSettings->GetPrintRange(&data->printRange());
+
+  // I have no idea if I'm doing this string copying correctly...
+  nsXPIDLString title;
+  aSettings->GetTitle(getter_Copies(title));
+  data->title() = title;
+
+  nsXPIDLString docURL;
+  aSettings->GetDocURL(getter_Copies(docURL));
+  data->docURL() = docURL;
+
+  // Header strings...
+  nsXPIDLString headerStrLeft;
+  aSettings->GetHeaderStrLeft(getter_Copies(headerStrLeft));
+  data->headerStrLeft() = headerStrLeft;
+
+  nsXPIDLString headerStrCenter;
+  aSettings->GetHeaderStrCenter(getter_Copies(headerStrCenter));
+  data->headerStrCenter() = headerStrCenter;
+
+  nsXPIDLString headerStrRight;
+  aSettings->GetHeaderStrRight(getter_Copies(headerStrRight));
+  data->headerStrRight() = headerStrRight;
+
+  // Footer strings...
+  nsXPIDLString footerStrLeft;
+  aSettings->GetFooterStrLeft(getter_Copies(footerStrLeft));
+  data->footerStrLeft() = footerStrLeft;
+
+  nsXPIDLString footerStrCenter;
+  aSettings->GetFooterStrCenter(getter_Copies(footerStrCenter));
+  data->footerStrCenter() = footerStrCenter;
+
+  nsXPIDLString footerStrRight;
+  aSettings->GetFooterStrRight(getter_Copies(footerStrRight));
+  data->footerStrRight() = footerStrRight;
+
+  aSettings->GetHowToEnableFrameUI(&data->howToEnableFrameUI());
+  aSettings->GetIsCancelled(&data->isCancelled());
+  aSettings->GetPrintFrameTypeUsage(&data->printFrameTypeUsage());
+  aSettings->GetPrintFrameType(&data->printFrameType());
+  aSettings->GetPrintSilent(&data->printSilent());
+  aSettings->GetShrinkToFit(&data->shrinkToFit());
+  aSettings->GetShowPrintProgress(&data->showPrintProgress());
+
+  nsXPIDLString paperName;
+  aSettings->GetPaperName(getter_Copies(paperName));
+  data->paperName() = paperName;
+
+  aSettings->GetPaperSizeType(&data->paperSizeType());
+  aSettings->GetPaperData(&data->paperData());
+  aSettings->GetPaperWidth(&data->paperWidth());
+  aSettings->GetPaperHeight(&data->paperHeight());
+  aSettings->GetPaperSizeUnit(&data->paperSizeUnit());
+
+  nsXPIDLString plexName;
+  aSettings->GetPlexName(getter_Copies(plexName));
+  data->plexName() = plexName;
+
+  nsXPIDLString colorspace;
+  aSettings->GetColorspace(getter_Copies(colorspace));
+  data->colorspace() = colorspace;
+
+  nsXPIDLString resolutionName;
+  aSettings->GetResolutionName(getter_Copies(resolutionName));
+  data->resolutionName() = resolutionName;
+
+  aSettings->GetDownloadFonts(&data->downloadFonts());
+  aSettings->GetPrintReversed(&data->printReversed());
+  aSettings->GetPrintInColor(&data->printInColor());
+  aSettings->GetOrientation(&data->orientation());
+
+  nsXPIDLString printCommand;
+  aSettings->GetPrintCommand(getter_Copies(printCommand));
+  data->printCommand() = printCommand;
+
+  aSettings->GetNumCopies(&data->numCopies());
+
+  nsXPIDLString printerName;
+  aSettings->GetPrinterName(getter_Copies(printerName));
+  data->printerName() = printerName;
+
+  aSettings->GetPrintToFile(&data->printToFile());
+
+  nsXPIDLString toFileName;
+  aSettings->GetToFileName(getter_Copies(toFileName));
+  data->toFileName() = toFileName;
+
+  aSettings->GetOutputFormat(&data->outputFormat());
+  aSettings->GetPrintPageDelay(&data->printPageDelay());
+  aSettings->GetResolution(&data->resolution());
+  aSettings->GetDuplex(&data->duplex());
+  aSettings->GetIsInitializedFromPrinter(&data->isInitializedFromPrinter());
+  aSettings->GetIsInitializedFromPrefs(&data->isInitializedFromPrefs());
+  aSettings->GetPersistMarginBoxSettings(&data->persistMarginBoxSettings());
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrintOptions::DeserializeToPrintSettings(const PrintData& data,
+                                           nsIPrintSettings* settings)
+{
+  settings->SetStartPageRange(data.startPageRange());
+  settings->SetEndPageRange(data.endPageRange());
+
+  settings->SetEdgeTop(data.edgeTop());
+  settings->SetEdgeLeft(data.edgeLeft());
+  settings->SetEdgeBottom(data.edgeBottom());
+  settings->SetEdgeRight(data.edgeRight());
+
+  settings->SetMarginTop(data.marginTop());
+  settings->SetMarginLeft(data.marginLeft());
+  settings->SetMarginBottom(data.marginBottom());
+  settings->SetMarginRight(data.marginRight());
+  settings->SetUnwriteableMarginTop(data.unwriteableMarginTop());
+  settings->SetUnwriteableMarginLeft(data.unwriteableMarginLeft());
+  settings->SetUnwriteableMarginBottom(data.unwriteableMarginBottom());
+  settings->SetUnwriteableMarginRight(data.unwriteableMarginRight());
+
+  settings->SetScaling(data.scaling());
+
+  settings->SetPrintBGColors(data.printBGColors());
+  settings->SetPrintBGImages(data.printBGImages());
+  settings->SetPrintRange(data.printRange());
+
+  // I have no idea if I'm doing this string copying correctly...
+  settings->SetTitle(data.title().get());
+  settings->SetDocURL(data.docURL().get());
+
+  // Header strings...
+  settings->SetHeaderStrLeft(data.headerStrLeft().get());
+  settings->SetHeaderStrCenter(data.headerStrCenter().get());
+  settings->SetHeaderStrRight(data.headerStrRight().get());
+
+  // Footer strings...
+  settings->SetFooterStrLeft(data.footerStrLeft().get());
+  settings->SetFooterStrCenter(data.footerStrCenter().get());
+  settings->SetFooterStrRight(data.footerStrRight().get());
+
+  settings->SetHowToEnableFrameUI(data.howToEnableFrameUI());
+  settings->SetIsCancelled(data.isCancelled());
+  settings->SetPrintFrameTypeUsage(data.printFrameTypeUsage());
+  settings->SetPrintFrameType(data.printFrameType());
+  settings->SetPrintSilent(data.printSilent());
+  settings->SetShrinkToFit(data.shrinkToFit());
+  settings->SetShowPrintProgress(data.showPrintProgress());
+
+  settings->SetPaperName(data.paperName().get());
+
+  settings->SetPaperSizeType(data.paperSizeType());
+  settings->SetPaperData(data.paperData());
+  settings->SetPaperWidth(data.paperWidth());
+  settings->SetPaperHeight(data.paperHeight());
+  settings->SetPaperSizeUnit(data.paperSizeUnit());
+
+  settings->SetPlexName(data.plexName().get());
+
+  settings->SetColorspace(data.colorspace().get());
+
+  settings->SetResolutionName(data.resolutionName().get());
+
+  settings->SetDownloadFonts(data.downloadFonts());
+  settings->SetPrintReversed(data.printReversed());
+  settings->SetPrintInColor(data.printInColor());
+  settings->SetOrientation(data.orientation());
+
+  settings->SetPrintCommand(data.printCommand().get());
+
+  settings->SetNumCopies(data.numCopies());
+
+  settings->SetPrinterName(data.printerName().get());
+
+  settings->SetPrintToFile(data.printToFile());
+
+  settings->SetToFileName(data.toFileName().get());
+
+  settings->SetOutputFormat(data.outputFormat());
+  settings->SetPrintPageDelay(data.printPageDelay());
+  settings->SetResolution(data.resolution());
+  settings->SetDuplex(data.duplex());
+  settings->SetIsInitializedFromPrinter(data.isInitializedFromPrinter());
+  settings->SetIsInitializedFromPrefs(data.isInitializedFromPrefs());
+  settings->SetPersistMarginBoxSettings(data.persistMarginBoxSettings());
+
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
 nsPrintOptions::ShowPrintSetupDialog(nsIPrintSettings *aPS)
 {
   NS_ENSURE_ARG_POINTER(aPS);
   nsresult rv;
 
   // create a nsISupportsArray of the parameters
   // being passed to the window
   nsCOMPtr<nsISupportsArray> array;
--- a/widget/nsPrintOptionsImpl.h
+++ b/widget/nsPrintOptionsImpl.h
@@ -2,22 +2,26 @@
  *
  * 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/. */
 
 #ifndef nsPrintOptionsImpl_h__
 #define nsPrintOptionsImpl_h__
 
+#include "mozilla/embedding/PPrinting.h"
 #include "nsCOMPtr.h"
 #include "nsIPrintOptions.h"
 #include "nsIPrintSettingsService.h"
 #include "nsString.h"
 #include "nsFont.h"
 
+class nsIPrintSettings;
+class nsIWebBrowserPrint;
+
 /**
  *   Class nsPrintOptions
  */
 class nsPrintOptions : public nsIPrintOptions,
                        public nsIPrintSettingsService
 {
 public:
   NS_DECL_ISUPPORTS
--- a/widget/windows/nsPrintOptionsWin.cpp
+++ b/widget/windows/nsPrintOptionsWin.cpp
@@ -1,20 +1,25 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
 #include "nsCOMPtr.h"
 #include "nsPrintOptionsWin.h"
 #include "nsPrintSettingsWin.h"
+#include "nsPrintDialogUtil.h"
 
 #include "nsGfxCIID.h"
 #include "nsIServiceManager.h"
+#include "nsIWebBrowserPrint.h"
+
 const char kPrinterEnumeratorContractID[] = "@mozilla.org/gfx/printerenumerator;1";
 
+using namespace mozilla::embedding;
+
 /** ---------------------------------------------------
  *  See documentation in nsPrintOptionsWin.h
  *	@update 6/21/00 dwc
  */
 nsPrintOptionsWin::nsPrintOptionsWin()
 {
 
 }
@@ -22,16 +27,81 @@ nsPrintOptionsWin::nsPrintOptionsWin()
 /** ---------------------------------------------------
  *  See documentation in nsPrintOptionsImpl.h
  *	@update 6/21/00 dwc
  */
 nsPrintOptionsWin::~nsPrintOptionsWin()
 {
 }
 
+NS_IMETHODIMP
+nsPrintOptionsWin::SerializeToPrintData(nsIPrintSettings* aSettings,
+                                        nsIWebBrowserPrint* aWBP,
+                                        PrintData* data)
+{
+  nsresult rv = nsPrintOptions::SerializeToPrintData(aSettings, aWBP, data);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Windows wants this information for its print dialogs
+  if (aWBP) {
+    aWBP->GetIsFramesetDocument(&data->isFramesetDocument());
+    aWBP->GetIsFramesetFrameSelected(&data->isFramesetFrameSelected());
+    aWBP->GetIsIFrameSelected(&data->isIFrameSelected());
+    aWBP->GetIsRangeSelection(&data->isRangeSelection());
+  }
+
+  nsCOMPtr<nsIPrintSettingsWin> psWin = do_QueryInterface(aSettings);
+  if (!psWin) {
+    return NS_ERROR_FAILURE;
+  }
+
+  char16_t* deviceName;
+  char16_t* driverName;
+
+  psWin->GetDeviceName(&deviceName);
+  psWin->GetDriverName(&driverName);
+
+  data->deviceName().Assign(deviceName);
+  data->driverName().Assign(driverName);
+
+  free(deviceName);
+  free(driverName);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrintOptionsWin::DeserializeToPrintSettings(const PrintData& data,
+                                              nsIPrintSettings* settings)
+{
+  nsresult rv = nsPrintOptions::DeserializeToPrintSettings(data, settings);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIPrintSettingsWin> psWin = do_QueryInterface(settings);
+  if (!settings) {
+    return NS_ERROR_FAILURE;
+  }
+
+  psWin->SetDeviceName(data.deviceName().get());
+  psWin->SetDriverName(data.driverName().get());
+
+  // We also need to prepare a DevMode and stuff it into our newly
+  // created nsIPrintSettings...
+  nsXPIDLString printerName;
+  settings->GetPrinterName(getter_Copies(printerName));
+  HGLOBAL gDevMode = CreateGlobalDevModeAndInit(printerName, settings);
+  LPDEVMODEW devMode = (LPDEVMODEW)::GlobalLock(gDevMode);
+  psWin->SetDevMode(devMode);
+
+  ::GlobalUnlock(gDevMode);
+  ::GlobalFree(gDevMode);
+
+  return NS_OK;
+}
+
 /* nsIPrintSettings CreatePrintSettings (); */
 nsresult nsPrintOptionsWin::_CreatePrintSettings(nsIPrintSettings **_retval)
 {
   *_retval = nullptr;
   nsPrintSettingsWin* printSettings = new nsPrintSettingsWin(); // does not initially ref count
   NS_ENSURE_TRUE(printSettings, NS_ERROR_OUT_OF_MEMORY);
 
   NS_ADDREF(*_retval = printSettings); // ref count
--- a/widget/windows/nsPrintOptionsWin.h
+++ b/widget/windows/nsPrintOptionsWin.h
@@ -2,26 +2,35 @@
  *
  * 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/. */
 
 #ifndef nsPrintOptionsWin_h__
 #define nsPrintOptionsWin_h__
 
+#include "mozilla/embedding/PPrinting.h"
 #include "nsPrintOptionsImpl.h"  
 
+class nsIPrintSettings;
+class nsIWebBrowserPrint;
 
 //*****************************************************************************
 //***    nsPrintOptions
 //*****************************************************************************
 class nsPrintOptionsWin : public nsPrintOptions
 {
 public:
   nsPrintOptionsWin();
   virtual ~nsPrintOptionsWin();
 
+  NS_IMETHODIMP SerializeToPrintData(nsIPrintSettings* aSettings,
+                                     nsIWebBrowserPrint* aWBP,
+                                     mozilla::embedding::PrintData* data);
+  NS_IMETHODIMP DeserializeToPrintSettings(const mozilla::embedding::PrintData& data,
+                                           nsIPrintSettings* settings);
+
   virtual nsresult _CreatePrintSettings(nsIPrintSettings **_retval);
 };
 
 
 
 #endif /* nsPrintOptions_h__ */
--- a/widget/windows/nsWidgetFactory.cpp
+++ b/widget/windows/nsWidgetFactory.cpp
@@ -239,22 +239,19 @@ static const mozilla::Module::CIDEntry k
   { &kNS_WIN_JUMPLISTSHORTCUT_CID, false, nullptr, JumpListShortcutConstructor },
   { &kNS_DRAGSERVICE_CID, false, nullptr, nsDragServiceConstructor, Module::MAIN_PROCESS_ONLY },
   { &kNS_BIDIKEYBOARD_CID, false, nullptr, nsBidiKeyboardConstructor },
 #ifdef MOZ_METRO
   { &kNS_WIN_METROUTILS_CID, false, nullptr, nsWinMetroUtilsConstructor },
 #endif
 #ifdef NS_PRINTING
   { &kNS_PRINTSETTINGSSERVICE_CID, false, nullptr, nsPrintOptionsWinConstructor },
-  { &kNS_PRINTER_ENUMERATOR_CID, false, nullptr, nsPrinterEnumeratorWinConstructor,
-    Module::MAIN_PROCESS_ONLY },
-  { &kNS_PRINTSESSION_CID, false, nullptr, nsPrintSessionConstructor,
-    Module::MAIN_PROCESS_ONLY },
-  { &kNS_DEVICE_CONTEXT_SPEC_CID, false, nullptr, nsDeviceContextSpecWinConstructor,
-    Module::MAIN_PROCESS_ONLY },
+  { &kNS_PRINTER_ENUMERATOR_CID, false, nullptr, nsPrinterEnumeratorWinConstructor },
+  { &kNS_PRINTSESSION_CID, false, nullptr, nsPrintSessionConstructor },
+  { &kNS_DEVICE_CONTEXT_SPEC_CID, false, nullptr, nsDeviceContextSpecWinConstructor },
 #endif
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
   { "@mozilla.org/widgets/window/win;1", &kNS_WINDOW_CID },
   { "@mozilla.org/widgets/child_window/win;1", &kNS_CHILD_CID },
   { "@mozilla.org/filepicker;1", &kNS_FILEPICKER_CID, Module::MAIN_PROCESS_ONLY },
@@ -277,22 +274,19 @@ static const mozilla::Module::ContractID
   { "@mozilla.org/windows-jumplistshortcut;1", &kNS_WIN_JUMPLISTSHORTCUT_CID },
   { "@mozilla.org/widget/dragservice;1", &kNS_DRAGSERVICE_CID, Module::MAIN_PROCESS_ONLY },
   { "@mozilla.org/widget/bidikeyboard;1", &kNS_BIDIKEYBOARD_CID },
 #ifdef MOZ_METRO
   { "@mozilla.org/windows-metroutils;1", &kNS_WIN_METROUTILS_CID },
 #endif
 #ifdef NS_PRINTING
   { "@mozilla.org/gfx/printsettings-service;1", &kNS_PRINTSETTINGSSERVICE_CID },
-  { "@mozilla.org/gfx/printerenumerator;1", &kNS_PRINTER_ENUMERATOR_CID,
-    Module::MAIN_PROCESS_ONLY },
-  { "@mozilla.org/gfx/printsession;1", &kNS_PRINTSESSION_CID,
-    Module::MAIN_PROCESS_ONLY },
-  { "@mozilla.org/gfx/devicecontextspec;1", &kNS_DEVICE_CONTEXT_SPEC_CID,
-    Module::MAIN_PROCESS_ONLY },
+  { "@mozilla.org/gfx/printerenumerator;1", &kNS_PRINTER_ENUMERATOR_CID },
+  { "@mozilla.org/gfx/printsession;1", &kNS_PRINTSESSION_CID },
+  { "@mozilla.org/gfx/devicecontextspec;1", &kNS_DEVICE_CONTEXT_SPEC_CID },
 #endif
   { nullptr }
 };
 
 static void
 nsWidgetWindowsModuleDtor()
 {
   KeyboardLayout::Shutdown();