Bug 1269300 Patch 5;r?mixedpuppy draft
authordw-dev <dw-dev@gmx.com>
Thu, 13 Jul 2017 18:34:29 +0100
changeset 608447 1e8c38f439f11d3df8d82042a8094ce3cf2b843d
parent 607967 30ea2905130e85f9e1d8d56fa3097901eec6514b
child 637299 09ca216225d853a5a66fc9fef0aae344d81da9f6
push id68272
push userbmo:dw-dev@gmx.com
push dateThu, 13 Jul 2017 17:36:16 +0000
reviewersmixedpuppy
bugs1269300
milestone56.0a1
Bug 1269300 Patch 5;r?mixedpuppy MozReview-Commit-ID: ByikIYU4ASF
browser/components/extensions/ext-tabs.js
browser/components/extensions/schemas/tabs.json
browser/components/extensions/test/browser/browser-common.ini
browser/components/extensions/test/browser/browser_ext_tabs_printPreview.js
browser/components/extensions/test/mochitest/test_ext_all_apis.html
toolkit/locales/en-US/chrome/global/extensions.properties
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -1,15 +1,20 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 // The ext-* files are imported into the same scopes.
 /* import-globals-from ext-utils.js */
 
+XPCOMUtils.defineLazyGetter(this, "strBundle", function() {
+  const stringSvc = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
+  return stringSvc.createBundle("chrome://global/locale/extensions.properties");
+});
+
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
                                   "resource://gre/modules/PromiseUtils.jsm");
@@ -746,13 +751,149 @@ this.tabs = class extends ExtensionAPI {
           return () => {
             tabTracker.off("tab-attached", tabCreated);
             tabTracker.off("tab-created", tabCreated);
 
             windowTracker.removeListener("FullZoomChange", zoomListener);
             windowTracker.removeListener("TextZoomChange", zoomListener);
           };
         }).api(),
