зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1711480 - Remove proton doorhanger and modal pref changes from tests r=Gijs
Differential Revision: https://phabricator.services.mozilla.com/D115248
This commit is contained in:
Родитель
248e714e1a
Коммит
4b9bb34823
|
@ -137,8 +137,6 @@ async function checkDialog(
|
|||
add_task(async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.proton.enabled", true],
|
||||
["browser.proton.modals.enabled", true],
|
||||
["prompts.contentPromptSubDialog", true],
|
||||
["prompts.modalType.httpAuth", Ci.nsIPrompt.MODAL_TYPE_TAB],
|
||||
["prompts.tabChromePromptSubDialog", true],
|
||||
|
|
|
@ -91,7 +91,7 @@ add_task(async function test_tabdialog_null_principal_title() {
|
|||
await BrowserTestUtils.withNewTab(TEST_DATA_URI, async function(browser) {
|
||||
info("Waiting for dialog to open.");
|
||||
await dialogShown;
|
||||
await checkOriginText(browser, "ScriptDlgNullPrincipalHeading");
|
||||
await checkOriginText(browser);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -111,7 +111,7 @@ add_task(async function test_tabdialog_extension_title() {
|
|||
await BrowserTestUtils.withNewTab(url, async function(browser) {
|
||||
info("Waiting for dialog to open.");
|
||||
await dialogShown;
|
||||
await checkOriginText(browser, "ScriptDlgHeading", "Test Extension");
|
||||
await checkOriginText(browser, "Test Extension");
|
||||
});
|
||||
|
||||
await extension.unload();
|
||||
|
@ -129,7 +129,7 @@ add_task(async function test_tabdialog_page_title() {
|
|||
await BrowserTestUtils.withNewTab(TEST_PAGE, async function(browser) {
|
||||
info("Waiting for dialog to open.");
|
||||
await dialogShown;
|
||||
await checkOriginText(browser, "ScriptDlgHeading", TEST_ORIGIN);
|
||||
await checkOriginText(browser, TEST_ORIGIN);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -138,12 +138,10 @@ add_task(async function test_tabdialog_page_title() {
|
|||
*
|
||||
* @param {Object} browser
|
||||
* The browser the dialog was opened from.
|
||||
* @param {String} stringKey
|
||||
* The bundle key for the text that should be displayed.
|
||||
* @param {String|null} origin
|
||||
* The page origin that should be displayed in the header, if any.
|
||||
*/
|
||||
async function checkOriginText(browser, stringKey, origin = null) {
|
||||
async function checkOriginText(browser, origin = null) {
|
||||
info("Check the title is visible.");
|
||||
let dialogBox = gBrowser.getTabDialogBox(browser);
|
||||
let contentPromptManager = dialogBox.getContentDialogManager();
|
||||
|
@ -153,39 +151,24 @@ async function checkOriginText(browser, stringKey, origin = null) {
|
|||
await dialog._dialogReady;
|
||||
|
||||
let dialogDoc = dialog._frame.contentWindow.document;
|
||||
let protonModals = Services.prefs.getBoolPref(
|
||||
"browser.proton.modals.enabled",
|
||||
false
|
||||
);
|
||||
let titleSelector = protonModals ? "#titleText" : "#infoTitle";
|
||||
let titleSelector = "#titleText";
|
||||
let infoTitle = dialogDoc.querySelector(titleSelector);
|
||||
ok(BrowserTestUtils.is_visible(infoTitle), "Title text is visible");
|
||||
|
||||
info("Check the displayed origin text is correct.");
|
||||
let titleText;
|
||||
|
||||
if (protonModals) {
|
||||
if (origin) {
|
||||
let host = origin;
|
||||
try {
|
||||
host = new URL(origin).host;
|
||||
} catch (ex) {
|
||||
/* will fail for the extension case. */
|
||||
}
|
||||
is(infoTitle.textContent, host, "Origin should be in header.");
|
||||
} else {
|
||||
is(
|
||||
infoTitle.dataset.l10nId,
|
||||
"common-dialog-title-null",
|
||||
"Null principal string should be in header."
|
||||
);
|
||||
if (origin) {
|
||||
let host = origin;
|
||||
try {
|
||||
host = new URL(origin).host;
|
||||
} catch (ex) {
|
||||
/* will fail for the extension case. */
|
||||
}
|
||||
is(infoTitle.textContent, host, "Origin should be in header.");
|
||||
} else {
|
||||
if (origin) {
|
||||
titleText = commonDialogsBundle.formatStringFromName(stringKey, [origin]);
|
||||
} else {
|
||||
titleText = commonDialogsBundle.GetStringFromName(stringKey);
|
||||
}
|
||||
is(infoTitle.textContent, titleText, "Origin header is correct.");
|
||||
is(
|
||||
infoTitle.dataset.l10nId,
|
||||
"common-dialog-title-null",
|
||||
"Null principal string should be in header."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,10 +72,7 @@ async function checkSeparatorInsertion(menuId, buttonId, subviewId) {
|
|||
}
|
||||
|
||||
add_task(async function check_devtools_separator() {
|
||||
const protonEnabled =
|
||||
Services.prefs.getBoolPref("browser.proton.doorhangers.enabled", false) &&
|
||||
Services.prefs.getBoolPref("browser.proton.enabled", false);
|
||||
const panelviewId = protonEnabled ? "appmenu-moreTools" : "PanelUI-developer";
|
||||
const panelviewId = "appmenu-moreTools";
|
||||
|
||||
await checkSeparatorInsertion(
|
||||
"menuWebDeveloperPopup",
|
||||
|
|
|
@ -14,15 +14,8 @@ add_task(async function testMoreToolsPanelInToolbar() {
|
|||
// We need to force DevToolsStartup to rebuild the developer tool toggle so that
|
||||
// proton prefs are applied to the new browser window for this test.
|
||||
DevToolsStartup.developerToggleCreated = false;
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.proton.enabled", true],
|
||||
["browser.proton.doorhangers.enabled", true],
|
||||
],
|
||||
});
|
||||
CustomizableUI.destroyWidget("developer-button");
|
||||
|
||||
// Now open a new window where the proton prefs will be applied.
|
||||
const win = await BrowserTestUtils.openNewBrowserWindow();
|
||||
|
||||
CustomizableUI.addWidgetToArea(
|
||||
|
|
|
@ -12,5 +12,3 @@ EXTRA_JS_MODULES += [
|
|||
]
|
||||
|
||||
JAR_MANIFESTS += ["jar.mn"]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ["test/browser/browser.ini"]
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[browser_fxmonitor_doorhanger.js]
|
||||
skip-if = debug # bug 1547517
|
||||
tags = remote-settings
|
|
@ -1,339 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"RemoteSettings",
|
||||
"resource://services-settings/remote-settings.js"
|
||||
);
|
||||
|
||||
const kNotificationId = "fxmonitor";
|
||||
const kRemoteSettingsKey = "fxmonitor-breaches";
|
||||
|
||||
async function fxmonitorNotificationShown() {
|
||||
await TestUtils.waitForCondition(() => {
|
||||
return (
|
||||
PopupNotifications.getNotification(kNotificationId) &&
|
||||
PopupNotifications.panel.state == "open"
|
||||
);
|
||||
}, "Waiting for fxmonitor notification to be shown");
|
||||
ok(true, "Firefox Monitor PopupNotification was added.");
|
||||
}
|
||||
|
||||
async function fxmonitorNotificationGone() {
|
||||
await TestUtils.waitForCondition(() => {
|
||||
return (
|
||||
!PopupNotifications.getNotification(kNotificationId) &&
|
||||
PopupNotifications.panel.state == "closed"
|
||||
);
|
||||
}, "Waiting for fxmonitor notification to go away");
|
||||
ok(true, "Firefox Monitor PopupNotification was removed.");
|
||||
}
|
||||
|
||||
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
|
||||
Ci.nsIContentPrefService2
|
||||
);
|
||||
|
||||
async function clearWarnedHosts() {
|
||||
return new Promise((resolve, reject) => {
|
||||
cps2.removeByName(
|
||||
"extensions.fxmonitor.hostAlreadyWarned",
|
||||
Cu.createLoadContext(),
|
||||
{
|
||||
handleCompletion: resolve,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
// This test used to rely on the initial timer of
|
||||
// TestUtils.waitForCondition. See bug 1700683.
|
||||
let originalWaitForCondition = TestUtils.waitForCondition;
|
||||
TestUtils.waitForCondition = async function(
|
||||
condition,
|
||||
msg,
|
||||
interval = 100,
|
||||
maxTries = 50
|
||||
) {
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
return originalWaitForCondition(condition, msg, interval, maxTries);
|
||||
};
|
||||
registerCleanupFunction(function() {
|
||||
TestUtils.waitForCondition = originalWaitForCondition;
|
||||
});
|
||||
|
||||
// Disable Proton. The Firefox Monitor doorhanger is disabled with Proton
|
||||
// enabled, so most of these tests won't work in that configuration. We are,
|
||||
// however, keeping these tests around for now until Proton ships.
|
||||
//
|
||||
// There is a test later in this file that checks that the panel doesn't appear
|
||||
// with Proton enabled.
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.proton.doorhangers.enabled", false]],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_main_flow() {
|
||||
info("Test that we show the first alert correctly for a recent breach.");
|
||||
|
||||
// Pre-populate the Remote Settings collection with a breach.
|
||||
let db = await RemoteSettings(kRemoteSettingsKey).db;
|
||||
let BreachDate = new Date();
|
||||
let AddedDate = new Date();
|
||||
await db.create({
|
||||
Domain: "example.com",
|
||||
Name: "Example Site",
|
||||
BreachDate: `${BreachDate.getFullYear()}-${BreachDate.getMonth() +
|
||||
1}-${BreachDate.getDate()}`,
|
||||
AddedDate: `${AddedDate.getFullYear()}-${AddedDate.getMonth() +
|
||||
1}-${AddedDate.getDate()}`,
|
||||
PwnCount: 1000000,
|
||||
});
|
||||
await db.importChanges({}, 1234567);
|
||||
|
||||
// Trigger a sync.
|
||||
await RemoteSettings(kRemoteSettingsKey).emit("sync", {
|
||||
data: {
|
||||
current: await RemoteSettings(kRemoteSettingsKey).get(),
|
||||
},
|
||||
});
|
||||
|
||||
// Enable the extension.
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["extensions.fxmonitor.FirefoxMonitorURL", "http://example.org"]],
|
||||
});
|
||||
|
||||
// Open a tab and wait for the alert.
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(
|
||||
gBrowser,
|
||||
"http://example.com"
|
||||
);
|
||||
await fxmonitorNotificationShown();
|
||||
|
||||
// Test that dismissing works.
|
||||
let notification = Array.prototype.find.call(
|
||||
PopupNotifications.panel.children,
|
||||
elt => elt.getAttribute("popupid") == kNotificationId
|
||||
);
|
||||
EventUtils.synthesizeMouseAtCenter(notification.secondaryButton, {});
|
||||
await fxmonitorNotificationGone();
|
||||
|
||||
// Reload and make sure the alert isn't shown again.
|
||||
let promise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
tab.linkedBrowser.reload();
|
||||
await promise;
|
||||
await fxmonitorNotificationGone();
|
||||
|
||||
// Reset state.
|
||||
await db.clear();
|
||||
await db.importChanges({}, 1234567);
|
||||
await clearWarnedHosts();
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
clear: [["extensions.fxmonitor.firstAlertShown"]],
|
||||
});
|
||||
|
||||
// Reload and wait for the alert.
|
||||
promise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
tab.linkedBrowser.reload();
|
||||
await promise;
|
||||
await fxmonitorNotificationShown();
|
||||
|
||||
// Test that the primary button opens Firefox Monitor in a new tab.
|
||||
notification = Array.prototype.find.call(
|
||||
PopupNotifications.panel.children,
|
||||
elt => elt.getAttribute("popupid") == kNotificationId
|
||||
);
|
||||
let url = `http://example.org/?breach=${encodeURIComponent(
|
||||
"Example Site"
|
||||
)}&utm_source=firefox&utm_medium=popup`;
|
||||
promise = BrowserTestUtils.waitForNewTab(gBrowser, url);
|
||||
EventUtils.synthesizeMouseAtCenter(notification.button, {});
|
||||
let newtab = await promise;
|
||||
|
||||
// Close the new tab and check that the alert is gone.
|
||||
BrowserTestUtils.removeTab(newtab);
|
||||
await fxmonitorNotificationGone();
|
||||
|
||||
// Reset state (but not firstAlertShown).
|
||||
await db.clear();
|
||||
await db.importChanges({}, 1234567);
|
||||
await clearWarnedHosts();
|
||||
|
||||
info(
|
||||
"Test that we do not show the second alert for a breach added over two months ago."
|
||||
);
|
||||
|
||||
// Add a new "old" breach - added over 2 months ago.
|
||||
AddedDate.setMonth(AddedDate.getMonth() - 3);
|
||||
await db.create({
|
||||
Domain: "example.com",
|
||||
Name: "Example Site",
|
||||
BreachDate: `${BreachDate.getFullYear()}-${BreachDate.getMonth() +
|
||||
1}-${BreachDate.getDate()}`,
|
||||
AddedDate: `${AddedDate.getFullYear()}-${AddedDate.getMonth() +
|
||||
1}-${AddedDate.getDate()}`,
|
||||
PwnCount: 1000000,
|
||||
});
|
||||
await db.importChanges({}, 1234567);
|
||||
|
||||
// Trigger a sync.
|
||||
await RemoteSettings(kRemoteSettingsKey).emit("sync", {
|
||||
data: {
|
||||
current: await RemoteSettings(kRemoteSettingsKey).get(),
|
||||
},
|
||||
});
|
||||
|
||||
// Check that there's no alert for the old breach.
|
||||
promise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
tab.linkedBrowser.reload();
|
||||
await promise;
|
||||
await fxmonitorNotificationGone();
|
||||
|
||||
// Reset state (but not firstAlertShown).
|
||||
AddedDate.setMonth(AddedDate.getMonth() + 3);
|
||||
await db.clear();
|
||||
await db.importChanges({}, 1234567);
|
||||
await clearWarnedHosts();
|
||||
|
||||
info("Test that we do show the second alert for a recent breach.");
|
||||
|
||||
// Add a new "recent" breach.
|
||||
await db.create({
|
||||
Domain: "example.com",
|
||||
Name: "Example Site",
|
||||
BreachDate: `${BreachDate.getFullYear()}-${BreachDate.getMonth() +
|
||||
1}-${BreachDate.getDate()}`,
|
||||
AddedDate: `${AddedDate.getFullYear()}-${AddedDate.getMonth() +
|
||||
1}-${AddedDate.getDate()}`,
|
||||
PwnCount: 1000000,
|
||||
});
|
||||
await db.importChanges({}, 1234567);
|
||||
|
||||
// Trigger a sync.
|
||||
await RemoteSettings(kRemoteSettingsKey).emit("sync", {
|
||||
data: {
|
||||
current: await RemoteSettings(kRemoteSettingsKey).get(),
|
||||
},
|
||||
});
|
||||
|
||||
// Check that there's an alert for the new breach.
|
||||
promise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
tab.linkedBrowser.reload();
|
||||
await promise;
|
||||
await fxmonitorNotificationShown();
|
||||
|
||||
// Reset state (including firstAlertShown)
|
||||
await db.clear();
|
||||
await db.importChanges({}, 1234567);
|
||||
await clearWarnedHosts();
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
clear: [["extensions.fxmonitor.firstAlertShown"]],
|
||||
});
|
||||
|
||||
info(
|
||||
"Test that we do not show the first alert for a breach added over a year ago."
|
||||
);
|
||||
|
||||
// Add a new "old" breach - added over a year ago.
|
||||
AddedDate.setFullYear(AddedDate.getFullYear() - 2);
|
||||
await db.create({
|
||||
Domain: "example.com",
|
||||
Name: "Example Site",
|
||||
BreachDate: `${BreachDate.getFullYear()}-${BreachDate.getMonth() +
|
||||
1}-${BreachDate.getDate()}`,
|
||||
AddedDate: `${AddedDate.getFullYear()}-${AddedDate.getMonth() +
|
||||
1}-${AddedDate.getDate()}`,
|
||||
PwnCount: 1000000,
|
||||
});
|
||||
await db.importChanges({}, 1234567);
|
||||
|
||||
// Trigger a sync.
|
||||
await RemoteSettings(kRemoteSettingsKey).emit("sync", {
|
||||
data: {
|
||||
current: await RemoteSettings(kRemoteSettingsKey).get(),
|
||||
},
|
||||
});
|
||||
|
||||
// Check that there's no alert for the old breach.
|
||||
promise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
tab.linkedBrowser.reload();
|
||||
await promise;
|
||||
await fxmonitorNotificationGone();
|
||||
|
||||
// Clean up.
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
await db.clear();
|
||||
await db.importChanges({}, 1234567);
|
||||
// Trigger a sync to clear.
|
||||
await RemoteSettings(kRemoteSettingsKey).emit("sync", {
|
||||
data: {
|
||||
current: [],
|
||||
},
|
||||
});
|
||||
await clearWarnedHosts();
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
clear: [["extensions.fxmonitor.firstAlertShown"]],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_proton_disabled() {
|
||||
info("Test that we don't show an alert if proton is enabled.");
|
||||
|
||||
// Pre-populate the Remote Settings collection with a breach.
|
||||
let db = await RemoteSettings(kRemoteSettingsKey).db;
|
||||
let BreachDate = new Date();
|
||||
let AddedDate = new Date();
|
||||
await db.create({
|
||||
Domain: "example.com",
|
||||
Name: "Example Site",
|
||||
BreachDate: `${BreachDate.getFullYear()}-${BreachDate.getMonth() +
|
||||
1}-${BreachDate.getDate()}`,
|
||||
AddedDate: `${AddedDate.getFullYear()}-${AddedDate.getMonth() +
|
||||
1}-${AddedDate.getDate()}`,
|
||||
PwnCount: 1000000,
|
||||
});
|
||||
await db.importChanges({}, 1234567);
|
||||
|
||||
// Trigger a sync.
|
||||
await RemoteSettings(kRemoteSettingsKey).emit("sync", {
|
||||
data: {
|
||||
current: await RemoteSettings(kRemoteSettingsKey).get(),
|
||||
},
|
||||
});
|
||||
|
||||
// Enable proton
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.proton.doorhangers.enabled", true]],
|
||||
});
|
||||
|
||||
// Open a tab and wait for the alert.
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(
|
||||
gBrowser,
|
||||
"http://example.com"
|
||||
);
|
||||
|
||||
try {
|
||||
await fxmonitorNotificationShown();
|
||||
ok(false, "Notification was shown but shouldn't have been!");
|
||||
} catch (e) {
|
||||
ok(true, "No notifaction was shown with proton enabled.");
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
await db.clear();
|
||||
await db.importChanges({}, 1234567);
|
||||
// Trigger a sync to clear.
|
||||
await RemoteSettings(kRemoteSettingsKey).emit("sync", {
|
||||
data: {
|
||||
current: [],
|
||||
},
|
||||
});
|
||||
});
|
|
@ -470,12 +470,8 @@ function execute_test_in_sidebar(test) {
|
|||
}
|
||||
|
||||
async function promise_properties_window(dialogUrl = DIALOG_URL) {
|
||||
let protonModal = Services.prefs.getBoolPref(
|
||||
"browser.proton.modals.enabled",
|
||||
false
|
||||
);
|
||||
let win = await BrowserTestUtils.promiseAlertDialogOpen(null, dialogUrl, {
|
||||
isSubDialog: protonModal,
|
||||
isSubDialog: true,
|
||||
});
|
||||
await SimpleTest.promiseFocus(win);
|
||||
await TestUtils.waitForCondition(
|
||||
|
|
|
@ -214,13 +214,11 @@ var withBookmarksDialog = async function(
|
|||
skipOverlayWait = false
|
||||
) {
|
||||
let closed = false;
|
||||
let protonModal =
|
||||
Services.prefs.getBoolPref("browser.proton.modals.enabled", false) &&
|
||||
// We can't show the in-window prompt for windows which don't have
|
||||
// gDialogBox, like the library (Places:Organizer) window.
|
||||
Services.wm.getMostRecentWindow("").gDialogBox;
|
||||
// We can't show the in-window prompt for windows which don't have
|
||||
// gDialogBox, like the library (Places:Organizer) window.
|
||||
let hasDialogBox = !!Services.wm.getMostRecentWindow("").gDialogBox;
|
||||
let dialogPromise;
|
||||
if (protonModal) {
|
||||
if (hasDialogBox) {
|
||||
dialogPromise = BrowserTestUtils.promiseAlertDialogOpen(null, dialogUrl, {
|
||||
isSubDialog: true,
|
||||
});
|
||||
|
@ -237,7 +235,7 @@ var withBookmarksDialog = async function(
|
|||
});
|
||||
}
|
||||
let dialogClosePromise = dialogPromise.then(win => {
|
||||
if (!protonModal) {
|
||||
if (!hasDialogBox) {
|
||||
return BrowserTestUtils.domWindowClosed(win);
|
||||
}
|
||||
let container = win.top.document.getElementById("window-modal-dialog");
|
||||
|
|
|
@ -112,22 +112,13 @@ add_task(async function test() {
|
|||
|
||||
is(prompts.length, tabs.length, "Should have one prompt per tab");
|
||||
|
||||
if (Services.prefs.getBoolPref("browser.proton.modals.enabled", false)) {
|
||||
for (let i = 0; i < prompts.length; i++) {
|
||||
let titleEl = prompts[i].ui.prompt.document.querySelector("#titleText");
|
||||
is(
|
||||
titleEl.textContent,
|
||||
new URL(tabs[i].origin).host,
|
||||
"Prompt matches the tab's host"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < prompts.length; i++) {
|
||||
ok(
|
||||
prompts[i].args.text.startsWith(tabs[i].origin),
|
||||
"Prompt matches the tabs origin"
|
||||
);
|
||||
}
|
||||
for (let i = 0; i < prompts.length; i++) {
|
||||
let titleEl = prompts[i].ui.prompt.document.querySelector("#titleText");
|
||||
is(
|
||||
titleEl.textContent,
|
||||
new URL(tabs[i].origin).host,
|
||||
"Prompt matches the tab's host"
|
||||
);
|
||||
}
|
||||
|
||||
// Interact with the prompts. This is deliberately done out of order
|
||||
|
|
Загрузка…
Ссылка в новой задаче