Merge autoland to mozilla-central a=merge

This commit is contained in:
Dorel Luca 2018-02-15 00:19:43 +02:00
commit 90035255ae
502 changed files with 44333 additions and 11120 deletions

View File

@ -1086,6 +1086,10 @@ pref("security.sandbox.gpu.level", 0);
pref("security.sandbox.content.level", 3);
#endif
#if defined(NIGHTLY_BUILD) && defined(XP_MACOSX) && defined(MOZ_SANDBOX)
pref("security.sandbox.mac.flash.enabled", false);
#endif
#if defined(XP_LINUX) && defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
// This pref is introduced as part of bug 742434, the naming is inspired from
// its Windows/Mac counterpart, but on Linux it's an integer which means:

View File

@ -137,8 +137,10 @@ appUpdater.prototype =
// true when updating is disabled by an administrator.
get updateDisabledAndLocked() {
return !this.updateEnabled &&
Services.prefs.prefIsLocked("app.update.enabled");
return (!this.updateEnabled &&
Services.prefs.prefIsLocked("app.update.enabled")) ||
(Services.policies &&
!Services.policies.isAllowed("appUpdate"));
},
// true when updating is enabled.

View File

@ -30,6 +30,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
LanguagePrompt: "resource://gre/modules/LanguagePrompt.jsm",
LightweightThemeConsumer: "resource://gre/modules/LightweightThemeConsumer.jsm",
LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
Log: "resource://gre/modules/Log.jsm",
LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
@ -1313,6 +1314,7 @@ var gBrowserInit = {
}
// Misc. inits.
new LightweightThemeConsumer(document);
TabletModeUpdater.init();
CombinedStopReload.ensureInitialized();
gPrivateBrowsingUI.init();

View File

@ -51,7 +51,6 @@
tabsintitlebar="true"
#endif
titlemenuseparator="&mainWindow.titlemodifiermenuseparator;"
lightweightthemes="true"
windowtype="navigator:browser"
macanimationtype="document"
screenX="4" screenY="4"

View File

@ -105,7 +105,7 @@ function openSelectPopup(selectPopup, mode = "key", selector = "select", win = w
return Promise.all([popupShownPromise, mousePromise]);
}
EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true, code: "ArrowDown" }, win);
EventUtils.synthesizeKey("KEY_ArrowDown", {altKey: true}, win);
return popupShownPromise;
}
@ -144,15 +144,15 @@ async function doSelectTests(contentType, content) {
is(selectPopup.childNodes[1].localName, "menuitem", "option is menuitem");
is(selectPopup.childNodes[1].getAttribute("label"), "One", "option label");
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
EventUtils.synthesizeKey("KEY_ArrowDown", {});
is(menulist.menuBoxObject.activeChild, menulist.getItemAtIndex(2), "Select item 2");
is(menulist.selectedIndex, isWindows ? 2 : 1, "Select item 2 selectedIndex");
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
EventUtils.synthesizeKey("KEY_ArrowDown", {});
is(menulist.menuBoxObject.activeChild, menulist.getItemAtIndex(3), "Select item 3");
is(menulist.selectedIndex, isWindows ? 3 : 1, "Select item 3 selectedIndex");
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
EventUtils.synthesizeKey("KEY_ArrowDown", {});
// On Windows, one can navigate on disabled menuitems
is(menulist.menuBoxObject.activeChild, menulist.getItemAtIndex(9),
@ -163,7 +163,7 @@ async function doSelectTests(contentType, content) {
is(menulist.getItemAtIndex(i).disabled, i >= 4 && i <= 7, "item " + i + " disabled");
}
EventUtils.synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
EventUtils.synthesizeKey("KEY_ArrowUp", {});
is(menulist.menuBoxObject.activeChild, menulist.getItemAtIndex(3), "Select item 3 again");
is(menulist.selectedIndex, isWindows ? 3 : 1, "Select item 3 selectedIndex");
@ -205,7 +205,7 @@ async function doSelectTests(contentType, content) {
is((await getClickEvents()), 1, "Tab away from select with no change - number of click events");
await openSelectPopup(selectPopup, "click");
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
EventUtils.synthesizeKey("KEY_ArrowDown", {});
await hideSelectPopup(selectPopup, "escape");
is((await getInputEvents()), isWindows ? 2 : 1, "Open and close with change - number of input events");
is((await getChangeEvents()), isWindows ? 2 : 1, "Open and close with change - number of change events");
@ -435,7 +435,7 @@ add_task(async function test_event_order() {
});
});
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
EventUtils.synthesizeKey("KEY_ArrowDown", {});
await hideSelectPopup(selectPopup, mode);
await eventsPromise;
}

View File

@ -203,7 +203,7 @@ function testOptionColors(index, item, menulist) {
// Press Down to move the selected item to the next item in the
// list and check the colors of this item when it's not selected.
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
EventUtils.synthesizeKey("KEY_ArrowDown", {});
if (expected.end) {
return;

View File

@ -6,9 +6,9 @@ function hideSelectPopup(selectPopup, mode = "enter", win = window) {
});
if (mode == "escape") {
EventUtils.synthesizeKey("KEY_Escape", { code: "Escape" }, win);
EventUtils.synthesizeKey("KEY_Escape", {}, win);
} else if (mode == "enter") {
EventUtils.synthesizeKey("KEY_Enter", { code: "Enter" }, win);
EventUtils.synthesizeKey("KEY_Enter", {}, win);
} else if (mode == "click") {
EventUtils.synthesizeMouseAtCenter(selectPopup.lastChild, { }, win);
}

View File

@ -19,14 +19,14 @@ add_task(async function test_content_and_chrome_selection() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage);
await BrowserTestUtils.synthesizeMouse("#textarea", 0, 0, {}, gBrowser.selectedBrowser);
await BrowserTestUtils.synthesizeKey("KEY_ArrowRight",
{shiftKey: true, ctrlKey: true, code: "ArrowRight"}, gBrowser.selectedBrowser);
{shiftKey: true, ctrlKey: true}, gBrowser.selectedBrowser);
selectedText = DOMWindowUtils.GetSelectionAsPlaintext();
is(selectedText, "Write something here", "The macOS services got the selected content text");
gURLBar.value = "test.mozilla.org";
await gURLBar.focus();
await BrowserTestUtils.synthesizeKey("KEY_ArrowRight",
{shiftKey: true, ctrlKey: true, code: "ArrowRight"}, gBrowser.selectedBrowser);
{shiftKey: true, ctrlKey: true}, gBrowser.selectedBrowser);
selectedText = DOMWindowUtils.GetSelectionAsPlaintext();
is(selectedText, "test.mozilla.org", "The macOS services got the selected chrome text");
@ -51,12 +51,12 @@ add_task(async function test_active_selection_switches_properly() {
let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage1);
await BrowserTestUtils.synthesizeMouse("#textarea", 0, 0, {}, gBrowser.selectedBrowser);
await BrowserTestUtils.synthesizeKey("KEY_ArrowRight",
{shiftKey: true, ctrlKey: true, code: "ArrowRight"}, gBrowser.selectedBrowser);
{shiftKey: true, ctrlKey: true}, gBrowser.selectedBrowser);
let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage2);
await BrowserTestUtils.synthesizeMouse("#textarea", 0, 0, {}, gBrowser.selectedBrowser);
await BrowserTestUtils.synthesizeKey("KEY_ArrowRight",
{shiftKey: true, ctrlKey: true, code: "ArrowRight"}, gBrowser.selectedBrowser);
{shiftKey: true, ctrlKey: true}, gBrowser.selectedBrowser);
await BrowserTestUtils.switchTab(gBrowser, tab1);
selectedText = DOMWindowUtils.GetSelectionAsPlaintext();

View File

@ -54,7 +54,7 @@ add_task(async function() {
resolve();
}, true);
sendAsyncMessage("Test:SendKey", { key: "c", code: "KeyC" });
sendAsyncMessage("Test:SendKey", {key: "c"});
});
selection.modify("move", "right", "line");
@ -73,7 +73,7 @@ add_task(async function() {
resolve();
}, true);
sendAsyncMessage("Test:SendKey", {key: "v", code: "KeyV"});
sendAsyncMessage("Test:SendKey", {key: "v"});
});
Assert.equal(main.innerHTML, "Test <b>Bold</b> After Textt <b>Bold</b>", "Copy and paste html");
@ -92,7 +92,7 @@ add_task(async function() {
resolve();
}, true);
sendAsyncMessage("Test:SendKey", {key: "x", code: "KeyX"});
sendAsyncMessage("Test:SendKey", {key: "x"});
});
selection.modify("move", "left", "line");
@ -111,7 +111,7 @@ add_task(async function() {
resolve();
}, true);
sendAsyncMessage("Test:SendKey", {key: "v", code: "KeyV"});
sendAsyncMessage("Test:SendKey", {key: "v"});
});
Assert.equal(main.innerHTML, "<i>Italic</i> Test <b>Bold</b> After<b></b>",
@ -155,7 +155,7 @@ add_task(async function() {
resolve();
}, true);
sendAsyncMessage("Test:SendKey", {key: "v", code: "KeyV"});
sendAsyncMessage("Test:SendKey", {key: "v"});
});
// The new content should now include an image.

View File