+
+        print() {
+          let activeTab = getTabOrActive(null);
+          let {PrintUtils} = activeTab.ownerGlobal;
+
+          PrintUtils.printWindow(activeTab.linkedBrowser.outerWindowID, activeTab.linkedBrowser);
+        },
+
+        printPreview() {
+          let activeTab = getTabOrActive(null);
+          let {
+            PrintUtils,
+            PrintPreviewListener,
+          } = activeTab.ownerGlobal;
+
+          return new Promise(resolve => {
+            let ppBrowser = PrintUtils._shouldSimplify ?
+              PrintPreviewListener.getSimplifiedPrintPreviewBrowser() :
+              PrintPreviewListener.getPrintPreviewBrowser();
+
+            let mm = ppBrowser.messageManager;
+
+            let onEntered = (message) => {
+              mm.removeMessageListener("Printing:Preview:Entered", onEntered);
+              if (message.data.failed) {
+                throw new ExtensionError("Print preview failed");
+              }
+              resolve();
+            };
+
+            mm.addMessageListener("Printing:Preview:Entered", onEntered);
+
+            PrintUtils.printPreview(PrintPreviewListener);
+          });
+        },
+
+        saveAsPDF(pageSettings) {
+          let activeTab = getTabOrActive(null);
+          let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+          let title = strBundle.GetStringFromName("saveaspdf.saveasdialog.title");
+
+          picker.init(activeTab.ownerGlobal, title, Ci.nsIFilePicker.modeSave);
+          picker.appendFilter("PDF", "*.pdf");
+          picker.defaultExtension = "pdf";
+          picker.defaultString = activeTab.linkedBrowser.contentTitle + ".pdf";
+
+          return new Promise(resolve => {
+            picker.open(function(retval) {
+              if (retval == 0 || retval == 2) {
+                // OK clicked (retval == 0) or replace confirmed (retval == 2)
+                try {
+                  let fstream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
+                  fstream.init(picker.file, 0x2A, 0x1B6, 0);  // write|create|truncate, file permissions rw-rw-rw- = 0666 = 0x1B6
+                  fstream.close();  // unlock file
+                } catch (e) {
+                  resolve(retval == 0 ? "Not saved" : "Not replaced");
+                  return;
+                }
+
+                let psService = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(Ci.nsIPrintSettingsService);
+                let printSettings = psService.newPrintSettings;
+
+                printSettings.printToFile = true;
+                printSettings.toFileName = picker.file.path;
+
+                printSettings.printSilent = true;
+                printSettings.showPrintProgress = false;
+
+                printSettings.printFrameType = Ci.nsIPrintSettings.kFramesAsIs;
+                printSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
+
+                if (pageSettings.orientation !== null) {
+                  printSettings.orientation = pageSettings.orientation;
+                }
+                if (pageSettings.scaling !== null) {
+                  printSettings.scaling = pageSettings.scaling;
+                }
+                if (pageSettings.shrinkToFit !== null) {
+                  printSettings.shrinkToFit = pageSettings.shrinkToFit;
+                }
+                if (pageSettings.showBackgroundColors !== null) {
+                  printSettings.printBGColors = pageSettings.showBackgroundColors;
+                }
+                if (pageSettings.showBackgroundImages !== null) {
+                  printSettings.printBGImages = pageSettings.showBackgroundImages;
+                }
+                if (pageSettings.paperSizeUnit !== null) {
+                  printSettings.paperSizeUnit = pageSettings.paperSizeUnit;
+                }
+                if (pageSettings.paperWidth !== null) {
+                  printSettings.paperWidth = pageSettings.paperWidth;
+                }
+                if (pageSettings.paperHeight !== null) {
+                  printSettings.paperHeight = pageSettings.paperHeight;
+                }
+                if (pageSettings.headerLeft !== null) {
+                  printSettings.headerStrLeft = pageSettings.headerLeft;
+                }
+                if (pageSettings.headerCenter !== null) {
+                  printSettings.headerStrCenter = pageSettings.headerCenter;
+                }
+                if (pageSettings.headerRight !== null) {
+                  printSettings.headerStrRight = pageSettings.headerRight;
+                }
+                if (pageSettings.footerLeft !== null) {
+                  printSettings.footerStrLeft = pageSettings.footerLeft;
+                }
+                if (pageSettings.footerCenter !== null) {
+                  printSettings.footerStrCenter = pageSettings.footerCenter;
+                }
+                if (pageSettings.footerRight !== null) {
+                  printSettings.footerStrRight = pageSettings.footerRight;
+                }
+                if (pageSettings.marginLeft !== null) {
+                  printSettings.marginLeft = pageSettings.marginLeft;
+                }
+                if (pageSettings.marginRight !== null) {
+                  printSettings.marginRight = pageSettings.marginRight;
+                }
+                if (pageSettings.marginTop !== null) {
+                  printSettings.marginTop = pageSettings.marginTop;
+                }
+                if (pageSettings.marginBottom !== null) {
+                  printSettings.marginBottom = pageSettings.marginBottom;
+                }
+
+                activeTab.linkedBrowser.print(activeTab.linkedBrowser.outerWindowID, printSettings, null);
+
+                resolve(retval == 0 ? "Saved" : "Replaced");
+              } else {
+                // Cancel clicked (retval == 1)
+                resolve("Cancelled");
+              }
+            });
+          });
+        },
       },
     };
     return self;
   }
 };
