Bug 1590485: Add UI/UX and global zoom functionality. r=fluent-reviewers,Gijs

Differential Revision: https://phabricator.services.mozilla.com/D50845

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Morgan Reschenberg 2019-12-20 05:09:43 +00:00
parent b95cb66316
commit 2895fb8739
17 changed files with 821 additions and 70 deletions

View File

@ -180,9 +180,6 @@ var FullZoom = {
return;
}
this._globalValue =
aValue === undefined ? aValue : this._ensureValid(aValue);
// If the current page doesn't have a site-specific preference, then its
// zoom should be set to the new global preference now that the global
// preference has changed.
@ -386,7 +383,7 @@ var FullZoom = {
browser.messageManager.sendAsyncMessage("PDFJS:ZoomReset");
}
let token = this._getBrowserToken(browser);
let result = this._getGlobalValue(browser).then(value => {
let result = ZoomUI.getGlobalValue().then(value => {
if (token.isCurrent) {
ZoomManager.setZoomForBrowser(browser, value === undefined ? 1 : value);
this._ignorePendingZoomAccesses(browser);
@ -445,7 +442,7 @@ var FullZoom = {
}
let token = this._getBrowserToken(aBrowser);
this._getGlobalValue(aBrowser).then(value => {
ZoomUI.getGlobalValue().then(value => {
if (token.isCurrent) {
ZoomManager.setZoomForBrowser(
aBrowser,
@ -598,35 +595,6 @@ var FullZoom = {
return aValue;
},
/**
* Gets the global browser.content.full-zoom content preference.
*
* @param browser The browser pertaining to the zoom.
* @returns Promise<prefValue>
* Resolves to the preference value when done.
*/
_getGlobalValue: function FullZoom__getGlobalValue(browser) {
// * !("_globalValue" in this) => global value not yet cached.
// * this._globalValue === undefined => global value known not to exist.
// * Otherwise, this._globalValue is a number, the global value.
return new Promise(resolve => {
if ("_globalValue" in this) {
resolve(this._globalValue);
return;
}
let value = undefined;
this._cps2.getGlobal(this.name, this._loadContextFromBrowser(browser), {
handleResult(pref) {
value = pref.value;
},
handleCompletion: reason => {
this._globalValue = this._ensureValid(value);
resolve(this._globalValue);
},
});
});
},
/**
* Gets the load context from the given Browser.
*

View File

@ -106,6 +106,9 @@ with Files("test/webextensions/**"):
with Files("test/webrtc/**"):
BUG_COMPONENT = ("Core", "WebRTC")
with Files("test/zoom/**"):
BUG_COMPONENT = ("Firefox", "General")
with Files("aboutNetError.xhtml"):
BUG_COMPONENT = ("Firefox", "Security")

View File

@ -67,6 +67,7 @@ const startupPhases = {
"resource://gre/modules/PlacesUtils.jsm",
"resource://gre/modules/Promise.jsm", // imported by devtools during _delayedStartup
"resource://gre/modules/Preferences.jsm",
"resource://gre/modules/Sqlite.jsm",
]),
services: new Set(["@mozilla.org/browser/search-service;1"]),
},
@ -94,7 +95,6 @@ const startupPhases = {
"resource://gre/modules/PlacesBackups.jsm",
"resource://gre/modules/PlacesSyncUtils.jsm",
"resource://gre/modules/PushComponents.jsm",
"resource://gre/modules/Sqlite.jsm",
]),
services: new Set([
"@mozilla.org/browser/annotation-service;1",

View File

@ -0,0 +1,7 @@
"use strict";
module.exports = {
"extends": [
"plugin:mozilla/browser-test"
]
};

View File

@ -0,0 +1,8 @@
[DEFAULT]
support-files =
head.js
[browser_default_zoom.js]
[browser_default_zoom_multitab.js]
[browser_default_zoom_fission.js]
skip-if = fission # Bug 1603282

View File

@ -0,0 +1,160 @@
/* Any copyright is dedicated to the Public Domain.
* https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(async function test_init_default_zoom() {
const TEST_PAGE_URL =
"data:text/html;charset=utf-8,<body>test_init_default_zoom</body>";
// Prepare the test tab
let tab = BrowserTestUtils.addTab(gBrowser);
let tabBrowser = gBrowser.getBrowserForTab(tab);
await FullZoomHelper.selectTabAndWaitForLocationChange(tab);
await FullZoomHelper.load(tab, TEST_PAGE_URL);
// 100% global zoom
let defaultZoom = await FullZoomHelper.getGlobalValue();
is(defaultZoom, 1, "Global zoom is init at 100%");
// 100% tab zoom
is(
ZoomManager.getZoomForBrowser(tabBrowser),
1,
"Current zoom is init at 100%"
);
await FullZoomHelper.removeTabAndWaitForLocationChange();
});
add_task(async function test_set_default_zoom() {
const TEST_PAGE_URL =
"data:text/html;charset=utf-8,<body>test_set_default_zoom</body>";
// Prepare the test tab
let tab = BrowserTestUtils.addTab(gBrowser);
let tabBrowser = gBrowser.getBrowserForTab(tab);
await FullZoomHelper.selectTabAndWaitForLocationChange(tab);
await FullZoomHelper.load(tab, TEST_PAGE_URL);
// 120% global zoom
await FullZoomHelper.changeDefaultZoom(120);
let defaultZoom = await FullZoomHelper.getGlobalValue();
is(defaultZoom, 1.2, "Global zoom is at 120%");
// 120% tab zoom
await TestUtils.waitForCondition(() => {
return ZoomManager.getZoomForBrowser(tabBrowser) == 1.2;
});
is(
ZoomManager.getZoomForBrowser(tabBrowser),
1.2,
"Current zoom matches changed default zoom"
);
await FullZoomHelper.removeTabAndWaitForLocationChange();
});
add_task(async function test_enlarge_reduce_reset_local_zoom() {
const TEST_PAGE_URL =
"data:text/html;charset=utf-8,<body>test_enlarge_reduce_reset_local_zoom</body>";
// Prepare the test tab
let tab = BrowserTestUtils.addTab(gBrowser);
let tabBrowser = gBrowser.getBrowserForTab(tab);
await FullZoomHelper.selectTabAndWaitForLocationChange(tab);
await FullZoomHelper.load(tab, TEST_PAGE_URL);
// 120% global zoom persists from previous test
let defaultZoom = FullZoomHelper.getGlobalValue();
console.log("Current global zoom is ", defaultZoom);
console.log(
"Current tab zoom is ",
ZoomManager.getZoomForBrowser(tabBrowser)
);
await FullZoom.enlarge();
console.log("Enlarged!");
defaultZoom = FullZoomHelper.getGlobalValue();
console.log("Current global zoom is ", defaultZoom);
console.log(
"Current tab zoom is ",
ZoomManager.getZoomForBrowser(tabBrowser)
);
// 133% tab zoom
await TestUtils.waitForCondition(() => {
console.log(
"Current tab zoom is ",
ZoomManager.getZoomForBrowser(tabBrowser)
);
return ZoomManager.getZoomForBrowser(tabBrowser) == 1.33;
});
is(
ZoomManager.getZoomForBrowser(tabBrowser),
1.33,
"Increasing zoom changes zoom of current tab."
);
defaultZoom = await FullZoomHelper.getGlobalValue();
// 120% global zoom
is(
defaultZoom,
1.2,
"Increasing zoom of current tab doesn't change default zoom."
);
console.log("Reducing...");
console.log(
"Current tab zoom is ",
ZoomManager.getZoomForBrowser(tabBrowser)
);
await FullZoom.reduce(); // 120% tab zoom
console.log(
"Current tab zoom is ",
ZoomManager.getZoomForBrowser(tabBrowser)
);
await FullZoom.reduce(); // 110% tab zoom
console.log(
"Current tab zoom is ",
ZoomManager.getZoomForBrowser(tabBrowser)
);
await FullZoom.reduce(); // 100% tab zoom
console.log(
"Current tab zoom is ",
ZoomManager.getZoomForBrowser(tabBrowser)
);
await TestUtils.waitForCondition(() => {
console.log(
"Current tab zoom is ",
ZoomManager.getZoomForBrowser(tabBrowser)
);
return ZoomManager.getZoomForBrowser(tabBrowser) == 1;
});
is(
ZoomManager.getZoomForBrowser(tabBrowser),
1,
"Decreasing zoom changes zoom of current tab."
);
defaultZoom = await FullZoomHelper.getGlobalValue();
// 120% global zoom
is(
defaultZoom,
1.2,
"Decreasing zoom of current tab doesn't change default zoom."
);
console.log("Resetting...");
FullZoom.reset(); // 120% tab zoom
await TestUtils.waitForCondition(() => {
console.log(
"Current tab zoom is ",
ZoomManager.getZoomForBrowser(tabBrowser)
);
return ZoomManager.getZoomForBrowser(tabBrowser) == 1.2;
});
is(
ZoomManager.getZoomForBrowser(tabBrowser),
1.2,
"Reseting zoom causes current tab to zoom to default zoom."
);
// no reset necessary, it was performed as part of the test
await FullZoomHelper.removeTabAndWaitForLocationChange();
});

View File

@ -0,0 +1,102 @@
/* Any copyright is dedicated to the Public Domain.
* https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(async function test_sitespecific_iframe_global_zoom() {
const TEST_PAGE_URL =
'data:text/html;charset=utf-8,<body>test_sitespecific_iframe_global_zoom<iframe src=""></iframe></body>';
const TEST_IFRAME_URL = "https://example.com/";
// Prepare the test tab
let tab = BrowserTestUtils.addTab(gBrowser);
let tabBrowser = gBrowser.getBrowserForTab(tab);
await FullZoomHelper.selectTabAndWaitForLocationChange(tab);
await FullZoomHelper.load(tab, TEST_PAGE_URL);
// 67% global zoom
await FullZoomHelper.changeDefaultZoom(67);
let defaultZoom = await FullZoomHelper.getGlobalValue();
is(defaultZoom, 0.67, "Global zoom is at 67%");
await TestUtils.waitForCondition(
() => ZoomManager.getZoomForBrowser(tabBrowser) == 0.67
);
let zoomLevel = ZoomManager.getZoomForBrowser(tabBrowser);
is(zoomLevel, 0.67, "tab zoom has been set to 67%");
let frameLoadedPromise = BrowserTestUtils.browserLoaded(
tabBrowser,
true,
TEST_IFRAME_URL
);
SpecialPowers.spawn(tabBrowser, [TEST_IFRAME_URL], url => {
content.document.querySelector("iframe").src = url;
});
let loadedURL = await frameLoadedPromise;
is(loadedURL, TEST_IFRAME_URL, "got the load event for the iframe");
let frameZoom = await SpecialPowers.spawn(
gBrowser.selectedBrowser.browsingContext.getChildren()[0],
[],
async () => {
await ContentTaskUtils.waitForCondition(() => {
return content.docShell.contentViewer.fullZoom.toFixed(2) == 0.67;
});
return content.docShell.contentViewer.fullZoom.toFixed(2);
}
);
is(frameZoom, zoomLevel, "global zoom is reflected in iframe");
await FullZoomHelper.removeTabAndWaitForLocationChange();
});
add_task(async function test_sitespecific_global_zoom_enlarge() {
const TEST_PAGE_URL =
'data:text/html;charset=utf-8,<body>test_sitespecific_global_zoom_enlarge<iframe src=""></iframe></body>';
const TEST_IFRAME_URL = "https://example.org/";
// Prepare the test tab
let tab = BrowserTestUtils.addTab(gBrowser);
let tabBrowser = gBrowser.getBrowserForTab(tab);
await FullZoomHelper.selectTabAndWaitForLocationChange(tab);
await FullZoomHelper.load(tab, TEST_PAGE_URL);
// 67% global zoom persists from previous test
let frameLoadedPromise = BrowserTestUtils.browserLoaded(
tabBrowser,
true,
TEST_IFRAME_URL
);
SpecialPowers.spawn(tabBrowser, [TEST_IFRAME_URL], url => {
content.document.querySelector("iframe").src = url;
});
let loadedURL = await frameLoadedPromise;
is(loadedURL, TEST_IFRAME_URL, "got the load event for the iframe");
await FullZoom.enlarge();
// 80% local zoom
await TestUtils.waitForCondition(() => {
return ZoomManager.getZoomForBrowser(tabBrowser) == 0.8;
});
is(ZoomManager.getZoomForBrowser(tabBrowser), 0.8, "Local zoom is increased");
let frameZoom = await SpecialPowers.spawn(
gBrowser.selectedBrowser.browsingContext.getChildren()[0],
[],
async () => {
await ContentTaskUtils.waitForCondition(() => {
return content.docShell.contentViewer.fullZoom.toFixed(2) == 0.8;
});
return content.docShell.contentViewer.fullZoom.toFixed(2);
}
);
is(frameZoom, 0.8, "(without fission) iframe zoom matches page zoom");
// To ensure site-specific zoom settings don't persist
// across multiple runs, we reset the tab and iframe after use.
await FullZoom.reduce();
await FullZoomHelper.removeTabAndWaitForLocationChange();
});

View File

@ -0,0 +1,154 @@
/* Any copyright is dedicated to the Public Domain.
* https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(async function test_multidomain_global_zoom() {
const TEST_PAGE_URL_1 = "https://example.com";
const TEST_PAGE_URL_2 = "https://example.org";
// Prepare the test tabs
let tab1 = BrowserTestUtils.addTab(gBrowser);
let tabBrowser1 = gBrowser.getBrowserForTab(tab1);
await FullZoomHelper.selectTabAndWaitForLocationChange(tab1);
await FullZoomHelper.load(tab1, TEST_PAGE_URL_1);
let tab2 = BrowserTestUtils.addTab(gBrowser);
let tabBrowser2 = gBrowser.getBrowserForTab(tab2);
await FullZoomHelper.selectTabAndWaitForLocationChange(tab2);
await FullZoomHelper.load(tab2, TEST_PAGE_URL_2);
// 67% global zoom
await FullZoomHelper.changeDefaultZoom(67);
// 67% local zoom tab 1
await FullZoomHelper.selectTabAndWaitForLocationChange(tab1);
await TestUtils.waitForCondition(
() => ZoomManager.getZoomForBrowser(tabBrowser1) == 0.67
);
is(
ZoomManager.getZoomForBrowser(tabBrowser1),
0.67,
"Setting default zoom causes tab 1 (background) to zoom to default zoom."
);
// 67% local zoom tab 2
await FullZoomHelper.selectTabAndWaitForLocationChange(tab2);
await TestUtils.waitForCondition(
() => ZoomManager.getZoomForBrowser(tabBrowser2) == 0.67
);
is(
ZoomManager.getZoomForBrowser(tabBrowser2),
0.67,
"Setting default zoom causes tab 2 (foreground) to zoom to default zoom."
);
await FullZoom.enlarge();
// 80% local zoom tab 2
await TestUtils.waitForCondition(
() => ZoomManager.getZoomForBrowser(tabBrowser2) == 0.8
);
is(
ZoomManager.getZoomForBrowser(tabBrowser2),
0.8,
"Enlarging local zoom of tab 2."
);
// 67% local zoom tab 1
await FullZoomHelper.selectTabAndWaitForLocationChange(tab1);
await TestUtils.waitForCondition(
() => ZoomManager.getZoomForBrowser(tabBrowser1) == 0.67
);
is(
ZoomManager.getZoomForBrowser(tabBrowser1),
0.67,
"Tab 1 is unchanged by tab 2's enlarge call."
);
await FullZoomHelper.removeTabAndWaitForLocationChange();
await FullZoomHelper.removeTabAndWaitForLocationChange();
});
add_task(async function test_site_specific_global_zoom() {
const TEST_PAGE_URL_1 = "http://example.net";
const TEST_PAGE_URL_2 = "http://example.net";
// Prepare the test tabs
let tab1 = BrowserTestUtils.addTab(gBrowser);
let tabBrowser1 = gBrowser.getBrowserForTab(tab1);
await FullZoomHelper.selectTabAndWaitForLocationChange(tab1);
await FullZoomHelper.load(tab1, TEST_PAGE_URL_1);
let tab2 = BrowserTestUtils.addTab(gBrowser);
let tabBrowser2 = gBrowser.getBrowserForTab(tab2);
await FullZoomHelper.selectTabAndWaitForLocationChange(tab2);
await FullZoomHelper.load(tab2, TEST_PAGE_URL_2);
// 67% global zoom persists from previous test
let defaultZoom = FullZoomHelper.getGlobalValue();
console.log("Global zoom is: ", defaultZoom);
// 67% local zoom tab 1
await FullZoomHelper.selectTabAndWaitForLocationChange(tab1);
await TestUtils.waitForCondition(() => {
console.log(
"Current tab 1 zoom is: ",
ZoomManager.getZoomForBrowser(tabBrowser1)
);
return ZoomManager.getZoomForBrowser(tabBrowser1) == 0.67;
});
is(
ZoomManager.getZoomForBrowser(tabBrowser1),
0.67,
"Setting default zoom causes tab 1 (background) to zoom to default zoom."
);
// 67% local zoom tab 2
await FullZoomHelper.selectTabAndWaitForLocationChange(tab2);
await TestUtils.waitForCondition(() => {
console.log(
"Current tab 2 zoom is: ",
ZoomManager.getZoomForBrowser(tabBrowser2)
);
return ZoomManager.getZoomForBrowser(tabBrowser2) == 0.67;
});
is(
ZoomManager.getZoomForBrowser(tabBrowser2),
0.67,
"Setting default zoom causes tab 2 (foreground) to zoom to default zoom."
);
// 80% site specific zoom
await FullZoomHelper.selectTabAndWaitForLocationChange(tab1);
console.log("Enlarging");
await FullZoom.enlarge();
await TestUtils.waitForCondition(() => {
console.log(
"Current tab 1 zoom is: ",
ZoomManager.getZoomForBrowser(tabBrowser1)
);
return ZoomManager.getZoomForBrowser(tabBrowser1) == 0.8;
});
is(
ZoomManager.getZoomForBrowser(tabBrowser1),
0.8,
"Changed local zoom in tab one."
);
await FullZoomHelper.selectTabAndWaitForLocationChange(tab2);
await TestUtils.waitForCondition(() => {
console.log(
"Current tab 2 zoom is: ",
ZoomManager.getZoomForBrowser(tabBrowser2)
);
return ZoomManager.getZoomForBrowser(tabBrowser2) == 0.8;
});
is(
ZoomManager.getZoomForBrowser(tabBrowser2),
0.8,
"Second tab respects site specific zoom."
);
await FullZoomHelper.removeTabAndWaitForLocationChange();
await FullZoomHelper.removeTabAndWaitForLocationChange();
});

View File

@ -0,0 +1,173 @@
/* Any copyright is dedicated to the Public Domain.
* https://creativecommons.org/publicdomain/zero/1.0/ */
const { BrowserTestUtils } = ChromeUtils.import(
"resource://testing-common/BrowserTestUtils.jsm"
);
let gContentPrefs = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
let gLoadContext = Cu.createLoadContext();
registerCleanupFunction(async function() {
await new Promise(resolve => {
gContentPrefs.removeByName(window.FullZoom.name, gLoadContext, {
handleResult() {},
handleCompletion() {
resolve();
},
});
});
});
var FullZoomHelper = {
async changeDefaultZoom(newZoom) {
let nonPrivateLoadContext = Cu.createLoadContext();
/* Because our setGlobal function takes in a browsing context, and
* because we want to keep this property consistent across both private
* and non-private contexts, we crate a non-private context and use that
* to set the property, regardless of our actual context.
*/
let parsedZoomValue = parseFloat((parseInt(newZoom) / 100).toFixed(2));
await new Promise(resolve => {
gContentPrefs.setGlobal(
FullZoom.name,
parsedZoomValue,
nonPrivateLoadContext,
{
handleCompletion(reason) {
resolve();
},
}
);
});
},
async getGlobalValue() {
return new Promise(resolve => {
let cachedVal = parseFloat(
gContentPrefs.getCachedGlobal(FullZoom.name, gLoadContext)
);
if (cachedVal) {
// We've got cached information, though it may be we've cached
// an undefined value, or the cached info is invalid. To ensure
// a valid return, we opt to return the default 1.0 in the
// undefined and invalid cases.
resolve(parseFloat(cachedVal.value) || 1.0);
return;
}
let value = 1.0;
gContentPrefs.getGlobal(FullZoom.name, gLoadContext, {
handleResult(pref) {
if (pref.value) {
value = parseFloat(pref.value);
}
},
handleCompletion(reason) {
resolve(value);
},
handleError(error) {
Cu.reportError(error);
},
});
});
},
waitForLocationChange: function waitForLocationChange() {
return new Promise(resolve => {
Services.obs.addObserver(function obs(subj, topic, data) {
Services.obs.removeObserver(obs, topic);
resolve();
}, "browser-fullZoom:location-change");
});
},
selectTabAndWaitForLocationChange: function selectTabAndWaitForLocationChange(
tab
) {
if (!tab) {
throw new Error("tab must be given.");
}
if (gBrowser.selectedTab == tab) {
return Promise.resolve();
}
return Promise.all([
BrowserTestUtils.switchTab(gBrowser, tab),
this.waitForLocationChange(),
]);
},
removeTabAndWaitForLocationChange: function removeTabAndWaitForLocationChange(
tab
) {
tab = tab || gBrowser.selectedTab;
let selected = gBrowser.selectedTab == tab;
gBrowser.removeTab(tab);
if (selected) {
return this.waitForLocationChange();
}
return Promise.resolve();
},
load: function load(tab, url) {
return new Promise(resolve => {
let didLoad = false;
let didZoom = false;
promiseTabLoadEvent(tab).then(event => {
didLoad = true;
if (didZoom) {
resolve();
}
}, true);
this.waitForLocationChange().then(function() {
didZoom = true;
if (didLoad) {
resolve();
}
});
BrowserTestUtils.loadURI(tab.linkedBrowser, url);
});
},
};
/**
* Waits for a load (or custom) event to finish in a given tab. If provided
* load an uri into the tab.
*
* @param tab
* The tab to load into.
* @param [optional] url
* The url to load, or the current url.
* @return {Promise} resolved when the event is handled.
* @resolves to the received event
* @rejects if a valid load event is not received within a meaningful interval
*/
function promiseTabLoadEvent(tab, url) {
console.info("Wait tab event: load");
function handle(loadedUrl) {
if (loadedUrl === "about:blank" || (url && loadedUrl !== url)) {
console.info(`Skipping spurious load event for ${loadedUrl}`);
return false;
}
console.info("Tab event received: load");
return true;
}
let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle);
if (url) {
BrowserTestUtils.loadURI(tab.linkedBrowser, url);
}
return loaded;
}

View File

@ -57,6 +57,7 @@ BROWSER_CHROME_MANIFESTS += [
'content/test/trackingUI/browser.ini',
'content/test/webextensions/browser.ini',
'content/test/webrtc/browser.ini',
'content/test/zoom/browser.ini',
]
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']

View File

@ -4,18 +4,31 @@
"use strict";
var initialPageZoom = ZoomManager.zoom;
add_task(async function() {
info("Check zoom in button existence and functionality");
is(initialPageZoom, 1, "Initial zoom factor should be 1");
is(ZoomManager.zoom, 1, "Initial zoom factor should be 1");
CustomizableUI.addWidgetToArea(
"zoom-controls",
CustomizableUI.AREA_FIXED_OVERFLOW_PANEL
);
registerCleanupFunction(() => CustomizableUI.reset());
registerCleanupFunction(async () => {
CustomizableUI.reset();
let gContentPrefs = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
let gLoadContext = Cu.createLoadContext();
await new Promise(resolve => {
gContentPrefs.removeByName(window.FullZoom.name, gLoadContext, {
handleResult() {},
handleCompletion() {
resolve();
},
});
});
});
await waitForOverflowButtonShown();
@ -27,12 +40,18 @@ add_task(async function() {
zoomInButton.click();
let pageZoomLevel = parseInt(ZoomManager.zoom * 100);
console.log("Page oom level is: ", pageZoomLevel);
let zoomResetButton = document.getElementById("zoom-reset-button");
let expectedZoomLevel = parseInt(zoomResetButton.getAttribute("label"), 10);
ok(
pageZoomLevel > 100 && pageZoomLevel == expectedZoomLevel,
"Page zoomed in correctly"
);
await TestUtils.waitForCondition(() => {
console.log(
"Current zoom is ",
parseInt(zoomResetButton.getAttribute("label"), 10)
);
return parseInt(zoomResetButton.getAttribute("label"), 10) == pageZoomLevel;
});
ok(pageZoomLevel > 100, "Page zoomed in correctly");
// close the Panel
let panelHiddenPromise = promiseOverflowHidden(window);
@ -40,9 +59,3 @@ add_task(async function() {
await panelHiddenPromise;
info("Menu panel was closed");
});
add_task(async function asyncCleanup() {
// reset zoom level
ZoomManager.zoom = initialPageZoom;
info("Zoom level was restored");
});

View File

@ -4,18 +4,31 @@
"use strict";
var initialPageZoom = ZoomManager.zoom;
add_task(async function() {
info("Check zoom out button existence and functionality");
is(initialPageZoom, 1, "Initial zoom factor should be 1");
is(ZoomManager.zoom, 1, "Initial zoom factor should be 1");
CustomizableUI.addWidgetToArea(
"zoom-controls",
CustomizableUI.AREA_FIXED_OVERFLOW_PANEL
);
registerCleanupFunction(() => CustomizableUI.reset());
registerCleanupFunction(async () => {
CustomizableUI.reset();
let gContentPrefs = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
let gLoadContext = Cu.createLoadContext();
await new Promise(resolve => {
gContentPrefs.removeByName(window.FullZoom.name, gLoadContext, {
handleResult() {},
handleCompletion() {
resolve();
},
});
});
});
await waitForOverflowButtonShown();
@ -27,13 +40,18 @@ add_task(async function() {
zoomOutButton.click();
let pageZoomLevel = Math.round(ZoomManager.zoom * 100);
console.log("Page zoom level is: ", pageZoomLevel);
let zoomResetButton = document.getElementById("zoom-reset-button");
let expectedZoomLevel = parseInt(zoomResetButton.getAttribute("label"), 10);
ok(
pageZoomLevel < 100 && pageZoomLevel == expectedZoomLevel,
"Page zoomed out correctly"
);
await TestUtils.waitForCondition(() => {
console.log(
"Current zoom is ",
parseInt(zoomResetButton.getAttribute("label"), 10)
);
return parseInt(zoomResetButton.getAttribute("label"), 10) == pageZoomLevel;
});
ok(pageZoomLevel < 100, "Page zoomed out correctly");
// close the panel
let panelHiddenPromise = promiseOverflowHidden(window);
@ -41,9 +59,3 @@ add_task(async function() {
await panelHiddenPromise;
info("Menu panel was closed");
});
add_task(async function asyncCleanup() {
// reset zoom level
ZoomManager.zoom = initialPageZoom;
info("Zoom level was restored");
});

View File

@ -268,6 +268,26 @@
</hbox>
</groupbox>
<!-- Zoom -->
<groupbox id="zoomGroup" data-category="paneGeneral" hidden="true">
<label><html:h2 data-l10n-id="preferences-zoom-header"/></label>
<hbox id="zoomBox" align="center" hidden="true">
<label control="defaultZoom" data-l10n-id="preferences-default-zoom"/>
<!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
<hbox flex="1">
<menulist id="defaultZoom">
<menupopup/>
</menulist>
</hbox>
</hbox>
<checkbox id="zoomText"
data-l10n-id="preferences-zoom-text-only"
preference="browser.zoom.full"/>
</groupbox>
<!-- Languages -->
<groupbox id="languagesGroup" data-category="paneGeneral" hidden="true">
<label><html:h2 data-l10n-id="language-header"/></label>

View File

@ -369,6 +369,11 @@ var gMainPane = {
gMainPane.initBrowserLocale();
}
// We call `initDefaultZoomValues` to set and unhide the
// default zoom preferences menu, and to establish a
// listener for future menu changes.
gMainPane.initDefaultZoomValues();
let cfrLearnMoreUrl =
Services.urlFormatter.formatURLPref("app.support.baseURL") +
"extensionrecommendations";
@ -926,6 +931,41 @@ var gMainPane = {
}
}
},
/**
* Fetch the existing default zoom value, initialise and unhide
* the preferences menu. This method also establishes a listener
* to ensure handleDefaultZoomChange is called on future menu
* changes.
*/
async initDefaultZoomValues() {
let win = window.docShell.rootTreeItem.domWindow;
let selected = await win.ZoomUI.getGlobalValue();
let menulist = document.getElementById("defaultZoom");
new SelectionChangedMenulist(menulist, event => {
let parsedZoom = parseFloat((event.target.value / 100).toFixed(2));
gMainPane.handleDefaultZoomChange(parsedZoom);
});
let zoomValues = win.ZoomManager.zoomValues.map(a => {
return Math.round(a * 100);
});
let fragment = document.createDocumentFragment();
for (let zoomLevel of zoomValues) {
let menuitem = document.createXULElement("menuitem");
document.l10n.setAttributes(menuitem, "preferences-default-zoom-value", {
percentage: zoomLevel,
});
menuitem.setAttribute("value", zoomLevel);
fragment.appendChild(menuitem);
}
let menupopup = menulist.querySelector("menupopup");
menupopup.appendChild(fragment);
menulist.value = Math.round(selected * 100);
document.getElementById("zoomBox").hidden = false;
},
initBrowserLocale() {
// Enable telemetry.
@ -1101,6 +1141,27 @@ var gMainPane = {
this.showConfirmLanguageChangeMessageBar(locales);
},
/**
* Takes as newZoom a floating point value representing the
* new default zoom. This value should not be a string, and
* should not carry a percentage sign/other localisation
* characteristics.
*/
handleDefaultZoomChange(newZoom) {
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
let nonPrivateLoadContext = Cu.createLoadContext();
/* Because our setGlobal function takes in a browsing context, and
* because we want to keep this property consistent across both private
* and non-private contexts, we crate a non-private context and use that
* to set the property, regardless of our actual context.
*/
let win = window.docShell.rootTreeItem.domWindow;
cps2.setGlobal(win.FullZoom.name, newZoom, nonPrivateLoadContext);
},
onBrowserRestoreSessionChange(event) {
const value = event.target.checked;
const startupPref = Preferences.get("browser.startup.page");

View File

@ -139,6 +139,7 @@ add_task(async function search_for_password_show_passwordGroup() {
child.id == "startupGroup" ||
child.id == "languagesGroup" ||
child.id == "fontsGroup" ||
child.id == "zoomGroup" ||
child.id == "downloadsGroup" ||
child.id == "applicationsGroup" ||
child.id == "drmGroup" ||

View File

@ -245,6 +245,19 @@ colors-settings =
.label = Colors…
.accesskey = C
# Zoom is a noun, and the message is used as header for a group of options
preferences-zoom-header = Zoom
preferences-default-zoom = Default zoom
.accesskey = z
preferences-default-zoom-value =
.label = { $percentage }%
preferences-zoom-text-only =
.label = Zoom text only
.accesskey = t
language-header = Language
choose-language-description = Choose your preferred language for displaying pages

View File

@ -6,8 +6,12 @@
"use strict";
var EXPORTED_SYMBOLS = ["ZoomUI"];
const gLoadContext = Cu.createLoadContext();
const gContentPrefs = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const gZoomPropertyName = "browser.content.full-zoom";
var ZoomUI = {
init(aWindow) {
@ -28,6 +32,45 @@ var ZoomUI = {
{ once: true }
);
},
/**
* Gets the global browser.content.full-zoom content preference.
*
* @returns Promise<prefValue>
* Resolves to the preference value (float) when done.
*/
getGlobalValue() {
return new Promise(resolve => {
let cachedVal = gContentPrefs.getCachedGlobal(
gZoomPropertyName,
gLoadContext
);
if (cachedVal) {
// We've got cached information, though it may be we've cached
// an undefined value, or the cached info is invalid. To ensure
// a valid return, we opt to return the default 1.0 in the
// undefined and invalid cases.
resolve(parseFloat(cachedVal.value) || 1.0);
return;
}
// Otherwise, nothing is cached, so we must do a full lookup
// with `gContentPrefs.getGlobal()`.
let value = 1.0;
gContentPrefs.getGlobal(gZoomPropertyName, gLoadContext, {
handleResult(pref) {
if (pref.value) {
value = parseFloat(pref.value);
}
},
handleCompletion(reason) {
resolve(value);
},
handleError(error) {
Cu.reportError(error);
},
});
});
},
};
function fullZoomLocationChangeObserver(aSubject, aTopic) {
@ -74,7 +117,7 @@ function onZoomChange(event) {
* @param {boolean} aAnimate Should be True for all cases unless the zoom
* change is related to tab switching. Optional
*/
function updateZoomUI(aBrowser, aAnimate = false) {
async function updateZoomUI(aBrowser, aAnimate = false) {
let win = aBrowser.ownerGlobal;
if (!win.gBrowser || win.gBrowser.selectedBrowser != aBrowser) {
return;
@ -88,10 +131,22 @@ function updateZoomUI(aBrowser, aAnimate = false) {
let urlbarZoomButton = win.document.getElementById("urlbar-zoom-button");
let zoomFactor = Math.round(win.ZoomManager.zoom * 100);
// Hide urlbar zoom button if zoom is at 100% or the customizable control is
// in the toolbar.
// Hide urlbar zoom button if zoom is at the default zoom level,
// or the customizable control is in the toolbar
let defaultZoom = (await ZoomUI.getGlobalValue()) * 100;
if (!win.gBrowser || win.gBrowser.selectedBrowser != aBrowser) {
// Because the CPS call is async, at this point the selected browser
// may have changed. We should re-check whether the browser for which we've
// been notified is still the selected browser and bail out if not.
// If the selected browser changed (again), we will have been called again
// with the "right" browser, and that'll update the zoom level.
return;
}
urlbarZoomButton.hidden =
zoomFactor == 100 ||
defaultZoom == zoomFactor ||
(customizableZoomControls &&
customizableZoomControls.getAttribute("cui-areatype") == "toolbar");