Merge fx-team to central, a=merge CLOSED TREE

This commit is contained in:
Wes Kocher 2016-07-08 15:02:31 -07:00
commit 2a800a6763
28 changed files with 588 additions and 276 deletions

View File

@ -454,7 +454,6 @@ var gExtensionCount = 0;
/* eslint-disable mozilla/balanced-listeners */
extensions.on("startup", (type, extension) => {
gContextMenuMap.set(extension, new Map());
gRootItems.delete(extension);
if (++gExtensionCount == 1) {
Services.obs.addObserver(contextMenuObserver,
"on-build-contextmenu",
@ -464,6 +463,7 @@ extensions.on("startup", (type, extension) => {
extensions.on("shutdown", (type, extension) => {
gContextMenuMap.delete(extension);
gRootItems.delete(extension);
if (--gExtensionCount == 0) {
Services.obs.removeObserver(contextMenuObserver,
"on-build-contextmenu");

View File

@ -19,8 +19,12 @@
"clickBrowserAction": true,
"clickPageAction": true,
"CustomizableUI": true,
"closeContextMenu": true,
"closeExtensionContextMenu": true,
"focusWindow": true,
"makeWidgetId": true,
"openContextMenu": true,
"openExtensionContextMenu": true,
"CustomizableUI": true,
}
}

View File

@ -28,6 +28,10 @@ support-files =
[browser_ext_commands_onCommand.js]
[browser_ext_contentscript_connect.js]
[browser_ext_contextMenus.js]
[browser_ext_contextMenus_checkboxes.js]
[browser_ext_contextMenus_icons.js]
[browser_ext_contextMenus_radioGroups.js]
[browser_ext_contextMenus_uninstall.js]
[browser_ext_currentWindow.js]
[browser_ext_getViews.js]
[browser_ext_history.js]

View File

@ -27,36 +27,15 @@ add_task(function* () {
yield extension.startup();
yield extension.awaitFinish();
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
yield BrowserTestUtils.synthesizeMouseAtCenter("#img1", {
type: "contextmenu",
button: 2,
}, gBrowser.selectedBrowser);
yield popupShownPromise;
let contentAreaContextMenu = yield openContextMenu("#img1");
let item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
is(item.length, 1, "contextMenu item for image was found");
yield closeContextMenu();
let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
EventUtils.synthesizeMouseAtCenter(item[0], {});
yield popupHiddenPromise;
contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
yield BrowserTestUtils.synthesizeMouseAtCenter("body", {
type: "contextmenu",
button: 2,
}, gBrowser.selectedBrowser);
yield popupShownPromise;
contentAreaContextMenu = yield openContextMenu("body");
item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
is(item.length, 0, "no contextMenu item for image was found");
// click something to close the context menu
popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
EventUtils.synthesizeMouseAtCenter(document.getElementById("context-selectall"), {});
yield popupHiddenPromise;
yield closeContextMenu();
yield extension.unload();
@ -141,48 +120,6 @@ add_task(function* () {
});
browser.contextMenus.remove(parentToDel);
browser.contextMenus.create({
title: "radio-group-1",
type: "radio",
checked: true,
onclick: genericOnClick,
});
browser.contextMenus.create({
title: "Checkbox",
type: "checkbox",
onclick: genericOnClick,
});
browser.contextMenus.create({
title: "radio-group-2",
type: "radio",
onclick: genericOnClick,
});
browser.contextMenus.create({
title: "radio-group-2",
type: "radio",
onclick: genericOnClick,
});
browser.contextMenus.create({
type: "separator",
});
browser.contextMenus.create({
title: "Checkbox",
type: "checkbox",
checked: true,
onclick: genericOnClick,
});
browser.contextMenus.create({
title: "Checkbox",
type: "checkbox",
onclick: genericOnClick,
});
browser.contextMenus.create({
title: "Without onclick property",
id: "ext-without-onclick",
@ -190,159 +127,58 @@ add_task(function* () {
browser.contextMenus.update(parent, {parentId: child2}).then(
() => {
browser.test.notifyFail();
browser.test.notifyFail("contextmenus");
},
() => {
browser.test.notifyPass();
browser.test.notifyPass("contextmenus");
}
);
},
});
yield extension.startup();
yield extension.awaitFinish();
yield extension.awaitFinish("contextmenus");
let contentAreaContextMenu;
let expectedClickInfo = {
menuItemId: "ext-image",
mediaType: "image",
srcUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/ctxmenu-image.png",
pageUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html",
};
function getTop() {
contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
let items = contentAreaContextMenu.getElementsByAttribute("ext-type", "top-level-menu");
is(items.length, 1, "top level item was found (context=selection)");
let topItem = items[0];
return topItem.childNodes[0];
}
function* openExtensionMenu() {
contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
yield BrowserTestUtils.synthesizeMouseAtCenter("#img1", {
type: "contextmenu",
button: 2,
}, gBrowser.selectedBrowser);
yield popupShownPromise;
popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
EventUtils.synthesizeMouseAtCenter(getTop(), {});
yield popupShownPromise;
}
function* closeContextMenu(itemToSelect, expectedClickInfo, hasOnclickProperty = true) {
function checkClickInfo(info, tab) {
for (let i of Object.keys(expectedClickInfo)) {
is(info[i], expectedClickInfo[i],
"click info " + i + " expected to be: " + expectedClickInfo[i] + " but was: " + info[i]);
}
is(expectedClickInfo.pageSrc, tab.url);
function checkClickInfo(result) {
for (let i of Object.keys(expectedClickInfo)) {
is(result.info[i], expectedClickInfo[i],
"click info " + i + " expected to be: " + expectedClickInfo[i] + " but was: " + info[i]);
}
let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
EventUtils.synthesizeMouseAtCenter(itemToSelect, {});
if (hasOnclickProperty) {
let {info, tab} = yield extension.awaitMessage("onclick");
if (expectedClickInfo) {
checkClickInfo(info, tab);
}
}
let {info, tab} = yield extension.awaitMessage("browser.contextMenus.onClicked");
if (expectedClickInfo) {
checkClickInfo(info, tab);
}
yield popupHiddenPromise;
is(expectedClickInfo.pageSrc, result.tab.url);
}
function confirmRadioGroupStates(expectedStates) {
let top = getTop();
let radioItems = top.getElementsByAttribute("type", "radio");
let radioGroup1 = top.getElementsByAttribute("label", "radio-group-1");
let radioGroup2 = top.getElementsByAttribute("label", "radio-group-2");
is(radioItems.length, 3, "there should be 3 radio items in the context menu");
is(radioGroup1.length, 1, "the first radio group should only have 1 radio item");
is(radioGroup2.length, 2, "the second radio group should only have 2 radio items");
is(radioGroup1[0].hasAttribute("checked"), expectedStates[0], `radio item 1 has state (checked=${expectedStates[0]})`);
is(radioGroup2[0].hasAttribute("checked"), expectedStates[1], `radio item 2 has state (checked=${expectedStates[1]})`);
is(radioGroup2[1].hasAttribute("checked"), expectedStates[2], `radio item 3 has state (checked=${expectedStates[2]})`);
}
function confirmCheckboxStates(expectedStates) {
let checkboxItems = getTop().getElementsByAttribute("type", "checkbox");
is(checkboxItems.length, 3, "there should be 3 checkbox items in the context menu");
is(checkboxItems[0].hasAttribute("checked"), expectedStates[0], `checkbox item 1 has state (checked=${expectedStates[0]})`);
is(checkboxItems[1].hasAttribute("checked"), expectedStates[1], `checkbox item 2 has state (checked=${expectedStates[1]})`);
is(checkboxItems[2].hasAttribute("checked"), expectedStates[2], `checkbox item 3 has state (checked=${expectedStates[2]})`);
}
yield openExtensionMenu();
let extensionMenuRoot = yield openExtensionContextMenu();
// Check some menu items
let top = getTop();
let items = top.getElementsByAttribute("label", "image");
let items = extensionMenuRoot.getElementsByAttribute("label", "image");
is(items.length, 1, "contextMenu item for image was found (context=image)");
let image = items[0];
items = top.getElementsByAttribute("label", "selection-edited");
items = extensionMenuRoot.getElementsByAttribute("label", "selection-edited");
is(items.length, 0, "contextMenu item for selection was not found (context=image)");
items = top.getElementsByAttribute("label", "parentToDel");
items = extensionMenuRoot.getElementsByAttribute("label", "parentToDel");
is(items.length, 0, "contextMenu item for removed parent was not found (context=image)");
items = top.getElementsByAttribute("label", "parent");
items = extensionMenuRoot.getElementsByAttribute("label", "parent");
is(items.length, 1, "contextMenu item for parent was found (context=image)");
is(items[0].childNodes[0].childNodes.length, 2, "child items for parent were found (context=image)");
// Click on ext-image item and check the click results
yield closeContextMenu(image, {
menuItemId: "ext-image",
mediaType: "image",
srcUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/ctxmenu-image.png",
pageUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html",
});
yield closeExtensionContextMenu(image);
// Test radio groups
yield openExtensionMenu();
confirmRadioGroupStates([true, false, false]);
items = getTop().getElementsByAttribute("type", "radio");
yield closeContextMenu(items[1]);
yield openExtensionMenu();
confirmRadioGroupStates([true, true, false]);
items = getTop().getElementsByAttribute("type", "radio");
yield closeContextMenu(items[2]);
yield openExtensionMenu();
confirmRadioGroupStates([true, false, true]);
items = getTop().getElementsByAttribute("type", "radio");
yield closeContextMenu(items[0]);
yield openExtensionMenu();
confirmRadioGroupStates([true, false, true]);
// Test checkboxes
items = getTop().getElementsByAttribute("type", "checkbox");
confirmCheckboxStates([false, true, false]);
yield closeContextMenu(items[0]);
yield openExtensionMenu();
confirmCheckboxStates([true, true, false]);
items = getTop().getElementsByAttribute("type", "checkbox");
yield closeContextMenu(items[2]);
yield openExtensionMenu();
confirmCheckboxStates([true, true, true]);
items = getTop().getElementsByAttribute("type", "checkbox");
yield closeContextMenu(items[0]);
yield openExtensionMenu();
confirmCheckboxStates([false, true, true]);
items = getTop().getElementsByAttribute("type", "checkbox");
yield closeContextMenu(items[2]);
let result = yield extension.awaitMessage("onclick");
checkClickInfo(result);
result = yield extension.awaitMessage("browser.contextMenus.onClicked");
checkClickInfo(result);
// Select some text
yield ContentTask.spawn(gBrowser.selectedBrowser, { }, function* (arg) {
@ -357,40 +193,51 @@ add_task(function* () {
});
// Bring up context menu again
yield openExtensionMenu();
extensionMenuRoot = yield openExtensionContextMenu();
// Check some menu items
top = getTop();
items = top.getElementsByAttribute("label", "Without onclick property");
items = extensionMenuRoot.getElementsByAttribute("label", "Without onclick property");
is(items.length, 1, "contextMenu item was found (context=page)");
yield closeContextMenu(items[0], {
yield closeExtensionContextMenu(items[0]);
expectedClickInfo = {
menuItemId: "ext-without-onclick",
pageUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html",
}, false /* hasOnclickProperty */);
};
result = yield extension.awaitMessage("browser.contextMenus.onClicked");
checkClickInfo(result);
// Bring up context menu again
yield openExtensionMenu();
extensionMenuRoot = yield openExtensionContextMenu();
// Check some menu items
top = getTop();
items = top.getElementsByAttribute("label", "selection is: 'just some text 123456789012345678901234567890...'");
items = extensionMenuRoot.getElementsByAttribute("label", "selection is: 'just some text 123456789012345678901234567890...'");
is(items.length, 1, "contextMenu item for selection was found (context=selection)");
let selectionItem = items[0];
items = top.getElementsByAttribute("label", "selection");
items = extensionMenuRoot.getElementsByAttribute("label", "selection");
is(items.length, 0, "contextMenu item label update worked (context=selection)");
yield closeContextMenu(selectionItem, {
yield closeExtensionContextMenu(selectionItem);
expectedClickInfo = {
menuItemId: "ext-selection",
pageUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html",
selectionText: "just some text 1234567890123456789012345678901234567890123456789012345678901234567890123456789012",
});
};
result = yield extension.awaitMessage("onclick");
checkClickInfo(result);
result = yield extension.awaitMessage("browser.contextMenus.onClicked");
checkClickInfo(result);
let contentAreaContextMenu = yield openContextMenu("#img1");
items = contentAreaContextMenu.getElementsByAttribute("ext-type", "top-level-menu");
is(items.length, 0, "top level item was not found (after removeAll()");
yield closeContextMenu();
yield extension.unload();
yield BrowserTestUtils.removeTab(tab1);
});

View File

@ -0,0 +1,74 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* () {
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
"http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html");
gBrowser.selectedTab = tab1;
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["contextMenus"],
},
background: function() {
browser.contextMenus.create({
title: "Checkbox",
type: "checkbox",
});
browser.contextMenus.create({
type: "separator",
});
browser.contextMenus.create({
title: "Checkbox",
type: "checkbox",
checked: true,
});
browser.contextMenus.create({
title: "Checkbox",
type: "checkbox",
});
browser.test.notifyPass("contextmenus-checkboxes");
},
});
yield extension.startup();
yield extension.awaitFinish("contextmenus-checkboxes");
function confirmCheckboxStates(extensionMenuRoot, expectedStates) {
let checkboxItems = extensionMenuRoot.getElementsByAttribute("type", "checkbox");
is(checkboxItems.length, 3, "there should be 3 checkbox items in the context menu");
is(checkboxItems[0].hasAttribute("checked"), expectedStates[0], `checkbox item 1 has state (checked=${expectedStates[0]})`);
is(checkboxItems[1].hasAttribute("checked"), expectedStates[1], `checkbox item 2 has state (checked=${expectedStates[1]})`);
is(checkboxItems[2].hasAttribute("checked"), expectedStates[2], `checkbox item 3 has state (checked=${expectedStates[2]})`);
return extensionMenuRoot.getElementsByAttribute("type", "checkbox");
}
let extensionMenuRoot = yield openExtensionContextMenu();
let items = confirmCheckboxStates(extensionMenuRoot, [false, true, false]);
yield closeExtensionContextMenu(items[0]);
extensionMenuRoot = yield openExtensionContextMenu();
items = confirmCheckboxStates(extensionMenuRoot, [true, true, false]);
yield closeExtensionContextMenu(items[2]);
extensionMenuRoot = yield openExtensionContextMenu();
items = confirmCheckboxStates(extensionMenuRoot, [true, true, true]);
yield closeExtensionContextMenu(items[0]);
extensionMenuRoot = yield openExtensionContextMenu();
items = confirmCheckboxStates(extensionMenuRoot, [false, true, true]);
yield closeExtensionContextMenu(items[2]);
yield extension.unload();
yield BrowserTestUtils.removeTab(tab1);
});

View File

@ -0,0 +1,69 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* () {
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
"http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html");
let encodedImageData = "iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAC4klEQVRYhdWXLWzbQBSADQtDAwsHC1tUhUxqfL67lk2tdn+OJg0ODU0rLByqgqINBY6tmlbn7LMTJ5FaFVVBk1G0oUGjG2jT2Y7jxmmcbU/6iJ+f36fz+e5sGP9riCGm9hB37RG+scd4Yo/wsDXCZyIE2xuXsce4bY+wXkAsQtzYmExrfFgvkJkRbkzo1ehoxx5iXcgI/9iYUGt8WH9MqDXEcmNChmEYrRCf2SHWeYgQx3x0tLNRIeKQLTtEFyJEep4NTuhk8BC+yMrwEE3+iozo42d8gK7FAOkMsRiiN8QhW2ttSK5QTfRRV4QoymVeJMvPvDp7gCZigD613MN6yRFA3SWarow9QB9LCfG+NeF9qCtjAKOSQjCqVKhfVsiHEQ+grgx/lRGqUihAc1uL8EFD+KCRO+GrF4J61phcoRoPoEzkYhZYpykh5sMb7kOdIeY+jHKur4QI4Feh4AFX1nVeLxrAvQchGsBz5ls6wa2QdwcvIcE2863bTH79KOvsz/uUYJsp+J0pSzNlDckVqqVGUAF+n6uS7txcOl6wot4JVy70ufDLy4pWLUQVPE81pRI0mGe9oxLMHSeohHvMs/STUNaUK6vDPCvOyxMFDx4achehRDJmHnydnkPww5OFfLxrGIZBFDyYl4LpMzlTQFIP6AQx86w2UeYBccFpJrcKv5L9eGDtUAU6RIELqsB74uynjy/UBRF1gS5BTFxwQT1wTiXoUg9MH7m/3NZRRoi5IJytUbMgzv4Wc832+oQkiKgEehmyMkkpKsFkQV11QsRJL5rJYBLItQgRaUZEmnoZXsomz3vGiWw+I9KMF9SVFOqZEemZekli1jN3U/UOqhHHvC6oWWGElhfSpGdOk6+O9prdwvtLj5BjRsQxdRnot+Zeifpy/2/0stktKTRNLmbk0mwXyl8253fyojj+8rxOHNAhjjm5n0/5OOCGOKBzkrMO0Z75lvSAzKlrF32Z/3z8BqLAn+yMV7VhAAAAAElFTkSuQmCC";
let decodedImageData = atob(encodedImageData);
const IMAGE_ARRAYBUFFER = Uint8Array.from(decodedImageData, byte => byte.charCodeAt(0)).buffer;
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["contextMenus"],
"icons": {
"18": "extension.png",
},
},
files: {
"extension.png": IMAGE_ARRAYBUFFER,
},
background: function() {
let menuitemId = browser.contextMenus.create({
title: "child-to-delete",
onclick: () => {
browser.contextMenus.remove(menuitemId);
},
});
browser.contextMenus.create({
title: "child",
});
browser.test.notifyPass("contextmenus-icons");
},
});
let confirmContextMenuIcon = (rootElement) => {
let expectedURL = new RegExp(String.raw`^moz-extension://[^/]+/extension\.png$`);
let imageUrl = rootElement.getAttribute("image");
ok(expectedURL.test(imageUrl), "The context menu should display the extension icon next to the root element");
};
yield extension.startup();
yield extension.awaitFinish("contextmenus-icons");
let extensionMenu = yield openExtensionContextMenu();
let contextMenu = document.getElementById("contentAreaContextMenu");
let topLevelMenuItem = contextMenu.getElementsByAttribute("ext-type", "top-level-menu")[0];
confirmContextMenuIcon(topLevelMenuItem);
let childToDelete = extensionMenu.getElementsByAttribute("label", "child-to-delete")[0];
yield closeExtensionContextMenu(childToDelete);
yield openExtensionContextMenu();
contextMenu = document.getElementById("contentAreaContextMenu");
topLevelMenuItem = contextMenu.getElementsByAttribute("label", "child")[0];
confirmContextMenuIcon(topLevelMenuItem);
yield closeContextMenu();
yield extension.unload();
yield BrowserTestUtils.removeTab(tab1);
});

View File

@ -0,0 +1,78 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* () {
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
"http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html");
gBrowser.selectedTab = tab1;
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["contextMenus"],
},
background: function() {
browser.contextMenus.create({
title: "radio-group-1",
type: "radio",
checked: true,
});
browser.contextMenus.create({
type: "separator",
});
browser.contextMenus.create({
title: "radio-group-2",
type: "radio",
});
browser.contextMenus.create({
title: "radio-group-2",
type: "radio",
});
browser.test.notifyPass("contextmenus-radio-groups");
},
});
yield extension.startup();
yield extension.awaitFinish("contextmenus-radio-groups");
function confirmRadioGroupStates(extensionMenuRoot, expectedStates) {
let radioItems = extensionMenuRoot.getElementsByAttribute("type", "radio");
let radioGroup1 = extensionMenuRoot.getElementsByAttribute("label", "radio-group-1");
let radioGroup2 = extensionMenuRoot.getElementsByAttribute("label", "radio-group-2");
is(radioItems.length, 3, "there should be 3 radio items in the context menu");
is(radioGroup1.length, 1, "the first radio group should only have 1 radio item");
is(radioGroup2.length, 2, "the second radio group should only have 2 radio items");
is(radioGroup1[0].hasAttribute("checked"), expectedStates[0], `radio item 1 has state (checked=${expectedStates[0]})`);
is(radioGroup2[0].hasAttribute("checked"), expectedStates[1], `radio item 2 has state (checked=${expectedStates[1]})`);
is(radioGroup2[1].hasAttribute("checked"), expectedStates[2], `radio item 3 has state (checked=${expectedStates[2]})`);
return extensionMenuRoot.getElementsByAttribute("type", "radio");
}
let extensionMenuRoot = yield openExtensionContextMenu();
let items = confirmRadioGroupStates(extensionMenuRoot, [true, false, false]);
yield closeExtensionContextMenu(items[1]);
extensionMenuRoot = yield openExtensionContextMenu();
items = confirmRadioGroupStates(extensionMenuRoot, [true, true, false]);
yield closeExtensionContextMenu(items[2]);
extensionMenuRoot = yield openExtensionContextMenu();
items = confirmRadioGroupStates(extensionMenuRoot, [true, false, true]);
yield closeExtensionContextMenu(items[0]);
extensionMenuRoot = yield openExtensionContextMenu();
items = confirmRadioGroupStates(extensionMenuRoot, [true, false, true]);
yield closeExtensionContextMenu(items[0]);
yield extension.unload();
yield BrowserTestUtils.removeTab(tab1);
});