--- a/browser/components/extensions/schemas/tabs.json
+++ b/browser/components/extensions/schemas/tabs.json
@@ -129,16 +129,113 @@
           "defaultZoomFactor": {
             "type": "number",
             "optional": true,
             "description": "Used to return the default zoom level for the current tab in calls to tabs.getZoomSettings."
           }
         }
       },
       {
+        "id": "PageSettings",
+        "type": "object",
+        "description": "The page settings including: orientation, scale, background, margins, headers, footers.",
+        "properties": {
+          "orientation": {
+            "type": "integer",
+            "optional": true,
+            "description": "The page content orientation: 0 = portrait, 1 = landscape. Default: 0."
+          },
+          "scaling": {
+            "type": "number",
+            "optional": true,
+            "description": "The page content scaling factor: 1.0 = 100% = normal size. Default: 1.0."
+          },
+          "shrinkToFit": {
+            "type": "boolean",
+            "optional": true,
+            "description": "Whether the page content should shrink to fit the page width (overrides scaling). Default: true."
+          },
+          "showBackgroundColors": {
+            "type": "boolean",
+            "optional": true,
+            "description": "Whether the page background colors should be shown. Default: false."
+          },
+          "showBackgroundImages": {
+            "type": "boolean",
+            "optional": true,
+            "description": "Whether the page background images should be shown. Default: false."
+          },
+          "paperSizeUnit": {
+            "type": "integer",
+            "optional": true,
+            "description": "The page size unit: 0 = inches, 1 = millimeters. Default: 0."
+          },
+          "paperWidth": {
+            "type": "number",
+            "optional": true,
+            "description": "The paper width in paper size units. Default: 8.5."
+          },
+          "paperHeight": {
+            "type": "number",
+            "optional": true,
+            "description": "The paper height in paper size units. Default: 11.0."
+          },
+          "headerLeft": {
+            "type": "string",
+            "optional": true,
+            "description": "The text for the page's left header. Default: '&T'."
+          },
+          "headerCenter": {
+            "type": "string",
+            "optional": true,
+            "description": "The text for the page's center header. Default: ''."
+          },
+          "headerRight": {
+            "type": "string",
+            "optional": true,
+            "description": "The text for the page's right header. Default: '&U'."
+          },
+          "footerLeft": {
+            "type": "string",
+            "optional": true,
+            "description": "The text for the page's left footer. Default: '&PT'."
+          },
+          "footerCenter": {
+            "type": "string",
+            "optional": true,
+            "description": "The text for the page's center footer. Default: ''."
+          },
+          "footerRight": {
+            "type": "string",
+            "optional": true,
+            "description": "The text for the page's right footer. Default: '&D'."
+          },
+          "marginLeft": {
+            "type": "number",
+            "optional": true,
+            "description": "The margin between the page content and the left edge of the paper (inches). Default: 0.5."
+          },
+          "marginRight": {
+            "type": "number",
+            "optional": true,
+            "description": "The margin between the page content and the right edge of the paper (inches). Default: 0.5."
+          },
+          "marginTop": {
+            "type": "number",
+            "optional": true,
+            "description": "The margin between the page content and the top edge of the paper (inches). Default: 0.5."
+          },
+          "marginBottom": {
+            "type": "number",
+            "optional": true,
+            "description": "The margin between the page content and the bottom edge of the paper (inches). Default: 0.5."
+          }
+        }
+      },
+      {
         "id": "TabStatus",
         "type": "string",
         "enum": ["loading", "complete"],
         "description": "Whether the tabs have completed loading."
       },
       {
         "id": "WindowType",
         "type": "string",
@@ -1009,16 +1106,63 @@
               {
                 "$ref": "ZoomSettings",
                 "name": "zoomSettings",
                 "description": "The tab's current zoom settings."
               }
             ]
           }
         ]
