author | Wes Kocher <wkocher@mozilla.com> |
Tue, 28 Oct 2014 19:02:36 -0700 | |
changeset 212789 | 7e3c85754d32544625f785703d98e0e7d0ee4daf |
parent 212774 | e7e1418113ccc4b02b71fcdcb223c6dd16464929 (current diff) |
parent 212788 | 99d578d3c8a1ca10e16f84067daa5d99d5b7c8f5 (diff) |
child 212790 | 366d06412304c16c2c8cdafed6afecb5576c92cd |
child 212805 | 6bc7eedcee7f1b1255193808186d2c23597cb826 |
child 212840 | 35870244d187f49ad5c50cb8cf08caf27df432e6 |
child 212847 | d2e06eb128bdb262747420f09db0b002cd4ba019 |
push id | 27728 |
push user | kwierso@gmail.com |
push date | Wed, 29 Oct 2014 02:08:04 +0000 |
treeherder | mozilla-central@7e3c85754d32 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 36.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
36.0a1
/
20141029030207
/
pushlog to previous
nightly linux64
36.0a1
/
20141029030207
/
pushlog to previous
nightly mac
36.0a1
/
20141029030207
/
pushlog to previous
nightly win32
36.0a1
/
20141029030207
/
pushlog to previous
nightly win64
36.0a1
/
20141029030207
/
pushlog to previous
|
--- 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();