View File

@ -0,0 +1,84 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* () {
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
"http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html");
// Install an extension.
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["contextMenus"],
},
background: function() {
browser.contextMenus.create({title: "a"});
browser.contextMenus.create({title: "b"});
browser.test.notifyPass("contextmenus-icons");
},
});
yield extension.startup();
yield extension.awaitFinish("contextmenus-icons");
// Open the context menu.
let contextMenu = yield openContextMenu("#img1");
// Confirm that the extension menu item exists.
let topLevelExtensionMenuItems = contextMenu.getElementsByAttribute("ext-type", "top-level-menu");
is(topLevelExtensionMenuItems.length, 1, "the top level extension menu item exists");
yield closeContextMenu();
// Uninstall the extension.
yield extension.unload();
// Open the context menu.
contextMenu = yield openContextMenu("#img1");
// Confirm that the extension menu item has been removed.
topLevelExtensionMenuItems = contextMenu.getElementsByAttribute("ext-type", "top-level-menu");
is(topLevelExtensionMenuItems.length, 0, "no top level extension menu items should exist");
yield closeContextMenu();
// Install a new extension.
extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["contextMenus"],
},
background: function() {
browser.contextMenus.create({title: "c"});
browser.contextMenus.create({title: "d"});
browser.test.notifyPass("contextmenus-uninstall-second-extension");
},
});
yield extension.startup();
yield extension.awaitFinish("contextmenus-uninstall-second-extension");
// Open the context menu.
contextMenu = yield openContextMenu("#img1");
// Confirm that only the new extension menu item is in the context menu.
topLevelExtensionMenuItems = contextMenu.getElementsByAttribute("ext-type", "top-level-menu");
is(topLevelExtensionMenuItems.length, 1, "only one top level extension menu item should exist");
// Close the context menu.
yield closeContextMenu();
// Uninstall the extension.
yield extension.unload();
// Open the context menu.
contextMenu = yield openContextMenu("#img1");
// Confirm that no extension menu items exist.
topLevelExtensionMenuItems = contextMenu.getElementsByAttribute("ext-type", "top-level-menu");
is(topLevelExtensionMenuItems.length, 0, "no top level extension menu items should exist");
yield closeContextMenu();
yield BrowserTestUtils.removeTab(tab1);
});