+      },
+      {
+        "name": "print",
+        "type": "function",
+        "description": "Prints page in active tab.",
+        "parameters": []
+      },
+      {
+        "name": "printPreview",
+        "type": "function",
+        "description": "Shows print preview for page in active tab.",
+        "async": "callback",
+        "parameters": [
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "description": "Called after print preview entered.",
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "saveAsPDF",
+        "type": "function",
+        "description": "Saves page in active tab as a PDF file.",
+        "async": "callback",
+        "parameters": [
+          {
+            "$ref": "PageSettings",
+            "name": "pageSettings",
+            "description": "The page settings used to save the PDF file."
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "description": "Called after save as dialog closed.",
+            "parameters": [
+              {
+                "type": "string",
+                "name": "status",
+                "description": "Save status: Saved, Replaced, Cancelled, Not Saved, Not Replaced."
+              }
+            ]
+          }
+        ]
       }
     ],
     "events": [
       {
         "name": "onCreated",
         "type": "function",
         "description": "Fired when a tab is created. Note that the tab's URL may not be set at the time this event fired, but you can listen to onUpdated events to be notified when a URL is set.",
         "parameters": [
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -125,16 +125,17 @@ skip-if = debug || asan # Bug 1354681
 [browser_ext_tabs_insertCSS.js]
 [browser_ext_tabs_removeCSS.js]
 [browser_ext_tabs_move_array.js]
 [browser_ext_tabs_move_window.js]
 [browser_ext_tabs_move_window_multiple.js]
 [browser_ext_tabs_move_window_pinned.js]
 [browser_ext_tabs_onHighlighted.js]
 [browser_ext_tabs_onUpdated.js]
+[browser_ext_tabs_printPreview.js]
 [browser_ext_tabs_query.js]
 [browser_ext_tabs_reload.js]
 [browser_ext_tabs_reload_bypass_cache.js]
 [browser_ext_tabs_sendMessage.js]
 [browser_ext_tabs_cookieStoreId.js]
 [browser_ext_tabs_update.js]
 [browser_ext_tabs_zoom.js]
 [browser_ext_tabs_update_url.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_printPreview.js
@@ -0,0 +1,43 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(async function testPrintPreview() {
+  await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/");
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": ["tabs"],
+    },
+
+    background: async function() {
+      await browser.tabs.printPreview();
+      browser.test.assertTrue(true, "print preview entered");
+      browser.test.notifyPass("tabs.printPreview");
+    },
+  });
+
+  await extension.startup();
+  await extension.awaitFinish("tabs.printPreview");
+  await extension.unload();
+
+  let ppTab = PrintUtils._shouldSimplify ?
+      PrintPreviewListener._simplifiedPrintPreviewTab :
+      PrintPreviewListener._printPreviewTab;
+
+  let ppToolbar = document.getElementById("print-preview-toolbar");
+
+  is(window.gInPrintPreviewMode, true, "window in print preview mode");
+
+  isnot(ppTab, null, "print preview tab created");
+  isnot(ppTab.linkedBrowser, null, "print preview browser created");
+  isnot(ppToolbar, null, "print preview toolbar created");
+
+  is(ppTab, gBrowser.selectedTab, "print preview tab selected");
+  is(ppTab.linkedBrowser.currentURI.spec, "about:printpreview", "print preview browser url correct");
+
+  PrintUtils.exitPrintPreview();
+  await BrowserTestUtils.waitForCondition(() => !window.gInPrintPreviewMode);
+
+  await BrowserTestUtils.removeTab(gBrowser.tabs[1]);
+});
--- a/browser/components/extensions/test/mochitest/test_ext_all_apis.html
+++ b/browser/components/extensions/test/mochitest/test_ext_all_apis.html
@@ -38,20 +38,23 @@ let expectedBackgroundApisTargetSpecific
   "tabs.onCreated",
   "tabs.onDetached",
   "tabs.onHighlighted",
   "tabs.onMoved",
   "tabs.onRemoved",
   "tabs.onReplaced",
   "tabs.onUpdated",
   "tabs.onZoomChange",
+  "tabs.print",
+  "tabs.printPreview",
   "tabs.query",
   "tabs.reload",
   "tabs.remove",
   "tabs.removeCSS",
+  "tabs.saveAsPDF",
   "tabs.sendMessage",
   "tabs.setZoom",
   "tabs.setZoomSettings",
   "tabs.update",
   "windows.CreateType",
   "windows.WINDOW_ID_CURRENT",
   "windows.WINDOW_ID_NONE",
   "windows.WindowState",
--- a/toolkit/locales/en-US/chrome/global/extensions.properties
+++ b/toolkit/locales/en-US/chrome/global/extensions.properties
@@ -22,8 +22,10 @@ csp.error.illegal-host-wildcard = %2$S: wildcard sources in ā€˜%1$Sā€™ directives must include at least one non-generic sub-domain (e.g., *.example.com rather than *.com)
 #LOCALIZATION NOTE (uninstall.confirmation.title) %S is the name of the extension which is about to be uninstalled.
 uninstall.confirmation.title = Uninstall %S
 
 #LOCALIZATION NOTE (uninstall.confirmation.message) %S is the name of the extension which is about to be uninstalled.
 uninstall.confirmation.message = The extension ā€œ%Sā€ is requesting to be uninstalled. What would you like to do?
 
 uninstall.confirmation.button-0.label = Uninstall
 uninstall.confirmation.button-1.label = Keep Installed
+
+saveaspdf.saveasdialog.title = Save As