Bug 1238310: Part 3 - Implement the base browser.tabs zoom API. r?aswan f?gabor draft
authorKris Maglione <maglione.k@gmail.com>
Fri, 01 Apr 2016 15:44:29 -0700
changeset 353955 b43761bbeccfa2da0fad969676c2e7ac9b434f7d
parent 353954 5da4e631789e8da63e45b0385131dbf6ae3e72f7
child 353956 72e6f860df44b31c25940d0ea6b75242bf340851
push id15974
push usermaglione.k@gmail.com
push dateWed, 20 Apr 2016 03:00:13 +0000
reviewersaswan
bugs1238310
milestone48.0a1
Bug 1238310: Part 3 - Implement the base browser.tabs zoom API. r?aswan f?gabor MozReview-Commit-ID: BZEFOnCRMba
browser/components/extensions/ext-tabs.js
browser/components/extensions/test/browser/browser.ini
browser/components/extensions/test/browser/browser_ext_tabs_zoom.js
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -856,12 +856,67 @@ extensions.registerSchemaAPI("tabs", nul
           newTab.addEventListener("SSTabRestored", function listener() {
             // Once it has been restored, select it and return the promise.
             newTab.removeEventListener("SSTabRestored", listener);
             gBrowser.selectedTab = newTab;
             return resolve(TabManager.convert(extension, newTab));
           });
         });
       },
+
+      getZoom(tabId) {
+        let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
+
+        let {ZoomManager} = tab.ownerDocument.defaultView;
+        let zoom = ZoomManager.getZoomForBrowser(tab.linkedBrowser);
+
+        return Promise.resolve(zoom);
+      },
+
+      setZoom(tabId, zoom) {
+        let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
+
+        let {FullZoom, ZoomManager} = tab.ownerDocument.defaultView;
+
+        if (zoom === 0) {
+          // A value of zero means use the default zoom factor.
+          return FullZoom.reset(tab.linkedBrowser);
+        } else if (zoom >= ZoomManager.MIN && zoom <= ZoomManager.MAX) {
+          FullZoom.setZoom(zoom, tab.linkedBrowser);
+        } else {
+          return Promise.reject({
+            message: `Zoom value ${zoom} out of range (must be between ${ZoomManager.MIN} and ${ZoomManager.MAX})`,
+          });
+        }
+
+        return Promise.resolve();
+      },
+
+      _getZoomSettings(tabId) {
+        let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
+
+        let {FullZoom} = tab.ownerDocument.defaultView;
+
+        return {
+          mode: "automatic",
+          scope: FullZoom.siteSpecific ? "per-origin" : "per-tab",
+          defaultZoomFactor: 1,
+        };
+      },
+
+      getZoomSettings(tabId) {
+        return Promise.resolve(this._getZoomSettings(tabId));
+      },
+
+      setZoomSettings(tabId, settings) {
+        let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
+
+        let currentSettings = this._getZoomSettings(tab.id);
+
+        if (!Object.keys(settings).every(key => settings[key] === currentSettings[key])) {
+          return Promise.reject(`Unsupported zoom settings: ${JSON.stringify(settings)}`);
+        }
+        return Promise.resolve();
+      },
     },
   };
   return self;
 });
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -55,16 +55,17 @@ support-files =
 [browser_ext_tabs_move_window_pinned.js]
 [browser_ext_tabs_onHighlighted.js]
 [browser_ext_tabs_onUpdated.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_update.js]