View File

@ -8,6 +8,8 @@
* getBrowserActionPopup getPageActionPopup
* closeBrowserAction closePageAction
* promisePopupShown promisePopupHidden
* openContextMenu closeContextMenu
* openExtensionContextMenu closeExtensionContextMenu
*/
var {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm");
@ -104,6 +106,44 @@ function closeBrowserAction(extension, win = window) {
return Promise.resolve();
}
function* openContextMenu(id) {
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
yield BrowserTestUtils.synthesizeMouseAtCenter(id, {type: "contextmenu", button: 2}, gBrowser.selectedBrowser);
yield popupShownPromise;
return contentAreaContextMenu;
}
function* closeContextMenu() {
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
contentAreaContextMenu.hidePopup();
yield popupHiddenPromise;
}
function* openExtensionContextMenu() {
let contextMenu = yield openContextMenu("#img1");
let topLevelMenu = contextMenu.getElementsByAttribute("ext-type", "top-level-menu");
// Return null if the extension only has one item and therefore no extension menu.
if (topLevelMenu.length == 0) {
return null;
}
let extensionMenu = topLevelMenu[0].childNodes[0];
let popupShownPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
EventUtils.synthesizeMouseAtCenter(extensionMenu, {});
yield popupShownPromise;
return extensionMenu;
}
function* closeExtensionContextMenu(itemToSelect) {
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
EventUtils.synthesizeMouseAtCenter(itemToSelect, {});
yield popupHiddenPromise;
}
function getPageActionPopup(extension, win = window) {
let panelId = makeWidgetId(extension.id) + "-panel";
return win.document.getElementById(panelId);

View File

@ -94,8 +94,6 @@ browser.jar:
skin/classic/browser/social/share-button-active.png (social/share-button-active.png)
skin/classic/browser/tabbrowser/alltabs.png (tabbrowser/alltabs.png)
skin/classic/browser/tabbrowser/alltabs-inverted.png (tabbrowser/alltabs-inverted.png)
skin/classic/browser/tabbrowser/connecting.png (tabbrowser/connecting.png)
skin/classic/browser/tabbrowser/connecting@2x.png (tabbrowser/connecting@2x.png)
skin/classic/browser/tabbrowser/newtab.svg (tabbrowser/newtab.svg)
skin/classic/browser/tabbrowser/newtab-inverted.svg (tabbrowser/newtab-inverted.svg)
skin/classic/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png)

View File

@ -145,8 +145,6 @@ browser.jar:
skin/classic/browser/tabbrowser/newtab@2x.png (tabbrowser/newtab@2x.png)
skin/classic/browser/tabbrowser/newtab-inverted.png (tabbrowser/newtab-inverted.png)
skin/classic/browser/tabbrowser/newtab-inverted@2x.png (tabbrowser/newtab-inverted@2x.png)
skin/classic/browser/tabbrowser/connecting.png (tabbrowser/connecting.png)
skin/classic/browser/tabbrowser/connecting@2x.png (tabbrowser/connecting@2x.png)
skin/classic/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png)
skin/classic/browser/tabbrowser/tab-active-middle@2x.png (tabbrowser/tab-active-middle@2x.png)
skin/classic/browser/tabbrowser/tab-arrow-left.png (tabbrowser/tab-arrow-left.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@ -100,6 +100,8 @@
skin/classic/browser/social/chat-icons.svg (../shared/social/chat-icons.svg)
skin/classic/browser/social/gear_default.png (../shared/social/gear_default.png)
skin/classic/browser/social/gear_clicked.png (../shared/social/gear_clicked.png)
skin/classic/browser/tabbrowser/connecting.png (../shared/tabbrowser/connecting.png)
skin/classic/browser/tabbrowser/connecting@2x.png (../shared/tabbrowser/connecting@2x.png)
skin/classic/browser/tabbrowser/crashed.svg (../shared/tabbrowser/crashed.svg)
skin/classic/browser/tabbrowser/pendingpaint.png (../shared/tabbrowser/pendingpaint.png)
* skin/classic/browser/tabbrowser/tab-audio.svg (../shared/tabbrowser/tab-audio.svg)

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -153,8 +153,6 @@ browser.jar:
skin/classic/browser/tabbrowser/newtab-XPVista7.svg (tabbrowser/newtab-XPVista7.svg)
skin/classic/browser/tabbrowser/newtab-inverted.svg (tabbrowser/newtab-inverted.svg)
skin/classic/browser/tabbrowser/newtab-inverted-XPVista7.svg (tabbrowser/newtab-inverted-XPVista7.svg)
skin/classic/browser/tabbrowser/connecting.png (tabbrowser/connecting.png)
skin/classic/browser/tabbrowser/connecting@2x.png (tabbrowser/connecting@2x.png)
skin/classic/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png)
skin/classic/browser/tabbrowser/tab-active-middle@2x.png (tabbrowser/tab-active-middle@2x.png)
skin/classic/browser/tabbrowser/tab-arrow-left.svg (tabbrowser/tab-arrow-left.svg)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@ -336,6 +336,22 @@ nsDOMWindowUtils::UpdateLayerTree()
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetContentViewerSize(uint32_t *aDisplayWidth, uint32_t *aDisplayHeight)
{
nsIPresShell* presShell = GetPresShell();
LayoutDeviceIntSize displaySize;
if (!presShell || !nsLayoutUtils::GetContentViewerSize(presShell->GetPresContext(), displaySize)) {
return NS_ERROR_FAILURE;
}
*aDisplayWidth = displaySize.width;
*aDisplayHeight = displaySize.height;
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetViewportInfo(uint32_t aDisplayWidth,
uint32_t aDisplayHeight,
@ -540,14 +556,17 @@ nsDOMWindowUtils::SetResolutionAndScaleTo(float aResolution)
}
NS_IMETHODIMP
nsDOMWindowUtils::SetRestoreResolution(float aResolution)
nsDOMWindowUtils::SetRestoreResolution(float aResolution,
uint32_t aDisplayWidth,
uint32_t aDisplayHeight)
{
nsIPresShell* presShell = GetPresShell();
if (!presShell) {
return NS_ERROR_FAILURE;
}
presShell->SetRestoreResolution(aResolution);
presShell->SetRestoreResolution(aResolution,
LayoutDeviceIntSize(aDisplayWidth, aDisplayHeight));
return NS_OK;
}

View File

@ -121,6 +121,11 @@ interface nsIDOMWindowUtils : nsISupports {
out uint32_t aWidth, out uint32_t aHeight,
out boolean aAutoSize);
/**
* Information about the window size in device pixels.
*/
void getContentViewerSize(out uint32_t aDisplayWidth, out uint32_t aDisplayHeight);
/**
* For any scrollable element, this allows you to override the
* visible region and draw more than what is visible, which is
@ -224,10 +229,15 @@ interface nsIDOMWindowUtils : nsISupports {
/**
* Set a resolution on the presShell which is the "restored" from history.
* The display dimensions are compared to their current values and used
* to scale the resolution value if necessary, e.g. if the device was
* rotated between saving and restoring of the session data.
* This resolution should be used when painting for the first time. Calling
* this too late may have no effect.
*/
void setRestoreResolution(in float aResolution);
void setRestoreResolution(in float aResolution,
in uint32_t aDisplayWidth,
in uint32_t aDisplayHeight);
/**
* Whether the resolution has been set by the user.

View File

@ -85,9 +85,13 @@ MobileViewportManager::Destroy()
}
void
MobileViewportManager::SetRestoreResolution(float aResolution)
MobileViewportManager::SetRestoreResolution(float aResolution,
LayoutDeviceIntSize aDisplaySize)
{
mRestoreResolution = Some(aResolution);
ScreenIntSize restoreDisplaySize = ViewAs<ScreenPixel>(aDisplaySize,
PixelCastJustification::LayoutDeviceIsScreenForBounds);
mRestoreDisplaySize = Some(restoreDisplaySize);
}
void
@ -167,6 +171,21 @@ MobileViewportManager::ClampZoom(const CSSToScreenScale& aZoom,
return zoom;
}
LayoutDeviceToLayerScale
MobileViewportManager::ScaleResolutionWithDisplayWidth(const LayoutDeviceToLayerScale& aRes,
const float& aDisplayWidthChangeRatio,
const CSSSize& aNewViewport,
const CSSSize& aOldViewport)
{
float cssViewportChangeRatio = (aOldViewport.width == 0)
? 1.0f : aNewViewport.width / aOldViewport.width;
LayoutDeviceToLayerScale newRes(aRes.scale * aDisplayWidthChangeRatio
/ cssViewportChangeRatio);
MVM_LOG("%p: Old resolution was %f, changed by %f/%f to %f\n", this, aRes.scale,
aDisplayWidthChangeRatio, cssViewportChangeRatio, newRes.scale);
return newRes;
}
CSSToScreenScale
MobileViewportManager::UpdateResolution(const nsViewportInfo& aViewportInfo,
const ScreenIntSize& aDisplaySize,
@ -180,7 +199,19 @@ MobileViewportManager::UpdateResolution(const nsViewportInfo& aViewportInfo,
if (mIsFirstPaint) {
CSSToScreenScale defaultZoom;
if (mRestoreResolution) {
defaultZoom = CSSToScreenScale(mRestoreResolution.value() * cssToDev.scale);
LayoutDeviceToLayerScale restoreResolution(mRestoreResolution.value());
if (mRestoreDisplaySize) {
CSSSize prevViewport = mDocument->GetViewportInfo(mRestoreDisplaySize.value()).GetSize();
float restoreDisplayWidthChangeRatio = (mRestoreDisplaySize.value().width > 0)
? (float)aDisplaySize.width / (float)mRestoreDisplaySize.value().width : 1.0f;
restoreResolution =
ScaleResolutionWithDisplayWidth(restoreResolution,
restoreDisplayWidthChangeRatio,
aViewport,
prevViewport);
}
defaultZoom = CSSToScreenScale(restoreResolution.scale * cssToDev.scale);
MVM_LOG("%p: restored zoom is %f\n", this, defaultZoom.scale);
defaultZoom = ClampZoom(defaultZoom, aViewportInfo);
} else {
@ -231,14 +262,9 @@ MobileViewportManager::UpdateResolution(const nsViewportInfo& aViewportInfo,
// tag is added or removed)
// 4. neither screen size nor CSS viewport changes
if (aDisplayWidthChangeRatio) {
float cssViewportChangeRatio = (mMobileViewportSize.width == 0)
? 1.0f : aViewport.width / mMobileViewportSize.width;
LayoutDeviceToLayerScale newRes(res.scale * aDisplayWidthChangeRatio.value()
/ cssViewportChangeRatio);
MVM_LOG("%p: Old resolution was %f, changed by %f/%f to %f\n", this, res.scale,
aDisplayWidthChangeRatio.value(), cssViewportChangeRatio, newRes.scale);
mPresShell->SetResolutionAndScaleTo(newRes.scale);
res = newRes;
res = ScaleResolutionWithDisplayWidth(res, aDisplayWidthChangeRatio.value(),
aViewport, mMobileViewportSize);
mPresShell->SetResolutionAndScaleTo(res.scale);
}
return ViewTargetAs<ScreenPixel>(cssToDev * res / ParentLayerToLayerScale(1),

View File

@ -29,8 +29,12 @@ public:
/* Provide a resolution to use during the first paint instead of the default
* resolution computed from the viewport info metadata. This is in the same
* "units" as the argument to nsDOMWindowUtils::SetResolutionAndScaleTo. */
void SetRestoreResolution(float aResolution);
* "units" as the argument to nsDOMWindowUtils::SetResolutionAndScaleTo.
* Also takes the previous display dimensions as they were at the time the
* resolution was stored in order to correctly adjust the resolution if the
* device was rotated in the meantime. */
void SetRestoreResolution(float aResolution,
mozilla::LayoutDeviceIntSize aDisplaySize);
/* Notify the MobileViewportManager that a reflow was requested in the
* presShell.*/
@ -58,14 +62,23 @@ private:
mozilla::CSSToScreenScale ClampZoom(const mozilla::CSSToScreenScale& aZoom,
const nsViewportInfo& aViewportInfo);
/* Helper to update the given resolution according to changed display and viewport widths. */
mozilla::LayoutDeviceToLayerScale
ScaleResolutionWithDisplayWidth(const mozilla::LayoutDeviceToLayerScale& aRes,
const float& aDisplayWidthChangeRatio,
const mozilla::CSSSize& aNewViewport,
const mozilla::CSSSize& aOldViewport);
/* Updates the presShell resolution and returns the new zoom. */
mozilla::CSSToScreenScale UpdateResolution(const nsViewportInfo& aViewportInfo,
const mozilla::ScreenIntSize& aDisplaySize,
const mozilla::CSSSize& aViewport,
const mozilla::Maybe<float>& aDisplayWidthChangeRatio);
/* Updates the scroll-position-clamping scrollport size */
void UpdateSPCSPS(const mozilla::ScreenIntSize& aDisplaySize,
const mozilla::CSSToScreenScale& aZoom);
/* Updates the displayport margins for the presShell's root scrollable frame */
void UpdateDisplayPortMargins();
@ -77,6 +90,7 @@ private:
mozilla::LayoutDeviceIntSize mDisplaySize;
mozilla::CSSSize mMobileViewportSize;
mozilla::Maybe<float> mRestoreResolution;
mozilla::Maybe<mozilla::ScreenIntSize> mRestoreDisplaySize;
};
#endif

