mirror of
https://github.com/zen-browser/desktop.git
synced 2025-12-16 03:15:29 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ae7c19c30 | ||
|
|
e525b32c18 | ||
|
|
e3d13d534e | ||
|
|
e73ea97ea0 | ||
|
|
fb9bbc3a51 | ||
|
|
bdfb810212 | ||
|
|
f1c6c1b321 |
4
.github/workflows/sync-upstream.yml
vendored
4
.github/workflows/sync-upstream.yml
vendored
@@ -95,6 +95,10 @@ jobs:
|
||||
echo "Checking if patches apply cleanly..."
|
||||
npm run import
|
||||
|
||||
- name: Import external tests
|
||||
if: steps.git-check.outputs.files_changed == 'true'
|
||||
run: python3 scripts/import_external_tests.py
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
if: steps.git-check.outputs.files_changed == 'true'
|
||||
|
||||
@@ -17,6 +17,8 @@ engine/
|
||||
|
||||
surfer.json
|
||||
|
||||
src/zen/tests/mochitests/*
|
||||
|
||||
src/browser/app/profile/*.js
|
||||
pnpm-lock.yaml
|
||||
|
||||
|
||||
@@ -15,6 +15,5 @@
|
||||
"ebay",
|
||||
"ebay-*"
|
||||
]
|
||||
},
|
||||
"timestamp": 1765455207275
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import js from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import { defineConfig } from 'eslint/config';
|
||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||
import zenGlobals from './src/zen/zen.globals.js';
|
||||
|
||||
export default defineConfig([
|
||||
@@ -23,4 +23,5 @@ export default defineConfig([
|
||||
},
|
||||
ignores: ['**/vendor/**', '**/tests/**'],
|
||||
},
|
||||
globalIgnores(['**/mochitests/**']),
|
||||
]);
|
||||
|
||||
@@ -191,6 +191,9 @@ category-zen-CKS =
|
||||
.tooltiptext = { pane-zen-CKS-title }
|
||||
pane-settings-CKS-title = { -brand-short-name } Keyboard Shortcuts
|
||||
|
||||
category-zen-marketplace =
|
||||
.tooltiptext = Zen Mods
|
||||
|
||||
zen-settings-CKS-header = Customize your keyboard shortcuts
|
||||
zen-settings-CKS-description = Change the default keyboard shortcuts to your liking and improve your browsing experience
|
||||
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
- name: browser.sessionstore.restore_pinned_tabs_on_demand
|
||||
value: true
|
||||
|
||||
- name: browser.tabs.loadBookmarksInTabs
|
||||
value: false
|
||||
|
||||
- name: browser.tabs.hoverPreview.enabled
|
||||
value: false
|
||||
|
||||
@@ -23,18 +20,6 @@
|
||||
- name: browser.toolbars.bookmarks.visibility
|
||||
value: 'never'
|
||||
|
||||
- name: browser.bookmarks.openInTabClosesMenu
|
||||
value: false
|
||||
|
||||
- name: browser.menu.showViewImageInfo
|
||||
value: true
|
||||
|
||||
- name: findbar.highlightAll
|
||||
value: true
|
||||
|
||||
- name: layout.word_select.eat_space_to_next_word
|
||||
value: false
|
||||
|
||||
- name: widget.non-native-theme.scrollbar.style
|
||||
value: 2
|
||||
|
||||
@@ -58,9 +43,6 @@
|
||||
- name: browser.download.manager.addToRecentDocs
|
||||
value: false
|
||||
|
||||
- name: browser.download.open_pdf_attachments_inline
|
||||
value: true
|
||||
|
||||
- name: browser.download.alwaysOpenPanel
|
||||
value: false
|
||||
|
||||
|
||||
@@ -8,3 +8,12 @@
|
||||
|
||||
- name: network.predictor.enable-hover-on-ssl
|
||||
value: true
|
||||
|
||||
# See https://github.com/zen-browser/desktop/issues/11599, this pref seems to
|
||||
# have disabled itself on macos for some unknown reason.
|
||||
# Make sure its in sync with:
|
||||
# https://searchfox.org/firefox-main/rev/1477feb9706f4ccc5bd571c1c215832a6fbb7464/modules/libpref/init/StaticPrefList.yaml#7741-7748
|
||||
- name: gfx.webrender.compositor
|
||||
condition: 'defined(XP_WIN) || defined(XP_DARWIN)'
|
||||
value: '@cond'
|
||||
mirror: once
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
value: true
|
||||
locked: true
|
||||
|
||||
# Enable private suggestions
|
||||
- name: browser.search.suggest.enabled
|
||||
value: false
|
||||
|
||||
|
||||
@@ -7,3 +7,9 @@
|
||||
|
||||
- name: zen.downloads.download-animation-duration
|
||||
value: 1000 # ms
|
||||
|
||||
- name: zen.downloads.icon-popup-position
|
||||
# 0: Follow tab's position
|
||||
# 1: Left side always
|
||||
# 2: Right side always
|
||||
value: 0
|
||||
|
||||
124
scripts/import_external_tests.py
Normal file
124
scripts/import_external_tests.py
Normal file
@@ -0,0 +1,124 @@
|
||||
# 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/.
|
||||
|
||||
import os
|
||||
import tomllib
|
||||
import shutil
|
||||
|
||||
BASE_PATH = os.path.join("src", "zen", "tests")
|
||||
EXTERNAL_TESTS_MANIFEST = os.path.join(BASE_PATH, "manifest.toml")
|
||||
EXTERNAL_TESTS_OUTPUT = os.path.join(BASE_PATH, "mochitests")
|
||||
|
||||
FILE_PREFIX = """
|
||||
# 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/.
|
||||
|
||||
# This file is autogenerated by scripts/import_external_tests.py
|
||||
# Do not edit manually.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
"""
|
||||
|
||||
FILE_SUFFIX = "]"
|
||||
|
||||
|
||||
def get_tests_manifest():
|
||||
with open(EXTERNAL_TESTS_MANIFEST, "rb") as f:
|
||||
return tomllib.load(f)
|
||||
|
||||
|
||||
def die_with_error(message):
|
||||
print(f"ERROR: {message}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def validate_tests_path(path, files, ignore_list):
|
||||
for ignore in ignore_list:
|
||||
if ignore not in files:
|
||||
die_with_error(f"Ignore file '{ignore}' not found in tests folder '{path}'")
|
||||
if "browser.toml" not in files or "browser.js" in ignore_list:
|
||||
die_with_error(f"'browser.toml' not found in tests folder '{path}'")
|
||||
|
||||
|
||||
def disable_and_replace_manifest(manifest, output_path):
|
||||
toml_file = os.path.join(output_path, "browser.toml")
|
||||
disabled_tests = manifest.get("disable", [])
|
||||
with open(toml_file, "r") as f:
|
||||
data = f.read()
|
||||
for test in disabled_tests:
|
||||
segment = f'["{test}"]'
|
||||
if segment not in data:
|
||||
die_with_error(f"Could not disable test '{test}' as it was not found in '{toml_file}'")
|
||||
replace_with = f'["{test}"]\ndisabled="Disabled by import_external_tests.py"'
|
||||
data = data.replace(segment, replace_with)
|
||||
for replacement in manifest.get("replace-manifest", {}).keys():
|
||||
if replacement not in data:
|
||||
die_with_error(f"Could not replace manifest entry '{replacement}' as it was not found in '{toml_file}'")
|
||||
data = data.replace(replacement, manifest["replace-manifest"][replacement])
|
||||
with open(toml_file, "w") as f:
|
||||
f.write(data)
|
||||
|
||||
|
||||
def import_test_suite(test_suite, source_path, output_path, ignore_list, manifest, is_direct_path=False):
|
||||
print(f"Importing test suite '{test_suite}' from '{source_path}'")
|
||||
tests_folder = os.path.join("engine", source_path)
|
||||
if not is_direct_path:
|
||||
tests_folder = os.path.join(tests_folder, "tests")
|
||||
if not os.path.exists(tests_folder):
|
||||
die_with_error(f"Tests folder not found: {tests_folder}")
|
||||
files = os.listdir(tests_folder)
|
||||
validate_tests_path(tests_folder, files, ignore_list)
|
||||
if os.path.exists(output_path):
|
||||
shutil.rmtree(output_path)
|
||||
os.makedirs(output_path, exist_ok=True)
|
||||
for item in files:
|
||||
if item in ignore_list:
|
||||
continue
|
||||
s = os.path.join(tests_folder, item)
|
||||
d = os.path.join(output_path, item)
|
||||
if os.path.isdir(s):
|
||||
shutil.copytree(s, d)
|
||||
else:
|
||||
shutil.copy2(s, d)
|
||||
disable_and_replace_manifest(manifest[test_suite], output_path)
|
||||
|
||||
|
||||
def write_moz_build_file(manifest):
|
||||
moz_build_path = os.path.join(EXTERNAL_TESTS_OUTPUT, "moz.build")
|
||||
print(f"Writing moz.build file to '{moz_build_path}'")
|
||||
with open(moz_build_path, "w") as f:
|
||||
f.write(FILE_PREFIX)
|
||||
for test_suite in manifest.keys():
|
||||
f.write(f'\t"{test_suite}/browser.toml",\n')
|
||||
f.write(FILE_SUFFIX)
|
||||
|
||||
|
||||
def make_sure_ordered_tests(manifest):
|
||||
ordered_tests = sorted(manifest.keys())
|
||||
if list(manifest.keys()) != ordered_tests:
|
||||
die_with_error("Test suites in manifest.toml are not in alphabetical order.")
|
||||
|
||||
|
||||
def main():
|
||||
manifest = get_tests_manifest()
|
||||
if os.path.exists(EXTERNAL_TESTS_OUTPUT):
|
||||
shutil.rmtree(EXTERNAL_TESTS_OUTPUT)
|
||||
os.makedirs(EXTERNAL_TESTS_OUTPUT, exist_ok=True)
|
||||
|
||||
make_sure_ordered_tests(manifest)
|
||||
for test_suite, config in manifest.items():
|
||||
import_test_suite(
|
||||
test_suite=test_suite,
|
||||
source_path=config["source"],
|
||||
output_path=os.path.join(EXTERNAL_TESTS_OUTPUT, test_suite),
|
||||
ignore_list=config.get("ignore", []),
|
||||
is_direct_path=config.get("is_direct_path", False),
|
||||
manifest=manifest
|
||||
)
|
||||
write_moz_build_file(manifest)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -5,6 +5,7 @@
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
|
||||
class JSONWithCommentsDecoder(json.JSONDecoder):
|
||||
def __init__(self, **kw):
|
||||
super().__init__(**kw)
|
||||
|
||||
@@ -15,6 +15,8 @@ IGNORE_PREFS_FILE_OUT = os.path.join(
|
||||
'engine', 'testing', 'mochitest', 'ignorePrefs.json'
|
||||
)
|
||||
|
||||
MOCHITEST_NAME = "mochitests"
|
||||
|
||||
|
||||
def copy_ignore_prefs():
|
||||
print("Copying ignorePrefs.json from src/zen/tests to engine/testing/mochitest...")
|
||||
@@ -59,7 +61,9 @@ def main():
|
||||
os.execvp(command[0], command)
|
||||
|
||||
if path in ("", "all"):
|
||||
test_dirs = [p for p in Path("zen/tests").iterdir() if p.is_dir()]
|
||||
test_dirs = [p for p in Path("zen/tests").iterdir() if p.is_dir() and p.name != MOCHITEST_NAME]
|
||||
mochitest_dirs = [p for p in Path(f"zen/tests/{MOCHITEST_NAME}").iterdir() if p.is_dir()]
|
||||
test_dirs.extend(mochitest_dirs)
|
||||
test_paths = [str(p) for p in test_dirs]
|
||||
run_mach_with_paths(test_paths)
|
||||
else:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/base/content/browser-addons.js b/browser/base/content/browser-addons.js
|
||||
index fadcbfca95ee28140579430c0371baad0e2f216a..7454b801b4ad892d6ad122277eb7c7736e976f9f 100644
|
||||
index fadcbfca95ee28140579430c0371baad0e2f216a..22a92ca450ef5087610169b15d7f3344c9accddf 100644
|
||||
--- a/browser/base/content/browser-addons.js
|
||||
+++ b/browser/base/content/browser-addons.js
|
||||
@@ -1069,7 +1069,7 @@ var gXPInstallObserver = {
|
||||
@@ -38,37 +38,131 @@ index fadcbfca95ee28140579430c0371baad0e2f216a..7454b801b4ad892d6ad122277eb7c773
|
||||
}
|
||||
|
||||
return anchorID;
|
||||
@@ -2657,11 +2657,7 @@ var gUnifiedExtensions = {
|
||||
// Lazy load the unified-extensions-panel panel the first time we need to
|
||||
// display it.
|
||||
if (!this._panel) {
|
||||
- let template = document.getElementById(
|
||||
- "unified-extensions-panel-template"
|
||||
- );
|
||||
- template.replaceWith(template.content);
|
||||
- this._panel = document.getElementById("unified-extensions-panel");
|
||||
+ this._panel = document.getElementById("zen-unified-site-data-panel");
|
||||
let customizationArea = this._panel.querySelector(
|
||||
"#unified-extensions-area"
|
||||
@@ -2547,7 +2547,7 @@ var gUnifiedExtensions = {
|
||||
requestAnimationFrame(() => this.updateAttention());
|
||||
},
|
||||
|
||||
- onToolbarVisibilityChange(toolbarId, isVisible) {
|
||||
+ onToolbarVisibilityChange(toolbarId, isVisible, panel = this.panel) {
|
||||
// A list of extension widget IDs (possibly empty).
|
||||
let widgetIDs;
|
||||
|
||||
@@ -2561,7 +2561,7 @@ var gUnifiedExtensions = {
|
||||
}
|
||||
|
||||
// The list of overflowed extensions in the extensions panel.
|
||||
- const overflowedExtensionsList = this.panel.querySelector(
|
||||
+ const overflowedExtensionsList = panel.querySelector(
|
||||
"#overflowed-extensions-list"
|
||||
);
|
||||
|
||||
@@ -2662,37 +2662,41 @@ var gUnifiedExtensions = {
|
||||
);
|
||||
@@ -2714,6 +2710,7 @@ var gUnifiedExtensions = {
|
||||
// and no alternative content is available for display in the panel.
|
||||
const policies = this.getActivePolicies();
|
||||
template.replaceWith(template.content);
|
||||
this._panel = document.getElementById("unified-extensions-panel");
|
||||
- let customizationArea = this._panel.querySelector(
|
||||
- "#unified-extensions-area"
|
||||
- );
|
||||
- CustomizableUI.registerPanelNode(
|
||||
- customizationArea,
|
||||
- CustomizableUI.AREA_ADDONS
|
||||
- );
|
||||
- CustomizableUI.addPanelCloseListeners(this._panel);
|
||||
-
|
||||
- this._panel
|
||||
- .querySelector("#unified-extensions-manage-extensions")
|
||||
- .addEventListener("command", () => {
|
||||
- BrowserAddonUI.openAddonsMgr("addons://list/extension");
|
||||
- });
|
||||
-
|
||||
- // Lazy-load the l10n strings. Those strings are used for the CUI and
|
||||
- // non-CUI extensions in the unified extensions panel.
|
||||
- document
|
||||
- .getElementById("unified-extensions-context-menu")
|
||||
- .querySelectorAll("[data-lazy-l10n-id]")
|
||||
- .forEach(el => {
|
||||
- el.setAttribute("data-l10n-id", el.getAttribute("data-lazy-l10n-id"));
|
||||
- el.removeAttribute("data-lazy-l10n-id");
|
||||
- });
|
||||
+ this.initializePanel(this._panel);
|
||||
}
|
||||
return this._panel;
|
||||
},
|
||||
|
||||
+ initializePanel(panel) {
|
||||
+ let customizationArea = panel.querySelector(
|
||||
+ "#unified-extensions-area"
|
||||
+ );
|
||||
+ CustomizableUI.registerPanelNode(
|
||||
+ customizationArea,
|
||||
+ CustomizableUI.AREA_ADDONS
|
||||
+ );
|
||||
+ CustomizableUI.addPanelCloseListeners(panel);
|
||||
+
|
||||
+ panel
|
||||
+ .querySelector("#unified-extensions-manage-extensions")
|
||||
+ .addEventListener("command", () => {
|
||||
+ BrowserAddonUI.openAddonsMgr("addons://list/extension");
|
||||
+ });
|
||||
+
|
||||
+ // Lazy-load the l10n strings. Those strings are used for the CUI and
|
||||
+ // non-CUI extensions in the unified extensions panel.
|
||||
+ document
|
||||
+ .getElementById("unified-extensions-context-menu")
|
||||
+ .querySelectorAll("[data-lazy-l10n-id]")
|
||||
+ .forEach(el => {
|
||||
+ el.setAttribute("data-l10n-id", el.getAttribute("data-lazy-l10n-id"));
|
||||
+ el.removeAttribute("data-lazy-l10n-id");
|
||||
+ });
|
||||
+ },
|
||||
+
|
||||
// `aEvent` and `reason` are optional. If `reason` is specified, it should be
|
||||
// a valid argument to gUnifiedExtensions.recordButtonTelemetry().
|
||||
- async togglePanel(aEvent, reason) {
|
||||
+ async togglePanel(aEvent, reason, panel = this._panel, view = "unified-extensions-view", button = this._button) {
|
||||
if (!CustomizationHandler.isCustomizing()) {
|
||||
if (aEvent) {
|
||||
if (
|
||||
+ false &&
|
||||
policies.length &&
|
||||
!this.hasExtensionsInPanel(policies) &&
|
||||
!this.isPrivateWindowMissingExtensionsWithoutPBMAccess() &&
|
||||
@@ -2754,7 +2751,7 @@ var gUnifiedExtensions = {
|
||||
@@ -2729,32 +2733,30 @@ var gUnifiedExtensions = {
|
||||
this.blocklistAttentionInfo =
|
||||
await AddonManager.getBlocklistAttentionInfo();
|
||||
|
||||
- let panel = this.panel;
|
||||
-
|
||||
if (!this._listView) {
|
||||
this._listView = PanelMultiView.getViewNode(
|
||||
document,
|
||||
- "unified-extensions-view"
|
||||
+ view,
|
||||
);
|
||||
this._listView.addEventListener("ViewShowing", this);
|
||||
this._listView.addEventListener("ViewHiding", this);
|
||||
}
|
||||
|
||||
- if (this._button.open) {
|
||||
+ if (button.open) {
|
||||
PanelMultiView.hidePopup(panel);
|
||||
- this._button.open = false;
|
||||
+ button.open = false;
|
||||
} else {
|
||||
// Overflow extensions placed in collapsed toolbars, if any.
|
||||
for (const toolbarId of CustomizableUI.getCollapsedToolbarIds(window)) {
|
||||
// We pass `false` because all these toolbars are collapsed.
|
||||
- this.onToolbarVisibilityChange(toolbarId, /* isVisible */ false);
|
||||
+ this.onToolbarVisibilityChange(toolbarId, /* isVisible */ false, panel);
|
||||
}
|
||||
|
||||
panel.hidden = false;
|
||||
this.recordButtonTelemetry(reason || "extensions_panel_showing");
|
||||
this.ensureButtonShownBeforeAttachingPanel(panel);
|
||||
PanelMultiView.openPopup(panel, this._button, {
|
||||
- PanelMultiView.openPopup(panel, this._button, {
|
||||
- position: "bottomright topright",
|
||||
+ position: gZenUIManager.panelUIPosition(panel, this._button),
|
||||
+ PanelMultiView.openPopup(panel, button, {
|
||||
+ position: gZenUIManager.panelUIPosition(panel, button),
|
||||
triggerEvent: aEvent,
|
||||
});
|
||||
}
|
||||
@@ -2941,18 +2938,20 @@ var gUnifiedExtensions = {
|
||||
@@ -2941,18 +2943,20 @@ var gUnifiedExtensions = {
|
||||
this._maybeMoveWidgetNodeBack(widgetId);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/base/content/navigator-toolbox.js b/browser/base/content/navigator-toolbox.js
|
||||
index 7b776b15d52367a008ce6bf53dcfcbbe007b7453..d9a3404905b73db7c8f202ab166d5f3c625351f6 100644
|
||||
index 7b776b15d52367a008ce6bf53dcfcbbe007b7453..da23f716c753f5a43f17bb5ed7a3d335891168c2 100644
|
||||
--- a/browser/base/content/navigator-toolbox.js
|
||||
+++ b/browser/base/content/navigator-toolbox.js
|
||||
@@ -6,7 +6,7 @@
|
||||
@@ -27,21 +27,28 @@ index 7b776b15d52367a008ce6bf53dcfcbbe007b7453..d9a3404905b73db7c8f202ab166d5f3c
|
||||
gBrowser.handleNewTabMiddleClick(element, event);
|
||||
break;
|
||||
|
||||
@@ -315,7 +317,7 @@ document.addEventListener(
|
||||
#pageActionButton,
|
||||
@@ -316,6 +318,7 @@ document.addEventListener(
|
||||
#downloads-button,
|
||||
#fxa-toolbar-menu-button,
|
||||
- #unified-extensions-button,
|
||||
#unified-extensions-button,
|
||||
+ #zen-site-data-icon-button,
|
||||
#library-button
|
||||
`);
|
||||
if (!element) {
|
||||
@@ -394,7 +396,7 @@ document.addEventListener(
|
||||
gSync.toggleAccountPanel(element, event);
|
||||
break;
|
||||
|
||||
- case "unified-extensions-button":
|
||||
+ case "zen-site-data-icon-button":
|
||||
@@ -398,6 +401,16 @@ document.addEventListener(
|
||||
gUnifiedExtensions.togglePanel(event);
|
||||
break;
|
||||
|
||||
+ case "zen-site-data-icon-button":
|
||||
+ gUnifiedExtensions.togglePanel(
|
||||
+ event,
|
||||
+ null,
|
||||
+ window.gZenSiteDataPanel.unifiedPanel,
|
||||
+ window.gZenSiteDataPanel.unifiedPanelView,
|
||||
+ window.gZenSiteDataPanel.anchor,
|
||||
+ );
|
||||
+ break;
|
||||
+
|
||||
case "library-button":
|
||||
PanelUI.showSubView("appMenu-libraryView", element, event);
|
||||
break;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
command="Browser:AddBookmarkAs"
|
||||
flex="1" />
|
||||
</hbox>
|
||||
<vbox class="zen-site-data-section">
|
||||
<vbox id="zen-site-data-section-addons" class="zen-site-data-section">
|
||||
<hbox class="zen-site-data-section-header">
|
||||
<label data-l10n-id="unified-extensions-header-title" flex="1" />
|
||||
<label data-l10n-id="zen-generic-manage" id="zen-site-data-manage-addons" />
|
||||
@@ -72,7 +72,7 @@
|
||||
# for this specific button / id
|
||||
<toolbarbutton id="unified-extensions-manage-extensions"
|
||||
class="subviewbutton panel-subview-footer-button unified-extensions-manage-extensions"
|
||||
data-l10n-id="unified-extensions-manage-extensions"
|
||||
data-l10n-id="unified-extensions-manage-extensions"
|
||||
hidden="true" />
|
||||
</vbox>
|
||||
<vbox class="zen-site-data-section">
|
||||
@@ -85,7 +85,7 @@
|
||||
</vbox>
|
||||
</vbox>
|
||||
<hbox id="zen-site-data-footer">
|
||||
<toolbarbutton id="zen-site-data-security-info"
|
||||
<toolbarbutton id="zen-site-data-security-info"
|
||||
class="subviewbutton zen-interactive-button" />
|
||||
<toolbarbutton id="zen-site-data-actions"
|
||||
class="subviewbutton zen-interactive-button"
|
||||
|
||||
@@ -669,7 +669,9 @@ window.gZenUIManager = {
|
||||
}
|
||||
if (
|
||||
(gZenVerticalTabsManager._hasSetSingleToolbar && gZenVerticalTabsManager._prefsRightSide) ||
|
||||
(panel?.id === 'zen-unified-site-data-panel' && !gZenVerticalTabsManager._hasSetSingleToolbar)
|
||||
(panel?.id === 'zen-unified-site-data-panel' &&
|
||||
!gZenVerticalTabsManager._hasSetSingleToolbar) ||
|
||||
(panel?.id === 'unified-extensions-panel' && gZenVerticalTabsManager._hasSetSingleToolbar)
|
||||
) {
|
||||
block = 'bottomright';
|
||||
inline = 'topright';
|
||||
|
||||
@@ -261,6 +261,7 @@ body > #confetti {
|
||||
}
|
||||
|
||||
#TabsToolbar {
|
||||
gap: var(--zen-toolbox-padding);
|
||||
-moz-window-dragging: unset;
|
||||
}
|
||||
|
||||
@@ -494,20 +495,22 @@ body > #confetti {
|
||||
}
|
||||
|
||||
@media -moz-pref('zen.theme.hide-unified-extensions-button') {
|
||||
#unified-extensions-button {
|
||||
#unified-extensions-button:not([showing]) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
#unified-extensions-button:not([showing]) {
|
||||
display: none !important;
|
||||
@media not -moz-pref('zen.theme.hide-unified-extensions-button') {
|
||||
#zen-site-data-section-addons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#zen-site-data-header {
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
padding: 10px 9px;
|
||||
padding-bottom: 0;
|
||||
padding-bottom: 8px;
|
||||
|
||||
:root[zen-single-toolbar='true']:not([zen-right-side='true']) & {
|
||||
flex-direction: row-reverse;
|
||||
@@ -519,7 +522,7 @@ body > #confetti {
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
color: light-dark(rgba(0, 0, 0, 0.8), rgba(255, 255, 255, 0.8));
|
||||
max-width: 48px;
|
||||
width: 48px;
|
||||
height: 32px;
|
||||
position: relative;
|
||||
|
||||
|
||||
@@ -131,6 +131,9 @@ class nsZenDownloadAnimationElement extends HTMLElement {
|
||||
}
|
||||
|
||||
#areTabsOnRightSide() {
|
||||
const position = Services.prefs.getIntPref('zen.downloads.icon-popup-position', 0);
|
||||
if (position === 1) return false;
|
||||
if (position === 2) return true;
|
||||
return Services.prefs.getBoolPref('zen.tabs.vertical.right-side');
|
||||
}
|
||||
|
||||
|
||||
@@ -439,7 +439,6 @@
|
||||
background: transparent;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
padding-top: var(--zen-element-separation);
|
||||
|
||||
& > toolbarbutton:not(#zen-workspaces-button) {
|
||||
padding: 0 !important;
|
||||
|
||||
@@ -10,5 +10,9 @@
|
||||
"zen.mods.last-update",
|
||||
"zen.view.compact.enable-at-startup",
|
||||
"zen.urlbar.suggestions-learner",
|
||||
"browser.newtabpage.activity-stream.trendingSearch.defaultSearchEngine"
|
||||
"browser.newtabpage.activity-stream.trendingSearch.defaultSearchEngine",
|
||||
|
||||
// From the imported safebrowsing tests
|
||||
"urlclassifier.phishTable",
|
||||
"urlclassifier.malwareTable"
|
||||
]
|
||||
|
||||
30
src/zen/tests/manifest.toml
Normal file
30
src/zen/tests/manifest.toml
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
# 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/.
|
||||
|
||||
[reportbrokensite]
|
||||
source = "browser/components/reportbrokensite/test/browser"
|
||||
is_direct_path = true
|
||||
disable = [
|
||||
"browser_addon_data_sent.js"
|
||||
]
|
||||
|
||||
[reportbrokensite.replace-manifest]
|
||||
"../../../../../" = "../../../../"
|
||||
|
||||
[safebrowsing]
|
||||
source = "browser/components/safebrowsing/content/test"
|
||||
is_direct_path = true
|
||||
|
||||
[shell]
|
||||
source = "browser/components/shell/test"
|
||||
is_direct_path = true
|
||||
disable = [
|
||||
"browser_1119088.js",
|
||||
"browser_setDesktopBackgroundPreview.js",
|
||||
]
|
||||
|
||||
[tooltiptext]
|
||||
source = "toolkit/components/tooltiptext"
|
||||
|
||||
14
src/zen/tests/mochitests/moz.build
Normal file
14
src/zen/tests/mochitests/moz.build
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
# 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/.
|
||||
|
||||
# This file is autogenerated by scripts/import_external_tests.py
|
||||
# Do not edit manually.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
"reportbrokensite/browser.toml",
|
||||
"safebrowsing/browser.toml",
|
||||
"shell/browser.toml",
|
||||
"tooltiptext/browser.toml",
|
||||
]
|
||||
57
src/zen/tests/mochitests/reportbrokensite/browser.toml
Normal file
57
src/zen/tests/mochitests/reportbrokensite/browser.toml
Normal file
@@ -0,0 +1,57 @@
|
||||
[DEFAULT]
|
||||
tags = "report-broken-site"
|
||||
support-files = [
|
||||
"example_report_page.html",
|
||||
"head.js",
|
||||
"sendMoreInfoTestEndpoint.html",
|
||||
]
|
||||
|
||||
["browser_addon_data_sent.js"]
|
||||
disabled="Disabled by import_external_tests.py"
|
||||
support-files = [ "send_more_info.js" ]
|
||||
skip-if = ["os == 'win' && os_version == '11.26100' && processor == 'x86_64' && opt"] # Bug 1955805
|
||||
|
||||
["browser_antitracking_data_sent.js"]
|
||||
support-files = [ "send_more_info.js" ]
|
||||
|
||||
["browser_back_buttons.js"]
|
||||
|
||||
["browser_error_messages.js"]
|
||||
|
||||
["browser_experiment_data_sent.js"]
|
||||
support-files = [ "send_more_info.js" ]
|
||||
|
||||
["browser_keyboard_navigation.js"]
|
||||
skip-if = [
|
||||
"os == 'linux' && os_version == '24.04' && processor == 'x86_64' && tsan", # Bug 1867132
|
||||
"os == 'linux' && os_version == '24.04' && processor == 'x86_64' && asan", # Bug 1867132
|
||||
"os == 'linux' && os_version == '24.04' && processor == 'x86_64' && debug", # Bug 1867132
|
||||
"os == 'win' && os_version == '11.26100' && processor == 'x86_64' && asan", # Bug 1867132
|
||||
]
|
||||
|
||||
["browser_learn_more_link.js"]
|
||||
|
||||
["browser_parent_menuitems.js"]
|
||||
|
||||
["browser_prefers_contrast.js"]
|
||||
|
||||
["browser_reason_dropdown.js"]
|
||||
|
||||
["browser_report_send.js"]
|
||||
support-files = [ "send.js" ]
|
||||
|
||||
["browser_send_more_info.js"]
|
||||
support-files = [
|
||||
"send_more_info.js",
|
||||
"../../../../toolkit/components/gfx/content/videotest.mp4",
|
||||
]
|
||||
|
||||
["browser_tab_key_order.js"]
|
||||
|
||||
["browser_tab_switch_handling.js"]
|
||||
|
||||
["browser_webcompat.com_fallback.js"]
|
||||
support-files = [
|
||||
"send_more_info.js",
|
||||
"../../../../toolkit/components/gfx/content/videotest.mp4",
|
||||
]
|
||||
@@ -0,0 +1,99 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that the right data is sent for
|
||||
* private windows and when ETP blocks content.
|
||||
*/
|
||||
|
||||
/* import-globals-from send.js */
|
||||
/* import-globals-from send_more_info.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { AddonTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/AddonTestUtils.sys.mjs"
|
||||
);
|
||||
AddonTestUtils.initMochitest(this);
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send_more_info.js",
|
||||
this
|
||||
);
|
||||
|
||||
add_common_setup();
|
||||
|
||||
const TEMP_ID = "testtempaddon@tests.mozilla.org";
|
||||
const TEMP_NAME = "Temporary Addon";
|
||||
const TEMP_VERSION = "0.1.0";
|
||||
|
||||
const PERM_ID = "testpermaddon@tests.mozilla.org";
|
||||
const PERM_NAME = "Permanent Addon";
|
||||
const PERM_VERSION = "0.2.0";
|
||||
|
||||
const DISABLED_ID = "testdisabledaddon@tests.mozilla.org";
|
||||
const DISABLED_NAME = "Disabled Addon";
|
||||
const DISABLED_VERSION = "0.3.0";
|
||||
|
||||
const EXPECTED_ADDONS = [
|
||||
{ id: PERM_ID, name: PERM_NAME, temporary: false, version: PERM_VERSION },
|
||||
{ id: TEMP_ID, name: TEMP_NAME, temporary: true, version: TEMP_VERSION },
|
||||
];
|
||||
|
||||
function loadAddon(id, name, version, isTemp = false) {
|
||||
return ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
browser_specific_settings: { gecko: { id } },
|
||||
name,
|
||||
version,
|
||||
},
|
||||
useAddonManager: isTemp ? "temporary" : "permanent",
|
||||
});
|
||||
}
|
||||
|
||||
async function installAddons() {
|
||||
const temp = await loadAddon(TEMP_ID, TEMP_NAME, TEMP_VERSION, true);
|
||||
await temp.startup();
|
||||
|
||||
const perm = await loadAddon(PERM_ID, PERM_NAME, PERM_VERSION);
|
||||
await perm.startup();
|
||||
|
||||
const dis = await loadAddon(DISABLED_ID, DISABLED_NAME, DISABLED_VERSION);
|
||||
await dis.startup();
|
||||
await (await AddonManager.getAddonByID(DISABLED_ID)).disable();
|
||||
|
||||
return async () => {
|
||||
await temp.unload();
|
||||
await perm.unload();
|
||||
await dis.unload();
|
||||
};
|
||||
}
|
||||
|
||||
add_task(async function testSendButton() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonOptional();
|
||||
const addonCleanup = await installAddons();
|
||||
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSend(tab, AppMenu(), {
|
||||
addons: EXPECTED_ADDONS,
|
||||
});
|
||||
|
||||
closeTab(tab);
|
||||
await addonCleanup();
|
||||
});
|
||||
|
||||
add_task(async function testSendingMoreInfo() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureSendMoreInfoEnabled();
|
||||
const addonCleanup = await installAddons();
|
||||
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSendMoreInfo(tab, HelpMenu(), {
|
||||
addons: EXPECTED_ADDONS,
|
||||
});
|
||||
|
||||
closeTab(tab);
|
||||
await addonCleanup();
|
||||
});
|
||||
@@ -0,0 +1,126 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that the right data is sent for
|
||||
* private windows and when ETP blocks content.
|
||||
*/
|
||||
|
||||
/* import-globals-from send.js */
|
||||
/* import-globals-from send_more_info.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send_more_info.js",
|
||||
this
|
||||
);
|
||||
|
||||
add_common_setup();
|
||||
|
||||
add_task(setupStrictETP);
|
||||
|
||||
function getEtpCategory() {
|
||||
return Services.prefs.getStringPref(
|
||||
"browser.contentblocking.category",
|
||||
"standard"
|
||||
);
|
||||
}
|
||||
|
||||
add_task(async function testSendButton() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonOptional();
|
||||
|
||||
const win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
|
||||
const blockedPromise = waitForContentBlockingEvent(3, win);
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL3, win);
|
||||
await blockedPromise;
|
||||
|
||||
await testSend(tab, AppMenu(win), {
|
||||
breakageCategory: "adblocker",
|
||||
description: "another test description",
|
||||
antitracking: {
|
||||
blockList: "strict",
|
||||
blockedOrigins: null,
|
||||
isPrivateBrowsing: true,
|
||||
hasTrackingContentBlocked: true,
|
||||
hasMixedActiveContentBlocked: true,
|
||||
hasMixedDisplayContentBlocked: true,
|
||||
btpHasPurgedSite: false,
|
||||
etpCategory: getEtpCategory(),
|
||||
},
|
||||
frameworks: {
|
||||
fastclick: true,
|
||||
marfeel: true,
|
||||
mobify: true,
|
||||
},
|
||||
});
|
||||
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
|
||||
add_task(async function testSendingMoreInfo() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureSendMoreInfoEnabled();
|
||||
|
||||
const win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
|
||||
const blockedPromise = waitForContentBlockingEvent(3, win);
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL3, win);
|
||||
await blockedPromise;
|
||||
|
||||
await testSendMoreInfo(tab, HelpMenu(win), {
|
||||
antitracking: {
|
||||
blockList: "strict",
|
||||
blockedOrigins: ["https://trackertest.org"],
|
||||
isPrivateBrowsing: true,
|
||||
hasTrackingContentBlocked: true,
|
||||
hasMixedActiveContentBlocked: true,
|
||||
hasMixedDisplayContentBlocked: true,
|
||||
btpHasPurgedSite: false,
|
||||
etpCategory: getEtpCategory(),
|
||||
},
|
||||
frameworks: { fastclick: true, mobify: true, marfeel: true },
|
||||
consoleLog: [
|
||||
{
|
||||
level: "error",
|
||||
log(actual) {
|
||||
// "Blocked loading mixed display content http://example.com/tests/image/test/mochitest/blue.png"
|
||||
return (
|
||||
Array.isArray(actual) &&
|
||||
actual.length == 1 &&
|
||||
actual[0].includes("blue.png")
|
||||
);
|
||||
},
|
||||
pos: "0:1",
|
||||
uri: REPORTABLE_PAGE_URL3,
|
||||
},
|
||||
{
|
||||
level: "error",
|
||||
log(actual) {
|
||||
// "Blocked loading mixed active content http://tracking.example.org/browser/browser/base/content/test/protectionsUI/benignPage.html",
|
||||
return (
|
||||
Array.isArray(actual) &&
|
||||
actual.length == 1 &&
|
||||
actual[0].includes("benignPage.html")
|
||||
);
|
||||
},
|
||||
pos: "0:1",
|
||||
uri: REPORTABLE_PAGE_URL3,
|
||||
},
|
||||
{
|
||||
level: "warn",
|
||||
log(actual) {
|
||||
// "The resource at https://trackertest.org/ was blocked because content blocking is enabled.",
|
||||
return (
|
||||
Array.isArray(actual) &&
|
||||
actual.length == 1 &&
|
||||
actual[0].includes("trackertest.org")
|
||||
);
|
||||
},
|
||||
pos: "0:1",
|
||||
uri: REPORTABLE_PAGE_URL3,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that Report Broken Site popups will be
|
||||
* reset to whichever tab the user is on as they change
|
||||
* between windows and tabs. */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
add_task(async function testBackButtonsAreAdded() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
let rbs = await AppMenu().openReportBrokenSite();
|
||||
rbs.isBackButtonEnabled();
|
||||
await rbs.clickBack();
|
||||
await rbs.close();
|
||||
|
||||
rbs = await HelpMenu().openReportBrokenSite();
|
||||
ok(!rbs.backButton, "Back button is not shown for Help Menu");
|
||||
await rbs.close();
|
||||
|
||||
rbs = await ProtectionsPanel().openReportBrokenSite();
|
||||
rbs.isBackButtonEnabled();
|
||||
await rbs.clickBack();
|
||||
await rbs.close();
|
||||
|
||||
rbs = await HelpMenu().openReportBrokenSite();
|
||||
ok(!rbs.backButton, "Back button is not shown for Help Menu");
|
||||
await rbs.close();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Test that the Report Broken Site errors messages are shown on
|
||||
* the UI if the user enters an invalid URL or clicks the send
|
||||
* button while it is disabled due to not selecting a "reason"
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
add_task(async function test() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonRequired();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
for (const menu of [AppMenu(), ProtectionsPanel(), HelpMenu()]) {
|
||||
const rbs = await menu.openReportBrokenSite();
|
||||
const { sendButton, URLInput } = rbs;
|
||||
|
||||
rbs.isURLInvalidMessageHidden();
|
||||
rbs.isReasonNeededMessageHidden();
|
||||
|
||||
rbs.setURL("");
|
||||
window.document.activeElement.blur();
|
||||
rbs.isURLInvalidMessageShown();
|
||||
rbs.isReasonNeededMessageHidden();
|
||||
|
||||
rbs.setURL("https://asdf");
|
||||
window.document.activeElement.blur();
|
||||
rbs.isURLInvalidMessageHidden();
|
||||
rbs.isReasonNeededMessageHidden();
|
||||
|
||||
rbs.setURL("http:/ /asdf");
|
||||
window.document.activeElement.blur();
|
||||
rbs.isURLInvalidMessageShown();
|
||||
rbs.isReasonNeededMessageHidden();
|
||||
|
||||
rbs.setURL("https://asdf");
|
||||
const selectPromise = BrowserTestUtils.waitForSelectPopupShown(window);
|
||||
EventUtils.synthesizeMouseAtCenter(sendButton, {}, window);
|
||||
await selectPromise;
|
||||
rbs.isURLInvalidMessageHidden();
|
||||
rbs.isReasonNeededMessageShown();
|
||||
await rbs.dismissDropdownPopup();
|
||||
|
||||
rbs.chooseReason("slow");
|
||||
rbs.isURLInvalidMessageHidden();
|
||||
rbs.isReasonNeededMessageHidden();
|
||||
|
||||
rbs.setURL("");
|
||||
rbs.chooseReason("choose");
|
||||
window.ownerGlobal.document.activeElement?.blur();
|
||||
const focusPromise = BrowserTestUtils.waitForEvent(URLInput, "focus");
|
||||
EventUtils.synthesizeMouseAtCenter(sendButton, {}, window);
|
||||
await focusPromise;
|
||||
rbs.isURLInvalidMessageShown();
|
||||
rbs.isReasonNeededMessageShown();
|
||||
|
||||
rbs.clickCancel();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that the right data is sent for
|
||||
* private windows and when ETP blocks content.
|
||||
*/
|
||||
|
||||
/* import-globals-from send.js */
|
||||
/* import-globals-from send_more_info.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send_more_info.js",
|
||||
this
|
||||
);
|
||||
|
||||
const { ExperimentAPI } = ChromeUtils.importESModule(
|
||||
"resource://nimbus/ExperimentAPI.sys.mjs"
|
||||
);
|
||||
const { NimbusTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/NimbusTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
add_common_setup();
|
||||
|
||||
const EXPECTED_EXPERIMENTS_IN_REPORT = [
|
||||
{ slug: "test-experiment", branch: "branch", kind: "nimbusExperiment" },
|
||||
{ slug: "test-experiment-rollout", branch: "branch", kind: "nimbusRollout" },
|
||||
];
|
||||
|
||||
let EXPERIMENT_CLEANUPS;
|
||||
|
||||
add_setup(async function () {
|
||||
await ExperimentAPI.ready();
|
||||
EXPERIMENT_CLEANUPS = [
|
||||
await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{ featureId: "no-feature-firefox-desktop", value: {} },
|
||||
{ slug: "test-experiment", branchSlug: "branch" }
|
||||
),
|
||||
await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{ featureId: "no-feature-firefox-desktop", value: {} },
|
||||
{ slug: "test-experiment-rollout", isRollout: true, branchSlug: "branch" }
|
||||
),
|
||||
async () => {
|
||||
ExperimentAPI.manager.store._deleteForTests("test-experiment-disabled");
|
||||
await NimbusTestUtils.flushStore();
|
||||
},
|
||||
];
|
||||
|
||||
await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{ featureId: "no-feature-firefox-desktop", value: {} },
|
||||
{ slug: "test-experiment-disabled" }
|
||||
);
|
||||
await ExperimentAPI.manager.unenroll("test-experiment-disabled");
|
||||
});
|
||||
|
||||
add_task(async function testSendButton() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonOptional();
|
||||
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSend(tab, AppMenu(), {
|
||||
experiments: EXPECTED_EXPERIMENTS_IN_REPORT,
|
||||
});
|
||||
|
||||
closeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function testSendingMoreInfo() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureSendMoreInfoEnabled();
|
||||
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSendMoreInfo(tab, HelpMenu(), {
|
||||
experiments: EXPECTED_EXPERIMENTS_IN_REPORT,
|
||||
});
|
||||
|
||||
closeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function teardown() {
|
||||
for (const cleanup of EXPERIMENT_CLEANUPS) {
|
||||
await cleanup();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,107 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that sending or canceling reports with
|
||||
* the Send and Cancel buttons work (as well as the Okay button)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
async function testPressingKey(key, tabToMatch, makePromise, followUp) {
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
for (const menu of [AppMenu(), ProtectionsPanel(), HelpMenu()]) {
|
||||
info(
|
||||
`Opening RBS to test pressing ${key} for ${tabToMatch} on ${menu.menuDescription}`
|
||||
);
|
||||
const rbs = await menu.openReportBrokenSite();
|
||||
const promise = makePromise(rbs);
|
||||
if (tabToMatch) {
|
||||
if (await tabTo(tabToMatch)) {
|
||||
await pressKeyAndAwait(promise, key);
|
||||
followUp && (await followUp(rbs));
|
||||
await rbs.close();
|
||||
ok(true, `was able to activate ${tabToMatch} with keyboard`);
|
||||
} else {
|
||||
await rbs.close();
|
||||
ok(false, `could not tab to ${tabToMatch}`);
|
||||
}
|
||||
} else {
|
||||
await pressKeyAndAwait(promise, key);
|
||||
followUp && (await followUp(rbs));
|
||||
await rbs.close();
|
||||
ok(true, `was able to use keyboard`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function testSendMoreInfo() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureSendMoreInfoEnabled();
|
||||
await testPressingKey(
|
||||
"KEY_Enter",
|
||||
"#report-broken-site-popup-send-more-info-link",
|
||||
rbs => rbs.waitForSendMoreInfoTab(),
|
||||
() => gBrowser.removeCurrentTab()
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function testCancel() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
await testPressingKey(
|
||||
"KEY_Enter",
|
||||
"#report-broken-site-popup-cancel-button",
|
||||
rbs => BrowserTestUtils.waitForEvent(rbs.mainView, "ViewHiding")
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function testSendAndOkay() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
await testPressingKey(
|
||||
"KEY_Enter",
|
||||
"#report-broken-site-popup-send-button",
|
||||
rbs => rbs.awaitReportSentViewOpened(),
|
||||
async rbs => {
|
||||
await tabTo("#report-broken-site-popup-okay-button");
|
||||
const promise = BrowserTestUtils.waitForEvent(rbs.sentView, "ViewHiding");
|
||||
await pressKeyAndAwait(promise, "KEY_Enter");
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function testESCOnMain() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
await testPressingKey("KEY_Escape", undefined, rbs =>
|
||||
BrowserTestUtils.waitForEvent(rbs.mainView, "ViewHiding")
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function testESCOnSent() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
await testPressingKey(
|
||||
"KEY_Enter",
|
||||
"#report-broken-site-popup-send-button",
|
||||
rbs => rbs.awaitReportSentViewOpened(),
|
||||
async rbs => {
|
||||
const promise = BrowserTestUtils.waitForEvent(rbs.sentView, "ViewHiding");
|
||||
await pressKeyAndAwait(promise, "KEY_Escape");
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function testBackButtons() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
for (const menu of [AppMenu(), ProtectionsPanel()]) {
|
||||
await menu.openReportBrokenSite();
|
||||
await tabTo("#report-broken-site-popup-mainView .subviewbutton-back");
|
||||
const promise = BrowserTestUtils.waitForEvent(menu.popup, "ViewShown");
|
||||
await pressKeyAndAwait(promise, "KEY_Enter");
|
||||
menu.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that the reason dropdown is shown or hidden
|
||||
* based on its pref, and that its optional and required modes affect
|
||||
* the Send button and report appropriately.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
async function ensureLearnMoreLinkWorks(menu) {
|
||||
const rbs = await menu.openReportBrokenSite();
|
||||
const { win, mainView, learnMoreLink } = rbs;
|
||||
ok(learnMoreLink, "Found a learn more link");
|
||||
|
||||
const promises = [
|
||||
BrowserTestUtils.waitForEvent(mainView, "ViewHiding"),
|
||||
BrowserTestUtils.waitForNewTab(win.gBrowser, LEARN_MORE_TEST_URL),
|
||||
];
|
||||
EventUtils.synthesizeMouseAtCenter(learnMoreLink, {}, win);
|
||||
const results = await Promise.all(promises);
|
||||
gBrowser.removeTab(results[1]);
|
||||
}
|
||||
|
||||
add_task(async function testLearnMoreLink() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await ensureLearnMoreLinkWorks(AppMenu());
|
||||
await ensureLearnMoreLinkWorks(HelpMenu());
|
||||
await ensureLearnMoreLinkWorks(ProtectionsPanel());
|
||||
});
|
||||
const telemetry = Glean.webcompatreporting.learnMore.testGetValue();
|
||||
is(telemetry.length, 3, "Got telemetry");
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Test that the Report Broken Site menu items are disabled
|
||||
* when the active tab is not on a reportable URL, and is hidden
|
||||
* when the feature is disabled via pref. Also ensure that the
|
||||
* Report Broken Site item that is automatically generated in
|
||||
* the app menu's help sub-menu is hidden.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
add_task(async function testMenus() {
|
||||
ensureReportBrokenSitePreffedOff();
|
||||
|
||||
const appMenu = AppMenu();
|
||||
const menus = [appMenu, ProtectionsPanel(), HelpMenu()];
|
||||
|
||||
async function forceMenuItemStateUpdate() {
|
||||
ReportBrokenSite.enableOrDisableMenuitems(window);
|
||||
|
||||
// the hidden/disabled state of all of the menuitems may not update until one
|
||||
// is rendered; then the related <command>'s state is propagated to them all.
|
||||
await appMenu.open();
|
||||
await appMenu.close();
|
||||
}
|
||||
|
||||
await BrowserTestUtils.withNewTab("about:blank", async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemHidden(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option hidden on invalid page when preffed off`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemHidden(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option hidden on valid page when preffed off`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
await BrowserTestUtils.withNewTab("about:blank", async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemDisabled(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option disabled on invalid page when preffed on`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemEnabled(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option enabled on valid page when preffed on`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ensureReportBrokenSitePreffedOff();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemHidden(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option hidden again when pref toggled back off`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReportBrokenSiteDisabledByPolicy();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await forceMenuItemStateUpdate();
|
||||
for (const { menuDescription, reportBrokenSite } of menus) {
|
||||
isMenuItemHidden(
|
||||
reportBrokenSite,
|
||||
`${menuDescription} option hidden when disabled by DisableFeedbackCommands enterprise policy`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Test that the background color of the "report sent"
|
||||
* view is not green in non-default contrast modes.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
const HIGH_CONTRAST_MODE_OFF = [[PREFS.USE_ACCESSIBILITY_THEME, 0]];
|
||||
|
||||
const HIGH_CONTRAST_MODE_ON = [[PREFS.USE_ACCESSIBILITY_THEME, 1]];
|
||||
|
||||
add_task(async function testReportSentViewBGColor() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonDisabled();
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
REPORTABLE_PAGE_URL,
|
||||
async function (browser) {
|
||||
const { defaultView } = browser.ownerGlobal.document;
|
||||
|
||||
const menu = AppMenu();
|
||||
|
||||
await SpecialPowers.pushPrefEnv({ set: HIGH_CONTRAST_MODE_OFF });
|
||||
const rbs = await menu.openReportBrokenSite();
|
||||
const { mainView, sentView } = rbs;
|
||||
mainView.style.backgroundColor = "var(--background-color-success)";
|
||||
const expectedReportSentBGColor =
|
||||
defaultView.getComputedStyle(mainView).backgroundColor;
|
||||
mainView.style.backgroundColor = "";
|
||||
const expectedPrefersReducedBGColor =
|
||||
defaultView.getComputedStyle(mainView).backgroundColor;
|
||||
|
||||
await rbs.clickSend();
|
||||
is(
|
||||
defaultView.getComputedStyle(sentView).backgroundColor,
|
||||
expectedReportSentBGColor,
|
||||
"Using green bgcolor when not prefers-contrast"
|
||||
);
|
||||
await rbs.clickOkay();
|
||||
|
||||
await SpecialPowers.pushPrefEnv({ set: HIGH_CONTRAST_MODE_ON });
|
||||
await menu.openReportBrokenSite();
|
||||
await rbs.clickSend();
|
||||
is(
|
||||
defaultView.getComputedStyle(sentView).backgroundColor,
|
||||
expectedPrefersReducedBGColor,
|
||||
"Using default bgcolor when prefers-contrast"
|
||||
);
|
||||
await rbs.clickOkay();
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,156 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that the reason dropdown is shown or hidden
|
||||
* based on its pref, and that its optional and required modes affect
|
||||
* the Send button and report appropriately.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
async function clickSendAndCheckPing(rbs, expectedReason = null) {
|
||||
await GleanPings.brokenSiteReport.testSubmission(
|
||||
() =>
|
||||
Assert.equal(
|
||||
Glean.brokenSiteReport.breakageCategory.testGetValue(),
|
||||
expectedReason
|
||||
),
|
||||
() => rbs.clickSend()
|
||||
);
|
||||
}
|
||||
|
||||
add_task(async function testReasonDropdown() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
ensureReasonDisabled();
|
||||
|
||||
let rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isReasonHidden();
|
||||
await rbs.isSendButtonEnabled();
|
||||
await clickSendAndCheckPing(rbs);
|
||||
await rbs.clickOkay();
|
||||
|
||||
ensureReasonOptional();
|
||||
rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isReasonOptional();
|
||||
await rbs.isSendButtonEnabled();
|
||||
await clickSendAndCheckPing(rbs);
|
||||
await rbs.clickOkay();
|
||||
|
||||
rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isReasonOptional();
|
||||
rbs.chooseReason("slow");
|
||||
await rbs.isSendButtonEnabled();
|
||||
await clickSendAndCheckPing(rbs, "slow");
|
||||
await rbs.clickOkay();
|
||||
|
||||
ensureReasonRequired();
|
||||
rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isReasonRequired();
|
||||
await rbs.isSendButtonEnabled();
|
||||
const selectPromise = BrowserTestUtils.waitForSelectPopupShown(window);
|
||||
EventUtils.synthesizeMouseAtCenter(rbs.sendButton, {}, window);
|
||||
await selectPromise;
|
||||
rbs.chooseReason("media");
|
||||
await rbs.dismissDropdownPopup();
|
||||
await rbs.isSendButtonEnabled();
|
||||
await clickSendAndCheckPing(rbs, "media");
|
||||
await rbs.clickOkay();
|
||||
});
|
||||
});
|
||||
|
||||
async function getListItems(rbs) {
|
||||
const items = Array.from(rbs.reasonInput.querySelectorAll("option")).map(i =>
|
||||
i.id.replace("report-broken-site-popup-reason-", "")
|
||||
);
|
||||
Assert.equal(items[0], "choose", "First option is always 'choose'");
|
||||
return items.join(",");
|
||||
}
|
||||
|
||||
add_task(async function testReasonDropdownRandomized() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonOptional();
|
||||
|
||||
const USER_ID_PREF = "app.normandy.user_id";
|
||||
const RANDOMIZE_PREF = "ui.new-webcompat-reporter.reason-dropdown.randomized";
|
||||
|
||||
const origNormandyUserID = Services.prefs.getCharPref(
|
||||
USER_ID_PREF,
|
||||
undefined
|
||||
);
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
// confirm that the default order is initially used
|
||||
Services.prefs.setBoolPref(RANDOMIZE_PREF, false);
|
||||
const rbs = await AppMenu().openReportBrokenSite();
|
||||
const defaultOrder = [
|
||||
"choose",
|
||||
"checkout",
|
||||
"load",
|
||||
"slow",
|
||||
"media",
|
||||
"content",
|
||||
"account",
|
||||
"adblocker",
|
||||
"notsupported",
|
||||
"other",
|
||||
];
|
||||
Assert.deepEqual(
|
||||
await getListItems(rbs),
|
||||
defaultOrder,
|
||||
"non-random order is correct"
|
||||
);
|
||||
|
||||
// confirm that a random order happens per user
|
||||
let randomOrder;
|
||||
let isRandomized = false;
|
||||
Services.prefs.setBoolPref(RANDOMIZE_PREF, true);
|
||||
|
||||
// This becomes ClientEnvironment.randomizationId, which we can set to
|
||||
// any value which results in a different order from the default ordering.
|
||||
Services.prefs.setCharPref("app.normandy.user_id", "dummy");
|
||||
|
||||
// clicking cancel triggers a reset, which is when the randomization
|
||||
// logic is called. so we must click cancel after pref-changes here.
|
||||
rbs.clickCancel();
|
||||
await AppMenu().openReportBrokenSite();
|
||||
randomOrder = await getListItems(rbs);
|
||||
Assert.notEqual(
|
||||
randomOrder,
|
||||
defaultOrder,
|
||||
"options are randomized with pref on"
|
||||
);
|
||||
|
||||
// confirm that the order doesn't change per user
|
||||
isRandomized = false;
|
||||
for (let attempt = 0; attempt < 5; ++attempt) {
|
||||
rbs.clickCancel();
|
||||
await AppMenu().openReportBrokenSite();
|
||||
const order = await getListItems(rbs);
|
||||
|
||||
if (order != randomOrder) {
|
||||
isRandomized = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert.ok(!isRandomized, "options keep the same order per user");
|
||||
|
||||
// confirm that the order reverts to the default if pref flipped to false
|
||||
Services.prefs.setBoolPref(RANDOMIZE_PREF, false);
|
||||
rbs.clickCancel();
|
||||
await AppMenu().openReportBrokenSite();
|
||||
Assert.deepEqual(
|
||||
defaultOrder,
|
||||
await getListItems(rbs),
|
||||
"reverts to non-random order correctly"
|
||||
);
|
||||
rbs.clickCancel();
|
||||
});
|
||||
|
||||
Services.prefs.setCharPref(USER_ID_PREF, origNormandyUserID);
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that sending or canceling reports with
|
||||
* the Send and Cancel buttons work (as well as the Okay button)
|
||||
*/
|
||||
|
||||
/* import-globals-from send.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send.js",
|
||||
this
|
||||
);
|
||||
|
||||
add_common_setup();
|
||||
|
||||
requestLongerTimeout(10);
|
||||
|
||||
async function testCancel(menu, url, description) {
|
||||
let rbs = await menu.openAndPrefillReportBrokenSite(url, description);
|
||||
await rbs.clickCancel();
|
||||
ok(!rbs.opened, "clicking Cancel closes Report Broken Site");
|
||||
|
||||
// re-opening the panel, the url and description should be reset
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
}
|
||||
|
||||
add_task(async function testSendButton() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureReasonOptional();
|
||||
|
||||
const tab1 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSend(tab1, AppMenu());
|
||||
|
||||
const tab2 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSend(tab2, ProtectionsPanel(), {
|
||||
url: "https://test.org/test/#fake",
|
||||
breakageCategory: "media",
|
||||
description: "test description",
|
||||
});
|
||||
|
||||
closeTab(tab1);
|
||||
closeTab(tab2);
|
||||
});
|
||||
|
||||
add_task(async function testCancelButton() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
const tab1 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testCancel(AppMenu());
|
||||
await testCancel(ProtectionsPanel());
|
||||
await testCancel(HelpMenu());
|
||||
|
||||
const tab2 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testCancel(AppMenu());
|
||||
await testCancel(ProtectionsPanel());
|
||||
await testCancel(HelpMenu());
|
||||
|
||||
const win2 = await BrowserTestUtils.openNewBrowserWindow();
|
||||
const tab3 = await openTab(REPORTABLE_PAGE_URL2, win2);
|
||||
|
||||
await testCancel(AppMenu(win2));
|
||||
await testCancel(ProtectionsPanel(win2));
|
||||
await testCancel(HelpMenu(win2));
|
||||
|
||||
closeTab(tab3);
|
||||
await BrowserTestUtils.closeWindow(win2);
|
||||
|
||||
closeTab(tab1);
|
||||
closeTab(tab2);
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests that the send more info link appears only when its pref
|
||||
* is set to true, and that when clicked it will open a tab to
|
||||
* the webcompat.com endpoint and send the right data.
|
||||
*/
|
||||
|
||||
/* import-globals-from send_more_info.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
const VIDEO_URL = `${BASE_URL}/videotest.mp4`;
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send_more_info.js",
|
||||
this
|
||||
);
|
||||
|
||||
add_common_setup();
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
add_task(async function testSendMoreInfoPref() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await changeTab(gBrowser.selectedTab, REPORTABLE_PAGE_URL);
|
||||
|
||||
ensureSendMoreInfoDisabled();
|
||||
let rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isSendMoreInfoHidden();
|
||||
await rbs.close();
|
||||
|
||||
ensureSendMoreInfoEnabled();
|
||||
rbs = await AppMenu().openReportBrokenSite();
|
||||
await rbs.isSendMoreInfoShown();
|
||||
await rbs.close();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function testSendingMoreInfo() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureSendMoreInfoEnabled();
|
||||
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testSendMoreInfo(tab, AppMenu());
|
||||
|
||||
await changeTab(tab, REPORTABLE_PAGE_URL2);
|
||||
|
||||
await testSendMoreInfo(tab, ProtectionsPanel(), {
|
||||
url: "https://override.com",
|
||||
description: "another",
|
||||
expectNoTabDetails: true,
|
||||
});
|
||||
|
||||
// also load a video to ensure system codec
|
||||
// information is loaded and properly sent
|
||||
const tab2 = await openTab(VIDEO_URL);
|
||||
await testSendMoreInfo(tab2, HelpMenu());
|
||||
closeTab(tab2);
|
||||
|
||||
closeTab(tab);
|
||||
});
|
||||
@@ -0,0 +1,134 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests of the expected tab key element focus order */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
async function ensureTabOrder(order, win = window) {
|
||||
const config = { window: win };
|
||||
for (let matches of order) {
|
||||
// We need to tab through all elements in each match array in any order
|
||||
if (!Array.isArray(matches)) {
|
||||
matches = [matches];
|
||||
}
|
||||
let matchesLeft = matches.length;
|
||||
while (matchesLeft--) {
|
||||
const target = await pressKeyAndGetFocus("VK_TAB", config);
|
||||
let foundMatch = false;
|
||||
for (const [i, selector] of matches.entries()) {
|
||||
foundMatch = selector && target.matches(selector);
|
||||
if (foundMatch) {
|
||||
matches[i] = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
ok(
|
||||
foundMatch,
|
||||
`Expected [${matches}] next, got id=${target.id}, class=${target.className}, ${target}`
|
||||
);
|
||||
if (!foundMatch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function ensureExpectedTabOrder(
|
||||
expectBackButton,
|
||||
expectReason,
|
||||
expectSendMoreInfo
|
||||
) {
|
||||
const { activeElement } = window.document;
|
||||
is(
|
||||
activeElement?.id,
|
||||
"report-broken-site-popup-url",
|
||||
"URL is already focused"
|
||||
);
|
||||
const order = [];
|
||||
if (expectReason) {
|
||||
order.push("#report-broken-site-popup-reason");
|
||||
}
|
||||
order.push("#report-broken-site-popup-description");
|
||||
order.push("#report-broken-site-popup-blocked-trackers-checkbox");
|
||||
if (expectSendMoreInfo) {
|
||||
order.push("#report-broken-site-popup-send-more-info-link");
|
||||
}
|
||||
// moz-button-groups swap the order of buttons to follow
|
||||
// platform conventions, so the order of send/cancel will vary.
|
||||
order.push([
|
||||
"#report-broken-site-popup-cancel-button",
|
||||
"#report-broken-site-popup-send-button",
|
||||
]);
|
||||
if (expectBackButton) {
|
||||
order.push(".subviewbutton-back");
|
||||
}
|
||||
order.push("#report-broken-site-popup-learn-more-link");
|
||||
order.push("#report-broken-site-popup-url"); // check that we've cycled back
|
||||
return ensureTabOrder(order);
|
||||
}
|
||||
|
||||
async function testTabOrder(menu) {
|
||||
ensureReasonDisabled();
|
||||
ensureSendMoreInfoDisabled();
|
||||
|
||||
const { showsBackButton } = menu;
|
||||
|
||||
let rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, false, false);
|
||||
await rbs.close();
|
||||
|
||||
ensureSendMoreInfoEnabled();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, false, true);
|
||||
await rbs.close();
|
||||
|
||||
ensureReasonOptional();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, true, true);
|
||||
await rbs.close();
|
||||
|
||||
ensureReasonRequired();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, true, true);
|
||||
await rbs.close();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
rbs.chooseReason("slow");
|
||||
await ensureExpectedTabOrder(showsBackButton, true, true);
|
||||
await rbs.clickCancel();
|
||||
|
||||
ensureSendMoreInfoDisabled();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, true, false);
|
||||
await rbs.close();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
rbs.chooseReason("slow");
|
||||
await ensureExpectedTabOrder(showsBackButton, true, false);
|
||||
await rbs.clickCancel();
|
||||
|
||||
ensureReasonOptional();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, true, false);
|
||||
await rbs.close();
|
||||
|
||||
ensureReasonDisabled();
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
await ensureExpectedTabOrder(showsBackButton, false, false);
|
||||
await rbs.close();
|
||||
}
|
||||
|
||||
add_task(async function testTabOrdering() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
ensureSendMoreInfoEnabled();
|
||||
|
||||
await BrowserTestUtils.withNewTab(REPORTABLE_PAGE_URL, async function () {
|
||||
await testTabOrder(AppMenu());
|
||||
await testTabOrder(ProtectionsPanel());
|
||||
await testTabOrder(HelpMenu());
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests to ensure that Report Broken Site popups will be
|
||||
* reset to whichever tab the user is on as they change
|
||||
* between windows and tabs. */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_common_setup();
|
||||
|
||||
add_task(async function testResetsProperlyOnTabSwitch() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
const badTab = await openTab("about:blank");
|
||||
const goodTab1 = await openTab(REPORTABLE_PAGE_URL);
|
||||
const goodTab2 = await openTab(REPORTABLE_PAGE_URL2);
|
||||
|
||||
const appMenu = AppMenu();
|
||||
const protPanel = ProtectionsPanel();
|
||||
|
||||
let rbs = await appMenu.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
|
||||
gBrowser.selectedTab = goodTab1;
|
||||
|
||||
rbs = await protPanel.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
|
||||
gBrowser.selectedTab = badTab;
|
||||
await appMenu.open();
|
||||
appMenu.isReportBrokenSiteDisabled();
|
||||
await appMenu.close();
|
||||
|
||||
gBrowser.selectedTab = goodTab1;
|
||||
rbs = await protPanel.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
|
||||
closeTab(badTab);
|
||||
closeTab(goodTab1);
|
||||
closeTab(goodTab2);
|
||||
});
|
||||
|
||||
add_task(async function testResetsProperlyOnWindowSwitch() {
|
||||
ensureReportBrokenSitePreffedOn();
|
||||
|
||||
const tab1 = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
const win2 = await BrowserTestUtils.openNewBrowserWindow();
|
||||
const tab2 = await openTab(REPORTABLE_PAGE_URL2, win2);
|
||||
|
||||
const appMenu1 = AppMenu();
|
||||
const appMenu2 = ProtectionsPanel(win2);
|
||||
|
||||
let rbs2 = await appMenu2.openReportBrokenSite();
|
||||
rbs2.isMainViewResetToCurrentTab();
|
||||
rbs2.close();
|
||||
|
||||
// flip back to tab1's window and ensure its URL pops up instead of tab2's URL
|
||||
await switchToWindow(window);
|
||||
isSelectedTab(window, tab1); // sanity check
|
||||
|
||||
let rbs1 = await appMenu1.openReportBrokenSite();
|
||||
rbs1.isMainViewResetToCurrentTab();
|
||||
rbs1.close();
|
||||
|
||||
// likewise flip back to tab2's window and ensure its URL pops up instead of tab1's URL
|
||||
await switchToWindow(win2);
|
||||
isSelectedTab(win2, tab2); // sanity check
|
||||
|
||||
rbs2 = await appMenu2.openReportBrokenSite();
|
||||
rbs2.isMainViewResetToCurrentTab();
|
||||
rbs2.close();
|
||||
|
||||
closeTab(tab1);
|
||||
closeTab(tab2);
|
||||
await BrowserTestUtils.closeWindow(win2);
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests that when Report Broken Site is disabled, it will
|
||||
* send the user to webcompat.com when clicked and it the
|
||||
* relevant tab's report data.
|
||||
*/
|
||||
|
||||
/* import-globals-from send_more_info.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send_more_info.js",
|
||||
this
|
||||
);
|
||||
|
||||
add_common_setup();
|
||||
|
||||
const VIDEO_URL = `${BASE_URL}/videotest.mp4`;
|
||||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["test.wait300msAfterTabSwitch", true]],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function testWebcompatComFallbacks() {
|
||||
ensureReportBrokenSitePreffedOff();
|
||||
|
||||
const tab = await openTab(REPORTABLE_PAGE_URL);
|
||||
|
||||
await testWebcompatComFallback(tab, AppMenu());
|
||||
|
||||
await changeTab(tab, REPORTABLE_PAGE_URL2);
|
||||
await testWebcompatComFallback(tab, ProtectionsPanel());
|
||||
|
||||
// also load a video to ensure system codec
|
||||
// information is loaded and properly sent
|
||||
const tab2 = await openTab(VIDEO_URL);
|
||||
await testWebcompatComFallback(tab2, HelpMenu());
|
||||
closeTab(tab2);
|
||||
|
||||
closeTab(tab);
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!-- 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/. -->
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
<script>
|
||||
window.marfeel = 1;
|
||||
window.Mobify = { Tag: 1 };
|
||||
window.FastClick = 1;
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- blocked tracking content -->
|
||||
<iframe src="https://trackertest.org/"></iframe>
|
||||
<!-- mixed active content -->
|
||||
<iframe src="http://tracking.example.org/browser/browser/base/content/test/protectionsUI/benignPage.html"></iframe>
|
||||
<!-- mixed display content -->
|
||||
<img src="http://example.com/tests/image/test/mochitest/blue.png"></img>
|
||||
</body>
|
||||
</html>
|
||||
918
src/zen/tests/mochitests/reportbrokensite/head.js
Normal file
918
src/zen/tests/mochitests/reportbrokensite/head.js
Normal file
@@ -0,0 +1,918 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const { CustomizableUITestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/CustomizableUITestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
const { EnterprisePolicyTesting, PoliciesPrefTracker } =
|
||||
ChromeUtils.importESModule(
|
||||
"resource://testing-common/EnterprisePolicyTesting.sys.mjs"
|
||||
);
|
||||
|
||||
const { UrlClassifierTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/UrlClassifierTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
const { ReportBrokenSite } = ChromeUtils.importESModule(
|
||||
"moz-src:///browser/components/reportbrokensite/ReportBrokenSite.sys.mjs"
|
||||
);
|
||||
|
||||
const BASE_URL =
|
||||
"https://example.com/browser/browser/components/reportbrokensite/test/browser/";
|
||||
|
||||
const REPORTABLE_PAGE_URL = "https://example.com";
|
||||
|
||||
const REPORTABLE_PAGE_URL2 = REPORTABLE_PAGE_URL.replace(".com", ".org");
|
||||
|
||||
const REPORTABLE_PAGE_URL3 = `${BASE_URL}example_report_page.html`;
|
||||
|
||||
const SUMO_BASE_URL = Services.urlFormatter.formatURLPref(
|
||||
"app.support.baseURL"
|
||||
);
|
||||
const LEARN_MORE_TEST_URL = `${SUMO_BASE_URL}report-broken-site`;
|
||||
|
||||
const NEW_REPORT_ENDPOINT_TEST_URL = `${BASE_URL}sendMoreInfoTestEndpoint.html`;
|
||||
|
||||
const PREFS = {
|
||||
DATAREPORTING_ENABLED: "datareporting.healthreport.uploadEnabled",
|
||||
REPORTER_ENABLED: "ui.new-webcompat-reporter.enabled",
|
||||
REASON: "ui.new-webcompat-reporter.reason-dropdown",
|
||||
SEND_MORE_INFO: "ui.new-webcompat-reporter.send-more-info-link",
|
||||
NEW_REPORT_ENDPOINT: "ui.new-webcompat-reporter.new-report-endpoint",
|
||||
TOUCH_EVENTS: "dom.w3c_touch_events.enabled",
|
||||
USE_ACCESSIBILITY_THEME: "ui.useAccessibilityTheme",
|
||||
};
|
||||
|
||||
function add_common_setup() {
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[PREFS.NEW_REPORT_ENDPOINT, NEW_REPORT_ENDPOINT_TEST_URL],
|
||||
|
||||
// set touch events to auto-detect, as the pref gets set to 1 somewhere
|
||||
// while tests are running, making hasTouchScreen checks unreliable.
|
||||
[PREFS.TOUCH_EVENTS, 2],
|
||||
],
|
||||
});
|
||||
registerCleanupFunction(function () {
|
||||
for (const prefName of Object.values(PREFS)) {
|
||||
Services.prefs.clearUserPref(prefName);
|
||||
}
|
||||
Services.telemetry.clearEvents();
|
||||
Services.fog.testResetFOG();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function areObjectsEqual(actual, expected, path = "") {
|
||||
if (typeof expected == "function") {
|
||||
try {
|
||||
const passes = expected(actual);
|
||||
if (!passes) {
|
||||
info(`${path} not pass check function: ${actual}`);
|
||||
}
|
||||
return passes;
|
||||
} catch (e) {
|
||||
info(`${path} threw exception:
|
||||
got: ${typeof actual}, ${actual}
|
||||
expected: ${typeof expected}, ${expected}
|
||||
exception: ${e.message}
|
||||
${e.stack}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof actual != typeof expected) {
|
||||
info(`${path} types do not match:
|
||||
got: ${typeof actual}, ${actual}
|
||||
expected: ${typeof expected}, ${expected}`);
|
||||
return false;
|
||||
}
|
||||
if (typeof actual != "object" || actual === null || expected === null) {
|
||||
if (actual !== expected) {
|
||||
info(`${path} does not match
|
||||
got: ${typeof actual}, ${actual}
|
||||
expected: ${typeof expected}, ${expected}`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const prefix = path ? `${path}.` : path;
|
||||
for (const [key, val] of Object.entries(actual)) {
|
||||
if (!(key in expected)) {
|
||||
info(`Extra ${prefix}${key}: ${val}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let result = true;
|
||||
for (const [key, expectedVal] of Object.entries(expected)) {
|
||||
if (key in actual) {
|
||||
if (!areObjectsEqual(actual[key], expectedVal, `${prefix}${key}`)) {
|
||||
result = false;
|
||||
}
|
||||
} else {
|
||||
info(`Missing ${prefix}${key} (${expectedVal})`);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function clickAndAwait(toClick, evt, target) {
|
||||
const menuPromise = BrowserTestUtils.waitForEvent(target, evt);
|
||||
EventUtils.synthesizeMouseAtCenter(toClick, {}, window);
|
||||
return menuPromise;
|
||||
}
|
||||
|
||||
async function openTab(url, win) {
|
||||
const options = {
|
||||
gBrowser:
|
||||
win?.gBrowser ||
|
||||
Services.wm.getMostRecentWindow("navigator:browser").gBrowser,
|
||||
url,
|
||||
};
|
||||
return BrowserTestUtils.openNewForegroundTab(options);
|
||||
}
|
||||
|
||||
async function changeTab(tab, url) {
|
||||
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, url);
|
||||
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
}
|
||||
|
||||
function closeTab(tab) {
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
function switchToWindow(win) {
|
||||
const promises = [
|
||||
BrowserTestUtils.waitForEvent(win, "focus"),
|
||||
BrowserTestUtils.waitForEvent(win, "activate"),
|
||||
];
|
||||
win.focus();
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function isSelectedTab(win, tab) {
|
||||
const selectedTab = win.document.querySelector(".tabbrowser-tab[selected]");
|
||||
is(selectedTab, tab);
|
||||
}
|
||||
|
||||
async function setupPolicyEngineWithJson(json, customSchema) {
|
||||
PoliciesPrefTracker.restoreDefaultValues();
|
||||
if (typeof json != "object") {
|
||||
let filePath = getTestFilePath(json ? json : "non-existing-file.json");
|
||||
return EnterprisePolicyTesting.setupPolicyEngineWithJson(
|
||||
filePath,
|
||||
customSchema
|
||||
);
|
||||
}
|
||||
return EnterprisePolicyTesting.setupPolicyEngineWithJson(json, customSchema);
|
||||
}
|
||||
|
||||
async function ensureReportBrokenSiteDisabledByPolicy() {
|
||||
await setupPolicyEngineWithJson({
|
||||
policies: {
|
||||
DisableFeedbackCommands: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
registerCleanupFunction(async function resetPolicies() {
|
||||
if (Services.policies.status != Ci.nsIEnterprisePolicies.INACTIVE) {
|
||||
await setupPolicyEngineWithJson("");
|
||||
}
|
||||
EnterprisePolicyTesting.resetRunOnceState();
|
||||
PoliciesPrefTracker.restoreDefaultValues();
|
||||
PoliciesPrefTracker.stop();
|
||||
});
|
||||
|
||||
function ensureReportBrokenSitePreffedOn() {
|
||||
Services.prefs.setBoolPref(PREFS.DATAREPORTING_ENABLED, true);
|
||||
Services.prefs.setBoolPref(PREFS.REPORTER_ENABLED, true);
|
||||
ensureReasonDisabled();
|
||||
}
|
||||
|
||||
function ensureReportBrokenSitePreffedOff() {
|
||||
Services.prefs.setBoolPref(PREFS.REPORTER_ENABLED, false);
|
||||
}
|
||||
|
||||
function ensureSendMoreInfoEnabled() {
|
||||
Services.prefs.setBoolPref(PREFS.SEND_MORE_INFO, true);
|
||||
}
|
||||
|
||||
function ensureSendMoreInfoDisabled() {
|
||||
Services.prefs.setBoolPref(PREFS.SEND_MORE_INFO, false);
|
||||
}
|
||||
|
||||
function ensureReasonDisabled() {
|
||||
Services.prefs.setIntPref(PREFS.REASON, 0);
|
||||
}
|
||||
|
||||
function ensureReasonOptional() {
|
||||
Services.prefs.setIntPref(PREFS.REASON, 1);
|
||||
}
|
||||
|
||||
function ensureReasonRequired() {
|
||||
Services.prefs.setIntPref(PREFS.REASON, 2);
|
||||
}
|
||||
|
||||
function isMenuItemEnabled(menuItem, itemDesc) {
|
||||
ok(!menuItem.hidden, `${itemDesc} menu item is shown`);
|
||||
ok(!menuItem.disabled, `${itemDesc} menu item is enabled`);
|
||||
}
|
||||
|
||||
function isMenuItemHidden(menuItem, itemDesc) {
|
||||
ok(
|
||||
!menuItem || menuItem.hidden || !BrowserTestUtils.isVisible(menuItem),
|
||||
`${itemDesc} menu item is hidden`
|
||||
);
|
||||
}
|
||||
|
||||
function isMenuItemDisabled(menuItem, itemDesc) {
|
||||
ok(!menuItem.hidden, `${itemDesc} menu item is shown`);
|
||||
ok(menuItem.disabled, `${itemDesc} menu item is disabled`);
|
||||
}
|
||||
|
||||
function waitForWebcompatComTab(gBrowser) {
|
||||
return BrowserTestUtils.waitForNewTab(gBrowser, NEW_REPORT_ENDPOINT_TEST_URL);
|
||||
}
|
||||
|
||||
class ReportBrokenSiteHelper {
|
||||
sourceMenu = undefined;
|
||||
win = undefined;
|
||||
|
||||
constructor(sourceMenu) {
|
||||
this.sourceMenu = sourceMenu;
|
||||
this.win = sourceMenu.win;
|
||||
}
|
||||
|
||||
getViewNode(id) {
|
||||
return PanelMultiView.getViewNode(this.win.document, id);
|
||||
}
|
||||
|
||||
get mainView() {
|
||||
return this.getViewNode("report-broken-site-popup-mainView");
|
||||
}
|
||||
|
||||
get sentView() {
|
||||
return this.getViewNode("report-broken-site-popup-reportSentView");
|
||||
}
|
||||
|
||||
get openPanel() {
|
||||
return this.mainView?.closest("panel");
|
||||
}
|
||||
|
||||
get opened() {
|
||||
return this.openPanel?.hasAttribute("panelopen");
|
||||
}
|
||||
|
||||
async click(triggerMenuItem) {
|
||||
const window = triggerMenuItem.ownerGlobal;
|
||||
await EventUtils.synthesizeMouseAtCenter(triggerMenuItem, {}, window);
|
||||
}
|
||||
|
||||
async open(triggerMenuItem) {
|
||||
const shownPromise = BrowserTestUtils.waitForEvent(
|
||||
this.mainView,
|
||||
"ViewShown"
|
||||
);
|
||||
const focusPromise = BrowserTestUtils.waitForEvent(this.URLInput, "focus");
|
||||
await this.click(triggerMenuItem);
|
||||
await shownPromise;
|
||||
await focusPromise;
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() => this.URLInput.selectionStart === 0
|
||||
);
|
||||
}
|
||||
|
||||
async #assertClickAndViewChanges(button, view, newView, newFocus) {
|
||||
ok(view.closest("panel").hasAttribute("panelopen"), "Panel is open");
|
||||
ok(BrowserTestUtils.isVisible(button), "Button is visible");
|
||||
ok(!button.disabled, "Button is enabled");
|
||||
const promises = [];
|
||||
if (newView) {
|
||||
if (newView.nodeName == "panel") {
|
||||
promises.push(BrowserTestUtils.waitForEvent(newView, "popupshown"));
|
||||
} else {
|
||||
promises.push(BrowserTestUtils.waitForEvent(newView, "ViewShown"));
|
||||
}
|
||||
} else {
|
||||
promises.push(BrowserTestUtils.waitForEvent(view, "ViewHiding"));
|
||||
}
|
||||
if (newFocus) {
|
||||
promises.push(BrowserTestUtils.waitForEvent(newFocus, "focus"));
|
||||
}
|
||||
EventUtils.synthesizeMouseAtCenter(button, {}, this.win);
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
async awaitReportSentViewOpened() {
|
||||
await Promise.all([
|
||||
BrowserTestUtils.waitForEvent(this.sentView, "ViewShown"),
|
||||
BrowserTestUtils.waitForEvent(this.okayButton, "focus"),
|
||||
]);
|
||||
}
|
||||
|
||||
async clickSend() {
|
||||
await this.#assertClickAndViewChanges(
|
||||
this.sendButton,
|
||||
this.mainView,
|
||||
this.sentView,
|
||||
this.okayButton
|
||||
);
|
||||
}
|
||||
|
||||
waitForSendMoreInfoTab() {
|
||||
return BrowserTestUtils.waitForNewTab(
|
||||
this.win.gBrowser,
|
||||
NEW_REPORT_ENDPOINT_TEST_URL
|
||||
);
|
||||
}
|
||||
|
||||
async clickSendMoreInfo() {
|
||||
const newTabPromise = waitForWebcompatComTab(this.win.gBrowser);
|
||||
EventUtils.synthesizeMouseAtCenter(this.sendMoreInfoLink, {}, this.win);
|
||||
const newTab = await newTabPromise;
|
||||
const receivedData = await SpecialPowers.spawn(
|
||||
newTab.linkedBrowser,
|
||||
[],
|
||||
async function () {
|
||||
await content.wrappedJSObject.messageArrived;
|
||||
return content.wrappedJSObject.message;
|
||||
}
|
||||
);
|
||||
this.win.gBrowser.removeCurrentTab();
|
||||
return receivedData;
|
||||
}
|
||||
|
||||
async clickCancel() {
|
||||
await this.#assertClickAndViewChanges(this.cancelButton, this.mainView);
|
||||
}
|
||||
|
||||
async clickOkay() {
|
||||
await this.#assertClickAndViewChanges(this.okayButton, this.sentView);
|
||||
}
|
||||
|
||||
async clickBack() {
|
||||
await this.#assertClickAndViewChanges(
|
||||
this.backButton,
|
||||
this.sourceMenu.popup
|
||||
);
|
||||
}
|
||||
|
||||
isBackButtonEnabled() {
|
||||
ok(BrowserTestUtils.isVisible(this.backButton), "Back button is visible");
|
||||
ok(!this.backButton.disabled, "Back button is enabled");
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.opened) {
|
||||
this.openPanel?.hidePopup(false);
|
||||
}
|
||||
this.sourceMenu?.close();
|
||||
}
|
||||
|
||||
// UI element getters
|
||||
get URLInput() {
|
||||
return this.getViewNode("report-broken-site-popup-url");
|
||||
}
|
||||
|
||||
get URLInvalidMessage() {
|
||||
return this.getViewNode("report-broken-site-popup-invalid-url-msg");
|
||||
}
|
||||
|
||||
get reasonInput() {
|
||||
return this.getViewNode("report-broken-site-popup-reason");
|
||||
}
|
||||
|
||||
get reasonDropdownPopup() {
|
||||
return this.win.document.getElementById("ContentSelectDropdown").menupopup;
|
||||
}
|
||||
|
||||
get reasonRequiredMessage() {
|
||||
return this.getViewNode("report-broken-site-popup-missing-reason-msg");
|
||||
}
|
||||
|
||||
get reasonLabelRequired() {
|
||||
return this.getViewNode("report-broken-site-popup-reason-label");
|
||||
}
|
||||
|
||||
get reasonLabelOptional() {
|
||||
return this.getViewNode("report-broken-site-popup-reason-optional-label");
|
||||
}
|
||||
|
||||
get descriptionTextarea() {
|
||||
return this.getViewNode("report-broken-site-popup-description");
|
||||
}
|
||||
|
||||
get learnMoreLink() {
|
||||
return this.getViewNode("report-broken-site-popup-learn-more-link");
|
||||
}
|
||||
|
||||
get sendMoreInfoLink() {
|
||||
return this.getViewNode("report-broken-site-popup-send-more-info-link");
|
||||
}
|
||||
|
||||
get backButton() {
|
||||
return this.mainView.querySelector(".subviewbutton-back");
|
||||
}
|
||||
|
||||
get blockedTrackersCheckbox() {
|
||||
return this.getViewNode(
|
||||
"report-broken-site-popup-blocked-trackers-checkbox"
|
||||
);
|
||||
}
|
||||
|
||||
set blockedTrackersCheckbox(checked) {
|
||||
this.blockedTrackersCheckbox.checked = checked;
|
||||
}
|
||||
|
||||
get sendButton() {
|
||||
return this.getViewNode("report-broken-site-popup-send-button");
|
||||
}
|
||||
|
||||
get cancelButton() {
|
||||
return this.getViewNode("report-broken-site-popup-cancel-button");
|
||||
}
|
||||
|
||||
get okayButton() {
|
||||
return this.getViewNode("report-broken-site-popup-okay-button");
|
||||
}
|
||||
|
||||
// Test helpers
|
||||
|
||||
#setInput(input, value) {
|
||||
input.value = value;
|
||||
input.dispatchEvent(
|
||||
new UIEvent("input", { bubbles: true, view: this.win })
|
||||
);
|
||||
}
|
||||
|
||||
setURL(value) {
|
||||
this.#setInput(this.URLInput, value);
|
||||
}
|
||||
|
||||
chooseReason(value) {
|
||||
const item = this.getViewNode(`report-broken-site-popup-reason-${value}`);
|
||||
this.reasonInput.selectedIndex = item.index;
|
||||
}
|
||||
|
||||
dismissDropdownPopup() {
|
||||
const popup = this.reasonDropdownPopup;
|
||||
const menuPromise = BrowserTestUtils.waitForPopupEvent(popup, "hidden");
|
||||
popup.hidePopup();
|
||||
return menuPromise;
|
||||
}
|
||||
|
||||
setDescription(value) {
|
||||
this.#setInput(this.descriptionTextarea, value);
|
||||
}
|
||||
|
||||
isURL(expected) {
|
||||
is(this.URLInput.value, expected);
|
||||
}
|
||||
|
||||
isURLInvalidMessageShown() {
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.URLInvalidMessage),
|
||||
"'Please enter a valid URL' message is shown"
|
||||
);
|
||||
}
|
||||
|
||||
isURLInvalidMessageHidden() {
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.URLInvalidMessage),
|
||||
"'Please enter a valid URL' message is hidden"
|
||||
);
|
||||
}
|
||||
|
||||
isReasonNeededMessageShown() {
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.reasonRequiredMessage),
|
||||
"'Please choose a reason' message is shown"
|
||||
);
|
||||
}
|
||||
|
||||
isReasonNeededMessageHidden() {
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.reasonRequiredMessage),
|
||||
"'Please choose a reason' message is hidden"
|
||||
);
|
||||
}
|
||||
|
||||
isSendButtonEnabled() {
|
||||
ok(BrowserTestUtils.isVisible(this.sendButton), "Send button is visible");
|
||||
ok(!this.sendButton.disabled, "Send button is enabled");
|
||||
}
|
||||
|
||||
isSendButtonDisabled() {
|
||||
ok(BrowserTestUtils.isVisible(this.sendButton), "Send button is visible");
|
||||
ok(this.sendButton.disabled, "Send button is disabled");
|
||||
}
|
||||
|
||||
isSendMoreInfoShown() {
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.sendMoreInfoLink),
|
||||
"send more info is shown"
|
||||
);
|
||||
}
|
||||
|
||||
isSendMoreInfoHidden() {
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.sendMoreInfoLink),
|
||||
"send more info is hidden"
|
||||
);
|
||||
}
|
||||
|
||||
isSendMoreInfoShownOrHiddenAppropriately() {
|
||||
if (Services.prefs.getBoolPref(PREFS.SEND_MORE_INFO)) {
|
||||
this.isSendMoreInfoShown();
|
||||
} else {
|
||||
this.isSendMoreInfoHidden();
|
||||
}
|
||||
}
|
||||
|
||||
isReasonHidden() {
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.reasonInput),
|
||||
"reason drop-down is hidden"
|
||||
);
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.reasonLabelOptional),
|
||||
"optional reason label is hidden"
|
||||
);
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.reasonLabelRequired),
|
||||
"required reason label is hidden"
|
||||
);
|
||||
}
|
||||
|
||||
isReasonRequired() {
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.reasonInput),
|
||||
"reason drop-down is shown"
|
||||
);
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.reasonLabelOptional),
|
||||
"optional reason label is hidden"
|
||||
);
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.reasonLabelRequired),
|
||||
"required reason label is shown"
|
||||
);
|
||||
}
|
||||
|
||||
isReasonOptional() {
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.reasonInput),
|
||||
"reason drop-down is shown"
|
||||
);
|
||||
ok(
|
||||
BrowserTestUtils.isVisible(this.reasonLabelOptional),
|
||||
"optional reason label is shown"
|
||||
);
|
||||
ok(
|
||||
!BrowserTestUtils.isVisible(this.reasonLabelRequired),
|
||||
"required reason label is hidden"
|
||||
);
|
||||
}
|
||||
|
||||
isReasonShownOrHiddenAppropriately() {
|
||||
const pref = Services.prefs.getIntPref(PREFS.REASON);
|
||||
if (pref == 2) {
|
||||
this.isReasonOptional();
|
||||
} else if (pref == 1) {
|
||||
this.isReasonOptional();
|
||||
} else {
|
||||
this.isReasonHidden();
|
||||
}
|
||||
}
|
||||
|
||||
isDescription(expected) {
|
||||
return this.descriptionTextarea.value == expected;
|
||||
}
|
||||
|
||||
isMainViewResetToCurrentTab() {
|
||||
this.isURL(this.win.gBrowser.selectedBrowser.currentURI.spec);
|
||||
this.isDescription("");
|
||||
this.isReasonShownOrHiddenAppropriately();
|
||||
this.isSendMoreInfoShownOrHiddenAppropriately();
|
||||
}
|
||||
}
|
||||
|
||||
class MenuHelper {
|
||||
menuDescription = undefined;
|
||||
|
||||
win = undefined;
|
||||
|
||||
constructor(win = window) {
|
||||
this.win = win;
|
||||
}
|
||||
|
||||
getViewNode(id) {
|
||||
return PanelMultiView.getViewNode(this.win.document, id);
|
||||
}
|
||||
|
||||
get showsBackButton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
get reportBrokenSite() {
|
||||
throw new Error("Should be defined in derived class");
|
||||
}
|
||||
|
||||
get popup() {
|
||||
throw new Error("Should be defined in derived class");
|
||||
}
|
||||
|
||||
get opened() {
|
||||
return this.popup?.hasAttribute("panelopen");
|
||||
}
|
||||
|
||||
async open() {}
|
||||
|
||||
async close() {}
|
||||
|
||||
isReportBrokenSiteDisabled() {
|
||||
return isMenuItemDisabled(this.reportBrokenSite, this.menuDescription);
|
||||
}
|
||||
|
||||
isReportBrokenSiteEnabled() {
|
||||
return isMenuItemEnabled(this.reportBrokenSite, this.menuDescription);
|
||||
}
|
||||
|
||||
isReportBrokenSiteHidden() {
|
||||
return isMenuItemHidden(this.reportBrokenSite, this.menuDescription);
|
||||
}
|
||||
|
||||
async clickReportBrokenSiteAndAwaitWebCompatTabData() {
|
||||
const newTabPromise = waitForWebcompatComTab(this.win.gBrowser);
|
||||
await this.clickReportBrokenSite();
|
||||
const newTab = await newTabPromise;
|
||||
const receivedData = await SpecialPowers.spawn(
|
||||
newTab.linkedBrowser,
|
||||
[],
|
||||
async function () {
|
||||
await content.wrappedJSObject.messageArrived;
|
||||
return content.wrappedJSObject.message;
|
||||
}
|
||||
);
|
||||
|
||||
this.win.gBrowser.removeCurrentTab();
|
||||
return receivedData;
|
||||
}
|
||||
|
||||
async clickReportBrokenSite() {
|
||||
if (!this.opened) {
|
||||
await this.open();
|
||||
}
|
||||
isMenuItemEnabled(this.reportBrokenSite, this.menuDescription);
|
||||
const rbs = new ReportBrokenSiteHelper(this);
|
||||
await rbs.click(this.reportBrokenSite);
|
||||
return rbs;
|
||||
}
|
||||
|
||||
async openReportBrokenSite() {
|
||||
if (!this.opened) {
|
||||
await this.open();
|
||||
}
|
||||
isMenuItemEnabled(this.reportBrokenSite, this.menuDescription);
|
||||
const rbs = new ReportBrokenSiteHelper(this);
|
||||
await rbs.open(this.reportBrokenSite);
|
||||
return rbs;
|
||||
}
|
||||
|
||||
async openAndPrefillReportBrokenSite(url = null, description = "") {
|
||||
let rbs = await this.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
if (url) {
|
||||
rbs.setURL(url);
|
||||
}
|
||||
if (description) {
|
||||
rbs.setDescription(description);
|
||||
}
|
||||
return rbs;
|
||||
}
|
||||
}
|
||||
|
||||
class AppMenuHelper extends MenuHelper {
|
||||
menuDescription = "AppMenu";
|
||||
|
||||
get reportBrokenSite() {
|
||||
return this.getViewNode("appMenu-report-broken-site-button");
|
||||
}
|
||||
|
||||
get popup() {
|
||||
return this.win.document.getElementById("appMenu-popup");
|
||||
}
|
||||
|
||||
async open() {
|
||||
await new CustomizableUITestUtils(this.win).openMainMenu();
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (this.opened) {
|
||||
await new CustomizableUITestUtils(this.win).hideMainMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HelpMenuHelper extends MenuHelper {
|
||||
menuDescription = "Help Menu";
|
||||
|
||||
get showsBackButton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
get reportBrokenSite() {
|
||||
return this.win.document.getElementById("help_reportBrokenSite");
|
||||
}
|
||||
|
||||
get popup() {
|
||||
return this.getViewNode("PanelUI-helpView");
|
||||
}
|
||||
|
||||
get helpMenu() {
|
||||
return this.win.document.getElementById("menu_HelpPopup");
|
||||
}
|
||||
|
||||
async openReportBrokenSite() {
|
||||
// We can't actually open the Help menu properly in testing, so the best
|
||||
// we can do to open its Report Broken Site panel is to force its DOM to be
|
||||
// prepared, and then soft-click the Report Broken Site menuitem to open it.
|
||||
await this.open();
|
||||
const shownPromise = BrowserTestUtils.waitForEvent(
|
||||
this.win,
|
||||
"ViewShown",
|
||||
true,
|
||||
e => e.target.classList.contains("report-broken-site-view")
|
||||
);
|
||||
this.reportBrokenSite.click();
|
||||
await shownPromise;
|
||||
return new ReportBrokenSiteHelper(this);
|
||||
}
|
||||
|
||||
async clickReportBrokenSite() {
|
||||
await this.open();
|
||||
this.reportBrokenSite.click();
|
||||
return new ReportBrokenSiteHelper(this);
|
||||
}
|
||||
|
||||
async open() {
|
||||
const { helpMenu } = this;
|
||||
const promise = BrowserTestUtils.waitForEvent(helpMenu, "popupshown");
|
||||
|
||||
// This event-faking method was copied from browser_title_case_menus.js.
|
||||
// We can't actually open the Help menu in testing, but this lets us
|
||||
// force its DOM to be properly built.
|
||||
helpMenu.dispatchEvent(new MouseEvent("popupshowing", { bubbles: true }));
|
||||
helpMenu.dispatchEvent(new MouseEvent("popupshown", { bubbles: true }));
|
||||
|
||||
await promise;
|
||||
}
|
||||
|
||||
async close() {
|
||||
const { helpMenu } = this;
|
||||
const promise = BrowserTestUtils.waitForPopupEvent(helpMenu, "hidden");
|
||||
|
||||
// (Also copied from browser_title_case_menus.js)
|
||||
// Just for good measure, we'll fire the popuphiding/popuphidden events
|
||||
// after we close the menupopups.
|
||||
helpMenu.dispatchEvent(new MouseEvent("popuphiding", { bubbles: true }));
|
||||
helpMenu.dispatchEvent(new MouseEvent("popuphidden", { bubbles: true }));
|
||||
|
||||
await promise;
|
||||
}
|
||||
}
|
||||
|
||||
class ProtectionsPanelHelper extends MenuHelper {
|
||||
menuDescription = "Protections Panel";
|
||||
|
||||
get reportBrokenSite() {
|
||||
this.win.gProtectionsHandler._initializePopup();
|
||||
return this.getViewNode("protections-popup-report-broken-site-button");
|
||||
}
|
||||
|
||||
get popup() {
|
||||
this.win.gProtectionsHandler._initializePopup();
|
||||
return this.win.document.getElementById("protections-popup");
|
||||
}
|
||||
|
||||
async open() {
|
||||
const promise = BrowserTestUtils.waitForEvent(
|
||||
this.win,
|
||||
"popupshown",
|
||||
true,
|
||||
e => e.target.id == "protections-popup"
|
||||
);
|
||||
this.win.gProtectionsHandler.showProtectionsPopup();
|
||||
await promise;
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (this.opened) {
|
||||
const popup = this.popup;
|
||||
const promise = BrowserTestUtils.waitForPopupEvent(popup, "hidden");
|
||||
PanelMultiView.hidePopup(popup, false);
|
||||
await promise;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function AppMenu(win = window) {
|
||||
return new AppMenuHelper(win);
|
||||
}
|
||||
|
||||
function HelpMenu(win = window) {
|
||||
return new HelpMenuHelper(win);
|
||||
}
|
||||
|
||||
function ProtectionsPanel(win = window) {
|
||||
return new ProtectionsPanelHelper(win);
|
||||
}
|
||||
|
||||
function pressKeyAndAwait(event, key, config = {}) {
|
||||
const win = config.window || window;
|
||||
if (!event.then) {
|
||||
event = BrowserTestUtils.waitForEvent(win, event, config.timeout || 200);
|
||||
}
|
||||
EventUtils.synthesizeKey(key, config, win);
|
||||
return event;
|
||||
}
|
||||
|
||||
async function pressKeyAndGetFocus(key, config = {}) {
|
||||
return (await pressKeyAndAwait("focus", key, config)).target;
|
||||
}
|
||||
|
||||
async function tabTo(match, win = window) {
|
||||
const config = { window: win };
|
||||
const { activeElement } = win.document;
|
||||
if (activeElement?.matches(match)) {
|
||||
return activeElement;
|
||||
}
|
||||
let initial = await pressKeyAndGetFocus("VK_TAB", config);
|
||||
let target = initial;
|
||||
do {
|
||||
if (target.matches(match)) {
|
||||
return target;
|
||||
}
|
||||
target = await pressKeyAndGetFocus("VK_TAB", config);
|
||||
} while (target && target !== initial);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function filterFrameworkDetectorFails(ping, expected) {
|
||||
// the framework detector's frame-script may fail to run in low memory or other
|
||||
// weird corner-cases, so we ignore the results in that case if they don't match.
|
||||
if (!areObjectsEqual(ping.frameworks, expected.frameworks)) {
|
||||
const { fastclick, mobify, marfeel } = ping.frameworks;
|
||||
if (!fastclick && !mobify && !marfeel) {
|
||||
console.info("Ignoring failure to get framework data");
|
||||
expected.frameworks = ping.frameworks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function setupStrictETP() {
|
||||
await UrlClassifierTestUtils.addTestTrackers();
|
||||
registerCleanupFunction(() => {
|
||||
UrlClassifierTestUtils.cleanupTestTrackers();
|
||||
});
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["security.mixed_content.block_active_content", true],
|
||||
["security.mixed_content.block_display_content", true],
|
||||
["security.mixed_content.upgrade_display_content", false],
|
||||
[
|
||||
"urlclassifier.trackingTable",
|
||||
"content-track-digest256,mochitest2-track-simple",
|
||||
],
|
||||
["browser.contentblocking.category", "strict"],
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// copied from browser/base/content/test/protectionsUI/head.js
|
||||
function waitForContentBlockingEvent(numChanges = 1, win = null) {
|
||||
if (!win) {
|
||||
win = window;
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
let n = 0;
|
||||
let listener = {
|
||||
onContentBlockingEvent(webProgress, request, event) {
|
||||
n = n + 1;
|
||||
info(
|
||||
`Received onContentBlockingEvent event: ${event} (${n} of ${numChanges})`
|
||||
);
|
||||
if (n >= numChanges) {
|
||||
win.gBrowser.removeProgressListener(listener);
|
||||
resolve(n);
|
||||
}
|
||||
},
|
||||
};
|
||||
win.gBrowser.addProgressListener(listener);
|
||||
});
|
||||
}
|
||||
355
src/zen/tests/mochitests/reportbrokensite/send.js
Normal file
355
src/zen/tests/mochitests/reportbrokensite/send.js
Normal file
@@ -0,0 +1,355 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Helper methods for testing sending reports with
|
||||
* the Report Broken Site feature.
|
||||
*/
|
||||
|
||||
/* import-globals-from head.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { Troubleshoot } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/Troubleshoot.sys.mjs"
|
||||
);
|
||||
|
||||
function getSysinfoProperty(propertyName, defaultValue) {
|
||||
try {
|
||||
return Services.sysinfo.getProperty(propertyName);
|
||||
} catch (e) {}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
function securityStringToArray(str) {
|
||||
return str ? str.split(";") : null;
|
||||
}
|
||||
|
||||
function getExpectedGraphicsDevices(snapshot) {
|
||||
const { graphics } = snapshot;
|
||||
return [
|
||||
graphics.adapterDeviceID,
|
||||
graphics.adapterVendorID,
|
||||
graphics.adapterDeviceID2,
|
||||
graphics.adapterVendorID2,
|
||||
]
|
||||
.filter(i => i)
|
||||
.sort();
|
||||
}
|
||||
|
||||
function compareGraphicsDevices(expected, rawActual) {
|
||||
const actual = rawActual
|
||||
.map(({ deviceID, vendorID }) => [deviceID, vendorID])
|
||||
.flat()
|
||||
.filter(i => i)
|
||||
.sort();
|
||||
return areObjectsEqual(actual, expected);
|
||||
}
|
||||
|
||||
function getExpectedGraphicsDrivers(snapshot) {
|
||||
const { graphics } = snapshot;
|
||||
const expected = [];
|
||||
for (let i = 1; i < 3; ++i) {
|
||||
const version = graphics[`webgl${i}Version`];
|
||||
if (version && version != "-") {
|
||||
expected.push(graphics[`webgl${i}Renderer`]);
|
||||
expected.push(version);
|
||||
}
|
||||
}
|
||||
return expected.filter(i => i).sort();
|
||||
}
|
||||
|
||||
function compareGraphicsDrivers(expected, rawActual) {
|
||||
const actual = rawActual
|
||||
.map(({ renderer, version }) => [renderer, version])
|
||||
.flat()
|
||||
.filter(i => i)
|
||||
.sort();
|
||||
return areObjectsEqual(actual, expected);
|
||||
}
|
||||
|
||||
function getExpectedGraphicsFeatures(snapshot) {
|
||||
const expected = {};
|
||||
for (let { name, log, status } of snapshot.graphics.featureLog.features) {
|
||||
for (const item of log?.reverse() ?? []) {
|
||||
if (item.failureId && item.status == status) {
|
||||
status = `${status} (${item.message || item.failureId})`;
|
||||
}
|
||||
}
|
||||
expected[name] = status;
|
||||
}
|
||||
return expected;
|
||||
}
|
||||
|
||||
async function getExpectedWebCompatInfo(tab, snapshot, fullAppData = false) {
|
||||
const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
|
||||
|
||||
const { application, graphics, intl, securitySoftware } = snapshot;
|
||||
|
||||
const { fissionAutoStart, memorySizeBytes, updateChannel, userAgent } =
|
||||
application;
|
||||
|
||||
const app = {
|
||||
defaultLocales: intl.localeService.available,
|
||||
defaultUseragentString: userAgent,
|
||||
fissionEnabled: fissionAutoStart,
|
||||
};
|
||||
if (fullAppData) {
|
||||
app.applicationName = application.name;
|
||||
app.osArchitecture = getSysinfoProperty("arch", null);
|
||||
app.osName = getSysinfoProperty("name", null);
|
||||
app.osVersion = getSysinfoProperty("version", null);
|
||||
app.updateChannel = updateChannel;
|
||||
app.version = application.version;
|
||||
}
|
||||
|
||||
const hasTouchScreen = graphics.info.ApzTouchInput == 1;
|
||||
|
||||
const { registeredAntiVirus, registeredAntiSpyware, registeredFirewall } =
|
||||
securitySoftware;
|
||||
|
||||
const browserInfo = {
|
||||
addons: [],
|
||||
app,
|
||||
experiments: [],
|
||||
graphics: {
|
||||
devicesJson(actualStr) {
|
||||
const expected = getExpectedGraphicsDevices(snapshot);
|
||||
// If undefined is saved to the Glean value here, we'll get the string "undefined" (invalid JSON).
|
||||
// We should stop using JSON like this in bug 1875185.
|
||||
if (!actualStr || actualStr == "undefined") {
|
||||
return !expected.length;
|
||||
}
|
||||
return compareGraphicsDevices(expected, JSON.parse(actualStr));
|
||||
},
|
||||
driversJson(actualStr) {
|
||||
const expected = getExpectedGraphicsDrivers(snapshot);
|
||||
// If undefined is saved to the Glean value here, we'll get the string "undefined" (invalid JSON).
|
||||
// We should stop using JSON like this in bug 1875185.
|
||||
if (!actualStr || actualStr == "undefined") {
|
||||
return !expected.length;
|
||||
}
|
||||
return compareGraphicsDrivers(expected, JSON.parse(actualStr));
|
||||
},
|
||||
featuresJson(actualStr) {
|
||||
const expected = getExpectedGraphicsFeatures(snapshot);
|
||||
// If undefined is saved to the Glean value here, we'll get the string "undefined" (invalid JSON).
|
||||
// We should stop using JSON like this in bug 1875185.
|
||||
if (!actualStr || actualStr == "undefined") {
|
||||
return !expected.length;
|
||||
}
|
||||
return areObjectsEqual(JSON.parse(actualStr), expected);
|
||||
},
|
||||
hasTouchScreen,
|
||||
monitorsJson(actualStr) {
|
||||
const expected = gfxInfo.getMonitors();
|
||||
// If undefined is saved to the Glean value here, we'll get the string "undefined" (invalid JSON).
|
||||
// We should stop using JSON like this in bug 1875185.
|
||||
if (!actualStr || actualStr == "undefined") {
|
||||
return !expected.length;
|
||||
}
|
||||
return areObjectsEqual(JSON.parse(actualStr), expected);
|
||||
},
|
||||
},
|
||||
prefs: {
|
||||
cookieBehavior: Services.prefs.getIntPref(
|
||||
"network.cookie.cookieBehavior",
|
||||
-1
|
||||
),
|
||||
forcedAcceleratedLayers: Services.prefs.getBoolPref(
|
||||
"layers.acceleration.force-enabled",
|
||||
false
|
||||
),
|
||||
globalPrivacyControlEnabled: Services.prefs.getBoolPref(
|
||||
"privacy.globalprivacycontrol.enabled",
|
||||
false
|
||||
),
|
||||
installtriggerEnabled: Services.prefs.getBoolPref(
|
||||
"extensions.InstallTrigger.enabled",
|
||||
false
|
||||
),
|
||||
opaqueResponseBlocking: Services.prefs.getBoolPref(
|
||||
"browser.opaqueResponseBlocking",
|
||||
false
|
||||
),
|
||||
resistFingerprintingEnabled: Services.prefs.getBoolPref(
|
||||
"privacy.resistFingerprinting",
|
||||
false
|
||||
),
|
||||
softwareWebrender: Services.prefs.getBoolPref(
|
||||
"gfx.webrender.software",
|
||||
false
|
||||
),
|
||||
thirdPartyCookieBlockingEnabled: Services.prefs.getBoolPref(
|
||||
"network.cookie.cookieBehavior.optInPartitioning",
|
||||
false
|
||||
),
|
||||
thirdPartyCookieBlockingEnabledInPbm: Services.prefs.getBoolPref(
|
||||
"network.cookie.cookieBehavior.optInPartitioning.pbmode",
|
||||
false
|
||||
),
|
||||
},
|
||||
security: {
|
||||
antispyware: securityStringToArray(registeredAntiSpyware),
|
||||
antivirus: securityStringToArray(registeredAntiVirus),
|
||||
firewall: securityStringToArray(registeredFirewall),
|
||||
},
|
||||
system: {
|
||||
isTablet: getSysinfoProperty("tablet", false),
|
||||
memory: Math.round(memorySizeBytes / 1024 / 1024),
|
||||
},
|
||||
};
|
||||
|
||||
const tabInfo = await tab.linkedBrowser.ownerGlobal.SpecialPowers.spawn(
|
||||
tab.linkedBrowser,
|
||||
[],
|
||||
async function () {
|
||||
return {
|
||||
devicePixelRatio: `${content.devicePixelRatio}`,
|
||||
antitracking: {
|
||||
blockList: "basic",
|
||||
blockedOrigins: null,
|
||||
isPrivateBrowsing: false,
|
||||
hasTrackingContentBlocked: false,
|
||||
hasMixedActiveContentBlocked: false,
|
||||
hasMixedDisplayContentBlocked: false,
|
||||
btpHasPurgedSite: false,
|
||||
etpCategory: "standard",
|
||||
},
|
||||
frameworks: {
|
||||
fastclick: false,
|
||||
marfeel: false,
|
||||
mobify: false,
|
||||
},
|
||||
languages: content.navigator.languages,
|
||||
useragentString: content.navigator.userAgent,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
browserInfo.graphics.devicePixelRatio = tabInfo.devicePixelRatio;
|
||||
delete tabInfo.devicePixelRatio;
|
||||
|
||||
return { browserInfo, tabInfo };
|
||||
}
|
||||
|
||||
function extractPingData(branch) {
|
||||
const data = {};
|
||||
for (const [name, value] of Object.entries(branch)) {
|
||||
data[name] = value.testGetValue();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function extractBrokenSiteReportFromGleanPing(Glean) {
|
||||
const ping = extractPingData(Glean.brokenSiteReport);
|
||||
ping.tabInfo = extractPingData(Glean.brokenSiteReportTabInfo);
|
||||
ping.tabInfo.antitracking = extractPingData(
|
||||
Glean.brokenSiteReportTabInfoAntitracking
|
||||
);
|
||||
ping.tabInfo.frameworks = extractPingData(
|
||||
Glean.brokenSiteReportTabInfoFrameworks
|
||||
);
|
||||
ping.browserInfo = {
|
||||
addons: Array.from(Glean.brokenSiteReportBrowserInfo.addons.testGetValue()),
|
||||
app: extractPingData(Glean.brokenSiteReportBrowserInfoApp),
|
||||
graphics: extractPingData(Glean.brokenSiteReportBrowserInfoGraphics),
|
||||
experiments: Array.from(
|
||||
Glean.brokenSiteReportBrowserInfo.experiments.testGetValue()
|
||||
),
|
||||
prefs: extractPingData(Glean.brokenSiteReportBrowserInfoPrefs),
|
||||
security: extractPingData(Glean.brokenSiteReportBrowserInfoSecurity),
|
||||
system: extractPingData(Glean.brokenSiteReportBrowserInfoSystem),
|
||||
};
|
||||
return ping;
|
||||
}
|
||||
|
||||
async function testSend(tab, menu, expectedOverrides = {}) {
|
||||
const url = expectedOverrides.url ?? menu.win.gBrowser.currentURI.spec;
|
||||
const description = expectedOverrides.description ?? "";
|
||||
const breakageCategory = expectedOverrides.breakageCategory ?? null;
|
||||
|
||||
let rbs = await menu.openAndPrefillReportBrokenSite(url, description);
|
||||
|
||||
const snapshot = await Troubleshoot.snapshot();
|
||||
const expected = await getExpectedWebCompatInfo(tab, snapshot);
|
||||
|
||||
expected.url = url;
|
||||
expected.description = description;
|
||||
expected.breakageCategory = breakageCategory;
|
||||
|
||||
if (expectedOverrides.addons) {
|
||||
expected.browserInfo.addons = expectedOverrides.addons;
|
||||
}
|
||||
|
||||
if (expectedOverrides.experiments) {
|
||||
expected.browserInfo.experiments = expectedOverrides.experiments;
|
||||
}
|
||||
|
||||
if (expectedOverrides.antitracking) {
|
||||
expected.tabInfo.antitracking = expectedOverrides.antitracking;
|
||||
|
||||
if (expectedOverrides.antitracking.blockedOrigins) {
|
||||
rbs.blockedTrackersCheckbox = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (expectedOverrides.frameworks) {
|
||||
expected.tabInfo.frameworks = expectedOverrides.frameworks;
|
||||
}
|
||||
|
||||
if (breakageCategory) {
|
||||
rbs.chooseReason(breakageCategory);
|
||||
}
|
||||
|
||||
Services.fog.testResetFOG();
|
||||
await GleanPings.brokenSiteReport.testSubmission(
|
||||
() => {
|
||||
const ping = extractBrokenSiteReportFromGleanPing(Glean);
|
||||
|
||||
// sanity checks
|
||||
const { browserInfo, tabInfo } = ping;
|
||||
ok(ping.url?.length, "Got a URL");
|
||||
ok(
|
||||
["basic", "strict"].includes(tabInfo.antitracking.blockList),
|
||||
"Got a blockList"
|
||||
);
|
||||
if (rbs.blockedTrackersCheckbox.checked) {
|
||||
ok(
|
||||
Array.isArray(tabInfo.antitracking.blockedOrigins),
|
||||
"Got an array for blockedOrigins"
|
||||
);
|
||||
} else {
|
||||
ok(!tabInfo.antitracking.blockedOrigins, "No blockedOrigins included");
|
||||
}
|
||||
ok(tabInfo.useragentString?.length, "Got a final UA string");
|
||||
ok(
|
||||
browserInfo.app.defaultUseragentString?.length,
|
||||
"Got a default UA string"
|
||||
);
|
||||
|
||||
filterFrameworkDetectorFails(ping.tabInfo, expected.tabInfo);
|
||||
|
||||
ok(areObjectsEqual(ping, expected), "ping matches expectations");
|
||||
},
|
||||
() => rbs.clickSend()
|
||||
);
|
||||
|
||||
await rbs.clickOkay();
|
||||
|
||||
const telemetry = Glean.webcompatreporting.send.testGetValue();
|
||||
is(telemetry?.length, 1, "Got a 'send' telemetry event");
|
||||
is(
|
||||
telemetry[0].extra.sent_with_blocked_trackers,
|
||||
String(!!expectedOverrides.antitracking?.blockedOrigins),
|
||||
"Got correct 'sent_with_blocked_trackers' flag"
|
||||
);
|
||||
|
||||
// re-opening the panel, the url and description should be reset
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
ok(
|
||||
!rbs.blockedTrackersCheckbox.checked,
|
||||
"blocked trackers checkbox is reset"
|
||||
);
|
||||
rbs.close();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!-- 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/. -->
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
let ready;
|
||||
window.wrtReady = new Promise(r => ready = r);
|
||||
|
||||
let arrived;
|
||||
window.messageArrived = new Promise(r => arrived = r);
|
||||
|
||||
window.addEventListener("message", e => {
|
||||
window.message = e.data;
|
||||
arrived();
|
||||
});
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
setTimeout(ready, 100);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
307
src/zen/tests/mochitests/reportbrokensite/send_more_info.js
Normal file
307
src/zen/tests/mochitests/reportbrokensite/send_more_info.js
Normal file
@@ -0,0 +1,307 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Helper methods for testing the "send more info" link
|
||||
* of the Report Broken Site feature.
|
||||
*/
|
||||
|
||||
/* import-globals-from head.js */
|
||||
/* import-globals-from send.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
getRootDirectory(gTestPath) + "send.js",
|
||||
this
|
||||
);
|
||||
|
||||
async function reformatExpectedWebCompatInfo(tab, overrides) {
|
||||
const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
|
||||
const snapshot = await Troubleshoot.snapshot();
|
||||
const expected = await getExpectedWebCompatInfo(tab, snapshot, true);
|
||||
const { browserInfo, tabInfo } = expected;
|
||||
const { app, graphics, prefs, security } = browserInfo;
|
||||
const {
|
||||
applicationName,
|
||||
defaultUseragentString,
|
||||
fissionEnabled,
|
||||
osArchitecture,
|
||||
osName,
|
||||
osVersion,
|
||||
updateChannel,
|
||||
version,
|
||||
} = app;
|
||||
const { devicePixelRatio, hasTouchScreen } = graphics;
|
||||
const { antitracking, languages, useragentString } = tabInfo;
|
||||
|
||||
const addons = overrides.addons || [];
|
||||
const experiments = overrides.experiments || [];
|
||||
const atOverrides = overrides.antitracking;
|
||||
const blockList = atOverrides?.blockList ?? antitracking.blockList;
|
||||
const blockedOrigins =
|
||||
atOverrides?.blockedOrigins ?? antitracking.blockedOrigins ?? [];
|
||||
const hasMixedActiveContentBlocked =
|
||||
atOverrides?.hasMixedActiveContentBlocked ??
|
||||
antitracking.hasMixedActiveContentBlocked;
|
||||
const hasMixedDisplayContentBlocked =
|
||||
atOverrides?.hasMixedDisplayContentBlocked ??
|
||||
antitracking.hasMixedDisplayContentBlocked;
|
||||
const hasTrackingContentBlocked =
|
||||
atOverrides?.hasTrackingContentBlocked ??
|
||||
antitracking.hasTrackingContentBlocked;
|
||||
const isPrivateBrowsing =
|
||||
atOverrides?.isPrivateBrowsing ?? antitracking.isPrivateBrowsing;
|
||||
const btpHasPurgedSite =
|
||||
atOverrides?.btpHasPurgedSite ?? antitracking.btpHasPurgedSite;
|
||||
const etpCategory = atOverrides?.etpCategory ?? antitracking.etpCategory;
|
||||
|
||||
const extra_labels = [];
|
||||
const frameworks = overrides.frameworks ?? {
|
||||
fastclick: false,
|
||||
mobify: false,
|
||||
marfeel: false,
|
||||
};
|
||||
|
||||
// ignore the console log unless explicily testing for it.
|
||||
const consoleLog = overrides.consoleLog ?? (() => true);
|
||||
|
||||
const finalPrefs = {};
|
||||
for (const [key, pref] of Object.entries({
|
||||
cookieBehavior: "network.cookie.cookieBehavior",
|
||||
forcedAcceleratedLayers: "layers.acceleration.force-enabled",
|
||||
globalPrivacyControlEnabled: "privacy.globalprivacycontrol.enabled",
|
||||
installtriggerEnabled: "extensions.InstallTrigger.enabled",
|
||||
opaqueResponseBlocking: "browser.opaqueResponseBlocking",
|
||||
resistFingerprintingEnabled: "privacy.resistFingerprinting",
|
||||
softwareWebrender: "gfx.webrender.software",
|
||||
thirdPartyCookieBlockingEnabled:
|
||||
"network.cookie.cookieBehavior.optInPartitioning",
|
||||
thirdPartyCookieBlockingEnabledInPbm:
|
||||
"network.cookie.cookieBehavior.optInPartitioning.pbmode",
|
||||
})) {
|
||||
if (key in prefs) {
|
||||
finalPrefs[pref] = prefs[key];
|
||||
}
|
||||
}
|
||||
|
||||
const reformatted = {
|
||||
blockList,
|
||||
details: {
|
||||
additionalData: {
|
||||
addons,
|
||||
applicationName,
|
||||
blockList,
|
||||
blockedOrigins,
|
||||
buildId: snapshot.application.buildID,
|
||||
devicePixelRatio: parseInt(devicePixelRatio),
|
||||
experiments,
|
||||
finalUserAgent: useragentString,
|
||||
fissionEnabled,
|
||||
gfxData: {
|
||||
devices(actual) {
|
||||
const devices = getExpectedGraphicsDevices(snapshot);
|
||||
return compareGraphicsDevices(devices, actual);
|
||||
},
|
||||
drivers(actual) {
|
||||
const drvs = getExpectedGraphicsDrivers(snapshot);
|
||||
return compareGraphicsDrivers(drvs, actual);
|
||||
},
|
||||
features(actual) {
|
||||
const features = getExpectedGraphicsFeatures(snapshot);
|
||||
return areObjectsEqual(actual, features);
|
||||
},
|
||||
hasTouchScreen,
|
||||
monitors(actual) {
|
||||
return areObjectsEqual(actual, gfxInfo.getMonitors());
|
||||
},
|
||||
},
|
||||
hasMixedActiveContentBlocked,
|
||||
hasMixedDisplayContentBlocked,
|
||||
hasTrackingContentBlocked,
|
||||
btpHasPurgedSite,
|
||||
isPB: isPrivateBrowsing,
|
||||
etpCategory,
|
||||
languages,
|
||||
locales: snapshot.intl.localeService.available,
|
||||
memoryMB: browserInfo.system.memory,
|
||||
osArchitecture,
|
||||
osName,
|
||||
osVersion,
|
||||
prefs: finalPrefs,
|
||||
version,
|
||||
},
|
||||
blockList,
|
||||
channel: updateChannel,
|
||||
consoleLog,
|
||||
defaultUserAgent: defaultUseragentString,
|
||||
frameworks,
|
||||
hasTouchScreen,
|
||||
"gfx.webrender.software": prefs.softwareWebrender,
|
||||
"mixed active content blocked": hasMixedActiveContentBlocked,
|
||||
"mixed passive content blocked": hasMixedDisplayContentBlocked,
|
||||
"tracking content blocked": hasTrackingContentBlocked
|
||||
? `true (${blockList})`
|
||||
: "false",
|
||||
"btp has purged site": btpHasPurgedSite,
|
||||
},
|
||||
extra_labels,
|
||||
src: "desktop-reporter",
|
||||
utm_campaign: "report-broken-site",
|
||||
utm_source: "desktop-reporter",
|
||||
};
|
||||
|
||||
const { gfxData } = reformatted.details.additionalData;
|
||||
for (const optional of [
|
||||
"directWriteEnabled",
|
||||
"directWriteVersion",
|
||||
"clearTypeParameters",
|
||||
"targetFrameRate",
|
||||
]) {
|
||||
if (optional in snapshot.graphics) {
|
||||
gfxData[optional] = snapshot.graphics[optional];
|
||||
}
|
||||
}
|
||||
|
||||
// We only care about this pref on Linux right now on webcompat.com.
|
||||
if (AppConstants.platform != "linux") {
|
||||
delete finalPrefs["layers.acceleration.force-enabled"];
|
||||
} else {
|
||||
reformatted.details["layers.acceleration.force-enabled"] =
|
||||
finalPrefs["layers.acceleration.force-enabled"];
|
||||
}
|
||||
|
||||
// Only bother adding the security key if it has any data
|
||||
if (Object.values(security).filter(e => e).length) {
|
||||
reformatted.details.additionalData.sec = security;
|
||||
}
|
||||
|
||||
const expectedCodecs = snapshot.media.codecSupportInfo
|
||||
.replaceAll(" NONE", "")
|
||||
.split("\n")
|
||||
.sort()
|
||||
.join("\n");
|
||||
if (expectedCodecs) {
|
||||
reformatted.details.additionalData.gfxData.codecSupport = rawActual => {
|
||||
const actual = Object.entries(rawActual)
|
||||
.map(
|
||||
([
|
||||
name,
|
||||
{ hardwareDecode, softwareDecode, hardwareEncode, softwareEncode },
|
||||
]) =>
|
||||
(
|
||||
`${name} ` +
|
||||
`${softwareDecode ? "SWDEC " : ""}` +
|
||||
`${hardwareDecode ? "HWDEC " : ""}` +
|
||||
`${softwareEncode ? "SWENC " : ""}` +
|
||||
`${hardwareEncode ? "HWENC " : ""}`
|
||||
).trim()
|
||||
)
|
||||
.sort()
|
||||
.join("\n");
|
||||
return areObjectsEqual(actual, expectedCodecs);
|
||||
};
|
||||
}
|
||||
|
||||
if (blockList != "basic") {
|
||||
extra_labels.push(`type-tracking-protection-${blockList}`);
|
||||
}
|
||||
|
||||
if (overrides.expectNoTabDetails) {
|
||||
delete reformatted.details.frameworks;
|
||||
delete reformatted.details.consoleLog;
|
||||
delete reformatted.details["mixed active content blocked"];
|
||||
delete reformatted.details["mixed passive content blocked"];
|
||||
delete reformatted.details["tracking content blocked"];
|
||||
delete reformatted.details["btp has purged site"];
|
||||
} else {
|
||||
const { fastclick, mobify, marfeel } = frameworks;
|
||||
if (fastclick) {
|
||||
extra_labels.push("type-fastclick");
|
||||
reformatted.details.fastclick = true;
|
||||
}
|
||||
if (mobify) {
|
||||
extra_labels.push("type-mobify");
|
||||
reformatted.details.mobify = true;
|
||||
}
|
||||
if (marfeel) {
|
||||
extra_labels.push("type-marfeel");
|
||||
reformatted.details.marfeel = true;
|
||||
}
|
||||
}
|
||||
|
||||
extra_labels.sort();
|
||||
|
||||
return reformatted;
|
||||
}
|
||||
|
||||
async function testSendMoreInfo(tab, menu, expectedOverrides = {}) {
|
||||
const url = expectedOverrides.url ?? menu.win.gBrowser.currentURI.spec;
|
||||
const description = expectedOverrides.description ?? "";
|
||||
|
||||
let rbs = await menu.openAndPrefillReportBrokenSite(url, description);
|
||||
|
||||
const receivedData = await rbs.clickSendMoreInfo();
|
||||
await checkWebcompatComPayload(
|
||||
tab,
|
||||
url,
|
||||
description,
|
||||
expectedOverrides,
|
||||
receivedData
|
||||
);
|
||||
|
||||
// re-opening the panel, the url and description should be reset
|
||||
rbs = await menu.openReportBrokenSite();
|
||||
rbs.isMainViewResetToCurrentTab();
|
||||
rbs.close();
|
||||
}
|
||||
|
||||
async function testWebcompatComFallback(tab, menu) {
|
||||
const url = menu.win.gBrowser.currentURI.spec;
|
||||
const receivedData =
|
||||
await menu.clickReportBrokenSiteAndAwaitWebCompatTabData();
|
||||
await checkWebcompatComPayload(tab, url, "", {}, receivedData);
|
||||
menu.close();
|
||||
}
|
||||
|
||||
async function checkWebcompatComPayload(
|
||||
tab,
|
||||
url,
|
||||
description,
|
||||
expectedOverrides,
|
||||
receivedData
|
||||
) {
|
||||
const expected = await reformatExpectedWebCompatInfo(tab, expectedOverrides);
|
||||
expected.url = url;
|
||||
expected.description = description;
|
||||
|
||||
// sanity checks
|
||||
const { message } = receivedData;
|
||||
const { details } = message;
|
||||
const { additionalData } = details;
|
||||
ok(message.url?.length, "Got a URL");
|
||||
ok(["basic", "strict"].includes(details.blockList), "Got a blockList");
|
||||
ok(additionalData.applicationName?.length, "Got an app name");
|
||||
ok(additionalData.osArchitecture?.length, "Got an OS arch");
|
||||
ok(additionalData.osName?.length, "Got an OS name");
|
||||
ok(additionalData.osVersion?.length, "Got an OS version");
|
||||
ok(additionalData.version?.length, "Got an app version");
|
||||
ok(details.channel?.length, "Got an app channel");
|
||||
ok(details.defaultUserAgent?.length, "Got a default UA string");
|
||||
ok(additionalData.finalUserAgent?.length, "Got a final UA string");
|
||||
|
||||
// If we're sending any tab-specific data (which includes console logs),
|
||||
// check that there is also a valid screenshot.
|
||||
if ("consoleLog" in details) {
|
||||
const isScreenshotValid = await new Promise(done => {
|
||||
var image = new Image();
|
||||
image.onload = () => done(image.width > 0);
|
||||
image.onerror = () => done(false);
|
||||
image.src = receivedData.screenshot;
|
||||
});
|
||||
ok(isScreenshotValid, "Got a valid screenshot");
|
||||
}
|
||||
|
||||
filterFrameworkDetectorFails(message.details, expected.details);
|
||||
|
||||
ok(areObjectsEqual(message, expected), "sent info matches expectations");
|
||||
}
|
||||
14
src/zen/tests/mochitests/safebrowsing/browser.toml
Normal file
14
src/zen/tests/mochitests/safebrowsing/browser.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[DEFAULT]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"empty_file.html",
|
||||
]
|
||||
|
||||
["browser_bug400731.js"]
|
||||
|
||||
["browser_bug415846.js"]
|
||||
skip-if = ["true"] # Bug 1248632
|
||||
|
||||
["browser_mixedcontent_aboutblocked.js"]
|
||||
|
||||
["browser_whitelisted.js"]
|
||||
65
src/zen/tests/mochitests/safebrowsing/browser_bug400731.js
Normal file
65
src/zen/tests/mochitests/safebrowsing/browser_bug400731.js
Normal file
@@ -0,0 +1,65 @@
|
||||
/* Check presence of the "Ignore this warning" button */
|
||||
|
||||
function checkWarningState() {
|
||||
return SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
|
||||
return !!content.document.getElementById("ignore_warning_link");
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function testMalware() {
|
||||
await new Promise(resolve => waitForDBInit(resolve));
|
||||
|
||||
await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
|
||||
|
||||
const url = "http://www.itisatrap.org/firefox/its-an-attack.html";
|
||||
BrowserTestUtils.startLoadingURIString(gBrowser.selectedBrowser, url);
|
||||
await BrowserTestUtils.browserLoaded(
|
||||
gBrowser.selectedBrowser,
|
||||
false,
|
||||
url,
|
||||
true
|
||||
);
|
||||
|
||||
let buttonPresent = await checkWarningState();
|
||||
ok(buttonPresent, "Ignore warning link should be present for malware");
|
||||
});
|
||||
|
||||
add_task(async function testUnwanted() {
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.allowOverride", false);
|
||||
|
||||
// Now launch the unwanted software test
|
||||
const url = "http://www.itisatrap.org/firefox/unwanted.html";
|
||||
BrowserTestUtils.startLoadingURIString(gBrowser.selectedBrowser, url);
|
||||
await BrowserTestUtils.browserLoaded(
|
||||
gBrowser.selectedBrowser,
|
||||
false,
|
||||
url,
|
||||
true
|
||||
);
|
||||
|
||||
// Confirm that "Ignore this warning" is visible - bug 422410
|
||||
let buttonPresent = await checkWarningState();
|
||||
ok(
|
||||
!buttonPresent,
|
||||
"Ignore warning link should be missing for unwanted software"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function testPhishing() {
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.allowOverride", true);
|
||||
|
||||
// Now launch the phishing test
|
||||
const url = "http://www.itisatrap.org/firefox/its-a-trap.html";
|
||||
BrowserTestUtils.startLoadingURIString(gBrowser.selectedBrowser, url);
|
||||
await BrowserTestUtils.browserLoaded(
|
||||
gBrowser.selectedBrowser,
|
||||
false,
|
||||
url,
|
||||
true
|
||||
);
|
||||
|
||||
let buttonPresent = await checkWarningState();
|
||||
ok(buttonPresent, "Ignore warning link should be present for phishing");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
98
src/zen/tests/mochitests/safebrowsing/browser_bug415846.js
Normal file
98
src/zen/tests/mochitests/safebrowsing/browser_bug415846.js
Normal file
@@ -0,0 +1,98 @@
|
||||
/* Check for the correct behaviour of the report web forgery/not a web forgery
|
||||
menu items.
|
||||
|
||||
Mac makes this astonishingly painful to test since their help menu is special magic,
|
||||
but we can at least test it on the other platforms.*/
|
||||
|
||||
const NORMAL_PAGE = "http://example.com";
|
||||
const PHISH_PAGE = "http://www.itisatrap.org/firefox/its-a-trap.html";
|
||||
|
||||
/**
|
||||
* Opens a new tab and browses to some URL, tests for the existence
|
||||
* of the phishing menu items, and then runs a test function to check
|
||||
* the state of the menu once opened. This function will take care of
|
||||
* opening and closing the menu.
|
||||
*
|
||||
* @param url (string)
|
||||
* The URL to browse the tab to.
|
||||
* @param testFn (function)
|
||||
* The function to run once the menu has been opened. This
|
||||
* function will be passed the "reportMenu" and "errorMenu"
|
||||
* DOM nodes as arguments, in that order. This function
|
||||
* should not yield anything.
|
||||
* @returns Promise
|
||||
*/
|
||||
function check_menu_at_page(url, testFn) {
|
||||
return BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:blank",
|
||||
},
|
||||
async function (browser) {
|
||||
// We don't get load events when the DocShell redirects to error
|
||||
// pages, but we do get DOMContentLoaded, so we'll wait for that.
|
||||
let dclPromise = SpecialPowers.spawn(browser, [], async function () {
|
||||
await ContentTaskUtils.waitForEvent(this, "DOMContentLoaded", false);
|
||||
});
|
||||
BrowserTestUtils.startLoadingURIString(browser, url);
|
||||
await dclPromise;
|
||||
|
||||
let menu = document.getElementById("menu_HelpPopup");
|
||||
ok(menu, "Help menu should exist");
|
||||
|
||||
let reportMenu = document.getElementById(
|
||||
"menu_HelpPopup_reportPhishingtoolmenu"
|
||||
);
|
||||
ok(reportMenu, "Report phishing menu item should exist");
|
||||
|
||||
let errorMenu = document.getElementById(
|
||||
"menu_HelpPopup_reportPhishingErrortoolmenu"
|
||||
);
|
||||
ok(errorMenu, "Report phishing error menu item should exist");
|
||||
|
||||
let menuOpen = BrowserTestUtils.waitForEvent(menu, "popupshown");
|
||||
menu.openPopup(null, "", 0, 0, false, null);
|
||||
await menuOpen;
|
||||
|
||||
testFn(reportMenu, errorMenu);
|
||||
|
||||
let menuClose = BrowserTestUtils.waitForEvent(menu, "popuphidden");
|
||||
menu.hidePopup();
|
||||
await menuClose;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we show the "Report this page" menu item at a normal
|
||||
* page.
|
||||
*/
|
||||
add_task(async function () {
|
||||
await check_menu_at_page(NORMAL_PAGE, (reportMenu, errorMenu) => {
|
||||
ok(
|
||||
!reportMenu.hidden,
|
||||
"Report phishing menu should be visible on normal sites"
|
||||
);
|
||||
ok(
|
||||
errorMenu.hidden,
|
||||
"Report error menu item should be hidden on normal sites"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that we show the "Report this page is okay" menu item at
|
||||
* a reported attack site.
|
||||
*/
|
||||
add_task(async function () {
|
||||
await check_menu_at_page(PHISH_PAGE, (reportMenu, errorMenu) => {
|
||||
ok(
|
||||
reportMenu.hidden,
|
||||
"Report phishing menu should be hidden on phishing sites"
|
||||
);
|
||||
ok(
|
||||
!errorMenu.hidden,
|
||||
"Report error menu item should be visible on phishing sites"
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const SECURE_CONTAINER_URL =
|
||||
"https://example.com/browser/browser/components/safebrowsing/content/test/empty_file.html";
|
||||
|
||||
add_task(async function testNormalBrowsing() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.safebrowsing.only_top_level", false]],
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
SECURE_CONTAINER_URL,
|
||||
async function (browser) {
|
||||
// Before we load the phish url, we have to make sure the hard-coded
|
||||
// black list has been added to the database.
|
||||
await new Promise(resolve => waitForDBInit(resolve));
|
||||
|
||||
let promise = new Promise(resolve => {
|
||||
// Register listener before loading phish URL.
|
||||
let removeFunc = BrowserTestUtils.addContentEventListener(
|
||||
browser,
|
||||
"AboutBlockedLoaded",
|
||||
() => {
|
||||
removeFunc();
|
||||
resolve();
|
||||
},
|
||||
{ wantUntrusted: true }
|
||||
);
|
||||
});
|
||||
|
||||
await SpecialPowers.spawn(
|
||||
browser,
|
||||
[PHISH_URL],
|
||||
async function (aPhishUrl) {
|
||||
// Create an iframe which is going to load a phish url.
|
||||
let iframe = content.document.createElement("iframe");
|
||||
iframe.src = aPhishUrl;
|
||||
content.document.body.appendChild(iframe);
|
||||
}
|
||||
);
|
||||
|
||||
await promise;
|
||||
ok(true, "about:blocked is successfully loaded!");
|
||||
}
|
||||
);
|
||||
});
|
||||
46
src/zen/tests/mochitests/safebrowsing/browser_whitelisted.js
Normal file
46
src/zen/tests/mochitests/safebrowsing/browser_whitelisted.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/* Ensure that hostnames in the whitelisted pref are not blocked. */
|
||||
|
||||
const PREF_WHITELISTED_HOSTNAMES = "urlclassifier.skipHostnames";
|
||||
const TEST_PAGE = "http://www.itisatrap.org/firefox/its-an-attack.html";
|
||||
var tabbrowser = null;
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
tabbrowser = null;
|
||||
Services.prefs.clearUserPref(PREF_WHITELISTED_HOSTNAMES);
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
});
|
||||
|
||||
function testBlockedPage() {
|
||||
info("Non-whitelisted pages must be blocked");
|
||||
ok(true, "about:blocked was shown");
|
||||
}
|
||||
|
||||
function testWhitelistedPage(window) {
|
||||
info("Whitelisted pages must be skipped");
|
||||
var getmeout_button = window.document.getElementById("getMeOutButton");
|
||||
var ignorewarning_button = window.document.getElementById(
|
||||
"ignoreWarningButton"
|
||||
);
|
||||
ok(!getmeout_button, "GetMeOut button not present");
|
||||
ok(!ignorewarning_button, "IgnoreWarning button not present");
|
||||
}
|
||||
|
||||
add_task(async function testNormalBrowsing() {
|
||||
tabbrowser = gBrowser;
|
||||
let tab = (tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser));
|
||||
|
||||
info("Load a test page that's whitelisted");
|
||||
Services.prefs.setCharPref(
|
||||
PREF_WHITELISTED_HOSTNAMES,
|
||||
"example.com,www.ItIsaTrap.org,example.net"
|
||||
);
|
||||
await promiseTabLoadEvent(tab, TEST_PAGE, "load");
|
||||
testWhitelistedPage(tab.ownerGlobal);
|
||||
|
||||
info("Load a test page that's no longer whitelisted");
|
||||
Services.prefs.setCharPref(PREF_WHITELISTED_HOSTNAMES, "");
|
||||
await promiseTabLoadEvent(tab, TEST_PAGE, "AboutBlockedLoaded");
|
||||
testBlockedPage(tab.ownerGlobal);
|
||||
});
|
||||
1
src/zen/tests/mochitests/safebrowsing/empty_file.html
Normal file
1
src/zen/tests/mochitests/safebrowsing/empty_file.html
Normal file
@@ -0,0 +1 @@
|
||||
<html><body></body></html>
|
||||
103
src/zen/tests/mochitests/safebrowsing/head.js
Normal file
103
src/zen/tests/mochitests/safebrowsing/head.js
Normal file
@@ -0,0 +1,103 @@
|
||||
// This url must sync with the table, url in SafeBrowsing.sys.mjs addMozEntries
|
||||
const PHISH_TABLE = "moztest-phish-simple";
|
||||
const PHISH_URL = "https://www.itisatrap.org/firefox/its-a-trap.html";
|
||||
|
||||
/**
|
||||
* Waits for a load (or custom) event to finish in a given tab. If provided
|
||||
* load an uri into the tab.
|
||||
*
|
||||
* @param tab
|
||||
* The tab to load into.
|
||||
* @param [optional] url
|
||||
* The url to load, or the current url.
|
||||
* @param [optional] event
|
||||
* The load event type to wait for. Defaults to "load".
|
||||
* @return {Promise} resolved when the event is handled.
|
||||
* @resolves to the received event
|
||||
* @rejects if a valid load event is not received within a meaningful interval
|
||||
*/
|
||||
function promiseTabLoadEvent(tab, url, eventType = "load") {
|
||||
info(`Wait tab event: ${eventType}`);
|
||||
|
||||
function handle(loadedUrl) {
|
||||
if (loadedUrl === "about:blank" || (url && loadedUrl !== url)) {
|
||||
info(`Skipping spurious load event for ${loadedUrl}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
info("Tab event received: load");
|
||||
return true;
|
||||
}
|
||||
|
||||
let loaded;
|
||||
if (eventType === "load") {
|
||||
loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle);
|
||||
} else {
|
||||
// No need to use handle.
|
||||
loaded = BrowserTestUtils.waitForContentEvent(
|
||||
tab.linkedBrowser,
|
||||
eventType,
|
||||
true,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if (url) {
|
||||
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, url);
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
// This function is mostly ported from classifierCommon.js
|
||||
// under toolkit/components/url-classifier/tests/mochitest.
|
||||
function waitForDBInit(callback) {
|
||||
// Since there are two cases that may trigger the callback,
|
||||
// we have to carefully avoid multiple callbacks and observer
|
||||
// leaking.
|
||||
let didCallback = false;
|
||||
function callbackOnce() {
|
||||
if (!didCallback) {
|
||||
Services.obs.removeObserver(obsFunc, "mozentries-update-finished");
|
||||
callback();
|
||||
}
|
||||
didCallback = true;
|
||||
}
|
||||
|
||||
// The first part: listen to internal event.
|
||||
function obsFunc() {
|
||||
ok(true, "Received internal event!");
|
||||
callbackOnce();
|
||||
}
|
||||
Services.obs.addObserver(obsFunc, "mozentries-update-finished");
|
||||
|
||||
// The second part: we might have missed the event. Just do
|
||||
// an internal database lookup to confirm if the url has been
|
||||
// added.
|
||||
let principal = Services.scriptSecurityManager.createContentPrincipal(
|
||||
Services.io.newURI(PHISH_URL),
|
||||
{}
|
||||
);
|
||||
|
||||
let dbService = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
|
||||
Ci.nsIUrlClassifierDBService
|
||||
);
|
||||
dbService.lookup(principal, PHISH_TABLE, value => {
|
||||
if (value === PHISH_TABLE) {
|
||||
ok(true, "DB lookup success!");
|
||||
callbackOnce();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Services.prefs.setCharPref(
|
||||
"urlclassifier.malwareTable",
|
||||
"moztest-malware-simple,moztest-unwanted-simple,moztest-harmful-simple"
|
||||
);
|
||||
Services.prefs.setCharPref("urlclassifier.phishTable", "moztest-phish-simple");
|
||||
Services.prefs.setCharPref(
|
||||
"urlclassifier.blockedTable",
|
||||
"moztest-block-simple"
|
||||
);
|
||||
SafeBrowsing.init();
|
||||
103
src/zen/tests/mochitests/shell/browser.toml
Normal file
103
src/zen/tests/mochitests/shell/browser.toml
Normal file
@@ -0,0 +1,103 @@
|
||||
[DEFAULT]
|
||||
|
||||
["browser_1119088.js"]
|
||||
disabled="Disabled by import_external_tests.py"
|
||||
support-files = ["mac_desktop_image.py"]
|
||||
run-if = ["os == 'mac'"]
|
||||
tags = "os_integration"
|
||||
skip-if = ["os == 'mac' && os_version == '14.70' && processor == 'x86_64'"] # Bug 1869703
|
||||
|
||||
["browser_420786.js"]
|
||||
run-if = ["os == 'linux'"]
|
||||
|
||||
["browser_633221.js"]
|
||||
run-if = ["os == 'linux'"]
|
||||
|
||||
["browser_createWindowsShortcut.js"]
|
||||
run-if = ["os == 'win'"]
|
||||
|
||||
["browser_doesAppNeedPin.js"]
|
||||
|
||||
["browser_headless_screenshot_1.js"]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"headless.html",
|
||||
]
|
||||
skip-if = [
|
||||
"os == 'win'",
|
||||
"ccov",
|
||||
"tsan", # Bug 1429950, Bug 1583315, Bug 1696109, Bug 1701449
|
||||
]
|
||||
tags = "os_integration"
|
||||
|
||||
["browser_headless_screenshot_2.js"]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"headless.html",
|
||||
]
|
||||
skip-if = [
|
||||
"os == 'win'",
|
||||
"ccov",
|
||||
"tsan", # Bug 1429950, Bug 1583315, Bug 1696109, Bug 1701449
|
||||
]
|
||||
|
||||
["browser_headless_screenshot_3.js"]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"headless.html",
|
||||
]
|
||||
skip-if = [
|
||||
"os == 'win'",
|
||||
"ccov",
|
||||
"tsan", # Bug 1429950, Bug 1583315, Bug 1696109, Bug 1701449
|
||||
]
|
||||
|
||||
["browser_headless_screenshot_4.js"]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"headless.html",
|
||||
]
|
||||
skip-if = [
|
||||
"os == 'win'",
|
||||
"ccov",
|
||||
"tsan", # Bug 1429950, Bug 1583315, Bug 1696109, Bug 1701449
|
||||
]
|
||||
|
||||
["browser_headless_screenshot_cross_origin.js"]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"headless_cross_origin.html",
|
||||
"headless_iframe.html",
|
||||
]
|
||||
skip-if = [
|
||||
"os == 'win'",
|
||||
"ccov",
|
||||
"tsan", # Bug 1429950, Bug 1583315, Bug 1696109, Bug 1701449
|
||||
]
|
||||
|
||||
["browser_headless_screenshot_redirect.js"]
|
||||
support-files = [
|
||||
"head.js",
|
||||
"headless.html",
|
||||
"headless_redirect.html",
|
||||
"headless_redirect.html^headers^",
|
||||
]
|
||||
skip-if = [
|
||||
"os == 'win'",
|
||||
"ccov",
|
||||
"tsan", # Bug 1429950, Bug 1583315, Bug 1696109, Bug 1701449
|
||||
]
|
||||
|
||||
["browser_processAUMID.js"]
|
||||
run-if = ["os == 'win'"]
|
||||
|
||||
["browser_setDefaultBrowser.js"]
|
||||
tags = "os_integration"
|
||||
|
||||
["browser_setDefaultPDFHandler.js"]
|
||||
run-if = ["os == 'win'"]
|
||||
tags = "os_integration"
|
||||
|
||||
["browser_setDesktopBackgroundPreview.js"]
|
||||
disabled="Disabled by import_external_tests.py"
|
||||
tags = "os_integration"
|
||||
173
src/zen/tests/mochitests/shell/browser_1119088.js
Normal file
173
src/zen/tests/mochitests/shell/browser_1119088.js
Normal file
@@ -0,0 +1,173 @@
|
||||
// Where we save the desktop background to (~/Pictures).
|
||||
const NS_OSX_PICTURE_DOCUMENTS_DIR = "Pct";
|
||||
|
||||
// Paths used to run the CLI command (python script) that is used to
|
||||
// 1) check the desktop background image matches what we set it to via
|
||||
// nsIShellService::setDesktopBackground() and
|
||||
// 2) revert the desktop background image to the OS default
|
||||
|
||||
let kPythonPath = "/usr/bin/python";
|
||||
if (AppConstants.isPlatformAndVersionAtLeast("macosx", 23.0)) {
|
||||
kPythonPath = "/usr/local/bin/python3";
|
||||
}
|
||||
|
||||
const kDesktopCheckerScriptPath =
|
||||
"browser/browser/components/shell/test/mac_desktop_image.py";
|
||||
const kDefaultBackgroundImage =
|
||||
"/System/Library/Desktop Pictures/Solid Colors/Teal.png";
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
|
||||
});
|
||||
|
||||
function getPythonExecutableFile() {
|
||||
let python = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
info(`Using python at location ${kPythonPath}`);
|
||||
python.initWithPath(kPythonPath);
|
||||
return python;
|
||||
}
|
||||
|
||||
function createProcess() {
|
||||
return Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
|
||||
}
|
||||
|
||||
// Use a CLI command to set the desktop background to |imagePath|. Returns the
|
||||
// exit code of the CLI command which reflects whether or not the background
|
||||
// image was successfully set. Returns 0 on success.
|
||||
function setDesktopBackgroundCLI(imagePath) {
|
||||
let setBackgroundProcess = createProcess();
|
||||
setBackgroundProcess.init(getPythonExecutableFile());
|
||||
let args = [
|
||||
kDesktopCheckerScriptPath,
|
||||
"--verbose",
|
||||
"--set-background-image",
|
||||
imagePath,
|
||||
];
|
||||
setBackgroundProcess.run(true, args, args.length);
|
||||
return setBackgroundProcess.exitValue;
|
||||
}
|
||||
|
||||
// Check the desktop background is |imagePath| using a CLI command.
|
||||
// Returns the exit code of the CLI command which reflects whether or not
|
||||
// the provided image path matches the path of the current desktop background
|
||||
// image. A return value of 0 indicates success/match.
|
||||
function checkDesktopBackgroundCLI(imagePath) {
|
||||
let checkBackgroundProcess = createProcess();
|
||||
checkBackgroundProcess.init(getPythonExecutableFile());
|
||||
let args = [
|
||||
kDesktopCheckerScriptPath,
|
||||
"--verbose",
|
||||
"--check-background-image",
|
||||
imagePath,
|
||||
];
|
||||
checkBackgroundProcess.run(true, args, args.length);
|
||||
return checkBackgroundProcess.exitValue;
|
||||
}
|
||||
|
||||
// Use the python script to set/check the desktop background is |imagePath|
|
||||
function setAndCheckDesktopBackgroundCLI(imagePath) {
|
||||
Assert.ok(FileUtils.File(imagePath).exists(), `${imagePath} exists`);
|
||||
|
||||
let setExitCode = setDesktopBackgroundCLI(imagePath);
|
||||
Assert.equal(setExitCode, 0, `Setting background via CLI to ${imagePath}`);
|
||||
|
||||
let checkExitCode = checkDesktopBackgroundCLI(imagePath);
|
||||
Assert.equal(checkExitCode, 0, `Checking background via CLI is ${imagePath}`);
|
||||
}
|
||||
|
||||
// Restore the automation default background image. i.e., the default used
|
||||
// in the automated test environment, not the OS default.
|
||||
function restoreDefaultBackground() {
|
||||
let defaultBackgroundPath;
|
||||
defaultBackgroundPath = kDefaultBackgroundImage;
|
||||
setAndCheckDesktopBackgroundCLI(defaultBackgroundPath);
|
||||
}
|
||||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["test.wait300msAfterTabSwitch", true]],
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests "Set As Desktop Background" platform implementation on macOS.
|
||||
*
|
||||
* Sets the desktop background image to the browser logo from the about:logo
|
||||
* page and verifies it was set successfully. Setting the desktop background
|
||||
* (which uses the nsIShellService::setDesktopBackground() interface method)
|
||||
* downloads the image to ~/Pictures using a unique file name and sets the
|
||||
* desktop background to the downloaded file leaving the download in place.
|
||||
* After setDesktopBackground() is called, the test uses a python script to
|
||||
* validate that the current desktop background is in fact set to the
|
||||
* downloaded logo.
|
||||
*/
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:logo",
|
||||
},
|
||||
async () => {
|
||||
let dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(
|
||||
Ci.nsIDirectoryServiceProvider
|
||||
);
|
||||
let uuidGenerator = Services.uuid;
|
||||
let shellSvc = Cc["@mozilla.org/browser/shell-service;1"].getService(
|
||||
Ci.nsIShellService
|
||||
);
|
||||
|
||||
// Ensure we are starting with the default background. Log a
|
||||
// failure if we can not set the background to the default, but
|
||||
// ignore the case where the background is not already set as that
|
||||
// that may be due to a previous test failure.
|
||||
restoreDefaultBackground();
|
||||
|
||||
// Generate a UUID (with non-alphanumberic characters removed) to build
|
||||
// up a filename for the desktop background. Use a UUID to distinguish
|
||||
// between runs so we won't be confused by images that were not properly
|
||||
// cleaned up after previous runs.
|
||||
let uuid = uuidGenerator.generateUUID().toString().replace(/\W/g, "");
|
||||
|
||||
// Set the background image path to be $HOME/Pictures/<UUID>.png.
|
||||
// nsIShellService.setDesktopBackground() downloads the image to this
|
||||
// path and then sets it as the desktop background image, leaving the
|
||||
// image in place.
|
||||
let backgroundImage = dirSvc.getFile(NS_OSX_PICTURE_DOCUMENTS_DIR, {});
|
||||
backgroundImage.append(uuid + ".png");
|
||||
if (backgroundImage.exists()) {
|
||||
backgroundImage.remove(false);
|
||||
}
|
||||
|
||||
// For simplicity, we're going to reach in and access the image on the
|
||||
// page directly, which means the page shouldn't be running in a remote
|
||||
// browser. Thankfully, about:logo runs in the parent process for now.
|
||||
Assert.ok(
|
||||
!gBrowser.selectedBrowser.isRemoteBrowser,
|
||||
"image can be accessed synchronously from the parent process"
|
||||
);
|
||||
let image = gBrowser.selectedBrowser.contentDocument.images[0];
|
||||
|
||||
info(`Setting/saving desktop background to ${backgroundImage.path}`);
|
||||
|
||||
// Saves the file in ~/Pictures
|
||||
shellSvc.setDesktopBackground(image, 0, backgroundImage.leafName);
|
||||
|
||||
await BrowserTestUtils.waitForCondition(() => backgroundImage.exists());
|
||||
info(`${backgroundImage.path} downloaded`);
|
||||
Assert.ok(
|
||||
FileUtils.File(backgroundImage.path).exists(),
|
||||
`${backgroundImage.path} exists`
|
||||
);
|
||||
|
||||
// Check that the desktop background image is the image we set above.
|
||||
let exitCode = checkDesktopBackgroundCLI(backgroundImage.path);
|
||||
Assert.equal(exitCode, 0, `background should be ${backgroundImage.path}`);
|
||||
|
||||
// Restore the background image to the Mac default.
|
||||
restoreDefaultBackground();
|
||||
|
||||
// We no longer need the downloaded image.
|
||||
backgroundImage.remove(false);
|
||||
}
|
||||
);
|
||||
});
|
||||
101
src/zen/tests/mochitests/shell/browser_420786.js
Normal file
101
src/zen/tests/mochitests/shell/browser_420786.js
Normal file
@@ -0,0 +1,101 @@
|
||||
const DG_BACKGROUND = "/desktop/gnome/background";
|
||||
const DG_IMAGE_KEY = DG_BACKGROUND + "/picture_filename";
|
||||
const DG_OPTION_KEY = DG_BACKGROUND + "/picture_options";
|
||||
const DG_DRAW_BG_KEY = DG_BACKGROUND + "/draw_background";
|
||||
|
||||
const GS_BG_SCHEMA = "org.gnome.desktop.background";
|
||||
const GS_IMAGE_KEY = "picture-uri";
|
||||
const GS_OPTION_KEY = "picture-options";
|
||||
const GS_DRAW_BG_KEY = "draw-background";
|
||||
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:logo",
|
||||
},
|
||||
() => {
|
||||
var brandName = Services.strings
|
||||
.createBundle("chrome://branding/locale/brand.properties")
|
||||
.GetStringFromName("brandShortName");
|
||||
|
||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(
|
||||
Ci.nsIDirectoryServiceProvider
|
||||
);
|
||||
var homeDir = dirSvc.getFile("Home", {});
|
||||
|
||||
var wpFile = homeDir.clone();
|
||||
wpFile.append(brandName + "_wallpaper.png");
|
||||
|
||||
// Backup the existing wallpaper so that this test doesn't change the user's
|
||||
// settings.
|
||||
var wpFileBackup = homeDir.clone();
|
||||
wpFileBackup.append(brandName + "_wallpaper.png.backup");
|
||||
|
||||
if (wpFileBackup.exists()) {
|
||||
wpFileBackup.remove(false);
|
||||
}
|
||||
|
||||
if (wpFile.exists()) {
|
||||
wpFile.copyTo(null, wpFileBackup.leafName);
|
||||
}
|
||||
|
||||
var shell = Cc["@mozilla.org/browser/shell-service;1"]
|
||||
.getService(Ci.nsIShellService)
|
||||
.QueryInterface(Ci.nsIGNOMEShellService);
|
||||
|
||||
// For simplicity, we're going to reach in and access the image on the
|
||||
// page directly, which means the page shouldn't be running in a remote
|
||||
// browser. Thankfully, about:logo runs in the parent process for now.
|
||||
Assert.ok(
|
||||
!gBrowser.selectedBrowser.isRemoteBrowser,
|
||||
"image can be accessed synchronously from the parent process"
|
||||
);
|
||||
|
||||
var image = content.document.images[0];
|
||||
|
||||
let checkWallpaper, restoreSettings;
|
||||
try {
|
||||
const prevImage = shell.getGSettingsString(GS_BG_SCHEMA, GS_IMAGE_KEY);
|
||||
const prevOption = shell.getGSettingsString(
|
||||
GS_BG_SCHEMA,
|
||||
GS_OPTION_KEY
|
||||
);
|
||||
|
||||
checkWallpaper = function (position, expectedGSettingsPosition) {
|
||||
shell.setDesktopBackground(image, position, "");
|
||||
ok(wpFile.exists(), "Wallpaper was written to disk");
|
||||
is(
|
||||
shell.getGSettingsString(GS_BG_SCHEMA, GS_IMAGE_KEY),
|
||||
encodeURI("file://" + wpFile.path),
|
||||
"Wallpaper file GSettings key is correct"
|
||||
);
|
||||
is(
|
||||
shell.getGSettingsString(GS_BG_SCHEMA, GS_OPTION_KEY),
|
||||
expectedGSettingsPosition,
|
||||
"Wallpaper position GSettings key is correct"
|
||||
);
|
||||
};
|
||||
|
||||
restoreSettings = function () {
|
||||
shell.setGSettingsString(GS_BG_SCHEMA, GS_IMAGE_KEY, prevImage);
|
||||
shell.setGSettingsString(GS_BG_SCHEMA, GS_OPTION_KEY, prevOption);
|
||||
};
|
||||
} catch (e) {}
|
||||
|
||||
checkWallpaper(Ci.nsIShellService.BACKGROUND_TILE, "wallpaper");
|
||||
checkWallpaper(Ci.nsIShellService.BACKGROUND_STRETCH, "stretched");
|
||||
checkWallpaper(Ci.nsIShellService.BACKGROUND_CENTER, "centered");
|
||||
checkWallpaper(Ci.nsIShellService.BACKGROUND_FILL, "zoom");
|
||||
checkWallpaper(Ci.nsIShellService.BACKGROUND_FIT, "scaled");
|
||||
checkWallpaper(Ci.nsIShellService.BACKGROUND_SPAN, "spanned");
|
||||
|
||||
restoreSettings();
|
||||
|
||||
// Restore files
|
||||
if (wpFileBackup.exists()) {
|
||||
wpFileBackup.moveTo(null, wpFile.leafName);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
11
src/zen/tests/mochitests/shell/browser_633221.js
Normal file
11
src/zen/tests/mochitests/shell/browser_633221.js
Normal file
@@ -0,0 +1,11 @@
|
||||
function test() {
|
||||
ShellService.setDefaultBrowser(false);
|
||||
ok(
|
||||
ShellService.isDefaultBrowser(true, false),
|
||||
"we got here and are the default browser"
|
||||
);
|
||||
ok(
|
||||
ShellService.isDefaultBrowser(true, true),
|
||||
"we got here and are the default browser"
|
||||
);
|
||||
}
|
||||
218
src/zen/tests/mochitests/shell/browser_createWindowsShortcut.js
Normal file
218
src/zen/tests/mochitests/shell/browser_createWindowsShortcut.js
Normal file
@@ -0,0 +1,218 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
FileTestUtils: "resource://testing-common/FileTestUtils.sys.mjs",
|
||||
MockRegistrar: "resource://testing-common/MockRegistrar.sys.mjs",
|
||||
});
|
||||
|
||||
const gBase = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
gBase.append("CreateWindowsShortcut");
|
||||
createDirectory(gBase);
|
||||
|
||||
const gTmpDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
|
||||
|
||||
const gDirectoryServiceProvider = {
|
||||
getFile(prop, persistent) {
|
||||
persistent.value = false;
|
||||
|
||||
// We only expect a narrow range of calls.
|
||||
let folder = gBase.clone();
|
||||
switch (prop) {
|
||||
case "Progs":
|
||||
folder.append("Programs");
|
||||
break;
|
||||
case "Desk":
|
||||
folder.append("Desktop");
|
||||
break;
|
||||
case "UpdRootD":
|
||||
// We really want DataRoot, but UpdateSubdir is what we usually get.
|
||||
folder.append("DataRoot");
|
||||
folder.append("UpdateDir");
|
||||
folder.append("UpdateSubdir");
|
||||
break;
|
||||
case "ProfD":
|
||||
// Used by test infrastructure.
|
||||
folder = folder.parent;
|
||||
break;
|
||||
case "TmpD":
|
||||
// Used by FileTestUtils.
|
||||
folder = gTmpDir;
|
||||
break;
|
||||
default:
|
||||
console.error(`Access to unexpected directory '${prop}'`);
|
||||
return Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
createDirectory(folder);
|
||||
return folder;
|
||||
},
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIDirectoryServiceProvider]),
|
||||
};
|
||||
|
||||
add_setup(() => {
|
||||
Services.dirsvc
|
||||
.QueryInterface(Ci.nsIDirectoryService)
|
||||
.registerProvider(gDirectoryServiceProvider);
|
||||
});
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
gBase.remove(true);
|
||||
Services.dirsvc
|
||||
.QueryInterface(Ci.nsIDirectoryService)
|
||||
.unregisterProvider(gDirectoryServiceProvider);
|
||||
});
|
||||
|
||||
add_task(async function test_CreateWindowsShortcut() {
|
||||
const DEST = "browser_createWindowsShortcut_TestFile.lnk";
|
||||
|
||||
const file = FileTestUtils.getTempFile("program.exe");
|
||||
const iconPath = FileTestUtils.getTempFile("program.ico");
|
||||
|
||||
let shortcut;
|
||||
|
||||
const defaults = {
|
||||
shellService: Cc["@mozilla.org/toolkit/shell-service;1"].getService(),
|
||||
targetFile: file,
|
||||
iconFile: iconPath,
|
||||
description: "made by browser_createWindowsShortcut.js",
|
||||
aumid: "TESTTEST",
|
||||
};
|
||||
|
||||
shortcut = Services.dirsvc.get("Progs", Ci.nsIFile);
|
||||
shortcut.append(DEST);
|
||||
await testShortcut({
|
||||
shortcutFile: shortcut,
|
||||
relativePath: DEST,
|
||||
specialFolder: "Programs",
|
||||
logHeader: "STARTMENU",
|
||||
...defaults,
|
||||
});
|
||||
|
||||
let subdir = Services.dirsvc.get("Progs", Ci.nsIFile);
|
||||
subdir.append("Shortcut Test");
|
||||
tryRemove(subdir);
|
||||
|
||||
shortcut = subdir.clone();
|
||||
shortcut.append(DEST);
|
||||
await testShortcut({
|
||||
shortcutFile: shortcut,
|
||||
relativePath: "Shortcut Test\\" + DEST,
|
||||
specialFolder: "Programs",
|
||||
logHeader: "STARTMENU",
|
||||
...defaults,
|
||||
});
|
||||
tryRemove(subdir);
|
||||
|
||||
shortcut = Services.dirsvc.get("Desk", Ci.nsIFile);
|
||||
shortcut.append(DEST);
|
||||
await testShortcut({
|
||||
shortcutFile: shortcut,
|
||||
relativePath: DEST,
|
||||
specialFolder: "Desktop",
|
||||
logHeader: "DESKTOP",
|
||||
...defaults,
|
||||
});
|
||||
});
|
||||
|
||||
async function testShortcut({
|
||||
shortcutFile,
|
||||
relativePath,
|
||||
specialFolder,
|
||||
logHeader,
|
||||
|
||||
// Generally provided by the defaults.
|
||||
shellService,
|
||||
targetFile,
|
||||
iconFile,
|
||||
description,
|
||||
aumid,
|
||||
}) {
|
||||
// If it already exists, remove it.
|
||||
tryRemove(shortcutFile);
|
||||
|
||||
await shellService.createShortcut(
|
||||
targetFile,
|
||||
[],
|
||||
description,
|
||||
iconFile,
|
||||
0,
|
||||
aumid,
|
||||
specialFolder,
|
||||
relativePath
|
||||
);
|
||||
ok(
|
||||
shortcutFile.exists(),
|
||||
`${specialFolder}\\${relativePath}: Shortcut should exist`
|
||||
);
|
||||
ok(
|
||||
queryShortcutLog(relativePath, logHeader),
|
||||
`${specialFolder}\\${relativePath}: Shortcut log entry was added`
|
||||
);
|
||||
await shellService.deleteShortcut(specialFolder, relativePath);
|
||||
ok(
|
||||
!shortcutFile.exists(),
|
||||
`${specialFolder}\\${relativePath}: Shortcut does not exist after deleting`
|
||||
);
|
||||
ok(
|
||||
!queryShortcutLog(relativePath, logHeader),
|
||||
`${specialFolder}\\${relativePath}: Shortcut log entry was removed`
|
||||
);
|
||||
}
|
||||
|
||||
function queryShortcutLog(aShortcutName, aSection) {
|
||||
const parserFactory = Cc[
|
||||
"@mozilla.org/xpcom/ini-parser-factory;1"
|
||||
].createInstance(Ci.nsIINIParserFactory);
|
||||
|
||||
const dir = Services.dirsvc.get("UpdRootD", Ci.nsIFile).parent.parent;
|
||||
const enumerator = dir.directoryEntries;
|
||||
|
||||
for (const file of enumerator) {
|
||||
// We don't know the user's SID from JS-land, so just look at all of them.
|
||||
if (!file.path.match(/[^_]+_S[^_]*_shortcuts.ini/)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const parser = parserFactory.createINIParser(file);
|
||||
parser.QueryInterface(Ci.nsIINIParser);
|
||||
parser.QueryInterface(Ci.nsIINIParserWriter);
|
||||
|
||||
for (let i = 0; ; i++) {
|
||||
try {
|
||||
let string = parser.getString(aSection, `Shortcut${i}`);
|
||||
if (string == aShortcutName) {
|
||||
enumerator.close();
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
// The key didn't exist, stop here.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enumerator.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
function createDirectory(aFolder) {
|
||||
try {
|
||||
aFolder.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
|
||||
} catch (e) {
|
||||
if (e.result != Cr.NS_ERROR_FILE_ALREADY_EXISTS) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tryRemove(file) {
|
||||
try {
|
||||
file.remove(false);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
54
src/zen/tests/mochitests/shell/browser_doesAppNeedPin.js
Normal file
54
src/zen/tests/mochitests/shell/browser_doesAppNeedPin.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
NimbusTestUtils: "resource://testing-common/NimbusTestUtils.sys.mjs",
|
||||
});
|
||||
|
||||
let defaultValue;
|
||||
add_task(async function default_need() {
|
||||
defaultValue = await ShellService.doesAppNeedPin();
|
||||
|
||||
Assert.notStrictEqual(
|
||||
defaultValue,
|
||||
undefined,
|
||||
"Got a default app need pin value"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function remote_disable() {
|
||||
if (defaultValue === false) {
|
||||
info("Default pin already false, so nothing to test");
|
||||
return;
|
||||
}
|
||||
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: { disablePin: true, enabled: true },
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
await ShellService.doesAppNeedPin(),
|
||||
false,
|
||||
"Pinning disabled via nimbus"
|
||||
);
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
add_task(async function restore_default() {
|
||||
if (defaultValue === undefined) {
|
||||
info("No default pin value set, so nothing to test");
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.equal(
|
||||
await ShellService.doesAppNeedPin(),
|
||||
defaultValue,
|
||||
"Pinning restored to original"
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function () {
|
||||
// Test all four basic variations of the "screenshot" argument
|
||||
// when a file path is specified.
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
screenshotPath,
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
`-screenshot=${screenshotPath}`,
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"--screenshot",
|
||||
screenshotPath,
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
`--screenshot=${screenshotPath}`,
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
|
||||
// Test when the requested URL redirects
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless_redirect.html",
|
||||
"-screenshot",
|
||||
screenshotPath,
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
|
||||
// Test with additional command options
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
screenshotPath,
|
||||
"-attach-console",
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-attach-console",
|
||||
"-screenshot",
|
||||
screenshotPath,
|
||||
"-headless",
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
add_task(async function () {
|
||||
const cwdScreenshotPath = PathUtils.join(
|
||||
Services.dirsvc.get("CurWorkD", Ci.nsIFile).path,
|
||||
"screenshot.png"
|
||||
);
|
||||
|
||||
// Test variations of the "screenshot" argument when a file path
|
||||
// isn't specified.
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-screenshot",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"--screenshot",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"--screenshot",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
|
||||
// Test with additional command options
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"--screenshot",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-attach-console",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function () {
|
||||
const cwdScreenshotPath = PathUtils.join(
|
||||
Services.dirsvc.get("CurWorkD", Ci.nsIFile).path,
|
||||
"screenshot.png"
|
||||
);
|
||||
|
||||
// Test invalid URL arguments (either no argument or too many arguments).
|
||||
await testFileCreationNegative(["-screenshot"], cwdScreenshotPath);
|
||||
await testFileCreationNegative(
|
||||
[
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"http://mochi.test:8888/headless.html",
|
||||
"-screenshot",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
|
||||
// Test all four basic variations of the "window-size" argument.
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
"-window-size",
|
||||
"800",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
"-window-size=800",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
"--window-size",
|
||||
"800",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
"--window-size=800",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function () {
|
||||
const cwdScreenshotPath = PathUtils.join(
|
||||
Services.dirsvc.get("CurWorkD", Ci.nsIFile).path,
|
||||
"screenshot.png"
|
||||
);
|
||||
// Test other variations of the "window-size" argument.
|
||||
await testWindowSizePositive(800, 600);
|
||||
await testWindowSizePositive(1234);
|
||||
await testFileCreationNegative(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
"-window-size",
|
||||
"hello",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
await testFileCreationNegative(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
"-window-size",
|
||||
"800,",
|
||||
],
|
||||
cwdScreenshotPath
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function () {
|
||||
// Test cross origin iframes work.
|
||||
await testGreen(
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless_cross_origin.html",
|
||||
screenshotPath
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function () {
|
||||
// Test when the requested URL redirects
|
||||
await testFileCreationPositive(
|
||||
[
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless_redirect.html",
|
||||
"-screenshot",
|
||||
screenshotPath,
|
||||
],
|
||||
screenshotPath
|
||||
);
|
||||
});
|
||||
27
src/zen/tests/mochitests/shell/browser_processAUMID.js
Normal file
27
src/zen/tests/mochitests/shell/browser_processAUMID.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Bug 1950734 tracks how calling PinCurrentAppToTaskbarWin11
|
||||
* on MSIX may cause the process AUMID to be unnecessarily changed.
|
||||
* This test verifies that the behaviour will no longer happen
|
||||
*/
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
ShellService: "moz-src:///browser/components/shell/ShellService.sys.mjs",
|
||||
});
|
||||
|
||||
add_task(async function test_processAUMID() {
|
||||
let processAUMID = ShellService.checkCurrentProcessAUMIDForTesting();
|
||||
|
||||
// This function will trigger the relevant code paths that
|
||||
// incorrectly changes the process AUMID on MSIX, prior to
|
||||
// Bug 1950734 being fixed
|
||||
await ShellService.checkPinCurrentAppToTaskbarAsync(false);
|
||||
|
||||
is(
|
||||
processAUMID,
|
||||
ShellService.checkCurrentProcessAUMIDForTesting(),
|
||||
"The process AUMID should not be changed"
|
||||
);
|
||||
});
|
||||
239
src/zen/tests/mochitests/shell/browser_setDefaultBrowser.js
Normal file
239
src/zen/tests/mochitests/shell/browser_setDefaultBrowser.js
Normal file
@@ -0,0 +1,239 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
ASRouter: "resource:///modules/asrouter/ASRouter.sys.mjs",
|
||||
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
NimbusTestUtils: "resource://testing-common/NimbusTestUtils.sys.mjs",
|
||||
sinon: "resource://testing-common/Sinon.sys.mjs",
|
||||
});
|
||||
|
||||
const setDefaultBrowserUserChoiceStub = async () => {
|
||||
throw Components.Exception("", Cr.NS_ERROR_WDBA_NO_PROGID);
|
||||
};
|
||||
|
||||
const defaultAgentStub = sinon
|
||||
.stub(ShellService, "defaultAgent")
|
||||
.value({ setDefaultBrowserUserChoiceAsync: setDefaultBrowserUserChoiceStub });
|
||||
|
||||
const _userChoiceImpossibleTelemetryResultStub = sinon
|
||||
.stub(ShellService, "_userChoiceImpossibleTelemetryResult")
|
||||
.callsFake(() => null);
|
||||
|
||||
const userChoiceStub = sinon
|
||||
.stub(ShellService, "setAsDefaultUserChoice")
|
||||
.resolves();
|
||||
const setDefaultStub = sinon.stub();
|
||||
const shellStub = sinon
|
||||
.stub(ShellService, "shellService")
|
||||
.value({ setDefaultBrowser: setDefaultStub });
|
||||
|
||||
const sendTriggerStub = sinon.stub(ASRouter, "sendTriggerMessage");
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
let defaultUserChoice;
|
||||
add_task(async function need_user_choice() {
|
||||
await ShellService.setDefaultBrowser();
|
||||
defaultUserChoice = userChoiceStub.called;
|
||||
|
||||
Assert.notStrictEqual(
|
||||
defaultUserChoice,
|
||||
undefined,
|
||||
"Decided which default browser method to use"
|
||||
);
|
||||
Assert.equal(
|
||||
setDefaultStub.notCalled,
|
||||
defaultUserChoice,
|
||||
"Only one default behavior was used"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function remote_disable() {
|
||||
if (defaultUserChoice === false) {
|
||||
info("Default behavior already not user choice, so nothing to test");
|
||||
return;
|
||||
}
|
||||
|
||||
userChoiceStub.resetHistory();
|
||||
setDefaultStub.resetHistory();
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultBrowserUserChoice: false,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.ok(
|
||||
userChoiceStub.notCalled,
|
||||
"Set default with user choice disabled via nimbus"
|
||||
);
|
||||
Assert.ok(setDefaultStub.called, "Used plain set default instead");
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
add_task(async function restore_default() {
|
||||
if (defaultUserChoice === undefined) {
|
||||
info("No default user choice behavior set, so nothing to test");
|
||||
return;
|
||||
}
|
||||
|
||||
userChoiceStub.resetHistory();
|
||||
setDefaultStub.resetHistory();
|
||||
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.equal(
|
||||
userChoiceStub.called,
|
||||
defaultUserChoice,
|
||||
"Set default with user choice restored to original"
|
||||
);
|
||||
Assert.equal(
|
||||
setDefaultStub.notCalled,
|
||||
defaultUserChoice,
|
||||
"Plain set default behavior restored to original"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function ensure_fallback() {
|
||||
if (AppConstants.platform != "win") {
|
||||
info("Nothing to test on non-Windows");
|
||||
return;
|
||||
}
|
||||
|
||||
let userChoicePromise = Promise.resolve();
|
||||
userChoiceStub.callsFake(function (...args) {
|
||||
return (userChoicePromise = userChoiceStub.wrappedMethod.apply(this, args));
|
||||
});
|
||||
userChoiceStub.resetHistory();
|
||||
setDefaultStub.resetHistory();
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultBrowserUserChoice: true,
|
||||
setDefaultPDFHandler: false,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.ok(userChoiceStub.called, "Set default with user choice called");
|
||||
|
||||
let message = "";
|
||||
await userChoicePromise.catch(err => (message = err.message || ""));
|
||||
|
||||
Assert.ok(
|
||||
message.includes("ErrExeProgID"),
|
||||
"Set default with user choice threw an expected error"
|
||||
);
|
||||
Assert.ok(setDefaultStub.called, "Fallbacked to plain set default");
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
async function setUpNotificationTests(guidanceEnabled, oneClick) {
|
||||
sinon.reset();
|
||||
const experimentCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultGuidanceNotifications: guidanceEnabled,
|
||||
setDefaultBrowserUserChoice: oneClick,
|
||||
setDefaultBrowserUserChoiceRegRename: oneClick,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
const doCleanup = async () => {
|
||||
await experimentCleanup();
|
||||
sinon.reset();
|
||||
};
|
||||
|
||||
await ShellService.setDefaultBrowser();
|
||||
return doCleanup;
|
||||
}
|
||||
|
||||
add_task(
|
||||
async function show_notification_when_set_to_default_guidance_enabled_and_one_click_disabled() {
|
||||
if (!AppConstants.isPlatformAndVersionAtLeast("win", 10)) {
|
||||
info("Nothing to test on non-Windows or older Windows versions");
|
||||
return;
|
||||
}
|
||||
const doCleanup = await setUpNotificationTests(
|
||||
true, // guidance enabled
|
||||
false // one-click disabled
|
||||
);
|
||||
|
||||
Assert.ok(setDefaultStub.called, "Fallback method used to set default");
|
||||
|
||||
Assert.equal(
|
||||
sendTriggerStub.firstCall.args[0].id,
|
||||
"deeplinkedToWindowsSettingsUI",
|
||||
`Set to default guidance message trigger was sent`
|
||||
);
|
||||
|
||||
await doCleanup();
|
||||
}
|
||||
);
|
||||
|
||||
add_task(
|
||||
async function do_not_show_notification_when_set_to_default_guidance_disabled_and_one_click_enabled() {
|
||||
if (!AppConstants.isPlatformAndVersionAtLeast("win", 10)) {
|
||||
info("Nothing to test on non-Windows or older Windows versions");
|
||||
return;
|
||||
}
|
||||
|
||||
const doCleanup = await setUpNotificationTests(
|
||||
false, // guidance disabled
|
||||
true // one-click enabled
|
||||
);
|
||||
|
||||
Assert.ok(setDefaultStub.notCalled, "Fallback method not called");
|
||||
|
||||
Assert.equal(
|
||||
sendTriggerStub.callCount,
|
||||
0,
|
||||
`Set to default guidance message trigger was not sent`
|
||||
);
|
||||
|
||||
await doCleanup();
|
||||
}
|
||||
);
|
||||
|
||||
add_task(
|
||||
async function do_not_show_notification_when_set_to_default_guidance_enabled_and_one_click_enabled() {
|
||||
if (!AppConstants.isPlatformAndVersionAtLeast("win", 10)) {
|
||||
info("Nothing to test on non-Windows or older Windows versions");
|
||||
return;
|
||||
}
|
||||
|
||||
const doCleanup = await setUpNotificationTests(
|
||||
true, // guidance enabled
|
||||
true // one-click enabled
|
||||
);
|
||||
|
||||
Assert.ok(setDefaultStub.notCalled, "Fallback method not called");
|
||||
Assert.equal(
|
||||
sendTriggerStub.callCount,
|
||||
0,
|
||||
`Set to default guidance message trigger was not sent`
|
||||
);
|
||||
await doCleanup();
|
||||
}
|
||||
);
|
||||
279
src/zen/tests/mochitests/shell/browser_setDefaultPDFHandler.js
Normal file
279
src/zen/tests/mochitests/shell/browser_setDefaultPDFHandler.js
Normal file
@@ -0,0 +1,279 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
NimbusTestUtils: "resource://testing-common/NimbusTestUtils.sys.mjs",
|
||||
sinon: "resource://testing-common/Sinon.sys.mjs",
|
||||
});
|
||||
|
||||
const setDefaultBrowserUserChoiceStub = sinon.stub();
|
||||
const setDefaultExtensionHandlersUserChoiceStub = sinon
|
||||
.stub()
|
||||
.callsFake(() => Promise.resolve());
|
||||
|
||||
const defaultAgentStub = sinon.stub(ShellService, "defaultAgent").value({
|
||||
setDefaultBrowserUserChoiceAsync: setDefaultBrowserUserChoiceStub,
|
||||
setDefaultExtensionHandlersUserChoice:
|
||||
setDefaultExtensionHandlersUserChoiceStub,
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"XreDirProvider",
|
||||
"@mozilla.org/xre/directory-provider;1",
|
||||
Ci.nsIXREDirProvider
|
||||
);
|
||||
|
||||
const _userChoiceImpossibleTelemetryResultStub = sinon
|
||||
.stub(ShellService, "_userChoiceImpossibleTelemetryResult")
|
||||
.callsFake(() => null);
|
||||
|
||||
// Ensure we don't fall back to a real implementation.
|
||||
const setDefaultStub = sinon.stub();
|
||||
// We'll dynamically update this as needed during the tests.
|
||||
const queryCurrentDefaultHandlerForStub = sinon.stub();
|
||||
const shellStub = sinon.stub(ShellService, "shellService").value({
|
||||
setDefaultBrowser: setDefaultStub,
|
||||
queryCurrentDefaultHandlerFor: queryCurrentDefaultHandlerForStub,
|
||||
});
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
defaultAgentStub.restore();
|
||||
_userChoiceImpossibleTelemetryResultStub.restore();
|
||||
shellStub.restore();
|
||||
});
|
||||
|
||||
add_task(async function ready() {
|
||||
await ExperimentAPI.ready();
|
||||
});
|
||||
|
||||
// Everything here is Windows.
|
||||
Assert.equal(AppConstants.platform, "win", "Platform is Windows");
|
||||
|
||||
add_task(async function remoteEnableWithPDF() {
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultBrowserUserChoice: true,
|
||||
setDefaultPDFHandlerOnlyReplaceBrowsers: false,
|
||||
setDefaultPDFHandler: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultBrowserUserChoice"),
|
||||
true
|
||||
);
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultPDFHandler"),
|
||||
true
|
||||
);
|
||||
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
const aumi = XreDirProvider.getInstallHash();
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.called);
|
||||
Assert.deepEqual(setDefaultBrowserUserChoiceStub.firstCall.args, [
|
||||
aumi,
|
||||
[".pdf", "FirefoxPDF"],
|
||||
]);
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
add_task(async function remoteEnableWithPDF_testOnlyReplaceBrowsers() {
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultBrowserUserChoice: true,
|
||||
setDefaultPDFHandlerOnlyReplaceBrowsers: true,
|
||||
setDefaultPDFHandler: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultBrowserUserChoice"),
|
||||
true
|
||||
);
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultPDFHandler"),
|
||||
true
|
||||
);
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable(
|
||||
"setDefaultPDFHandlerOnlyReplaceBrowsers"
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
const aumi = XreDirProvider.getInstallHash();
|
||||
|
||||
// We'll take the default from a missing association or a known browser.
|
||||
for (let progId of ["", "MSEdgePDF"]) {
|
||||
queryCurrentDefaultHandlerForStub.callsFake(() => progId);
|
||||
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.called);
|
||||
Assert.deepEqual(
|
||||
setDefaultBrowserUserChoiceStub.firstCall.args,
|
||||
[aumi, [".pdf", "FirefoxPDF"]],
|
||||
`Will take default from missing association or known browser with ProgID '${progId}'`
|
||||
);
|
||||
}
|
||||
|
||||
// But not from a non-browser.
|
||||
queryCurrentDefaultHandlerForStub.callsFake(() => "Acrobat.Document.DC");
|
||||
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.called);
|
||||
Assert.deepEqual(
|
||||
setDefaultBrowserUserChoiceStub.firstCall.args,
|
||||
[aumi, []],
|
||||
`Will not take default from non-browser`
|
||||
);
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
add_task(async function remoteEnableWithoutPDF() {
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultBrowserUserChoice: true,
|
||||
setDefaultPDFHandler: false,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultBrowserUserChoice"),
|
||||
true
|
||||
);
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultPDFHandler"),
|
||||
false
|
||||
);
|
||||
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
const aumi = XreDirProvider.getInstallHash();
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.called);
|
||||
Assert.deepEqual(setDefaultBrowserUserChoiceStub.firstCall.args, [aumi, []]);
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
add_task(async function remoteDisable() {
|
||||
let doCleanup = await NimbusTestUtils.enrollWithFeatureConfig(
|
||||
{
|
||||
featureId: NimbusFeatures.shellService.featureId,
|
||||
value: {
|
||||
setDefaultBrowserUserChoice: false,
|
||||
setDefaultPDFHandler: true,
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
{ isRollout: true }
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultBrowserUserChoice"),
|
||||
false
|
||||
);
|
||||
Assert.equal(
|
||||
NimbusFeatures.shellService.getVariable("setDefaultPDFHandler"),
|
||||
true
|
||||
);
|
||||
|
||||
setDefaultBrowserUserChoiceStub.resetHistory();
|
||||
await ShellService.setDefaultBrowser();
|
||||
|
||||
Assert.ok(setDefaultBrowserUserChoiceStub.notCalled);
|
||||
Assert.ok(setDefaultStub.called);
|
||||
|
||||
await doCleanup();
|
||||
});
|
||||
|
||||
add_task(async function test_setAsDefaultPDFHandler_knownBrowser() {
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
||||
const aumi = XreDirProvider.getInstallHash();
|
||||
const expectedArguments = [aumi, [".pdf", "FirefoxPDF"]];
|
||||
|
||||
try {
|
||||
const pdfHandlerResult = { registered: true, knownBrowser: true };
|
||||
sandbox
|
||||
.stub(ShellService, "getDefaultPDFHandler")
|
||||
.returns(pdfHandlerResult);
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(true) when knownBrowser = true");
|
||||
ShellService.setAsDefaultPDFHandler(true);
|
||||
Assert.ok(
|
||||
setDefaultExtensionHandlersUserChoiceStub.called,
|
||||
"Called default browser agent"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
setDefaultExtensionHandlersUserChoiceStub.firstCall.args,
|
||||
expectedArguments,
|
||||
"Called default browser agent with expected arguments"
|
||||
);
|
||||
setDefaultExtensionHandlersUserChoiceStub.resetHistory();
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(false) when knownBrowser = true");
|
||||
ShellService.setAsDefaultPDFHandler(false);
|
||||
Assert.ok(
|
||||
setDefaultExtensionHandlersUserChoiceStub.called,
|
||||
"Called default browser agent"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
setDefaultExtensionHandlersUserChoiceStub.firstCall.args,
|
||||
expectedArguments,
|
||||
"Called default browser agent with expected arguments"
|
||||
);
|
||||
setDefaultExtensionHandlersUserChoiceStub.resetHistory();
|
||||
|
||||
pdfHandlerResult.knownBrowser = false;
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(true) when knownBrowser = false");
|
||||
ShellService.setAsDefaultPDFHandler(true);
|
||||
Assert.ok(
|
||||
setDefaultExtensionHandlersUserChoiceStub.notCalled,
|
||||
"Did not call default browser agent"
|
||||
);
|
||||
setDefaultExtensionHandlersUserChoiceStub.resetHistory();
|
||||
|
||||
info("Testing setAsDefaultPDFHandler(false) when knownBrowser = false");
|
||||
ShellService.setAsDefaultPDFHandler(false);
|
||||
Assert.ok(
|
||||
setDefaultExtensionHandlersUserChoiceStub.called,
|
||||
"Called default browser agent"
|
||||
);
|
||||
Assert.deepEqual(
|
||||
setDefaultExtensionHandlersUserChoiceStub.firstCall.args,
|
||||
expectedArguments,
|
||||
"Called default browser agent with expected arguments"
|
||||
);
|
||||
setDefaultExtensionHandlersUserChoiceStub.resetHistory();
|
||||
} finally {
|
||||
sandbox.restore();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Check whether the preview image for setDesktopBackground is rendered
|
||||
* correctly, without stretching
|
||||
*/
|
||||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["test.wait300msAfterTabSwitch", true]],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "about:logo",
|
||||
},
|
||||
async () => {
|
||||
const dialogLoad = BrowserTestUtils.domWindowOpened(null, async win => {
|
||||
await BrowserTestUtils.waitForEvent(win, "load");
|
||||
Assert.equal(
|
||||
win.document.documentElement.getAttribute("windowtype"),
|
||||
"Shell:SetDesktopBackground",
|
||||
"Opened correct window"
|
||||
);
|
||||
return true;
|
||||
});
|
||||
|
||||
const image = content.document.images[0];
|
||||
EventUtils.synthesizeMouseAtCenter(image, { type: "contextmenu" });
|
||||
|
||||
const menu = document.getElementById("contentAreaContextMenu");
|
||||
await BrowserTestUtils.waitForPopupEvent(menu, "shown");
|
||||
const menuClosed = BrowserTestUtils.waitForPopupEvent(menu, "hidden");
|
||||
|
||||
const menuItem = document.getElementById("context-setDesktopBackground");
|
||||
try {
|
||||
menu.activateItem(menuItem);
|
||||
} catch (ex) {
|
||||
ok(
|
||||
menuItem.hidden,
|
||||
"should only fail to activate when menu item is hidden"
|
||||
);
|
||||
ok(
|
||||
!ShellService.canSetDesktopBackground,
|
||||
"Should only hide when not able to set the desktop background"
|
||||
);
|
||||
is(
|
||||
AppConstants.platform,
|
||||
"linux",
|
||||
"Should always be able to set desktop background on non-linux platforms"
|
||||
);
|
||||
todo(false, "Skipping test on this configuration");
|
||||
|
||||
menu.hidePopup();
|
||||
await menuClosed;
|
||||
return;
|
||||
}
|
||||
|
||||
await menuClosed;
|
||||
|
||||
const win = await dialogLoad;
|
||||
|
||||
/* setDesktopBackground.js does a setTimeout to wait for correct
|
||||
dimensions. If we don't wait here we could read the preview dimensions
|
||||
before they're changed to match the screen */
|
||||
await TestUtils.waitForTick();
|
||||
|
||||
const canvas = win.document.getElementById("screen");
|
||||
const screenRatio = screen.width / screen.height;
|
||||
const previewRatio = canvas.clientWidth / canvas.clientHeight;
|
||||
|
||||
info(`Screen dimensions are ${screen.width}x${screen.height}`);
|
||||
info(`Screen's raw ratio is ${screenRatio}`);
|
||||
info(
|
||||
`Preview dimensions are ${canvas.clientWidth}x${canvas.clientHeight}`
|
||||
);
|
||||
info(`Preview's raw ratio is ${previewRatio}`);
|
||||
|
||||
Assert.ok(
|
||||
previewRatio < screenRatio + 0.01 && previewRatio > screenRatio - 0.01,
|
||||
"Preview's aspect ratio is within ±.01 of screen's"
|
||||
);
|
||||
|
||||
win.close();
|
||||
|
||||
await menuClosed;
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "Windows11LimitedAccessFeatures.h"
|
||||
#include "WinUtils.h"
|
||||
|
||||
TEST(LimitedAccessFeature, VerifyGeneratedInfo)
|
||||
{
|
||||
// If running on MSIX we have no guarantee that the
|
||||
// generated LAF info will match the known values.
|
||||
if (mozilla::widget::WinUtils::HasPackageIdentity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LimitedAccessFeatureInfo knownLafInfo = {
|
||||
// Win11LimitedAccessFeatureType::Taskbar
|
||||
"Win11LimitedAccessFeatureType::Taskbar"_ns, // debugName
|
||||
u"com.microsoft.windows.taskbar.pin"_ns, // feature
|
||||
u"kRFiWpEK5uS6PMJZKmR7MQ=="_ns, // token
|
||||
u"pcsmm0jrprpb2 has registered their use of "_ns // attestation
|
||||
u"com.microsoft.windows.taskbar.pin with Microsoft and agrees to the "_ns
|
||||
u"terms "_ns
|
||||
u"of use."_ns};
|
||||
|
||||
auto generatedLafInfoResult = GenerateLimitedAccessFeatureInfo(
|
||||
"Win11LimitedAccessFeatureType::Taskbar"_ns,
|
||||
u"com.microsoft.windows.taskbar.pin"_ns);
|
||||
ASSERT_TRUE(generatedLafInfoResult.isOk());
|
||||
LimitedAccessFeatureInfo generatedLafInfo = generatedLafInfoResult.unwrap();
|
||||
|
||||
// Check for equality between generated values and known good values
|
||||
ASSERT_TRUE(knownLafInfo.debugName.Equals(generatedLafInfo.debugName));
|
||||
ASSERT_TRUE(knownLafInfo.feature.Equals(generatedLafInfo.feature));
|
||||
ASSERT_TRUE(knownLafInfo.token.Equals(generatedLafInfo.token));
|
||||
ASSERT_TRUE(knownLafInfo.attestation.Equals(generatedLafInfo.attestation));
|
||||
}
|
||||
54
src/zen/tests/mochitests/shell/gtest/ShellLinkTests.cpp
Normal file
54
src/zen/tests/mochitests/shell/gtest/ShellLinkTests.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsWindowsShellServiceInternal.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
TEST(ShellLink, NarrowCharacterArguments)
|
||||
{
|
||||
nsCOMPtr<nsIFile> exe;
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(exe));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
RefPtr<IShellLinkW> link;
|
||||
rv = CreateShellLinkObject(exe, {u"test"_ns}, u"test"_ns, exe, 0, u"aumid"_ns,
|
||||
getter_AddRefs(link));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_TRUE(link != nullptr);
|
||||
|
||||
std::wstring testArgs = L"\"test\" ";
|
||||
|
||||
wchar_t resultArgs[sizeof(testArgs)];
|
||||
HRESULT hr = link->GetArguments(resultArgs, sizeof(resultArgs));
|
||||
ASSERT_TRUE(SUCCEEDED(hr));
|
||||
|
||||
ASSERT_TRUE(testArgs == resultArgs);
|
||||
}
|
||||
|
||||
TEST(ShellLink, WideCharacterArguments)
|
||||
{
|
||||
nsCOMPtr<nsIFile> exe;
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(exe));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
RefPtr<IShellLinkW> link;
|
||||
rv = CreateShellLinkObject(exe, {u"Test\\テスト用アカウント\\Test"_ns},
|
||||
u"test"_ns, exe, 0, u"aumid"_ns,
|
||||
getter_AddRefs(link));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_TRUE(link != nullptr);
|
||||
|
||||
std::wstring testArgs = L"\"Test\\テスト用アカウント\\Test\" ";
|
||||
|
||||
wchar_t resultArgs[sizeof(testArgs)];
|
||||
HRESULT hr = link->GetArguments(resultArgs, sizeof(resultArgs));
|
||||
ASSERT_TRUE(SUCCEEDED(hr));
|
||||
|
||||
ASSERT_TRUE(testArgs == resultArgs);
|
||||
}
|
||||
15
src/zen/tests/mochitests/shell/gtest/moz.build
Normal file
15
src/zen/tests/mochitests/shell/gtest/moz.build
Normal file
@@ -0,0 +1,15 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
|
||||
LOCAL_INCLUDES += ["/browser/components/shell"]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"LimitedAccessFeatureTests.cpp",
|
||||
"ShellLinkTests.cpp",
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = "xul-gtest"
|
||||
159
src/zen/tests/mochitests/shell/head.js
Normal file
159
src/zen/tests/mochitests/shell/head.js
Normal file
@@ -0,0 +1,159 @@
|
||||
"use strict";
|
||||
|
||||
const { Subprocess } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/Subprocess.sys.mjs"
|
||||
);
|
||||
|
||||
const TEMP_DIR = Services.dirsvc.get("TmpD", Ci.nsIFile).path;
|
||||
|
||||
const screenshotPath = PathUtils.join(TEMP_DIR, "headless_test_screenshot.png");
|
||||
|
||||
async function runFirefox(args) {
|
||||
const XRE_EXECUTABLE_FILE = "XREExeF";
|
||||
const firefoxExe = Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsIFile).path;
|
||||
const NS_APP_PREFS_50_FILE = "PrefF";
|
||||
const mochiPrefsFile = Services.dirsvc.get(NS_APP_PREFS_50_FILE, Ci.nsIFile);
|
||||
const mochiPrefsPath = mochiPrefsFile.path;
|
||||
const mochiPrefsName = mochiPrefsFile.leafName;
|
||||
const profilePath = PathUtils.join(
|
||||
TEMP_DIR,
|
||||
"headless_test_screenshot_profile"
|
||||
);
|
||||
const prefsPath = PathUtils.join(profilePath, mochiPrefsName);
|
||||
const firefoxArgs = ["-profile", profilePath];
|
||||
|
||||
await IOUtils.makeDirectory(profilePath);
|
||||
await IOUtils.copy(mochiPrefsPath, prefsPath);
|
||||
let proc = await Subprocess.call({
|
||||
command: firefoxExe,
|
||||
arguments: firefoxArgs.concat(args),
|
||||
// Disable leak detection to avoid intermittent failure bug 1331152.
|
||||
environmentAppend: true,
|
||||
environment: {
|
||||
ASAN_OPTIONS:
|
||||
"detect_leaks=0:quarantine_size=50331648:malloc_context_size=5",
|
||||
// Don't enable Marionette.
|
||||
MOZ_MARIONETTE: null,
|
||||
},
|
||||
});
|
||||
let stdout;
|
||||
while ((stdout = await proc.stdout.readString())) {
|
||||
dump(`>>> ${stdout}\n`);
|
||||
}
|
||||
let { exitCode } = await proc.wait();
|
||||
is(exitCode, 0, "Firefox process should exit with code 0");
|
||||
await IOUtils.remove(profilePath, { recursive: true });
|
||||
}
|
||||
|
||||
async function testFileCreationPositive(args, path) {
|
||||
await runFirefox(args);
|
||||
|
||||
let saved = IOUtils.exists(path);
|
||||
ok(saved, "A screenshot should be saved as " + path);
|
||||
if (!saved) {
|
||||
return;
|
||||
}
|
||||
|
||||
let info = await IOUtils.stat(path);
|
||||
Assert.greater(info.size, 0, "Screenshot should not be an empty file");
|
||||
await IOUtils.remove(path);
|
||||
}
|
||||
|
||||
async function testFileCreationNegative(args, path) {
|
||||
await runFirefox(args);
|
||||
|
||||
let saved = await IOUtils.exists(path);
|
||||
ok(!saved, "A screenshot should not be saved");
|
||||
await IOUtils.remove(path);
|
||||
}
|
||||
|
||||
async function testWindowSizePositive(width, height) {
|
||||
let size = String(width);
|
||||
if (height) {
|
||||
size += "," + height;
|
||||
}
|
||||
|
||||
await runFirefox([
|
||||
"-url",
|
||||
"http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
|
||||
"-screenshot",
|
||||
screenshotPath,
|
||||
"-window-size",
|
||||
size,
|
||||
]);
|
||||
|
||||
let saved = await IOUtils.exists(screenshotPath);
|
||||
ok(saved, "A screenshot should be saved in the tmp directory");
|
||||
if (!saved) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = await IOUtils.read(screenshotPath);
|
||||
await new Promise(resolve => {
|
||||
let blob = new Blob([data], { type: "image/png" });
|
||||
let reader = new FileReader();
|
||||
reader.onloadend = function () {
|
||||
let screenshot = new Image();
|
||||
screenshot.onload = function () {
|
||||
is(
|
||||
screenshot.width,
|
||||
width,
|
||||
"Screenshot should be " + width + " pixels wide"
|
||||
);
|
||||
if (height) {
|
||||
is(
|
||||
screenshot.height,
|
||||
height,
|
||||
"Screenshot should be " + height + " pixels tall"
|
||||
);
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
screenshot.src = reader.result;
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
await IOUtils.remove(screenshotPath);
|
||||
}
|
||||
|
||||
async function testGreen(url, path) {
|
||||
await runFirefox(["-url", url, `--screenshot=${path}`]);
|
||||
|
||||
let saved = await IOUtils.exists(path);
|
||||
ok(saved, "A screenshot should be saved in the tmp directory");
|
||||
if (!saved) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = await IOUtils.read(path);
|
||||
let image = await new Promise(resolve => {
|
||||
let blob = new Blob([data], { type: "image/png" });
|
||||
let reader = new FileReader();
|
||||
reader.onloadend = function () {
|
||||
let screenshot = new Image();
|
||||
screenshot.onload = function () {
|
||||
resolve(screenshot);
|
||||
};
|
||||
screenshot.src = reader.result;
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
let canvas = document.createElement("canvas");
|
||||
canvas.width = image.naturalWidth;
|
||||
canvas.height = image.naturalHeight;
|
||||
let ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(image, 0, 0);
|
||||
let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
let rgba = imageData.data;
|
||||
|
||||
let found = false;
|
||||
for (let i = 0; i < rgba.length; i += 4) {
|
||||
if (rgba[i] === 0 && rgba[i + 1] === 255 && rgba[i + 2] === 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ok(found, "There should be a green pixel in the screenshot.");
|
||||
|
||||
await IOUtils.remove(path);
|
||||
}
|
||||
6
src/zen/tests/mochitests/shell/headless.html
Normal file
6
src/zen/tests/mochitests/shell/headless.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<head><meta content="text/html; charset=utf-8" http-equiv="Content-Type"></head>
|
||||
<body style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 255)">
|
||||
Hi
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<head><meta content="text/html; charset=utf-8" http-equiv="Content-Type"></head>
|
||||
<body>
|
||||
<iframe width="300" height="200" src="http://example.com/browser/browser/components/shell/test/headless_iframe.html"></iframe>
|
||||
Hi
|
||||
</body>
|
||||
</html>
|
||||
6
src/zen/tests/mochitests/shell/headless_iframe.html
Normal file
6
src/zen/tests/mochitests/shell/headless_iframe.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<head><meta content="text/html; charset=utf-8" http-equiv="Content-Type"></head>
|
||||
<body style="background-color: rgb(0, 255, 0);">
|
||||
Hi
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,2 @@
|
||||
HTTP 302 Moved Temporarily
|
||||
Location: headless.html
|
||||
168
src/zen/tests/mochitests/shell/mac_desktop_image.py
Executable file
168
src/zen/tests/mochitests/shell/mac_desktop_image.py
Executable file
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/python
|
||||
# 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/. */
|
||||
|
||||
"""
|
||||
mac_desktop_image.py
|
||||
|
||||
Mac-specific utility to get/set the desktop background image or check that
|
||||
the current background image path matches a provided path.
|
||||
|
||||
Depends on Objective-C python binding imports which are in the python import
|
||||
paths by default when using macOS's /usr/bin/python.
|
||||
|
||||
Includes generous amount of logging to aid debugging for use in automated tests.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
#
|
||||
# These Objective-C bindings imports are included in the import path by default
|
||||
# for the Mac-bundled python installed in /usr/bin/python. They're needed to
|
||||
# call the Objective-C API's to set and retrieve the current desktop background
|
||||
# image.
|
||||
#
|
||||
from AppKit import NSScreen, NSWorkspace
|
||||
from Cocoa import NSURL
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Utility to print, set, or "
|
||||
+ "check the path to image being used as "
|
||||
+ "the desktop background image. By "
|
||||
+ "default, prints the path to the "
|
||||
+ "current desktop background image."
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
help="print verbose debugging information",
|
||||
default=False,
|
||||
)
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument(
|
||||
"-s",
|
||||
"--set-background-image",
|
||||
dest="newBackgroundImagePath",
|
||||
required=False,
|
||||
help="path to the new background image to set. A zero "
|
||||
+ "exit code indicates no errors occurred.",
|
||||
default=None,
|
||||
)
|
||||
group.add_argument(
|
||||
"-c",
|
||||
"--check-background-image",
|
||||
dest="checkBackgroundImagePath",
|
||||
required=False,
|
||||
help="check if the provided background image path "
|
||||
+ "matches the provided path. A zero exit code "
|
||||
+ "indicates the paths match.",
|
||||
default=None,
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Using logging for verbose output
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
else:
|
||||
logging.basicConfig(level=logging.CRITICAL)
|
||||
logger = logging.getLogger("desktopImage")
|
||||
|
||||
# Print what we're going to do
|
||||
if args.checkBackgroundImagePath is not None:
|
||||
logger.debug(
|
||||
"checking provided desktop image %s matches current "
|
||||
"image" % args.checkBackgroundImagePath
|
||||
)
|
||||
elif args.newBackgroundImagePath is not None:
|
||||
logger.debug("setting image to %s " % args.newBackgroundImagePath)
|
||||
else:
|
||||
logger.debug("retrieving desktop image path")
|
||||
|
||||
focussedScreen = NSScreen.mainScreen()
|
||||
if not focussedScreen:
|
||||
raise RuntimeError("mainScreen error")
|
||||
|
||||
ws = NSWorkspace.sharedWorkspace()
|
||||
if not ws:
|
||||
raise RuntimeError("sharedWorkspace error")
|
||||
|
||||
# If we're just checking the image path, check it and then return.
|
||||
# A successful exit code (0) indicates the paths match.
|
||||
if args.checkBackgroundImagePath is not None:
|
||||
# Get existing desktop image path and resolve it
|
||||
existingImageURL = getCurrentDesktopImageURL(focussedScreen, ws, logger)
|
||||
existingImagePath = existingImageURL.path()
|
||||
existingImagePathReal = os.path.realpath(existingImagePath)
|
||||
logger.debug("existing desktop image: %s" % existingImagePath)
|
||||
logger.debug("existing desktop image realpath: %s" % existingImagePath)
|
||||
|
||||
# Resolve the path we're going to check
|
||||
checkImagePathReal = os.path.realpath(args.checkBackgroundImagePath)
|
||||
logger.debug("check desktop image: %s" % args.checkBackgroundImagePath)
|
||||
logger.debug("check desktop image realpath: %s" % checkImagePathReal)
|
||||
|
||||
if existingImagePathReal == checkImagePathReal:
|
||||
print("desktop image path matches provided path")
|
||||
return True
|
||||
|
||||
print("desktop image path does NOT match provided path")
|
||||
return False
|
||||
|
||||
# Log the current desktop image
|
||||
if args.verbose:
|
||||
existingImageURL = getCurrentDesktopImageURL(focussedScreen, ws, logger)
|
||||
logger.debug("existing desktop image: %s" % existingImageURL.path())
|
||||
|
||||
# Set the desktop image
|
||||
if args.newBackgroundImagePath is not None:
|
||||
newImagePath = args.newBackgroundImagePath
|
||||
if not os.path.exists(newImagePath):
|
||||
logger.critical("%s does not exist" % newImagePath)
|
||||
return False
|
||||
if not os.access(newImagePath, os.R_OK):
|
||||
logger.critical("%s is not readable" % newImagePath)
|
||||
return False
|
||||
|
||||
logger.debug("new desktop image to set: %s" % newImagePath)
|
||||
newImageURL = NSURL.fileURLWithPath_(newImagePath)
|
||||
logger.debug("new desktop image URL to set: %s" % newImageURL)
|
||||
|
||||
status = False
|
||||
(status, error) = ws.setDesktopImageURL_forScreen_options_error_(
|
||||
newImageURL, focussedScreen, None, None
|
||||
)
|
||||
if not status:
|
||||
raise RuntimeError("setDesktopImageURL error")
|
||||
|
||||
# Print the current desktop image
|
||||
imageURL = getCurrentDesktopImageURL(focussedScreen, ws, logger)
|
||||
imagePath = imageURL.path()
|
||||
imagePathReal = os.path.realpath(imagePath)
|
||||
logger.debug("updated desktop image URL: %s" % imageURL)
|
||||
logger.debug("updated desktop image path: %s" % imagePath)
|
||||
logger.debug("updated desktop image path (resolved): %s" % imagePathReal)
|
||||
print(imagePathReal)
|
||||
return True
|
||||
|
||||
|
||||
def getCurrentDesktopImageURL(focussedScreen, workspace, logger):
|
||||
imageURL = workspace.desktopImageURLForScreen_(focussedScreen)
|
||||
if not imageURL:
|
||||
raise RuntimeError("desktopImageURLForScreen returned invalid URL")
|
||||
if not imageURL.isFileURL():
|
||||
logger.warning("desktop image URL is not a file URL")
|
||||
return imageURL
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not main():
|
||||
sys.exit(1)
|
||||
else:
|
||||
sys.exit(0)
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test the macOS ShowSecurityPreferences shell service method.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// eslint-disable-next-line mozilla/no-redeclare-with-import-autofix
|
||||
const { AppConstants } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/AppConstants.sys.mjs"
|
||||
);
|
||||
|
||||
function killSystemPreferences() {
|
||||
let killallFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
killallFile.initWithPath("/usr/bin/killall");
|
||||
let sysPrefsArg = ["System Preferences"];
|
||||
if (AppConstants.isPlatformAndVersionAtLeast("macosx", 22)) {
|
||||
sysPrefsArg = ["System Settings"];
|
||||
}
|
||||
let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
|
||||
process.init(killallFile);
|
||||
process.run(true, sysPrefsArg, 1);
|
||||
return process.exitValue;
|
||||
}
|
||||
|
||||
add_setup(async function () {
|
||||
info("Ensure System Preferences isn't already running");
|
||||
killSystemPreferences();
|
||||
});
|
||||
|
||||
add_task(async function test_prefsOpen() {
|
||||
let shellSvc = Cc["@mozilla.org/browser/shell-service;1"].getService(
|
||||
Ci.nsIMacShellService
|
||||
);
|
||||
shellSvc.showSecurityPreferences("Privacy_AllFiles");
|
||||
|
||||
equal(killSystemPreferences(), 0, "Ensure System Preferences was started");
|
||||
});
|
||||
7
src/zen/tests/mochitests/shell/unit/xpcshell.toml
Normal file
7
src/zen/tests/mochitests/shell/unit/xpcshell.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[DEFAULT]
|
||||
run-if = ["os != 'android'"]
|
||||
firefox-appdir = "browser"
|
||||
tags = "os_integration"
|
||||
|
||||
["test_macOS_showSecurityPreferences.js"]
|
||||
run-if = ["os == 'mac'"]
|
||||
17
src/zen/tests/mochitests/tooltiptext/browser.toml
Normal file
17
src/zen/tests/mochitests/tooltiptext/browser.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[DEFAULT]
|
||||
|
||||
["browser_bug329212.js"]
|
||||
support-files = ["title_test.svg"]
|
||||
|
||||
["browser_bug331772_xul_tooltiptext_in_html.js"]
|
||||
support-files = ["xul_tooltiptext.xhtml"]
|
||||
|
||||
["browser_bug561623.js"]
|
||||
|
||||
["browser_bug581947.js"]
|
||||
|
||||
["browser_input_file_tooltips.js"]
|
||||
|
||||
["browser_nac_tooltip.js"]
|
||||
|
||||
["browser_shadow_dom_tooltip.js"]
|
||||
48
src/zen/tests/mochitests/tooltiptext/browser_bug329212.js
Normal file
48
src/zen/tests/mochitests/tooltiptext/browser_bug329212.js
Normal file
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "http://mochi.test:8888/browser/toolkit/components/tooltiptext/tests/title_test.svg",
|
||||
},
|
||||
async function (browser) {
|
||||
await SpecialPowers.spawn(browser, [""], function () {
|
||||
let tttp = Cc[
|
||||
"@mozilla.org/embedcomp/default-tooltiptextprovider;1"
|
||||
].getService(Ci.nsITooltipTextProvider);
|
||||
function checkElement(id, expectedTooltipText) {
|
||||
let el = content.document.getElementById(id);
|
||||
let textObj = {};
|
||||
let shouldHaveTooltip = expectedTooltipText !== null;
|
||||
is(
|
||||
tttp.getNodeText(el, textObj, {}),
|
||||
shouldHaveTooltip,
|
||||
"element " +
|
||||
id +
|
||||
" should " +
|
||||
(shouldHaveTooltip ? "" : "not ") +
|
||||
"have a tooltip"
|
||||
);
|
||||
if (shouldHaveTooltip) {
|
||||
is(
|
||||
textObj.value,
|
||||
expectedTooltipText,
|
||||
"element " + id + " should have the right tooltip text"
|
||||
);
|
||||
}
|
||||
}
|
||||
checkElement("svg1", "This is a non-root SVG element title");
|
||||
checkElement("text1", "\n\n\n This is a title\n\n ");
|
||||
checkElement("text2", null);
|
||||
checkElement("text3", null);
|
||||
checkElement("link1", "\n This is a title\n ");
|
||||
checkElement("text4", "\n This is a title\n ");
|
||||
checkElement("link2", null);
|
||||
checkElement("link3", "This is an xlink:title attribute");
|
||||
checkElement("link4", "This is an xlink:title attribute");
|
||||
checkElement("text5", null);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Tests that the tooltiptext attribute is used for XUL elements in an HTML doc.
|
||||
*/
|
||||
add_task(async function () {
|
||||
await SpecialPowers.pushPermissions([
|
||||
{ type: "allowXULXBL", allow: true, context: "http://mochi.test:8888" },
|
||||
]);
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "http://mochi.test:8888/browser/toolkit/components/tooltiptext/tests/xul_tooltiptext.xhtml",
|
||||
},
|
||||
async function (browser) {
|
||||
await SpecialPowers.spawn(browser, [""], function () {
|
||||
let textObj = {};
|
||||
let tttp = Cc[
|
||||
"@mozilla.org/embedcomp/default-tooltiptextprovider;1"
|
||||
].getService(Ci.nsITooltipTextProvider);
|
||||
let xulToolbarButton =
|
||||
content.document.getElementById("xulToolbarButton");
|
||||
ok(
|
||||
tttp.getNodeText(xulToolbarButton, textObj, {}),
|
||||
"should get tooltiptext"
|
||||
);
|
||||
is(textObj.value, "XUL tooltiptext");
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
33
src/zen/tests/mochitests/tooltiptext/browser_bug561623.js
Normal file
33
src/zen/tests/mochitests/tooltiptext/browser_bug561623.js
Normal file
@@ -0,0 +1,33 @@
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "data:text/html,<!DOCTYPE html><html><body><input id='i'></body></html>",
|
||||
},
|
||||
async function (browser) {
|
||||
await SpecialPowers.spawn(browser, [""], function () {
|
||||
let tttp = Cc[
|
||||
"@mozilla.org/embedcomp/default-tooltiptextprovider;1"
|
||||
].getService(Ci.nsITooltipTextProvider);
|
||||
let i = content.document.getElementById("i");
|
||||
|
||||
ok(
|
||||
!tttp.getNodeText(i, {}, {}),
|
||||
"No tooltip should be shown when @title is null"
|
||||
);
|
||||
|
||||
i.title = "foo";
|
||||
ok(
|
||||
tttp.getNodeText(i, {}, {}),
|
||||
"A tooltip should be shown when @title is not the empty string"
|
||||
);
|
||||
|
||||
i.pattern = "bar";
|
||||
ok(
|
||||
tttp.getNodeText(i, {}, {}),
|
||||
"A tooltip should be shown when @title is not the empty string"
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
107
src/zen/tests/mochitests/tooltiptext/browser_bug581947.js
Normal file
107
src/zen/tests/mochitests/tooltiptext/browser_bug581947.js
Normal file
@@ -0,0 +1,107 @@
|
||||
function check(aBrowser, aElementName, aBarred, aType) {
|
||||
return SpecialPowers.spawn(
|
||||
aBrowser,
|
||||
[[aElementName, aBarred, aType]],
|
||||
async function ([aElementName, aBarred, aType]) {
|
||||
let e = content.document.createElement(aElementName);
|
||||
let contentElement = content.document.getElementById("content");
|
||||
contentElement.appendChild(e);
|
||||
|
||||
if (aType) {
|
||||
e.type = aType;
|
||||
}
|
||||
|
||||
let tttp = Cc[
|
||||
"@mozilla.org/embedcomp/default-tooltiptextprovider;1"
|
||||
].getService(Ci.nsITooltipTextProvider);
|
||||
ok(
|
||||
!tttp.getNodeText(e, {}, {}),
|
||||
"No tooltip should be shown when the element is valid"
|
||||
);
|
||||
|
||||
e.setCustomValidity("foo");
|
||||
if (aBarred) {
|
||||
ok(
|
||||
!tttp.getNodeText(e, {}, {}),
|
||||
"No tooltip should be shown when the element is barred from constraint validation"
|
||||
);
|
||||
} else {
|
||||
ok(
|
||||
tttp.getNodeText(e, {}, {}),
|
||||
e.tagName + " A tooltip should be shown when the element isn't valid"
|
||||
);
|
||||
}
|
||||
|
||||
e.setAttribute("title", "");
|
||||
ok(
|
||||
!tttp.getNodeText(e, {}, {}),
|
||||
"No tooltip should be shown if the title attribute is set"
|
||||
);
|
||||
|
||||
e.removeAttribute("title");
|
||||
contentElement.setAttribute("novalidate", "");
|
||||
ok(
|
||||
!tttp.getNodeText(e, {}, {}),
|
||||
"No tooltip should be shown if the novalidate attribute is set on the form owner"
|
||||
);
|
||||
contentElement.removeAttribute("novalidate");
|
||||
|
||||
e.remove();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function todo_check(aBrowser, aElementName, aBarred) {
|
||||
return SpecialPowers.spawn(
|
||||
aBrowser,
|
||||
[[aElementName, aBarred]],
|
||||
async function ([aElementName]) {
|
||||
let e = content.document.createElement(aElementName);
|
||||
let contentElement = content.document.getElementById("content");
|
||||
contentElement.appendChild(e);
|
||||
|
||||
let caught = false;
|
||||
try {
|
||||
e.setCustomValidity("foo");
|
||||
} catch (e) {
|
||||
caught = true;
|
||||
}
|
||||
|
||||
todo(!caught, "setCustomValidity should exist for " + aElementName);
|
||||
|
||||
e.remove();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "data:text/html,<!DOCTYPE html><html><body><form id='content'></form></body></html>",
|
||||
},
|
||||
async function (browser) {
|
||||
let testData = [
|
||||
/* element name, barred */
|
||||
["input", false, null],
|
||||
["textarea", false, null],
|
||||
["button", true, "button"],
|
||||
["button", false, "submit"],
|
||||
["select", false, null],
|
||||
["output", true, null],
|
||||
["fieldset", true, null],
|
||||
["object", true, null],
|
||||
];
|
||||
|
||||
for (let data of testData) {
|
||||
await check(browser, data[0], data[1], data[2]);
|
||||
}
|
||||
|
||||
let todo_testData = [["keygen", "false"]];
|
||||
|
||||
for (let data of todo_testData) {
|
||||
await todo_check(browser, data[0], data[1]);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,131 @@
|
||||
/* eslint-disable mozilla/no-arbitrary-setTimeout */
|
||||
|
||||
let tempFile;
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({ set: [["ui.tooltip.delay_ms", 0]] });
|
||||
tempFile = createTempFile();
|
||||
registerCleanupFunction(function () {
|
||||
tempFile.remove(true);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_singlefile_selected() {
|
||||
await do_test({ value: true, result: "testfile_bug1251809" });
|
||||
});
|
||||
|
||||
add_task(async function test_title_set() {
|
||||
await do_test({ title: "foo", result: "foo" });
|
||||
});
|
||||
|
||||
add_task(async function test_nofile_selected() {
|
||||
await do_test({ result: "No file selected." });
|
||||
});
|
||||
|
||||
add_task(async function test_multipleset_nofile_selected() {
|
||||
await do_test({ multiple: true, result: "No files selected." });
|
||||
});
|
||||
|
||||
add_task(async function test_requiredset() {
|
||||
await do_test({ required: true, result: "Please select a file." });
|
||||
});
|
||||
|
||||
async function do_test(test) {
|
||||
info(`starting test ${JSON.stringify(test)}`);
|
||||
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
|
||||
info("Moving mouse out of the way.");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(
|
||||
tab.linkedBrowser,
|
||||
300,
|
||||
300
|
||||
);
|
||||
|
||||
info("creating input field");
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [test], async function (test) {
|
||||
let doc = content.document;
|
||||
let input = doc.createElement("input");
|
||||
doc.body.appendChild(input);
|
||||
input.id = "test_input";
|
||||
input.setAttribute("style", "position: absolute; top: 0; left: 0;");
|
||||
input.type = "file";
|
||||
if (test.title) {
|
||||
input.setAttribute("title", test.title);
|
||||
}
|
||||
if (test.multiple) {
|
||||
input.multiple = true;
|
||||
}
|
||||
if (test.required) {
|
||||
input.required = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (test.value) {
|
||||
info("Creating mock filepicker to select files");
|
||||
let MockFilePicker = SpecialPowers.MockFilePicker;
|
||||
MockFilePicker.init(window.browsingContext);
|
||||
MockFilePicker.returnValue = MockFilePicker.returnOK;
|
||||
MockFilePicker.displayDirectory = FileUtils.getDir("TmpD", []);
|
||||
MockFilePicker.setFiles([tempFile]);
|
||||
MockFilePicker.afterOpenCallback = MockFilePicker.cleanup;
|
||||
|
||||
try {
|
||||
// Open the File Picker dialog (MockFilePicker) to select
|
||||
// the files for the test.
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#test_input",
|
||||
{},
|
||||
tab.linkedBrowser
|
||||
);
|
||||
info("Waiting for the input to have the requisite files");
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
|
||||
let input = content.document.querySelector("#test_input");
|
||||
await ContentTaskUtils.waitForCondition(
|
||||
() => input.files.length,
|
||||
"The input should have at least one file selected"
|
||||
);
|
||||
info(`The input has ${input.files.length} file(s) selected.`);
|
||||
});
|
||||
} catch (e) {}
|
||||
} else {
|
||||
info("No real file selection required.");
|
||||
}
|
||||
|
||||
let awaitTooltipOpen = new Promise(resolve => {
|
||||
let tooltipId = Services.appinfo.browserTabsRemoteAutostart
|
||||
? "remoteBrowserTooltip"
|
||||
: "aHTMLTooltip";
|
||||
let tooltip = document.getElementById(tooltipId);
|
||||
tooltip.addEventListener(
|
||||
"popupshown",
|
||||
function (event) {
|
||||
resolve(event.target);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
info("Initial mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 50, 5);
|
||||
info("Waiting");
|
||||
await new Promise(resolve => setTimeout(resolve, 400));
|
||||
info("Second mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 70, 5);
|
||||
info("Waiting for tooltip to open");
|
||||
let tooltip = await awaitTooltipOpen;
|
||||
|
||||
is(
|
||||
tooltip.getAttribute("label"),
|
||||
test.result,
|
||||
"tooltip label should match expectation"
|
||||
);
|
||||
|
||||
info("Closing tab");
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
function createTempFile() {
|
||||
let file = FileUtils.getDir("TmpD", []);
|
||||
file.append("testfile_bug1251809");
|
||||
file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o644);
|
||||
return file;
|
||||
}
|
||||
66
src/zen/tests/mochitests/tooltiptext/browser_nac_tooltip.js
Normal file
66
src/zen/tests/mochitests/tooltiptext/browser_nac_tooltip.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable mozilla/no-arbitrary-setTimeout */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({ set: [["ui.tooltip.delay_ms", 0]] });
|
||||
});
|
||||
|
||||
add_task(async function () {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url: "data:text/html,<!DOCTYPE html>",
|
||||
},
|
||||
async function (browser) {
|
||||
info("Moving mouse out of the way.");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(browser, 300, 300);
|
||||
|
||||
await SpecialPowers.spawn(browser, [], function () {
|
||||
let widget = content.document.insertAnonymousContent();
|
||||
widget.root.innerHTML = `<button style="pointer-events: auto; position: absolute; width: 200px; height: 200px;" title="foo">bar</button>`;
|
||||
let tttp = Cc[
|
||||
"@mozilla.org/embedcomp/default-tooltiptextprovider;1"
|
||||
].getService(Ci.nsITooltipTextProvider);
|
||||
|
||||
let text = {};
|
||||
let dir = {};
|
||||
ok(
|
||||
tttp.getNodeText(widget.root.querySelector("button"), text, dir),
|
||||
"A tooltip should be shown for NAC"
|
||||
);
|
||||
is(text.value, "foo", "Tooltip text should be correct");
|
||||
});
|
||||
|
||||
let awaitTooltipOpen = new Promise(resolve => {
|
||||
let tooltipId = Services.appinfo.browserTabsRemoteAutostart
|
||||
? "remoteBrowserTooltip"
|
||||
: "aHTMLTooltip";
|
||||
let tooltip = document.getElementById(tooltipId);
|
||||
tooltip.addEventListener(
|
||||
"popupshown",
|
||||
function (event) {
|
||||
resolve(event.target);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
|
||||
info("Initial mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(browser, 50, 5);
|
||||
info("Waiting");
|
||||
await new Promise(resolve => setTimeout(resolve, 400));
|
||||
info("Second mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(browser, 70, 5);
|
||||
info("Waiting for tooltip to open");
|
||||
let tooltip = await awaitTooltipOpen;
|
||||
is(
|
||||
tooltip.getAttribute("label"),
|
||||
"foo",
|
||||
"tooltip label should match expectation"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,166 @@
|
||||
/* eslint-disable mozilla/no-arbitrary-setTimeout */
|
||||
|
||||
add_setup(async function () {
|
||||
await SpecialPowers.pushPrefEnv({ set: [["ui.tooltip.delay_ms", 0]] });
|
||||
});
|
||||
|
||||
add_task(async function test_title_in_shadow_dom() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
|
||||
info("Moving mouse out of the way.");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(
|
||||
tab.linkedBrowser,
|
||||
300,
|
||||
300
|
||||
);
|
||||
|
||||
info("creating host");
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
|
||||
let doc = content.document;
|
||||
let host = doc.createElement("div");
|
||||
doc.body.appendChild(host);
|
||||
host.setAttribute("style", "position: absolute; top: 0; left: 0;");
|
||||
var sr = host.attachShadow({ mode: "closed" });
|
||||
sr.innerHTML =
|
||||
"<div title='shadow' style='width: 200px; height: 200px;'>shadow</div>";
|
||||
});
|
||||
|
||||
let awaitTooltipOpen = new Promise(resolve => {
|
||||
let tooltipId = Services.appinfo.browserTabsRemoteAutostart
|
||||
? "remoteBrowserTooltip"
|
||||
: "aHTMLTooltip";
|
||||
let tooltip = document.getElementById(tooltipId);
|
||||
tooltip.addEventListener(
|
||||
"popupshown",
|
||||
function (event) {
|
||||
resolve(event.target);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
info("Initial mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 50, 5);
|
||||
info("Waiting");
|
||||
await new Promise(resolve => setTimeout(resolve, 400));
|
||||
info("Second mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 70, 5);
|
||||
info("Waiting for tooltip to open");
|
||||
let tooltip = await awaitTooltipOpen;
|
||||
|
||||
is(
|
||||
tooltip.getAttribute("label"),
|
||||
"shadow",
|
||||
"tooltip label should match expectation"
|
||||
);
|
||||
|
||||
info("Closing tab");
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function test_title_in_light_dom() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
|
||||
info("Moving mouse out of the way.");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(
|
||||
tab.linkedBrowser,
|
||||
300,
|
||||
300
|
||||
);
|
||||
|
||||
info("creating host");
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
|
||||
let doc = content.document;
|
||||
let host = doc.createElement("div");
|
||||
host.title = "light";
|
||||
doc.body.appendChild(host);
|
||||
host.setAttribute("style", "position: absolute; top: 0; left: 0;");
|
||||
var sr = host.attachShadow({ mode: "closed" });
|
||||
sr.innerHTML = "<div style='width: 200px; height: 200px;'>shadow</div>";
|
||||
});
|
||||
|
||||
let awaitTooltipOpen = new Promise(resolve => {
|
||||
let tooltipId = Services.appinfo.browserTabsRemoteAutostart
|
||||
? "remoteBrowserTooltip"
|
||||
: "aHTMLTooltip";
|
||||
let tooltip = document.getElementById(tooltipId);
|
||||
tooltip.addEventListener(
|
||||
"popupshown",
|
||||
function (event) {
|
||||
resolve(event.target);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
info("Initial mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 50, 5);
|
||||
info("Waiting");
|
||||
await new Promise(resolve => setTimeout(resolve, 400));
|
||||
info("Second mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 70, 5);
|
||||
info("Waiting for tooltip to open");
|
||||
let tooltip = await awaitTooltipOpen;
|
||||
|
||||
is(
|
||||
tooltip.getAttribute("label"),
|
||||
"light",
|
||||
"tooltip label should match expectation"
|
||||
);
|
||||
|
||||
info("Closing tab");
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function test_title_through_slot() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
|
||||
info("Moving mouse out of the way.");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(
|
||||
tab.linkedBrowser,
|
||||
300,
|
||||
300
|
||||
);
|
||||
|
||||
info("creating host");
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
|
||||
let doc = content.document;
|
||||
let host = doc.createElement("div");
|
||||
host.title = "light";
|
||||
host.innerHTML = "<div style='width: 200px; height: 200px;'>light</div>";
|
||||
doc.body.appendChild(host);
|
||||
host.setAttribute("style", "position: absolute; top: 0; left: 0;");
|
||||
var sr = host.attachShadow({ mode: "closed" });
|
||||
sr.innerHTML =
|
||||
"<div title='shadow' style='width: 200px; height: 200px;'><slot></slot></div>";
|
||||
});
|
||||
|
||||
let awaitTooltipOpen = new Promise(resolve => {
|
||||
let tooltipId = Services.appinfo.browserTabsRemoteAutostart
|
||||
? "remoteBrowserTooltip"
|
||||
: "aHTMLTooltip";
|
||||
let tooltip = document.getElementById(tooltipId);
|
||||
tooltip.addEventListener(
|
||||
"popupshown",
|
||||
function (event) {
|
||||
resolve(event.target);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
info("Initial mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 50, 5);
|
||||
info("Waiting");
|
||||
await new Promise(resolve => setTimeout(resolve, 400));
|
||||
info("Second mouse move");
|
||||
await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 70, 5);
|
||||
info("Waiting for tooltip to open");
|
||||
let tooltip = await awaitTooltipOpen;
|
||||
|
||||
is(
|
||||
tooltip.getAttribute("label"),
|
||||
"shadow",
|
||||
"tooltip label should match expectation"
|
||||
);
|
||||
|
||||
info("Closing tab");
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
59
src/zen/tests/mochitests/tooltiptext/title_test.svg
Normal file
59
src/zen/tests/mochitests/tooltiptext/title_test.svg
Normal file
@@ -0,0 +1,59 @@
|
||||
<svg width="640px" height="480px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>This is a root SVG element's title</title>
|
||||
<foreignObject>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="svg1">
|
||||
<title>This is a non-root SVG element title</title>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
||||
</foreignObject>
|
||||
<text id="text1" x="10px" y="32px" font-size="24px">
|
||||
This contains only <title>
|
||||
<title>
|
||||
|
||||
|
||||
This is a title
|
||||
|
||||
</title>
|
||||
</text>
|
||||
<text id="text2" x="10px" y="96px" font-size="24px">
|
||||
This contains only <desc>
|
||||
<desc>This is a desc</desc>
|
||||
</text>
|
||||
<text id="text3" x="10px" y="128px" font-size="24px" title="ignored for SVG">
|
||||
This contains nothing.
|
||||
</text>
|
||||
<a id="link1" href="#">
|
||||
This link contains <title>
|
||||
<title>
|
||||
This is a title
|
||||
</title>
|
||||
<text id="text4" x="10px" y="192px" font-size="24px">
|
||||
</text>
|
||||
</a>
|
||||
<a id="link2" href="#">
|
||||
<text x="10px" y="192px" font-size="24px">
|
||||
This text contains <title>
|
||||
<title>
|
||||
This is a title
|
||||
</title>
|
||||
</text>
|
||||
</a>
|
||||
<a id="link3" href="#" xlink:title="This is an xlink:title attribute">
|
||||
<text x="10px" y="224px" font-size="24px">
|
||||
This link contains <title> & xlink:title attr.
|
||||
<title>This is a title</title>
|
||||
</text>
|
||||
</a>
|
||||
<a id="link4" href="#" xlink:title="This is an xlink:title attribute">
|
||||
<text x="10px" y="256px" font-size="24px">
|
||||
This link contains xlink:title attr.
|
||||
</text>
|
||||
</a>
|
||||
<text id="text5" x="10px" y="160px" font-size="24px"
|
||||
xlink:title="This is an xlink:title attribute but it isn't on a link" >
|
||||
This contains nothing.
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
12
src/zen/tests/mochitests/tooltiptext/xul_tooltiptext.xhtml
Normal file
12
src/zen/tests/mochitests/tooltiptext/xul_tooltiptext.xhtml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<xul:toolbox xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<toolbar>
|
||||
<toolbarbutton id="xulToolbarButton"
|
||||
tooltiptext="XUL tooltiptext"
|
||||
title="XUL title"/>
|
||||
</toolbar>
|
||||
</xul:toolbox>
|
||||
</html>
|
||||
@@ -15,3 +15,7 @@ BROWSER_CHROME_MANIFESTS += [
|
||||
"welcome/browser.toml",
|
||||
"workspaces/browser.toml",
|
||||
]
|
||||
|
||||
DIRS += [
|
||||
"mochitests",
|
||||
]
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
|
||||
import { AppConstants } from 'resource://gre/modules/AppConstants.sys.mjs';
|
||||
|
||||
const ADDONS_BUTTONS_HIDDEN = Services.prefs.getBoolPref(
|
||||
'zen.theme.hide-unified-extensions-button',
|
||||
true
|
||||
);
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
@@ -21,7 +26,19 @@ export class nsZenSiteDataPanel {
|
||||
this.window = window;
|
||||
this.document = window.document;
|
||||
|
||||
this.panel = this.document.getElementById('zen-unified-site-data-panel');
|
||||
this.unifiedPanel = this.#initUnifiedPanel();
|
||||
this.unifiedPanelView = 'unified-extensions-view';
|
||||
this.extensionsPanelView = 'original-unified-extensions-view';
|
||||
|
||||
if (ADDONS_BUTTONS_HIDDEN) {
|
||||
this.window.gUnifiedExtensions._panel = this.unifiedPanel;
|
||||
|
||||
// Remove the old permissions dialog
|
||||
this.document.getElementById('unified-extensions-panel-template').remove();
|
||||
} else {
|
||||
this.extensionsPanel = this.#initExtensionsPanel();
|
||||
}
|
||||
|
||||
this.#init();
|
||||
}
|
||||
|
||||
@@ -34,22 +51,23 @@ export class nsZenSiteDataPanel {
|
||||
`);
|
||||
this.anchor = button.querySelector('#zen-site-data-icon-button');
|
||||
this.document.getElementById('identity-icon-box').before(button);
|
||||
this.window.gUnifiedExtensions._button = this.anchor;
|
||||
|
||||
this.extensionsPanelButton = this.document.getElementById('unified-extensions-button');
|
||||
this.window.gUnifiedExtensions._button = ADDONS_BUTTONS_HIDDEN
|
||||
? this.anchor
|
||||
: this.extensionsPanelButton;
|
||||
|
||||
this.document
|
||||
.getElementById('nav-bar')
|
||||
.setAttribute('addon-webext-overflowbutton', 'zen-site-data-icon-button');
|
||||
|
||||
// Remove the old permissions dialog
|
||||
this.document.getElementById('unified-extensions-panel-template').remove();
|
||||
|
||||
this.#initCopyUrlButton();
|
||||
this.#initEventListeners();
|
||||
this.#maybeShowFeatureCallout();
|
||||
}
|
||||
|
||||
#initEventListeners() {
|
||||
this.panel.addEventListener('popupshowing', this);
|
||||
this.unifiedPanel.addEventListener('popupshowing', this);
|
||||
this.document.getElementById('zen-site-data-manage-addons').addEventListener('click', this);
|
||||
this.document.getElementById('zen-site-data-settings-more').addEventListener('click', this);
|
||||
this.anchor.addEventListener('click', this);
|
||||
@@ -124,6 +142,24 @@ export class nsZenSiteDataPanel {
|
||||
}
|
||||
}
|
||||
|
||||
#initExtensionsPanel() {
|
||||
const panel = this.window.gUnifiedExtensions.panel;
|
||||
|
||||
const extensionsView = panel?.querySelector('#unified-extensions-view');
|
||||
extensionsView.setAttribute('id', this.extensionsPanelView);
|
||||
|
||||
const panelMultiView = panel?.querySelector('panelmultiview');
|
||||
panelMultiView.setAttribute('mainViewId', this.extensionsPanelView);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
#initUnifiedPanel() {
|
||||
const panel = this.document.getElementById('zen-unified-site-data-panel');
|
||||
this.window.gUnifiedExtensions.initializePanel(panel);
|
||||
return panel;
|
||||
}
|
||||
|
||||
#preparePanel() {
|
||||
this.#setSitePermissions();
|
||||
this.#setSiteSecurityInfo();
|
||||
@@ -498,7 +534,7 @@ export class nsZenSiteDataPanel {
|
||||
this.window.gZenCommonActions.copyCurrentURLToClipboard();
|
||||
}
|
||||
if (AppConstants.platform !== 'macosx') {
|
||||
this.panel.hidePopup();
|
||||
this.unifiedPanel.hidePopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -556,7 +592,13 @@ export class nsZenSiteDataPanel {
|
||||
break;
|
||||
}
|
||||
case 'zen-site-data-icon-button': {
|
||||
this.window.gUnifiedExtensions.togglePanel(event);
|
||||
this.window.gUnifiedExtensions.togglePanel(
|
||||
event,
|
||||
null,
|
||||
this.unifiedPanel,
|
||||
this.unifiedPanelView,
|
||||
this.anchor
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
||||
@@ -3140,7 +3140,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
|
||||
return;
|
||||
}
|
||||
const maxButtonSize = 32; // IMPORTANT: This should match the CSS size of the icons
|
||||
const minButtonSize = 15;
|
||||
const minButtonSize = maxButtonSize / 2; // Minimum size for icons when space is limited
|
||||
const separation = 3; // Space between icons
|
||||
|
||||
// Calculate the total width needed for all icons
|
||||
@@ -3160,9 +3160,7 @@ class nsZenWorkspaces extends nsZenMultiWindowFeature {
|
||||
|
||||
// Set the width of each icon to the maximum size they can fit on
|
||||
const widthPerButton = Math.max(
|
||||
Math.floor(
|
||||
(parent.clientWidth - separation * (parent.children.length - 1)) / parent.children.length
|
||||
),
|
||||
(parent.clientWidth - separation * (parent.children.length - 1)) / parent.children.length,
|
||||
minButtonSize
|
||||
);
|
||||
for (const icon of parent.children) {
|
||||
|
||||
@@ -4,31 +4,40 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 4px;
|
||||
border-radius: 99px;
|
||||
height: 4px;
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--zen-primary-color) 10%,
|
||||
light-dark(rgba(0, 0, 0, 0.4), rgba(255, 255, 255, 0.4)) 90%
|
||||
);
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
filter: saturate(140%) brightness(110%) !important;
|
||||
transform: translate(-50%, -50%);
|
||||
transition: opacity 150ms ease-in-out;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
& .zen-workspace-icon {
|
||||
transition: opacity 150ms ease-in-out, transform 150ms ease-in-out;
|
||||
z-index: 2;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
&:not(:hover) {
|
||||
min-width: 10px;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 4px;
|
||||
border-radius: 99px;
|
||||
height: 4px;
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--zen-primary-color) 10%,
|
||||
light-dark(rgba(0, 0, 0, 0.4), rgba(255, 255, 255, 0.4)) 90%
|
||||
);
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
filter: saturate(140%) brightness(110%) !important;
|
||||
transform: translate(-50%, -50%);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
& .zen-workspace-icon {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
width: 20px !important;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
|
||||
&[icons-overflow] {
|
||||
gap: 0 !important;
|
||||
justify-content: space-between;
|
||||
justify-content: center;
|
||||
|
||||
& toolbarbutton {
|
||||
margin: 0;
|
||||
@@ -108,7 +108,7 @@
|
||||
}
|
||||
|
||||
&:has(toolbarbutton:hover) toolbarbutton[active='true']:not([dragged='true']) {
|
||||
%include overflow-icons.inc.css
|
||||
%include overflow-icons.inc.css
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"brandShortName": "Zen",
|
||||
"brandFullName": "Zen Browser",
|
||||
"release": {
|
||||
"displayVersion": "1.17.13b",
|
||||
"displayVersion": "1.17.14b",
|
||||
"github": {
|
||||
"repo": "zen-browser/desktop"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user