зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
--HG-- extra : rebase_source : 3825f1263f779efcb8dca836b3de0571b536d98e
This commit is contained in:
Коммит
f62c999a8d
|
@ -119,7 +119,7 @@ tasks:
|
|||
TASKCLUSTER_CACHES: /builds/worker/checkouts
|
||||
- $if: 'tasks_for == "action"'
|
||||
then:
|
||||
ACTION_TASK_GROUP_ID: '${taskGroupId}' # taskGroupId of the target task
|
||||
ACTION_TASK_GROUP_ID: '${action.taskGroupId}' # taskGroupId of the target task
|
||||
ACTION_TASK_ID: {$json: {$eval: 'taskId'}} # taskId of the target task (JSON-encoded)
|
||||
ACTION_INPUT: {$json: {$eval: 'input'}}
|
||||
ACTION_CALLBACK: '${action.cb_name}'
|
||||
|
|
|
@ -11,7 +11,7 @@ function hideSelectPopup(selectPopup, mode = "enter", win = window) {
|
|||
} else if (mode == "enter") {
|
||||
EventUtils.synthesizeKey("KEY_Enter", {}, win);
|
||||
} else if (mode == "click") {
|
||||
EventUtils.synthesizeMouseAtCenter(selectPopup.lastChild, { }, win);
|
||||
EventUtils.synthesizeMouseAtCenter(selectPopup.lastElementChild, { }, win);
|
||||
}
|
||||
|
||||
return selectClosedPromise;
|
||||
|
|
|
@ -47,6 +47,17 @@ if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") {
|
|||
);
|
||||
}
|
||||
|
||||
// We'll assume the changes we are seeing are due to this focus change if
|
||||
// there are at least 5 areas that changed near the top of the screen, or if
|
||||
// the toolbar background is involved on OSX, but will only ignore this once.
|
||||
function isLikelyFocusChange(rects) {
|
||||
if (rects.length > 5 && rects.every(r => r.y2 < 100))
|
||||
return true;
|
||||
if (Services.appinfo.OS == "Darwin" && rects.length == 2 && rects.every(r => r.y1 == 0 && r.h == 33))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* This test ensures that there are no unexpected
|
||||
* uninterruptible reflows or flickering areas when opening new windows.
|
||||
|
@ -71,11 +82,7 @@ add_task(async function() {
|
|||
filter(rects, frame, previousFrame) {
|
||||
// The first screenshot we get in OSX / Windows shows an unfocused browser
|
||||
// window for some reason. See bug 1445161.
|
||||
//
|
||||
// We'll assume the changes we are seeing are due to this focus change if
|
||||
// there are at least 5 areas that changed near the top of the screen, but
|
||||
// will only ignore this once (hence the alreadyFocused variable).
|
||||
if (!alreadyFocused && rects.length > 5 && rects.every(r => r.y2 < 100)) {
|
||||
if (!alreadyFocused && isLikelyFocusChange(rects)) {
|
||||
alreadyFocused = true;
|
||||
todo(false,
|
||||
"bug 1445161 - the window should be focused at first paint, " +
|
||||
|
|
|
@ -27,7 +27,7 @@ add_task(async function testMainViewVisible() {
|
|||
|
||||
await BrowserTestUtils.withNewTab(AUTOPLAY_PAGE, async function() {
|
||||
let permissionsList = document.getElementById("identity-popup-permission-list");
|
||||
let emptyLabel = permissionsList.nextSibling.nextSibling;
|
||||
let emptyLabel = permissionsList.nextElementSibling.nextElementSibling;
|
||||
|
||||
ok(BrowserTestUtils.is_hidden(autoplayBlockedIcon()), "Blocked icon not shown");
|
||||
|
||||
|
@ -40,7 +40,7 @@ add_task(async function testMainViewVisible() {
|
|||
|
||||
await BrowserTestUtils.withNewTab(AUTOPLAY_PAGE, async function() {
|
||||
let permissionsList = document.getElementById("identity-popup-permission-list");
|
||||
let emptyLabel = permissionsList.nextSibling.nextSibling;
|
||||
let emptyLabel = permissionsList.nextElementSibling.nextElementSibling;
|
||||
|
||||
ok(!BrowserTestUtils.is_hidden(autoplayBlockedIcon()), "Blocked icon is shown");
|
||||
|
||||
|
|
|
@ -289,7 +289,7 @@ function triggerSecondaryCommand(popup, index) {
|
|||
}
|
||||
|
||||
// Extra secondary actions appear in a menu.
|
||||
notification.secondaryButton.nextSibling.nextSibling.focus();
|
||||
notification.secondaryButton.nextElementSibling.nextElementSibling.focus();
|
||||
|
||||
popup.addEventListener("popupshown", function() {
|
||||
info("Command popup open for notification " + notification.id);
|
||||
|
|
|
@ -278,7 +278,7 @@ function promisePageActionViewShown() {
|
|||
}
|
||||
|
||||
function promisePageActionViewChildrenVisible(panelViewNode) {
|
||||
return promiseNodeVisible(panelViewNode.firstChild.firstChild);
|
||||
return promiseNodeVisible(panelViewNode.firstElementChild.firstElementChild);
|
||||
}
|
||||
|
||||
function promiseNodeVisible(node) {
|
||||
|
|
|
@ -31,7 +31,7 @@ function promisePopupNotificationShown(name) {
|
|||
ok(PopupNotifications.isPanelOpen, "notification panel open");
|
||||
|
||||
PopupNotifications.panel.removeEventListener("popupshown", popupshown);
|
||||
resolve(PopupNotifications.panel.firstChild);
|
||||
resolve(PopupNotifications.panel.firstElementChild);
|
||||
}
|
||||
|
||||
PopupNotifications.panel.addEventListener("popupshown", popupshown);
|
||||
|
|
|
@ -298,7 +298,7 @@ function promisePopupNotificationShown(aName, aAction) {
|
|||
PopupNotifications.panel.addEventListener("popupshown", function() {
|
||||
ok(!!PopupNotifications.getNotification(aName), aName + " notification shown");
|
||||
ok(PopupNotifications.isPanelOpen, "notification panel open");
|
||||
ok(!!PopupNotifications.panel.firstChild, "notification panel populated");
|
||||
ok(!!PopupNotifications.panel.firstElementChild, "notification panel populated");
|
||||
|
||||
executeSoon(resolve);
|
||||
}, {once: true});
|
||||
|
@ -341,7 +341,7 @@ const kActionDeny = 2;
|
|||
const kActionNever = 3;
|
||||
|
||||
function activateSecondaryAction(aAction) {
|
||||
let notification = PopupNotifications.panel.firstChild;
|
||||
let notification = PopupNotifications.panel.firstElementChild;
|
||||
switch (aAction) {
|
||||
case kActionNever:
|
||||
notification.checkbox.setAttribute("checked", true); // fallthrough
|
||||
|
|
|
@ -283,7 +283,7 @@ AreaPositionManager.prototype = {
|
|||
_getVisibleSiblingForDirection(aNode, aDirection) {
|
||||
let rv = aNode;
|
||||
do {
|
||||
rv = rv[aDirection + "Sibling"];
|
||||
rv = rv[aDirection + "ElementSibling"];
|
||||
} while (rv && rv.getAttribute("hidden") == "true");
|
||||
return rv;
|
||||
},
|
||||
|
|
|
@ -6,6 +6,8 @@ ChromeUtils.defineModuleGetter(this, "CustomizableUI",
|
|||
"resource:///modules/CustomizableUI.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "clearTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "ExtensionTelemetry",
|
||||
"resource://gre/modules/ExtensionTelemetry.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "setTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "ViewPopup",
|
||||
|
@ -14,7 +16,6 @@ ChromeUtils.defineModuleGetter(this, "ViewPopup",
|
|||
var {
|
||||
DefaultWeakMap,
|
||||
ExtensionError,
|
||||
ExtensionTelemetry,
|
||||
} = ExtensionUtils;
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "ExtensionTelemetry",
|
||||
"resource://gre/modules/ExtensionTelemetry.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "PageActions",
|
||||
"resource:///modules/PageActions.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "PanelPopup",
|
||||
|
@ -16,7 +18,6 @@ var {
|
|||
|
||||
var {
|
||||
DefaultWeakMap,
|
||||
ExtensionTelemetry,
|
||||
} = ExtensionUtils;
|
||||
|
||||
// WeakMap[Extension -> PageAction]
|
||||
|
|
|
@ -79,6 +79,7 @@ skip-if = (verify && (os == 'linux' || os == 'mac'))
|
|||
[browser_ext_commands_update.js]
|
||||
[browser_ext_connect_and_move_tabs.js]
|
||||
[browser_ext_contentscript_connect.js]
|
||||
[browser_ext_contentscript_nontab_connect.js]
|
||||
[browser_ext_contextMenus.js]
|
||||
[browser_ext_contextMenus_checkboxes.js]
|
||||
[browser_ext_contextMenus_commands.js]
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
// This script is loaded in a non-tab extension context, and starts the test by
|
||||
// loading an iframe that runs contentScript as a content script.
|
||||
function extensionScript() {
|
||||
let FRAME_URL = browser.runtime.getManifest().content_scripts[0].matches[0];
|
||||
// Cannot use :8888 in the manifest because of bug 1468162.
|
||||
FRAME_URL = FRAME_URL.replace("mochi.test", "mochi.test:8888");
|
||||
|
||||
browser.runtime.onConnect.addListener((port) => {
|
||||
browser.test.assertEq(port.sender.tab, undefined, "Sender is not a tab");
|
||||
browser.test.assertEq(port.sender.url, FRAME_URL, "Expected sender URL");
|
||||
port.onMessage.addListener(msg => {
|
||||
browser.test.assertEq("pong", msg, "Reply from content script");
|
||||
port.disconnect();
|
||||
});
|
||||
port.postMessage("ping");
|
||||
});
|
||||
|
||||
browser.test.log(`Going to open ${FRAME_URL} at ${location.pathname}`);
|
||||
let f = document.createElement("iframe");
|
||||
f.src = FRAME_URL;
|
||||
document.body.appendChild(f);
|
||||
}
|
||||
|
||||
function contentScript() {
|
||||
browser.test.log(`Running content script at ${document.URL}`);
|
||||
|
||||
let port = browser.runtime.connect();
|
||||
port.onMessage.addListener(msg => {
|
||||
browser.test.assertEq("ping", msg, "Expected message to content script");
|
||||
port.postMessage("pong");
|
||||
});
|
||||
port.onDisconnect.addListener(() => {
|
||||
browser.test.sendMessage("disconnected_in_content_script");
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function connect_from_background_frame() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
content_scripts: [{
|
||||
matches: ["http://mochi.test/?background"],
|
||||
js: ["contentscript.js"],
|
||||
all_frames: true,
|
||||
}],
|
||||
},
|
||||
files: {
|
||||
"contentscript.js": contentScript,
|
||||
},
|
||||
background: extensionScript,
|
||||
});
|
||||
await extension.startup();
|
||||
await extension.awaitMessage("disconnected_in_content_script");
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function connect_from_sidebar_panel() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
useAddonManager: "temporary", // To automatically show sidebar on load.
|
||||
manifest: {
|
||||
content_scripts: [{
|
||||
matches: ["http://mochi.test/?sidebar"],
|
||||
js: ["contentscript.js"],
|
||||
all_frames: true,
|
||||
}],
|
||||
sidebar_action: {
|
||||
default_panel: "sidebar.html",
|
||||
},
|
||||
},
|
||||
files: {
|
||||
"contentscript.js": contentScript,
|
||||
"sidebar.html": `<!DOCTYPE html><meta charset="utf-8"><body><script src="sidebar.js"></script></body>`,
|
||||
"sidebar.js": extensionScript,
|
||||
},
|
||||
});
|
||||
await extension.startup();
|
||||
await extension.awaitMessage("disconnected_in_content_script");
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function connect_from_browser_action_popup() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
content_scripts: [{
|
||||
matches: ["http://mochi.test/?browser_action_popup"],
|
||||
js: ["contentscript.js"],
|
||||
all_frames: true,
|
||||
}],
|
||||
browser_action: {
|
||||
default_popup: "popup.html",
|
||||
},
|
||||
},
|
||||
files: {
|
||||
"contentscript.js": contentScript,
|
||||
"popup.html": `<!DOCTYPE html><meta charset="utf-8"><body><script src="popup.js"></script></body>`,
|
||||
"popup.js": extensionScript,
|
||||
},
|
||||
});
|
||||
await extension.startup();
|
||||
await clickBrowserAction(extension);
|
||||
await extension.awaitMessage("disconnected_in_content_script");
|
||||
await closeBrowserAction(extension);
|
||||
await extension.unload();
|
||||
});
|
|
@ -398,7 +398,7 @@ async function openChromeContextMenu(menuId, target, win = window) {
|
|||
}
|
||||
|
||||
async function openSubmenu(submenuItem, win = window) {
|
||||
const submenu = submenuItem.firstChild;
|
||||
const submenu = submenuItem.firstElementChild;
|
||||
const shown = BrowserTestUtils.waitForEvent(submenu, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(submenuItem, {}, win);
|
||||
await shown;
|
||||
|
|
|
@ -26,28 +26,32 @@ add_task(async function test() {
|
|||
|
||||
// Step 1
|
||||
let non_private_browser = gBrowser.selectedBrowser;
|
||||
non_private_browser.loadURI(prefix + "?action=set&name=test&value=value&initial=true");
|
||||
await BrowserTestUtils.browserLoaded(non_private_browser);
|
||||
let url = prefix + "?action=set&name=test&value=value&initial=true";
|
||||
non_private_browser.loadURI(url);
|
||||
await BrowserTestUtils.browserLoaded(non_private_browser, false, url);
|
||||
|
||||
|
||||
// Step 2
|
||||
let private_window = await BrowserTestUtils.openNewBrowserWindow({ private: true });
|
||||
let private_browser = private_window.getBrowser().selectedBrowser;
|
||||
private_browser.loadURI(prefix + "?action=set&name=test2&value=value2");
|
||||
await BrowserTestUtils.browserLoaded(private_browser);
|
||||
url = prefix + "?action=set&name=test2&value=value2";
|
||||
private_browser.loadURI(url);
|
||||
await BrowserTestUtils.browserLoaded(private_browser, false, url);
|
||||
|
||||
|
||||
// Step 3
|
||||
non_private_browser.loadURI(prefix + "?action=get&name=test2");
|
||||
await BrowserTestUtils.browserLoaded(non_private_browser);
|
||||
url = prefix + "?action=get&name=test2";
|
||||
non_private_browser.loadURI(url);
|
||||
await BrowserTestUtils.browserLoaded(non_private_browser, false, url);
|
||||
let elts = await getElts(non_private_browser);
|
||||
isnot(elts[0], "value2", "public window shouldn't see private storage");
|
||||
is(elts[1], "1", "public window should only see public items");
|
||||
|
||||
|
||||
// Step 4
|
||||
private_browser.loadURI(prefix + "?action=get&name=test");
|
||||
await BrowserTestUtils.browserLoaded(private_browser);
|
||||
url = prefix + "?action=get&name=test";
|
||||
private_browser.loadURI(url);
|
||||
await BrowserTestUtils.browserLoaded(private_browser, false, url);
|
||||
elts = await getElts(private_browser);
|
||||
isnot(elts[0], "value", "private window shouldn't see public storage");
|
||||
is(elts[1], "1", "private window should only see private items");
|
||||
|
@ -61,8 +65,9 @@ add_task(async function test() {
|
|||
await new Promise(resolve => Cu.schedulePreciseGC(resolve));
|
||||
private_browser = private_window.getBrowser().selectedBrowser;
|
||||
|
||||
private_browser.loadURI(prefix + "?action=get&name=test2");
|
||||
await BrowserTestUtils.browserLoaded(private_browser, false, prefix + "?action=get&name=test2");
|
||||
url = prefix + "?action=get&name=test2";
|
||||
private_browser.loadURI(url);
|
||||
await BrowserTestUtils.browserLoaded(private_browser, false, url);
|
||||
elts = await getElts(private_browser);
|
||||
isnot(elts[0], "value2", "public window shouldn't see cleared private storage");
|
||||
is(elts[1], "1", "public window should only see public items");
|
||||
|
@ -76,13 +81,15 @@ add_task(async function test() {
|
|||
await new Promise(resolve => Cu.schedulePreciseGC(resolve));
|
||||
private_browser = private_window.getBrowser().selectedBrowser;
|
||||
|
||||
private_browser.loadURI(prefix + "?action=set&name=test3&value=value3");
|
||||
await BrowserTestUtils.browserLoaded(private_browser);
|
||||
url = prefix + "?action=set&name=test3&value=value3";
|
||||
private_browser.loadURI(url);
|
||||
await BrowserTestUtils.browserLoaded(private_browser, false, url);
|
||||
elts = await getElts(private_browser);
|
||||
is(elts[1], "1", "private window should only see new private items");
|
||||
|
||||
// Cleanup.
|
||||
non_private_browser.loadURI(prefix + "?final=true");
|
||||
await BrowserTestUtils.browserLoaded(non_private_browser);
|
||||
url = prefix + "?final=true";
|
||||
non_private_browser.loadURI(url);
|
||||
await BrowserTestUtils.browserLoaded(non_private_browser, false, url);
|
||||
private_window.close();
|
||||
});
|
||||
|
|
|
@ -56,8 +56,8 @@ function promisePageActionViewChildrenVisible(panelViewNode) {
|
|||
info("promisePageActionViewChildrenVisible waiting for a child node to be visible");
|
||||
let dwu = window.windowUtils;
|
||||
return BrowserTestUtils.waitForCondition(() => {
|
||||
let bodyNode = panelViewNode.firstChild;
|
||||
for (let childNode of bodyNode.childNodes) {
|
||||
let bodyNode = panelViewNode.firstElementChild;
|
||||
for (let childNode of bodyNode.children) {
|
||||
let bounds = dwu.getBoundsWithoutFlushing(childNode);
|
||||
if (bounds.width > 0 && bounds.height > 0) {
|
||||
return true;
|
||||
|
|
|
@ -18,12 +18,12 @@ body:not([lwt-sidebar]) .content-container {
|
|||
font-size: 12px;
|
||||
}
|
||||
|
||||
.item.selected > .item-title-container {
|
||||
body:not([lwt-sidebar]) .item.selected > .item-title-container {
|
||||
-moz-appearance: -moz-mac-source-list-selection;
|
||||
-moz-font-smoothing-background-color: -moz-mac-source-list-selection;
|
||||
}
|
||||
|
||||
.item.selected:focus > .item-title-container {
|
||||
body:not([lwt-sidebar-highlight]) .item.selected:focus > .item-title-container {
|
||||
-moz-appearance: -moz-mac-active-source-list-selection;
|
||||
-moz-font-smoothing-background-color: -moz-mac-active-source-list-selection;
|
||||
}
|
||||
|
|
|
@ -196,7 +196,6 @@ body {
|
|||
.sync-state > p {
|
||||
padding-inline-end: 10px;
|
||||
padding-inline-start: 10px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.text-link {
|
||||
|
@ -255,6 +254,11 @@ body {
|
|||
margin: 0 auto;
|
||||
}
|
||||
|
||||
body[lwt-sidebar] .deck .instructions {
|
||||
color: inherit;
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.deck .button {
|
||||
display: block;
|
||||
background-color: #0060df;
|
||||
|
@ -309,12 +313,10 @@ body[lwt-sidebar] .item.selected > .item-title-container {
|
|||
}
|
||||
|
||||
body[lwt-sidebar-brighttext] .item.selected > .item-title-container {
|
||||
-moz-appearance: none;
|
||||
background-color: rgba(249,249,250,.1);
|
||||
}
|
||||
|
||||
body[lwt-sidebar-highlight] .item.selected:focus > .item-title-container {
|
||||
-moz-appearance: none;
|
||||
background-color: var(--lwt-sidebar-highlight-background-color);
|
||||
color: var(--lwt-sidebar-highlight-text-color);
|
||||
}
|
||||
|
|
|
@ -24,11 +24,11 @@ add_task(async function() {
|
|||
ok(pageMenuSep && !pageMenuSep.hidden,
|
||||
"Page menu separator should be shown");
|
||||
|
||||
let testMenuSep = pageMenuSep.previousSibling;
|
||||
let testMenuSep = pageMenuSep.previousElementSibling;
|
||||
ok(testMenuSep && !testMenuSep.hidden,
|
||||
"User-added menu separator should be shown");
|
||||
|
||||
let testMenuItem = testMenuSep.previousSibling;
|
||||
let testMenuItem = testMenuSep.previousElementSibling;
|
||||
is(testMenuItem.label, "Test Context Menu Click", "Got context menu item");
|
||||
|
||||
let promiseCtxMenuClick = ContentTask.spawn(aBrowser, null, async function() {
|
||||
|
|
|
@ -834,7 +834,8 @@ ParticularProcessPriorityManager::CurrentPriority()
|
|||
ProcessPriority
|
||||
ParticularProcessPriorityManager::ComputePriority()
|
||||
{
|
||||
if (!mActiveTabParents.IsEmpty()) {
|
||||
if (!mActiveTabParents.IsEmpty() ||
|
||||
mContentParent->GetRemoteType().EqualsLiteral(EXTENSION_REMOTE_TYPE)) {
|
||||
return PROCESS_PRIORITY_FOREGROUND;
|
||||
}
|
||||
|
||||
|
|
|
@ -118,7 +118,7 @@ add_task(async function test_register() {
|
|||
|
||||
// Cancel the request.
|
||||
ok(active, "request should still be active");
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
PopupNotifications.panel.firstElementChild.button.click();
|
||||
await request;
|
||||
|
||||
// Close tab.
|
||||
|
@ -139,7 +139,7 @@ add_task(async function test_sign() {
|
|||
|
||||
// Cancel the request.
|
||||
ok(active, "request should still be active");
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
PopupNotifications.panel.firstElementChild.button.click();
|
||||
await request;
|
||||
|
||||
// Close tab.
|
||||
|
@ -159,7 +159,7 @@ add_task(async function test_register_direct_cancel() {
|
|||
|
||||
// Cancel the request.
|
||||
ok(active, "request should still be active");
|
||||
PopupNotifications.panel.firstChild.secondaryButton.click();
|
||||
PopupNotifications.panel.firstElementChild.secondaryButton.click();
|
||||
await promise;
|
||||
|
||||
// Close tab.
|
||||
|
@ -186,7 +186,7 @@ add_task(async function test_register_direct_proceed() {
|
|||
await promiseNotification("webauthn-prompt-register-direct");
|
||||
|
||||
// Proceed.
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
PopupNotifications.panel.firstElementChild.button.click();
|
||||
|
||||
// Ensure we got "direct" attestation.
|
||||
await request.then(verifyDirectCertificate);
|
||||
|
@ -204,8 +204,8 @@ add_task(async function test_register_direct_proceed_anon() {
|
|||
await promiseNotification("webauthn-prompt-register-direct");
|
||||
|
||||
// Check "anonymize anyway" and proceed.
|
||||
PopupNotifications.panel.firstChild.checkbox.checked = true;
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
PopupNotifications.panel.firstElementChild.checkbox.checked = true;
|
||||
PopupNotifications.panel.firstElementChild.button.click();
|
||||
|
||||
// Ensure we got "none" attestation.
|
||||
await request.then(verifyAnonymizedCertificate);
|
||||
|
|
|
@ -531,13 +531,13 @@ typedef int64_t (*Prototype_General7)(int64_t arg0, int64_t arg1, int64_t arg2,
|
|||
int64_t arg4, int64_t arg5, int64_t arg6);
|
||||
typedef int64_t (*Prototype_General8)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3,
|
||||
int64_t arg4, int64_t arg5, int64_t arg6, int64_t arg7);
|
||||
typedef int64_t (*Prototype_GeneralGeneralGeneralInt64)(int64_t arg0, int32_t arg1, int32_t arg2,
|
||||
typedef int64_t (*Prototype_GeneralGeneralGeneralInt64)(int64_t arg0, int64_t arg1, int64_t arg2,
|
||||
int64_t arg3);
|
||||
typedef int64_t (*Prototype_GeneralGeneralInt64Int64)(int64_t arg0, int32_t arg1, int64_t arg2,
|
||||
typedef int64_t (*Prototype_GeneralGeneralInt64Int64)(int64_t arg0, int64_t arg1, int64_t arg2,
|
||||
int64_t arg3);
|
||||
|
||||
typedef int64_t (*Prototype_Int_Double)(double arg0);
|
||||
typedef int64_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1);
|
||||
typedef int64_t (*Prototype_Int_IntDouble)(int64_t arg0, double arg1);
|
||||
typedef int64_t (*Prototype_Int_DoubleIntInt)(double arg0, uint64_t arg1, uint64_t arg2);
|
||||
typedef int64_t (*Prototype_Int_IntDoubleIntInt)(uint64_t arg0, double arg1,
|
||||
uint64_t arg2, uint64_t arg3);
|
||||
|
@ -547,7 +547,7 @@ typedef float (*Prototype_Float32_Float32Float32)(float arg0, float arg1);
|
|||
|
||||
typedef double (*Prototype_Double_None)();
|
||||
typedef double (*Prototype_Double_Double)(double arg0);
|
||||
typedef double (*Prototype_Double_Int)(int32_t arg0);
|
||||
typedef double (*Prototype_Double_Int)(int64_t arg0);
|
||||
typedef double (*Prototype_Double_DoubleInt)(double arg0, int64_t arg1);
|
||||
typedef double (*Prototype_Double_IntDouble)(int64_t arg0, double arg1);
|
||||
typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1);
|
||||
|
|
|
@ -4526,6 +4526,12 @@ ScrollFrameHelper::FireScrollPortEvent()
|
|||
nsSize scrollportSize = mScrollPort.Size();
|
||||
nsSize childSize = GetScrolledRect().Size();
|
||||
|
||||
// TODO(emilio): why do we need the whole WillPaintObserver infrastructure and
|
||||
// can't use AddScriptRunner & co? I guess it made sense when we used
|
||||
// WillPaintObserver for scroll events too, or when this used to flush.
|
||||
//
|
||||
// Should we remove this?
|
||||
|
||||
bool newVerticalOverflow = childSize.height > scrollportSize.height;
|
||||
bool vertChanged = mVerticalOverflow != newVerticalOverflow;
|
||||
|
||||
|
@ -5087,10 +5093,6 @@ ScrollFrameHelper::PostScrollEvent()
|
|||
NS_IMETHODIMP
|
||||
ScrollFrameHelper::AsyncScrollPortEvent::Run()
|
||||
{
|
||||
if (mHelper) {
|
||||
mHelper->mOuter->PresContext()->Document()->
|
||||
FlushPendingNotifications(FlushType::InterruptibleLayout);
|
||||
}
|
||||
return mHelper ? mHelper->FireScrollPortEvent() : NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.mozilla.gecko.GeckoActivityMonitor;
|
|||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.mozglue.SafeIntent;
|
||||
import org.mozilla.gecko.updater.UpdateServiceHelper;
|
||||
import org.mozilla.gecko.util.BitmapUtils;
|
||||
import org.mozilla.gecko.util.BundleEventListener;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
|
@ -38,6 +39,7 @@ import java.io.File;
|
|||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -118,7 +120,10 @@ public final class NotificationHelper implements BundleEventListener {
|
|||
|
||||
// Holds the mapping between the Channel enum used by the rest of our codebase and the
|
||||
// channel ID used for communication with the system NotificationManager.
|
||||
private final Map<Channel, String> mDefinedNotificationChannels = new HashMap<Channel, String>(7) {{
|
||||
// How to determine the initialCapacity: Count all channels (including the Updater, which is
|
||||
// only added further down in initNotificationChannels), multiply by 4/3 for a maximum load
|
||||
// factor of 75 % and round up to the next multiple of two.
|
||||
private final Map<Channel, String> mDefinedNotificationChannels = new HashMap<Channel, String>(16) {{
|
||||
final String DEFAULT_CHANNEL_TAG = "default2-notification-channel";
|
||||
put(Channel.DEFAULT, DEFAULT_CHANNEL_TAG);
|
||||
|
||||
|
@ -136,20 +141,15 @@ public final class NotificationHelper implements BundleEventListener {
|
|||
put(Channel.LP_DEFAULT, LP_DEFAULT_CHANNEL_TAG);
|
||||
}
|
||||
|
||||
if (AppConstants.MOZ_UPDATER) {
|
||||
final String UPDATER_CHANNEL_TAG = "updater-notification-channel";
|
||||
put(Channel.UPDATER, UPDATER_CHANNEL_TAG);
|
||||
}
|
||||
|
||||
final String SYNCED_TABS_CHANNEL_TAG = "synced-tabs-notification-channel";
|
||||
put(Channel.SYNCED_TABS, SYNCED_TABS_CHANNEL_TAG);
|
||||
}};
|
||||
|
||||
// These are channels we no longer require and want to retire from Android's settings UI.
|
||||
private final List<String> mDeprecatedNotificationChannels = Arrays.asList(
|
||||
private final List<String> mDeprecatedNotificationChannels = new ArrayList<>(Arrays.asList(
|
||||
"default-notification-channel",
|
||||
null
|
||||
);
|
||||
));
|
||||
|
||||
// Holds a list of notifications that should be cleared if the Fennec Activity is shut down.
|
||||
// Will not include ongoing or persistent notifications that are tied to Gecko's lifecycle.
|
||||
|
@ -192,6 +192,13 @@ public final class NotificationHelper implements BundleEventListener {
|
|||
}
|
||||
|
||||
private void initNotificationChannels() {
|
||||
final String UPDATER_CHANNEL_TAG = "updater-notification-channel";
|
||||
if (UpdateServiceHelper.isUpdaterEnabled(mContext)) {
|
||||
mDefinedNotificationChannels.put(Channel.UPDATER, UPDATER_CHANNEL_TAG);
|
||||
} else {
|
||||
mDeprecatedNotificationChannels.add(UPDATER_CHANNEL_TAG);
|
||||
}
|
||||
|
||||
final NotificationManager notificationManager =
|
||||
(NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
|
|
|
@ -2797,7 +2797,12 @@ ServerHandler.prototype =
|
|||
// getting the line number where we evaluate the SJS file. Don't
|
||||
// separate these two lines!
|
||||
var line = new Error().lineNumber;
|
||||
Cu.evalInSandbox(sis.read(file.fileSize), s, "latest");
|
||||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newFileURI(file);
|
||||
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader);
|
||||
scriptLoader.loadSubScript(uri.spec, s);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
|
|
|
@ -25,6 +25,7 @@ job-defaults:
|
|||
default: scriptworker-prov-v1/treescript-dev
|
||||
worker:
|
||||
implementation: treescript
|
||||
dontbuild: true
|
||||
tags: ['release']
|
||||
bump: true
|
||||
bump-files:
|
||||
|
|
|
@ -17,35 +17,32 @@ from subprocess import CalledProcessError
|
|||
from mozbuild.util import memoize
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
_cache = {}
|
||||
|
||||
|
||||
@memoize
|
||||
def get_changed_files(repository, revision):
|
||||
"""
|
||||
Get the set of files changed in the push headed by the given revision.
|
||||
Responses are cached, so multiple calls with the same arguments are OK.
|
||||
"""
|
||||
key = repository, revision
|
||||
if key not in _cache:
|
||||
url = '%s/json-automationrelevance/%s' % (repository.rstrip('/'), revision)
|
||||
logger.debug("Querying version control for metadata: %s", url)
|
||||
url = '%s/json-automationrelevance/%s' % (repository.rstrip('/'), revision)
|
||||
logger.debug("Querying version control for metadata: %s", url)
|
||||
|
||||
def get_automationrelevance():
|
||||
response = requests.get(url, timeout=5)
|
||||
return response.json()
|
||||
contents = retry(get_automationrelevance, attempts=10, sleeptime=10)
|
||||
def get_automationrelevance():
|
||||
response = requests.get(url, timeout=5)
|
||||
return response.json()
|
||||
contents = retry(get_automationrelevance, attempts=10, sleeptime=10)
|
||||
|
||||
logger.debug('{} commits influencing task scheduling:'
|
||||
.format(len(contents['changesets'])))
|
||||
changed_files = set()
|
||||
for c in contents['changesets']:
|
||||
logger.debug(" {cset} {desc}".format(
|
||||
cset=c['node'][0:12],
|
||||
desc=c['desc'].splitlines()[0].encode('ascii', 'ignore')))
|
||||
changed_files |= set(c['files'])
|
||||
logger.debug('{} commits influencing task scheduling:'
|
||||
.format(len(contents['changesets'])))
|
||||
changed_files = set()
|
||||
for c in contents['changesets']:
|
||||
logger.debug(" {cset} {desc}".format(
|
||||
cset=c['node'][0:12],
|
||||
desc=c['desc'].splitlines()[0].encode('ascii', 'ignore')))
|
||||
changed_files |= set(c['files'])
|
||||
|
||||
_cache[key] = changed_files
|
||||
return _cache[key]
|
||||
return changed_files
|
||||
|
||||
|
||||
def check(params, file_patterns):
|
||||
|
|
|
@ -558,7 +558,7 @@ Tester.prototype = {
|
|||
// Remove stale tabs
|
||||
if (this.currentTest && window.gBrowser && gBrowser.tabs.length > 1) {
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
let lastTab = gBrowser.tabContainer.lastChild;
|
||||
let lastTab = gBrowser.tabContainer.lastElementChild;
|
||||
if (!lastTab.closing) {
|
||||
// Report the stale tab as an error only when they're not closing.
|
||||
// Tests can finish without waiting for the closing tabs.
|
||||
|
|
|
@ -35,6 +35,9 @@ from mozharness.base.python import Python3Virtualenv
|
|||
from mozharness.mozilla.testing.testbase import TestingMixin
|
||||
from mozharness.base.vcs.vcsbase import MercurialScript
|
||||
|
||||
# import mozharness write_autoconfig_files
|
||||
from mozharness.mozilla.firefox.autoconfig import write_autoconfig_files
|
||||
|
||||
raptor_dir = os.path.join(here, '..')
|
||||
sys.path.insert(0, raptor_dir)
|
||||
|
||||
|
@ -58,7 +61,7 @@ except Exception:
|
|||
DEFAULT_CERT_PATH = os.path.join(os.getenv('HOMEDRIVE'), os.getenv('HOMEPATH'),
|
||||
'.mitmproxy', 'mitmproxy-ca-cert.cer')
|
||||
|
||||
MITMPROXY_SETTINGS = '''// Start with a comment
|
||||
MITMPROXY_ON_SETTINGS = '''// Start with a comment
|
||||
// Load up mitmproxy cert
|
||||
var certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(Ci.nsIX509CertDB);
|
||||
var certdb2 = certdb;
|
||||
|
@ -79,6 +82,11 @@ pref("network.proxy.ssl", "127.0.0.1");
|
|||
pref("network.proxy.ssl_port", 8080);
|
||||
'''
|
||||
|
||||
MITMPROXY_OFF_SETTINGS = '''// Start with a comment
|
||||
// Turn off proxy
|
||||
pref("network.proxy.type", 0);
|
||||
'''
|
||||
|
||||
|
||||
class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript):
|
||||
|
||||
|
@ -191,13 +199,8 @@ class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript):
|
|||
|
||||
def setup(self):
|
||||
# install the generated CA certificate into Firefox
|
||||
# mitmproxy cert setup needs path to mozharness install; mozharness has set this
|
||||
# value in the SCRIPTSPATH env var for us in mozharness/mozilla/testing/talos.py
|
||||
scripts_path = os.environ.get('SCRIPTSPATH')
|
||||
LOG.info('scripts_path: %s' % str(scripts_path))
|
||||
self.install_mitmproxy_cert(self.mitmproxy_proc,
|
||||
self.browser_path,
|
||||
str(scripts_path))
|
||||
self.browser_path)
|
||||
return
|
||||
|
||||
def start(self):
|
||||
|
@ -214,20 +217,15 @@ class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript):
|
|||
|
||||
def stop(self):
|
||||
self.stop_mitmproxy_playback()
|
||||
self.turn_off_browser_proxy()
|
||||
return
|
||||
|
||||
def configure_mitmproxy(self,
|
||||
fx_install_dir,
|
||||
scripts_path,
|
||||
certificate_path=DEFAULT_CERT_PATH):
|
||||
# scripts_path is path to mozharness on test machine; needed so can import
|
||||
if scripts_path is not False:
|
||||
sys.path.insert(1, scripts_path)
|
||||
sys.path.insert(1, os.path.join(scripts_path, 'mozharness'))
|
||||
from mozharness.mozilla.firefox.autoconfig import write_autoconfig_files
|
||||
certificate = self._read_certificate(certificate_path)
|
||||
write_autoconfig_files(fx_install_dir=fx_install_dir,
|
||||
cfg_contents=MITMPROXY_SETTINGS % {
|
||||
cfg_contents=MITMPROXY_ON_SETTINGS % {
|
||||
'cert': certificate})
|
||||
|
||||
def _read_certificate(self, certificate_path):
|
||||
|
@ -245,7 +243,7 @@ class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript):
|
|||
# read autoconfig file, confirm mitmproxy cert is in there
|
||||
certificate = self._read_certificate(DEFAULT_CERT_PATH)
|
||||
contents = read_autoconfig_file(browser_install)
|
||||
if (MITMPROXY_SETTINGS % {'cert': certificate}) in contents:
|
||||
if (MITMPROXY_ON_SETTINGS % {'cert': certificate}) in contents:
|
||||
LOG.info("Verified mitmproxy CA certificate is installed in Firefox")
|
||||
else:
|
||||
LOG.info("Firefox autoconfig file contents:")
|
||||
|
@ -256,19 +254,19 @@ class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript):
|
|||
return False
|
||||
return True
|
||||
|
||||
def install_mitmproxy_cert(self, mitmproxy_proc, browser_path, scripts_path):
|
||||
def install_mitmproxy_cert(self, mitmproxy_proc, browser_path):
|
||||
"""Install the CA certificate generated by mitmproxy, into Firefox"""
|
||||
LOG.info("Installing mitmxproxy CA certficate into Firefox")
|
||||
# browser_path is exe, we want install dir
|
||||
browser_install = os.path.dirname(browser_path)
|
||||
self.browser_install = os.path.dirname(browser_path)
|
||||
# on macosx we need to remove the last folders 'Content/MacOS'
|
||||
if mozinfo.os == 'mac':
|
||||
browser_install = browser_install[:-14]
|
||||
self.browser_install = self.browser_install[:-14]
|
||||
|
||||
LOG.info('Calling configure_mitmproxy with browser folder: %s' % browser_install)
|
||||
self.configure_mitmproxy(browser_install, scripts_path)
|
||||
LOG.info('Calling configure_mitmproxy with browser folder: %s' % self.browser_install)
|
||||
self.configure_mitmproxy(self.browser_install)
|
||||
# cannot continue if failed to add CA cert to Firefox, need to check
|
||||
if not self.is_mitmproxy_cert_installed(browser_install):
|
||||
if not self.is_mitmproxy_cert_installed(self.browser_install):
|
||||
LOG.error('Aborting: failed to install mitmproxy CA cert into Firefox')
|
||||
self.stop_mitmproxy_playback(mitmproxy_proc)
|
||||
sys.exit()
|
||||
|
@ -342,3 +340,10 @@ class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript):
|
|||
LOG.info(str(status))
|
||||
else:
|
||||
LOG.info("Successfully killed the mitmproxy playback process")
|
||||
|
||||
def turn_off_browser_proxy(self):
|
||||
"""Turn off the browser proxy that was used for mitmproxy playback"""
|
||||
LOG.info("Turning off the browser proxy")
|
||||
|
||||
write_autoconfig_files(fx_install_dir=self.browser_install,
|
||||
cfg_contents=MITMPROXY_OFF_SETTINGS)
|
||||
|
|
|
@ -45,6 +45,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
ExtensionPermissions: "resource://gre/modules/ExtensionPermissions.jsm",
|
||||
ExtensionStorage: "resource://gre/modules/ExtensionStorage.jsm",
|
||||
ExtensionStorageIDB: "resource://gre/modules/ExtensionStorageIDB.jsm",
|
||||
ExtensionTelemetry: "resource://gre/modules/ExtensionTelemetry.jsm",
|
||||
ExtensionTestCommon: "resource://testing-common/ExtensionTestCommon.jsm",
|
||||
FileSource: "resource://gre/modules/L10nRegistry.jsm",
|
||||
L10nRegistry: "resource://gre/modules/L10nRegistry.jsm",
|
||||
|
@ -98,7 +99,6 @@ var {
|
|||
const {
|
||||
getUniqueId,
|
||||
promiseTimeout,
|
||||
ExtensionTelemetry,
|
||||
} = ExtensionUtils;
|
||||
|
||||
const {
|
||||
|
|
|
@ -13,6 +13,7 @@ ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
ExtensionTelemetry: "resource://gre/modules/ExtensionTelemetry.jsm",
|
||||
LanguageDetector: "resource:///modules/translation/LanguageDetector.jsm",
|
||||
MessageChannel: "resource://gre/modules/MessageChannel.jsm",
|
||||
Schemas: "resource://gre/modules/Schemas.jsm",
|
||||
|
@ -41,7 +42,6 @@ XPCOMUtils.defineLazyGlobalGetters(this, ["crypto", "TextEncoder"]);
|
|||
const {
|
||||
DefaultMap,
|
||||
DefaultWeakMap,
|
||||
ExtensionTelemetry,
|
||||
getInnerWindowID,
|
||||
getWinUtils,
|
||||
promiseDocumentIdle,
|
||||
|
|
|
@ -481,6 +481,15 @@ ProxyMessenger = {
|
|||
return {messageManager: browser.messageManager, xulBrowser: browser};
|
||||
}
|
||||
|
||||
// port.postMessage / port.disconnect to non-tab contexts.
|
||||
if (recipient.envType === "content_child") {
|
||||
let childId = `${recipient.extensionId}.${recipient.contextId}`;
|
||||
let context = ParentAPIManager.proxyContexts.get(childId);
|
||||
if (context) {
|
||||
return {messageManager: context.parentMessageManager, xulBrowser: context.xulBrowser};
|
||||
}
|
||||
}
|
||||
|
||||
// runtime.sendMessage / runtime.connect
|
||||
let extension = GlobalManager.extensionMap.get(recipient.extensionId);
|
||||
if (extension) {
|
||||
|
|
|
@ -8,11 +8,11 @@ this.EXPORTED_SYMBOLS = ["ExtensionStorageIDB"];
|
|||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/IndexedDB.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
|
||||
ExtensionStorage: "resource://gre/modules/ExtensionStorage.jsm",
|
||||
getTrimmedString: "resource://gre/modules/ExtensionTelemetry.jsm",
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
});
|
||||
|
@ -24,10 +24,6 @@ XPCOMUtils.defineLazyGetter(this, "WEBEXT_STORAGE_USER_CONTEXT_ID", () => {
|
|||
"userContextIdInternal.webextStorageLocal").userContextId;
|
||||
});
|
||||
|
||||
var {
|
||||
getTrimmedString,
|
||||
} = ExtensionUtils;
|
||||
|
||||
const IDB_NAME = "webExtensions-storage-local";
|
||||
const IDB_DATA_STORENAME = "storage-local-data";
|
||||
const IDB_VERSION = 1;
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
/* 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";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["ExtensionTelemetry", "getTrimmedString"];
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
|
||||
"resource://gre/modules/TelemetryStopwatch.jsm");
|
||||
|
||||
// Map of the base histogram ids for the metrics recorded for the extensions.
|
||||
const histograms = {
|
||||
"extensionStartup": "WEBEXT_EXTENSION_STARTUP_MS",
|
||||
"backgroundPageLoad": "WEBEXT_BACKGROUND_PAGE_LOAD_MS",
|
||||
"browserActionPopupOpen": "WEBEXT_BROWSERACTION_POPUP_OPEN_MS",
|
||||
"browserActionPreloadResult": "WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT",
|
||||
"contentScriptInjection": "WEBEXT_CONTENT_SCRIPT_INJECTION_MS",
|
||||
"pageActionPopupOpen": "WEBEXT_PAGEACTION_POPUP_OPEN_MS",
|
||||
"storageLocalGetJSON": "WEBEXT_STORAGE_LOCAL_GET_MS",
|
||||
"storageLocalSetJSON": "WEBEXT_STORAGE_LOCAL_SET_MS",
|
||||
"storageLocalGetIDB": "WEBEXT_STORAGE_LOCAL_IDB_GET_MS",
|
||||
"storageLocalSetIDB": "WEBEXT_STORAGE_LOCAL_IDB_SET_MS",
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a trimmed version of the given string if it is longer than 80 chars (used in telemetry
|
||||
* when a string may be longer than allowed).
|
||||
*
|
||||
* @param {string} str
|
||||
* The original string content.
|
||||
*
|
||||
* @returns {string}
|
||||
* The trimmed version of the string when longer than 80 chars, or the given string
|
||||
* unmodified otherwise.
|
||||
*/
|
||||
function getTrimmedString(str) {
|
||||
if (str.length <= 80) {
|
||||
return str;
|
||||
}
|
||||
|
||||
const length = str.length;
|
||||
|
||||
// Trim the string to prevent a flood of warnings messages logged internally by recordEvent,
|
||||
// the trimmed version is going to be composed by the first 40 chars and the last 37 and 3 dots
|
||||
// that joins the two parts, to visually indicate that the string has been trimmed.
|
||||
return `${str.slice(0, 40)}...${str.slice(length - 37, length)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a internal helper object which contains a collection of helpers used to make it easier
|
||||
* to collect extension telemetry (in both the general histogram and in the one keyed by addon id).
|
||||
*
|
||||
* This helper object is not exported from ExtensionUtils, it is used by the ExtensionTelemetry
|
||||
* Proxy which is exported and used by the callers to record telemetry data for one of the
|
||||
* supported metrics.
|
||||
*/
|
||||
class ExtensionTelemetryMetric {
|
||||
constructor(metric) {
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
// Stopwatch methods.
|
||||
stopwatchStart(extension, obj = extension) {
|
||||
this._wrappedStopwatchMethod("start", this.metric, extension, obj);
|
||||
}
|
||||
|
||||
stopwatchFinish(extension, obj = extension) {
|
||||
this._wrappedStopwatchMethod("finish", this.metric, extension, obj);
|
||||
}
|
||||
|
||||
stopwatchCancel(extension, obj = extension) {
|
||||
this._wrappedStopwatchMethod("cancel", this.metric, extension, obj);
|
||||
}
|
||||
|
||||
// Histogram counters methods.
|
||||
histogramAdd(opts) {
|
||||
this._histogramAdd(this.metric, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a call to a TelemetryStopwatch method for a given metric and extension.
|
||||
*
|
||||
* @param {string} method
|
||||
* The stopwatch method to call ("start", "finish" or "cancel").
|
||||
* @param {string} metric
|
||||
* The stopwatch metric to record (used to retrieve the base histogram id from the _histogram object).
|
||||
* @param {Extension | BrowserExtensionContent} extension
|
||||
* The extension to record the telemetry for.
|
||||
* @param {any | undefined} [obj = extension]
|
||||
* An optional telemetry stopwatch object (which defaults to the extension parameter when missing).
|
||||
*/
|
||||
_wrappedStopwatchMethod(method, metric, extension, obj = extension) {
|
||||
if (!extension) {
|
||||
throw new Error(`Mandatory extension parameter is undefined`);
|
||||
}
|
||||
|
||||
const baseId = histograms[metric];
|
||||
if (!baseId) {
|
||||
throw new Error(`Unknown metric ${metric}`);
|
||||
}
|
||||
|
||||
// Record metric in the general histogram.
|
||||
TelemetryStopwatch[method](baseId, obj);
|
||||
|
||||
// Record metric in the histogram keyed by addon id.
|
||||
let extensionId = getTrimmedString(extension.id);
|
||||
TelemetryStopwatch[`${method}Keyed`](`${baseId}_BY_ADDONID`, extensionId, obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a telemetry category and/or value for a given metric.
|
||||
*
|
||||
* @param {string} metric
|
||||
* The metric to record (used to retrieve the base histogram id from the _histogram object).
|
||||
* @param {Object} options
|
||||
* @param {Extension | BrowserExtensionContent} options.extension
|
||||
* The extension to record the telemetry for.
|
||||
* @param {string | undefined} [options.category]
|
||||
* An optional histogram category.
|
||||
* @param {number | undefined} [options.value]
|
||||
* An optional value to record.
|
||||
*/
|
||||
_histogramAdd(metric, {category, extension, value}) {
|
||||
if (!extension) {
|
||||
throw new Error(`Mandatory extension parameter is undefined`);
|
||||
}
|
||||
|
||||
const baseId = histograms[metric];
|
||||
if (!baseId) {
|
||||
throw new Error(`Unknown metric ${metric}`);
|
||||
}
|
||||
|
||||
const histogram = Services.telemetry.getHistogramById(baseId);
|
||||
if (typeof category === "string") {
|
||||
histogram.add(category, value);
|
||||
} else {
|
||||
histogram.add(value);
|
||||
}
|
||||
|
||||
const keyedHistogram = Services.telemetry.getKeyedHistogramById(`${baseId}_BY_ADDONID`);
|
||||
const extensionId = getTrimmedString(extension.id);
|
||||
|
||||
if (typeof category === "string") {
|
||||
keyedHistogram.add(extensionId, category, value);
|
||||
} else {
|
||||
keyedHistogram.add(extensionId, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cache of the ExtensionTelemetryMetric instances that has been lazily created by the
|
||||
// Extension Telemetry Proxy.
|
||||
const metricsCache = new Map();
|
||||
|
||||
/**
|
||||
* This proxy object provides the telemetry helpers for the currently supported metrics (the ones listed in
|
||||
* ExtensionTelemetryHelpers._histograms), the telemetry helpers for a particular metric are lazily created
|
||||
* when the related property is being accessed on this object for the first time, e.g.:
|
||||
*
|
||||
* ExtensionTelemetry.extensionStartup.stopwatchStart(extension);
|
||||
* ExtensionTelemetry.browserActionPreloadResult.histogramAdd({category: "Shown", extension});
|
||||
*/
|
||||
var ExtensionTelemetry = new Proxy(metricsCache, {
|
||||
get(target, prop, receiver) {
|
||||
if (!(prop in histograms)) {
|
||||
throw new Error(`Unknown metric ${prop}`);
|
||||
}
|
||||
|
||||
// Lazily create and cache the metric result object.
|
||||
if (!target.has(prop)) {
|
||||
target.set(prop, new ExtensionTelemetryMetric(prop));
|
||||
}
|
||||
|
||||
return target.get(prop);
|
||||
},
|
||||
});
|
|
@ -12,8 +12,6 @@ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
|
||||
ChromeUtils.defineModuleGetter(this, "setTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
|
||||
"resource://gre/modules/TelemetryStopwatch.jsm");
|
||||
|
||||
// xpcshell doesn't handle idle callbacks well.
|
||||
XPCOMUtils.defineLazyGetter(this, "idleTimeout",
|
||||
|
@ -269,165 +267,12 @@ const chromeModifierKeyMap = {
|
|||
"Shift": "shift",
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a trimmed version of the given string if it is longer than 80 chars (used in telemetry
|
||||
* when a string may be longer than allowed).
|
||||
*
|
||||
* @param {string} str
|
||||
* The original string content.
|
||||
*
|
||||
* @returns {string}
|
||||
* The trimmed version of the string when longer than 80 chars, or the given string
|
||||
* unmodified otherwise.
|
||||
*/
|
||||
function getTrimmedString(str) {
|
||||
if (str.length <= 80) {
|
||||
return str;
|
||||
}
|
||||
|
||||
const length = str.length;
|
||||
|
||||
// Trim the string to prevent a flood of warnings messages logged internally by recordEvent,
|
||||
// the trimmed version is going to be composed by the first 40 chars and the last 37 and 3 dots
|
||||
// that joins the two parts, to visually indicate that the string has been trimmed.
|
||||
return `${str.slice(0, 40)}...${str.slice(length - 37, length)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a internal helper object which contains a collection of helpers used to make it easier
|
||||
* to collect extension telemetry (in both the general histogram and in the one keyed by addon id).
|
||||
*
|
||||
* This helper object is not exported from ExtensionUtils, it is used by the ExtensionTelemetry
|
||||
* Proxy which is exported and used by the callers to record telemetry data for one of the
|
||||
* supported metrics.
|
||||
*/
|
||||
const ExtensionTelemetryHelpers = {
|
||||
// Allow callers to refer to the existing metrics by accessing it as properties of the
|
||||
// ExtensionTelemetry.metrics (e.g. ExtensionTelemetry.metrics.extensionStartup).
|
||||
|
||||
// Cache of the metrics helper lazily created by the ExtensionTelemetry Proxy.
|
||||
_metricsMap: new Map(),
|
||||
|
||||
// Map of the base histogram ids for the metrics recorded for the extensions.
|
||||
_histograms: {
|
||||
"extensionStartup": "WEBEXT_EXTENSION_STARTUP_MS",
|
||||
"backgroundPageLoad": "WEBEXT_BACKGROUND_PAGE_LOAD_MS",
|
||||
"browserActionPopupOpen": "WEBEXT_BROWSERACTION_POPUP_OPEN_MS",
|
||||
"browserActionPreloadResult": "WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT",
|
||||
"contentScriptInjection": "WEBEXT_CONTENT_SCRIPT_INJECTION_MS",
|
||||
"pageActionPopupOpen": "WEBEXT_PAGEACTION_POPUP_OPEN_MS",
|
||||
"storageLocalGetJSON": "WEBEXT_STORAGE_LOCAL_GET_MS",
|
||||
"storageLocalSetJSON": "WEBEXT_STORAGE_LOCAL_SET_MS",
|
||||
"storageLocalGetIDB": "WEBEXT_STORAGE_LOCAL_IDB_GET_MS",
|
||||
"storageLocalSetIDB": "WEBEXT_STORAGE_LOCAL_IDB_SET_MS",
|
||||
},
|
||||
// Wraps a call to a TelemetryStopwatch method.
|
||||
/**
|
||||
* Wraps a call to a TelemetryStopwatch method for a given metric and extension.
|
||||
*
|
||||
* @param {string} method
|
||||
* The stopwatch method to call ("start", "finish" or "cancel").
|
||||
* @param {string} metric
|
||||
* The stopwatch metric to record (used to retrieve the base histogram id from the _histogram object).
|
||||
* @param {Extension | BrowserExtensionContent} extension
|
||||
* The extension to record the telemetry for.
|
||||
* @param {any | undefined} [obj = extension]
|
||||
* An optional telemetry stopwatch object (which defaults to the extension parameter when missing).
|
||||
*/
|
||||
_wrappedStopwatchMethod(method, metric, extension, obj = extension) {
|
||||
if (!extension) {
|
||||
throw new Error(`Mandatory extension parameter is undefined`);
|
||||
}
|
||||
|
||||
const baseId = this._histograms[metric];
|
||||
if (!baseId) {
|
||||
throw new Error(`Unknown metric ${metric}`);
|
||||
}
|
||||
|
||||
// Record metric in the general histogram.
|
||||
TelemetryStopwatch[method](baseId, obj);
|
||||
|
||||
// Record metric in the histogram keyed by addon id.
|
||||
let extensionId = getTrimmedString(extension.id);
|
||||
TelemetryStopwatch[`${method}Keyed`](`${baseId}_BY_ADDONID`, extensionId, obj);
|
||||
},
|
||||
/**
|
||||
* Record a telemetry category and/or value for a given metric.
|
||||
*
|
||||
* @param {string} metric
|
||||
* The metric to record (used to retrieve the base histogram id from the _histogram object).
|
||||
* @param {Object} options
|
||||
* @param {Extension | BrowserExtensionContent} options.extension
|
||||
* The extension to record the telemetry for.
|
||||
* @param {string | undefined} [options.category]
|
||||
* An optional histogram category.
|
||||
* @param {number | undefined} [options.value]
|
||||
* An optional value to record.
|
||||
*/
|
||||
_histogramAdd(metric, {category, extension, value}) {
|
||||
if (!extension) {
|
||||
throw new Error(`Mandatory extension parameter is undefined`);
|
||||
}
|
||||
|
||||
const baseId = this._histograms[metric];
|
||||
if (!baseId) {
|
||||
throw new Error(`Unknown metric ${metric}`);
|
||||
}
|
||||
|
||||
const histogram = Services.telemetry.getHistogramById(baseId);
|
||||
if (typeof category === "string") {
|
||||
histogram.add(category, value);
|
||||
} else {
|
||||
histogram.add(value);
|
||||
}
|
||||
|
||||
const keyedHistogram = Services.telemetry.getKeyedHistogramById(`${baseId}_BY_ADDONID`);
|
||||
const extensionId = getTrimmedString(extension.id);
|
||||
|
||||
if (typeof category === "string") {
|
||||
keyedHistogram.add(extensionId, category, value);
|
||||
} else {
|
||||
keyedHistogram.add(extensionId, value);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* This proxy object provides the telemetry helpers for the currently supported metrics (the ones listed in
|
||||
* ExtensionTelemetryHelpers._histograms), the telemetry helpers for a particular metric are lazily created
|
||||
* when the related property is being accessed on this object for the first time, e.g.:
|
||||
*
|
||||
* ExtensionTelemetry.extensionStartup.stopwatchStart(extension);
|
||||
* ExtensionTelemetry.browserActionPreloadResult.histogramAdd({category: "Shown", extension});
|
||||
*/
|
||||
const ExtensionTelemetry = new Proxy(ExtensionTelemetryHelpers, {
|
||||
get(target, prop, receiver) {
|
||||
if (!(prop in target._histograms)) {
|
||||
throw new Error(`Unknown metric ${prop}`);
|
||||
}
|
||||
|
||||
// Lazily create and cache the metric result object.
|
||||
if (!target._metricsMap.has(prop)) {
|
||||
target._metricsMap.set(prop, {
|
||||
// Stopwatch histogram helpers.
|
||||
stopwatchStart: target._wrappedStopwatchMethod.bind(target, "start", prop),
|
||||
stopwatchFinish: target._wrappedStopwatchMethod.bind(target, "finish", prop),
|
||||
stopwatchCancel: target._wrappedStopwatchMethod.bind(target, "cancel", prop),
|
||||
// Result histogram helpers.
|
||||
histogramAdd: target._histogramAdd.bind(target, prop),
|
||||
});
|
||||
}
|
||||
|
||||
return target._metricsMap.get(prop);
|
||||
},
|
||||
});
|
||||
|
||||
var ExtensionUtils = {
|
||||
chromeModifierKeyMap,
|
||||
flushJarCache,
|
||||
getInnerWindowID,
|
||||
getMessageManager,
|
||||
getTrimmedString,
|
||||
getUniqueId,
|
||||
filterStack,
|
||||
getWinUtils,
|
||||
|
@ -440,6 +285,5 @@ var ExtensionUtils = {
|
|||
DefaultMap,
|
||||
DefaultWeakMap,
|
||||
ExtensionError,
|
||||
ExtensionTelemetry,
|
||||
LimitedSet,
|
||||
};
|
||||
|
|
|
@ -4,13 +4,11 @@ ChromeUtils.defineModuleGetter(this, "ExtensionStorage",
|
|||
"resource://gre/modules/ExtensionStorage.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "ExtensionStorageIDB",
|
||||
"resource://gre/modules/ExtensionStorageIDB.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "ExtensionTelemetry",
|
||||
"resource://gre/modules/ExtensionTelemetry.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
var {
|
||||
ExtensionTelemetry,
|
||||
} = ExtensionUtils;
|
||||
|
||||
// Wrap a storage operation in a TelemetryStopWatch.
|
||||
async function measureOp(telemetryMetric, extension, fn) {
|
||||
const stopwatchKey = {};
|
||||
|
|
|
@ -22,6 +22,7 @@ EXTRA_JS_MODULES += [
|
|||
'ExtensionStorage.jsm',
|
||||
'ExtensionStorageIDB.jsm',
|
||||
'ExtensionStorageSync.jsm',
|
||||
'ExtensionTelemetry.jsm',
|
||||
'ExtensionUtils.jsm',
|
||||
'FindContent.jsm',
|
||||
'LegacyExtensionsUtils.jsm',
|
||||
|
|
|
@ -6,10 +6,9 @@ var {
|
|||
promiseExtensionViewLoaded,
|
||||
} = ExtensionParent;
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
var {
|
||||
ExtensionTelemetry,
|
||||
} = ExtensionUtils;
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "ExtensionTelemetry",
|
||||
"resource://gre/modules/ExtensionTelemetry.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "DELAYED_STARTUP",
|
||||
"extensions.webextensions.background-delayed-startup");
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
const server = createHttpServer({hosts: ["example.com"]});
|
||||
server.registerPathHandler("/dummyFrame", (request, response) => {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "text/html; charset=utf-8", false);
|
||||
response.write("");
|
||||
});
|
||||
|
||||
add_task(async function connect_from_background_frame() {
|
||||
async function background() {
|
||||
const FRAME_URL = "http://example.com:8888/dummyFrame";
|
||||
browser.runtime.onConnect.addListener((port) => {
|
||||
browser.test.assertEq(port.sender.tab, undefined, "Sender is not a tab");
|
||||
browser.test.assertEq(port.sender.url, FRAME_URL, "Expected sender URL");
|
||||
port.onMessage.addListener(msg => {
|
||||
browser.test.assertEq("pong", msg, "Reply from content script");
|
||||
port.disconnect();
|
||||
});
|
||||
port.postMessage("ping");
|
||||
});
|
||||
|
||||
await browser.contentScripts.register({
|
||||
matches: ["http://example.com/dummyFrame"],
|
||||
js: [{file: "contentscript.js"}],
|
||||
allFrames: true,
|
||||
});
|
||||
|
||||
let f = document.createElement("iframe");
|
||||
f.src = FRAME_URL;
|
||||
document.body.appendChild(f);
|
||||
}
|
||||
|
||||
function contentScript() {
|
||||
browser.test.log(`Running content script at ${document.URL}`);
|
||||
|
||||
let port = browser.runtime.connect();
|
||||
port.onMessage.addListener(msg => {
|
||||
browser.test.assertEq("ping", msg, "Expected message to content script");
|
||||
port.postMessage("pong");
|
||||
});
|
||||
port.onDisconnect.addListener(() => {
|
||||
browser.test.sendMessage("disconnected_in_content_script");
|
||||
});
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
permissions: ["http://example.com/*"],
|
||||
},
|
||||
files: {
|
||||
"contentscript.js": contentScript,
|
||||
},
|
||||
background,
|
||||
});
|
||||
await extension.startup();
|
||||
await extension.awaitMessage("disconnected_in_content_script");
|
||||
await extension.unload();
|
||||
});
|
|
@ -15,6 +15,7 @@ const STARTUP_MODULES = [
|
|||
// this.
|
||||
"resource://gre/modules/ExtensionPermissions.jsm",
|
||||
"resource://gre/modules/ExtensionUtils.jsm",
|
||||
"resource://gre/modules/ExtensionTelemetry.jsm",
|
||||
];
|
||||
|
||||
if (!Services.prefs.getBoolPref("extensions.webextensions.remote")) {
|
||||
|
|
|
@ -10,13 +10,16 @@ AddonTestUtils.createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42"
|
|||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionStorage.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/TelemetryController.jsm");
|
||||
|
||||
const {
|
||||
ExtensionStorageIDB,
|
||||
} = ChromeUtils.import("resource://gre/modules/ExtensionStorageIDB.jsm", {});
|
||||
|
||||
const {
|
||||
getTrimmedString,
|
||||
} = ChromeUtils.import("resource://gre/modules/ExtensionTelemetry.jsm", {});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
});
|
||||
|
@ -252,7 +255,7 @@ add_task(async function test_extensionId_trimmed_in_telemetry_event() {
|
|||
|
||||
await extension.awaitMessage("storage-local-data-migrated");
|
||||
|
||||
const expectedTrimmedExtensionId = ExtensionUtils.getTrimmedString(EXTENSION_ID);
|
||||
const expectedTrimmedExtensionId = getTrimmedString(EXTENSION_ID);
|
||||
|
||||
equal(expectedTrimmedExtensionId.length, 80, "The trimmed version of the extensionId should be 80 chars long");
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ skip-if = os == 'android' && debug # The generated script takes too long to load
|
|||
[test_ext_contentscript_create_iframe.js]
|
||||
[test_ext_contentscript_css.js]
|
||||
[test_ext_contentscript_exporthelpers.js]
|
||||
[test_ext_contentscript_in_background.js]
|
||||
[test_ext_contentscript_restrictSchemes.js]
|
||||
[test_ext_contentscript_teardown.js]
|
||||
[test_ext_contextual_identities.js]
|
||||
|
|
|
@ -6,15 +6,10 @@
|
|||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this, "CleanupManager", "resource://normandy/lib/CleanupManager.jsm",
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this, "AddonStudies", "resource://normandy/lib/AddonStudies.jsm",
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this, "RecipeRunner", "resource://normandy/lib/RecipeRunner.jsm",
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(this, "AddonStudyAction", "resource://normandy/actions/AddonStudyAction.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "CleanupManager", "resource://normandy/lib/CleanupManager.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "AddonStudies", "resource://normandy/lib/AddonStudies.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "RecipeRunner", "resource://normandy/lib/RecipeRunner.jsm");
|
||||
|
||||
var EXPORTED_SYMBOLS = ["AboutPages"];
|
||||
|
||||
|
@ -183,7 +178,8 @@ XPCOMUtils.defineLazyGetter(this.AboutPages, "aboutStudies", () => {
|
|||
* @param {String} studyName
|
||||
*/
|
||||
async removeStudy(recipeId, reason) {
|
||||
await AddonStudies.stop(recipeId, reason);
|
||||
const action = new AddonStudyAction();
|
||||
await action.unenroll(recipeId, reason);
|
||||
|
||||
// Update any open tabs with the new study list now that it has changed.
|
||||
Services.mm.broadcastAsyncMessage("Shield:ReceiveStudyList", {
|
||||
|
|
|
@ -66,7 +66,7 @@ add_task(async function test_backButton_forwardButton() {
|
|||
let forwardButton = document.getElementById("forward-button");
|
||||
|
||||
let forwardTransitionPromise;
|
||||
if (forwardButton.nextSibling == gURLBar) {
|
||||
if (forwardButton.nextElementSibling == gURLBar) {
|
||||
// We need to wait for the forward button transition to complete before we
|
||||
// can click it, so we hook up a listener to wait for it to be ready.
|
||||
forwardTransitionPromise = BrowserTestUtils.waitForEvent(forwardButton, "transitionend");
|
||||
|
|
|
@ -5766,10 +5766,10 @@
|
|||
"description": "Firefox: Time taken to kick off image compression of the canvas that will be used during swiping through history (ms)."
|
||||
},
|
||||
"FX_TAB_CLOSE_TIME_ANIM_MS": {
|
||||
"record_in_processes": ["main", "content"],
|
||||
"alert_emails": ["mconley@mozilla.com", "hkirschner@mozilla.com", "sphilp@mozilla.com"],
|
||||
"bug_numbers": [1340842],
|
||||
"expires_in_version": "65",
|
||||
"record_in_processes": ["main"],
|
||||
"alert_emails": ["mconley@mozilla.com", "dolske@mozilla.com"],
|
||||
"bug_numbers": [1340842, 1488952],
|
||||
"expires_in_version": "never",
|
||||
"releaseChannelCollection": "opt-out",
|
||||
"kind": "exponential",
|
||||
"high": 10000,
|
||||
|
@ -5777,20 +5777,20 @@
|
|||
"description": "Firefox: Time taken from the point of closing a tab (with animation), to the browser element being removed from the DOM. (ms)."
|
||||
},
|
||||
"FX_TAB_CLOSE_TIME_NO_ANIM_MS": {
|
||||
"record_in_processes": ["main", "content"],
|
||||
"record_in_processes": ["main"],
|
||||
"alert_emails": ["mconley@mozilla.com", "hkirschner@mozilla.com"],
|
||||
"bug_numbers": [1340842],
|
||||
"expires_in_version": "65",
|
||||
"expires_in_version": "68",
|
||||
"kind": "exponential",
|
||||
"high": 10000,
|
||||
"n_buckets": 50,
|
||||
"description": "Firefox: Time taken from the point of closing a tab (without animation) to the browser element being removed from the DOM. (ms)."
|
||||
},
|
||||
"FX_TAB_CLOSE_PERMIT_UNLOAD_TIME_MS": {
|
||||
"record_in_processes": ["main", "content"],
|
||||
"record_in_processes": ["main"],
|
||||
"alert_emails": ["mconley@mozilla.com", "hkirschner@mozilla.com"],
|
||||
"bug_numbers": [1340842],
|
||||
"expires_in_version": "65",
|
||||
"expires_in_version": "68",
|
||||
"kind": "exponential",
|
||||
"high": 10000,
|
||||
"n_buckets": 50,
|
||||
|
|
|
@ -159,7 +159,7 @@ async function testAutoplayUnknownPermission(args) {
|
|||
PopupNotifications.panel.firstElementChild.button.click();
|
||||
} else if (args.button == "block") {
|
||||
info("Clicking block button");
|
||||
PopupNotifications.panel.firstChild.secondaryButton.click();
|
||||
PopupNotifications.panel.firstElementChild.secondaryButton.click();
|
||||
} else {
|
||||
ok(false, "Invalid button field");
|
||||
}
|
||||
|
|
|
@ -278,7 +278,7 @@
|
|||
<body>
|
||||
<![CDATA[
|
||||
var startTab = this.selectedItem;
|
||||
var next = startTab[aDir == -1 ? "previousSibling" : "nextSibling"];
|
||||
var next = startTab[(aDir == -1 ? "previous" : "next") + "ElementSibling"];
|
||||
if (!next && aWrap) {
|
||||
next = aDir == -1 ? this.children[this.children.length - 1] :
|
||||
this.children[0];
|
||||
|
|
|
@ -33,7 +33,7 @@ function promisePopupNotificationShown(name) {
|
|||
ok(PopupNotifications.isPanelOpen, "notification panel open");
|
||||
|
||||
PopupNotifications.panel.removeEventListener("popupshown", popupshown);
|
||||
resolve(PopupNotifications.panel.firstChild);
|
||||
resolve(PopupNotifications.panel.firstElementChild);
|
||||
}
|
||||
|
||||
PopupNotifications.panel.addEventListener("popupshown", popupshown);
|
||||
|
|
|
@ -1386,7 +1386,7 @@ function promiseNotification(id = "addon-webext-permissions") {
|
|||
let notification = PopupNotifications.getNotification(id);
|
||||
if (notification) {
|
||||
PopupNotifications.panel.removeEventListener("popupshown", popupshown);
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
PopupNotifications.panel.firstElementChild.button.click();
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -478,9 +478,9 @@ async function test_allUnverified() {
|
|||
is(message, "Caution: This site would like to install an unverified add-on in " + gApp + ". Proceed at your own risk.");
|
||||
|
||||
let container = document.getElementById("addon-install-confirmation-content");
|
||||
is(container.childNodes.length, 1, "Should be one item listed");
|
||||
is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on");
|
||||
is(container.childNodes[0].childNodes.length, 1, "Shouldn't have the unverified marker");
|
||||
is(container.children.length, 1, "Should be one item listed");
|
||||
is(container.children[0].firstElementChild.getAttribute("value"), "XPI Test", "Should have the right add-on");
|
||||
is(container.children[0].children.length, 1, "Shouldn't have the unverified marker");
|
||||
|
||||
let notificationPromise = waitForNotification("addon-installed");
|
||||
acceptInstallDialog(installDialog);
|
||||
|
|
|
@ -265,12 +265,12 @@ var Harness = {
|
|||
|
||||
handleEvent(event) {
|
||||
if (event.type === "popupshown") {
|
||||
if (event.target.firstChild) {
|
||||
if (event.target.firstElementChild) {
|
||||
let popupId = event.target.getAttribute("popupid");
|
||||
if (popupId === "addon-webext-permissions") {
|
||||
this.popupReady(event.target.firstChild);
|
||||
this.popupReady(event.target.firstElementChild);
|
||||
} else if (popupId === "addon-installed" || popupId === "addon-install-failed") {
|
||||
event.target.firstChild.button.click();
|
||||
event.target.firstElementChild.button.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,35 +17,25 @@ namespace mozilla {
|
|||
inline bool
|
||||
EnsureLongPath(nsAString& aDosPath)
|
||||
{
|
||||
uint32_t aDosPathOriginalLen = aDosPath.Length();
|
||||
auto inputPath = PromiseFlatString(aDosPath);
|
||||
// Try to get the long path, or else get the required length of the long path
|
||||
DWORD longPathLen = GetLongPathNameW(inputPath.get(),
|
||||
reinterpret_cast<wchar_t*>(aDosPath.BeginWriting()),
|
||||
aDosPathOriginalLen);
|
||||
if (longPathLen == 0) {
|
||||
return false;
|
||||
nsAutoString inputPath(aDosPath);
|
||||
while (true) {
|
||||
DWORD requiredLength = GetLongPathNameW(inputPath.get(),
|
||||
reinterpret_cast<wchar_t*>(aDosPath.BeginWriting()),
|
||||
aDosPath.Length());
|
||||
if (!requiredLength) {
|
||||
return false;
|
||||
}
|
||||
if (requiredLength < aDosPath.Length()) {
|
||||
// When GetLongPathNameW deems the last argument too small,
|
||||
// it returns a value, but when you pass that value back, it's
|
||||
// satisfied and returns a number that's one smaller. If the above
|
||||
// check was == instead of <, the loop would go on forever with
|
||||
// GetLongPathNameW returning oscillating values!
|
||||
aDosPath.Truncate(requiredLength);
|
||||
return true;
|
||||
}
|
||||
aDosPath.SetLength(requiredLength);
|
||||
}
|
||||
aDosPath.SetLength(longPathLen);
|
||||
if (longPathLen <= aDosPathOriginalLen) {
|
||||
// Our string happened to be long enough for the first call to succeed.
|
||||
return true;
|
||||
}
|
||||
// Now we have a large enough buffer, get the actual string
|
||||
longPathLen = GetLongPathNameW(inputPath.get(),
|
||||
reinterpret_cast<wchar_t*>(aDosPath.BeginWriting()), aDosPath.Length());
|
||||
if (longPathLen == 0) {
|
||||
return false;
|
||||
}
|
||||
// This success check should always be less-than because longPathLen excludes
|
||||
// the null terminator on success, but includes it in the first call that
|
||||
// returned the required size.
|
||||
if (longPathLen < aDosPath.Length()) {
|
||||
aDosPath.SetLength(longPathLen);
|
||||
return true;
|
||||
}
|
||||
// We shouldn't reach this, but if we do then it's a failure!
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
@ -67,24 +57,32 @@ NtPathToDosPath(const nsAString& aNtPath, nsAString& aDosPath)
|
|||
return true;
|
||||
}
|
||||
nsAutoString logicalDrives;
|
||||
DWORD len = 0;
|
||||
while (true) {
|
||||
len = GetLogicalDriveStringsW(
|
||||
len, reinterpret_cast<wchar_t*>(logicalDrives.BeginWriting()));
|
||||
if (!len) {
|
||||
DWORD requiredLength = GetLogicalDriveStringsW(
|
||||
logicalDrives.Length(), reinterpret_cast<wchar_t*>(logicalDrives.BeginWriting()));
|
||||
if (!requiredLength) {
|
||||
return false;
|
||||
} else if (len > logicalDrives.Length()) {
|
||||
logicalDrives.SetLength(len);
|
||||
} else {
|
||||
}
|
||||
if (requiredLength < logicalDrives.Length()) {
|
||||
// When GetLogicalDriveStringsW deems the first argument too small,
|
||||
// it returns a value, but when you pass that value back, it's
|
||||
// satisfied and returns a number that's one smaller. If the above
|
||||
// check was == instead of <, the loop would go on forever with
|
||||
// GetLogicalDriveStringsW returning oscillating values!
|
||||
logicalDrives.Truncate(requiredLength);
|
||||
// logicalDrives now has the format "C:\\\0D:\\\0Z:\\\0". That is,
|
||||
// the sequence drive letter, colon, backslash, U+0000 repeats.
|
||||
break;
|
||||
}
|
||||
logicalDrives.SetLength(requiredLength);
|
||||
}
|
||||
|
||||
const char16_t* cur = logicalDrives.BeginReading();
|
||||
const char16_t* end = logicalDrives.EndReading();
|
||||
nsString targetPath;
|
||||
targetPath.SetLength(MAX_PATH);
|
||||
wchar_t driveTemplate[] = L" :";
|
||||
do {
|
||||
while (cur < end) {
|
||||
// Unfortunately QueryDosDevice doesn't support the idiom for querying the
|
||||
// output buffer size, so it may require retries.
|
||||
driveTemplate[0] = *cur;
|
||||
|
@ -113,9 +111,16 @@ NtPathToDosPath(const nsAString& aNtPath, nsAString& aDosPath)
|
|||
return EnsureLongPath(aDosPath);
|
||||
}
|
||||
}
|
||||
// Advance to the next NUL character in logicalDrives
|
||||
while (*cur++);
|
||||
} while (cur != end);
|
||||
// Find the next U+0000 within the logical string
|
||||
while (*cur) {
|
||||
// This loop skips the drive letter, the colon
|
||||
// and the backslash.
|
||||
cur++;
|
||||
}
|
||||
// Skip over the U+0000 that ends a drive entry
|
||||
// within the logical string
|
||||
cur++;
|
||||
}
|
||||
// Try to handle UNC paths. NB: This must happen after we've checked drive
|
||||
// mappings in case a UNC path is mapped to a drive!
|
||||
NS_NAMED_LITERAL_STRING(uncPrefix, "\\\\");
|
||||
|
|
Загрузка…
Ссылка в новой задаче