View File

@ -48,6 +48,7 @@
#include "nsPresArena.h"
#include "nsMargin.h"
#include "nsFrameState.h"
#include "Units.h"
#include "Visibility.h"
#ifdef MOZ_B2G
@ -1446,7 +1447,8 @@ public:
* Used by session restore code to restore a resolution before the first
* paint.
*/
virtual void SetRestoreResolution(float aResolution) = 0;
virtual void SetRestoreResolution(float aResolution,
mozilla::LayoutDeviceIntSize aDisplaySize) = 0;
/**
* Returns whether we are in a DrawWindow() call that used the

View File

@ -5633,10 +5633,11 @@ float PresShell::GetCumulativeNonRootScaleResolution()
return resolution;
}
void PresShell::SetRestoreResolution(float aResolution)
void PresShell::SetRestoreResolution(float aResolution,
LayoutDeviceIntSize aDisplaySize)
{
if (mMobileViewportManager) {
mMobileViewportManager->SetRestoreResolution(aResolution);
mMobileViewportManager->SetRestoreResolution(aResolution, aDisplaySize);
}
}

View File

@ -240,7 +240,8 @@ public:
virtual bool ScaleToResolution() const override;
virtual float GetCumulativeResolution() override;
virtual float GetCumulativeNonRootScaleResolution() override;
virtual void SetRestoreResolution(float aResolution) override;
virtual void SetRestoreResolution(float aResolution,
mozilla::LayoutDeviceIntSize aDisplaySize) override;
//nsIViewObserver interface

View File

@ -753,11 +753,8 @@ SessionStore.prototype = {
// Save some data that'll help in adjusting the zoom level
// when restoring in a different screen orientation.
let viewportInfo = this._getViewportInfo(aWindow.outerWidth, aWindow.outerHeight, content);
scrolldata.zoom.autoSize = viewportInfo.autoSize;
log("onTabScroll() autoSize: " + scrolldata.zoom.autoSize);
scrolldata.zoom.windowWidth = aWindow.outerWidth;
log("onTabScroll() windowWidth: " + scrolldata.zoom.windowWidth);
scrolldata.zoom.displaySize = this._getContentViewerSize(content);
log("onTabScroll() displayWidth: " + scrolldata.zoom.displaySize.width);
// Save zoom and scroll data.
data.scrolldata = scrolldata;
@ -767,23 +764,16 @@ SessionStore.prototype = {
this.saveStateDelayed();
},
_getViewportInfo: function ss_getViewportInfo(aDisplayWidth, aDisplayHeight, aWindow) {
let viewportInfo = {};
let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom ={},
width = {}, height = {}, autoSize = {};
_getContentViewerSize: function ss_getContentViewerSize(aWindow) {
let displaySize = {};
let width = {}, height = {};
aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(
Ci.nsIDOMWindowUtils).getViewportInfo(aDisplayWidth, aDisplayHeight,
defaultZoom, allowZoom, minZoom, maxZoom, width, height, autoSize);
Ci.nsIDOMWindowUtils).getContentViewerSize(width, height);
viewportInfo.defaultZoom = defaultZoom.value;
viewportInfo.allowZoom = allowZoom.value;
viewportInfo.minZoom = maxZoom.value;
viewportInfo.maxZoom = maxZoom.value;
viewportInfo.width = width.value;
viewportInfo.height = height.value;
viewportInfo.autoSize = autoSize.value;
displaySize.width = width.value;
displaySize.height = height.value;
return viewportInfo;
return displaySize;
},
saveStateDelayed: function ss_saveStateDelayed() {
@ -1385,40 +1375,23 @@ SessionStore.prototype = {
},
/**
* Restores the zoom level of the window. This needs to be called before
* first paint/load (whichever comes first) to take any effect.
*/
* Restores the zoom level of the window. This needs to be called before
* first paint/load (whichever comes first) to take any effect.
*/
_restoreZoom: function ss_restoreZoom(aScrollData, aBrowser) {
if (aScrollData && aScrollData.zoom) {
let recalculatedZoom = this._recalculateZoom(aScrollData.zoom);
log("_restoreZoom(), resolution: " + recalculatedZoom);
if (aScrollData && aScrollData.zoom && aScrollData.zoom.displaySize) {
log("_restoreZoom(), resolution: " + aScrollData.zoom.resolution +
", old displayWidth: " + aScrollData.zoom.displaySize.width);
let utils = aBrowser.contentWindow.QueryInterface(
Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
// Restore zoom level.
utils.setRestoreResolution(recalculatedZoom);
utils.setRestoreResolution(aScrollData.zoom.resolution,
aScrollData.zoom.displaySize.width,
aScrollData.zoom.displaySize.height);
}
},
/**
* Recalculates the zoom level to account for a changed display width,
* e.g. because the device was rotated.
*/
_recalculateZoom: function ss_recalculateZoom(aZoomData) {
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
// Pages with "width=device-width" won't need any zoom level scaling.
if (!aZoomData.autoSize) {
let oldWidth = aZoomData.windowWidth;
let newWidth = browserWin.outerWidth;
if (oldWidth != newWidth && oldWidth > 0 && newWidth > 0) {
log("_recalculateZoom(), old resolution: " + aZoomData.resolution);
return newWidth / oldWidth * aZoomData.resolution;
}
}
return aZoomData.resolution;
},
/**
* Takes serialized scroll positions and restores them into the given browser.
*/

View File

@ -32,6 +32,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=810981
// Use something with enough content to allow for scrolling.
const URL = "http://example.org/chrome/mobile/android/tests/browser/chrome/basic_article_mobile.html";
// Something to test the zoom level scaling on rotation with.
const URL_desktop = "http://example.org/chrome/mobile/android/tests/browser/chrome/basic_article.html";
function dispatchUIEvent(browser, type) {
let event = browser.contentDocument.createEvent("UIEvents");
@ -169,6 +171,74 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=810981
BrowserApp.closeTab(BrowserApp.getTabForBrowser(browser));
});
add_task(function* test_sessionStoreZoomLevelRecalc() {
const ZOOM = 4.2;
const SCROLL_X = 42;
const SCROLL_Y = 42;
chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
let BrowserApp = chromeWin.BrowserApp;
// Creates a tab, sets a scroll position and zoom level and closes the tab.
function createAndRemoveTab() {
return Task.spawn(function () {
// Create a new tab.
tabScroll = BrowserApp.addTab(URL_desktop);
let browser = tabScroll.browser;
yield promiseBrowserEvent(browser, "pageshow");
// Modify scroll position and zoom level.
setZoomLevel(browser, ZOOM);
setScrollPosition(browser, SCROLL_X, SCROLL_Y);
yield promiseTabEvent(browser, "SSTabScrollCaptured");
// Check that we've actually scrolled and zoomed.
let ifreq = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor);
let utils = ifreq.getInterface(Ci.nsIDOMWindowUtils);
let scrollX = {}, scrollY = {}, zoom = {};
utils.getResolution(zoom);
utils.getScrollXY(false, scrollX, scrollY);
ok(fuzzyEquals(zoom.value, ZOOM), "zoom set correctly");
is(scrollX.value, SCROLL_X, "scrollX set correctly");
is(scrollY.value, SCROLL_Y, "scrollY set correctly");
// Remove the tab.
BrowserApp.closeTab(tabScroll);
yield promiseTabEvent(browser, "SSTabCloseProcessed");
});
}
yield createAndRemoveTab();
let state = ss.getClosedTabs(chromeWin);
let [{scrolldata}] = state;
is(scrolldata.scroll, SCROLL_X + "," + SCROLL_Y, "stored scroll position is correct");
ok(fuzzyEquals(scrolldata.zoom.resolution, ZOOM), "stored zoom level is correct");
// Pretend the zoom level was originally saved on a rotated device.
let closedTabData = ss.getClosedTabs(chromeWin)[0];
let displayWidth = scrolldata.zoom.displaySize.width;
let displayHeight = scrolldata.zoom.displaySize.height;
closedTabData.scrolldata.zoom.displaySize.width = displayHeight;
closedTabData.scrolldata.zoom.displaySize.height = displayWidth;
// Restore the closed tab.
let browser = ss.undoCloseTab(chromeWin, closedTabData);
yield promiseBrowserEvent(browser, "pageshow");
// Check the scroll position and zoom level.
let ifreq = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor);
let utils = ifreq.getInterface(Ci.nsIDOMWindowUtils);
let scrollX = {}, scrollY = {}, zoom = {};
utils.getResolution(zoom);
utils.getScrollXY(false, scrollX, scrollY);
ok(fuzzyEquals(zoom.value, ZOOM * displayWidth / displayHeight), "recalculated zoom restored correctly");
is(scrollX.value, SCROLL_X, "scrollX restored correctly");
is(scrollY.value, SCROLL_Y, "scrollY restored correctly");
// Remove the tab.
BrowserApp.closeTab(BrowserApp.getTabForBrowser(browser));
});
</script>
</head>
<body>