/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
var { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
var { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
ChromeUtils.importESModule(
"resource://gre/modules/MemoryNotificationDB.sys.mjs"
);
ChromeUtils.importESModule(
"resource://gre/modules/NotificationDB.sys.mjs");
// lazy module getters
ChromeUtils.defineESModuleGetters(
this, {
AMTelemetry:
"resource://gre/modules/AddonManager.sys.mjs",
AboutNewTab:
"resource:///modules/AboutNewTab.sys.mjs",
AboutReaderParent:
"resource:///actors/AboutReaderParent.sys.mjs",
AddonManager:
"resource://gre/modules/AddonManager.sys.mjs",
BrowserSearchTelemetry:
"resource:///modules/BrowserSearchTelemetry.sys.mjs",
BrowserTelemetryUtils:
"resource://gre/modules/BrowserTelemetryUtils.sys.mjs",
BrowserUIUtils:
"resource:///modules/BrowserUIUtils.sys.mjs",
BrowserUsageTelemetry:
"resource:///modules/BrowserUsageTelemetry.sys.mjs",
BrowserWindowTracker:
"resource:///modules/BrowserWindowTracker.sys.mjs",
CFRPageActions:
"resource:///modules/asrouter/CFRPageActions.sys.mjs",
Color:
"resource://gre/modules/Color.sys.mjs",
ContentAnalysis:
"resource:///modules/ContentAnalysis.sys.mjs",
ContextualIdentityService:
"resource://gre/modules/ContextualIdentityService.sys.mjs",
CustomizableUI:
"resource:///modules/CustomizableUI.sys.mjs",
DevToolsSocketStatus:
"resource://devtools/shared/security/DevToolsSocketStatus.sys.mjs",
DownloadUtils:
"resource://gre/modules/DownloadUtils.sys.mjs",
DownloadsCommon:
"resource:///modules/DownloadsCommon.sys.mjs",
E10SUtils:
"resource://gre/modules/E10SUtils.sys.mjs",
ExtensionsUI:
"resource:///modules/ExtensionsUI.sys.mjs",
HomePage:
"resource:///modules/HomePage.sys.mjs",
LightweightThemeConsumer:
"resource://gre/modules/LightweightThemeConsumer.sys.mjs",
LoginHelper:
"resource://gre/modules/LoginHelper.sys.mjs",
LoginManagerParent:
"resource://gre/modules/LoginManagerParent.sys.mjs",
MigrationUtils:
"resource:///modules/MigrationUtils.sys.mjs",
NetUtil:
"resource://gre/modules/NetUtil.sys.mjs",
NewTabPagePreloading:
"resource:///modules/NewTabPagePreloading.sys.mjs",
NewTabUtils:
"resource://gre/modules/NewTabUtils.sys.mjs",
NimbusFeatures:
"resource://nimbus/ExperimentAPI.sys.mjs",
nsContextMenu:
"chrome://browser/content/nsContextMenu.sys.mjs",
OpenInTabsUtils:
"resource:///modules/OpenInTabsUtils.sys.mjs",
PageActions:
"resource:///modules/PageActions.sys.mjs",
PageThumbs:
"resource://gre/modules/PageThumbs.sys.mjs",
PanelMultiView:
"resource:///modules/PanelMultiView.sys.mjs",
PanelView:
"resource:///modules/PanelMultiView.sys.mjs",
PictureInPicture:
"resource://gre/modules/PictureInPicture.sys.mjs",
PlacesTransactions:
"resource://gre/modules/PlacesTransactions.sys.mjs",
PlacesUIUtils:
"resource:///modules/PlacesUIUtils.sys.mjs",
PlacesUtils:
"resource://gre/modules/PlacesUtils.sys.mjs",
Pocket:
"chrome://pocket/content/Pocket.sys.mjs",
PopupBlockerObserver:
"resource:///modules/PopupBlockerObserver.sys.mjs",
PrivateBrowsingUtils:
"resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
ProcessHangMonitor:
"resource:///modules/ProcessHangMonitor.sys.mjs",
PromptUtils:
"resource://gre/modules/PromptUtils.sys.mjs",
ReaderMode:
"resource://gre/modules/ReaderMode.sys.mjs",
ResetPBMPanel:
"resource:///modules/ResetPBMPanel.sys.mjs",
SafeBrowsing:
"resource://gre/modules/SafeBrowsing.sys.mjs",
Sanitizer:
"resource:///modules/Sanitizer.sys.mjs",
SaveToPocket:
"chrome://pocket/content/SaveToPocket.sys.mjs",
ScreenshotsUtils:
"resource:///modules/ScreenshotsUtils.sys.mjs",
SearchUIUtils:
"resource:///modules/SearchUIUtils.sys.mjs",
SessionStartup:
"resource:///modules/sessionstore/SessionStartup.sys.mjs",
SessionStore:
"resource:///modules/sessionstore/SessionStore.sys.mjs",
ShoppingSidebarParent:
"resource:///actors/ShoppingSidebarParent.sys.mjs",
ShoppingSidebarManager:
"resource:///actors/ShoppingSidebarParent.sys.mjs",
ShortcutUtils:
"resource://gre/modules/ShortcutUtils.sys.mjs",
SiteDataManager:
"resource:///modules/SiteDataManager.sys.mjs",
SitePermissions:
"resource:///modules/SitePermissions.sys.mjs",
SubDialog:
"resource://gre/modules/SubDialog.sys.mjs",
SubDialogManager:
"resource://gre/modules/SubDialog.sys.mjs",
TabCrashHandler:
"resource:///modules/ContentCrashHandlers.sys.mjs",
TabsSetupFlowManager:
"resource:///modules/firefox-view-tabs-setup-manager.sys.mjs",
TelemetryEnvironment:
"resource://gre/modules/TelemetryEnvironment.sys.mjs",
ToolbarContextMenu:
"resource:///modules/ToolbarContextMenu.sys.mjs",
TranslationsParent:
"resource://gre/actors/TranslationsParent.sys.mjs",
UITour:
"resource:///modules/UITour.sys.mjs",
UpdateUtils:
"resource://gre/modules/UpdateUtils.sys.mjs",
URILoadingHelper:
"resource:///modules/URILoadingHelper.sys.mjs",
UrlbarInput:
"resource:///modules/UrlbarInput.sys.mjs",
UrlbarPrefs:
"resource:///modules/UrlbarPrefs.sys.mjs",
UrlbarProviderSearchTips:
"resource:///modules/UrlbarProviderSearchTips.sys.mjs",
UrlbarTokenizer:
"resource:///modules/UrlbarTokenizer.sys.mjs",
UrlbarUtils:
"resource:///modules/UrlbarUtils.sys.mjs",
UrlbarValueFormatter:
"resource:///modules/UrlbarValueFormatter.sys.mjs",
Weave:
"resource://services-sync/main.sys.mjs",
WebNavigationFrames:
"resource://gre/modules/WebNavigationFrames.sys.mjs",
webrtcUI:
"resource:///modules/webrtcUI.sys.mjs",
WebsiteFilter:
"resource:///modules/policies/WebsiteFilter.sys.mjs",
ZoomUI:
"resource:///modules/ZoomUI.sys.mjs",
});
// Bug 1894239: We will move this up to ChromeUtils.defineESModuleGetters once
// the MOZ_SELECTABLE_PROFILES flag is removed
ChromeUtils.defineLazyGetter(
this,
"SelectableProfileService", () => {
if (!AppConstants.MOZ_SELECTABLE_PROFILES) {
return null;
}
return ChromeUtils.importESModule(
"resource:///modules/profiles/SelectableProfileService.sys.mjs"
).SelectableProfileService;
});
ChromeUtils.defineLazyGetter(
this,
"fxAccounts", () => {
return ChromeUtils.importESModule(
"resource://gre/modules/FxAccounts.sys.mjs"
).getFxAccountsSingleton();
});
XPCOMUtils.defineLazyScriptGetter(
this,
[
"BrowserCommands",
"kSkipCacheFlags"],
"chrome://browser/content/browser-commands.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"PlacesTreeView",
"chrome://browser/content/places/treeView.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
[
"PlacesInsertionPoint",
"PlacesController",
"PlacesControllerDragHelper"],
"chrome://browser/content/places/controller.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"PrintUtils",
"chrome://global/content/printUtils.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"ZoomManager",
"chrome://global/content/viewZoomOverlay.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"FullZoom",
"chrome://browser/content/tabbrowser/browser-fullZoom.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"PanelUI",
"chrome://browser/content/customizableui/panelUI.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gViewSourceUtils",
"chrome://global/content/viewSourceUtils.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gTabsPanel",
"chrome://browser/content/tabbrowser/browser-allTabsMenu.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
[
"BrowserAddonUI",
"gExtensionsNotifications",
"gUnifiedExtensions",
"gXPInstallObserver",
],
"chrome://browser/content/browser-addons.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"ctrlTab",
"chrome://browser/content/tabbrowser/browser-ctrlTab.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
[
"CustomizationHandler",
"AutoHideMenubar"],
"chrome://browser/content/browser-customization.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
[
"PointerLock",
"FullScreen"],
"chrome://browser/content/browser-fullScreenAndPointerLock.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gIdentityHandler",
"chrome://browser/content/browser-siteIdentity.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gPermissionPanel",
"chrome://browser/content/browser-sitePermissionPanel.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"SelectTranslationsPanel",
"chrome://browser/content/translations/selectTranslationsPanel.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"FullPageTranslationsPanel",
"chrome://browser/content/translations/fullPageTranslationsPanel.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gProtectionsHandler",
"chrome://browser/content/browser-siteProtections.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
[
"gGestureSupport",
"gHistorySwipeAnimation"],
"chrome://browser/content/browser-gestureSupport.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gSafeBrowsing",
"chrome://browser/content/browser-safebrowsing.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gSync",
"chrome://browser/content/browser-sync.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gBrowserThumbnails",
"chrome://browser/content/browser-thumbnails.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
[
"DownloadsPanel",
"DownloadsOverlayLoader",
"DownloadsView",
"DownloadsViewUI",
"DownloadsViewController",
"DownloadsSummary",
"DownloadsFooter",
"DownloadsBlockedSubview",
],
"chrome://browser/content/downloads/downloads.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
[
"DownloadsButton",
"DownloadsIndicatorView"],
"chrome://browser/content/downloads/indicator.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gEditItemOverlay",
"chrome://browser/content/places/editBookmark.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gGfxUtils",
"chrome://browser/content/browser-graphics-utils.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"pktUI",
"chrome://pocket/content/pktUI.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"ToolbarKeyboardNavigator",
"chrome://browser/content/browser-toolbarKeyNav.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"A11yUtils",
"chrome://browser/content/browser-a11yUtils.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gSharedTabWarning",
"chrome://browser/content/browser-webrtc.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gPageStyleMenu",
"chrome://browser/content/browser-pagestyle.js"
);
XPCOMUtils.defineLazyScriptGetter(
this,
"gProfiles",
"chrome://browser/content/browser-profiles.js"
);
// lazy service getters
XPCOMUtils.defineLazyServiceGetters(
this, {
ContentPrefService2: [
"@mozilla.org/content-pref/service;1",
"nsIContentPrefService2",
],
classifierService: [
"@mozilla.org/url-classifier/dbservice;1",
"nsIURIClassifier",
],
Favicons: [
"@mozilla.org/browser/favicon-service;1",
"nsIFaviconService"],
WindowsUIUtils: [
"@mozilla.org/windows-ui-utils;1",
"nsIWindowsUIUtils"],
BrowserHandler: [
"@mozilla.org/browser/clh;1",
"nsIBrowserHandler"],
});
if (AppConstants.ENABLE_WEBDRIVER) {
XPCOMUtils.defineLazyServiceGetter(
this,
"Marionette",
"@mozilla.org/remote/marionette;1",
"nsIMarionette"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"RemoteAgent",
"@mozilla.org/remote/agent;1",
"nsIRemoteAgent"
);
}
else {
this.Marionette = { running:
false };
this.RemoteAgent = { running:
false };
}
ChromeUtils.defineLazyGetter(
this,
"RTL_UI", () => {
return Services.locale.isAppLocaleRTL;
});
ChromeUtils.defineLazyGetter(
this,
"gBrandBundle", () => {
return Services.strings.createBundle(
"chrome://branding/locale/brand.properties"
);
});
ChromeUtils.defineLazyGetter(
this,
"gBrowserBundle", () => {
return Services.strings.createBundle(
"chrome://browser/locale/browser.properties"
);
});
ChromeUtils.defineLazyGetter(
this,
"gCustomizeMode", () => {
let { CustomizeMode } = ChromeUtils.importESModule(
"resource:///modules/CustomizeMode.sys.mjs"
);
return new CustomizeMode(window);
});
ChromeUtils.defineLazyGetter(
this,
"gNavToolbox", () => {
return document.getElementById(
"navigator-toolbox");
});
ChromeUtils.defineLazyGetter(
this,
"gURLBar", () => {
let urlbar =
new UrlbarInput({
textbox: document.getElementById(
"urlbar"),
eventTelemetryCategory:
"urlbar",
});
let beforeFocusOrSelect = event => {
// In customize mode, the url bar is disabled. If a new tab is opened or the
// user switches to a different tab, this function gets called before we've
// finished leaving customize mode, and the url bar will still be disabled.
// We can't focus it when it's disabled, so we need to re-run ourselves when
// we've finished leaving customize mode.
if (
CustomizationHandler.isCustomizing() ||
CustomizationHandler.isExitingCustomizeMode
) {
gNavToolbox.addEventListener(
"aftercustomization",
() => {
if (event.type ==
"beforeselect") {
gURLBar.select();
}
else {
gURLBar.focus();
}
},
{
once:
true,
}
);
event.preventDefault();
return;
}
if (window.fullScreen) {
FullScreen.showNavToolbox();
}
};
urlbar.addEventListener(
"beforefocus", beforeFocusOrSelect);
urlbar.addEventListener(
"beforeselect", beforeFocusOrSelect);
return urlbar;
});
ChromeUtils.defineLazyGetter(
this,
"ReferrerInfo", () =>
Components.Constructor(
"@mozilla.org/referrer-info;1",
"nsIReferrerInfo",
"init"
)
);
// High priority notification bars shown at the top of the window.
ChromeUtils.defineLazyGetter(
this,
"gNotificationBox", () => {
let securityDelayMS = Services.prefs.getIntPref(
"security.notification_enable_delay"
);
return new MozElements.NotificationBox(element => {
element.classList.add(
"global-notificationbox");
element.setAttribute(
"notificationside",
"top");
element.setAttribute(
"prepend-notifications",
true);
// We want this before the tab notifications.
document.getElementById(
"notifications-toolbar").prepend(element);
}, securityDelayMS);
});
ChromeUtils.defineLazyGetter(
this,
"InlineSpellCheckerUI", () => {
let { InlineSpellChecker } = ChromeUtils.importESModule(
"resource://gre/modules/InlineSpellChecker.sys.mjs"
);
return new InlineSpellChecker();
});
ChromeUtils.defineLazyGetter(
this,
"PopupNotifications", () => {
// eslint-disable-next-line no-shadow
let { PopupNotifications } = ChromeUtils.importESModule(
"resource://gre/modules/PopupNotifications.sys.mjs"
);
try {
// Hide all PopupNotifications while the the address bar has focus,
// including the virtual focus in the results popup, and the URL is being
// edited or the page proxy state is invalid while async tab switching.
let shouldSuppress = () => {
// "Blank" pages, like about:welcome, have a pageproxystate of "invalid", but
// popups like CFRs should not automatically be suppressed when the address
// bar has focus on these pages as it disrupts user navigation using FN+F6.
// See `UrlbarInput.setURI()` where pageproxystate is set to "invalid" for
// all pages that the "isBlankPageURL" method returns true for.
const urlBarEdited = isBlankPageURL(gBrowser.currentURI.spec)
? gURLBar.hasAttribute(
"usertyping")
: gURLBar.getAttribute(
"pageproxystate") !=
"valid";
return (
(urlBarEdited && gURLBar.focused) ||
(gURLBar.getAttribute(
"pageproxystate") !=
"valid" &&
gBrowser.selectedBrowser._awaitingSetURI) ||
shouldSuppressPopupNotifications()
);
};
// Before a Popup is shown, check that its anchor is visible.
// If the anchor is not visible, use one of the fallbacks.
// If no fallbacks are visible, return null.
const getVisibleAnchorElement = anchorElement => {
// If the anchor element is present in the Urlbar,
// ensure that both the anchor and page URL are visible.
gURLBar.maybeHandleRevertFromPopup(anchorElement);
if (anchorElement?.checkVisibility()) {
return anchorElement;
}
let fallback = [
document.getElementById(
"searchmode-switcher-icon"),
document.getElementById(
"identity-icon"),
gURLBar.querySelector(
".urlbar-search-button"),
document.getElementById(
"remote-control-icon"),
];
return fallback.find(element => element?.checkVisibility()) ??
null;
};
return new PopupNotifications(
gBrowser,
document.getElementById(
"notification-popup"),
document.getElementById(
"notification-popup-box"),
{ shouldSuppress, getVisibleAnchorElement }
);
}
catch (ex) {
console.error(ex);
return null;
}
});
ChromeUtils.defineLazyGetter(
this,
"MacUserActivityUpdater", () => {
if (AppConstants.platform !=
"macosx") {
return null;
}
return Cc[
"@mozilla.org/widget/macuseractivityupdater;1"].getService(
Ci.nsIMacUserActivityUpdater
);
});
ChromeUtils.defineLazyGetter(
this,
"Win7Features", () => {
if (AppConstants.platform !=
"win") {
return null;
}
const WINTASKBAR_CONTRACTID =
"@mozilla.org/windows-taskbar;1";
if (
WINTASKBAR_CONTRACTID in Cc &&
Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available
) {
let { AeroPeek } = ChromeUtils.importESModule(
"resource:///modules/WindowsPreviewPerTab.sys.mjs"
);
return {
onOpenWindow() {
AeroPeek.onOpenWindow(window);
this.handledOpening =
true;
},
onCloseWindow() {
if (
this.handledOpening) {
AeroPeek.onCloseWindow(window);
}
},
handledOpening:
false,
};
}
return null;
});
XPCOMUtils.defineLazyPreferenceGetter(
this,
"gToolbarKeyNavEnabled",
"browser.toolbars.keyboard_navigation",
false,
(aPref, aOldVal, aNewVal) => {
if (window.closed) {
return;
}
if (aNewVal) {
ToolbarKeyboardNavigator.init();
}
else {
ToolbarKeyboardNavigator.uninit();
}
}
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"gBookmarksToolbarVisibility",
"browser.toolbars.bookmarks.visibility",
"newtab"
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"gFxaToolbarEnabled",
"identity.fxaccounts.toolbar.enabled",
false,
(aPref, aOldVal, aNewVal) => {
updateFxaToolbarMenu(aNewVal);
}
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"gFxaToolbarAccessed",
"identity.fxaccounts.toolbar.accessed",
false,
() => {
updateFxaToolbarMenu(gFxaToolbarEnabled);
}
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"gAddonAbuseReportEnabled",
"extensions.abuseReport.enabled",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"gMiddleClickNewTabUsesPasteboard",
"browser.tabs.searchclipboardfor.middleclick",
true
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"gScreenshotsDisabled",
"extensions.screenshots.disabled",
false,
() => {
Services.obs.notifyObservers(
window,
"toggle-screenshot-disable",
gScreenshots.shouldScreenshotsButtonBeDisabled()
);
}
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"gPrintEnabled",
"print.enabled",
false,
(aPref, aOldVal, aNewVal) => {
updatePrintCommands(aNewVal);
}
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"gScreenshotsComponentEnabled",
"screenshots.browser.component.enabled",
false,
() => {
Services.obs.notifyObservers(
window,
"toggle-screenshot-disable",
gScreenshots.shouldScreenshotsButtonBeDisabled()
);
}
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"gTranslationsEnabled",
"browser.translations.enable",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"gUseFeltPrivacyUI",
"browser.privatebrowsing.felt-privacy-v1",
false
);
customElements.setElementCreationCallback(
"screenshots-buttons", () => {
Services.scriptloader.loadSubScript(
"chrome://browser/content/screenshots/screenshots-buttons.js",
window
);
});
customElements.setElementCreationCallback(
"fxa-menu-message", () => {
ChromeUtils.importESModule(
"chrome://browser/content/asrouter/components/fxa-menu-message.mjs",
{ global:
"current" }
);
});
var gBrowser;
var gContextMenu =
null;
// nsContextMenu instance
var gMultiProcessBrowser = window.docShell.QueryInterface(
Ci.nsILoadContext
).useRemoteTabs;
var gFissionBrowser = window.docShell.QueryInterface(
Ci.nsILoadContext
).useRemoteSubframes;
var gBrowserAllowScriptsToCloseInitialTabs =
false;
if (AppConstants.platform !=
"macosx") {
var gEditUIVisible =
true;
}
Object.defineProperty(
this,
"gReduceMotion", {
enumerable:
true,
get() {
return typeof gReduceMotionOverride ==
"boolean"
? gReduceMotionOverride
: gReduceMotionSetting;
},
});
// Reduce motion during startup. The setting will be reset later.
let gReduceMotionSetting =
true;
// This is for tests to set.
var gReduceMotionOverride;
// Smart getter for the findbar. If you don't wish to force the creation of
// the findbar, check gFindBarInitialized first.
Object.defineProperty(
this,
"gFindBar", {
enumerable:
true,
get() {
return gBrowser.getCachedFindBar();
},
});
Object.defineProperty(
this,
"gFindBarInitialized", {
enumerable:
true,
get() {
return gBrowser.isFindBarInitialized();
},
});
Object.defineProperty(
this,
"gFindBarPromise", {
enumerable:
true,
get() {
return gBrowser.getFindBar();
},
});
function shouldSuppressPopupNotifications() {
// We have to hide notifications explicitly when the window is
// minimized because of the effects of the "noautohide" attribute on Linux.
// This can be removed once bug 545265 and bug 1320361 are fixed.
// Hide popup notifications when system tab prompts are shown so they
// don't cover up the prompt.
return (
window.windowState == window.STATE_MINIMIZED ||
gBrowser?.selectedBrowser.hasAttribute(
"tabDialogShowing") ||
gDialogBox?.isOpen
);
}
async
function gLazyFindCommand(cmd, ...args) {
let fb = await gFindBarPromise;
// We could be closed by now, or the tab with XBL binding could have gone away:
if (fb && fb[cmd]) {
fb[cmd].apply(fb, args);
}
}
var gPageIcons = {
"about:home":
"chrome://branding/content/icon32.png",
"about:newtab":
"chrome://branding/content/icon32.png",
"about:welcome":
"chrome://branding/content/icon32.png",
"about:privatebrowsing":
"chrome://browser/skin/privatebrowsing/favicon.svg",
};
var gInitialPages = [
"about:blank",
"about:home",
"about:firefoxview",
"about:newtab",
"about:privatebrowsing",
"about:sessionrestore",
"about:welcome",
"about:welcomeback",
"chrome://browser/content/blanktab.html",
];
function isInitialPage(url) {
if (!(url
instanceof Ci.nsIURI)) {
try {
url = Services.io.newURI(url);
}
catch (ex) {
return false;
}
}
let nonQuery = url.prePath + url.filePath;
return gInitialPages.includes(nonQuery) || nonQuery == BROWSER_NEW_TAB_URL;
}
function browserWindows() {
return Services.wm.getEnumerator(
"navigator:browser");
}
function updateBookmarkToolbarVisibility() {
BookmarkingUI.updateEmptyToolbarMessage();
setToolbarVisibility(
BookmarkingUI.toolbar,
gBookmarksToolbarVisibility,
false,
false
);
}
// This is a stringbundle-like interface to gBrowserBundle, formerly a getter for
// the "bundle_browser" element.
var gNavigatorBundle = {
getString(key) {
return gBrowserBundle.GetStringFromName(key);
},
getFormattedString(key, array) {
return gBrowserBundle.formatStringFromName(key, array);
},
};
var gScreenshots = {
shouldScreenshotsButtonBeDisabled() {
// About pages other than about:reader are not currently supported by
// the screenshots extension (see Bug 1620992).
let uri = gBrowser.selectedBrowser.currentURI;
let shouldBeDisabled =
gScreenshotsDisabled ||
(!gScreenshotsComponentEnabled &&
uri.scheme ===
"about" &&
!uri.spec.startsWith(
"about:reader"));
return shouldBeDisabled;
},
};
function updateFxaToolbarMenu(enable, isInitialUpdate =
false) {
// We only show the Firefox Account toolbar menu if the feature is enabled and
// if sync is enabled.
const syncEnabled = Services.prefs.getBoolPref(
"identity.fxaccounts.enabled",
false
);
const mainWindowEl = document.documentElement;
const fxaPanelEl = PanelMultiView.getViewNode(document,
"PanelUI-fxa");
// To minimize the toolbar button flickering or appearing/disappearing during startup,
// we use this pref to anticipate the likely FxA status.
const statusGuess = !!Services.prefs.getStringPref(
"identity.fxaccounts.account.device.name",
""
);
mainWindowEl.setAttribute(
"fxastatus",
statusGuess ?
"signed_in" :
"not_configured"
);
fxaPanelEl.addEventListener(
"ViewShowing", gSync.updateSendToDeviceTitle);
if (enable && syncEnabled) {
mainWindowEl.setAttribute(
"fxatoolbarmenu",
"visible");
// We have to manually update the sync state UI when toggling the FxA toolbar
// because it could show an invalid icon if the user is logged in and no sync
// event was performed yet.
if (!isInitialUpdate) {
gSync.maybeUpdateUIState();
}
}
else {
mainWindowEl.removeAttribute(
"fxatoolbarmenu");
}
}
function UpdateBackForwardCommands(aWebNavigation) {
var backCommand = document.getElementById(
"Browser:Back");
var forwardCommand = document.getElementById(
"Browser:Forward");
// Avoid setting attributes on commands if the value hasn't changed!
// Remember, guys, setting attributes on elements is expensive! They
// get inherited into anonymous content, broadcast to other widgets, etc.!
// Don't do it if the value hasn't changed! - dwh
var backDisabled = backCommand.hasAttribute(
"disabled");
var forwardDisabled = forwardCommand.hasAttribute(
"disabled");
if (backDisabled == aWebNavigation.canGoBack) {
if (backDisabled) {
backCommand.removeAttribute(
"disabled");
}
else {
backCommand.setAttribute(
"disabled",
true);
}
}
if (forwardDisabled == aWebNavigation.canGoForward) {
if (forwardDisabled) {
forwardCommand.removeAttribute(
"disabled");
}
else {
forwardCommand.setAttribute(
"disabled",
true);
}
}
}
function updatePrintCommands(enabled) {
var printCommand = document.getElementById(
"cmd_print");
var printPreviewCommand = document.getElementById(
"cmd_printPreviewToggle");
if (enabled) {
printCommand.removeAttribute(
"disabled");
printPreviewCommand.removeAttribute(
"disabled");
}
else {
printCommand.setAttribute(
"disabled",
"true");
printPreviewCommand.setAttribute(
"disabled",
"true");
}
}
/**
* Click-and-Hold implementation for the Back and Forward buttons
* XXXmano: should this live in toolbarbutton.js?
*/
function SetClickAndHoldHandlers() {
// Bug 414797: Clone the back/forward buttons' context menu into both buttons.
let popup = document.getElementById(
"backForwardMenu").cloneNode(
true);
popup.removeAttribute(
"id");
// Prevent the back/forward buttons' context attributes from being inherited.
popup.setAttribute(
"context",
"");
function backForwardMenuCommand(event) {
BrowserCommands.gotoHistoryIndex(event);
// event.stopPropagation is here for the cloned version
// to prevent already-handled clicks on menu items from
// propagating to the back or forward button.
event.stopPropagation();
}
let backButton = document.getElementById(
"back-button");
backButton.setAttribute(
"type",
"menu");
popup.addEventListener(
"command", backForwardMenuCommand);
popup.addEventListener(
"popupshowing", FillHistoryMenu);
backButton.prepend(popup);
gClickAndHoldListenersOnElement.add(backButton);
let forwardButton = document.getElementById(
"forward-button");
popup = popup.cloneNode(
true);
forwardButton.setAttribute(
"type",
"menu");
popup.addEventListener(
"command", backForwardMenuCommand);
popup.addEventListener(
"popupshowing", FillHistoryMenu);
forwardButton.prepend(popup);
gClickAndHoldListenersOnElement.add(forwardButton);
}
const gClickAndHoldListenersOnElement = {
_timers:
new Map(),
_mousedownHandler(aEvent) {
if (
aEvent.button != 0 ||
aEvent.currentTarget.open ||
aEvent.currentTarget.disabled
) {
return;
}
// Prevent the menupopup from opening immediately
aEvent.currentTarget.menupopup.hidden =
true;
aEvent.currentTarget.addEventListener(
"mouseout",
this);
aEvent.currentTarget.addEventListener(
"mouseup",
this);
this._timers.set(
aEvent.currentTarget,
setTimeout(b =>
this._openMenu(b), 500, aEvent.currentTarget)
);
},
_clickHandler(aEvent) {
if (
aEvent.button == 0 &&
aEvent.target == aEvent.currentTarget &&
!aEvent.currentTarget.open &&
!aEvent.currentTarget.disabled &&
// When menupopup is not hidden and we receive
// a click event, it means the mousedown occurred
// on aEvent.currentTarget and mouseup occurred on
// aEvent.currentTarget.menupopup, we don't
// need to handle the click event as menupopup
// handled mouseup event already.
aEvent.currentTarget.menupopup.hidden
) {
let cmdEvent = document.createEvent(
"xulcommandevent");
cmdEvent.initCommandEvent(
"command",
true,
true,
window,
0,
aEvent.ctrlKey,
aEvent.altKey,
aEvent.shiftKey,
aEvent.metaKey,
0,
null,
aEvent.inputSource
);
aEvent.currentTarget.dispatchEvent(cmdEvent);
// This is here to cancel the XUL default event
// dom.click() triggers a command even if there is a click handler
// however this can now be prevented with preventDefault().
aEvent.preventDefault();
}
},
_openMenu(aButton) {
this._cancelHold(aButton);
aButton.firstElementChild.hidden =
false;
aButton.open =
true;
},
_mouseoutHandler(aEvent) {
let buttonRect = aEvent.currentTarget.getBoundingClientRect();
if (
aEvent.clientX >= buttonRect.left &&
aEvent.clientX <= buttonRect.right &&
aEvent.clientY >= buttonRect.bottom
) {
this._openMenu(aEvent.currentTarget);
}
else {
this._cancelHold(aEvent.currentTarget);
}
},
_mouseupHandler(aEvent) {
this._cancelHold(aEvent.currentTarget);
},
_cancelHold(aButton) {
clearTimeout(
this._timers.get(aButton));
aButton.removeEventListener(
"mouseout",
this);
aButton.removeEventListener(
"mouseup",
this);
},
_keypressHandler(aEvent) {
if (aEvent.key ==
" " || aEvent.key ==
"Enter") {
aEvent.preventDefault();
// Normally, command events get fired for keyboard activation. However,
// we've set type="menu", so that doesn't happen. Handle this the same
// way we handle clicks.
aEvent.target.click();
}
},
handleEvent(e) {
switch (e.type) {
case "mouseout":
this._mouseoutHandler(e);
break;
case "mousedown":
this._mousedownHandler(e);
break;
case "click":
this._clickHandler(e);
break;
case "mouseup":
this._mouseupHandler(e);
break;
case "keypress":
// Note that we might not be the only ones dealing with keypresses.
// See bug 1921772 for more context.
if (!e.defaultPrevented) {
this._keypressHandler(e);
}
break;
}
},
remove(aButton) {
aButton.removeEventListener(
"mousedown",
this,
true);
aButton.removeEventListener(
"click",
this,
true);
aButton.removeEventListener(
"keypress",
this,
true);
},
add(aElm) {
this._timers.
delete(aElm);
aElm.addEventListener(
"mousedown",
this,
true);
aElm.addEventListener(
"click",
this,
true);
aElm.addEventListener(
"keypress",
this,
true);
},
};
const gSessionHistoryObserver = {
observe(subject, topic) {
if (topic !=
"browser:purge-session-history") {
return;
}
var backCommand = document.getElementById(
"Browser:Back");
backCommand.setAttribute(
"disabled",
"true");
var fwdCommand = document.getElementById(
"Browser:Forward");
fwdCommand.setAttribute(
"disabled",
"true");
// Clear undo history of the URL bar
gURLBar.editor.clearUndoRedo();
},
};
const gStoragePressureObserver = {
_lastNotificationTime: -1,
async observe(subject, topic) {
if (topic !=
"QuotaManager::StoragePressure") {
return;
}
const NOTIFICATION_VALUE =
"storage-pressure-notification";
if (gNotificationBox.getNotificationWithValue(NOTIFICATION_VALUE)) {
// Do not display the 2nd notification when there is already one
return;
}
// Don't display notification twice within the given interval.
// This is because
// - not to annoy user
// - give user some time to clean space.
// Even user sees notification and starts acting, it still takes some time.
const MIN_NOTIFICATION_INTERVAL_MS = Services.prefs.getIntPref(
"browser.storageManager.pressureNotification.minIntervalMS"
);
let duration = Date.now() -
this._lastNotificationTime;
if (duration <= MIN_NOTIFICATION_INTERVAL_MS) {
return;
}
this._lastNotificationTime = Date.now();
MozXULElement.insertFTLIfNeeded(
"branding/brand.ftl");
MozXULElement.insertFTLIfNeeded(
"browser/preferences/preferences.ftl");
const BYTES_IN_GIGABYTE = 1073741824;
const USAGE_THRESHOLD_BYTES =
BYTES_IN_GIGABYTE *
Services.prefs.getIntPref(
"browser.storageManager.pressureNotification.usageThresholdGB"
);
let messageFragment = document.createDocumentFragment();
let message = document.createElement(
"span");
let buttons = [{ supportPage:
"storage-permissions" }];
let usage = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
if (usage < USAGE_THRESHOLD_BYTES) {
// The firefox-used space < 5GB, then warn user to free some disk space.
// This is because this usage is small and not the main cause for space issue.
// In order to avoid the bad and wrong impression among users that
// firefox eats disk space a lot, indicate users to clean up other disk space.
document.l10n.setAttributes(message,
"space-alert-under-5gb-message2");
}
else {
// The firefox-used space >= 5GB, then guide users to about:preferences
// to clear some data stored on firefox by websites.
document.l10n.setAttributes(message,
"space-alert-over-5gb-message2");
buttons.push({
"l10n-id":
"space-alert-over-5gb-settings-button",
callback() {
// The advanced subpanes are only supported in the old organization, which will
// be removed by bug 1349689.
openPreferences(
"privacy-sitedata");
},
});
}
messageFragment.appendChild(message);
await gNotificationBox.appendNotification(
NOTIFICATION_VALUE,
{
label: messageFragment,
priority: gNotificationBox.PRIORITY_WARNING_HIGH,
},
buttons
);
// This seems to be necessary to get the buttons to display correctly
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1504216
document.l10n.translateFragment(gNotificationBox.currentNotification);
},
};
var gKeywordURIFixup = {
check(browser, { fixedURI, keywordProviderName, preferredURI }) {
// We get called irrespective of whether we did a keyword search, or
// whether the original input would be vaguely interpretable as a URL,
// so figure that out first.
if (
!keywordProviderName ||
!fixedURI ||
!fixedURI.host ||
UrlbarPrefs.get(
"browser.fixup.dns_first_for_single_words") ||
UrlbarPrefs.get(
"dnsResolveSingleWordsAfterSearch") == 0
) {
return;
}
let contentPrincipal = browser.contentPrincipal;
// At this point we're still only just about to load this URI.
// When the async DNS lookup comes back, we may be in any of these states:
// 1) still on the previous URI, waiting for the preferredURI (keyword
// search) to respond;
// 2) at the keyword search URI (preferredURI)
// 3) at some other page because the user stopped navigation.
// We keep track of the currentURI to detect case (1) in the DNS lookup
// callback.
let previousURI = browser.currentURI;
// now swap for a weak ref so we don't hang on to browser needlessly
// even if the DNS query takes forever
let weakBrowser = Cu.getWeakReference(browser);
browser =
null;
// Additionally, we need the host of the parsed url
let hostName = fixedURI.displayHost;
// and the ascii-only host for the pref:
let asciiHost = fixedURI.asciiHost;
let onLookupCompleteListener = {
async onLookupComplete(request, record, status) {
let browserRef = weakBrowser.get();
if (!Components.isSuccessCode(status) || !browserRef) {
return;
}
let currentURI = browserRef.currentURI;
// If we're in case (3) (see above), don't show an info bar.
if (
!currentURI.equals(previousURI) &&
!currentURI.equals(preferredURI)
) {
return;
}
// show infobar offering to visit the host
let notificationBox = gBrowser.getNotificationBox(browserRef);
if (notificationBox.getNotificationWithValue(
"keyword-uri-fixup")) {
return;
}
let displayHostName =
"http://" + hostName + "/";
let message = gNavigatorBundle.getFormattedString(
"keywordURIFixup.message",
[displayHostName]
);
let yesMessage = gNavigatorBundle.getFormattedString(
"keywordURIFixup.goTo",
[displayHostName]
);
let buttons = [
{
label: yesMessage,
accessKey: gNavigatorBundle.getString(
"keywordURIFixup.goTo.accesskey"
),
callback() {
// Do not set this preference while in private browsing.
if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
let prefHost = asciiHost;
// Normalize out a single trailing dot - NB: not using endsWith/lastIndexOf
// because we need to be sure this last dot is the *only* dot, too.
// More generally, this is used for the pref and should stay in sync with
// the code in URIFixup::KeywordURIFixup .
if (prefHost.indexOf(
".") == prefHost.length - 1) {
prefHost = prefHost.slice(0, -1);
}
let pref =
"browser.fixup.domainwhitelist." + prefHost;
Services.prefs.setBoolPref(pref,
true);
}
openTrustedLinkIn(fixedURI.spec,
"current");
},
},
];
let notification = await notificationBox.appendNotification(
"keyword-uri-fixup",
{
label: message,
priority: notificationBox.PRIORITY_INFO_HIGH,
},
buttons
);
notification.persistence = 1;
},
};
Services.uriFixup.checkHost(
fixedURI,
onLookupCompleteListener,
contentPrincipal.originAttributes
);
},
observe(fixupInfo) {
fixupInfo.QueryInterface(Ci.nsIURIFixupInfo);
let browser = fixupInfo.consumer?.top?.embedderElement;
if (!browser || browser.ownerGlobal != window) {
return;
}
this.check(browser, fixupInfo);
},
};
/* Creates a null principal using the userContextId
from the current selected tab or a passed in tab argument */
function _createNullPrincipalFromTabUserContextId(tab = gBrowser.selectedTab) {
let userContextId;
if (tab.hasAttribute(
"usercontextid")) {
userContextId = tab.getAttribute(
"usercontextid");
}
return Services.scriptSecurityManager.createNullPrincipal({
userContextId,
});
}
function HandleAppCommandEvent(evt) {
switch (evt.command) {
case "Back":
BrowserCommands.back();
break;
case "Forward":
BrowserCommands.forward();
break;
case "Reload":
BrowserCommands.reloadSkipCache();
break;
case "Stop":
if (XULBrowserWindow.stopCommand.getAttribute(
"disabled") !=
"true") {
BrowserCommands.stop();
}
break;
case "Search":
BrowserSearch.webSearch();
break;
case "Bookmarks":
SidebarController.toggle(
"viewBookmarksSidebar");
break;
case "Home":
BrowserCommands.home();
break;
case "New":
BrowserCommands.openTab();
break;
case "Close":
BrowserCommands.closeTabOrWindow();
break;
case "Find":
gLazyFindCommand(
"onFindCommand");
break;
case "Help":
openHelpLink(
"firefox-help");
break;
case "Open":
BrowserCommands.openFileWindow();
break;
case "Print":
PrintUtils.startPrintWindow(gBrowser.selectedBrowser.browsingContext);
break;
case "Save":
saveBrowser(gBrowser.selectedBrowser);
break;
case "SendMail":
MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);
break;
default:
return;
}
evt.stopPropagation();
evt.preventDefault();
}
function loadOneOrMoreURIs(aURIString, aTriggeringPrincipal, aCsp) {
// we're not a browser window, pass the URI string to a new browser window
if (window.location.href != AppConstants.BROWSER_CHROME_URL) {
window.openDialog(
AppConstants.BROWSER_CHROME_URL,
"_blank",
"all,dialog=no",
aURIString
);
return;
}
// This function throws for certain malformed URIs, so use exception handling
// so that we don't disrupt startup
try {
gBrowser.loadTabs(aURIString.split(
"|"), {
inBackground:
false,
replace:
true,
triggeringPrincipal: aTriggeringPrincipal,
csp: aCsp,
});
}
catch (e) {}
}
function openLocation(event) {
if (window.location.href == AppConstants.BROWSER_CHROME_URL) {
gURLBar.select();
gURLBar.view.autoOpen({ event });
return;
}
// If there's an open browser window, redirect the command there.
let win = URILoadingHelper.getTargetWindow(window);
if (win) {
win.focus();
win.openLocation();
return;
}
// There are no open browser windows; open a new one.
window.openDialog(
AppConstants.BROWSER_CHROME_URL,
"_blank",
"chrome,all,dialog=no",
BROWSER_NEW_TAB_URL
);
}
var gLastOpenDirectory = {
_lastDir:
null,
get path() {
if (!
this._lastDir || !
this._lastDir.exists()) {
try {
this._lastDir = Services.prefs.getComplexValue(
"browser.open.lastDir",
Ci.nsIFile
);
if (!
this._lastDir.exists()) {
this._lastDir =
null;
}
}
catch (e) {}
}
return this._lastDir;
},
set path(val) {
try {
if (!val || !val.isDirectory()) {
return;
}
}
catch (e) {
return;
}
this._lastDir = val.clone();
// Don't save the last open directory pref inside the Private Browsing mode
if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
Services.prefs.setComplexValue(
"browser.open.lastDir",
Ci.nsIFile,
this._lastDir
);
}
},
reset() {
this._lastDir =
null;
},
};
function getLoadContext() {
return window.docShell.QueryInterface(Ci.nsILoadContext);
}
function readFromClipboard() {
var url;
try {
// Create transferable that will transfer the text.
var trans = Cc[
"@mozilla.org/widget/transferable;1"].createInstance(
Ci.nsITransferable
);
trans.init(getLoadContext());
trans.addDataFlavor(
"text/plain");
// If available, use selection clipboard, otherwise global one
let clipboard = Services.clipboard;
if (clipboard.isClipboardTypeSupported(clipboard.kSelectionClipboard)) {
clipboard.getData(trans, clipboard.kSelectionClipboard);
}
else {
clipboard.getData(trans, clipboard.kGlobalClipboard);
}
var data = {};
trans.getTransferData(
"text/plain", data);
if (data) {
data = data.value.QueryInterface(Ci.nsISupportsString);
url = data.data;
}
}
catch (ex) {}
return url;
}
function UpdateUrlbarSearchSplitterState() {
var splitter = document.getElementById(
"urlbar-search-splitter");
var urlbar = document.getElementById(
"urlbar-container");
var searchbar = document.getElementById(
"search-container");
if (document.documentElement.hasAttribute(
"customizing")) {
if (splitter) {
splitter.remove();
}
return;
}
// If the splitter is already in the right place, we don't need to do anything:
if (
splitter &&
((splitter.nextElementSibling == searchbar &&
splitter.previousElementSibling == urlbar) ||
(splitter.nextElementSibling == urlbar &&
splitter.previousElementSibling == searchbar))
) {
return;
}
let ibefore =
null;
let resizebefore =
"none";
let resizeafter =
"none";
if (urlbar && searchbar) {
if (urlbar.nextElementSibling == searchbar) {
resizeafter =
"sibling";
ibefore = searchbar;
}
else if (searchbar.nextElementSibling == urlbar) {
resizebefore =
"sibling";
ibefore = urlbar;
}
}
if (ibefore) {
if (!splitter) {
splitter = document.createXULElement(
"splitter");
splitter.id =
"urlbar-search-splitter";
splitter.setAttribute(
"resizebefore", resizebefore);
splitter.setAttribute(
"resizeafter", resizeafter);
splitter.setAttribute(
"skipintoolbarset",
"true");
splitter.setAttribute(
"overflows",
"false");
splitter.className =
"chromeclass-toolbar-additional";
}
urlbar.parentNode.insertBefore(splitter, ibefore);
}
else if (splitter) {
splitter.remove();
}
}
function UpdatePopupNotificationsVisibility() {
// Only need to update PopupNotifications if it has already been initialized
// for this window (i.e. its getter no longer exists).
if (!Object.getOwnPropertyDescriptor(window,
"PopupNotifications").get) {
// Notify PopupNotifications that the visible anchors may have changed. This
// also checks the suppression state according to the "shouldSuppress"
// function defined earlier in this file.
PopupNotifications.anchorVisibilityChange();
}
// This is similar to the above, but for notifications attached to the
// hamburger menu icon (such as update notifications and add-on install
// notifications.)
PanelUI?.updateNotifications();
}
function PageProxyClickHandler(aEvent) {
if (aEvent.button == 1 && Services.prefs.getBoolPref(
"middlemouse.paste")) {
middleMousePaste(aEvent);
}
}
/**
* Handle command events bubbling up from error page content
* or from about:newtab or from remote error pages that invoke
* us via async messaging.
*/
var BrowserOnClick = {
async ignoreWarningLink(reason, blockedInfo, browsingContext) {
// Add a notify bar before allowing the user to continue through to the
// site, so that they don't lose track after, e.g., tab switching.
// We can't use browser.contentPrincipal which is principal of about:blocked
// Create one from uri with current principal origin attributes
let principal = Services.scriptSecurityManager.createContentPrincipal(
Services.io.newURI(blockedInfo.uri),
browsingContext.currentWindowGlobal.documentPrincipal.originAttributes
);
Services.perms.addFromPrincipal(
principal,
"safe-browsing",
Ci.nsIPermissionManager.ALLOW_ACTION,
Ci.nsIPermissionManager.EXPIRE_SESSION
);
let buttons = [
{
label: gNavigatorBundle.getString(
"safebrowsing.getMeOutOfHereButton.label"
),
accessKey: gNavigatorBundle.getString(
"safebrowsing.getMeOutOfHereButton.accessKey"
),
callback() {
getMeOutOfHere(browsingContext);
},
},
];
let title;
if (reason ===
"malware") {
let reportUrl = gSafeBrowsing.getReportURL(
"MalwareMistake", blockedInfo);
title = gNavigatorBundle.getString(
"safebrowsing.reportedAttackSite");
// There's no button if we can not get report url, for example if the provider
// of blockedInfo is not Google
if (reportUrl) {
buttons[1] = {
label: gNavigatorBundle.getString(
"safebrowsing.notAnAttackButton.label"
),
accessKey: gNavigatorBundle.getString(
"safebrowsing.notAnAttackButton.accessKey"
),
callback() {
openTrustedLinkIn(reportUrl,
"tab");
},
};
}
}
else if (reason ===
"phishing") {
let reportUrl = gSafeBrowsing.getReportURL(
"PhishMistake", blockedInfo);
title = gNavigatorBundle.getString(
"safebrowsing.deceptiveSite");
// There's no button if we can not get report url, for example if the provider
// of blockedInfo is not Google
if (reportUrl) {
buttons[1] = {
label: gNavigatorBundle.getString(
"safebrowsing.notADeceptiveSiteButton.label"
),
accessKey: gNavigatorBundle.getString(
"safebrowsing.notADeceptiveSiteButton.accessKey"
),
callback() {
openTrustedLinkIn(reportUrl,
"tab");
},
};
}
}
else if (reason ===
"unwanted") {
title = gNavigatorBundle.getString(
"safebrowsing.reportedUnwantedSite");
// There is no button for reporting errors since Google doesn't currently
// provide a URL endpoint for these reports.
}
else if (reason ===
"harmful") {
title = gNavigatorBundle.getString(
"safebrowsing.reportedHarmfulSite");
// There is no button for reporting errors since Google doesn't currently
// provide a URL endpoint for these reports.
}
await SafeBrowsingNotificationBox.show(title, buttons);
// Allow users to override and continue through to the site.
// Note that we have to use the passed URI info and can't just
// rely on the document URI, because the latter contains
// additional query parameters that should be stripped.
let triggeringPrincipal =
blockedInfo.triggeringPrincipal ||
_createNullPrincipalFromTabUserContextId();
browsingContext.fixupAndLoadURIString(blockedInfo.uri, {
triggeringPrincipal,
loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
});
},
};
/**
* Re-direct the browser to a known-safe page. This function is
* used when, for example, the user browses to a known malware page
* and is presented with about:blocked. The "Get me out of here!"
* button should take the user to the default start page so that even
* when their own homepage is infected, we can get them somewhere safe.
*/
function getMeOutOfHere(browsingContext) {
browsingContext.top.fixupAndLoadURIString(getDefaultHomePage(), {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
// Also needs to load homepage
});
}
/**
* Return the default start page for the cases when the user's own homepage is
* infected, so we can get them somewhere safe.
*/
function getDefaultHomePage() {
let url = BROWSER_NEW_TAB_URL;
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
return url;
}
url = HomePage.getDefault();
// If url is a pipe-delimited set of pages, just take the first one.
if (url.includes(
"|")) {
url = url.split(
"|")[0];
}
return url;
}
// TODO: can we pull getPEMString in from pippki.js instead of
// duplicating them here?
function getPEMString(cert) {
var derb64 = cert.getBase64DERString();
// Wrap the Base64 string into lines of 64 characters,
// with CRLF line breaks (as specified in RFC 1421).
var wrapped = derb64.replace(/(\S{64}(?!$))/g,
"$1\r\n");
return (
"-----BEGIN CERTIFICATE-----\r\n" +
wrapped +
"\r\n-----END CERTIFICATE-----\r\n"
);
}
var browserDragAndDrop = {
canDropLink: aEvent => Services.droppedLinkHandler.canDropLink(aEvent,
true),
dragOver(aEvent) {
if (
this.canDropLink(aEvent)) {
aEvent.preventDefault();
}
},
getTriggeringPrincipal(aEvent) {
return Services.droppedLinkHandler.getTriggeringPrincipal(aEvent);
},
getCsp(aEvent) {
return Services.droppedLinkHandler.getCsp(aEvent);
},
validateURIsForDrop(aEvent, aURIs) {
return Services.droppedLinkHandler.validateURIsForDrop(aEvent, aURIs);
},
dropLinks(aEvent, aDisallowInherit) {
return Services.droppedLinkHandler.dropLinks(aEvent, aDisallowInherit);
},
};
var homeButtonObserver = {
onDrop(aEvent) {
// disallow setting home pages that inherit the principal
let links = browserDragAndDrop.dropLinks(aEvent,
true);
if (links.length) {
let urls = [];
for (let link of links) {
if (link.url.includes(
"|")) {
urls.push(...link.url.split(
"|"));
}
else {
urls.push(link.url);
}
}
try {
browserDragAndDrop.validateURIsForDrop(aEvent, urls);
}
catch (e) {
return;
}
setTimeout(openHomeDialog, 0, urls.join(
"|"));
}
},
onDragOver(aEvent) {
if (HomePage.locked) {
return;
}
browserDragAndDrop.dragOver(aEvent);
aEvent.dropEffect =
"link";
},
};
function openHomeDialog(aURL) {
var promptTitle = gNavigatorBundle.getString(
"droponhometitle");
var promptMsg;
if (aURL.includes(
"|")) {
promptMsg = gNavigatorBundle.getString(
"droponhomemsgMultiple");
}
else {
promptMsg = gNavigatorBundle.getString(
"droponhomemsg");
}
var pressedVal = Services.prompt.confirmEx(
window,
promptTitle,
promptMsg,
Services.prompt.STD_YES_NO_BUTTONS,
null,
null,
null,
null,
{ value: 0 }
);
if (pressedVal == 0) {
HomePage.set(aURL).
catch(console.error);
}
}
var newTabButtonObserver = {
onDragOver(aEvent) {
browserDragAndDrop.dragOver(aEvent);
},
async onDrop(aEvent) {
let links = browserDragAndDrop.dropLinks(aEvent);
if (
links.length >=
Services.prefs.getIntPref(
"browser.tabs.maxOpenBeforeWarn")
) {
// Sync dialog cannot be used inside drop event handler.
let answer = await OpenInTabsUtils.promiseConfirmOpenInTabs(
links.length,
window
);
if (!answer) {
return;
}
}
let where = aEvent.shiftKey ?
"tabshifted" :
"tab";
let triggeringPrincipal = browserDragAndDrop.getTriggeringPrincipal(aEvent);
let csp = browserDragAndDrop.getCsp(aEvent);
for (let link of links) {
if (link.url) {
let data = await UrlbarUtils.getShortcutOrURIAndPostData(link.url);
// Allow third-party services to fixup this URL.
openLinkIn(data.url, where, {
postData: data.postData,
allowThirdPartyFixup:
true,
triggeringPrincipal,
csp,
});
}
}
},
};
var newWindowButtonObserver = {
onDragOver(aEvent) {
browserDragAndDrop.dragOver(aEvent);
},
async onDrop(aEvent) {
let links = browserDragAndDrop.dropLinks(aEvent);
if (
links.length >=
Services.prefs.getIntPref(
"browser.tabs.maxOpenBeforeWarn")
) {
// Sync dialog cannot be used inside drop event handler.
let answer = await OpenInTabsUtils.promiseConfirmOpenInTabs(
links.length,
window
);
if (!answer) {
return;
}
}
let triggeringPrincipal = browserDragAndDrop.getTriggeringPrincipal(aEvent);
let csp = browserDragAndDrop.getCsp(aEvent);
for (let link of links) {
if (link.url) {
let data = await UrlbarUtils.getShortcutOrURIAndPostData(link.url);
// Allow third-party services to fixup this URL.
openLinkIn(data.url,
"window", {
// TODO fix allowInheritPrincipal
// (this is required by javascript: drop to the new window) Bug 1475201
allowInheritPrincipal:
true,
postData: data.postData,
allowThirdPartyFixup:
true,
triggeringPrincipal,
csp,
});
}
}
},
};
const BrowserSearch = {
_searchInitComplete:
false,
init() {
Services.obs.addObserver(
this,
"browser-search-engine-modified");
},
delayedStartupInit() {
// Asynchronously initialize the search service if necessary, to get the
// current engine for working out the placeholder.
this._updateURLBarPlaceholderFromDefaultEngine(
PrivateBrowsingUtils.isWindowPrivate(window),
// Delay the update for this until so that we don't change it while
// the user is looking at it / isn't expecting it.
true
).then(() => {
this._searchInitComplete =
true;
});
},
uninit() {
Services.obs.removeObserver(
this,
"browser-search-engine-modified");
},
observe(engine, topic, data) {
// There are two kinds of search engine objects, nsISearchEngine objects and
// plain { uri, title, icon } objects. `engine` in this method is the
// former. The browser.engines and browser.hiddenEngines arrays are the
// latter, and they're the engines offered by the the page in the browser.
//
// The two types of engines are currently related by their titles/names,
// although that may change; see bug 335102.
let engineName = engine.wrappedJSObject.name;
switch (data) {
case "engine-removed":
// An engine was removed from the search service. If a page is offering
// the engine, then the engine needs to be added back to the corresponding
// browser's offered engines.
this._addMaybeOfferedEngine(engineName);
break;
case "engine-added":
// An engine was added to the search service. If a page is offering the
// engine, then the engine needs to be removed from the corresponding
// browser's offered engines.
this._removeMaybeOfferedEngine(engineName);
break;
case "engine-default":
if (
this._searchInitComplete &&
!PrivateBrowsingUtils.isWindowPrivate(window)
) {
this._updateURLBarPlaceholder(engineName,
false);
}
break;
case "engine-default-private":
if (
this._searchInitComplete &&
PrivateBrowsingUtils.isWindowPrivate(window)
) {
this._updateURLBarPlaceholder(engineName,
true);
}
break;
}
},
_addMaybeOfferedEngine(engineName) {
let selectedBrowserOffersEngine =
false;
for (let browser of gBrowser.browsers) {
for (let i = 0; i < (browser.hiddenEngines || []).length; i++) {
if (browser.hiddenEngines[i].title == engineName) {
if (!browser.engines) {
browser.engines = [];
}
browser.engines.push(browser.hiddenEngines[i]);
browser.hiddenEngines.splice(i, 1);
if (browser == gBrowser.selectedBrowser) {
selectedBrowserOffersEngine =
true;
}
break;
}
}
}
if (selectedBrowserOffersEngine) {
this.updateOpenSearchBadge();
}
},
_removeMaybeOfferedEngine(engineName) {
let selectedBrowserOffersEngine =
false;
for (let browser of gBrowser.browsers) {
for (let i = 0; i < (browser.engines || []).length; i++) {
if (browser.engines[i].title == engineName) {
if (!browser.hiddenEngines) {
browser.hiddenEngines = [];
}
browser.hiddenEngines.push(browser.engines[i]);
browser.engines.splice(i, 1);
if (browser == gBrowser.selectedBrowser) {
selectedBrowserOffersEngine =
true;
}
break;
}
}
}
if (selectedBrowserOffersEngine) {
this.updateOpenSearchBadge();
}
},
/**
* Initializes the urlbar placeholder to the pre-saved engine name. We do this
* via a preference, to avoid needing to synchronously init the search service.
*
* This should be called around the time of DOMContentLoaded, so that it is
* initialized quickly before the user sees anything.
*
* Note: If the preference doesn't exist, we don't do anything as the default
* placeholder is a string which doesn't have the engine name; however, this
* can be overridden using the `force` parameter.
*
* @param {Boolean} force If true and the preference doesn't exist, the
* placeholder will be set to the default version
* without an engine name ("Search or enter address").
*/
initPlaceHolder(force =
false) {
const prefName =
"browser.urlbar.placeholderName" +
(PrivateBrowsingUtils.isWindowPrivate(window) ?
".private" :
"");
let engineName = Services.prefs.getStringPref(prefName,
"");
if (engineName || force) {
// We can do this directly, since we know we're at DOMContentLoaded.
this._setURLBarPlaceholder(engineName);
}
},
/**
* This is a wrapper around '_updateURLBarPlaceholder' that uses the
* appropriate default engine to get the engine name.
*
* @param {Boolean} isPrivate Set to true if this is a private window.
* @param {Boolean} [delayUpdate] Set to true, to delay update until the
* placeholder is not displayed.
*/
async _updateURLBarPlaceholderFromDefaultEngine(
isPrivate,
delayUpdate =
false
) {
const getDefault = isPrivate
? Services.search.getDefaultPrivate
: Services.search.getDefault;
let defaultEngine = await getDefault();
if (!
this._searchInitComplete) {
// If we haven't finished initialising, ensure the placeholder
// preference is set for the next startup.
SearchUIUtils.updatePlaceholderNamePreference(defaultEngine, isPrivate);
}
this._updateURLBarPlaceholder(defaultEngine.name, isPrivate, delayUpdate);
},
/**
* Updates the URLBar placeholder for the specified engine, delaying the
* update if required. This also saves the current engine name in preferences
* for the next restart.
*
* Note: The engine name will only be displayed for built-in engines, as we
* know they should have short names.
*
* @param {String} engineName The search engine name to use for the update.
* @param {Boolean} isPrivate Set to true if this is a private window.
* @param {Boolean} [delayUpdate] Set to true, to delay update until the
* placeholder is not displayed.
*/
_updateURLBarPlaceholder(engineName, isPrivate, delayUpdate =
false) {
if (!engineName) {
throw new Error(
"Expected an engineName to be specified");
}
const engine = Services.search.getEngineByName(engineName);
if (!engine.isAppProvided) {
// Set the engine name to an empty string for non-default engines, which'll
// make sure we display the default placeholder string.
engineName =
"";
}
// Only delay if requested, and we're not displaying text in the URL bar
// currently.
if (delayUpdate && !gURLBar.value) {
// Delays changing the URL Bar placeholder until the user is not going to be
// seeing it, e.g. when there is a value entered in the bar, or if there is
// a tab switch to a tab which has a url loaded. We delay the update until
// the user is out of search mode since an alternative placeholder is used
// in search mode.
let placeholderUpdateListener = () => {
if (gURLBar.value && !gURLBar.searchMode) {
// By the time the user has switched, they may have changed the engine
// again, so we need to call this function again but with the
// new engine name.
// No need to await for this to finish, we're in a listener here anyway.
this._updateURLBarPlaceholderFromDefaultEngine(isPrivate,
false);
gURLBar.removeEventListener(
"input", placeholderUpdateListener);
gBrowser.tabContainer.removeEventListener(
"TabSelect",
placeholderUpdateListener
);
}
};
gURLBar.addEventListener(
"input", placeholderUpdateListener);
gBrowser.tabContainer.addEventListener(
"TabSelect",
placeholderUpdateListener
);
}
else if (!gURLBar.searchMode) {
this._setURLBarPlaceholder(engineName);
}
},
/**
* Sets the URLBar placeholder to either something based on the engine name,
* or the default placeholder.
*
* @param {String} name The name of the engine to use, an empty string if to
* use the default placeholder.
*/
_setURLBarPlaceholder(name) {
document.l10n.setAttributes(
gURLBar.inputField,
name ?
"urlbar-placeholder-with-name" :
"urlbar-placeholder",
name ? { name } : undefined
);
},
addEngine(browser, engine) {
if (!
this._searchInitComplete) {
--> --------------------
--> maximum size reached
--> --------------------