+[browser_ext_tabs_zoom.js]
 [browser_ext_tabs_update_url.js]
 [browser_ext_topwindowid.js]
 [browser_ext_webNavigation_getFrames.js]
 [browser_ext_windows.js]
 [browser_ext_windows_create.js]
 tags = fullscreen
 [browser_ext_windows_create_tabId.js]
 [browser_ext_windows_events.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_zoom.js
@@ -0,0 +1,182 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+const SITE_SPECIFIC_PREF = "browser.zoom.siteSpecific";
+
+add_task(function* () {
+  let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+  let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/");
+
+  gBrowser.selectedTab = tab1;
+
+  function background() {
+    function promiseUpdated(tabId, attr) {
+      return new Promise(resolve => {
+        let onUpdated = (tabId_, changeInfo, tab) => {
+          if (tabId == tabId_ && attr in changeInfo) {
+            browser.tabs.onUpdated.removeListener(onUpdated);
+
+            resolve({changeInfo, tab});
+          }
+        };
+        browser.tabs.onUpdated.addListener(onUpdated);
+      });
+    }
+
+    let deferred = {};
+    browser.test.onMessage.addListener((message, msg, result) => {
+      if (message == "msg-done" && deferred[msg]) {
+        deferred[msg].resolve(result);
+      }
+    });
+
+    let _id = 0;
+    function msg(...args) {
+      return new Promise((resolve, reject) => {
+        let id = ++_id;
+        deferred[id] = {resolve, reject};
+        browser.test.sendMessage("msg", id, ...args);
+      });
+    }
+
+    let checkZoom = (tabId, newValue) => {
+      return Promise.all([
+        browser.tabs.getZoom(tabId),
+        msg("get-zoom", tabId),
+      ]).then(([apiZoom, realZoom]) => {
+        browser.test.assertEq(newValue, apiZoom, `Got expected zoom value from API`);
+        browser.test.assertEq(newValue, realZoom, `Got expected zoom value from parent`);
+      });
+    };
+
+    let tabIds;
+
+    browser.tabs.query({lastFocusedWindow: true}).then(tabs => {
+      browser.test.assertEq(tabs.length, 3, "We have three tabs");
+
+      tabIds = [tabs[1].id, tabs[2].id];
+
+      return checkZoom(tabIds[0], 1);
+    }).then(() => {
+      return browser.tabs.setZoom(tabIds[0], 2);
+    }).then(() => {
+      return checkZoom(tabIds[0], 2);
+    }).then(() => {
+      return browser.tabs.getZoomSettings(tabIds[0]);
+    }).then(zoomSettings => {
+      browser.test.assertEq(3, Object.keys(zoomSettings).length, `Zoom settings should have 3 keys`);
+      browser.test.assertEq("automatic", zoomSettings.mode, `Mode should be "automatic"`);
+      browser.test.assertEq("per-origin", zoomSettings.scope, `Scope should be "per-origin"`);
+      browser.test.assertEq(1, zoomSettings.defaultZoomFactor, `Default zoom should be 1`);
+
+      browser.test.log(`Switch to tab 2`);
+      return browser.tabs.update(tabIds[1], {active: true});
+    }).then(() => {
+      return checkZoom(tabIds[1], 1);
+    }).then(() => {
+      browser.test.log(`Navigate tab 2 to origin of tab 1`);
+      browser.tabs.update(tabIds[1], {url: "http://example.com"});
+
+      return promiseUpdated(tabIds[1], "url");
+    }).then(() => {
+      return checkZoom(tabIds[1], 2);
+    }).then(() => {
+      browser.test.log(`Update zoom in tab 2, expect changes in both tabs`);
+      return browser.tabs.setZoom(tabIds[1], 1.5);
+    }).then(() => {
+      return checkZoom(tabIds[1], 1.5);
+    }).then(() => {
+      browser.test.log(`Switch to tab 1, expect asynchronous zoom change just after the switch`);
+      return browser.tabs.update(tabIds[0], {active: true});
+    }).then(() => {
+      return new Promise(resolve => setTimeout(resolve, 0));
+    }).then(() => {
+      return checkZoom(tabIds[0], 1.5);
+    }).then(() => {
+      browser.test.log("Set zoom to 0, expect it set to 1");
+      return browser.tabs.setZoom(tabIds[0], 0);
+    }).then(() => {
+      return checkZoom(tabIds[0], 1);
+    }).then(() => {
+      browser.test.log("Change zoom externally, expect changes reflected");
+      return msg("enlarge");
+    }).then(() => {
+      return checkZoom(tabIds[0], 1.1);
+    }).then(() => {
+      return Promise.all([
+        browser.tabs.setZoom(tabIds[0], 0),
+        browser.tabs.setZoom(tabIds[1], 0),
+      ]);
+    }).then(() => {
+      return Promise.all([
+        checkZoom(tabIds[0], 1),
+        checkZoom(tabIds[1], 1),
+      ]);
+    }).then(() => {
+      browser.test.log("Check that invalid zoom values throw an error");
+      return browser.tabs.setZoom(tabIds[0], 42).then(
+        () => {
+          browser.test.fail("Expected an error");
+        },
+        error => {
+          browser.test.assertTrue(error.message.includes("Zoom value 42 out of range"),
+                                  "Got expected error");
+        });
+    }).then(() => {
+      browser.test.log("Disable site-specific zoom, expect correct scope");
+      return msg("site-specific", false);
+    }).then(() => {
+      return browser.tabs.getZoomSettings(tabIds[0]);
+    }).then(zoomSettings => {
+      browser.test.assertEq("per-tab", zoomSettings.scope, `Scope should be "per-tab"`);
+    }).then(() => {
+      return msg("site-specific", null);
+    }).then(() => {
+      browser.test.notifyPass("tab-zoom");
+    }).catch(e => {
+      browser.test.fail(`Error: ${e} :: ${e.stack}`);
+      browser.test.notifyFail("tab-zoom");
+    });
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": ["tabs"],
+    },
+
+    background,
+  });
+
+  extension.onMessage("msg", (id, msg, ...args) => {
+    let {TabManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
+
+    let resp;
+    if (msg == "get-zoom") {
+      let tab = TabManager.getTab(args[0]);
+      resp = ZoomManager.getZoomForBrowser(tab.linkedBrowser);
+    } else if (msg == "set-zoom") {
+      let tab = TabManager.getTab(args[0]);
+      ZoomManager.setZoomForBrowser(tab.linkedBrowser);
+    } else if (msg == "enlarge") {
+      FullZoom.enlarge();
+    } else if (msg == "site-specific") {
+      if (args[0] == null) {
+        SpecialPowers.clearUserPref(SITE_SPECIFIC_PREF);
+      } else {
+        SpecialPowers.setBoolPref(SITE_SPECIFIC_PREF, args[0]);
+      }
+    }
+
+    extension.sendMessage("msg-done", id, resp);
+  });
+
+  yield extension.startup();
+
+  yield extension.awaitFinish("tab-zoom");
+
+  yield extension.unload();
+
+  yield BrowserTestUtils.removeTab(tab1);
+  yield BrowserTestUtils.removeTab(tab2);
+});