@ -77,7 +77,7 @@ if (!navigator.platform.includes("Mac")) {
let filePopup = document.getElementById("menu_FilePopup");
popupShown = BrowserTestUtils.waitForEvent(filePopup, "popupshown");
if (navigator.platform.includes("Win")) {
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
EventUtils.synthesizeKey("KEY_ArrowDown", {});
}
await popupShown;

View File

@ -122,10 +122,7 @@ add_task(async function() {
// the message and adds its event listener before synthesizing the key.
let keydownPromises = promiseEvent("keydown");
await keydownPromises[0];
EventUtils.synthesizeKey("KEY_ArrowDown", {
type: "keydown",
code: "ArrowDown",
});
EventUtils.synthesizeKey("KEY_ArrowDown", {type: "keydown"});
await keydownPromises[1];
// urlbar.getPanelHeight

View File

@ -0,0 +1,15 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
## Firefox Brand
##
## Firefox must be treated as a brand, and kept in English.
## It cannot be:
## - Declined to adapt to grammatical case.
## - Transliterated.
## - Translated.
##
## Reference: https://www.mozilla.org/styleguide/communications/translation/
-brand-short-name = Firefox Developer Edition

View File

@ -3,6 +3,8 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
[localization] @AB_CD@.jar:
branding (en-US/**/*.ftl)
@AB_CD@.jar:
% locale branding @AB_CD@ %locale/branding/

View File

@ -0,0 +1,15 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
## Firefox Brand
##
## Firefox must be treated as a brand, and kept in English.
## It cannot be:
## - Declined to adapt to grammatical case.
## - Transliterated.
## - Translated.
##
## Reference: https://www.mozilla.org/styleguide/communications/translation/
-brand-short-name = Nightly

View File

@ -3,6 +3,8 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
[localization] @AB_CD@.jar:
branding (en-US/**/*.ftl)
@AB_CD@.jar:
% locale branding @AB_CD@ %locale/branding/

View File

@ -0,0 +1,15 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
## Firefox Brand
##
## Firefox must be treated as a brand, and kept in English.
## It cannot be:
## - Declined to adapt to grammatical case.
## - Transliterated.
## - Translated.
##
## Reference: https://www.mozilla.org/styleguide/communications/translation/
-brand-short-name = Firefox

View File

@ -3,6 +3,8 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
[localization] @AB_CD@.jar:
branding (%*.ftl)
@AB_CD@.jar:
% locale branding @AB_CD@ %locale/branding/

View File

@ -0,0 +1,15 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
## Firefox Brand
##
## Firefox must be treated as a brand, and kept in English.
## It cannot be:
## - Declined to adapt to grammatical case.
## - Transliterated.
## - Translated.
##
## Reference: https://www.mozilla.org/styleguide/communications/translation/
-brand-short-name = Nightly

View File

@ -3,6 +3,8 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
[localization] @AB_CD@.jar:
branding (en-US/**/*.ftl)
@AB_CD@.jar:
% locale branding @AB_CD@ %locale/branding/

View File

@ -17,12 +17,12 @@ add_task(async function testUpDownKeys() {
for (let button of buttons) {
if (button.disabled)
continue;
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
EventUtils.synthesizeKey("KEY_ArrowDown", {});
Assert.equal(document.commandDispatcher.focusedElement, button,
"The correct button should be focused after navigating downward");
}
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
EventUtils.synthesizeKey("KEY_ArrowDown", {});
Assert.equal(document.commandDispatcher.focusedElement, buttons[0],
"Pressing upwards should cycle around and select the first button again");
@ -30,7 +30,7 @@ add_task(async function testUpDownKeys() {
let button = buttons[i];
if (button.disabled)
continue;
EventUtils.synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
EventUtils.synthesizeKey("KEY_ArrowUp", {});
Assert.equal(document.commandDispatcher.focusedElement, button,
"The first button should be focused after navigating upward");
}
@ -48,7 +48,7 @@ add_task(async function testEnterKeyBehaviors() {
let buttons = PanelView.forNode(PanelUI.mainView).getNavigableElements();
// Navigate to the 'Help' button, which points to a subview.
EventUtils.synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
EventUtils.synthesizeKey("KEY_ArrowUp", {});
let focusedElement = document.commandDispatcher.focusedElement;
Assert.equal(focusedElement, buttons[buttons.length - 1],
"The last button should be focused after navigating upward");
@ -56,10 +56,10 @@ add_task(async function testEnterKeyBehaviors() {
promise = BrowserTestUtils.waitForEvent(PanelUI.helpView, "ViewShown");
// Make sure the Help button is in focus.
while (!focusedElement || !focusedElement.id || focusedElement.id != kHelpButtonId) {
EventUtils.synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
EventUtils.synthesizeKey("KEY_ArrowUp", {});
focusedElement = document.commandDispatcher.focusedElement;
}
EventUtils.synthesizeKey("VK_RETURN", { code: "Enter" });
EventUtils.synthesizeKey("KEY_Enter", {});
await promise;
let helpButtons = PanelView.forNode(PanelUI.helpView).getNavigableElements();
@ -71,33 +71,33 @@ add_task(async function testEnterKeyBehaviors() {
let button = helpButtons[i];
if (button.disabled)
continue;
EventUtils.synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
EventUtils.synthesizeKey("KEY_ArrowUp", {});
focusedElement = document.commandDispatcher.focusedElement;
Assert.equal(focusedElement, button, "The first button should be focused after navigating upward");
}
// Make sure the back button is in focus again.
while (focusedElement != helpButtons[0]) {
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
EventUtils.synthesizeKey("KEY_ArrowDown", {});
focusedElement = document.commandDispatcher.focusedElement;
}
// The first button is the back button. Hittin Enter should navigate us back.
promise = BrowserTestUtils.waitForEvent(PanelUI.mainView, "ViewShown");
EventUtils.synthesizeKey("VK_RETURN", { code: "Enter" });
EventUtils.synthesizeKey("KEY_Enter", {});
await promise;
// Let's test a 'normal' command button.
focusedElement = document.commandDispatcher.focusedElement;
const kFindButtonId = "appMenu-find-button";
while (!focusedElement || !focusedElement.id || focusedElement.id != kFindButtonId) {
EventUtils.synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
EventUtils.synthesizeKey("KEY_ArrowUp", {});
focusedElement = document.commandDispatcher.focusedElement;
}
Assert.equal(focusedElement.id, kFindButtonId, "Find button should be selected");
promise = promisePanelHidden(window);
EventUtils.synthesizeKey("VK_RETURN", { code: "Enter" });
EventUtils.synthesizeKey("KEY_Enter", {});
await promise;
Assert.ok(!gFindBar.hidden, "Findbar should have opened");
@ -112,7 +112,7 @@ add_task(async function testLeftRightKeys() {
// Navigate to the 'Help' button, which points to a subview.
let focusedElement = document.commandDispatcher.focusedElement;
while (!focusedElement || !focusedElement.id || focusedElement.id != kHelpButtonId) {
EventUtils.synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
EventUtils.synthesizeKey("KEY_ArrowUp", {});
focusedElement = document.commandDispatcher.focusedElement;
}
Assert.equal(focusedElement.id, kHelpButtonId, "The last button should be focused after navigating upward");
@ -120,12 +120,12 @@ add_task(async function testLeftRightKeys() {
// Hitting ArrowRight on a button that points to a subview should navigate us
// there.
promise = BrowserTestUtils.waitForEvent(PanelUI.helpView, "ViewShown");
EventUtils.synthesizeKey("KEY_ArrowRight", { code: "ArrowRight" });
EventUtils.synthesizeKey("KEY_ArrowRight", {});
await promise;
// Hitting ArrowLeft should navigate us back.
promise = BrowserTestUtils.waitForEvent(PanelUI.mainView, "ViewShown");
EventUtils.synthesizeKey("KEY_ArrowLeft", { code: "ArrowLeft" });
EventUtils.synthesizeKey("KEY_ArrowLeft", {});
await promise;
focusedElement = document.commandDispatcher.focusedElement;
@ -147,12 +147,12 @@ add_task(async function testTabKey() {
for (let button of buttons) {
if (button.disabled)
continue;
EventUtils.synthesizeKey("KEY_Tab", { code: "Tab" });
EventUtils.synthesizeKey("KEY_Tab", {});
Assert.equal(document.commandDispatcher.focusedElement, button,
"The correct button should be focused after tabbing");
}
EventUtils.synthesizeKey("KEY_Tab", { code: "Tab" });
EventUtils.synthesizeKey("KEY_Tab", {});
Assert.equal(document.commandDispatcher.focusedElement, buttons[0],
"Pressing tab should cycle around and select the first button again");
@ -160,12 +160,12 @@ add_task(async function testTabKey() {
let button = buttons[i];
if (button.disabled)
continue;
EventUtils.synthesizeKey("Tab", { code: "Tab", shiftKey: true });
EventUtils.synthesizeKey("Tab", {shiftKey: true});
Assert.equal(document.commandDispatcher.focusedElement, button,
"The correct button should be focused after shift + tabbing");
}
EventUtils.synthesizeKey("KEY_Tab", { code: "Tab", shiftKey: true });
EventUtils.synthesizeKey("KEY_Tab", {shiftKey: true});
Assert.equal(document.commandDispatcher.focusedElement, buttons[buttons.length - 1],
"Pressing shift + tab should cycle around and select the last button again");
@ -186,9 +186,9 @@ add_task(async function testInterleavedTabAndArrowKeys() {
if (button.disabled)
continue;
if (tab) {
EventUtils.synthesizeKey("KEY_Tab", { code: "Tab" });
EventUtils.synthesizeKey("KEY_Tab", {});
} else {
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
EventUtils.synthesizeKey("KEY_ArrowDown", {});
}
tab = !tab;
}
@ -212,7 +212,7 @@ add_task(async function testSpaceDownAfterTabNavigation() {
for (button of buttons) {
if (button.disabled)
continue;
EventUtils.synthesizeKey("KEY_Tab", { code: "Tab" });
EventUtils.synthesizeKey("KEY_Tab", {});
if (button.id == kHelpButtonId) {
break;
}
@ -224,7 +224,7 @@ add_task(async function testSpaceDownAfterTabNavigation() {
// Pressing down space on a button that points to a subview should navigate us
// there, before keyup.
promise = BrowserTestUtils.waitForEvent(PanelUI.helpView, "ViewShown");
EventUtils.synthesizeKey(" ", { code: "Space", type: "keydown" });
EventUtils.synthesizeKey(" ", {type: "keydown"});
await promise;
promise = promisePanelHidden(window);

View File

@ -20,6 +20,12 @@ const POLICIES_FILENAME = "policies.json";
// and set PREF_ALTERNATE_PATH in firefox.js as:
// /your/repo/browser/components/enterprisepolicies/helpers/sample.json
const PREF_ALTERNATE_PATH = "browser.policies.alternatePath";
// For testing, we may want to set PREF_ALTERNATE_PATH to point to a file
// relative to the test root directory. In order to enable this, the string
// below may be placed at the beginning of that preference value and it will
// be replaced with the path to the test root directory.
const MAGIC_TEST_ROOT_PREFIX = "<test-root>";
const PREF_TEST_ROOT = "mochitest.testRoot";
// This pref is meant to be temporary: it will only be used while we're
// testing this feature without rolling it out officially. When the
@ -319,6 +325,18 @@ class JSONPoliciesProvider {
// the admin-provided policies by changing the user-controlled prefs.
// This pref is only meant for tests, so it's fine to use this extra
// synchronous configFile.exists() above.
if (alternatePath.startsWith(MAGIC_TEST_ROOT_PREFIX)) {
// Intentionally not using a default value on this pref lookup. If no
// test root is set, we are not currently testing and this function
// should throw rather than returning something.
let testRoot = Services.prefs.getStringPref(PREF_TEST_ROOT);
let relativePath = alternatePath.substring(MAGIC_TEST_ROOT_PREFIX.length);
if (AppConstants.platform == "win") {
relativePath = relativePath.replace(/\//g, "\\");
}
alternatePath = testRoot + relativePath;
}
configFile = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsIFile);
configFile.initWithPath(alternatePath);

View File

@ -37,6 +37,14 @@ this.Policies = {
}
},
"DisableAppUpdate": {
onBeforeAddons(manager, param) {
if (param == true) {
manager.disallowFeature("appUpdate");
}
}
},
"display_menu_bar": {
onBeforeUIStartup(manager, param) {
if (param == true) {

View File

@ -10,6 +10,15 @@
"enum": [true]
},
"DisableAppUpdate": {
"description": "Prevent the browser from updating.",
"first_available": "60.0",
"enterprise_only": true,
"type": "boolean",
"enum": [true]
},
"display_menu_bar": {
"description": "Causes the menu bar to be displayed by default.",
"first_available": "60.0",

View File

@ -11,6 +11,7 @@ support-files =
[browser_policies_setAndLockPref_API.js]
[browser_policies_simple_policies.js]
[browser_policies_validate_and_parse_API.js]
[browser_policy_app_update.js]
[browser_policy_block_set_desktop_background.js]
[browser_policy_default_browser_check.js]
[browser_policy_disable_fxscreenshots.js]

View File

@ -0,0 +1,19 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var updateService = Cc["@mozilla.org/updates/update-service;1"].
getService(Ci.nsIApplicationUpdateService);
// This test is intended to ensure that nsIUpdateService::canCheckForUpdates
// is true before the "DisableAppUpdate" policy is applied. Testing that
// nsIUpdateService::canCheckForUpdates is false after the "DisableAppUpdate"
// policy is applied needs to occur in a different test since the policy does
// not properly take effect unless it is applied during application startup.
add_task(async function test_updates_pre_policy() {
is(Services.policies.isAllowed("appUpdate"), true,
"Since no policies have been set, appUpdate should be allowed by default");
is(updateService.canCheckForUpdates, true,
"Should be able to check for updates before any policies are in effect.");
});

View File

@ -0,0 +1,8 @@
[DEFAULT]
prefs =
browser.policies.enabled=true
browser.policies.alternatePath='<test-root>/browser/components/enterprisepolicies/tests/browser/disable_app_update/config_disable_app_update.json'
support-files =
config_disable_app_update.json
[browser_policy_disable_app_update.js]

View File

@ -0,0 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var updateService = Cc["@mozilla.org/updates/update-service;1"].
getService(Ci.nsIApplicationUpdateService);
add_task(async function test_updates_post_policy() {
is(Services.policies.isAllowed("appUpdate"), false,
"appUpdate should be disabled by policy.");
is(updateService.canCheckForUpdates, false,
"Should not be able to check for updates with DisableAppUpdate enabled.");
});

View File

@ -0,0 +1,5 @@
{
"policies": {
"DisableAppUpdate": true
}
}

View File

@ -8,5 +8,6 @@ with Files("**"):
BUG_COMPONENT = ("Firefox", "General")
BROWSER_CHROME_MANIFESTS += [
'browser/browser.ini'
'browser/browser.ini',
'browser/disable_app_update/browser.ini'
]

View File

@ -22,9 +22,9 @@ async function testContextmenu(menuitem) {
EventUtils.synthesizeMouseAtCenter(menuitem, {type: "contextmenu", button: 2});
await promiseEvent;
let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null);
EventUtils.synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
EventUtils.synthesizeKey("KEY_ArrowDown", {});
BrowserTestUtils.waitForEvent(menuitem, "DOMMenuItemActive");
EventUtils.synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
EventUtils.synthesizeKey("KEY_ArrowDown", {});
BrowserTestUtils.waitForEvent(menuitem, "DOMMenuItemActive");
EventUtils.sendKey("return");
let newTab = await promiseTabOpened;

View File

@ -379,7 +379,8 @@
label="&translateWebPages.label;." accesskey="&translateWebPages.accesskey;"
onsyncfrompreference="return gMainPane.updateButtons('translateButton',
'browser.translation.detectLanguage');"/>
<hbox id="bingAttribution" hidden="true">
<hbox id="bingAttribution" hidden="true" align="center">
<separator orient="vertical" class="thin"/>
<label>&translation.options.attribution.beforeLogo;</label>
<separator orient="vertical" class="thin"/>
<image id="translationAttributionImage" aria-label="Microsoft Translator"

View File

@ -114,7 +114,7 @@ add_task(async function test_nested() {
await promiseBrowserLoaded(browser);
// Modify the input field's value.
await BrowserTestUtils.synthesizeKey("m", {code: "KeyM"}, browser);
await BrowserTestUtils.synthesizeKey("m", {}, browser);
// Remove the tab and check that we stored form data correctly.
await promiseRemoveTab(tab);
@ -151,7 +151,7 @@ add_task(async function test_design_mode() {
await promiseBrowserLoaded(browser);
// Modify the document content.
await BrowserTestUtils.synthesizeKey("m", {code: "KeyM"}, browser);
await BrowserTestUtils.synthesizeKey("m", {}, browser);
// Close and restore the tab.
await promiseRemoveTab(tab);

View File

@ -119,7 +119,7 @@ function triggerAutofillAndCheckProfile(profile) {
promises.push(checkFieldAutofilled);
}
// Press Enter key and trigger form autofill.
synthesizeKey("KEY_Enter", {code: "Enter"});
synthesizeKey("KEY_Enter", {});
return Promise.all(promises);
}
@ -243,10 +243,10 @@ function initPopupListener() {
async function triggerPopupAndHoverItem(fieldSelector, selectIndex) {
await focusAndWaitForFieldsIdentified(fieldSelector);
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
for (let i = 0; i <= selectIndex; i++) {
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
}
await notifySelectedIndex(selectIndex);
}

View File

@ -39,7 +39,7 @@ async function setupAddressStorage() {
add_task(async function check_autocomplete_on_autofocus_field() {
await setupAddressStorage();
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
JSON.stringify({primary: address.organization, secondary: address["street-address"]})

View File

@ -54,7 +54,7 @@ add_task(async function history_only_menu_checking() {
await setupFormHistory();
await setInput("#tel", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(["+1234567890"], false);
});
@ -67,7 +67,7 @@ add_task(async function all_saved_fields_less_than_threshold() {
});
await setInput("#email", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(["foo@mozilla.com"], false);
@ -79,7 +79,7 @@ add_task(async function check_menu_when_both_existed() {
await setupAddressStorage();
await setInput("#organization", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
JSON.stringify({
@ -89,7 +89,7 @@ add_task(async function check_menu_when_both_existed() {
));
await setInput("#street-address", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
JSON.stringify({
@ -99,7 +99,7 @@ add_task(async function check_menu_when_both_existed() {
));
await setInput("#tel", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
JSON.stringify({
@ -109,7 +109,7 @@ add_task(async function check_menu_when_both_existed() {
));
await setInput("#address-line1", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
JSON.stringify({
@ -122,7 +122,7 @@ add_task(async function check_menu_when_both_existed() {
// Display history search result if no matched data in addresses.
add_task(async function check_fallback_for_mismatched_field() {
await setInput("#email", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(["foo@mozilla.com"], false);
});
@ -134,7 +134,7 @@ add_task(async function check_search_result_for_pref_off() {
});
await setInput("#tel", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(["+1234567890"], false);
@ -144,7 +144,7 @@ add_task(async function check_search_result_for_pref_off() {
// Autofill the address from dropdown menu.
add_task(async function check_fields_after_form_autofill() {
const focusedInput = await setInput("#organization", "Moz");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
JSON.stringify({
@ -152,16 +152,16 @@ add_task(async function check_fields_after_form_autofill() {
secondary: FormAutofillUtils.toOneLineAddress(address["street-address"]),
})
).slice(1));
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await triggerAutofillAndCheckProfile(MOCK_STORAGE[1]);
synthesizeKey("KEY_Escape", {code: "Escape"});
synthesizeKey("KEY_Escape", {});
is(focusedInput.value, "Mozilla", "Filled field shouldn't be reverted by ESC key");
});
// Fallback to history search after autofill address.
add_task(async function check_fallback_after_form_autofill() {
await setInput("#tel", "", true);
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(["+1234567890"], false);
});
@ -171,7 +171,7 @@ add_task(async function check_form_autofill_resume() {
document.querySelector("#tel").blur();
document.querySelector("#form1").reset();
await setInput("#tel", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
JSON.stringify({

View File

@ -62,7 +62,7 @@ add_task(async function history_only_menu_checking() {
await setupFormHistory();
await setInput("#cc-exp-year", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(["2023"], false);
});
@ -72,7 +72,7 @@ add_task(async function all_saved_fields_less_than_threshold() {
await addCreditCard(reducedMockRecord);
await setInput("#cc-name", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries([reducedMockRecord].map(patchRecordCCNumber).map(cc => JSON.stringify({
primary: cc["cc-name"],
@ -87,7 +87,7 @@ add_task(async function check_menu_when_both_existed() {
await setupCreditCardStorage();
await setInput("#cc-number", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
primaryAffix: cc.ccNumberFmt.affix,
@ -96,7 +96,7 @@ add_task(async function check_menu_when_both_existed() {
})));
await setInput("#cc-name", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
primary: cc["cc-name"],
@ -104,7 +104,7 @@ add_task(async function check_menu_when_both_existed() {
})));
await setInput("#cc-exp-year", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
primary: cc["cc-exp-year"],
@ -112,7 +112,7 @@ add_task(async function check_menu_when_both_existed() {
})));
await setInput("#cc-exp-month", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
primary: cc["cc-exp-month"],
@ -127,7 +127,7 @@ add_task(async function check_fallback_for_mismatched_field() {
await addCreditCard(reducedMockRecord);
await setInput("#cc-exp-year", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(["2023"], false);
@ -143,7 +143,7 @@ add_task(async function check_search_result_for_pref_off() {
});
await setInput("#cc-name", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(["John Smith"], false);
@ -154,21 +154,21 @@ add_task(async function check_search_result_for_pref_off() {
add_task(async function check_fields_after_form_autofill() {
await setInput("#cc-exp-year", 202);
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.slice(1).map(patchRecordCCNumber).map(cc => JSON.stringify({
primary: cc["cc-exp-year"],
secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
})));
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await triggerAutofillAndCheckProfile(MOCK_STORAGE[1]);
});
// Fallback to history search after autofill address.
add_task(async function check_fallback_after_form_autofill() {
await setInput("#cc-name", "", true);
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(["John Smith"], false);
});
@ -179,7 +179,7 @@ add_task(async function check_form_autofill_resume() {
document.querySelector("#form1").reset();
await setInput("#cc-name", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
primary: cc["cc-name"],

View File

@ -71,7 +71,7 @@ add_task(async function simple_clear() {
await triggerAutofillAndCheckProfile(MOCK_ADDR_STORAGE[0]);
await triggerPopupAndHoverItem("#tel", 0);
synthesizeKey("KEY_Enter", {code: "Enter"});
synthesizeKey("KEY_Enter", {});
checkIsFormCleared();
});
@ -80,7 +80,7 @@ add_task(async function clear_adapted_record() {
await triggerAutofillAndCheckProfile(MOCK_ADDR_STORAGE[0]);
await triggerPopupAndHoverItem("#street-address", 0);
synthesizeKey("KEY_Enter", {code: "Enter"});
synthesizeKey("KEY_Enter", {});
checkIsFormCleared();
});
@ -91,7 +91,7 @@ add_task(async function clear_modified_form() {
await setInput("#tel", "+1111111111", true);
await triggerPopupAndHoverItem("#street-address", 0);
synthesizeKey("KEY_Enter", {code: "Enter"});
synthesizeKey("KEY_Enter", {});
checkIsFormCleared({tel: "+1111111111"});
});
@ -103,7 +103,7 @@ add_task(async function clear_distinct_section() {
await triggerPopupAndHoverItem("#organization", 0);
await triggerAutofillAndCheckProfile(MOCK_ADDR_STORAGE[0]);
await triggerPopupAndHoverItem("#street-address", 0);
synthesizeKey("KEY_Enter", {code: "Enter"});
synthesizeKey("KEY_Enter", {});
for (const [id, val] of Object.entries(MOCK_CC_STORAGE[0])) {
const element = document.getElementById(id);
@ -115,7 +115,7 @@ add_task(async function clear_distinct_section() {
}
await triggerPopupAndHoverItem("#cc-name", 0);
synthesizeKey("KEY_Enter", {code: "Enter"});
synthesizeKey("KEY_Enter", {});
checkIsFormCleared();
});

View File

@ -51,12 +51,12 @@ add_task(async function history_only_menu_checking() {
await setupFormHistory();
await setInput("#cc-number", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(["1234000056780000"], false);
await setInput("#cc-name", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await notExpectPopup();
});
@ -65,7 +65,7 @@ add_task(async function check_menu_when_both_with_autocomplete_off() {
await setupCreditCardStorage();
await setInput("#cc-number", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
primaryAffix: cc.ccNumberFmt.affix,
@ -74,7 +74,7 @@ add_task(async function check_menu_when_both_with_autocomplete_off() {
})));
await setInput("#cc-name", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
primary: cc["cc-name"],

View File

@ -50,7 +50,7 @@ function addInputField(form, className) {
async function checkFormChangeHappened(formId) {
info("expecting form changed");
await focusAndWaitForFieldsIdentified(`#${formId} input[name=tel]`);
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
JSON.stringify({primary: address.tel, secondary: address.name})
@ -60,7 +60,7 @@ async function checkFormChangeHappened(formId) {
addInputField(document.querySelector(`#${formId}`), "address-level2");
await focusAndWaitForFieldsIdentified(`#${formId} input[name=name]`);
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
JSON.stringify({primary: address.name, secondary: address["address-level2"]})
@ -71,7 +71,7 @@ async function checkFormChangeHappened(formId) {
addInputField(document.querySelector(`#${formId}`), "address-level2");
await focusAndWaitForFieldsIdentified(`#${formId} input[name=address-level2]`, true);
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
JSON.stringify({primary: address["address-level2"], secondary: address.name})

View File

@ -55,22 +55,22 @@ add_task(async function setup_storage() {
add_task(async function check_preview() {
const focusedInput = await setInput("#organization", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkFormFieldsStyle(null);
for (let i = 0; i < MOCK_STORAGE.length; i++) {
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await notifySelectedIndex(i);
checkFormFieldsStyle(MOCK_STORAGE[i]);
}
// Navigate to the footer
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await notifySelectedIndex(MOCK_STORAGE.length);
checkFormFieldsStyle(null);
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await notifySelectedIndex(-1);
checkFormFieldsStyle(null);

View File

@ -89,7 +89,7 @@ function checkFormFilled(selector, address) {
promises.push(...checkElementFilled(element, converted));
}
}
synthesizeKey("KEY_Enter", {code: "Enter"});
synthesizeKey("KEY_Enter", {});
return Promise.all(promises);
}
@ -106,19 +106,19 @@ add_task(async function autofill_with_level1_code() {
await setupAddressStorage();
await setInput("#organization-en", "Mozilla Toronto");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
// Replace address level 1 code with full name in English for test result
let result = Object.assign({}, MOCK_STORAGE[1], {"address-level1": "Ontario"});
await checkFormFilled("#form-en", result);
await setInput("#organization-fr", "Mozilla Vancouver");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
// Replace address level 1 code with full name in French for test result
result = Object.assign({}, MOCK_STORAGE[0], {"address-level1": "Colombie-Britannique"});
await checkFormFilled("#form-fr", result);
@ -129,19 +129,19 @@ add_task(async function autofill_with_level1_code() {
// Autofill the address with address level 1 full name.
add_task(async function autofill_with_level1_full_name() {
await setInput("#organization-en", "ExpoCité");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
// Replace address level 1 code with full name in French for test result
let result = Object.assign({}, MOCK_STORAGE[3], {"address-level1": "Quebec"});
await checkFormFilled("#form-en", result);
await setInput("#organization-fr", "Prince of Wales Northern Heritage");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
// Replace address level 1 code with full name in English for test result
result = Object.assign({}, MOCK_STORAGE[2], {"address-level1": "Territoires du Nord-Ouest"});
await checkFormFilled("#form-fr", result);

View File

@ -33,19 +33,19 @@ add_task(async function setupStorage() {
add_task(async function check_switch_form_popup() {
await setInput("#additional-name", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
// We need an intentional wait here before switching form.
await sleep();
await setInput("#organization", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
const {open: popupOpen} = await getPopupState();
is(popupOpen, false);
await sleep();
await setInput("#given-name", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
});

View File

@ -75,7 +75,7 @@ add_task(async function check_storage_after_another_address_submitted() {
ok(matching, "New address saved as expected");
await setInput("#organization", "");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
checkMenuEntries(addressesInMenu.map(address =>
JSON.stringify({primary: address.organization, secondary: address["street-address"]})
@ -110,10 +110,10 @@ add_task(async function check_storage_after_form_submitted() {
TEST_ADDRESSES[1].country = "US";
await setInput("#organization", "Moz");
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {});
await expectPopup();
synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
synthesizeKey("KEY_Enter", {code: "Enter"});
synthesizeKey("KEY_ArrowDown", {});
synthesizeKey("KEY_Enter", {});
clickOnElement("input[type=submit]");
let expectedAddresses = TEST_ADDRESSES.slice(0);

View File

@ -382,7 +382,7 @@ this.PreferenceExperiments = {
const newValue = getPref(UserPreferences, preferenceName, preferenceType);
if (newValue !== preferenceValue) {
PreferenceExperiments.stop(experimentName, {
didResetValue: false,
resetValue: false,
reason: "user-preference-changed",
}).catch(Cu.reportError);
}

View File

@ -1043,3 +1043,38 @@ decorate_task(
);
}
);
// Should send the correct event telemetry when a study ends because
// the user changed preferences during a browser run.
decorate_task(
withMockPreferences,
withStub(TelemetryEvents, "sendEvent"),
async function testPrefChangeEventTelemetry(mockPreferences, sendEventStub) {
is(Preferences.get("fake.preference"), null, "preference should start unset");
await PreferenceExperiments.start({
name: "test",
branch: "branch",
preferenceName: "fake.preference",
preferenceValue: "experimentvalue",
preferenceBranchType: "default",
preferenceType: "string",
});
// setting the preference on the user branch should trigger the observer to stop the experiment
mockPreferences.set("fake.preference", "uservalue", "user");
// let the event loop tick to run the observer
await Promise.resolve();
is(sendEventStub.getCall(0).args[0], "enroll", "There is an enrollment event from start()");
Assert.deepEqual(
sendEventStub.getCall(1).args,
["unenroll", "preference_study", "test", {
didResetValue: "false",
reason: "user-preference-changed",
}],
"stop should send a telemetry event indicating the user unenrolled manually",
);
},
);

View File

@ -779,21 +779,26 @@ def compiler(language, host_or_target, c_compiler=None, other_compiler=None,
# doesn't match the target C compiler.
# As a special case, since clang supports all kinds of targets in the same
# executable, when cross compiling with clang, default to the same compiler
# as the target compiler, resetting flags.
# as the target compiler, resetting flags. However, Android NDK clangs do
# not function as host compilers -- they're target compilers only -- so
# don't use clang target as host if the target OS is Android.
if host_or_target == host:
if other_c_compiler is not None:
args = (c_compiler, other_c_compiler)
else:
args = ()
@depends(provided_compiler, other_compiler, cross_compiling, *args)
def provided_compiler(value, other_compiler, cross_compiling, *args):
@depends(provided_compiler, other_compiler, cross_compiling,
target, *args)
def provided_compiler(value, other_compiler, cross_compiling,
target, *args):
if value:
return value
c_compiler, other_c_compiler = args if args else (None, None)
if not cross_compiling and c_compiler == other_c_compiler:
return other_compiler
if cross_compiling and other_compiler.type == 'clang':
if cross_compiling and other_compiler.type == 'clang' and \
target.os != 'Android':
return namespace(**{
k: [] if k == 'flags' else v
for k, v in other_compiler.__dict__.iteritems()

View File

@ -6,7 +6,6 @@
const {
UPDATE_PREVIEW_TEXT,
UPDATE_SHOW_ALL_FONTS,
} = require("./index");
module.exports = {
@ -21,14 +20,4 @@ module.exports = {
};
},
/**
* Update whether to show all fonts in the font inspector
*/
updateShowAllFonts(showAllFonts) {
return {
type: UPDATE_SHOW_ALL_FONTS,
showAllFonts,
};
},
};

View File

@ -13,10 +13,11 @@ module.exports = {
/**
* Update the list of fonts in the font inspector
*/
updateFonts(fonts) {
updateFonts(fonts, otherFonts) {
return {
type: UPDATE_FONTS,
fonts,
otherFonts,
};
},

View File

@ -14,7 +14,4 @@ createEnum([
// Update the preview text.
"UPDATE_PREVIEW_TEXT",
// Update whether to show all fonts.
"UPDATE_SHOW_ALL_FONTS",
], module.exports);

View File

@ -4,84 +4,123 @@
"use strict";
const { PureComponent } = require("devtools/client/shared/vendor/react");
const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const FontPreview = createFactory(require("./FontPreview"));
const { getStr } = require("../utils/l10n");
const Types = require("../types");
class Font extends PureComponent {
static get propTypes() {
return PropTypes.shape(Types.font).isRequired;
return {
font: PropTypes.shape(Types.font).isRequired,
fontOptions: PropTypes.shape(Types.fontOptions).isRequired,
onPreviewFonts: PropTypes.func.isRequired,
};
}
constructor(props) {
super(props);
this.renderFontCSS = this.renderFontCSS.bind(this);
this.renderFontCSSCode = this.renderFontCSSCode.bind(this);
this.renderFontFormatURL = this.renderFontFormatURL.bind(this);
this.renderFontName = this.renderFontName.bind(this);
this.renderFontPreview = this.renderFontPreview.bind(this);
this.state = {
isFontExpanded: false,
isFontFaceRuleExpanded: false,
};
this.onFontToggle = this.onFontToggle.bind(this);
this.onFontFaceRuleToggle = this.onFontFaceRuleToggle.bind(this);
}
componentWillReceiveProps(newProps) {
if (this.props.font.name === newProps.font.name) {
return;
}
this.setState({
isFontExpanded: false,
isFontFaceRuleExpanded: false,
});
}
onFontToggle() {
this.setState({
isFontExpanded: !this.state.isFontExpanded
});
}
onFontFaceRuleToggle() {
this.setState({
isFontFaceRuleExpanded: !this.state.isFontFaceRuleExpanded
});
}
renderFontCSS(cssFamilyName) {
return dom.p(
{
className: "font-css"
className: "font-css-name"
},
dom.span(
{},
getStr("fontinspector.usedAs")
),
" \"",
dom.span(
{
className: "font-css-name"
},
cssFamilyName
),
"\""
`${getStr("fontinspector.usedAs")} "${cssFamilyName}"`
);
}
renderFontCSSCode(ruleText) {
renderFontCSSCode(rule, ruleText) {
if (!rule) {
return null;
}
// Cut the rule text in 3 parts: the selector, the declarations, the closing brace.
// This way we can collapse the declarations by default and display an expander icon
// to expand them again.
let leading = ruleText.substring(0, ruleText.indexOf("{") + 1);
let body = ruleText.substring(ruleText.indexOf("{") + 1, ruleText.lastIndexOf("}"));
let trailing = ruleText.substring(ruleText.lastIndexOf("}"));
let { isFontFaceRuleExpanded } = this.state;
return dom.pre(
{
className: "font-css-code"
className: "font-css-code",
},
ruleText
this.renderFontCSSCodeTwisty(),
leading,
isFontFaceRuleExpanded ?
null
:
dom.span(
{
className: "font-css-code-expander"
}
),
isFontFaceRuleExpanded ? body : null,
trailing
);
}
renderFontFormatURL(url, format) {
renderFontTypeAndURL(url, format) {
if (!url) {
return dom.p(
{
className: "font-format-url"
},
getStr("fontinspector.system")
);
}
return dom.p(
{
className: "font-format-url"
},
dom.input(
getStr("fontinspector.remote"),
dom.a(
{
className: "font-url",
readOnly: "readonly",
value: url
}
),
" ",
format ?
dom.span(
{
className: "font-format"
},
format
)
:
dom.span(
{
className: "font-format",
hidden: "true"
},
format
)
href: url
},
format
)
);
}
@ -89,27 +128,43 @@ class Font extends PureComponent {
return dom.h1(
{
className: "font-name",
onClick: this.onFontToggle,
},
name
);
}
renderFontPreview(previewUrl) {
return dom.div(
{
className: "font-preview-container",
},
dom.img(
{
className: "font-preview",
src: previewUrl
}
)
);
renderFontTwisty() {
let { isFontExpanded } = this.state;
return this.renderTwisty(isFontExpanded, this.onFontToggle);
}
renderFontCSSCodeTwisty() {
let { isFontFaceRuleExpanded } = this.state;
return this.renderTwisty(isFontFaceRuleExpanded, this.onFontFaceRuleToggle);
}
renderTwisty(isExpanded, onClick) {
let attributes = {
className: "theme-twisty",
onClick,
};
if (isExpanded) {
attributes.open = "true";
}
return dom.span(attributes);
}
render() {
let { font } = this.props;
let {
font,
fontOptions,
onPreviewFonts,
} = this.props;
let { previewText } = fontOptions;
let {
CSSFamilyName,
format,
@ -120,16 +175,23 @@ class Font extends PureComponent {
URI,
} = font;
let { isFontExpanded } = this.state;
return dom.li(
{
className: "font",
className: "font" + (isFontExpanded ? " expanded" : ""),
},
this.renderFontPreview(previewUrl),
this.renderFontTwisty(),
this.renderFontName(name),
" " + (URI ? getStr("fontinspector.remote") : getStr("fontinspector.system")),
URI ? this.renderFontFormatURL(URI, format) : null,
this.renderFontCSS(CSSFamilyName),
rule ? this.renderFontCSSCode(ruleText) : null
FontPreview({ previewText, previewUrl, onPreviewFonts }),
dom.div(
{
className: "font-details"
},
this.renderFontTypeAndURL(URI, format),
this.renderFontCSSCode(rule, ruleText),
this.renderFontCSS(CSSFamilyName)
)
);
}
}

View File

@ -15,26 +15,29 @@ const Types = require("../types");
class FontList extends PureComponent {
static get propTypes() {
return {
fonts: PropTypes.arrayOf(PropTypes.shape(Types.font)).isRequired
fontOptions: PropTypes.shape(Types.fontOptions).isRequired,
fonts: PropTypes.arrayOf(PropTypes.shape(Types.font)).isRequired,
onPreviewFonts: PropTypes.func.isRequired,
};
}
render() {
let { fonts } = this.props;
let {
fonts,
fontOptions,
onPreviewFonts
} = this.props;
return dom.div(
return dom.ul(
{
id: "font-container"
className: "fonts-list"
},
dom.ul(
{
id: "all-fonts"
},
fonts.map((font, i) => Font({
key: i,
font
}))
)
fonts.map((font, i) => Font({
key: i,
font,
fontOptions,
onPreviewFonts,
}))
);
}
}

View File

@ -0,0 +1,93 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { PureComponent } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const Types = require("../types");
class FontPreview extends PureComponent {
static get propTypes() {
return {
previewText: Types.fontOptions.previewText.isRequired,
previewUrl: Types.font.previewUrl.isRequired,
onPreviewFonts: PropTypes.func.isRequired,
};
}
constructor(props) {
super(props);
this.state = {
// Is the text preview input field currently focused?
isFocused: false,
};
this.onBlur = this.onBlur.bind(this);
this.onClick = this.onClick.bind(this);
this.onChange = this.onChange.bind(this);
}
componentDidUpdate() {
if (this.state.isFocused) {
let input = this.fontPreviewInput;
input.focus();
input.selectionStart = input.selectionEnd = input.value.length;
}
}
onBlur() {
this.setState({ isFocused: false });
}
onClick() {
this.setState({ isFocused: true });
}
onChange(event) {
this.props.onPreviewFonts(event.target.value);
}
render() {
let {
previewText,
previewUrl,
} = this.props;
let { isFocused } = this.state;
return dom.div(
{
className: "font-preview-container",
},
isFocused ?
dom.input(
{
type: "search",
className: "font-preview-input devtools-searchinput",
value: previewText,
onBlur: this.onBlur,
onChange: this.onChange,
ref: input => {
this.fontPreviewInput = input;
}
}
)
:
null,
dom.img(
{
className: "font-preview",
src: previewUrl,
onClick: this.onClick,
}
)
);
}
}
module.exports = FontPreview;

View File

@ -9,30 +9,73 @@ const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const SearchBox = createFactory(require("devtools/client/shared/components/SearchBox"));
const Accordion = createFactory(require("devtools/client/inspector/layout/components/Accordion"));
const FontList = createFactory(require("./FontList"));
const { getStr } = require("../utils/l10n");
const Types = require("../types");
const PREVIEW_UPDATE_DELAY = 150;
class FontsApp extends PureComponent {
static get propTypes() {
return {
fonts: PropTypes.arrayOf(PropTypes.shape(Types.font)).isRequired,
fontData: PropTypes.shape(Types.fontData).isRequired,
fontOptions: PropTypes.shape(Types.fontOptions).isRequired,
onPreviewFonts: PropTypes.func.isRequired,
onShowAllFont: PropTypes.func.isRequired,
};
}
render() {
renderElementFonts() {
let {
fonts,
fontData,
fontOptions,
onPreviewFonts,
onShowAllFont,
} = this.props;
let { fonts } = fontData;
return fonts.length ?
FontList({
fonts,
fontOptions,
onPreviewFonts
})
:
dom.div(
{
className: "devtools-sidepanel-no-result"
},
getStr("fontinspector.noFontsOnSelectedElement")
);
}
renderOtherFonts() {
let {
fontData,
onPreviewFonts,
fontOptions,
} = this.props;
let { otherFonts } = fontData;
if (!otherFonts.length) {
return null;
}
return Accordion({
items: [
{
header: getStr("fontinspector.otherFontsInPageHeader"),
component: FontList,
componentProps: {
fontOptions,
fonts: otherFonts,
onPreviewFonts
},
opened: false
}
]
});
}
render() {
return dom.div(
{
className: "theme-sidebar inspector-tabpanel",
@ -40,33 +83,11 @@ class FontsApp extends PureComponent {
},
dom.div(
{
className: "devtools-toolbar"
id: "font-container"
},
SearchBox({
delay: PREVIEW_UPDATE_DELAY,
placeholder: getStr("fontinspector.previewText"),
type: "text",
onChange: onPreviewFonts,
}),
dom.label(
{
id: "font-showall",
className: "theme-link",
title: getStr("fontinspector.seeAll.tooltip"),
onClick: onShowAllFont,
},
getStr("fontinspector.seeAll")
)
),
fonts.length ?
FontList({ fonts })
:
dom.div(
{
className: "devtools-sidepanel-no-result"
},
getStr("fontinspector.noFontsOnSelectedElement")
)
this.renderElementFonts(),
this.renderOtherFonts()
)
);
}
}

View File

@ -7,5 +7,6 @@
DevToolsModules(
'Font.js',
'FontList.js',
'FontPreview.js',
'FontsApp.js',
)

View File

@ -20,7 +20,7 @@ const INSPECTOR_L10N =
new LocalizationHelper("devtools/client/locales/inspector.properties");
const { updateFonts } = require("./actions/fonts");
const { updatePreviewText, updateShowAllFonts } = require("./actions/font-options");
const { updatePreviewText } = require("./actions/font-options");
class FontInspector {
constructor(inspector, window) {
@ -33,7 +33,6 @@ class FontInspector {
this.onNewNode = this.onNewNode.bind(this);
this.onPreviewFonts = this.onPreviewFonts.bind(this);
this.onShowAllFont = this.onShowAllFont.bind(this);
this.onThemeChanged = this.onThemeChanged.bind(this);
this.init();
@ -46,7 +45,6 @@ class FontInspector {
let fontsApp = FontsApp({
onPreviewFonts: this.onPreviewFonts,
onShowAllFont: this.onShowAllFont,
});
let provider = createElement(Provider, {
@ -66,10 +64,26 @@ class FontInspector {
gDevTools.on("theme-switched", this.onThemeChanged);
this.store.dispatch(updatePreviewText(""));
this.store.dispatch(updateShowAllFonts(false));
this.update(false, "");
}
/**
* Given all fonts on the page, and given the fonts used in given node, return all fonts
* not from the page not used in this node.
*
* @param {Array} allFonts
* All fonts used on the entire page
* @param {Array} nodeFonts
* Fonts used only in one of the nodes
* @return {Array}
* All fonts, except the ones used in the current node
*/
excludeNodeFonts(allFonts, nodeFonts) {
return allFonts.filter(font => {
return !nodeFonts.some(nodeFont => nodeFont.name === font.name);
});
}
/**
* Destruction function called when the inspector is destroyed. Removes event listeners
* and cleans up references.
@ -85,6 +99,34 @@ class FontInspector {
this.store = null;
}
async getFontsForNode(node, options) {
// In case we've been destroyed in the meantime
if (!this.document) {
return [];
}
let fonts = await this.pageStyle.getUsedFontFaces(node, options).catch(console.error);
if (!fonts) {
return [];
}
return fonts;
}
async getFontsNotInNode(nodeFonts, options) {
// In case we've been destroyed in the meantime
if (!this.document) {
return [];
}
let allFonts = await this.pageStyle.getAllUsedFontFaces(options).catch(console.error);
if (!allFonts) {
allFonts = [];
}
return this.excludeNodeFonts(allFonts, nodeFonts);
}
/**
* Returns true if the font inspector panel is visible, and false otherwise.
*/
@ -98,7 +140,6 @@ class FontInspector {
*/
onNewNode() {
if (this.isPanelVisible()) {
this.store.dispatch(updateShowAllFonts(false));
this.update();
}
}
@ -111,14 +152,6 @@ class FontInspector {
this.update();
}
/**
* Handler for click on show all fonts button.
*/
onShowAllFont() {
this.store.dispatch(updateShowAllFonts(true));
this.update();
}
/**
* Handler for the "theme-switched" event.
*/
@ -135,20 +168,22 @@ class FontInspector {
}
let node = this.inspector.selection.nodeFront;
let fonts = [];
let otherFonts = [];
let { fontOptions } = this.store.getState();
let { showAllFonts, previewText } = fontOptions;
let { previewText } = fontOptions;
// Clear the list of fonts if the currently selected node is not connected or a text
// or element node unless all fonts are supposed to be shown.
let isElementOrTextNode = this.inspector.selection.isElementNode() ||
this.inspector.selection.isTextNode();
if (!showAllFonts &&
(!node ||
!this.isPanelVisible() ||
!this.inspector.selection.isConnected() ||
!isElementOrTextNode)) {
this.store.dispatch(updateFonts(fonts));
if (!node ||
!this.isPanelVisible() ||
!this.inspector.selection.isConnected() ||
!isElementOrTextNode) {
this.store.dispatch(updateFonts(fonts, otherFonts));
return;
}
@ -158,23 +193,18 @@ class FontInspector {
previewFillStyle: getColor("body-color")
};
if (showAllFonts) {
fonts = await this.pageStyle.getAllUsedFontFaces(options)
.catch(console.error);
} else {
fonts = await this.pageStyle.getUsedFontFaces(node, options)
.catch(console.error);
}
fonts = await this.getFontsForNode(node, options);
otherFonts = await this.getFontsNotInNode(fonts, options);
if (!fonts || !fonts.length) {
if (!fonts.length && !otherFonts.length) {
// No fonts to display. Clear the previously shown fonts.
if (this.store) {
this.store.dispatch(updateFonts(fonts));
this.store.dispatch(updateFonts(fonts, otherFonts));
}
return;
}
for (let font of fonts) {
for (let font of [...fonts, ...otherFonts]) {
font.previewUrl = await font.preview.data.string();
}
@ -183,7 +213,7 @@ class FontInspector {
return;
}
this.store.dispatch(updateFonts(fonts));
this.store.dispatch(updateFonts(fonts, otherFonts));
this.inspector.emit("fontinspector-updated");
}

View File

@ -6,12 +6,10 @@
const {
UPDATE_PREVIEW_TEXT,
UPDATE_SHOW_ALL_FONTS
} = require("../actions/index");
const INITIAL_FONT_OPTIONS = {
previewText: "Abc",
showAllFonts: false,
};
let reducers = {
@ -20,10 +18,6 @@ let reducers = {
return Object.assign({}, fontOptions, { previewText });
},
[UPDATE_SHOW_ALL_FONTS](fontOptions, { showAllFonts }) {
return Object.assign({}, fontOptions, { showAllFonts });
},
};
module.exports = function (fontOptions = INITIAL_FONT_OPTIONS, action) {

View File

@ -8,20 +8,23 @@ const {
UPDATE_FONTS,
} = require("../actions/index");
const INITIAL_FONTS = [];
const INITIAL_FONT_DATA = {
fonts: [],
otherFonts: []
};
let reducers = {
[UPDATE_FONTS](_, { fonts }) {
return fonts;
[UPDATE_FONTS](_, { fonts, otherFonts }) {
return { fonts, otherFonts };
},
};
module.exports = function (fonts = INITIAL_FONTS, action) {
module.exports = function (fontData = INITIAL_FONT_DATA, action) {
let reducer = reducers[action.type];
if (!reducer) {
return fonts;
return fontData;
}
return reducer(fonts, action);
return reducer(fontData, action);
};

View File

@ -16,6 +16,8 @@ support-files =
[browser_fontinspector.js]
[browser_fontinspector_edit-previews.js]
[browser_fontinspector_edit-previews-show-all.js]
[browser_fontinspector_expand-css-code.js]
[browser_fontinspector_expand-details.js]
[browser_fontinspector_other-fonts.js]
[browser_fontinspector_text-node.js]
[browser_fontinspector_theme-change.js]

View File

@ -40,33 +40,40 @@ add_task(function* () {
yield testBodyFonts(inspector, viewDoc);
yield testDivFonts(inspector, viewDoc);
yield testShowAllFonts(inspector, viewDoc);
});
function isRemote(fontLiEl) {
return fontLiEl.querySelectorAll(".font-format-url").length === 1;
function isRemote(fontLi) {
return fontLi.querySelectorAll(".font-format-url a").length === 1;
}
function getFormat(fontLi) {
let link = fontLi.querySelector(".font-format-url a");
if (!link) {
return null;
}
return link.textContent;
}
function getCSSName(fontLi) {
let text = fontLi.querySelector(".font-css-name").textContent;
return text.substring(text.indexOf('"') + 1, text.lastIndexOf('"'));
}
function* testBodyFonts(inspector, viewDoc) {
let lis = viewDoc.querySelectorAll("#all-fonts > li");
let lis = getUsedFontsEls(viewDoc);
is(lis.length, 5, "Found 5 fonts");
for (let i = 0; i < FONTS.length; i++) {
let li = lis[i];
let font = FONTS[i];
is(li.querySelector(".font-name").textContent, font.name,
"font " + i + " right font name");
is(isRemote(li), font.remote,
"font " + i + " remote value correct");
is(li.querySelector(".font-url").value, font.url,
"font " + i + " url correct");
is(li.querySelector(".font-format").hidden, !font.format,
"font " + i + " format hidden value correct");
is(li.querySelector(".font-format").textContent,
font.format, "font " + i + " format correct");
is(li.querySelector(".font-css-name").textContent,
font.cssName, "font " + i + " css name correct");
is(getName(li), font.name, "font " + i + " right font name");
is(isRemote(li), font.remote, "font " + i + " remote value correct");
is(li.querySelector(".font-url").href, font.url, "font " + i + " url correct");
is(getFormat(li), font.format, "font " + i + " format correct");
is(getCSSName(li), font.cssName, "font " + i + " css name correct");
}
// test that the bold and regular fonts have different previews
@ -75,8 +82,8 @@ function* testBodyFonts(inspector, viewDoc) {
isnot(regSrc, boldSrc, "preview for bold font is different from regular");
// test system font
let localFontName = lis[4].querySelector(".font-name").textContent;
let localFontCSSName = lis[4].querySelector(".font-css-name").textContent;
let localFontName = getName(lis[4]);
let localFontCSSName = getCSSName(lis[4]);
// On Linux test machines, the Arial font doesn't exist.
// The fallback is "Liberation Sans"
@ -92,22 +99,7 @@ function* testDivFonts(inspector, viewDoc) {
yield selectNode("div", inspector);
yield updated;
let lis = viewDoc.querySelectorAll("#all-fonts > li");
let lis = getUsedFontsEls(viewDoc);
is(lis.length, 1, "Found 1 font on DIV");
is(lis[0].querySelector(".font-name").textContent,
"Ostrich Sans Medium",
"The DIV font has the right name");
}
function* testShowAllFonts(inspector, viewDoc) {
info("testing showing all fonts");
let updated = inspector.once("fontinspector-updated");
viewDoc.querySelector("#font-showall").click();
yield updated;
// shouldn't change the node selection
is(inspector.selection.nodeFront.nodeName, "DIV", "Show all fonts selected");
let lis = viewDoc.querySelectorAll("#all-fonts > li");
is(lis.length, 6, "Font inspector shows 6 fonts (1 from iframe)");
is(getName(lis[0]), "Ostrich Sans Medium", "The DIV font has the right name");
}

View File

@ -1,44 +0,0 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that correct previews are shown if the text is edited after 'Show all'
// button is pressed.
const TEST_URI = URL_ROOT + "browser_fontinspector.html";
add_task(function* () {
let { inspector, view } = yield openFontInspectorForURL(TEST_URI);
let viewDoc = view.document;
info("Selecting a node that doesn't contain all document fonts.");
yield selectNode(".normal-text", inspector);
let normalTextNumPreviews =
viewDoc.querySelectorAll("#all-fonts .font-preview").length;
let onUpdated = inspector.once("fontinspector-updated");
info("Clicking 'Select all' button.");
viewDoc.getElementById("font-showall").click();
info("Waiting for font-inspector to update.");
yield onUpdated;
let allFontsNumPreviews =
viewDoc.querySelectorAll("#all-fonts .font-preview").length;
// Sanity check. If this fails all fonts apply also to the .normal-text node
// meaning we won't detect if preview editing causes the panel not to show all
// fonts.
isnot(allFontsNumPreviews, normalTextNumPreviews,
"The .normal-text didn't show all fonts.");
info("Editing the preview text.");
yield updatePreviewText(view, "The quick brown");
let numPreviews = viewDoc.querySelectorAll("#all-fonts .font-preview").length;
is(numPreviews, allFontsNumPreviews,
"All fonts are still shown after the preview text was edited.");
});

View File

@ -13,7 +13,7 @@ add_task(function* () {
let {view} = yield openFontInspectorForURL(TEST_URI);
let viewDoc = view.document;
let previews = viewDoc.querySelectorAll("#all-fonts .font-preview");
let previews = viewDoc.querySelectorAll("#font-container .font-preview");
let initialPreviews = [...previews].map(p => p.src);
info("Typing 'Abc' to check that the reference previews are correct.");
@ -42,7 +42,7 @@ add_task(function* () {
* URI's are different.
*/
function checkPreviewImages(viewDoc, originalURIs, assertIdentical) {
let previews = viewDoc.querySelectorAll("#all-fonts .font-preview");
let previews = viewDoc.querySelectorAll("#font-container .font-preview");
let newURIs = [...previews].map(p => p.src);
is(newURIs.length, originalURIs.length,

View File

@ -0,0 +1,36 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the font-face css rule code is collapsed by default, and can be expanded.
const TEST_URI = URL_ROOT + "browser_fontinspector.html";
add_task(function* () {
let { view } = yield openFontInspectorForURL(TEST_URI);
let viewDoc = view.document;
info("Expanding the details section of the first font");
let fontEl = getUsedFontsEls(viewDoc)[0];
yield expandFontDetails(fontEl);
info("Checking that the css font-face rule is collapsed by default");
let codeEl = fontEl.querySelector(".font-css-code");
is(codeEl.textContent, "@font-face {}", "The font-face rule is collapsed");
info("Expanding the rule by clicking on the expander icon");
let onExpanded = BrowserTestUtils.waitForCondition(() => {
return codeEl.textContent === `@font-face {
font-family: "bar";
src: url("bad/font/name.ttf"), url("ostrich-regular.ttf") format("truetype");
}`;
}, "Waiting for the font-face rule");
let expander = fontEl.querySelector(".font-css-code .theme-twisty");
expander.click();
yield onExpanded;
ok(true, "Font-face rule is now expanded");
});

View File

@ -0,0 +1,38 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that fonts are collapsed by default, and can be expanded.
const TEST_URI = URL_ROOT + "browser_fontinspector.html";
add_task(function* () {
let { inspector, view } = yield openFontInspectorForURL(TEST_URI);
let viewDoc = view.document;
info("Checking all font are collapsed by default");
let fonts = getUsedFontsEls(viewDoc);
checkAllFontsCollapsed(fonts);
info("Clicking on the first one to expand the font details");
yield expandFontDetails(fonts[0]);
ok(fonts[0].querySelector(".theme-twisty").hasAttribute("open"), `Twisty is open`);
ok(isFontDetailsVisible(fonts[0]), `Font details is shown`);
info("Selecting a node with different fonts and checking that all fonts are collapsed");
yield selectNode(".black-text", inspector);
fonts = getUsedFontsEls(viewDoc);
checkAllFontsCollapsed(fonts);
});
function checkAllFontsCollapsed(fonts) {
fonts.forEach((el, i) => {
let twisty = el.querySelector(".theme-twisty");
ok(twisty, `Twisty ${i} exists`);
ok(!twisty.hasAttribute("open"), `Twisty ${i} is closed`);
ok(!isFontDetailsVisible(el), `Font details ${i} is hidden`);
});
}

View File

@ -0,0 +1,39 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that the font inspector has a section for "other fonts" and that section contains
// the fonts *not* used in the currently selected element.
// Check that it's collapsed by default, but can be expanded. That it does not contain
// the fonts listed in the previous section.
const TEST_URI = URL_ROOT + "browser_fontinspector.html";
add_task(function* () {
const { inspector, view } = yield openFontInspectorForURL(TEST_URI);
const viewDoc = view.document;
let otherFontsAccordion = viewDoc.querySelector("#font-container .accordion");
ok(otherFontsAccordion, "There's an accordion in the panel");
is(otherFontsAccordion.textContent, "Other fonts in page", "It has the right title");
yield expandOtherFontsAccordion(viewDoc);
let otherFontsEls = getOtherFontsEls(viewDoc);
is(otherFontsEls.length, 1, "There is one font listed in the other fonts section");
// On Linux Times New Roman does not exist, Liberation Serif is used instead
let name = getName(otherFontsEls[0]);
ok(name === "Times New Roman" || name === "Liberation Serif",
"The other font listed is the right one");
info("Checking that fonts of the current element aren't listed");
yield selectNode(".bold-text", inspector);
yield expandOtherFontsAccordion(viewDoc);
otherFontsEls = getOtherFontsEls(viewDoc);
for (let otherFontEl of otherFontsEls) {
ok(![...getUsedFontsEls(viewDoc)].some(el => getName(el) === getName(otherFontEl)),
"Other font isn't listed in the main fonts section");
}
});

View File

@ -17,6 +17,6 @@ add_task(function* () {
let { nodes } = yield inspector.walker.children(bodyNode);
yield selectNode(nodes[0], inspector);
let sections = viewDoc.querySelectorAll("#all-fonts > li");
is(sections.length, 1, "Font inspector shows 1 font");
let lis = getUsedFontsEls(viewDoc);
is(lis.length, 1, "Font inspector shows 1 font");
});

View File

@ -24,17 +24,17 @@ add_task(function* () {
yield selectNode(".normal-text", inspector);
// Store the original preview URI for later comparison.
let originalURI = doc.querySelector("#all-fonts .font-preview").src;
let originalURI = doc.querySelector("#font-container .font-preview").src;
let newTheme = originalTheme === "light" ? "dark" : "light";
info(`Original theme was '${originalTheme}'.`);
yield setThemeAndWaitForUpdate(newTheme, inspector);
isnot(doc.querySelector("#all-fonts .font-preview").src, originalURI,
isnot(doc.querySelector("#font-container .font-preview").src, originalURI,
"The preview image changed with the theme.");
yield setThemeAndWaitForUpdate(originalTheme, inspector);
is(doc.querySelector("#all-fonts .font-preview").src, originalURI,
is(doc.querySelector("#font-container .font-preview").src, originalURI,
"The preview image is correct after the original theme was restored.");
});

View File

@ -51,7 +51,7 @@ var openFontInspectorForURL = Task.async(function* (url) {
});
/**
* Clears the preview input field, types new text into it and waits for the
* Focus one of the preview inputs, clear it, type new text into it and wait for the
* preview images to be updated.
*
* @param {FontInspector} view - The FontInspector instance.
@ -61,25 +61,95 @@ function* updatePreviewText(view, text) {
info(`Changing the preview text to '${text}'`);
let doc = view.document;
let input = doc.querySelector("#sidebar-panel-fontinspector .devtools-textinput");
let update = view.inspector.once("fontinspector-updated");
let previewImg = doc.querySelector("#sidebar-panel-fontinspector .font-preview");
info("Focusing the input field.");
input.focus();
info("Clicking the font preview element to turn it to edit mode");
let onClick = once(doc, "click");
previewImg.click();
yield onClick;
let input = previewImg.parentNode.querySelector("input");
is(doc.activeElement, input, "The input was focused.");
info("Blanking the input field.");
for (let i = input.value.length; i >= 0; i--) {
while (input.value.length) {
let update = view.inspector.once("fontinspector-updated");
EventUtils.sendKey("BACK_SPACE", doc.defaultView);
yield update;
}
is(input.value, "", "The input is now blank.");
if (text) {
info("Typing the specified text to the input field.");
let update = waitForNEvents(view.inspector, "fontinspector-updated", text.length);
EventUtils.sendString(text, doc.defaultView);
yield update;
}
info("Typing the specified text to the input field.");
EventUtils.sendString(text, doc.defaultView);
is(input.value, text, "The input now contains the correct text.");
info("Waiting for the font-inspector to update.");
yield update;
}
async function expandFontDetails(fontEl) {
info("Expanding a font details section");
let onExpanded = BrowserTestUtils.waitForCondition(() => isFontDetailsVisible(fontEl),
"Waiting for font details");
let twisty = fontEl.querySelector(".theme-twisty");
twisty.click();
await onExpanded;
}
function isFontDetailsVisible(fontEl) {
return [...fontEl.querySelectorAll(".font-css-name, .font-css-code, .font-format-url")]
.every(el => el.getBoxQuads().length);
}
/**
* Get all of the <li> elements for the fonts used on the currently selected element.
*
* @param {document} viewDoc
* @return {Array}
*/
function getUsedFontsEls(viewDoc) {
return viewDoc.querySelectorAll("#font-container > .fonts-list > li");
}
/**
* Expand the other fonts accordion.
*/
async function expandOtherFontsAccordion(viewDoc) {
info("Expanding the other fonts section");
let accordion = viewDoc.querySelector("#font-container .accordion");
let isExpanded = () => accordion.querySelector(".fonts-list");
if (isExpanded()) {
return;
}
let onExpanded = BrowserTestUtils.waitForCondition(isExpanded,
"Waiting for other fonts section");
accordion.querySelector(".theme-twisty").click();
await onExpanded;
}
/**
* Get all of the <li> elements for the fonts used elsewhere in the document.
*
* @param {document} viewDoc
* @return {Array}
*/
function getOtherFontsEls(viewDoc) {
return viewDoc.querySelectorAll("#font-container .accordion .fonts-list > li");
}
/**
* Given a font element, return its name.
*
* @param {DOMNode} fontEl
* The font element.
* @return {String}
* The name of the font as shown in the UI.
*/
function getName(fontEl) {
return fontEl.querySelector(".font-name").textContent;
}

View File

@ -9,7 +9,7 @@ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
/**
* A single font.
*/
exports.font = {
const font = exports.font = {
// The name of the font family
CSSFamilyName: PropTypes.string,
@ -31,3 +31,19 @@ exports.font = {
// The URI of the font file
URI: PropTypes.string,
};
exports.fontOptions = {
// The current preview text
previewText: PropTypes.string,
};
/**
* Font data
*/
exports.fontData = {
// The fonts used in the current element.
fonts: PropTypes.arrayOf(PropTypes.shape(font)),
// Fonts used elsewhere.
otherFonts: PropTypes.arrayOf(PropTypes.shape(font)),
};

View File

@ -14,6 +14,6 @@ exports.events = require("devtools/client/inspector/events/reducers/events");
exports.extensionsSidebar = require("devtools/client/inspector/extensions/reducers/sidebar");
exports.flexbox = require("devtools/client/inspector/flexbox/reducers/flexbox");
exports.fontOptions = require("devtools/client/inspector/fonts/reducers/font-options");
exports.fonts = require("devtools/client/inspector/fonts/reducers/fonts");
exports.fontData = require("devtools/client/inspector/fonts/reducers/fonts");
exports.grids = require("devtools/client/inspector/grids/reducers/grids");
exports.highlighterSettings = require("devtools/client/inspector/grids/reducers/highlighter-settings");

View File

@ -5,13 +5,6 @@
# LOCALIZATION NOTE This file contains the Font Inspector strings.
# The Font Inspector is a panel accessible in the Inspector sidebar.
# LOCALIZATION NOTE (fontinspector.seeAll) This is the label of a link that will show all
# the fonts used in the page, instead of the ones related to the inspected element.
fontinspector.seeAll=Show all fonts used
# LOCALIZATION NOTE (fontinspector.seeAll.tooltip) see fontinspector.seeAll.
fontinspector.seeAll.tooltip=See all the fonts used in the page
# LOCALIZATION NOTE (fontinspector.usedAs) This label introduces the name used to refer to
# the font in a stylesheet.
fontinspector.usedAs=Used as:
@ -24,10 +17,10 @@ fontinspector.system=system
# font.
fontinspector.remote=remote
# LOCALIZATION NOTE (fontinspector.previewText):
# This is the label shown as the placeholder in font inspector preview text box.
fontinspector.previewText=Preview Text
# LOCALIZATION NOTE (fontinspector.noFontsOnSelectedElement): This label is shown when
# no fonts found on the selected element.
fontinspector.noFontsOnSelectedElement=No fonts were found for the current element.
# LOCALIZATION NOTE (fontinspector.otherFontsInPageHeader): This is the text for the
# header of a collapsible section containing other fonts used in the page.
fontinspector.otherFontsInPageHeader=Other fonts in page

View File

@ -18,17 +18,10 @@ const { getToplevelWindow } = require("../utils/window");
const FRAME_SCRIPT = "resource://devtools/client/responsive.html/browser/content.js";
// Allow creation of HTML fragments without automatic sanitization, even
// though we're in a chrome-privileged document.
// This is, unfortunately, necessary in order to React to function
// correctly.
document.allowUnsafeHTML = true;
class Browser extends PureComponent {
/**
* This component is not allowed to depend directly on frequently changing
* data (width, height) due to the use of `dangerouslySetInnerHTML` below.
* Any changes in props will cause the <iframe> to be removed and added again,
* This component is not allowed to depend directly on frequently changing data (width,
* height). Any changes in props would cause the <iframe> to be removed and added again,
* throwing away the current state of the page.
*/
static get propTypes() {
@ -54,8 +47,7 @@ class Browser extends PureComponent {
componentWillMount() {
this.browserShown = new Promise(resolve => {
let handler = frameLoader => {
let browser = this.refs.browserContainer.querySelector("iframe.browser");
if (frameLoader.ownerElement != browser) {
if (frameLoader.ownerElement != this.browser) {
return;
}
Services.obs.removeObserver(handler, "remote-browser-shown");
@ -105,8 +97,10 @@ class Browser extends PureComponent {
}
async startFrameScript() {
let { onContentResize } = this;
let browser = this.refs.browserContainer.querySelector("iframe.browser");
let {
browser,
onContentResize,
} = this;
let mm = browser.frameLoader.messageManager;
// Notify tests when the content has received a resize event. This is not
@ -131,8 +125,10 @@ class Browser extends PureComponent {
}
async stopFrameScript() {
let { onContentResize } = this;
let browser = this.refs.browserContainer.querySelector("iframe.browser");
let {
browser,
onContentResize,
} = this;
let mm = browser.frameLoader.messageManager;
e10s.off(mm, "OnContentResize", onContentResize);
@ -141,31 +137,27 @@ class Browser extends PureComponent {
}
render() {
return dom.div(
// In the case of @remote and @remoteType, the attribute must be set before the
// element is added to the DOM to have any effect, which we are able to do with this
// approach.
//
// @noisolation and @allowfullscreen are needed so that these frames have the same
// access to browser features as regular browser tabs. The `swapFrameLoaders` platform
// API we use compares such features before allowing the swap to proceed.
return dom.iframe(
{
ref: "browserContainer",
className: "browser-container",
/**
* React uses a whitelist for attributes, so we need some way to set
* attributes it does not know about, such as @mozbrowser. If this were
* the only issue, we could use componentDidMount or ref: node => {} to
* set the atttibutes. In the case of @remote and @remoteType, the
* attribute must be set before the element is added to the DOM to have
* any effect, which we are able to do with this approach.
*
* @noisolation and @allowfullscreen are needed so that these frames
* have the same access to browser features as regular browser tabs.
* The `swapFrameLoaders` platform API we use compares such features
* before allowing the swap to proceed.
*/
dangerouslySetInnerHTML: {
__html: `<iframe class="browser" mozbrowser="true"
remote="true" remoteType="web"
noisolation="true" allowfullscreen="true"
src="about:blank" width="100%" height="100%">
</iframe>`
}
allowFullScreen: "true",
className: "browser",
height: "100%",
mozbrowser: "true",
noisolation: "true",
remote: "true",
remotetype: "web",
src: "about:blank",
width: "100%",
ref: browser => {
this.browser = browser;
},
}
);
}

View File

@ -268,11 +268,6 @@ select > option.divider {
* Viewport Browser
*/
.browser-container {
width: inherit;
height: inherit;
}
.browser {
display: block;
border: 0;

View File

@ -44,9 +44,10 @@ async function waitStartup(ui) {
async function testDefaults(ui) {
info("Test Defaults");
await testDevicePixelRatio(ui, window.devicePixelRatio);
let dppx = await getViewportDevicePixelRatio(ui);
is(dppx, DEFAULT_DPPX, "Content has expected devicePixelRatio");
testViewportDevicePixelRatioSelect(ui, {
value: window.devicePixelRatio,
value: DEFAULT_DPPX,
disabled: false,
});
testViewportDeviceSelectLabel(ui, "no device selected");
@ -55,12 +56,12 @@ async function testDefaults(ui) {
async function testChangingDevice(ui) {
info("Test Changing Device");
let waitPixelRatioChange = onceDevicePixelRatioChange(ui);
let reloaded = waitForViewportLoad(ui);
await selectDevice(ui, testDevice.name);
await reloaded;
await waitForViewportResizeTo(ui, testDevice.width, testDevice.height);
await waitPixelRatioChange;
await testDevicePixelRatio(ui, testDevice.pixelRatio);
let dppx = await waitForDevicePixelRatio(ui, testDevice.pixelRatio);
is(dppx, testDevice.pixelRatio, "Content has expected devicePixelRatio");
testViewportDevicePixelRatioSelect(ui, {
value: testDevice.pixelRatio,
disabled: true,
@ -71,18 +72,17 @@ async function testChangingDevice(ui) {
async function testResetWhenResizingViewport(ui) {
info("Test reset when resizing the viewport");
let waitPixelRatioChange = onceDevicePixelRatioChange(ui);
let deviceRemoved = once(ui, "device-association-removed");
let reloaded = waitForViewportLoad(ui);
await testViewportResize(ui, ".viewport-vertical-resize-handle",
[-10, -10], [testDevice.width, testDevice.height - 10], [0, -10], ui);
await deviceRemoved;
await Promise.all([ deviceRemoved, reloaded ]);
await waitPixelRatioChange;
await testDevicePixelRatio(ui, window.devicePixelRatio);
let dppx = await waitForDevicePixelRatio(ui, DEFAULT_DPPX);
is(dppx, DEFAULT_DPPX, "Content has expected devicePixelRatio");
testViewportDevicePixelRatioSelect(ui, {
value: window.devicePixelRatio,
value: DEFAULT_DPPX,
disabled: false,
});
testViewportDeviceSelectLabel(ui, "no device selected");
@ -91,11 +91,9 @@ async function testResetWhenResizingViewport(ui) {
async function testChangingDevicePixelRatio(ui) {
info("Test changing device pixel ratio");
let waitPixelRatioChange = onceDevicePixelRatioChange(ui);
await selectDevicePixelRatio(ui, VIEWPORT_DPPX);
await waitPixelRatioChange;
await testDevicePixelRatio(ui, VIEWPORT_DPPX);
let dppx = await waitForDevicePixelRatio(ui, VIEWPORT_DPPX);
is(dppx, VIEWPORT_DPPX, "Content has expected devicePixelRatio");
testViewportDevicePixelRatioSelect(ui, {
value: VIEWPORT_DPPX,
disabled: false,
@ -114,33 +112,22 @@ function testViewportDevicePixelRatioSelect(ui, expected) {
`DevicePixelRatio Select should be ${expected.disabled ? "disabled" : "enabled"}.`);
}
async function testDevicePixelRatio(ui, expected) {
info("Test device pixel ratio");
let dppx = await getViewportDevicePixelRatio(ui);
is(dppx, expected, `devicePixelRatio should be: ${expected}`);
}
function onceDevicePixelRatioChange(ui) {
return ContentTask.spawn(ui.getViewportBrowser(), {}, async function () {
info(`Listening for a pixel ratio change (current: ${content.devicePixelRatio}dppx)`);
let pixelRatio = content.devicePixelRatio;
let mql = content.matchMedia(`(resolution: ${pixelRatio}dppx)`);
function waitForDevicePixelRatio(ui, expected) {
return ContentTask.spawn(ui.getViewportBrowser(), { expected }, function (args) {
let initial = content.devicePixelRatio;
info(`Listening for pixel ratio change ` +
`(current: ${initial}, expected: ${args.expected})`);
return new Promise(resolve => {
const onWindowCreated = () => {
if (pixelRatio !== content.devicePixelRatio) {
resolve();
}
};
addEventListener("DOMWindowCreated", onWindowCreated, {once: true});
let mql = content.matchMedia(`(resolution: ${args.expected}dppx)`);
if (mql.matches) {
info(`Ratio already changed to ${args.expected}dppx`);
resolve(content.devicePixelRatio);
return;
}
mql.addListener(function listener() {
info(`Ratio changed to ${args.expected}dppx`);
mql.removeListener(listener);
removeEventListener("DOMWindowCreated", onWindowCreated, {once: true});
resolve();
resolve(content.devicePixelRatio);
});
});
});

View File

@ -45,7 +45,7 @@ async function setViewportSizeWithInputKeys(ui) {
resized = waitForViewportResizeTo(ui, 420, height);
dimensions[0].focus();
for (let i = 1; i <= 100; i++) {
EventUtils.synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
EventUtils.synthesizeKey("KEY_ArrowUp", {});
}
await resized;
@ -53,20 +53,20 @@ async function setViewportSizeWithInputKeys(ui) {
resized = waitForViewportResizeTo(ui, width, height);
dimensions[0].focus();
for (let i = 1; i <= 10; i++) {
EventUtils.synthesizeKey("KEY_ArrowDown", { shiftKey: true, code: "ArrowDown" });
EventUtils.synthesizeKey("KEY_ArrowDown", {shiftKey: true});
}
await resized;
// Increase height value to 600 by using `PageUp + Shift` key
resized = waitForViewportResizeTo(ui, width, 600);
dimensions[1].focus();
EventUtils.synthesizeKey("VK_PAGE_UP", { shiftKey: true });
EventUtils.synthesizeKey("VK_PAGE_UP", {shiftKey: true});
await resized;
// Resetting height value back to 500 by using `PageDown + Shift` key
resized = waitForViewportResizeTo(ui, width, height);
dimensions[1].focus();
EventUtils.synthesizeKey("VK_PAGE_DOWN", { shiftKey: true });
EventUtils.synthesizeKey("VK_PAGE_DOWN", {shiftKey: true});
await resized;
}

View File

@ -132,6 +132,7 @@ function waitForViewportResizeTo(ui, width, height) {
// If the viewport has already the expected size, we resolve the promise immediately.
let size = await getContentSize(ui);
if (isSizeMatching(size)) {
info(`Content already resized to ${width} x ${height}`);
resolve();
return;
}

View File

@ -108,7 +108,7 @@ async function testKeyboardInteraction(tree, win) {
info("Pressing down key to select next item");
event = defer();
tree.once("select", pass);
EventUtils.synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"}, win);
EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
let [name, data, attachment] = await event.promise;
is(name, "select", "Select event was fired after pressing down");
is(data[0], "level1", "Correct item was selected after pressing down");
@ -119,7 +119,7 @@ async function testKeyboardInteraction(tree, win) {
info("Pressing down key again to select next item");
event = defer();
tree.once("select", pass);
EventUtils.synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"}, win);
EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
[name, data, attachment] = await event.promise;
is(data.length, 2, "Correct level item was selected after second down keypress");
is(data[0], "level1", "Correct parent level");
@ -128,7 +128,7 @@ async function testKeyboardInteraction(tree, win) {
info("Pressing down key again to select next item");
event = defer();
tree.once("select", pass);
EventUtils.synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"}, win);
EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
[name, data, attachment] = await event.promise;
is(data.length, 3, "Correct level item was selected after third down keypress");
is(data[0], "level1", "Correct parent level");
@ -138,7 +138,7 @@ async function testKeyboardInteraction(tree, win) {
info("Pressing down key again to select next item");
event = defer();
tree.once("select", pass);
EventUtils.synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"}, win);
EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
[name, data, attachment] = await event.promise;
is(data.length, 2, "Correct level item was selected after fourth down keypress");
is(data[0], "level1", "Correct parent level");
@ -155,7 +155,7 @@ async function testKeyboardInteraction(tree, win) {
event = defer();
node = tree._selectedLabel;
ok(node.hasAttribute("expanded"), "Item is expanded before left keypress");
EventUtils.synthesizeKey("KEY_ArrowLeft", {code: "ArrowLeft"}, win);
EventUtils.synthesizeKey("KEY_ArrowLeft", {}, win);
await event.promise;
ok(!node.hasAttribute("expanded"), "Item is not expanded after left keypress");
@ -168,7 +168,7 @@ async function testKeyboardInteraction(tree, win) {
// parent node should have no effect of this keypress
node = tree.root.children.firstChild.nextSibling.firstChild;
ok(node.hasAttribute("expanded"), "Parent is expanded");
EventUtils.synthesizeKey("KEY_ArrowLeft", {code: "ArrowLeft"}, win);
EventUtils.synthesizeKey("KEY_ArrowLeft", {}, win);
[name, data] = await event.promise;
is(data.length, 3, "Correct level item was selected after second left keypress");
is(data[0], "level1", "Correct parent level");
@ -181,7 +181,7 @@ async function testKeyboardInteraction(tree, win) {
info("Pressing down key to select next item");
event = defer();
tree.once("select", pass);
EventUtils.synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"}, win);
EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
[name, data, attachment] = await event.promise;
is(data.length, 2, "Correct level item was selected after fifth down keypress");
is(data[0], "level1", "Correct parent level");
@ -196,7 +196,7 @@ async function testKeyboardInteraction(tree, win) {
event = defer();
node = tree._selectedLabel;
ok(node.hasAttribute("expanded"), "Item is expanded before left keypress");
EventUtils.synthesizeKey("KEY_ArrowLeft", {code: "ArrowLeft"}, win);
EventUtils.synthesizeKey("KEY_ArrowLeft", {}, win);
await event.promise;
ok(!node.hasAttribute("expanded"), "Item is collapsed after left keypress");
@ -209,7 +209,7 @@ async function testKeyboardInteraction(tree, win) {
event = defer();
node = tree._selectedLabel;
ok(!node.hasAttribute("expanded"), "Item is collapsed before right keypress");
EventUtils.synthesizeKey("KEY_ArrowRight", {code: "ArrowRight"}, win);
EventUtils.synthesizeKey("KEY_ArrowRight", {}, win);
await event.promise;
ok(node.hasAttribute("expanded"), "Item is expanded after right keypress");
@ -223,7 +223,7 @@ async function testKeyboardInteraction(tree, win) {
executeSoon(() => event.resolve(null));
}, {once: true});
info("Pressing down key on last item of the tree");
EventUtils.synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"}, win);
EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
await event.promise;
ok(tree.isSelected(["level1.1", "level2", "level3"]),

View File

@ -10,101 +10,144 @@
height: 100%;
}
#sidebar-panel-fontinspector > .devtools-toolbar {
display: flex;
}
#font-container {
overflow: auto;
flex: auto;
}
#all-fonts {
.fonts-list {
padding: 0;
margin: 0;
}
#font-showall {
cursor: pointer;
flex-shrink: 0;
}
#font-showall:hover {
text-decoration: underline;
}
.font-format::before {
content: "(";
}
.font-format::after {
content: ")";
}
.preview-input-toolbar {
display: flex;
width: 100%;
}
.font-preview-container {
overflow-x: auto;
}
#font-preview-text-input {
margin-top: 1px;
margin-bottom: 1px;
padding-top: 0;
padding-bottom: 0;
flex: 1;
list-style: none;
}
.font {
padding: 10px 10px;
border: 1px solid var(--theme-splitter-color);
border-width: 0 1px 1px 0;
display: grid;
grid-template-columns: 14px auto 1fr;
grid-template-rows: 50px;
grid-column-gap: 10px;
padding: 0 10px 0 5px;
}
.theme-dark .font {
border-bottom: 1px solid #444;
#font-container .theme-twisty {
display: inline-block;
cursor: pointer;
place-self: center;
vertical-align: text-top;
}
.theme-light .font {
border-bottom: 1px solid #DDD;
}
.font:last-of-type {
border-bottom: 0;
}
.theme-light .font:nth-child(even) {
background: #F4F4F4;
.font-preview-container {
grid-column: 3 / -1;
grid-row: 1;
overflow: hidden;
display: grid;
place-items: center end;
position: relative;
}
.font-preview {
margin-left: -4px;
height: 60px;
height: 50px;
display: block;
}
.font-preview:hover {
cursor: text;
background-image: linear-gradient(to right,
var(--grey-40) 3px, transparent 3px, transparent);
background-size: 6px 1px;
background-repeat: repeat-x;
background-position-y: 45px;
}
.font-preview-input {
position: absolute;
top: 0;
left: 0;
width: calc(100% - 5px);
height: calc(100% - 2px);
background: transparent;
color: transparent;
}
.font-preview-input::-moz-selection {
background: transparent;
color: transparent;
}
.font-name {
display: inline;
margin: 0;
font-size: 1em;
white-space: nowrap;
grid-column: 2;
place-self: center start;
}
.font-details {
grid-column: 2 / 4;
padding-inline-end: 14px;
width: 100%;
}
.font-css-code {
max-width: 100%;
direction: ltr;
padding: 5px;
margin: 0;
border: 1px solid var(--theme-splitter-color);
border-radius: 3px;
overflow: hidden;
text-overflow: ellipsis;
padding: 5px;
direction: ltr;
color: var(--theme-toolbar-color);
}
.theme-light .font-css-code,
.theme-light .font-url {
border: 1px solid #CCC;
background: white;
.font-css-code-expander::before {
content: "\2026";
display: inline-block;
width: 12px;
height: 8px;
margin: 0 2px;
line-height: 3px;
color: var(--theme-body-color-inactive);
border-radius: 3px;
border-style: solid;
border-width: 1px;
text-align: center;
vertical-align: middle;
}
.theme-dark .font-css-code,
.theme-dark .font-url {
border: 1px solid #333;
background: black;
color: white;
.font-format-url {
text-transform: capitalize;
margin-block-start: 0;
}
.font-url {
margin-inline-start: 1em;
text-transform: uppercase;
text-decoration: underline;
color: var(--theme-highlight-blue);
background: transparent;
border: none;
cursor: pointer;
}
.font-url::after {
content: "";
display: inline-block;
height: 13px;
width: 13px;
margin: -.3rem .15rem 0 0.25rem;
vertical-align: middle;
background-image: url(chrome://devtools-shim/content/aboutdevtools/images/external-link.svg);
background-repeat: no-repeat;
background-size: 13px 13px;
-moz-context-properties: fill;
fill: var(--blue-60);
}
.font:not(.expanded) .font-css-name,
.font:not(.expanded) .font-css-code,
.font:not(.expanded) .font-format-url {
display: none;
}

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

View File

@ -166,31 +166,31 @@ support-files =
!/devtools/client/shared/test/test-actor-registry.js
[browser_console.js]
skip-if = true # Bug 1406060
skip-if = true # Bug 1437806
[browser_console_addonsdk_loader_exception.js]
skip-if = true # Bug 1406060
skip-if = true # Bug 1437807
[browser_console_clear_method.js]
skip-if = true # Bug 1406060
skip-if = true # Bug 1437843
[browser_console_consolejsm_output.js]
skip-if = true # Bug 1406060
skip-if = true # Bug 1437844
[browser_console_dead_objects.js]
skip-if = true # Bug 1406060
skip-if = true # Bug 1437845
[browser_console_error_source_click.js]
skip-if = true # Bug 1406060
skip-if = true # Bug 1437847
[browser_console_filters.js]
skip-if = true # Bug 1406060
skip-if = true # Bug 1437848
[browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js]
skip-if = true # Bug 1406060
skip-if = true # Bug 1437849
[browser_console_nsiconsolemessage.js]
skip-if = true # Bug 1406060
skip-if = true # Bug 1437850
[browser_console_open_or_focus.js]
skip-if = true # Bug 1406060
skip-if = true # Bug 1437851
[browser_console_restore.js]
skip-if = true # Bug 1406060
skip-if = true # Bug 1437852
[browser_console_webconsole_ctrlw_close_tab.js]
skip-if = true # Bug 1406060
skip-if = true # Bug 1437854
[browser_console_webconsole_iframe_messages.js]
skip-if = true # Bug 1406060
skip-if = true # Bug 1437855
[browser_console_webconsole_private_browsing.js]
skip-if = true # Bug 1403188
# old console skip-if = e10s # Bug 1042253 - webconsole e10s tests
@ -336,8 +336,6 @@ subsuite = clipboard
skip-if = true # Bug 1403450
[browser_webconsole_sandbox_update_after_navigation.js]
[browser_webconsole_script_errordoc_urls.js]
skip-if = true # Bug 1403454
# old console skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
[browser_webconsole_scroll.js]
[browser_webconsole_select_all.js]
[browser_webconsole_show_subresource_security_errors.js]

View File

@ -3,12 +3,15 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from head.js */
// Ensure that [Learn More] links appear alongside any errors listed
// in "errordocs.js". Note: this only tests script execution.
"use strict";
const ErrorDocs = require("devtools/server/actors/errordocs");
const TEST_URI = "data:text/html;charset=utf8,errordoc tests";
function makeURIData(script) {
return `data:text/html;charset=utf8,<script>${script}</script>`;
@ -19,49 +22,51 @@ const TestData = [
jsmsg: "JSMSG_READ_ONLY",
script: "'use strict'; (Object.freeze({name: 'Elsa', score: 157})).score = 0;",
isException: true,
expected: 'TypeError: "score" is read-only',
},
{
jsmsg: "JSMSG_STMT_AFTER_RETURN",
script: "function a() { return; 1 + 1; };",
isException: false,
expected: "unreachable code after return statement",
}
];
add_task(function* () {
yield loadTab("data:text/html;charset=utf8,errordoc tests");
add_task(async function () {
let hud = await openNewTabAndConsole(TEST_URI);
let hud = yield openConsole();
for (let i = 0; i < TestData.length; i++) {
yield testScriptError(hud, TestData[i]);
for (let data of TestData) {
await testScriptError(hud, data);
}
});
function* testScriptError(hud, testData) {
if (testData.isException === true) {
async function testScriptError(hud, testData) {
let isE10s = Services.appinfo.browserTabsRemoteAutostart;
if (testData.isException && !isE10s) {
expectUncaughtException();
}
BrowserTestUtils.loadURI(gBrowser.selectedBrowser, makeURIData(testData.script));
await loadDocument(makeURIData(testData.script));
yield waitForMessages({
webconsole: hud,
messages: [
{
category: CATEGORY_JS
}
]
});
let msg = "the expected error message was displayed";
info(`waiting for ${msg} to be displayed`);
await waitFor(() => findMessage(hud, testData.expected));
ok(true, msg);
// grab the most current error doc URL
let url = ErrorDocs.GetURL({ errorMessageName: testData.jsmsg });
// grab the most current error doc URL.
let urlObj = new URL(ErrorDocs.GetURL({ errorMessageName: testData.jsmsg }));
let hrefs = {};
for (let link of hud.jsterm.outputNode.querySelectorAll("a")) {
hrefs[link.href] = true;
// strip all params from the URL.
let url = `${urlObj.origin}${urlObj.pathname}`;
// Gather all URLs displayed in the console. [Learn More] links have no href
// but have the URL in the title attribute.
let hrefs = new Set();
for (let link of hud.ui.outputNode.querySelectorAll("a")) {
hrefs.add(link.title);
}
ok(url in hrefs, `Expected a link to ${url}.`);
ok(hrefs.has(url), `Expected a link to ${url}.`);
hud.jsterm.clearOutput();
}

View File

@ -31,14 +31,14 @@ const STATUS_CODES_GA_PARAMS = `?${new URLSearchParams({
"utm_campaign": "default"
})}`;
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
registerCleanupFunction(function* () {
Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
Services.prefs.setBoolPref("devtools.browserconsole.new-frontend-enabled", true);
registerCleanupFunction(async function () {
Services.prefs.clearUserPref("devtools.browserconsole.new-frontend-enabled");
Services.prefs.clearUserPref("devtools.webconsole.ui.filterbar");
// Reset all filter prefs between tests. First flushPrefEnv in case one of the
// filter prefs has been pushed for the test
yield SpecialPowers.flushPrefEnv();
await SpecialPowers.flushPrefEnv();
Services.prefs.getChildList("devtools.webconsole.filter").forEach(pref => {
Services.prefs.clearUserPref(pref);
});
@ -47,7 +47,7 @@ registerCleanupFunction(function* () {
if (browserConsole.jsterm) {
browserConsole.jsterm.clearOutput(true);
}
yield HUDService.toggleBrowserConsole();
await HUDService.toggleBrowserConsole();
}
});

View File

@ -62,7 +62,7 @@ add_task(async function() {
// Alt + D is a shortcut key to move focus to the URL bar and selects its text.
info("Pressing Alt + D in the search bar...");
EventUtils.synthesizeKey("d", {code: "KeyD", altKey: true});
EventUtils.synthesizeKey("d", {altKey: true});
await promiseURLBarHasFocus();
await promiseURLBarSelectsAllText();
@ -74,7 +74,7 @@ add_task(async function() {
gURLBar.inputField.value.length;
info("Pressing Alt + D in the URL bar...");
EventUtils.synthesizeKey("d", {code: "KeyD", altKey: true});
EventUtils.synthesizeKey("d", {altKey: true});
await promiseURLBarHasFocus();
await promiseURLBarSelectsAllText();

View File

@ -38,7 +38,7 @@ function setTabFocus() {
var doc = document;
function tab_to(id) {
synthesizeKey("KEY_Tab", {code: "Tab"});
synthesizeKey("KEY_Tab", {});
is(doc.activeElement.id, id, "element with id=" + id + " should have focus");
}

View File

@ -92,7 +92,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=238987
function tab() {
// Send tab key events.
synthesizeKey("KEY_Tab", {code: "Tab", shiftKey: activateShift});
synthesizeKey("KEY_Tab", {shiftKey: activateShift});
if (shouldStop) {
// Did focus handling succeed
is(forwardFocusArray.length, 0,

View File

@ -119,7 +119,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=409604
}
function pressAccessKey(key) {
synthesizeKey(key.key, {code: key.code, altKey: true, shiftKey: true});
synthesizeKey(key.key, {altKey: true, shiftKey: true});
}
function testFocusableElements() {
@ -128,7 +128,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=409604
if (code == "b".charCodeAt(0))
continue;
var accessChar = String.fromCharCode(code).toUpperCase();
pressAccessKey({key: accessChar, code: "Key" + accessChar});
pressAccessKey({key: accessChar});
}
ok(focusArray.length == 0, "(focus) unhandled elements remaining: " + focusArray.join(","));
}
@ -187,7 +187,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=409604
var i, e;
for (i = 0; i < unfocusableTags.length; ++ i) {
createUnfocusableElement(unfocusableTags[i], "z");
pressAccessKey({key: "Z", code:"KeyZ"});
pressAccessKey({key: "Z"});
destroyUnfocusableElement();
}
for (i = 0; i < invalidElements.length; ++ i) {
@ -196,7 +196,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=409604
e.setAttribute("accesskey", "z");
e.setAttribute("onclick", "handleClick(event.target); event.preventDefault();");
e.setAttribute("onfocus", "handleInvalid(event.target);");
pressAccessKey({key: "Z", code:"KeyZ"});
pressAccessKey({key: "Z"});
e.removeAttribute("accesskey");
e.removeAttribute("onclick");
e.removeAttribute("onfocus");

View File

@ -25,7 +25,7 @@ function doTest() {
document.getElementById('a').focus();
is(document.activeElement, document.getElementById('a'), "link should have focus");
is(document.hasFocus(), true, "document should be focused");
synthesizeKey("KEY_Tab", {code: "Tab"});
synthesizeKey("KEY_Tab", {});
is(document.activeElement, document.getElementById('a'), "body element should be focused");
is(document.hasFocus(), false, "document should not be focused");

View File

@ -98,14 +98,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=493251
function(e) { dumpEvent(e); ++mouseClick; }, true);
ok(true, "doTest #1...");
synthesizeKey("a", {code: "KeyA"}, win);
synthesizeKey("a", {}, win);
is(keyDown, 1, "Wrong number events (1)");
is(keyPress, 1, "Wrong number events (2)");
is(keyUp, 1, "Wrong number events (3)");
ok(true, "doTest #2...");
suppressEventHandling(true);
synthesizeKey("a", {code: "KeyA"}, win);
synthesizeKey("a", {}, win);
is(keyDown, 1, "Wrong number events (4)");
is(keyPress, 1, "Wrong number events (5)");
is(keyUp, 1, "Wrong number events (6)");
@ -120,7 +120,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=493251
function continueTest1() {
ok(true, "continueTest1...");
win.addEventListener("keydown", () => { suppressEventHandling(true); }, {once: true});
synthesizeKey("a", {code: "KeyA"}, win);
synthesizeKey("a", {}, win);
is(keyDown, 2, "Wrong number events (10)");
is(keyPress, 1, "Wrong number events (11)");
is(keyUp, 1, "Wrong number events (12)");

View File

@ -46,7 +46,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=545268
subwin.addEventListener("mouseup", function(e) { ++mouseUp; }, true);
subwin.addEventListener("click", function(e) { ++mouseClick; }, true);
synthesizeKey("a", {code: "KeyA"}, subwin);
synthesizeKey("a", {}, subwin);
is(keyDown, 1, "Wrong number events (1)");
is(keyPress, 1, "Wrong number events (2)");
is(keyUp, 1, "Wrong number events (3)");
@ -54,7 +54,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=545268
// Test that suppressing events on the parent window prevents key
// events in the subdocument window
utils.suppressEventHandling(true);
synthesizeKey("a", {code: "KeyA"}, subwin);
synthesizeKey("a", {}, subwin);
is(keyDown, 1, "Wrong number events (4)");
is(keyPress, 1, "Wrong number events (5)");
is(keyUp, 1, "Wrong number events (6)");
@ -69,7 +69,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=545268
function continueTest1() {
var utils = SpecialPowers.getDOMWindowUtils(win);
subwin.addEventListener("keydown", () => { utils.suppressEventHandling(true); }, {once: true});
synthesizeKey("a", {code: "KeyA"}, subwin);
synthesizeKey("a", {}, subwin);
is(keyDown, 2, "Wrong number events (10)");
is(keyPress, 1, "Wrong number events (11)");
is(keyUp, 1, "Wrong number events (12)");

View File

@ -84,9 +84,9 @@ async function runTests()
iframe.contentWindow.addEventListener("scroll", onScroll, { once: true });
if (aVertical) {
synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
synthesizeKey("KEY_ArrowDown", {});
} else {
synthesizeKey("KEY_ArrowRight", { code: "ArrowRight" });
synthesizeKey("KEY_ArrowRight", {});
}
});
}

View File

@ -73,7 +73,7 @@ if (observers.hasMoreElements()) {
"control used for the submission");
var c = document.getElementById('c');
c.focus();
synthesizeKey("KEY_Enter", { code: "Enter" });
synthesizeKey("KEY_Enter", {});
});
document.getElementById('c').addEventListener("invalid", function(aEvent) {

View File

@ -210,11 +210,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
if (isDesktop) { // up/down arrow keys not supported on android/b2g
number.value = "";
number.focus();
synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
synthesizeKey("KEY_ArrowUp", {});
is(numberInput, 1, "input event should be dispatched for up/down arrow key keypress");
is(number.value, "1", "sanity check value of number control after keypress");
synthesizeKey("KEY_ArrowDown", { code: "ArrowDown", repeat: 3 });
synthesizeKey("KEY_ArrowDown", {repeat: 3});
is(numberInput, 4, "input event should be dispatched for each up/down arrow key keypress event, even when rapidly repeated");
is(number.value, "-2", "sanity check value of number control after multiple keydown events");

View File

@ -47,7 +47,7 @@ document.forms[1].addEventListener("submit", function(aEvent) {
ok(true, "novalidate has been correctly used for second form");
var c = document.getElementById('c');
c.focus();
synthesizeKey("KEY_Enter", { code: "Enter" });
synthesizeKey("KEY_Enter", {});
}, {once: true});
document.forms[2].addEventListener("submit", function(aEvent) {

View File

@ -54,15 +54,15 @@ function runTests() {
});
number.focus();
synthesizeKey("KEY_ArrowDown", {type: "keydown", code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {type: "keydown", code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {type: "keyup", code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {type: "keydown"});
synthesizeKey("KEY_ArrowDown", {type: "keydown"});
synthesizeKey("KEY_ArrowDown", {type: "keyup"});
number.blur();
range.focus();
waiting_event_idx = 0;
synthesizeKey("KEY_ArrowDown", {type: "keydown", code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {type: "keydown", code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {type: "keyup", code: "ArrowDown"});
synthesizeKey("KEY_ArrowDown", {type: "keydown"});
synthesizeKey("KEY_ArrowDown", {type: "keydown"});
synthesizeKey("KEY_ArrowDown", {type: "keyup"});
SimpleTest.finish();
}

View File

@ -101,7 +101,7 @@ function runTest()
document.getElementById('b').click();
var c = document.getElementById('c');
c.focus();
synthesizeKey("KEY_Enter", { code: "Enter" });
synthesizeKey("KEY_Enter", {});
document.getElementById('s2').click();
}

View File

@ -1543,25 +1543,9 @@ StartMacOSContentSandbox()
MOZ_CRASH("Error resolving child process path");
}
// During sandboxed content process startup, before reaching
// this point, NS_OS_TEMP_DIR is modified to refer to a sandbox-
// writable temporary directory
nsCOMPtr<nsIFile> tempDir;
nsresult rv = nsDirectoryService::gService->Get(NS_OS_TEMP_DIR,
NS_GET_IID(nsIFile), getter_AddRefs(tempDir));
if (NS_FAILED(rv)) {
MOZ_CRASH("Failed to get NS_OS_TEMP_DIR");
}
nsAutoCString tempDirPath;
tempDir->Normalize();
rv = tempDir->GetNativePath(tempDirPath);
if (NS_FAILED(rv)) {
MOZ_CRASH("Failed to get NS_OS_TEMP_DIR path");
}
ContentChild* cc = ContentChild::GetSingleton();
nsresult rv;
nsCOMPtr<nsIFile> profileDir;
cc->GetProfileDir(getter_AddRefs(profileDir));
nsCString profileDirPath;
@ -1584,7 +1568,6 @@ StartMacOSContentSandbox()
info.appPath.assign(appPath.get());
info.appBinaryPath.assign(appBinaryPath.get());
info.appDir.assign(appDir.get());
info.appTempDir.assign(tempDirPath.get());
info.hasAudio = !Preferences::GetBool("media.cubeb.sandbox");
// These paths are used to whitelist certain directories used by the testing

View File

@ -46,28 +46,16 @@ SetTmpEnvironmentVariable(nsIFile* aValue)
}
#endif
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
static void
SetTmpEnvironmentVariable(nsIFile* aValue)
{
nsAutoCString fullTmpPath;
nsresult rv = aValue->GetNativePath(fullTmpPath);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
Unused << NS_WARN_IF(setenv("TMPDIR", fullTmpPath.get(), 1) != 0);
}
#endif
#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
static void
SetUpSandboxEnvironment()
{
MOZ_ASSERT(nsDirectoryService::gService,
"SetUpSandboxEnvironment relies on nsDirectoryService being initialized");
// On macOS and Windows, a sandbox-writable temp directory is used whenever
// the sandbox is enabled.
// On Windows, a sandbox-writable temp directory is used whenever the sandbox
// is enabled.
if (!IsContentSandboxEnabled()) {
return;
}
@ -247,7 +235,7 @@ ContentProcess::Init(int aArgc, char* aArgv[])
mContent.SetProfileDir(profileDir);
#endif
#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
SetUpSandboxEnvironment();
#endif

View File

@ -235,6 +235,7 @@ nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo,
mFullPath(aPluginInfo->fFullPath),
mLastModifiedTime(aLastModifiedTime),
mSandboxLevel(0),
mIsSandboxLoggingEnabled(false),
mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
mCachedBlocklistStateValid(false),
mIsFromExtension(fromExtension)
@ -270,6 +271,7 @@ nsPluginTag::nsPluginTag(const char* aName,
mFullPath(aFullPath),
mLastModifiedTime(aLastModifiedTime),
mSandboxLevel(0),
mIsSandboxLoggingEnabled(false),
mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
mCachedBlocklistStateValid(false),
mIsFromExtension(fromExtension)
@ -306,6 +308,7 @@ nsPluginTag::nsPluginTag(uint32_t aId,
mSupportsAsyncRender(aSupportsAsyncRender),
mLastModifiedTime(aLastModifiedTime),
mSandboxLevel(aSandboxLevel),
mIsSandboxLoggingEnabled(false),
mNiceFileName(),
mCachedBlocklistState(aBlocklistState),
mCachedBlocklistStateValid(true),
@ -421,6 +424,30 @@ nsPluginTag::InitSandboxLevel()
}
#endif
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
// At present, the Mac Flash NPAPI plugin sandbox is controlled via
// a boolean with no support for different levels. When the sandbox
// is enabled, we set the level to 1.
if (mIsFlashPlugin) {
// Allow enabling the sandbox via the pref
// security.sandbox.mac.flash.enabled or via the environment variable
// MOZ_SANDBOX_MAC_FLASH_FORCE (which is useful while the sandbox is
// off by default).
if (Preferences::GetBool("security.sandbox.mac.flash.enabled") ||
PR_GetEnv("MOZ_SANDBOX_MAC_FLASH_FORCE")) {
mSandboxLevel = 1;
// Enable sandbox logging in the plugin process if it has
// been turned on via prefs or environment variables.
if (Preferences::GetBool("security.sandbox.logging.enabled") ||
PR_GetEnv("MOZ_SANDBOX_LOGGING") ||
PR_GetEnv("MOZ_SANDBOX_MAC_FLASH_LOGGING")) {
mIsSandboxLoggingEnabled = true;
}
}
}
#endif
}
#if !defined(XP_WIN) && !defined(XP_MACOSX)

View File

@ -173,6 +173,7 @@ public:
int64_t mLastModifiedTime;
nsCOMPtr<nsITimer> mUnloadTimer;
int32_t mSandboxLevel;
bool mIsSandboxLoggingEnabled;
void InvalidateBlocklistState();

View File

@ -54,6 +54,10 @@
#include "ChildProfilerController.h"
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
#include "mozilla/Sandbox.h"
#endif
using namespace mozilla;
using namespace mozilla::ipc;
using namespace mozilla::plugins;
@ -99,6 +103,10 @@ PluginModuleChild::PluginModuleChild(bool aIsChrome)
, mGlobalCallWndProcHook(nullptr)
, mAsyncRenderSupport(false)
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
, mEnableFlashSandbox(false)
, mEnableFlashSandboxLogging(false)
#endif
{
memset(&mFunctions, 0, sizeof(mFunctions));
if (mIsChrome) {
@ -191,6 +199,15 @@ PluginModuleChild::RecvDisableFlashProtectedMode()
return IPC_OK();
}
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
void
PluginModuleChild::EnableFlashSandbox(bool aShouldEnableLogging)
{
mEnableFlashSandbox = true;
mEnableFlashSandboxLogging = aShouldEnableLogging;
}
#endif
bool
PluginModuleChild::InitForChrome(const std::string& aPluginFilename,
base::ProcessId aParentPid,
@ -282,6 +299,22 @@ PluginModuleChild::InitForChrome(const std::string& aPluginFilename,
# error Please copy the initialization code from nsNPAPIPlugin.cpp
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
if (mEnableFlashSandbox) {
MacSandboxInfo flashSandboxInfo;
flashSandboxInfo.type = MacSandboxType_Plugin;
flashSandboxInfo.pluginInfo.type = MacSandboxPluginType_Flash;
flashSandboxInfo.pluginInfo.pluginBinaryPath = aPluginFilename;
flashSandboxInfo.shouldLog = mEnableFlashSandboxLogging;
std::string sbError;
if (!mozilla::StartMacSandbox(flashSandboxInfo, sbError)) {
fprintf(stderr, "Failed to start sandbox:\n%s\n", sbError.c_str());
return false;
}
}
#endif
return true;

View File

@ -313,7 +313,17 @@ public: // called by PluginInstanceChild
return mFunctions.destroy(instance->GetNPP(), 0);
}
#if defined(OS_MACOSX) && defined(MOZ_SANDBOX)
void EnableFlashSandbox(bool aShouldEnableLogging);
#endif
private:
#if defined(OS_MACOSX) && defined(MOZ_SANDBOX)
bool mEnableFlashSandbox;
bool mEnableFlashSandboxLogging;
#endif
#if defined(OS_WIN)
virtual void EnteredCall() override;
virtual void ExitedCall() override;

Some files were not shown because too many files have changed in this diff Show More