Issue #303 Part 1: Move basilisk files from /browser to /application/basilisk

This commit is contained in:
wolfbeast 2018-06-04 13:17:38 +02:00 • committed by Roy Tam
commit 177b28a898
1938 changed files with 0 additions and 0 deletions

View file

@ -1,428 +0,0 @@
/* 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/. */
// Note: this file is included in aboutDialog.xul if MOZ_UPDATER is defined.
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
"resource://gre/modules/UpdateUtils.jsm");
const PREF_APP_UPDATE_CANCELATIONS_OSX = "app.update.cancelations.osx";
const PREF_APP_UPDATE_ELEVATE_NEVER = "app.update.elevate.never";
var gAppUpdater;
function onUnload(aEvent) {
if (gAppUpdater.isChecking)
gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK);
// Safe to call even when there isn't a download in progress.
gAppUpdater.removeDownloadListener();
gAppUpdater = null;
}
function appUpdater()
{
XPCOMUtils.defineLazyServiceGetter(this, "aus",
"@mozilla.org/updates/update-service;1",
"nsIApplicationUpdateService");
XPCOMUtils.defineLazyServiceGetter(this, "checker",
"@mozilla.org/updates/update-checker;1",
"nsIUpdateChecker");
XPCOMUtils.defineLazyServiceGetter(this, "um",
"@mozilla.org/updates/update-manager;1",
"nsIUpdateManager");
this.updateDeck = document.getElementById("updateDeck");
// Hide the update deck when the update window is already open and it's not
// already applied, to avoid syncing issues between them. Applied updates
// don't have any information to sync between the windows as they both just
// show the "Restart to continue"-type button.
if (Services.wm.getMostRecentWindow("Update:Wizard") &&
!this.isApplied) {
this.updateDeck.hidden = true;
return;
}
this.bundle = Services.strings.
createBundle("chrome://browser/locale/browser.properties");
let manualURL = Services.urlFormatter.formatURLPref("app.update.url.manual");
let manualLink = document.getElementById("manualLink");
manualLink.value = manualURL;
manualLink.href = manualURL;
document.getElementById("failedLink").href = manualURL;
if (this.updateDisabledAndLocked) {
this.selectPanel("adminDisabled");
return;
}
if (this.isPending || this.isApplied) {
this.selectPanel("apply");
return;
}
if (this.aus.isOtherInstanceHandlingUpdates) {
this.selectPanel("otherInstanceHandlingUpdates");
return;
}
if (this.isDownloading) {
this.startDownload();
// selectPanel("downloading") is called from setupDownloadingUI().
return;
}
// Honor the "Never check for updates" option by not only disabling background
// update checks, but also in the About dialog, by presenting a
// "Check for updates" button.
// If updates are found, the user is then asked if he wants to "Update to <version>".
if (!this.updateEnabled ||
Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
this.selectPanel("checkForUpdates");
return;
}
// That leaves the options
// "Check for updates, but let me choose whether to install them", and
// "Automatically install updates".
// In both cases, we check for updates without asking.
// In the "let me choose" case, we ask before downloading though, in onCheckComplete.
this.checkForUpdates();
}
appUpdater.prototype =
{
// true when there is an update check in progress.
isChecking: false,
// true when there is an update already staged / ready to be applied.
get isPending() {
if (this.update) {
return this.update.state == "pending" ||
this.update.state == "pending-service" ||
this.update.state == "pending-elevate";
}
return this.um.activeUpdate &&
(this.um.activeUpdate.state == "pending" ||
this.um.activeUpdate.state == "pending-service" ||
this.um.activeUpdate.state == "pending-elevate");
},
// true when there is an update already installed in the background.
get isApplied() {
if (this.update)
return this.update.state == "applied" ||
this.update.state == "applied-service";
return this.um.activeUpdate &&
(this.um.activeUpdate.state == "applied" ||
this.um.activeUpdate.state == "applied-service");
},
// true when there is an update download in progress.
get isDownloading() {
if (this.update)
return this.update.state == "downloading";
return this.um.activeUpdate &&
this.um.activeUpdate.state == "downloading";
},
// true when updating is disabled by an administrator.
get updateDisabledAndLocked() {
return !this.updateEnabled &&
Services.prefs.prefIsLocked("app.update.enabled");
},
// true when updating is enabled.
get updateEnabled() {
try {
return Services.prefs.getBoolPref("app.update.enabled");
}
catch (e) { }
return true; // Firefox default is true
},
// true when updating in background is enabled.
get backgroundUpdateEnabled() {
return this.updateEnabled &&
gAppUpdater.aus.canStageUpdates;
},
// true when updating is automatic.
get updateAuto() {
try {
return Services.prefs.getBoolPref("app.update.auto");
}
catch (e) { }
return true; // Firefox default is true
},
/**
* Sets the panel of the updateDeck.
*
* @param aChildID
* The id of the deck's child to select, e.g. "apply".
*/
selectPanel: function(aChildID) {
let panel = document.getElementById(aChildID);
let button = panel.querySelector("button");
if (button) {
if (aChildID == "downloadAndInstall") {
let updateVersion = gAppUpdater.update.displayVersion;
button.label = this.bundle.formatStringFromName("update.downloadAndInstallButton.label", [updateVersion], 1);
button.accessKey = this.bundle.GetStringFromName("update.downloadAndInstallButton.accesskey");
}
this.updateDeck.selectedPanel = panel;
if (!document.commandDispatcher.focusedElement || // don't steal the focus
document.commandDispatcher.focusedElement.localName == "button") // except from the other buttons
button.focus();
} else {
this.updateDeck.selectedPanel = panel;
}
},
/**
* Check for updates
*/
checkForUpdates: function() {
// Clear prefs that could prevent a user from discovering available updates.
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CANCELATIONS_OSX)) {
Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX);
}
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER);
}
this.selectPanel("checkingForUpdates");
this.isChecking = true;
this.checker.checkForUpdates(this.updateCheckListener, true);
// after checking, onCheckComplete() is called
},
/**
* Handles oncommand for the "Restart to Update" button
* which is presented after the download has been downloaded.
*/
buttonRestartAfterDownload: function() {
if (!this.isPending && !this.isApplied) {
return;
}
gAppUpdater.selectPanel("restarting");
// Notify all windows that an application quit has been requested.
let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
createInstance(Components.interfaces.nsISupportsPRBool);
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
// Something aborted the quit process.
if (cancelQuit.data) {
gAppUpdater.selectPanel("apply");
return;
}
let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
getService(Components.interfaces.nsIAppStartup);
// If already in safe mode restart in safe mode (bug 327119)
if (Services.appinfo.inSafeMode) {
appStartup.restartInSafeMode(Components.interfaces.nsIAppStartup.eAttemptQuit);
return;
}
appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
Components.interfaces.nsIAppStartup.eRestart);
},
/**
* Implements nsIUpdateCheckListener. The methods implemented by
* nsIUpdateCheckListener are in a different scope from nsIIncrementalDownload
* to make it clear which are used by each interface.
*/
updateCheckListener: {
/**
* See nsIUpdateService.idl
*/
onCheckComplete: function(aRequest, aUpdates, aUpdateCount) {
gAppUpdater.isChecking = false;
gAppUpdater.update = gAppUpdater.aus.
selectUpdate(aUpdates, aUpdates.length);
if (!gAppUpdater.update) {
gAppUpdater.selectPanel("noUpdatesFound");
return;
}
if (gAppUpdater.update.unsupported) {
if (gAppUpdater.update.detailsURL) {
let unsupportedLink = document.getElementById("unsupportedLink");
unsupportedLink.href = gAppUpdater.update.detailsURL;
}
gAppUpdater.selectPanel("unsupportedSystem");
return;
}
if (!gAppUpdater.aus.canApplyUpdates) {
gAppUpdater.selectPanel("manualUpdate");
return;
}
if (gAppUpdater.updateAuto) // automatically download and install
gAppUpdater.startDownload();
else // ask
gAppUpdater.selectPanel("downloadAndInstall");
},
/**
* See nsIUpdateService.idl
*/
onError: function(aRequest, aUpdate) {
// Errors in the update check are treated as no updates found. If the
// update check fails repeatedly without a success the user will be
// notified with the normal app update user interface so this is safe.
gAppUpdater.isChecking = false;
gAppUpdater.selectPanel("noUpdatesFound");
},
/**
* See nsISupports.idl
*/
QueryInterface: function(aIID) {
if (!aIID.equals(Components.interfaces.nsIUpdateCheckListener) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
},
/**
* Starts the download of an update mar.
*/
startDownload: function() {
if (!this.update)
this.update = this.um.activeUpdate;
this.update.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
this.update.setProperty("foregroundDownload", "true");
this.aus.pauseDownload();
let state = this.aus.downloadUpdate(this.update, false);
if (state == "failed") {
this.selectPanel("downloadFailed");
return;
}
this.setupDownloadingUI();
},
/**
* Switches to the UI responsible for tracking the download.
*/
setupDownloadingUI: function() {
this.downloadStatus = document.getElementById("downloadStatus");
this.downloadStatus.value =
DownloadUtils.getTransferTotal(0, this.update.selectedPatch.size);
this.selectPanel("downloading");
this.aus.addDownloadListener(this);
},
removeDownloadListener: function() {
if (this.aus) {
this.aus.removeDownloadListener(this);
}
},
/**
* See nsIRequestObserver.idl
*/
onStartRequest: function(aRequest, aContext) {
},
/**
* See nsIRequestObserver.idl
*/
onStopRequest: function(aRequest, aContext, aStatusCode) {
switch (aStatusCode) {
case Components.results.NS_ERROR_UNEXPECTED:
if (this.update.selectedPatch.state == "download-failed" &&
(this.update.isCompleteUpdate || this.update.patchCount != 2)) {
// Verification error of complete patch, informational text is held in
// the update object.
this.removeDownloadListener();
this.selectPanel("downloadFailed");
break;
}
// Verification failed for a partial patch, complete patch is now
// downloading so return early and do NOT remove the download listener!
break;
case Components.results.NS_BINDING_ABORTED:
// Do not remove UI listener since the user may resume downloading again.
break;
case Components.results.NS_OK:
this.removeDownloadListener();
if (this.backgroundUpdateEnabled) {
this.selectPanel("applying");
let self = this;
Services.obs.addObserver(function (aSubject, aTopic, aData) {
// Update the UI when the background updater is finished
let status = aData;
if (status == "applied" || status == "applied-service" ||
status == "pending" || status == "pending-service" ||
status == "pending-elevate") {
// If the update is successfully applied, or if the updater has
// fallen back to non-staged updates, show the "Restart to Update"
// button.
self.selectPanel("apply");
} else if (status == "failed") {
// Background update has failed, let's show the UI responsible for
// prompting the user to update manually.
self.selectPanel("downloadFailed");
} else if (status == "downloading") {
// We've fallen back to downloading the full update because the
// partial update failed to get staged in the background.
// Therefore we need to keep our observer.
self.setupDownloadingUI();
return;
}
Services.obs.removeObserver(arguments.callee, "update-staged");
}, "update-staged", false);
} else {
this.selectPanel("apply");
}
break;
default:
this.removeDownloadListener();
this.selectPanel("downloadFailed");
break;
}
},
/**
* See nsIProgressEventSink.idl
*/
onStatus: function(aRequest, aContext, aStatus, aStatusArg) {
},
/**
* See nsIProgressEventSink.idl
*/
onProgress: function(aRequest, aContext, aProgress, aProgressMax) {
this.downloadStatus.value =
DownloadUtils.getTransferTotal(aProgress, aProgressMax);
},
/**
* See nsISupports.idl
*/
QueryInterface: function(aIID) {
if (!aIID.equals(Components.interfaces.nsIProgressEventSink) &&
!aIID.equals(Components.interfaces.nsIRequestObserver) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};

View file

@ -1,97 +0,0 @@
/* 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/. */
#aboutDialog {
width: 620px;
/* Set an explicit line-height to avoid discrepancies in 'auto' spacing
across screens with different device DPI, which may cause font metrics
to round differently. */
line-height: 1.5;
}
#rightBox {
background-image: url("chrome://branding/content/about-wordmark.png");
background-repeat: no-repeat;
/* padding-top creates room for the wordmark */
padding-top: 38px;
margin-top:20px;
}
#rightBox:-moz-locale-dir(rtl) {
background-position: 100% 0;
}
#bottomBox {
padding: 15px 10px 0;
}
#version {
font-weight: bold;
margin-top: 10px;
margin-left: 0;
-moz-user-select: text;
-moz-user-focus: normal;
cursor: text;
}
#version:-moz-locale-dir(rtl) {
direction: ltr;
text-align: right;
margin-left: 5px;
margin-right: 0;
}
#releasenotes {
margin-top: 10px;
}
#distribution,
#distributionId {
display: none;
margin-top: 0;
margin-bottom: 0;
}
.text-blurb {
margin-bottom: 10px;
margin-inline-start: 0;
padding-inline-start: 0;
}
#updateButton,
#updateDeck > hbox > label {
margin-inline-start: 0;
padding-inline-start: 0;
}
.update-throbber {
width: 16px;
min-height: 16px;
margin-inline-end: 3px;
list-style-image: url("chrome://global/skin/icons/loading.png");
}
@media (min-resolution: 1.1dppx) {
.update-throbber {
list-style-image: url("chrome://global/skin/icons/loading@2x.png");
}
}
description > .text-link,
description > .text-link:focus {
margin: 0px;
padding: 0px;
}
.bottom-link,
.bottom-link:focus {
text-align: center;
margin: 0 40px;
}
#currentChannel {
margin: 0;
padding: 0;
font-weight: bold;
}

View file

@ -1,91 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// Services = object with smart getters for common XPCOM services
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/AppConstants.jsm");
function init(aEvent)
{
if (aEvent.target != document)
return;
try {
var distroId = Services.prefs.getCharPref("distribution.id");
if (distroId) {
var distroVersion = Services.prefs.getCharPref("distribution.version");
var distroIdField = document.getElementById("distributionId");
distroIdField.value = distroId + " - " + distroVersion;
distroIdField.style.display = "block";
try {
// This is in its own try catch due to bug 895473 and bug 900925.
var distroAbout = Services.prefs.getComplexValue("distribution.about",
Components.interfaces.nsISupportsString);
var distroField = document.getElementById("distribution");
distroField.value = distroAbout;
distroField.style.display = "block";
}
catch (ex) {
// Pref is unset
Components.utils.reportError(ex);
}
}
}
catch (e) {
// Pref is unset
}
// Include the build ID
let versionField = document.getElementById("version");
let version = Services.appinfo.version;
let buildID = Services.appinfo.appBuildID;
let year = buildID.slice(0, 4);
let month = buildID.slice(4, 6);
let day = buildID.slice(6, 8);
let hour = buildID.slice(8, 10);
let minute = buildID.slice(10, 12);
if (Services.prefs.getBoolPref("general.useragent.appVersionIsBuildID")) {
versionField.textContent = `${year}.${month}.${day}`;
} else {
versionField.textContent = `v` + version + ` (${year}-${month}-${day})`;
}
// Display warning if this is an "a#" (nightly or aurora) build
if (/a\d+$/.test(version)) {
document.getElementById("experimental").hidden = false;
document.getElementById("communityDesc").hidden = true;
}
// Append "(32-bit)" or "(64-bit)" build architecture to the version number:
let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
let archResource = Services.appinfo.is64Bit
? "aboutDialog.architecture.sixtyFourBit"
: "aboutDialog.architecture.thirtyTwoBit";
let arch = bundle.GetStringFromName(archResource);
versionField.textContent += ` (${arch})`;
// Get Release Notes URL from Preferences
let releaseNotesURL = Services.prefs.getCharPref("app.releaseNotesURL");
document.getElementById("releasenotes").setAttribute("href", releaseNotesURL);
if (AppConstants.MOZ_UPDATER) {
gAppUpdater = new appUpdater();
let channelLabel = document.getElementById("currentChannel");
let currentChannelText = document.getElementById("currentChannelText");
channelLabel.value = UpdateUtils.UpdateChannel;
if (/^release($|\-)/.test(channelLabel.value))
currentChannelText.hidden = true;
}
if (AppConstants.platform == "macosx") {
// it may not be sized at this point, and we need its width to calculate its position
window.sizeToContent();
window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
}
}

View file

@ -1,157 +0,0 @@
<?xml version="1.0"?> <!-- -*- Mode: 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/.
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/aboutDialog.css" type="text/css"?>
<?xml-stylesheet href="chrome://branding/content/aboutDialog.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % aboutDialogDTD SYSTEM "chrome://browser/locale/aboutDialog.dtd" >
%aboutDialogDTD;
]>
#ifdef XP_MACOSX
<?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?>
#endif
<window xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="aboutDialog"
windowtype="Browser:About"
onload="init(event);"
#ifdef MOZ_UPDATER
onunload="onUnload(event);"
#endif
#ifdef XP_MACOSX
inwindowmenu="false"
#else
title="&aboutDialog.title;"
#endif
role="dialog"
aria-describedby="version distribution distributionId communityDesc contributeDesc trademark"
>
<script type="application/javascript" src="chrome://browser/content/aboutDialog.js"/>
#ifdef MOZ_UPDATER
<script type="application/javascript" src="chrome://browser/content/aboutDialog-appUpdater.js"/>
#endif
<vbox id="aboutDialogContainer">
<hbox id="clientBox">
<vbox id="leftBox" flex="1"/>
<vbox id="rightBox" flex="1">
<hbox align="baseline">
#expand <label id="version"></label>
#ifndef NIGHTLY_BUILD
<label id="releasenotes" class="text-link">&releaseNotes.link;</label>
#endif
</hbox>
<label id="distribution" class="text-blurb"/>
<label id="distributionId" class="text-blurb"/>
<vbox id="detailsBox">
<vbox id="updateBox">
#ifdef MOZ_UPDATER
<deck id="updateDeck" orient="vertical">
<hbox id="checkForUpdates" align="center">
<button id="checkForUpdatesButton" align="start"
label="&update.checkForUpdatesButton.label;"
accesskey="&update.checkForUpdatesButton.accesskey;"
oncommand="gAppUpdater.checkForUpdates();"/>
<spacer flex="1"/>
</hbox>
<hbox id="downloadAndInstall" align="center">
<button id="downloadAndInstallButton" align="start"
oncommand="gAppUpdater.startDownload();"/>
<!-- label and accesskey will be filled by JS -->
<spacer flex="1"/>
</hbox>
<hbox id="apply" align="center">
<button id="updateButton" align="start"
label="&update.updateButton.label2;"
accesskey="&update.updateButton.accesskey;"
oncommand="gAppUpdater.buttonRestartAfterDownload();"/>
<spacer flex="1"/>
</hbox>
<hbox id="checkingForUpdates" align="center">
<image class="update-throbber"/><label>&update.checkingForUpdates;</label>
</hbox>
<hbox id="downloading" align="center">
<image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
</hbox>
<hbox id="applying" align="center">
<image class="update-throbber"/><label>&update.applying;</label>
</hbox>
<hbox id="downloadFailed" align="center">
<label>&update.failed.start;</label><label id="failedLink" class="text-link">&update.failed.linkText;</label><label>&update.failed.end;</label>
</hbox>
<hbox id="adminDisabled" align="center">
<label>&update.adminDisabled;</label>
</hbox>
<hbox id="noUpdatesFound" align="center">
<label>&update.noUpdatesFound;</label>
</hbox>
<hbox id="otherInstanceHandlingUpdates" align="center">
<label>&update.otherInstanceHandlingUpdates;</label>
</hbox>
<hbox id="manualUpdate" align="center">
<label>&update.manual.start;</label><label id="manualLink" class="text-link"/><label>&update.manual.end;</label>
</hbox>
<hbox id="unsupportedSystem" align="center">
<label>&update.unsupported.start;</label><label id="unsupportedLink" class="text-link">&update.unsupported.linkText;</label><label>&update.unsupported.end;</label>
</hbox>
<hbox id="restarting" align="center">
<label>&update.restarting;</label>
</hbox>
</deck>
#endif
</vbox>
#ifdef MOZ_UPDATER
<description class="text-blurb" id="currentChannelText">
&channel.description.start;<label id="currentChannel"/>&channel.description.end;
</description>
#endif
<vbox id="experimental" hidden="true">
<description class="text-blurb" id="warningDesc">
&warningDesc.version;
</description>
</vbox>
#ifdef MC_PRIVATE_BUILD
<description class="text-blurb" id="communityDesc">
This is a private build of Basilisk. If you did not manually build this copy from source yourself, then please download an official version from the <label class="text-link" href="http://www.basilisk-browser.org/">Basilisk website</label>.
</description>
#else
<description class="text-blurb" id="communityDesc">
Basilisk is community software released by <label class="text-link" href="http://www.palemoon.org/">the Pale Moon team</label> and Mozilla developers. Learn <label class="text-link" useoriginprincipal="true" href="about:credits">who contributed</label> to this software.
</description>
<description class="text-blurb" id="contributeDesc">
Want to help? Please consider <label class="text-link" href="https://www.palemoon.org/donations.shtml">donating</label> or get involved with our <label class="text-link" href="https://github.com/MoonchildProductions/UXP">development</label> of the Unified XUL Platform.
</description>
#endif
</vbox>
</vbox>
</hbox>
<vbox id="bottomBox">
<hbox pack="center">
<label class="text-link bottom-link" useoriginprincipal="true" href="about:license">&bottomLinks.license;</label>
<label class="text-link bottom-link" useoriginprincipal="true" href="about:rights">&bottomLinks.rights;</label>
<label class="text-link bottom-link" href="https://www.palemoon.org/privacy.shtml">&bottomLinks.privacy;</label>
</hbox>
<description id="trademark">&trademarkInfo.part1;</description>
</vbox>
</vbox>
<keyset>
<key keycode="VK_ESCAPE" oncommand="window.close();"/>
</keyset>
#ifdef XP_MACOSX
#include browserMountPoints.inc
#endif
</window>

View file

@ -1,699 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % netErrorDTD
SYSTEM "chrome://global/locale/netError.dtd">
%netErrorDTD;
<!ENTITY % globalDTD
SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
]>
<!-- 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 xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&loadError.label;</title>
<link rel="stylesheet" href="chrome://browser/skin/aboutNetError.css" type="text/css" media="all" />
<!-- If the location of the favicon is changed here, the FAVICON_ERRORPAGE_URL symbol in
toolkit/components/places/src/nsFaviconService.h should be updated. -->
<link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/warning-16.png"/>
<script type="application/javascript"><![CDATA[
// The following parameters are parsed from the error URL:
// e - the error code
// s - custom CSS class to allow alternate styling/favicons
// d - error description
// captive - "true" to indicate we're behind a captive portal.
// Any other value is ignored.
// Note that this file uses document.documentURI to get
// the URL (with the format from above). This is because
// document.location.href gets the current URI off the docshell,
// which is the URL displayed in the location bar, i.e.
// the URI that the user attempted to load.
let searchParams = new URLSearchParams(document.documentURI.split("?")[1]);
// Set to true on init if the error code is nssBadCert.
let gIsCertError;
function getErrorCode()
{
return searchParams.get("e");
}
function getCSSClass()
{
return searchParams.get("s");
}
function getDescription()
{
return searchParams.get("d");
}
function isCaptive() {
return searchParams.get("captive") == "true";
}
function retryThis(buttonEl)
{
// Note: The application may wish to handle switching off "offline mode"
// before this event handler runs, but using a capturing event handler.
// Session history has the URL of the page that failed
// to load, not the one of the error page. So, just call
// reload(), which will also repost POST data correctly.
try {
location.reload();
} catch (e) {
// We probably tried to reload a URI that caused an exception to
// occur; e.g. a nonexistent file.
}
buttonEl.disabled = true;
}
function doOverride(buttonEl) {
var event = new CustomEvent("AboutNetErrorOverride", {bubbles:true});
document.dispatchEvent(event);
retryThis(buttonEl);
}
function toggleDisplay(node) {
const toggle = {
"": "block",
"none": "block",
"block": "none"
};
return (node.style.display = toggle[node.style.display]);
}
function showCertificateErrorReporting() {
// Display error reporting UI
document.getElementById("certificateErrorReporting").style.display = "block";
}
function showPrefChangeContainer() {
const panel = document.getElementById("prefChangeContainer");
panel.style.display = "block";
document.getElementById("netErrorButtonContainer").style.display = "none";
document.getElementById("prefResetButton").addEventListener("click", function resetPreferences(e) {
const event = new CustomEvent("AboutNetErrorResetPreferences", {bubbles:true});
document.dispatchEvent(event);
});
addAutofocus("prefResetButton", "beforeend");
}
function setupAdvancedButton(allowOverride) {
// Get the hostname and add it to the panel
var panelId = gIsCertError ? "badCertAdvancedPanel" : "weakCryptoAdvancedPanel";
var panel = document.getElementById(panelId);
for (var span of panel.querySelectorAll("span.hostname")) {
span.textContent = document.location.hostname;
}
if (!gIsCertError) {
panel.replaceChild(document.getElementById("errorLongDesc"),
document.getElementById("advancedLongDesc"));
}
// Register click handler for the weakCryptoAdvancedPanel
document.getElementById("advancedButton")
.addEventListener("click", function togglePanelVisibility() {
toggleDisplay(panel);
if (gIsCertError) {
// Toggling the advanced panel must ensure that the debugging
// information panel is hidden as well, since it's opened by the
// error code link in the advanced panel.
var div = document.getElementById("certificateErrorDebugInformation");
div.style.display = "none";
}
if (panel.style.display == "block") {
// send event to trigger telemetry ping
var event = new CustomEvent("AboutNetErrorUIExpanded", {bubbles:true});
document.dispatchEvent(event);
}
});
if (allowOverride) {
document.getElementById("overrideWeakCryptoPanel").style.display = "flex";
var overrideLink = document.getElementById("overrideWeakCrypto");
overrideLink.addEventListener("click", () => doOverride(overrideLink), false);
}
if (!gIsCertError) {
return;
}
if (getCSSClass() == "expertBadCert") {
toggleDisplay(document.getElementById("badCertAdvancedPanel"));
// Toggling the advanced panel must ensure that the debugging
// information panel is hidden as well, since it's opened by the
// error code link in the advanced panel.
var div = document.getElementById("certificateErrorDebugInformation");
div.style.display = "none";
}
disallowCertOverridesIfNeeded();
document.getElementById("badCertTechnicalInfo").textContent = getDescription();
}
function disallowCertOverridesIfNeeded() {
var cssClass = getCSSClass();
// Disallow overrides if this is a Strict-Transport-Security
// host and the cert is bad (STS Spec section 7.3) or if the
// certerror is in a frame (bug 633691).
if (cssClass == "badStsCert" || window != top) {
document.getElementById("exceptionDialogButton").setAttribute("hidden", "true");
}
if (cssClass == "badStsCert") {
document.getElementById("badStsCertExplanation").removeAttribute("hidden");
}
}
function initPage()
{
var err = getErrorCode();
gIsCertError = (err == "nssBadCert");
// Only worry about captive portals if this is a cert error.
let showCaptivePortalUI = isCaptive() && gIsCertError;
if (showCaptivePortalUI) {
err = "captivePortal";
}
// if it's an unknown error or there's no title or description
// defined, get the generic message
var errTitle = document.getElementById("et_" + err);
var errDesc = document.getElementById("ed_" + err);
if (!errTitle || !errDesc)
{
errTitle = document.getElementById("et_generic");
errDesc = document.getElementById("ed_generic");
}
document.querySelector(".title-text").innerHTML = errTitle.innerHTML;
var sd = document.getElementById("errorShortDescText");
if (sd) {
if (gIsCertError) {
sd.innerHTML = errDesc.innerHTML;
}
else {
sd.textContent = getDescription();
}
}
if (showCaptivePortalUI) {
initPageCaptivePortal();
return;
}
if (gIsCertError) {
initPageCertError();
return;
}
addAutofocus("errorTryAgain");
document.body.className = "neterror";
var ld = document.getElementById("errorLongDesc");
if (ld)
{
ld.innerHTML = errDesc.innerHTML;
}
if (err == "sslv3Used") {
document.getElementById("learnMoreContainer").style.display = "block";
var learnMoreLink = document.getElementById("learnMoreLink");
learnMoreLink.href = "https://support.mozilla.org/kb/how-resolve-sslv3-error-messages-firefox";
document.body.className = "certerror";
}
if (err == "weakCryptoUsed") {
document.body.className = "certerror";
}
// remove undisplayed errors to avoid bug 39098
var errContainer = document.getElementById("errorContainer");
errContainer.parentNode.removeChild(errContainer);
var className = getCSSClass();
if (className && className != "expertBadCert") {
// Associate a CSS class with the root of the page, if one was passed in,
// to allow custom styling.
// Not "expertBadCert" though, don't want to deal with the favicon
document.documentElement.className = className;
// Also, if they specified a CSS class, they must supply their own
// favicon. In order to trigger the browser to repaint though, we
// need to remove/add the link element.
var favicon = document.getElementById("favicon");
var faviconParent = favicon.parentNode;
faviconParent.removeChild(favicon);
favicon.setAttribute("href", "chrome://global/skin/icons/" + className + "_favicon.png");
faviconParent.appendChild(favicon);
}
if (err == "remoteXUL") {
// Remove the "Try again" button for remote XUL errors given that
// it is useless.
document.getElementById("netErrorButtonContainer").style.display = "none";
}
if (err == "cspBlocked") {
// Remove the "Try again" button for CSP violations, since it's
// almost certainly useless. (Bug 553180)
document.getElementById("netErrorButtonContainer").style.display = "none";
}
window.addEventListener("AboutNetErrorOptions", function(evt) {
// Pinning errors are of type nssFailure2
if (getErrorCode() == "nssFailure2" || getErrorCode() == "weakCryptoUsed") {
document.getElementById("learnMoreContainer").style.display = "block";
var learnMoreLink = document.getElementById("learnMoreLink");
// nssFailure2 also gets us other non-overrideable errors. Choose
// a "learn more" link based on description:
if (getDescription().includes("mozilla_pkix_error_key_pinning_failure")) {
learnMoreLink.href = "https://support.mozilla.org/kb/certificate-pinning-reports";
}
if (getErrorCode() == "weakCryptoUsed") {
learnMoreLink.href = "https://support.mozilla.org/kb/how-resolve-weak-crypto-error-messages-firefox";
}
var options = JSON.parse(evt.detail);
if (options && options.enabled) {
var checkbox = document.getElementById("automaticallyReportInFuture");
showCertificateErrorReporting();
if (options.automatic) {
// set the checkbox
checkbox.checked = true;
}
checkbox.addEventListener("change", function(evt) {
var event = new CustomEvent("AboutNetErrorSetAutomatic",
{bubbles:true, detail:evt.target.checked});
document.dispatchEvent(event);
}, false);
}
const hasPrefStyleError = [
"interrupted", // This happens with subresources that are above the max tls
"SSL_ERROR_PROTOCOL_VERSION_ALERT",
"SSL_ERROR_UNSUPPORTED_VERSION",
"SSL_ERROR_NO_CYPHER_OVERLAP",
"SSL_ERROR_NO_CIPHERS_SUPPORTED"
].some((substring) => getDescription().includes(substring));
// If it looks like an error that is user config based
if (getErrorCode() == "nssFailure2" && hasPrefStyleError && options && options.changedCertPrefs) {
showPrefChangeContainer();
}
}
if (getErrorCode() == "weakCryptoUsed" || getErrorCode() == "sslv3Used") {
setupAdvancedButton(getErrorCode() == "weakCryptoUsed");
}
}.bind(this), true, true);
var event = new CustomEvent("AboutNetErrorLoad", {bubbles:true});
document.dispatchEvent(event);
if (err == "inadequateSecurityError") {
// Remove the "Try again" button for HTTP/2 inadequate security as it
// is useless.
document.getElementById("errorTryAgain").style.display = "none";
var container = document.getElementById("errorLongDesc");
for (var span of container.querySelectorAll("span.hostname")) {
span.textContent = document.location.hostname;
}
}
addDomainErrorLinks();
}
function initPageCaptivePortal()
{
document.body.className = "captiveportal";
document.title = document.getElementById("captivePortalPageTitle").textContent;
document.getElementById("openPortalLoginPageButton")
.addEventListener("click", () => {
let event = new CustomEvent("AboutNetErrorOpenCaptivePortal", {bubbles:true});
document.dispatchEvent(event);
});
addAutofocus("openPortalLoginPageButton");
setupAdvancedButton(true);
addDomainErrorLinks();
// When the portal is freed, an event is generated by the frame script
// that we can pick up and attempt to reload the original page.
window.addEventListener("AboutNetErrorCaptivePortalFreed", () => {
document.location.reload();
});
}
function initPageCertError() {
document.body.className = "certerror";
document.title = document.getElementById("certErrorPageTitle").textContent;
for (let host of document.querySelectorAll(".hostname")) {
host.textContent = document.location.hostname;
}
addAutofocus("returnButton");
setupAdvancedButton(true);
document.getElementById("learnMoreContainer").style.display = "block";
let checkbox = document.getElementById("automaticallyReportInFuture");
checkbox.addEventListener("change", function({target: {checked}}) {
document.dispatchEvent(new CustomEvent("AboutNetErrorSetAutomatic", {
detail: checked,
bubbles: true
}));
});
addEventListener("AboutNetErrorOptions", function(event) {
var options = JSON.parse(event.detail);
if (options && options.enabled) {
// Display error reporting UI
document.getElementById("certificateErrorReporting").style.display = "block";
// set the checkbox
checkbox.checked = !!options.automatic;
}
}, true, true);
let event = new CustomEvent("AboutNetErrorLoad", {bubbles:true});
document.getElementById("advancedButton").dispatchEvent(event);
addDomainErrorLinks();
}
/* Only do autofocus if we're the toplevel frame; otherwise we
don't want to call attention to ourselves! The key part is
that autofocus happens on insertion into the tree, so we
can remove the button, add @autofocus, and reinsert the
button.
*/
function addAutofocus(buttonId, position = "afterbegin") {
if (window.top == window) {
var button = document.getElementById(buttonId);
var parent = button.parentNode;
button.remove();
button.setAttribute("autofocus", "true");
parent.insertAdjacentElement(position, button);
}
}
/* Try to preserve the links contained in the error description, like
the error code.
Also, in the case of SSL error pages about domain mismatch, see if
we can hyperlink the user to the correct site. We don't want
to do this generically since it allows MitM attacks to redirect
users to a site under attacker control, but in certain cases
it is safe (and helpful!) to do so. Bug 402210
*/
function addDomainErrorLinks() {
// Rather than textContent, we need to treat description as HTML
var sdid = gIsCertError ? "badCertTechnicalInfo" : "errorShortDescText";
var sd = document.getElementById(sdid);
if (sd) {
var desc = getDescription();
// sanitize description text - see bug 441169
// First, find the index of the <a> tags we care about, being
// careful not to use an over-greedy regex.
var codeRe = /<a id="errorCode" title="([^"]+)">/;
var codeResult = codeRe.exec(desc);
var domainRe = /<a id="cert_domain_link" title="([^"]+)">/;
var domainResult = domainRe.exec(desc);
// The order of these links in the description is fixed in
// TransportSecurityInfo.cpp:formatOverridableCertErrorMessage.
var firstResult = domainResult;
if (!domainResult)
firstResult = codeResult;
if (!firstResult)
return;
// Remove sd's existing children
sd.textContent = "";
// Everything up to the first link should be text content.
sd.appendChild(document.createTextNode(desc.slice(0, firstResult.index)));
// Now create the actual links.
if (domainResult) {
createLink(sd, "cert_domain_link", domainResult[1])
// Append text for anything between the two links.
sd.appendChild(document.createTextNode(desc.slice(desc.indexOf("</a>") + "</a>".length, codeResult.index)));
}
createLink(sd, "errorCode", codeResult[1])
// Finally, append text for anything after the last closing </a>.
sd.appendChild(document.createTextNode(desc.slice(desc.lastIndexOf("</a>") + "</a>".length)));
}
if (gIsCertError) {
// Initialize the error code link embedded in the error message to
// display debug information about the cert error.
var errorCode = document.getElementById("errorCode");
if (errorCode) {
errorCode.href = "javascript:void(0)";
errorCode.addEventListener("click", () => {
let debugInfo = document.getElementById("certificateErrorDebugInformation");
debugInfo.style.display = "block";
debugInfo.scrollIntoView({block: "start", behavior: "smooth"});
}, false);
}
}
// Initialize the cert domain link.
var link = document.getElementById("cert_domain_link");
if (!link)
return;
var okHost = link.getAttribute("title");
var thisHost = document.location.hostname;
var proto = document.location.protocol;
// If okHost is a wildcard domain ("*.example.com") let's
// use "www" instead. "*.example.com" isn't going to
// get anyone anywhere useful. bug 432491
okHost = okHost.replace(/^\*\./, "www.");
/* case #1:
* example.com uses an invalid security certificate.
*
* The certificate is only valid for www.example.com
*
* Make sure to include the "." ahead of thisHost so that
* a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
*
* We'd normally just use a RegExp here except that we lack a
* library function to escape them properly (bug 248062), and
* domain names are famous for having '.' characters in them,
* which would allow spurious and possibly hostile matches.
*/
if (okHost.endsWith("." + thisHost))
link.href = proto + okHost;
/* case #2:
* browser.garage.maemo.org uses an invalid security certificate.
*
* The certificate is only valid for garage.maemo.org
*/
if (thisHost.endsWith("." + okHost))
link.href = proto + okHost;
// If we set a link, meaning there's something helpful for
// the user here, expand the section by default
if (link.href && getCSSClass() != "expertBadCert") {
var panelId = gIsCertError ? "badCertAdvancedPanel" : "weakCryptoAdvancedPanel"
toggleDisplay(document.getElementById(panelId));
if (gIsCertError) {
// Toggling the advanced panel must ensure that the debugging
// information panel is hidden as well, since it's opened by the
// error code link in the advanced panel.
var div = document.getElementById("certificateErrorDebugInformation");
div.style.display = "none";
}
}
}
function createLink(el, id, text) {
var anchorEl = document.createElement("a");
anchorEl.setAttribute("id", id);
anchorEl.setAttribute("title", text);
anchorEl.appendChild(document.createTextNode(text));
el.appendChild(anchorEl);
}
]]></script>
</head>
<body dir="&locale.dir;">
<!-- Contains an alternate page title set on page init for cert errors. -->
<div id="certErrorPageTitle" style="display: none;">&certerror.pagetitle1;</div>
<div id="captivePortalPageTitle" style="display: none;">&captivePortal.title;</div>
<!-- ERROR ITEM CONTAINER (removed during loading to avoid bug 39098) -->
<div id="errorContainer">
<div id="errorTitlesContainer">
<h1 id="et_generic">&generic.title;</h1>
<h1 id="et_captivePortal">&captivePortal.title;</h1>
<h1 id="et_dnsNotFound">&dnsNotFound.title;</h1>
<h1 id="et_fileNotFound">&fileNotFound.title;</h1>
<h1 id="et_fileAccessDenied">&fileAccessDenied.title;</h1>
<h1 id="et_malformedURI">&malformedURI.title;</h1>
<h1 id="et_unknownProtocolFound">&unknownProtocolFound.title;</h1>
<h1 id="et_connectionFailure">&connectionFailure.title;</h1>
<h1 id="et_netTimeout">&netTimeout.title;</h1>
<h1 id="et_redirectLoop">&redirectLoop.title;</h1>
<h1 id="et_unknownSocketType">&unknownSocketType.title;</h1>
<h1 id="et_netReset">&netReset.title;</h1>
<h1 id="et_notCached">&notCached.title;</h1>
<h1 id="et_netOffline">&netOffline.title;</h1>
<h1 id="et_netInterrupt">&netInterrupt.title;</h1>
<h1 id="et_deniedPortAccess">&deniedPortAccess.title;</h1>
<h1 id="et_proxyResolveFailure">&proxyResolveFailure.title;</h1>
<h1 id="et_proxyConnectFailure">&proxyConnectFailure.title;</h1>
<h1 id="et_contentEncodingError">&contentEncodingError.title;</h1>
<h1 id="et_unsafeContentType">&unsafeContentType.title;</h1>
<h1 id="et_nssFailure2">&nssFailure2.title;</h1>
<h1 id="et_nssBadCert">&certerror.longpagetitle1;</h1>
<h1 id="et_cspBlocked">&cspBlocked.title;</h1>
<h1 id="et_remoteXUL">&remoteXUL.title;</h1>
<h1 id="et_corruptedContentErrorv2">&corruptedContentErrorv2.title;</h1>
<h1 id="et_sslv3Used">&sslv3Used.title;</h1>
<h1 id="et_weakCryptoUsed">&weakCryptoUsed.title;</h1>
<h1 id="et_inadequateSecurityError">&inadequateSecurityError.title;</h1>
</div>
<div id="errorDescriptionsContainer">
<div id="ed_generic">&generic.longDesc;</div>
<div id="ed_captivePortal">&captivePortal.longDesc;</div>
<div id="ed_dnsNotFound">&dnsNotFound.longDesc;</div>
<div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
<div id="ed_fileAccessDenied">&fileAccessDenied.longDesc;</div>
<div id="ed_malformedURI">&malformedURI.longDesc;</div>
<div id="ed_unknownProtocolFound">&unknownProtocolFound.longDesc;</div>
<div id="ed_connectionFailure">&connectionFailure.longDesc;</div>
<div id="ed_netTimeout">&netTimeout.longDesc;</div>
<div id="ed_redirectLoop">&redirectLoop.longDesc;</div>
<div id="ed_unknownSocketType">&unknownSocketType.longDesc;</div>
<div id="ed_netReset">&netReset.longDesc;</div>
<div id="ed_notCached">&notCached.longDesc;</div>
<div id="ed_netOffline">&netOffline.longDesc2;</div>
<div id="ed_netInterrupt">&netInterrupt.longDesc;</div>
<div id="ed_deniedPortAccess">&deniedPortAccess.longDesc;</div>
<div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc;</div>
<div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div>
<div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div>
<div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div>
<div id="ed_nssFailure2">&nssFailure2.longDesc2;</div>
<div id="ed_nssBadCert">&certerror.introPara;</div>
<div id="ed_cspBlocked">&cspBlocked.longDesc;</div>
<div id="ed_remoteXUL">&remoteXUL.longDesc;</div>
<div id="ed_corruptedContentErrorv2">&corruptedContentErrorv2.longDesc;</div>
<div id="ed_sslv3Used">&sslv3Used.longDesc2;</div>
<div id="ed_weakCryptoUsed">&weakCryptoUsed.longDesc2;</div>
<div id="ed_inadequateSecurityError">&inadequateSecurityError.longDesc;</div>
</div>
</div>
<!-- PAGE CONTAINER (for styling purposes only) -->
<div id="errorPageContainer" class="container">
<!-- Error Title -->
<div class="title">
<h1 class="title-text"/>
</div>
<!-- LONG CONTENT (the section most likely to require scrolling) -->
<div id="errorLongContent">
<!-- Short Description -->
<div id="errorShortDesc">
<p id="errorShortDescText" />
</div>
<p id="badStsCertExplanation" hidden="true">&certerror.whatShouldIDo.badStsCertExplanation;</p>
<div id="wrongSystemTimePanel" style="display: none;">
&certerror.wrongSystemTime;
</div>
<!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
<div id="errorLongDesc" />
<div id="learnMoreContainer">
<p><a href="https://support.mozilla.org/kb/what-does-your-connection-is-not-secure-mean" id="learnMoreLink" target="new">&errorReporting.learnMore;</a></p>
</div>
<div id="prefChangeContainer" class="button-container">
<p>&prefReset.longDesc;</p>
<button id="prefResetButton" class="primary" autocomplete="off">&prefReset.label;</button>
</div>
<div id="certErrorAndCaptivePortalButtonContainer" class="button-container">
<button id="returnButton" class="primary" autocomplete="off">&returnToPreviousPage.label;</button>
<button id="openPortalLoginPageButton" class="primary" autocomplete="off">&openPortalLoginPage.label;</button>
<div class="button-spacer"></div>
<button id="advancedButton" autocomplete="off">&advanced.label;</button>
</div>
</div>
<div id="netErrorButtonContainer" class="button-container">
<button id="errorTryAgain" class="primary" autocomplete="off" onclick="retryThis(this);">&retry.label;</button>
</div>
<!-- UI for option to report certificate errors to Mozilla. Removed on
init for other error types .-->
<div id="certificateErrorReporting">
<p class="toggle-container-with-text">
<input type="checkbox" id="automaticallyReportInFuture" />
<label for="automaticallyReportInFuture" id="automaticallyReportInFuture">&errorReporting.automatic2;</label>
</p>
</div>
<div id="advancedPanelContainer">
<div id="weakCryptoAdvancedPanel" class="advanced-panel">
<div id="weakCryptoAdvancedDescription">
<p>&weakCryptoAdvanced.longDesc;</p>
</div>
<div id="advancedLongDesc" />
<div id="overrideWeakCryptoPanel">
<a id="overrideWeakCrypto" href="#">&weakCryptoAdvanced.override;</a>
</div>
</div>
<div id="badCertAdvancedPanel" class="advanced-panel">
<p id="badCertTechnicalInfo"/>
<button id="exceptionDialogButton">&securityOverride.exceptionButtonLabel;</button>
</div>
</div>
</div>
<div id="certificateErrorDebugInformation">
<button id="copyToClipboard">&certerror.copyToClipboard.label;</button>
<div id="certificateErrorText"/>
<button id="copyToClipboard">&certerror.copyToClipboard.label;</button>
</div>
<!--
- Note: It is important to run the script this way, instead of using
- an onload handler. This is because error pages are loaded as
- LOAD_BACKGROUND, which means that onload handlers will not be executed.
-->
<script type="application/javascript">
initPage();
</script>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -1,108 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % netErrorDTD
SYSTEM "chrome://global/locale/netError.dtd">
%netErrorDTD;
<!ENTITY % globalDTD
SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % aboutrobotsDTD
SYSTEM "chrome://browser/locale/aboutRobots.dtd">
%aboutrobotsDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&robots.pagetitle;</title>
<link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all" />
<link rel="icon" type="image/png" id="favicon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAACGFjVEwAAAASAAAAAJNtBPIAAAAaZmNUTAAAAAAAAAAQAAAAEAAAAAAAAAAALuAD6AABhIDeugAAALhJREFUOI2Nk8sNxCAMRDlGohauXFOMpfTiAlxICqAELltHLqlgctg1InzMRhpFAc%2BLGWTnmoeZYamt78zXdZmaQtQMADlnU0OIAlbmJUBEcO4bRKQY2rUXIPmAGnDuG%2FBx3%2FfvOPVaDUg%2BoAPUf1PArIMCSD5glMEsUGaG%2BkyAFWIBaCsKuA%2BHGCNijLgP133XgOEtaPFMy2vUolEGJoCIzBmoRUR9%2B7rxj16DZaW%2FmgtmxnJ8V3oAnApQwNS5zpcAAAAaZmNUTAAAAAEAAAAQAAAAEAAAAAAAAAAAAB4D6AIB52fclgAAACpmZEFUAAAAAjiNY2AYBVhBc3Pzf2LEcGreqcbwH1kDNjHauWAUjAJyAADymxf9WF%2Bu8QAAABpmY1RMAAAAAwAAABAAAAAQAAAAAAAAAAAAHgPoAgEK8Q9%2FAAAAFmZkQVQAAAAEOI1jYBgFo2AUjAIIAAAEEAAB0xIn4wAAABpmY1RMAAAABQAAABAAAAAQAAAAAAAAAAAAHgPoAgHnO30FAAAAQGZkQVQAAAAGOI1jYBieYKcaw39ixHCC%2F6cwFWMTw2rz%2F1MM%2F6Vu%2Ff%2F%2F%2FxTD%2F51qEIwuRjsXILuEGLFRMApgAADhNCsVfozYcAAAABpmY1RMAAAABwAAABAAAAAQAAAAAAAAAAAAHgPoAgEKra7sAAAAFmZkQVQAAAAIOI1jYBgFo2AUjAIIAAAEEAABM9s3hAAAABpmY1RMAAAACQAAABAAAAAQAAAAAAAAAAAAHgPoAgHn3p%2BwAAAAKmZkQVQAAAAKOI1jYBgFWEFzc%2FN%2FYsRwat6pxvAfWQM2Mdq5YBSMAnIAAPKbF%2F1BhPl6AAAAGmZjVEwAAAALAAAAEAAAABAAAAAAAAAAAAAeA%2BgCAQpITFkAAAAWZmRBVAAAAAw4jWNgGAWjYBSMAggAAAQQAAHaszpmAAAAGmZjVEwAAAANAAAAEAAAABAAAAAAAAAAAAAeA%2BgCAeeCPiMAAABAZmRBVAAAAA44jWNgGJ5gpxrDf2LEcIL%2FpzAVYxPDavP%2FUwz%2FpW79%2F%2F%2F%2FFMP%2FnWoQjC5GOxcgu4QYsVEwCmAAAOE0KxUmBL0KAAAAGmZjVEwAAAAPAAAAEAAAABAAAAAAAAAAAAAeA%2BgCAQoU7coAAAAWZmRBVAAAABA4jWNgGAWjYBSMAggAAAQQAAEpOBELAAAAGmZjVEwAAAARAAAAEAAAABAAAAAAAAAAAAAeA%2BgCAeYVWtoAAAAqZmRBVAAAABI4jWNgGAVYQXNz839ixHBq3qnG8B9ZAzYx2rlgFIwCcgAA8psX%2FWvpAecAAAAaZmNUTAAAABMAAAAQAAAAEAAAAAAAAAAAAB4D6AIBC4OJMwAAABZmZEFUAAAAFDiNY2AYBaNgFIwCCAAABBAAAcBQHOkAAAAaZmNUTAAAABUAAAAQAAAAEAAAAAAAAAAAAB4D6AIB5kn7SQAAAEBmZEFUAAAAFjiNY2AYnmCnGsN%2FYsRwgv%2BnMBVjE8Nq8%2F9TDP%2Blbv3%2F%2F%2F8Uw%2F%2BdahCMLkY7FyC7hBixUTAKYAAA4TQrFc%2BcEoQAAAAaZmNUTAAAABcAAAAQAAAAEAAAAAAAAAAAAB4D6AIBC98ooAAAABZmZEFUAAAAGDiNY2AYBaNgFIwCCAAABBAAASCZDI4AAAAaZmNUTAAAABkAAAAQAAAAEAAAAAAAAAAAAB4D6AIB5qwZ%2FAAAACpmZEFUAAAAGjiNY2AYBVhBc3Pzf2LEcGreqcbwH1kDNjHauWAUjAJyAADymxf9cjJWbAAAABpmY1RMAAAAGwAAABAAAAAQAAAAAAAAAAAAHgPoAgELOsoVAAAAFmZkQVQAAAAcOI1jYBgFo2AUjAIIAAAEEAAByfEBbAAAABpmY1RMAAAAHQAAABAAAAAQAAAAAAAAAAAAHgPoAgHm8LhvAAAAQGZkQVQAAAAeOI1jYBieYKcaw39ixHCC%2F6cwFWMTw2rz%2F1MM%2F6Vu%2Ff%2F%2F%2FxTD%2F51qEIwuRjsXILuEGLFRMApgAADhNCsVlxR3%2FgAAABpmY1RMAAAAHwAAABAAAAAQAAAAAAAAAAAAHgPoAgELZmuGAAAAFmZkQVQAAAAgOI1jYBgFo2AUjAIIAAAEEAABHP5cFQAAABpmY1RMAAAAIQAAABAAAAAQAAAAAAAAAAAAHgPoAgHlgtAOAAAAKmZkQVQAAAAiOI1jYBgFWEFzc%2FN%2FYsRwat6pxvAfWQM2Mdq5YBSMAnIAAPKbF%2F0%2FMvDdAAAAAElFTkSuQmCC"/>
<script type="application/javascript"><![CDATA[
var buttonClicked = false;
function robotButton()
{
var button = document.getElementById('errorTryAgain');
if (buttonClicked) {
button.style.visibility = "hidden";
} else {
var newLabel = button.getAttribute("label2");
button.textContent = newLabel;
buttonClicked = true;
}
}
]]></script>
<style type="text/css"><![CDATA[
#errorPageContainer {
background-image: none;
}
#errorPageContainer:before {
content: url('chrome://browser/content/aboutRobots-icon.png');
position: absolute;
}
body[dir=rtl] #icon,
body[dir=rtl] #errorPageContainer:before {
transform: scaleX(-1);
}
]]></style>
</head>
<body dir="&locale.dir;">
<!-- PAGE CONTAINER (for styling purposes only) -->
<div id="errorPageContainer">
<!-- Error Title -->
<div id="errorTitle">
<h1 id="errorTitleText">&robots.errorTitleText;</h1>
</div>
<!-- LONG CONTENT (the section most likely to require scrolling) -->
<div id="errorLongContent">
<!-- Short Description -->
<div id="errorShortDesc">
<p id="errorShortDescText">&robots.errorShortDescText;</p>
</div>
<!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
<div id="errorLongDesc">
<ul>
<li>&robots.errorLongDesc1;</li>
<li>&robots.errorLongDesc2;</li>
<li>&robots.errorLongDesc3;</li>
<li>&robots.errorLongDesc4;</li>
</ul>
</div>
<!-- Short Description -->
<div id="errorTrailerDesc">
<p id="errorTrailerDescText">&robots.errorTrailerDescText;</p>
</div>
</div>
<!-- Button -->
<button id="errorTryAgain"
label2="&robots.dontpress;"
onclick="robotButton();">&retry.label;</button>
<img src="chrome://browser/content/aboutRobots-widget-left.png"
style="position: absolute; bottom: -12px; left: -10px;"/>
<img src="chrome://browser/content/aboutRobots-widget-left.png"
style="position: absolute; bottom: -12px; right: -10px; transform: scaleX(-1);"/>
</div>
</body>
</html>

View file

@ -1,11 +0,0 @@
/* 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:not(.crashDumpSubmitted) #reportSent,
html:not(.crashDumpAvailable) #reportBox,
.container[multiple="true"] > .offers > #offerHelpMessageSingle,
.container[multiple="false"] > .offers > #offerHelpMessageMultiple,
.container[multiple="false"] > .button-container > #restoreAll {
display: none;
}

View file

@ -1,309 +0,0 @@
/* 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 AboutTabCrashed = {
/**
* This can be set to true once this page receives a message from the
* parent saying whether or not a crash report is available.
*/
hasReport: false,
/**
* The messages that we might receive from the parent.
*/
MESSAGES: [
"SetCrashReportAvailable",
"CrashReportSent",
"UpdateCount",
],
/**
* Items for which we will listen for click events.
*/
CLICK_TARGETS: [
"closeTab",
"restoreTab",
"restoreAll",
"sendReport",
],
/**
* Returns information about this crashed tab.
*
* @return (Object) An object with the following properties:
* title (String):
* The title of the page that crashed.
* URL (String):
* The URL of the page that crashed.
*/
get pageData() {
delete this.pageData;
let URL = document.documentURI;
let queryString = URL.replace(/^about:tabcrashed?e=tabcrashed/, "");
let titleMatch = queryString.match(/d=([^&]*)/);
let URLMatch = queryString.match(/u=([^&]*)/);
return this.pageData = {
title: titleMatch && titleMatch[1] ? decodeURIComponent(titleMatch[1]) : "",
URL: URLMatch && URLMatch[1] ? decodeURIComponent(URLMatch[1]) : "",
};
},
init() {
this.MESSAGES.forEach((msg) => addMessageListener(msg, this.receiveMessage.bind(this)));
addEventListener("DOMContentLoaded", this);
document.title = this.pageData.title;
},
receiveMessage(message) {
switch (message.name) {
case "UpdateCount": {
this.setMultiple(message.data.count > 1);
break;
}
case "SetCrashReportAvailable": {
this.onSetCrashReportAvailable(message);
break;
}
case "CrashReportSent": {
this.onCrashReportSent();
break;
}
}
},
handleEvent(event) {
switch (event.type) {
case "DOMContentLoaded": {
this.onDOMContentLoaded();
break;
}
case "click": {
this.onClick(event);
break;
}
case "input": {
this.onInput(event);
break;
}
}
},
onDOMContentLoaded() {
this.CLICK_TARGETS.forEach((targetID) => {
let el = document.getElementById(targetID);
el.addEventListener("click", this);
});
// For setting "emailMe" checkbox automatically on email value change.
document.getElementById("email").addEventListener("input", this);
// Error pages are loaded as LOAD_BACKGROUND, so they don't get load events.
let event = new CustomEvent("AboutTabCrashedLoad", {bubbles:true});
document.dispatchEvent(event);
sendAsyncMessage("Load");
},
onClick(event) {
switch (event.target.id) {
case "closeTab": {
this.sendMessage("closeTab");
break;
}
case "restoreTab": {
this.sendMessage("restoreTab");
break;
}
case "restoreAll": {
this.sendMessage("restoreAll");
break;
}
case "sendReport": {
this.showCrashReportUI(event.target.checked);
break;
}
}
},
onInput(event) {
switch (event.target.id) {
case "email": {
document.getElementById("emailMe").checked = !!event.target.value;
break;
}
}
},
/**
* After this page tells the parent that it has loaded, the parent
* will respond with whether or not a crash report is available. This
* method handles that message.
*
* @param message
* The message from the parent, which should contain a data
* Object property with the following properties:
*
* hasReport (bool):
* Whether or not there is a crash report.
*
* sendReport (bool):
* Whether or not the the user prefers to send the report
* by default.
*
* includeURL (bool):
* Whether or not the user prefers to send the URL of
* the tab that crashed.
*
* emailMe (bool):
* Whether or not to send the email address of the user
* in the report.
*
* email (String):
* The email address of the user (empty if emailMe is false).
*
* requestAutoSubmit (bool):
* Whether or not we should ask the user to automatically
* submit backlogged crash reports.
*
*/
onSetCrashReportAvailable(message) {
let data = message.data;
if (data.hasReport) {
this.hasReport = true;
document.documentElement.classList.add("crashDumpAvailable");
document.getElementById("sendReport").checked = data.sendReport;
document.getElementById("includeURL").checked = data.includeURL;
if (data.requestEmail) {
document.getElementById("requestEmail").hidden = false;
document.getElementById("emailMe").checked = data.emailMe;
if (data.emailMe) {
document.getElementById("email").value = data.email;
}
}
this.showCrashReportUI(data.sendReport);
} else {
this.showCrashReportUI(false);
}
if (data.requestAutoSubmit) {
document.getElementById("requestAutoSubmit").hidden = false;
}
let event = new CustomEvent("AboutTabCrashedReady", {bubbles:true});
document.dispatchEvent(event);
},
/**
* Handler for when the parent reports that the crash report associated
* with this about:tabcrashed page has been sent.
*/
onCrashReportSent() {
document.documentElement.classList.remove("crashDumpAvailable");
document.documentElement.classList.add("crashDumpSubmitted");
},
/**
* Toggles the display of the crash report form.
*
* @param shouldShow (bool)
* True if the crash report form should be shown
*/
showCrashReportUI(shouldShow) {
let options = document.getElementById("options");
options.hidden = !shouldShow;
},
/**
* Toggles whether or not the page is one of several visible pages
* showing the crash reporter. This controls some of the language
* on the page, along with what the "primary" button is.
*
* @param hasMultiple (bool)
* True if there are multiple crash report pages being shown.
*/
setMultiple(hasMultiple) {
let main = document.getElementById("main");
main.setAttribute("multiple", hasMultiple);
let restoreTab = document.getElementById("restoreTab");
// The "Restore All" button has the "primary" class by default, so
// we only need to modify the "Restore Tab" button.
if (hasMultiple) {
restoreTab.classList.remove("primary");
} else {
restoreTab.classList.add("primary");
}
},
/**
* Sends a message to the parent in response to the user choosing
* one of the actions available on the page. This might also send up
* crash report information if the user has chosen to submit a crash
* report.
*
* @param messageName (String)
* The message to send to the parent
*/
sendMessage(messageName) {
let comments = "";
let email = "";
let URL = "";
let sendReport = false;
let emailMe = false;
let includeURL = false;
let autoSubmit = false;
if (this.hasReport) {
sendReport = document.getElementById("sendReport").checked;
if (sendReport) {
comments = document.getElementById("comments").value.trim();
includeURL = document.getElementById("includeURL").checked;
if (includeURL) {
URL = this.pageData.URL.trim();
}
if (!document.getElementById("requestEmail").hidden) {
emailMe = document.getElementById("emailMe").checked;
if (emailMe) {
email = document.getElementById("email").value.trim();
}
}
}
}
let requestAutoSubmit = document.getElementById("requestAutoSubmit");
if (requestAutoSubmit.hidden) {
// The checkbox is hidden if the user has already opted in to sending
// backlogged crash reports.
autoSubmit = true;
} else {
autoSubmit = document.getElementById("autoSubmit").checked;
}
sendAsyncMessage(messageName, {
sendReport,
comments,
email,
emailMe,
includeURL,
URL,
autoSubmit,
hasReport: this.hasReport,
});
},
};
AboutTabCrashed.init();

View file

@ -1,97 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD
SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % tabCrashedDTD
SYSTEM "chrome://browser/locale/aboutTabCrashed.dtd">
%tabCrashedDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://global/skin/in-content/info-pages.css"/>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://browser/content/aboutTabCrashed.css"/>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://browser/skin/aboutTabCrashed.css"/>
</head>
<body dir="&locale.dir;">
<div id="main" class="container" multiple="false">
<div class="title">
<h1 class="title-text">&tabCrashed.header2;</h1>
</div>
<div class="offers">
<h2>&tabCrashed.offerHelp;</h2>
<p id="offerHelpMessageSingle">&tabCrashed.single.offerHelpMessage;</p>
<p id="offerHelpMessageMultiple">&tabCrashed.multiple.offerHelpMessage;</p>
</div>
<div id="reportBox">
<h2>&tabCrashed.requestHelp;</h2>
<p>&tabCrashed.requestHelpMessage;</p>
<h2>&tabCrashed.requestReport;</h2>
<div class="checkbox-with-label">
<input type="checkbox" id="sendReport"/>
<label for="sendReport">&tabCrashed.sendReport2;</label>
</div>
<ul id="options">
<li>
<textarea id="comments" placeholder="&tabCrashed.commentPlaceholder2;" rows="4"></textarea>
</li>
<li class="checkbox-with-label">
<input type="checkbox" id="includeURL"/>
<label for="includeURL">&tabCrashed.includeURL2;</label>
</li>
<li id="requestEmail" hidden="true">
<div class="checkbox-with-label">
<input type="checkbox" id="emailMe"/>
<label for="emailMe">&tabCrashed.emailMe;</label>
</div>
<input type="text" id="email" placeholder="&tabCrashed.emailPlaceholder;"/>
</li>
</ul>
<div id="requestAutoSubmit" hidden="true">
<h2>&tabCrashed.requestAutoSubmit2;</h2>
<div class="checkbox-with-label">
<input type="checkbox" id="autoSubmit"/>
<label for="autoSubmit">&tabCrashed.autoSubmit;</label>
</div>
</div>
</div>
<p id="reportSent">&tabCrashed.reportSent;</p>
<div class="button-container">
<button id="closeTab">
&tabCrashed.closeTab;</button>
<button id="restoreTab" class="primary">
&tabCrashed.restoreTab;</button>
<button id="restoreAll" autofocus="true" class="primary">
&tabCrashed.restoreAll;</button>
</div>
</div>
</body>
<script type="text/javascript;version=1.8" src="chrome://browser/content/aboutTabCrashed.js"/>
</html>

View file

@ -1,24 +0,0 @@
html, body {
height: 100%;
}
#remote {
width: 100%;
height: 100%;
border: 0;
display: none;
}
#networkError, #manage, #intro, #stage, #configError {
display: none;
}
#oldsync {
background: none;
border: 0;
color: #0095dd;
}
#oldsync:focus {
outline: 1px dotted #0095dd;
}

View file

@ -1,543 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FxAccounts.jsm");
var fxAccountsCommon = {};
Cu.import("resource://gre/modules/FxAccountsCommon.js", fxAccountsCommon);
// for master-password utilities
Cu.import("resource://services-sync/util.js");
const PREF_LAST_FXA_USER = "identity.fxaccounts.lastSignedInUserHash";
const PREF_SYNC_SHOW_CUSTOMIZATION = "services.sync-setup.ui.showCustomizationDialog";
const ACTION_URL_PARAM = "action";
const OBSERVER_TOPICS = [
fxAccountsCommon.ONVERIFIED_NOTIFICATION,
fxAccountsCommon.ONLOGOUT_NOTIFICATION,
];
function log(msg) {
// dump("FXA: " + msg + "\n");
}
function error(msg) {
console.log("Firefox Account Error: " + msg + "\n");
}
function getPreviousAccountNameHash() {
try {
return Services.prefs.getComplexValue(PREF_LAST_FXA_USER, Ci.nsISupportsString).data;
} catch (_) {
return "";
}
}
function setPreviousAccountNameHash(acctName) {
let string = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
string.data = sha256(acctName);
Services.prefs.setComplexValue(PREF_LAST_FXA_USER, Ci.nsISupportsString, string);
}
function needRelinkWarning(acctName) {
let prevAcctHash = getPreviousAccountNameHash();
return prevAcctHash && prevAcctHash != sha256(acctName);
}
// Given a string, returns the SHA265 hash in base64
function sha256(str) {
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
// Data is an array of bytes.
let data = converter.convertToByteArray(str, {});
let hasher = Cc["@mozilla.org/security/hash;1"]
.createInstance(Ci.nsICryptoHash);
hasher.init(hasher.SHA256);
hasher.update(data, data.length);
return hasher.finish(true);
}
function promptForRelink(acctName) {
let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
let continueLabel = sb.GetStringFromName("continue.label");
let title = sb.GetStringFromName("relinkVerify.title");
let description = sb.formatStringFromName("relinkVerify.description",
[acctName], 1);
let body = sb.GetStringFromName("relinkVerify.heading") +
"\n\n" + description;
let ps = Services.prompt;
let buttonFlags = (ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING) +
(ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL) +
ps.BUTTON_POS_1_DEFAULT;
let pressed = Services.prompt.confirmEx(window, title, body, buttonFlags,
continueLabel, null, null, null,
{});
return pressed == 0; // 0 is the "continue" button
}
// If the last fxa account used for sync isn't this account, we display
// a modal dialog checking they really really want to do this...
// (This is sync-specific, so ideally would be in sync's identity module,
// but it's a little more seamless to do here, and sync is currently the
// only fxa consumer, so...
function shouldAllowRelink(acctName) {
return !needRelinkWarning(acctName) || promptForRelink(acctName);
}
function updateDisplayedEmail(user) {
let emailDiv = document.getElementById("email");
if (emailDiv && user) {
emailDiv.textContent = user.email;
}
}
var wrapper = {
iframe: null,
init: function (url, urlParams) {
// If a master-password is enabled, we want to encourage the user to
// unlock it. Things still work if not, but the user will probably need
// to re-auth next startup (in which case we will get here again and
// re-prompt)
Utils.ensureMPUnlocked();
let iframe = document.getElementById("remote");
this.iframe = iframe;
this.iframe.QueryInterface(Ci.nsIFrameLoaderOwner);
let docShell = this.iframe.frameLoader.docShell;
docShell.QueryInterface(Ci.nsIWebProgress);
docShell.addProgressListener(this.iframeListener, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
iframe.addEventListener("load", this);
// Ideally we'd just merge urlParams with new URL(url).searchParams, but our
// URLSearchParams implementation doesn't support iteration (bug 1085284).
let urlParamStr = urlParams.toString();
if (urlParamStr) {
url += (url.includes("?") ? "&" : "?") + urlParamStr;
}
this.url = url;
// Set the iframe's location with loadURI/LOAD_FLAGS_REPLACE_HISTORY to
// avoid having a new history entry being added. REPLACE_HISTORY is used
// to replace the current entry, which is `about:blank`.
let webNav = iframe.frameLoader.docShell.QueryInterface(Ci.nsIWebNavigation);
webNav.loadURI(url, Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY, null, null, null);
},
retry: function () {
let webNav = this.iframe.frameLoader.docShell.QueryInterface(Ci.nsIWebNavigation);
webNav.loadURI(this.url, Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY, null, null, null);
},
iframeListener: {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference,
Ci.nsISupports]),
onStateChange: function(aWebProgress, aRequest, aState, aStatus) {
let failure = false;
// Captive portals sometimes redirect users
if ((aState & Ci.nsIWebProgressListener.STATE_REDIRECTING)) {
failure = true;
} else if ((aState & Ci.nsIWebProgressListener.STATE_STOP)) {
if (aRequest instanceof Ci.nsIHttpChannel) {
try {
failure = aRequest.responseStatus != 200;
} catch (e) {
failure = aStatus != Components.results.NS_OK;
}
}
}
// Calling cancel() will raise some OnStateChange notifications by itself,
// so avoid doing that more than once
if (failure && aStatus != Components.results.NS_BINDING_ABORTED) {
aRequest.cancel(Components.results.NS_BINDING_ABORTED);
setErrorPage("networkError");
}
},
onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) {
if (aRequest && aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) {
aRequest.cancel(Components.results.NS_BINDING_ABORTED);
setErrorPage("networkError");
}
},
onProgressChange: function() {},
onStatusChange: function() {},
onSecurityChange: function() {},
},
handleEvent: function (evt) {
switch (evt.type) {
case "load":
this.iframe.contentWindow.addEventListener("FirefoxAccountsCommand", this);
this.iframe.removeEventListener("load", this);
break;
case "FirefoxAccountsCommand":
this.handleRemoteCommand(evt);
break;
}
},
/**
* onLogin handler receives user credentials from the jelly after a
* sucessful login and stores it in the fxaccounts service
*
* @param accountData the user's account data and credentials
*/
onLogin: function (accountData) {
log("Received: 'login'. Data:" + JSON.stringify(accountData));
if (accountData.customizeSync) {
Services.prefs.setBoolPref(PREF_SYNC_SHOW_CUSTOMIZATION, true);
}
delete accountData.customizeSync;
// sessionTokenContext is erroneously sent by the content server.
// https://github.com/mozilla/fxa-content-server/issues/2766
// To avoid having the FxA storage manager not knowing what to do with
// it we delete it here.
delete accountData.sessionTokenContext;
// We need to confirm a relink - see shouldAllowRelink for more
let newAccountEmail = accountData.email;
// The hosted code may have already checked for the relink situation
// by sending the can_link_account command. If it did, then
// it will indicate we don't need to ask twice.
if (!accountData.verifiedCanLinkAccount && !shouldAllowRelink(newAccountEmail)) {
// we need to tell the page we successfully received the message, but
// then bail without telling fxAccounts
this.injectData("message", { status: "login" });
// after a successful login we return to preferences
openPrefs();
return;
}
delete accountData.verifiedCanLinkAccount;
// Remember who it was so we can log out next time.
setPreviousAccountNameHash(newAccountEmail);
// A sync-specific hack - we want to ensure sync has been initialized
// before we set the signed-in user.
let xps = Cc["@mozilla.org/weave/service;1"]
.getService(Ci.nsISupports)
.wrappedJSObject;
xps.whenLoaded().then(() => {
updateDisplayedEmail(accountData);
return fxAccounts.setSignedInUser(accountData);
}).then(() => {
// If the user data is verified, we want it to immediately look like
// they are signed in without waiting for messages to bounce around.
if (accountData.verified) {
openPrefs();
}
this.injectData("message", { status: "login" });
// until we sort out a better UX, just leave the jelly page in place.
// If the account email is not yet verified, it will tell the user to
// go check their email, but then it will *not* change state after
// the verification completes (the browser will begin syncing, but
// won't notify the user). If the email has already been verified,
// the jelly will say "Welcome! You are successfully signed in as
// EMAIL", but it won't then say "syncing started".
}, (err) => this.injectData("message", { status: "error", error: err })
);
},
onCanLinkAccount: function(accountData) {
// We need to confirm a relink - see shouldAllowRelink for more
let ok = shouldAllowRelink(accountData.email);
this.injectData("message", { status: "can_link_account", data: { ok: ok } });
},
/**
* onSignOut handler erases the current user's session from the fxaccounts service
*/
onSignOut: function () {
log("Received: 'sign_out'.");
fxAccounts.signOut().then(
() => this.injectData("message", { status: "sign_out" }),
(err) => this.injectData("message", { status: "error", error: err })
);
},
handleRemoteCommand: function (evt) {
log('command: ' + evt.detail.command);
let data = evt.detail.data;
switch (evt.detail.command) {
case "login":
this.onLogin(data);
break;
case "can_link_account":
this.onCanLinkAccount(data);
break;
case "sign_out":
this.onSignOut(data);
break;
default:
log("Unexpected remote command received: " + evt.detail.command + ". Ignoring command.");
break;
}
},
injectData: function (type, content) {
return fxAccounts.promiseAccountsSignUpURI().then(authUrl => {
let data = {
type: type,
content: content
};
this.iframe.contentWindow.postMessage(data, authUrl);
})
.catch(e => {
console.log("Failed to inject data", e);
setErrorPage("configError");
});
},
};
// Button onclick handlers
function handleOldSync() {
let chromeWin = window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow)
.QueryInterface(Ci.nsIDOMChromeWindow);
let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "old-sync";
chromeWin.switchToTabHavingURI(url, true);
}
function getStarted() {
show("remote");
}
function retry() {
show("remote");
wrapper.retry();
}
function openPrefs() {
// Bug 1199303 calls for this tab to always be replaced with Preferences
// rather than it opening in a different tab.
window.location = "about:preferences#sync";
}
function init() {
fxAccounts.getSignedInUser().then(user => {
// tests in particular might cause the window to start closing before
// getSignedInUser has returned.
if (window.closed) {
return Promise.resolve();
}
updateDisplayedEmail(user);
// Ideally we'd use new URL(document.URL).searchParams, but for about: URIs,
// searchParams is empty.
let urlParams = new URLSearchParams(document.URL.split("?")[1] || "");
let action = urlParams.get(ACTION_URL_PARAM);
urlParams.delete(ACTION_URL_PARAM);
switch (action) {
case "signin":
if (user) {
// asking to sign-in when already signed in just shows manage.
show("stage", "manage");
} else {
return fxAccounts.promiseAccountsSignInURI().then(url => {
show("remote");
wrapper.init(url, urlParams);
});
}
break;
case "signup":
if (user) {
// asking to sign-up when already signed in just shows manage.
show("stage", "manage");
} else {
return fxAccounts.promiseAccountsSignUpURI().then(url => {
show("remote");
wrapper.init(url, urlParams);
});
}
break;
case "reauth":
// ideally we would only show this when we know the user is in a
// "must reauthenticate" state - but we don't.
// As the email address will be included in the URL returned from
// promiseAccountsForceSigninURI, just always show it.
return fxAccounts.promiseAccountsForceSigninURI().then(url => {
show("remote");
wrapper.init(url, urlParams);
});
default:
// No action specified.
if (user) {
show("stage", "manage");
} else {
// Attempt a migration if enabled or show the introductory page
// otherwise.
return migrateToDevEdition(urlParams).then(migrated => {
if (!migrated) {
show("stage", "intro");
// load the remote frame in the background
return fxAccounts.promiseAccountsSignUpURI().then(uri =>
wrapper.init(uri, urlParams));
}
return Promise.resolve();
});
}
break;
}
return Promise.resolve();
}).catch(err => {
console.log("Configuration or sign in error", err);
setErrorPage("configError");
});
}
function setErrorPage(errorType) {
show("stage", errorType);
}
// Causes the "top-level" element with |id| to be shown - all other top-level
// elements are hidden. Optionally, ensures that only 1 "second-level" element
// inside the top-level one is shown.
function show(id, childId) {
// top-level items are either <div> or <iframe>
let allTop = document.querySelectorAll("body > div, iframe");
for (let elt of allTop) {
if (elt.getAttribute("id") == id) {
elt.style.display = 'block';
} else {
elt.style.display = 'none';
}
}
if (childId) {
// child items are all <div>
let allSecond = document.querySelectorAll("#" + id + " > div");
for (let elt of allSecond) {
if (elt.getAttribute("id") == childId) {
elt.style.display = 'block';
} else {
elt.style.display = 'none';
}
}
}
}
// Migrate sync data from the default profile to the dev-edition profile.
// Returns a promise of a true value if migration succeeded, or false if it
// failed.
function migrateToDevEdition(urlParams) {
let defaultProfilePath;
try {
defaultProfilePath = window.getDefaultProfilePath();
} catch (e) {} // no default profile.
let migrateSyncCreds = false;
if (defaultProfilePath) {
try {
migrateSyncCreds = Services.prefs.getBoolPref("identity.fxaccounts.migrateToDevEdition");
} catch (e) {}
}
if (!migrateSyncCreds) {
return Promise.resolve(false);
}
Cu.import("resource://gre/modules/osfile.jsm");
let fxAccountsStorage = OS.Path.join(defaultProfilePath, fxAccountsCommon.DEFAULT_STORAGE_FILENAME);
return OS.File.read(fxAccountsStorage, { encoding: "utf-8" }).then(text => {
let accountData = JSON.parse(text).accountData;
updateDisplayedEmail(accountData);
return fxAccounts.setSignedInUser(accountData);
}).then(() => {
return fxAccounts.promiseAccountsForceSigninURI().then(url => {
show("remote");
wrapper.init(url, urlParams);
});
}).then(null, error => {
log("Failed to migrate FX Account: " + error);
show("stage", "intro");
// load the remote frame in the background
fxAccounts.promiseAccountsSignUpURI().then(uri => {
wrapper.init(uri, urlParams)
}).catch(e => {
console.log("Failed to load signup page", e);
setErrorPage("configError");
});
}).then(() => {
// Reset the pref after migration.
Services.prefs.setBoolPref("identity.fxaccounts.migrateToDevEdition", false);
return true;
}).then(null, err => {
Cu.reportError("Failed to reset the migrateToDevEdition pref: " + err);
return false;
});
}
// Helper function that returns the path of the default profile on disk. Will be
// overridden in tests.
function getDefaultProfilePath() {
let defaultProfile = Cc["@mozilla.org/toolkit/profile-service;1"]
.getService(Ci.nsIToolkitProfileService)
.defaultProfile;
return defaultProfile.rootDir.path;
}
document.addEventListener("DOMContentLoaded", function onload() {
document.removeEventListener("DOMContentLoaded", onload, true);
init();
var buttonGetStarted = document.getElementById('buttonGetStarted');
buttonGetStarted.addEventListener('click', getStarted);
var buttonRetry = document.getElementById('buttonRetry');
buttonRetry.addEventListener('click', retry);
var oldsync = document.getElementById('oldsync');
oldsync.addEventListener('click', handleOldSync);
var buttonOpenPrefs = document.getElementById('buttonOpenPrefs')
buttonOpenPrefs.addEventListener('click', openPrefs);
}, true);
function initObservers() {
function observe(subject, topic, data) {
log("about:accounts observed " + topic);
if (topic == fxAccountsCommon.ONLOGOUT_NOTIFICATION) {
// All about:account windows get changed to action=signin on logout.
window.location = "about:accounts?action=signin";
return;
}
// must be onverified - we want to open preferences.
openPrefs();
}
for (let topic of OBSERVER_TOPICS) {
Services.obs.addObserver(observe, topic, false);
}
window.addEventListener("unload", function(event) {
log("about:accounts unloading")
for (let topic of OBSERVER_TOPICS) {
Services.obs.removeObserver(observe, topic);
}
});
}
initObservers();

View file

@ -1,112 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % aboutAccountsDTD SYSTEM "chrome://browser/locale/aboutAccounts.dtd">
%aboutAccountsDTD;
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
%syncBrandDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml" dir="&locale.dir;">
<head>
<title>&syncBrand.fullName.label;</title>
<meta name="viewport" content="width=device-width"/>
<link rel="icon" type="image/png" id="favicon"
href="chrome://branding/content/icon32.png"/>
<link rel="stylesheet"
href="chrome://browser/content/aboutaccounts/normalize.css"
type="text/css" />
<link rel="stylesheet"
href="chrome://browser/content/aboutaccounts/main.css"
type="text/css" />
<link rel="stylesheet"
href="chrome://browser/content/aboutaccounts/aboutaccounts.css"
type="text/css" />
</head>
<body>
<div id="stage">
<div id="manage">
<header>
<h1>&aboutAccounts.connected;</h1>
<div id="email"></div>
</header>
<section>
<div class="graphic graphic-sync-intro"> </div>
<div class="button-row">
<button id="buttonOpenPrefs" class="button" href="#" tabindex="0">&aboutAccountsConfig.syncPreferences.label;</button>
</div>
</section>
</div>
<div id="intro">
<header>
<h1>&aboutAccounts.welcome;</h1>
</header>
<section>
<div class="graphic graphic-sync-intro"> </div>
<div class="description">&aboutAccountsConfig.description;</div>
<div class="button-row">
<button id="buttonGetStarted" class="button" tabindex="1">&aboutAccountsConfig.startButton.label;</button>
</div>
<div class="links">
<button id="oldsync" tabindex="2">&aboutAccountsConfig.useOldSync.label;</button>
</div>
</section>
</div>
<div id="networkError">
<header>
<h1>&aboutAccounts.noConnection.title;</h1>
</header>
<section>
<div class="graphic graphic-sync-intro"> </div>
<div class="description">&aboutAccounts.noConnection.description;</div>
<div class="button-row">
<button id="buttonRetry" class="button" tabindex="3">&aboutAccounts.noConnection.retry;</button>
</div>
</section>
</div>
<div id="configError">
<header>
<h1>&aboutAccounts.badConfig.title;</h1>
</header>
<section>
<div class="graphic graphic-sync-intro"> </div>
<div class="description">&aboutAccounts.badConfig.description;</div>
</section>
</div>
</div>
<iframe mozframetype="content" id="remote" />
<script type="application/javascript;version=1.8"
src="chrome://browser/content/utilityOverlay.js"/>
<script type="text/javascript;version=1.8"
src="chrome://browser/content/aboutaccounts/aboutaccounts.js" />
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,166 +0,0 @@
*,
*:before,
*:after {
box-sizing: border-box;
}
html {
background-color: #F2F2F2;
height: 100%;
}
body {
color: #424f59;
font: message-box;
font-size: 14px;
height: 100%;
}
a {
color: #0095dd;
cursor: pointer; /* Use the correct cursor for anchors without an href */
}
a:active {
outline: none;
}
a:focus {
outline: 1px dotted #0095dd;
}
a.no-underline {
text-decoration: none;
}
#stage {
background:#fff;
border-radius: 5px;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.25);
margin: 0 auto;
min-height: 300px;
padding: 60px 40px 40px 40px;
position: relative;
text-align: center;
top: 80px;
width: 420px;
}
header h1
{
font-size: 24px;
font-weight: 200;
line-height: 1em;
}
#intro header h1 {
margin: 0 0 32px 0;
}
#manage header h1 {
margin: 0 0 12px 0;
}
#manage header #email {
margin-bottom: 23px;
color: rgb(138, 155, 168);
font-size: 19px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.description {
font-size: 18px;
}
.button-row {
margin-top: 45px;
margin-bottom:20px;
}
.button-row button,
.button-row a.button {
background: #0095dd;
border: none;
border-radius: 5px;
color: #FFFFFF;
cursor: pointer;
font-size: 24px;
padding: 15px 0;
transition-duration: 150ms;
transition-property: background-color;
width: 100%;
}
.button-row a.button {
display: inline-block;
text-decoration: none;
}
.button-row a.button:active,
.button-row a.button:hover,
.button-row a.button:focus,
.button-row button:active,
.button-row button:hover,
.button-row button:focus {
background: #08c;
}
.graphic-sync-intro {
background-image: url(images/graphic_sync_intro.png);
background-repeat: no-repeat;
background-size: 150px 195px;
height: 195px;
margin: 0 auto;
overflow: hidden;
text-indent: 100%;
white-space: nowrap;
width: 150px;
}
.description,
.button-row {
margin-top: 30px;
}
.links {
margin: 20px 0;
}
@media only screen and (max-width: 500px) {
html {
background: #fff;
}
#stage {
box-shadow: none;
margin: 30px auto 0 auto;
min-height: none;
min-width: 320px;
padding: 0 10px;
width: 100%;
}
.button-row {
margin-top: 20px;
}
.button-row button,
.button-row a.button {
padding: 10px 0;
}
}
/* Retina */
@media
only screen and (min-device-pixel-ratio: 2),
only screen and ( min-resolution: 192dpi),
only screen and ( min-resolution: 2dppx) {
.graphic-sync-intro {
background-image: url(images/graphic_sync_intro@2x.png);
}
}

View file

@ -1,402 +0,0 @@
/*! normalize.css v2.1.3 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined in IE 8/9.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
nav,
section,
summary {
display: block;
}
/**
* Correct `inline-block` display not defined in IE 8/9.
*/
audio,
canvas,
video {
display: inline-block;
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9.
* Hide the `template` element in IE, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background: transparent;
}
/**
* Address `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari 5, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
box-sizing: content-box;
height: 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Correct font family set oddly in Safari 5 and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: monospace, serif;
font-size: 1em;
}
/**
* Improve readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre-wrap;
}
/**
* Set consistent quote types.
*/
q {
quotes: "\201C" "\201D" "\2018" "\2019";
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9.
*/
img {
border: 0;
}
/**
* Correct overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari 5.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* 1. Correct font family not being inherited in all browsers.
* 2. Correct font size not being inherited in all browsers.
* 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
*/
button,
input,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 2 */
margin: 0; /* 3 */
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
* Correct `select` style inheritance in Firefox 4+ and Opera.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome.
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
box-sizing: content-box; /* 2 */
}
/**
* Remove inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* 1. Remove default vertical scrollbar in IE 8/9.
* 2. Improve readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}

View file

@ -1,15 +0,0 @@
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
}
#remote-report {
width: 100%;
height: 100%;
border: 0;
display: flex;
}

View file

@ -1,180 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const prefs = new Preferences("datareporting.healthreport.");
const PREF_UNIFIED = "toolkit.telemetry.unified";
const PREF_REPORTING_URL = "datareporting.healthreport.about.reportUrl";
var healthReportWrapper = {
init: function () {
let iframe = document.getElementById("remote-report");
iframe.addEventListener("load", healthReportWrapper.initRemotePage, false);
iframe.src = this._getReportURI().spec;
prefs.observe("uploadEnabled", this.updatePrefState, healthReportWrapper);
},
uninit: function () {
prefs.ignore("uploadEnabled", this.updatePrefState, healthReportWrapper);
},
_getReportURI: function () {
let url = Services.urlFormatter.formatURLPref(PREF_REPORTING_URL);
return Services.io.newURI(url, null, null);
},
setDataSubmission: function (enabled) {
MozSelfSupport.healthReportDataSubmissionEnabled = enabled;
this.updatePrefState();
},
updatePrefState: function () {
try {
let prefs = {
enabled: MozSelfSupport.healthReportDataSubmissionEnabled,
};
healthReportWrapper.injectData("prefs", prefs);
}
catch (ex) {
healthReportWrapper.reportFailure(healthReportWrapper.ERROR_PREFS_FAILED);
}
},
sendTelemetryPingList: function () {
console.log("AboutHealthReport: Collecting Telemetry ping list.");
MozSelfSupport.getTelemetryPingList().then((list) => {
console.log("AboutHealthReport: Sending Telemetry ping list.");
this.injectData("telemetry-ping-list", list);
}).catch((ex) => {
console.log("AboutHealthReport: Collecting ping list failed: " + ex);
});
},
sendTelemetryPingData: function (pingId) {
console.log("AboutHealthReport: Collecting Telemetry ping data.");
MozSelfSupport.getTelemetryPing(pingId).then((ping) => {
console.log("AboutHealthReport: Sending Telemetry ping data.");
this.injectData("telemetry-ping-data", {
id: pingId,
pingData: ping,
});
}).catch((ex) => {
console.log("AboutHealthReport: Loading ping data failed: " + ex);
this.injectData("telemetry-ping-data", {
id: pingId,
error: "error-generic",
});
});
},
sendCurrentEnvironment: function () {
console.log("AboutHealthReport: Sending Telemetry environment data.");
MozSelfSupport.getCurrentTelemetryEnvironment().then((environment) => {
this.injectData("telemetry-current-environment-data", environment);
}).catch((ex) => {
console.log("AboutHealthReport: Collecting current environment data failed: " + ex);
});
},
sendCurrentPingData: function () {
console.log("AboutHealthReport: Sending current Telemetry ping data.");
MozSelfSupport.getCurrentTelemetrySubsessionPing().then((ping) => {
this.injectData("telemetry-current-ping-data", ping);
}).catch((ex) => {
console.log("AboutHealthReport: Collecting current ping data failed: " + ex);
});
},
injectData: function (type, content) {
let report = this._getReportURI();
// file URIs can't be used for targetOrigin, so we use "*" for this special case
// in all other cases, pass in the URL to the report so we properly restrict the message dispatch
let reportUrl = report.scheme == "file" ? "*" : report.spec;
let data = {
type: type,
content: content
}
let iframe = document.getElementById("remote-report");
iframe.contentWindow.postMessage(data, reportUrl);
},
handleRemoteCommand: function (evt) {
// Do an origin check to harden against the frame content being loaded from unexpected locations.
let allowedPrincipal = Services.scriptSecurityManager.getCodebasePrincipal(this._getReportURI());
let targetPrincipal = evt.target.nodePrincipal;
if (!allowedPrincipal.equals(targetPrincipal)) {
Cu.reportError(`Origin check failed for message "${evt.detail.command}": ` +
`target origin is "${targetPrincipal.origin}", expected "${allowedPrincipal.origin}"`);
return;
}
switch (evt.detail.command) {
case "DisableDataSubmission":
this.setDataSubmission(false);
break;
case "EnableDataSubmission":
this.setDataSubmission(true);
break;
case "RequestCurrentPrefs":
this.updatePrefState();
break;
case "RequestTelemetryPingList":
this.sendTelemetryPingList();
break;
case "RequestTelemetryPingData":
this.sendTelemetryPingData(evt.detail.id);
break;
case "RequestCurrentEnvironment":
this.sendCurrentEnvironment();
break;
case "RequestCurrentPingData":
this.sendCurrentPingData();
break;
default:
Cu.reportError("Unexpected remote command received: " + evt.detail.command + ". Ignoring command.");
break;
}
},
initRemotePage: function () {
let iframe = document.getElementById("remote-report").contentDocument;
iframe.addEventListener("RemoteHealthReportCommand",
function onCommand(e) { healthReportWrapper.handleRemoteCommand(e); },
false);
healthReportWrapper.updatePrefState();
},
// error handling
ERROR_INIT_FAILED: 1,
ERROR_PAYLOAD_FAILED: 2,
ERROR_PREFS_FAILED: 3,
reportFailure: function (error) {
let details = {
errorType: error,
}
healthReportWrapper.injectData("error", details);
},
handleInitFailure: function () {
healthReportWrapper.reportFailure(healthReportWrapper.ERROR_INIT_FAILED);
},
handlePayloadFailure: function () {
healthReportWrapper.reportFailure(healthReportWrapper.ERROR_PAYLOAD_FAILED);
},
}
window.addEventListener("load", function () { healthReportWrapper.init(); });
window.addEventListener("unload", function () { healthReportWrapper.uninit(); });

View file

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
<!ENTITY % securityPrefsDTD SYSTEM "chrome://browser/locale/preferences/security.dtd">
%securityPrefsDTD;
<!ENTITY % aboutHealthReportDTD SYSTEM "chrome://browser/locale/aboutHealthReport.dtd">
%aboutHealthReportDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&abouthealth.pagetitle;</title>
<link rel="icon" type="image/png" id="favicon"
href="chrome://branding/content/icon32.png"/>
<link rel="stylesheet"
href="chrome://browser/content/abouthealthreport/abouthealth.css"
type="text/css" />
<script type="text/javascript;version=1.8"
src="chrome://browser/content/abouthealthreport/abouthealth.js" />
</head>
<body>
<iframe id="remote-report"/>
</body>
</html>

View file

@ -1,397 +0,0 @@
%if 0
/* 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/. */
%endif
html {
font: message-box;
font-size: 100%;
background-color: hsl(0,0%,95%);
color: #000;
height: 100%;
}
body {
margin: 0;
display: -moz-box;
-moz-box-orient: vertical;
width: 100%;
height: 100%;
}
input,
button {
font-size: inherit;
font-family: inherit;
}
a {
color: -moz-nativehyperlinktext;
text-decoration: none;
}
.spacer {
-moz-box-flex: 1;
}
#topSection {
text-align: center;
}
#brandLogo {
height: 192px;
width: 192px;
margin: 22px auto 31px;
background-image: url("chrome://branding/content/about-logo.png");
background-size: 192px auto;
background-position: center center;
background-repeat: no-repeat;
}
#searchIconAndTextContainer {
width: 470px;
}
#searchIconAndTextContainer {
display: -moz-box;
height: 36px;
position: relative;
}
#searchIcon {
border: 1px transparent;
padding: 0;
margin: 0;
width: 36px;
height: 36px;
background: url("chrome://browser/skin/search-indicator-magnifying-glass.svg") center center no-repeat;
position: absolute;
}
#searchText {
margin-left: 0;
-moz-box-flex: 1;
padding-top: 6px;
padding-bottom: 6px;
padding-inline-start: 34px;
padding-inline-end: 8px;
background: hsla(0,0%,100%,.9) padding-box;
border: 1px solid;
border-radius: 2px 0 0 2px;
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset,
0 0 2px hsla(210,65%,9%,.1) inset,
0 1px 0 hsla(0,0%,100%,.2);
color: inherit;
unicode-bidi: plaintext;
}
#searchText:dir(rtl) {
border-radius: 0 2px 2px 0;
}
#searchText[aria-expanded="true"] {
border-radius: 2px 0 0 0;
}
#searchText[aria-expanded="true"]:dir(rtl) {
border-radius: 0 2px 0 0;
}
#searchText[keepfocus],
#searchText:focus,
#searchText[autofocus] {
border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6);
}
#searchSubmit {
margin-inline-start: -1px;
color: transparent;
background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go") center center no-repeat, linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
padding: 0;
border: 1px solid;
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
border-radius: 0 2px 2px 0;
border-inline-start: 1px solid transparent;
box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset,
0 1px 0 hsla(0,0%,100%,.2);
cursor: pointer;
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
width: 50px;
}
#searchSubmit:dir(rtl) {
border-radius: 2px 0 0 2px;
background-image: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-rtl"), linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1));
}
#searchText:focus + #searchSubmit,
#searchText[keepfocus] + #searchSubmit,
#searchText + #searchSubmit:hover,
#searchText[autofocus] + #searchSubmit {
border-color: #59b5fc #45a3e7 #3294d5;
}
#searchText:focus + #searchSubmit,
#searchText[keepfocus] + #searchSubmit,
#searchText[autofocus] + #searchSubmit {
background-image: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-inverted"), linear-gradient(#4cb1ff, #1793e5);
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
0 0 0 1px hsla(0,0%,100%,.1) inset,
0 1px 0 hsla(210,54%,20%,.03);
}
#searchText:focus + #searchSubmit:dir(rtl),
#searchText[keepfocus] + #searchSubmit:dir(rtl),
#searchText[autofocus] + #searchSubmit:dir(rtl) {
background-image: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-rtl-inverted"), linear-gradient(#4cb1ff, #1793e5);
}
#searchText + #searchSubmit:hover {
background-image: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-inverted"), linear-gradient(#66bdff, #0d9eff);
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
0 0 0 1px hsla(0,0%,100%,.1) inset,
0 1px 0 hsla(210,54%,20%,.03),
0 0 4px hsla(206,100%,20%,.2);
}
#searchText + #searchSubmit:dir(rtl):hover {
background-image: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-rtl-inverted"), linear-gradient(#66bdff, #0d9eff);
}
#searchText + #searchSubmit:hover:active {
box-shadow: 0 1px 1px hsla(211,79%,6%,.1) inset,
0 0 1px hsla(211,79%,6%,.2) inset;
transition-duration: 0ms;
}
#launcher {
display: -moz-box;
-moz-box-align: center;
-moz-box-pack: center;
width: 100%;
background-color: hsla(0,0%,0%,.03);
border-top: 1px solid hsla(0,0%,0%,.03);
box-shadow: 0 1px 2px hsla(0,0%,0%,.02) inset,
0 -1px 0 hsla(0,0%,100%,.25);
}
#launcher:not([session]),
body[narrow] #launcher[session] {
display: block; /* display separator and restore button on separate lines */
text-align: center;
white-space: nowrap; /* prevent navigational buttons from wrapping */
}
.launchButton {
display: -moz-box;
-moz-box-orient: vertical;
margin: 16px 1px;
padding: 14px 6px;
min-width: 88px;
max-width: 176px;
max-height: 85px;
vertical-align: top;
white-space: normal;
background: transparent padding-box;
border: 1px solid transparent;
border-radius: 2px;
color: #525c66;
font-size: 75%;
cursor: pointer;
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
}
body[narrow] #launcher[session] > .launchButton {
margin: 4px 1px;
}
.launchButton:hover {
background-color: hsla(211,79%,6%,.03);
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
}
.launchButton:hover:active {
background-image: linear-gradient(hsla(211,79%,6%,.02), hsla(211,79%,6%,.05));
border-color: hsla(210,54%,20%,.2) hsla(210,54%,20%,.23) hsla(210,54%,20%,.25);
box-shadow: 0 1px 1px hsla(211,79%,6%,.05) inset,
0 0 1px hsla(211,79%,6%,.1) inset;
transition-duration: 0ms;
}
.launchButton[hidden],
#launcher:not([session]) > #restorePreviousSessionSeparator,
#launcher:not([session]) > #restorePreviousSession {
display: none;
}
#restorePreviousSessionSeparator {
width: 3px;
height: 116px;
margin: 0 10px;
background-image: linear-gradient(hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)),
linear-gradient(hsla(211,79%,6%,0), hsla(211,79%,6%,.2), hsla(211,79%,6%,0)),
linear-gradient(hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0));
background-position: left top, center, right bottom;
background-size: 1px auto;
background-repeat: no-repeat;
}
body[narrow] #restorePreviousSessionSeparator {
margin: 0 auto;
width: 512px;
height: 3px;
background-image: linear-gradient(to right, hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)),
linear-gradient(to right, hsla(211,79%,6%,0), hsla(211,79%,6%,.2), hsla(211,79%,6%,0)),
linear-gradient(to right, hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0));
background-size: auto 1px;
}
#restorePreviousSession {
max-width: none;
font-size: 90%;
}
body[narrow] #restorePreviousSession {
font-size: 80%;
}
.launchButton::before {
display: block;
width: 32px;
height: 32px;
margin: 0 auto 6px;
line-height: 0; /* remove extra vertical space due to non-zero font-size */
}
#downloads::before {
content: url("chrome://browser/content/abouthome/downloads.png");
}
#bookmarks::before {
content: url("chrome://browser/content/abouthome/bookmarks.png");
}
#history::before {
content: url("chrome://browser/content/abouthome/history.png");
}
#addons::before {
content: url("chrome://browser/content/abouthome/addons.png");
}
#sync::before {
content: url("chrome://browser/content/abouthome/sync.png");
}
#settings::before {
content: url("chrome://browser/content/abouthome/settings.png");
}
#restorePreviousSession::before {
content: url("chrome://browser/content/abouthome/restore-large.png");
height: 48px;
width: 48px;
display: inline-block; /* display on same line as text label */
vertical-align: middle;
margin-bottom: 0;
margin-inline-end: 8px;
}
#restorePreviousSession:dir(rtl)::before {
transform: scaleX(-1);
}
body[narrow] #restorePreviousSession::before {
content: url("chrome://browser/content/abouthome/restore.png");
height: 32px;
width: 32px;
}
#aboutMozilla {
display: block;
position: relative; /* pin wordmark to edge of document, not of viewport */
-moz-box-ordinal-group: 0;
opacity: .5;
transition: opacity 150ms;
}
#aboutMozilla:hover {
opacity: 1;
}
#aboutMozilla::before {
content: url("chrome://browser/content/abouthome/mozilla.png");
display: block;
position: absolute;
top: 12px;
right: 12px;
width: 69px;
height: 19px;
}
/* [HiDPI]
* At resolutions above 1dppx, prefer downscaling the 2x Retina graphics
* rather than upscaling the original-size ones (bug 818940).
*/
@media not all and (max-resolution: 1dppx) {
#brandLogo {
background-image: url("chrome://branding/content/about-logo@2x.png");
}
.launchButton::before,
#aboutMozilla::before {
transform: scale(.5);
transform-origin: 0 0;
}
.launchButton:dir(rtl)::before,
#aboutMozilla:dir(rtl)::before {
transform: scale(.5) translateX(32px);
}
#downloads::before {
content: url("chrome://browser/content/abouthome/downloads@2x.png");
}
#bookmarks::before {
content: url("chrome://browser/content/abouthome/bookmarks@2x.png");
}
#history::before {
content: url("chrome://browser/content/abouthome/history@2x.png");
}
#addons::before {
content: url("chrome://browser/content/abouthome/addons@2x.png");
}
#sync::before {
content: url("chrome://browser/content/abouthome/sync@2x.png");
}
#settings::before {
content: url("chrome://browser/content/abouthome/settings@2x.png");
}
#restorePreviousSession::before {
content: url("chrome://browser/content/abouthome/restore-large@2x.png");
}
body[narrow] #restorePreviousSession::before {
content: url("chrome://browser/content/abouthome/restore@2x.png");
}
#restorePreviousSession:dir(rtl)::before {
transform: scale(-0.5, 0.5) translateX(24px);
transform-origin: top center;
}
#aboutMozilla::before {
content: url("chrome://browser/content/abouthome/mozilla@2x.png");
}
}

View file

@ -1,118 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* import-globals-from ../contentSearchUI.js */
// IndexedDB storage constants.
const DATABASE_NAME = "abouthome";
const DATABASE_VERSION = 1;
const DATABASE_STORAGE = "persistent";
var searchText;
// This global tracks if the page has been set up before, to prevent double inits
var gInitialized = false;
var gObserver = new MutationObserver(function (mutations) {
for (let mutation of mutations) {
// The addition of the restore session button changes our width:
if (mutation.attributeName == "session") {
fitToWidth();
}
}
});
window.addEventListener("pageshow", function () {
// Delay search engine setup, cause browser.js::BrowserOnAboutPageLoad runs
// later and may use asynchronous getters.
window.gObserver.observe(document.documentElement, { attributes: true });
window.gObserver.observe(document.getElementById("launcher"), { attributes: true });
fitToWidth();
setupSearch();
window.addEventListener("resize", fitToWidth);
// Ask chrome to update snippets.
var event = new CustomEvent("AboutHomeLoad", {bubbles:true});
document.dispatchEvent(event);
});
window.addEventListener("pagehide", function() {
window.gObserver.disconnect();
window.removeEventListener("resize", fitToWidth);
});
window.addEventListener("keypress", ev => {
if (ev.defaultPrevented) {
return;
}
// don't focus the search-box on keypress if something other than the
// body or document element has focus - don't want to steal input from other elements
// Make an exception for <a> and <button> elements (and input[type=button|submit])
// which don't usefully take keypresses anyway.
// (except space, which is handled below)
if (document.activeElement && document.activeElement != document.body &&
document.activeElement != document.documentElement &&
!["a", "button"].includes(document.activeElement.localName) &&
!document.activeElement.matches("input:-moz-any([type=button],[type=submit])")) {
return;
}
let modifiers = ev.ctrlKey + ev.altKey + ev.metaKey;
// ignore Ctrl/Cmd/Alt, but not Shift
// also ignore Tab, Insert, PageUp, etc., and Space
if (modifiers != 0 || ev.charCode == 0 || ev.charCode == 32)
return;
searchText.focus();
// need to send the first keypress outside the search-box manually to it
searchText.value += ev.key;
});
function onSearchSubmit(aEvent)
{
gContentSearchController.search(aEvent);
}
var gContentSearchController;
function setupSearch()
{
// Set submit button label for when CSS background are disabled (e.g.
// high contrast mode).
document.getElementById("searchSubmit").value =
document.body.getAttribute("dir") == "ltr" ? "\u25B6" : "\u25C0";
// The "autofocus" attribute doesn't focus the form element
// immediately when the element is first drawn, so the
// attribute is also used for styling when the page first loads.
searchText = document.getElementById("searchText");
searchText.addEventListener("blur", function searchText_onBlur() {
searchText.removeEventListener("blur", searchText_onBlur);
searchText.removeAttribute("autofocus");
});
if (!gContentSearchController) {
gContentSearchController =
new ContentSearchUIController(searchText, searchText.parentNode,
"abouthome", "homepage");
}
}
/**
* Inform the test harness that we're done loading the page.
*/
function loadCompleted()
{
}
function fitToWidth() {
if (document.documentElement.scrollWidth > window.innerWidth) {
document.body.setAttribute("narrow", "true");
} else if (document.body.hasAttribute("narrow")) {
document.body.removeAttribute("narrow");
fitToWidth();
}
}

View file

@ -1,68 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
%aboutHomeDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" >
%browserDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&abouthome.pageTitle;</title>
<link rel="icon" type="image/png" id="favicon"
href="chrome://branding/content/icon32.png"/>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://browser/content/contentSearchUI.css"/>
<link rel="stylesheet" type="text/css" media="all" defer="defer"
href="chrome://browser/content/abouthome/aboutHome.css"/>
<script type="text/javascript;version=1.8"
src="chrome://browser/content/abouthome/aboutHome.js"/>
<script type="text/javascript;version=1.8"
src="chrome://browser/content/contentSearchUI.js"/>
</head>
<body dir="&locale.dir;">
<div class="spacer"/>
<div id="topSection">
<div id="brandLogo"></div>
<div id="searchIconAndTextContainer">
<div id="searchIcon"/>
<input type="text" name="q" value="" id="searchText" maxlength="256"
aria-label="&contentSearchInput.label;" autofocus="autofocus"/>
<input id="searchSubmit" type="button" onclick="onSearchSubmit(event)"
title="&contentSearchSubmit.tooltip;"/>
</div>
</div>
<div class="spacer"/>
<div id="launcher">
<button class="launchButton" id="downloads">&abouthome.downloadsButton.label;</button>
<button class="launchButton" id="bookmarks">&abouthome.bookmarksButton.label;</button>
<button class="launchButton" id="history">&abouthome.historyButton.label;</button>
<button class="launchButton" id="addons">&abouthome.addonsButton.label;</button>
<button class="launchButton" id="sync">&abouthome.syncButton.label;</button>
#ifdef XP_WIN
<button class="launchButton" id="settings">&abouthome.preferencesButtonWin.label;</button>
#else
<button class="launchButton" id="settings">&abouthome.preferencesButtonUnix.label;</button>
#endif
<div id="restorePreviousSessionSeparator"/>
<button class="launchButton" id="restorePreviousSession">&historyRestoreLastSession.label;</button>
</div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 898 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -1,114 +0,0 @@
<?xml version="1.0"?>
# 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/.
<!DOCTYPE overlay [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
<!ENTITY % baseMenuOverlayDTD SYSTEM "chrome://browser/locale/baseMenuOverlay.dtd">
%baseMenuOverlayDTD;
]>
<overlay id="baseMenuOverlay"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
#ifdef XP_MACOSX
<!-- nsMenuBarX hides these and uses them to build the Application menu.
When using Carbon widgets for Mac OS X widgets, some of these are not
used as they only apply to Cocoa widget builds. All version of Firefox
through Firefox 2 will use Carbon widgets. -->
<menupopup id="menu_ToolsPopup">
<menuitem id="menu_preferences" label="&preferencesCmdMac.label;" key="key_preferencesCmdMac" oncommand="openPreferences();"/>
<menuitem id="menu_mac_services" label="&servicesMenuMac.label;"/>
<menuitem id="menu_mac_hide_app" label="&hideThisAppCmdMac2.label;" key="key_hideThisAppCmdMac"/>
<menuitem id="menu_mac_hide_others" label="&hideOtherAppsCmdMac.label;" key="key_hideOtherAppsCmdMac"/>
<menuitem id="menu_mac_show_all" label="&showAllAppsCmdMac.label;"/>
</menupopup>
<!-- Mac window menu -->
#include ../../../toolkit/content/macWindowMenu.inc
#endif
#ifdef XP_WIN
<menu id="helpMenu"
label="&helpMenuWin.label;"
accesskey="&helpMenuWin.accesskey;">
#else
<menu id="helpMenu"
label="&helpMenu.label;"
accesskey="&helpMenu.accesskey;">
#endif
<menupopup id="menu_HelpPopup" onpopupshowing="buildHelpMenu();">
<menuitem id="menu_openHelp"
oncommand="openHelpLink('firefox-help')"
onclick="checkForMiddleClick(this, event);"
label="&productHelp2.label;"
accesskey="&productHelp2.accesskey;"
#ifdef XP_MACOSX
key="key_openHelpMac"/>
#else
/>
#endif
<menuitem id="menu_keyboardShortcuts"
oncommand="openHelpLink('keyboard-shortcuts')"
onclick="checkForMiddleClick(this, event);"
label="&helpKeyboardShortcuts.label;"
accesskey="&helpKeyboardShortcuts.accesskey;"/>
#ifdef MOZ_TELEMETRY_REPORTING
<menuitem id="healthReport"
label="&healthReport2.label;"
accesskey="&healthReport2.accesskey;"
oncommand="openHealthReport()"
onclick="checkForMiddleClick(this, event);"/>
#endif
<menuitem id="troubleShooting"
accesskey="&helpTroubleshootingInfo.accesskey;"
label="&helpTroubleshootingInfo.label;"
oncommand="openTroubleshootingPage()"
onclick="checkForMiddleClick(this, event);"/>
<menuitem id="feedbackPage"
accesskey="&helpFeedbackPage.accesskey;"
label="&helpFeedbackPage.label;"
oncommand="openFeedbackPage()"
onclick="checkForMiddleClick(this, event);"/>
<menuitem id="helpSafeMode"
accesskey="&helpSafeMode.accesskey;"
label="&helpSafeMode.label;"
stopaccesskey="&helpSafeMode.stop.accesskey;"
stoplabel="&helpSafeMode.stop.label;"
oncommand="safeModeRestart();"/>
<menuseparator id="aboutSeparator"/>
<menuitem id="aboutName"
accesskey="&aboutProduct2.accesskey;"
label="&aboutProduct2.label;"
oncommand="openAboutDialog();"/>
</menupopup>
</menu>
<keyset id="baseMenuKeyset">
#ifdef XP_MACOSX
<key id="key_openHelpMac"
oncommand="openHelpLink('firefox-osxkey');"
key="&helpMac.commandkey;"
modifiers="accel"/>
<!-- These are used to build the Application menu under Cocoa widgets -->
<key id="key_preferencesCmdMac"
key="&preferencesCmdMac.commandkey;"
modifiers="accel"/>
<key id="key_hideThisAppCmdMac"
key="&hideThisAppCmdMac2.commandkey;"
modifiers="accel"/>
<key id="key_hideOtherAppsCmdMac"
key="&hideOtherAppsCmdMac.commandkey;"
modifiers="accel,alt"/>
#endif
</keyset>
<stringbundleset id="stringbundleset">
<stringbundle id="bundle_browser" src="chrome://browser/locale/browser.properties"/>
<stringbundle id="bundle_browser_region" src="chrome://browser-region/locale/region.properties"/>
</stringbundleset>
</overlay>

View file

@ -1,196 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % blockedSiteDTD SYSTEM "chrome://browser/locale/safebrowsing/phishing-afterload-warning-message.dtd">
%blockedSiteDTD;
]>
<!-- 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 xmlns="http://www.w3.org/1999/xhtml" class="blacklist">
<head>
<link rel="stylesheet" href="chrome://browser/skin/blockedSite.css" type="text/css" media="all" />
<link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/blacklist_favicon.png"/>
<script type="application/javascript"><![CDATA[
// Error url MUST be formatted like this:
// about:blocked?e=error_code&u=url(&o=1)?
// (o=1 when user overrides are allowed)
// Note that this file uses document.documentURI to get
// the URL (with the format from above). This is because
// document.location.href gets the current URI off the docshell,
// which is the URL displayed in the location bar, i.e.
// the URI that the user attempted to load.
function getErrorCode()
{
var url = document.documentURI;
var error = url.search(/e\=/);
var duffUrl = url.search(/\&u\=/);
return decodeURIComponent(url.slice(error + 2, duffUrl));
}
function getURL()
{
var url = document.documentURI;
var match = url.match(/&u=([^&]+)&/);
// match == null if not found; if so, return an empty string
// instead of what would turn out to be portions of the URI
if (!match)
return "";
url = decodeURIComponent(match[1]);
// If this is a view-source page, then get then real URI of the page
if (url.startsWith("view-source:"))
url = url.slice(12);
return url;
}
/**
* Check whether this warning page should be overridable or whether
* the "ignore warning" button should be hidden.
*/
function getOverride()
{
var url = document.documentURI;
var match = url.match(/&o=1&/);
return !!match;
}
/**
* Attempt to get the hostname via document.location. Fail back
* to getURL so that we always return something meaningful.
*/
function getHostString()
{
try {
return document.location.hostname;
} catch (e) {
return getURL();
}
}
function initPage()
{
var error = "";
switch (getErrorCode()) {
case "malwareBlocked" :
error = "malware";
break;
case "deceptiveBlocked" :
error = "phishing";
break;
case "unwantedBlocked" :
error = "unwanted";
break;
default:
return;
}
var el;
if (error !== "malware") {
el = document.getElementById("errorTitleText_malware");
el.parentNode.removeChild(el);
el = document.getElementById("errorShortDescText_malware");
el.parentNode.removeChild(el);
el = document.getElementById("errorLongDescText_malware");
el.parentNode.removeChild(el);
}
if (error !== "phishing") {
el = document.getElementById("errorTitleText_phishing");
el.parentNode.removeChild(el);
el = document.getElementById("errorShortDescText_phishing");
el.parentNode.removeChild(el);
el = document.getElementById("errorLongDescText_phishing");
el.parentNode.removeChild(el);
}
if (error !== "unwanted") {
el = document.getElementById("errorTitleText_unwanted");
el.parentNode.removeChild(el);
el = document.getElementById("errorShortDescText_unwanted");
el.parentNode.removeChild(el);
el = document.getElementById("errorLongDescText_unwanted");
el.parentNode.removeChild(el);
}
// Set sitename
document.getElementById(error + "_sitename").textContent = getHostString();
document.title = document.getElementById("errorTitleText_" + error)
.innerHTML;
if (!getOverride()) {
var btn = document.getElementById("ignoreWarningButton");
if (btn) {
btn.parentNode.removeChild(btn);
}
}
// Inform the test harness that we're done loading the page
var event = new CustomEvent("AboutBlockedLoaded");
document.dispatchEvent(event);
}
]]></script>
</head>
<body dir="&locale.dir;">
<div id="errorPageContainer" class="container">
<!-- Error Title -->
<div id="errorTitle" class="title">
<h1 class="title-text" id="errorTitleText_phishing">&safeb.blocked.phishingPage.title2;</h1>
<h1 class="title-text" id="errorTitleText_malware">&safeb.blocked.malwarePage.title;</h1>
<h1 class="title-text" id="errorTitleText_unwanted">&safeb.blocked.unwantedPage.title;</h1>
</div>
<div id="errorLongContent">
<!-- Short Description -->
<div id="errorShortDesc">
<p id="errorShortDescText_phishing">&safeb.blocked.phishingPage.shortDesc2;</p>
<p id="errorShortDescText_malware">&safeb.blocked.malwarePage.shortDesc;</p>
<p id="errorShortDescText_unwanted">&safeb.blocked.unwantedPage.shortDesc;</p>
</div>
<!-- Long Description -->
<div id="errorLongDesc">
<p id="errorLongDescText_phishing">&safeb.blocked.phishingPage.longDesc2;</p>
<p id="errorLongDescText_malware">&safeb.blocked.malwarePage.longDesc;</p>
<p id="errorLongDescText_unwanted">&safeb.blocked.unwantedPage.longDesc;</p>
</div>
<!-- Action buttons -->
<div id="buttons" class="button-container">
<!-- Commands handled in browser.js -->
<button id="getMeOutButton" class="primary">&safeb.palm.accept.label;</button>
<div class="button-spacer"></div>
<button id="reportButton">&safeb.palm.reportPage.label;</button>
</div>
</div>
<div id="ignoreWarning">
<button id="ignoreWarningButton">&safeb.palm.decline.label;</button>
</div>
</div>
<!--
- Note: It is important to run the script this way, instead of using
- an onload handler. This is because error pages are loaded as
- LOAD_BACKGROUND, which means that onload handlers will not be executed.
-->
<script type="application/javascript">
initPage();
</script>
</body>
</html>

View file

@ -1,742 +0,0 @@
/* -*- 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/. */
// Removes a doorhanger notification if all of the installs it was notifying
// about have ended in some way.
function removeNotificationOnEnd(notification, installs) {
let count = installs.length;
function maybeRemove(install) {
install.removeListener(this);
if (--count == 0) {
// Check that the notification is still showing
let current = PopupNotifications.getNotification(notification.id, notification.browser);
if (current === notification)
notification.remove();
}
}
for (let install of installs) {
install.addListener({
onDownloadCancelled: maybeRemove,
onDownloadFailed: maybeRemove,
onInstallFailed: maybeRemove,
onInstallEnded: maybeRemove
});
}
}
const gXPInstallObserver = {
_findChildShell: function (aDocShell, aSoughtShell)
{
if (aDocShell == aSoughtShell)
return aDocShell;
var node = aDocShell.QueryInterface(Components.interfaces.nsIDocShellTreeItem);
for (var i = 0; i < node.childCount; ++i) {
var docShell = node.getChildAt(i);
docShell = this._findChildShell(docShell, aSoughtShell);
if (docShell == aSoughtShell)
return docShell;
}
return null;
},
_getBrowser: function (aDocShell)
{
for (let browser of gBrowser.browsers) {
if (this._findChildShell(browser.docShell, aDocShell))
return browser;
}
return null;
},
pendingInstalls: new WeakMap(),
showInstallConfirmation: function(browser, installInfo, height = undefined) {
// If the confirmation notification is already open cache the installInfo
// and the new confirmation will be shown later
if (PopupNotifications.getNotification("addon-install-confirmation", browser)) {
let pending = this.pendingInstalls.get(browser);
if (pending) {
pending.push(installInfo);
} else {
this.pendingInstalls.set(browser, [installInfo]);
}
return;
}
let showNextConfirmation = () => {
// Make sure the browser is still alive.
if (gBrowser.browsers.indexOf(browser) == -1)
return;
let pending = this.pendingInstalls.get(browser);
if (pending && pending.length)
this.showInstallConfirmation(browser, pending.shift());
}
// If all installs have already been cancelled in some way then just show
// the next confirmation
if (installInfo.installs.every(i => i.state != AddonManager.STATE_DOWNLOADED)) {
showNextConfirmation();
return;
}
const anchorID = "addons-notification-icon";
// Make notifications persist a minimum of 30 seconds
var options = {
displayURI: installInfo.originatingURI,
timeout: Date.now() + 30000,
};
let cancelInstallation = () => {
if (installInfo) {
for (let install of installInfo.installs) {
// The notification may have been closed because the add-ons got
// cancelled elsewhere, only try to cancel those that are still
// pending install.
if (install.state != AddonManager.STATE_CANCELLED)
install.cancel();
}
}
this.acceptInstallation = null;
showNextConfirmation();
};
let unsigned = installInfo.installs.filter(i => i.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING);
let someUnsigned = unsigned.length > 0 && unsigned.length < installInfo.installs.length;
options.eventCallback = (aEvent) => {
switch (aEvent) {
case "removed":
cancelInstallation();
break;
case "shown":
let addonList = document.getElementById("addon-install-confirmation-content");
while (addonList.firstChild)
addonList.firstChild.remove();
for (let install of installInfo.installs) {
let container = document.createElement("hbox");
let name = document.createElement("label");
name.setAttribute("value", install.addon.name);
name.setAttribute("class", "addon-install-confirmation-name");
container.appendChild(name);
if (someUnsigned && install.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
let unsigned = document.createElement("label");
unsigned.setAttribute("value", gNavigatorBundle.getString("addonInstall.unsigned"));
unsigned.setAttribute("class", "addon-install-confirmation-unsigned");
container.appendChild(unsigned);
}
addonList.appendChild(container);
}
this.acceptInstallation = () => {
for (let install of installInfo.installs)
install.install();
installInfo = null;
Services.telemetry
.getHistogramById("SECURITY_UI")
.add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL_CLICK_THROUGH);
};
break;
}
};
options.learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
let messageString;
let notification = document.getElementById("addon-install-confirmation-notification");
if (unsigned.length == installInfo.installs.length) {
// None of the add-ons are verified
messageString = gNavigatorBundle.getString("addonConfirmInstallUnsigned.message");
notification.setAttribute("warning", "true");
options.learnMoreURL += "unsigned-addons";
}
else if (unsigned.length == 0) {
// All add-ons are verified or don't need to be verified
messageString = gNavigatorBundle.getString("addonConfirmInstall.message");
notification.removeAttribute("warning");
options.learnMoreURL += "find-and-install-add-ons";
}
else {
// Some of the add-ons are unverified, the list of names will indicate
// which
messageString = gNavigatorBundle.getString("addonConfirmInstallSomeUnsigned.message");
notification.setAttribute("warning", "true");
options.learnMoreURL += "unsigned-addons";
}
let brandBundle = document.getElementById("bundle_brand");
let brandShortName = brandBundle.getString("brandShortName");
messageString = PluralForm.get(installInfo.installs.length, messageString);
messageString = messageString.replace("#1", brandShortName);
messageString = messageString.replace("#2", installInfo.installs.length);
let cancelButton = document.getElementById("addon-install-confirmation-cancel");
cancelButton.label = gNavigatorBundle.getString("addonInstall.cancelButton.label");
cancelButton.accessKey = gNavigatorBundle.getString("addonInstall.cancelButton.accesskey");
let acceptButton = document.getElementById("addon-install-confirmation-accept");
acceptButton.label = gNavigatorBundle.getString("addonInstall.acceptButton.label");
acceptButton.accessKey = gNavigatorBundle.getString("addonInstall.acceptButton.accesskey");
if (height) {
let notification = document.getElementById("addon-install-confirmation-notification");
notification.style.minHeight = height + "px";
}
let tab = gBrowser.getTabForBrowser(browser);
if (tab) {
gBrowser.selectedTab = tab;
}
let popup = PopupNotifications.show(browser, "addon-install-confirmation",
messageString, anchorID, null, null,
options);
removeNotificationOnEnd(popup, installInfo.installs);
Services.telemetry
.getHistogramById("SECURITY_UI")
.add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
},
observe: function (aSubject, aTopic, aData)
{
var brandBundle = document.getElementById("bundle_brand");
var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo);
var browser = installInfo.browser;
// Make sure the browser is still alive.
if (!browser || gBrowser.browsers.indexOf(browser) == -1)
return;
const anchorID = "addons-notification-icon";
var messageString, action;
var brandShortName = brandBundle.getString("brandShortName");
var notificationID = aTopic;
// Make notifications persist a minimum of 30 seconds
var options = {
displayURI: installInfo.originatingURI,
timeout: Date.now() + 30000,
};
switch (aTopic) {
case "addon-install-disabled": {
notificationID = "xpinstall-disabled";
if (gPrefService.prefIsLocked("xpinstall.enabled")) {
messageString = gNavigatorBundle.getString("xpinstallDisabledMessageLocked");
buttons = [];
}
else {
messageString = gNavigatorBundle.getString("xpinstallDisabledMessage");
action = {
label: gNavigatorBundle.getString("xpinstallDisabledButton"),
accessKey: gNavigatorBundle.getString("xpinstallDisabledButton.accesskey"),
callback: function editPrefs() {
gPrefService.setBoolPref("xpinstall.enabled", true);
}
};
}
PopupNotifications.show(browser, notificationID, messageString, anchorID,
action, null, options);
break; }
case "addon-install-origin-blocked": {
messageString = gNavigatorBundle.getFormattedString("xpinstallPromptMessage",
[brandShortName]);
let secHistogram = Components.classes["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry).getHistogramById("SECURITY_UI");
secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED);
let popup = PopupNotifications.show(browser, notificationID,
messageString, anchorID,
null, null, options);
removeNotificationOnEnd(popup, installInfo.installs);
break; }
case "addon-install-blocked": {
messageString = gNavigatorBundle.getFormattedString("xpinstallPromptMessage",
[brandShortName]);
let secHistogram = Components.classes["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry).getHistogramById("SECURITY_UI");
action = {
label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
callback: function() {
secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED_CLICK_THROUGH);
installInfo.install();
}
};
secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED);
let popup = PopupNotifications.show(browser, notificationID,
messageString, anchorID,
action, null, options);
removeNotificationOnEnd(popup, installInfo.installs);
break; }
case "addon-install-started": {
let needsDownload = function needsDownload(aInstall) {
return aInstall.state != AddonManager.STATE_DOWNLOADED;
}
// If all installs have already been downloaded then there is no need to
// show the download progress
if (!installInfo.installs.some(needsDownload))
return;
notificationID = "addon-progress";
messageString = gNavigatorBundle.getString("addonDownloadingAndVerifying");
messageString = PluralForm.get(installInfo.installs.length, messageString);
messageString = messageString.replace("#1", installInfo.installs.length);
options.installs = installInfo.installs;
options.contentWindow = browser.contentWindow;
options.sourceURI = browser.currentURI;
options.eventCallback = (aEvent) => {
switch (aEvent) {
case "removed":
options.contentWindow = null;
options.sourceURI = null;
break;
}
};
let notification = PopupNotifications.show(browser, notificationID, messageString,
anchorID, null, null, options);
notification._startTime = Date.now();
let cancelButton = document.getElementById("addon-progress-cancel");
cancelButton.label = gNavigatorBundle.getString("addonInstall.cancelButton.label");
cancelButton.accessKey = gNavigatorBundle.getString("addonInstall.cancelButton.accesskey");
let acceptButton = document.getElementById("addon-progress-accept");
if (Preferences.get("xpinstall.customConfirmationUI", false)) {
acceptButton.label = gNavigatorBundle.getString("addonInstall.acceptButton.label");
acceptButton.accessKey = gNavigatorBundle.getString("addonInstall.acceptButton.accesskey");
} else {
acceptButton.hidden = true;
}
break; }
case "addon-install-failed": {
// TODO This isn't terribly ideal for the multiple failure case
for (let install of installInfo.installs) {
let host;
try {
host = options.displayURI.host;
} catch (e) {
// displayURI might be missing or 'host' might throw for non-nsStandardURL nsIURIs.
}
if (!host)
host = (install.sourceURI instanceof Ci.nsIStandardURL) &&
install.sourceURI.host;
let error = (host || install.error == 0) ? "addonInstallError" : "addonLocalInstallError";
let args;
if (install.error < 0) {
error += install.error;
args = [brandShortName, install.name];
} else if (install.addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) {
error += "Blocklisted";
args = [install.name];
} else {
error += "Incompatible";
args = [brandShortName, Services.appinfo.version, install.name];
}
// Add Learn More link when refusing to install an unsigned add-on
if (install.error == AddonManager.ERROR_SIGNEDSTATE_REQUIRED) {
options.learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "unsigned-addons";
}
messageString = gNavigatorBundle.getFormattedString(error, args);
PopupNotifications.show(browser, notificationID, messageString, anchorID,
action, null, options);
// Can't have multiple notifications with the same ID, so stop here.
break;
}
this._removeProgressNotification(browser);
break; }
case "addon-install-confirmation": {
let showNotification = () => {
let height = undefined;
if (PopupNotifications.isPanelOpen) {
let rect = document.getElementById("addon-progress-notification").getBoundingClientRect();
height = rect.height;
}
this._removeProgressNotification(browser);
this.showInstallConfirmation(browser, installInfo, height);
};
let progressNotification = PopupNotifications.getNotification("addon-progress", browser);
if (progressNotification) {
let downloadDuration = Date.now() - progressNotification._startTime;
let securityDelay = Services.prefs.getIntPref("security.dialog_enable_delay") - downloadDuration;
if (securityDelay > 0) {
setTimeout(() => {
// The download may have been cancelled during the security delay
if (PopupNotifications.getNotification("addon-progress", browser))
showNotification();
}, securityDelay);
break;
}
}
showNotification();
break; }
case "addon-install-complete": {
let needsRestart = installInfo.installs.some(function(i) {
return i.addon.pendingOperations != AddonManager.PENDING_NONE;
});
if (needsRestart) {
notificationID = "addon-install-restart";
messageString = gNavigatorBundle.getString("addonsInstalledNeedsRestart");
action = {
label: gNavigatorBundle.getString("addonInstallRestartButton"),
accessKey: gNavigatorBundle.getString("addonInstallRestartButton.accesskey"),
callback: function() {
BrowserUtils.restartApplication();
}
};
}
else {
messageString = gNavigatorBundle.getString("addonsInstalled");
action = null;
}
messageString = PluralForm.get(installInfo.installs.length, messageString);
messageString = messageString.replace("#1", installInfo.installs[0].name);
messageString = messageString.replace("#2", installInfo.installs.length);
messageString = messageString.replace("#3", brandShortName);
// Remove notificaion on dismissal, since it's possible to cancel the
// install through the addons manager UI, making the "restart" prompt
// irrelevant.
options.removeOnDismissal = true;
PopupNotifications.show(browser, notificationID, messageString, anchorID,
action, null, options);
break; }
}
},
_removeProgressNotification(aBrowser) {
let notification = PopupNotifications.getNotification("addon-progress", aBrowser);
if (notification)
notification.remove();
}
};
var LightWeightThemeWebInstaller = {
init: function () {
let mm = window.messageManager;
mm.addMessageListener("LightWeightThemeWebInstaller:Install", this);
mm.addMessageListener("LightWeightThemeWebInstaller:Preview", this);
mm.addMessageListener("LightWeightThemeWebInstaller:ResetPreview", this);
},
receiveMessage: function (message) {
// ignore requests from background tabs
if (message.target != gBrowser.selectedBrowser) {
return;
}
let data = message.data;
switch (message.name) {
case "LightWeightThemeWebInstaller:Install": {
this._installRequest(data.themeData, data.principal, data.baseURI);
break;
}
case "LightWeightThemeWebInstaller:Preview": {
this._preview(data.themeData, data.principal, data.baseURI);
break;
}
case "LightWeightThemeWebInstaller:ResetPreview": {
this._resetPreview(data && data.principal);
break;
}
}
},
handleEvent: function (event) {
switch (event.type) {
case "TabSelect": {
this._resetPreview();
break;
}
}
},
get _manager () {
let temp = {};
Cu.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
delete this._manager;
return this._manager = temp.LightweightThemeManager;
},
_installRequest(dataString, principal, baseURI) {
// Don't allow installing off null principals.
if (!principal.URI) {
return;
}
let data = this._manager.parseTheme(dataString, baseURI);
if (!data) {
return;
}
// A notification bar with the option to undo is normally shown after a
// theme is installed. But the discovery pane served from the url(s)
// below has its own toggle switch for quick undos, so don't show the
// notification in that case.
let notify = this._shouldShowUndoPrompt(principal);
if (this._isAllowed(principal)) {
this._install(data, notify);
return;
}
let allowButtonText =
gNavigatorBundle.getString("lwthemeInstallRequest.allowButton");
let allowButtonAccesskey =
gNavigatorBundle.getString("lwthemeInstallRequest.allowButton.accesskey");
let message =
gNavigatorBundle.getFormattedString("lwthemeInstallRequest.message",
[principal.URI.host]);
let buttons = [{
label: allowButtonText,
accessKey: allowButtonAccesskey,
callback: function () {
LightWeightThemeWebInstaller._install(data, notify);
}
}];
this._removePreviousNotifications();
let notificationBox = gBrowser.getNotificationBox();
let notificationBar =
notificationBox.appendNotification(message, "lwtheme-install-request", "",
notificationBox.PRIORITY_INFO_MEDIUM,
buttons);
notificationBar.persistence = 1;
},
_install: function (newLWTheme, notify) {
let previousLWTheme = this._manager.currentTheme;
let listener = {
onEnabling: function(aAddon, aRequiresRestart) {
if (!aRequiresRestart) {
return;
}
let messageString = gNavigatorBundle.getFormattedString("lwthemeNeedsRestart.message",
[aAddon.name], 1);
let action = {
label: gNavigatorBundle.getString("lwthemeNeedsRestart.button"),
accessKey: gNavigatorBundle.getString("lwthemeNeedsRestart.accesskey"),
callback: function () {
BrowserUtils.restartApplication();
}
};
let options = {
timeout: Date.now() + 30000
};
PopupNotifications.show(gBrowser.selectedBrowser, "addon-theme-change",
messageString, "addons-notification-icon",
action, null, options);
},
onEnabled: function(aAddon) {
if (notify) {
LightWeightThemeWebInstaller._postInstallNotification(newLWTheme, previousLWTheme);
}
}
};
AddonManager.addAddonListener(listener);
this._manager.currentTheme = newLWTheme;
AddonManager.removeAddonListener(listener);
},
_postInstallNotification: function (newTheme, previousTheme) {
function text(id) {
return gNavigatorBundle.getString("lwthemePostInstallNotification." + id);
}
let buttons = [{
label: text("undoButton"),
accessKey: text("undoButton.accesskey"),
callback: function () {
LightWeightThemeWebInstaller._manager.forgetUsedTheme(newTheme.id);
LightWeightThemeWebInstaller._manager.currentTheme = previousTheme;
}
}, {
label: text("manageButton"),
accessKey: text("manageButton.accesskey"),
callback: function () {
BrowserOpenAddonsMgr("addons://list/theme");
}
}];
this._removePreviousNotifications();
let notificationBox = gBrowser.getNotificationBox();
let notificationBar =
notificationBox.appendNotification(text("message"),
"lwtheme-install-notification", "",
notificationBox.PRIORITY_INFO_MEDIUM,
buttons);
notificationBar.persistence = 1;
notificationBar.timeout = Date.now() + 20000; // 20 seconds
},
_removePreviousNotifications: function () {
let box = gBrowser.getNotificationBox();
["lwtheme-install-request",
"lwtheme-install-notification"].forEach(function (value) {
let notification = box.getNotificationWithValue(value);
if (notification)
box.removeNotification(notification);
});
},
_preview(dataString, principal, baseURI) {
if (!this._isAllowed(principal))
return;
let data = this._manager.parseTheme(dataString, baseURI);
if (!data)
return;
this._resetPreview();
gBrowser.tabContainer.addEventListener("TabSelect", this, false);
this._manager.previewTheme(data);
},
_resetPreview(principal) {
if (!this._isAllowed(principal))
return;
gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
this._manager.resetPreview();
},
_isAllowed(principal) {
if (!principal || !principal.URI || !principal.URI.schemeIs("https")) {
return false;
}
let pm = Services.perms;
return pm.testPermission(principal.URI, "install") == pm.ALLOW_ACTION;
},
_shouldShowUndoPrompt(principal) {
if (!principal || !principal.URI) {
return true;
}
let prePath = principal.URI.prePath;
if (prePath == "https://addons.palemoon.org") {
return false;
}
return true;
}
};
/*
* Listen for Lightweight Theme styling changes and update the browser's theme accordingly.
*/
var LightweightThemeListener = {
_modifiedStyles: [],
init: function () {
XPCOMUtils.defineLazyGetter(this, "styleSheet", function() {
for (let i = document.styleSheets.length - 1; i >= 0; i--) {
let sheet = document.styleSheets[i];
if (sheet.href == "chrome://browser/skin/browser-lightweightTheme.css")
return sheet;
}
return undefined;
});
Services.obs.addObserver(this, "lightweight-theme-styling-update", false);
Services.obs.addObserver(this, "lightweight-theme-optimized", false);
if (document.documentElement.hasAttribute("lwtheme"))
this.updateStyleSheet(document.documentElement.style.backgroundImage);
},
uninit: function () {
Services.obs.removeObserver(this, "lightweight-theme-styling-update");
Services.obs.removeObserver(this, "lightweight-theme-optimized");
},
/**
* Append the headerImage to the background-image property of all rulesets in
* browser-lightweightTheme.css.
*
* @param headerImage - a string containing a CSS image for the lightweight theme header.
*/
updateStyleSheet: function(headerImage) {
if (!this.styleSheet)
return;
this.substituteRules(this.styleSheet.cssRules, headerImage);
},
substituteRules: function(ruleList, headerImage, existingStyleRulesModified = 0) {
let styleRulesModified = 0;
for (let i = 0; i < ruleList.length; i++) {
let rule = ruleList[i];
if (rule instanceof Ci.nsIDOMCSSGroupingRule) {
// Add the number of modified sub-rules to the modified count
styleRulesModified += this.substituteRules(rule.cssRules, headerImage, existingStyleRulesModified + styleRulesModified);
} else if (rule instanceof Ci.nsIDOMCSSStyleRule) {
if (!rule.style.backgroundImage)
continue;
let modifiedIndex = existingStyleRulesModified + styleRulesModified;
if (!this._modifiedStyles[modifiedIndex])
this._modifiedStyles[modifiedIndex] = { backgroundImage: rule.style.backgroundImage };
rule.style.backgroundImage = this._modifiedStyles[modifiedIndex].backgroundImage + ", " + headerImage;
styleRulesModified++;
} else {
Cu.reportError("Unsupported rule encountered");
}
}
return styleRulesModified;
},
// nsIObserver
observe: function (aSubject, aTopic, aData) {
if ((aTopic != "lightweight-theme-styling-update" && aTopic != "lightweight-theme-optimized") ||
!this.styleSheet)
return;
if (aTopic == "lightweight-theme-optimized" && aSubject != window)
return;
let themeData = JSON.parse(aData);
if (!themeData)
return;
this.updateStyleSheet("url(" + themeData.headerURL + ")");
},
};

View file

@ -1,257 +0,0 @@
/* 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/. */
XPCOMUtils.defineLazyServiceGetter(this, "cps",
"@mozilla.org/network/captive-portal-service;1",
"nsICaptivePortalService");
var CaptivePortalWatcher = {
/**
* This constant is chosen to be large enough for a portal recheck to complete,
* and small enough that the delay in opening a tab isn't too noticeable.
* Please see comments for _delayedCaptivePortalDetected for more details.
*/
PORTAL_RECHECK_DELAY_MS: Preferences.get("captivedetect.portalRecheckDelayMS", 500),
// This is the value used to identify the captive portal notification.
PORTAL_NOTIFICATION_VALUE: "captive-portal-detected",
// This holds a weak reference to the captive portal tab so that we
// don't leak it if the user closes it.
_captivePortalTab: null,
/**
* If a portal is detected when we don't have focus, we first wait for focus
* and then add the tab if, after a recheck, the portal is still active. This
* is set to true while we wait so that in the unlikely event that we receive
* another notification while waiting, we don't do things twice.
*/
_delayedCaptivePortalDetectedInProgress: false,
// In the situation above, this is set to true while we wait for the recheck.
// This flag exists so that tests can appropriately simulate a recheck.
_waitingForRecheck: false,
get _captivePortalNotification() {
let nb = document.getElementById("high-priority-global-notificationbox");
return nb.getNotificationWithValue(this.PORTAL_NOTIFICATION_VALUE);
},
get canonicalURL() {
return Services.prefs.getCharPref("captivedetect.canonicalURL");
},
get _browserBundle() {
delete this._browserBundle;
return this._browserBundle =
Services.strings.createBundle("chrome://browser/locale/browser.properties");
},
init() {
Services.obs.addObserver(this, "captive-portal-login", false);
Services.obs.addObserver(this, "captive-portal-login-abort", false);
Services.obs.addObserver(this, "captive-portal-login-success", false);
if (cps.state == cps.LOCKED_PORTAL) {
// A captive portal has already been detected.
this._captivePortalDetected();
// Automatically open a captive portal tab if there's no other browser window.
let windows = Services.wm.getEnumerator("navigator:browser");
if (windows.getNext() == window && !windows.hasMoreElements()) {
this.ensureCaptivePortalTab();
}
}
cps.recheckCaptivePortal();
},
uninit() {
Services.obs.removeObserver(this, "captive-portal-login");
Services.obs.removeObserver(this, "captive-portal-login-abort");
Services.obs.removeObserver(this, "captive-portal-login-success");
if (this._delayedCaptivePortalDetectedInProgress) {
Services.obs.removeObserver(this, "xul-window-visible");
}
},
observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "captive-portal-login":
this._captivePortalDetected();
break;
case "captive-portal-login-abort":
case "captive-portal-login-success":
this._captivePortalGone();
break;
case "xul-window-visible":
this._delayedCaptivePortalDetected();
break;
}
},
_captivePortalDetected() {
if (this._delayedCaptivePortalDetectedInProgress) {
return;
}
let win = RecentWindow.getMostRecentBrowserWindow();
// If no browser window has focus, open and show the tab when we regain focus.
// This is so that if a different application was focused, when the user
// (re-)focuses a browser window, we open the tab immediately in that window
// so they can log in before continuing to browse.
if (win != Services.ww.activeWindow) {
this._delayedCaptivePortalDetectedInProgress = true;
Services.obs.addObserver(this, "xul-window-visible", false);
}
this._showNotification();
},
/**
* Called after we regain focus if we detect a portal while a browser window
* doesn't have focus. Triggers a portal recheck to reaffirm state, and adds
* the tab if needed after a short delay to allow the recheck to complete.
*/
_delayedCaptivePortalDetected() {
if (!this._delayedCaptivePortalDetectedInProgress) {
return;
}
let win = RecentWindow.getMostRecentBrowserWindow();
if (win != Services.ww.activeWindow) {
// The window that got focused was not a browser window.
return;
}
Services.obs.removeObserver(this, "xul-window-visible");
this._delayedCaptivePortalDetectedInProgress = false;
if (win != window) {
// Some other browser window got focus, we don't have to do anything.
return;
}
// Trigger a portal recheck. The user may have logged into the portal via
// another client, or changed networks.
cps.recheckCaptivePortal();
this._waitingForRecheck = true;
let requestTime = Date.now();
let self = this;
Services.obs.addObserver(function observer() {
let time = Date.now() - requestTime;
Services.obs.removeObserver(observer, "captive-portal-check-complete");
self._waitingForRecheck = false;
if (cps.state != cps.LOCKED_PORTAL) {
// We're free of the portal!
return;
}
if (time <= self.PORTAL_RECHECK_DELAY_MS) {
// The amount of time elapsed since we requested a recheck (i.e. since
// the browser window was focused) was small enough that we can add and
// focus a tab with the login page with no noticeable delay.
self.ensureCaptivePortalTab();
}
}, "captive-portal-check-complete", false);
},
_captivePortalGone() {
if (this._delayedCaptivePortalDetectedInProgress) {
Services.obs.removeObserver(this, "xul-window-visible");
this._delayedCaptivePortalDetectedInProgress = false;
}
this._removeNotification();
},
handleEvent(aEvent) {
if (aEvent.type != "TabSelect" || !this._captivePortalTab || !this._captivePortalNotification) {
return;
}
let tab = this._captivePortalTab.get();
let n = this._captivePortalNotification;
if (!tab || !n) {
return;
}
let doc = tab.ownerDocument;
let button = n.querySelector("button.notification-button");
if (doc.defaultView.gBrowser.selectedTab == tab) {
button.style.visibility = "hidden";
} else {
button.style.visibility = "visible";
}
},
_showNotification() {
let buttons = [
{
label: this._browserBundle.GetStringFromName("captivePortal.showLoginPage"),
callback: () => {
this.ensureCaptivePortalTab();
// Returning true prevents the notification from closing.
return true;
},
isDefault: true,
},
];
let message = this._browserBundle.GetStringFromName("captivePortal.infoMessage2");
let closeHandler = (aEventName) => {
if (aEventName != "removed") {
return;
}
gBrowser.tabContainer.removeEventListener("TabSelect", this);
};
let nb = document.getElementById("high-priority-global-notificationbox");
nb.appendNotification(message, this.PORTAL_NOTIFICATION_VALUE, "",
nb.PRIORITY_INFO_MEDIUM, buttons, closeHandler);
gBrowser.tabContainer.addEventListener("TabSelect", this);
},
_removeNotification() {
let n = this._captivePortalNotification;
if (!n || !n.parentNode) {
return;
}
n.close();
},
ensureCaptivePortalTab() {
let tab;
if (this._captivePortalTab) {
tab = this._captivePortalTab.get();
}
// If the tab is gone or going, we need to open a new one.
if (!tab || tab.closing || !tab.parentNode) {
tab = gBrowser.addTab(this.canonicalURL, { ownerTab: gBrowser.selectedTab });
this._captivePortalTab = Cu.getWeakReference(tab);
}
gBrowser.selectedTab = tab;
let canonicalURI = makeURI(this.canonicalURL);
// When we are no longer captive, close the tab if it's at the canonical URL.
let tabCloser = () => {
Services.obs.removeObserver(tabCloser, "captive-portal-login-abort");
Services.obs.removeObserver(tabCloser, "captive-portal-login-success");
if (!tab || tab.closing || !tab.parentNode || !tab.linkedBrowser ||
!tab.linkedBrowser.currentURI.equalsExceptRef(canonicalURI)) {
return;
}
gBrowser.removeTab(tab);
}
Services.obs.addObserver(tabCloser, "captive-portal-login-abort", false);
Services.obs.addObserver(tabCloser, "captive-portal-login-success", false);
},
};

View file

@ -1,12 +0,0 @@
# 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/.
<menu id="charsetMenu"
label="&charsetMenu2.label;"
accesskey="&charsetMenu2.accesskey;"
oncommand="BrowserSetForcedCharacterSet(event.target.getAttribute('charset'));"
onpopupshowing="CharsetMenu.build(event.target); UpdateCurrentCharset(this);">
<menupopup>
</menupopup>
</menu>

View file

@ -1,454 +0,0 @@
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# 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/.
# NB: IF YOU ADD ITEMS TO THIS FILE, PLEASE UPDATE THE WHITELIST IN
# BrowserUITelemetry.jsm. SEE BUG 991757 FOR DETAILS.
<menugroup id="context-navigation">
<menuitem id="context-back"
class="menuitem-iconic"
tooltiptext="&backButton.tooltip;"
aria-label="&backCmd.label;"
command="Browser:BackOrBackDuplicate"
onclick="checkForMiddleClick(this, event);"/>
<menuitem id="context-forward"
class="menuitem-iconic"
tooltiptext="&forwardButton.tooltip;"
aria-label="&forwardCmd.label;"
command="Browser:ForwardOrForwardDuplicate"
onclick="checkForMiddleClick(this, event);"/>
<menuitem id="context-reload"
class="menuitem-iconic"
tooltiptext="&reloadButton.tooltip;"
aria-label="&reloadCmd.label;"
oncommand="gContextMenu.reload(event);"
onclick="checkForMiddleClick(this, event);"/>
<menuitem id="context-stop"
class="menuitem-iconic"
tooltiptext="&stopButton.tooltip;"
aria-label="&stopCmd.label;"
command="Browser:Stop"/>
<menuitem id="context-bookmarkpage"
class="menuitem-iconic"
observes="bookmarkThisPageBroadcaster"
aria-label="&bookmarkPageCmd2.label;"
oncommand="gContextMenu.bookmarkThisPage();"/>
</menugroup>
<menuseparator id="context-sep-navigation"/>
<menuseparator id="page-menu-separator"/>
<menuitem id="spell-no-suggestions"
disabled="true"
label="&spellNoSuggestions.label;"/>
<menuitem id="spell-add-to-dictionary"
label="&spellAddToDictionary.label;"
accesskey="&spellAddToDictionary.accesskey;"
oncommand="InlineSpellCheckerUI.addToDictionary();"/>
<menuitem id="spell-undo-add-to-dictionary"
label="&spellUndoAddToDictionary.label;"
accesskey="&spellUndoAddToDictionary.accesskey;"
oncommand="InlineSpellCheckerUI.undoAddToDictionary();" />
<menuseparator id="spell-suggestions-separator"/>
<menuitem id="context-openlinkincurrent"
label="&openLinkCmdInCurrent.label;"
accesskey="&openLinkCmdInCurrent.accesskey;"
oncommand="gContextMenu.openLinkInCurrent();"/>
# label and data-usercontextid are dynamically set.
<menuitem id="context-openlinkincontainertab"
accesskey="&openLinkCmdInTab.accesskey;"
oncommand="gContextMenu.openLinkInTab(event);"/>
<menuitem id="context-openlinkintab"
label="&openLinkCmdInTab.label;"
accesskey="&openLinkCmdInTab.accesskey;"
data-usercontextid="0"
oncommand="gContextMenu.openLinkInTab(event);"/>
<menu id="context-openlinkinusercontext-menu"
label="&openLinkCmdInContainerTab.label;"
accesskey="&openLinkCmdInContainerTab.accesskey;"
hidden="true">
<menupopup oncommand="gContextMenu.openLinkInTab(event);"
onpopupshowing="return gContextMenu.createContainerMenu(event);" />
</menu>
<menuitem id="context-openlink"
label="&openLinkCmd.label;"
accesskey="&openLinkCmd.accesskey;"
oncommand="gContextMenu.openLink();"/>
<menuitem id="context-openlinkprivate"
label="&openLinkInPrivateWindowCmd.label;"
accesskey="&openLinkInPrivateWindowCmd.accesskey;"
oncommand="gContextMenu.openLinkInPrivateWindow();"/>
<menuseparator id="context-sep-open"/>
<menuitem id="context-bookmarklink"
label="&bookmarkThisLinkCmd.label;"
accesskey="&bookmarkThisLinkCmd.accesskey;"
oncommand="gContextMenu.bookmarkLink();"/>
<menuitem id="context-savelink"
label="&saveLinkCmd.label;"
accesskey="&saveLinkCmd.accesskey;"
oncommand="gContextMenu.saveLink();"/>
<menuitem id="context-copyemail"
label="&copyEmailCmd.label;"
accesskey="&copyEmailCmd.accesskey;"
oncommand="gContextMenu.copyEmail();"/>
<menuitem id="context-copylink"
label="&copyLinkCmd.label;"
accesskey="&copyLinkCmd.accesskey;"
oncommand="gContextMenu.copyLink();"/>
<menuseparator id="context-sep-copylink"/>
<menuitem id="context-media-play"
label="&mediaPlay.label;"
accesskey="&mediaPlay.accesskey;"
oncommand="gContextMenu.mediaCommand('play');"/>
<menuitem id="context-media-pause"
label="&mediaPause.label;"
accesskey="&mediaPause.accesskey;"
oncommand="gContextMenu.mediaCommand('pause');"/>
<menuitem id="context-media-mute"
label="&mediaMute.label;"
accesskey="&mediaMute.accesskey;"
oncommand="gContextMenu.mediaCommand('mute');"/>
<menuitem id="context-media-unmute"
label="&mediaUnmute.label;"
accesskey="&mediaUnmute.accesskey;"
oncommand="gContextMenu.mediaCommand('unmute');"/>
<menu id="context-media-playbackrate" label="&mediaPlaybackRate2.label;" accesskey="&mediaPlaybackRate2.accesskey;">
<menupopup>
<menuitem id="context-media-playbackrate-050x"
label="&mediaPlaybackRate050x2.label;"
accesskey="&mediaPlaybackRate050x2.accesskey;"
type="radio"
name="playbackrate"
oncommand="gContextMenu.mediaCommand('playbackRate', 0.5);"/>
<menuitem id="context-media-playbackrate-100x"
label="&mediaPlaybackRate100x2.label;"
accesskey="&mediaPlaybackRate100x2.accesskey;"
type="radio"
name="playbackrate"
checked="true"
oncommand="gContextMenu.mediaCommand('playbackRate', 1.0);"/>
<menuitem id="context-media-playbackrate-125x"
label="&mediaPlaybackRate125x2.label;"
accesskey="&mediaPlaybackRate125x2.accesskey;"
type="radio"
name="playbackrate"
oncommand="gContextMenu.mediaCommand('playbackRate', 1.25);"/>
<menuitem id="context-media-playbackrate-150x"
label="&mediaPlaybackRate150x2.label;"
accesskey="&mediaPlaybackRate150x2.accesskey;"
type="radio"
name="playbackrate"
oncommand="gContextMenu.mediaCommand('playbackRate', 1.5);"/>
<menuitem id="context-media-playbackrate-200x"
label="&mediaPlaybackRate200x2.label;"
accesskey="&mediaPlaybackRate200x2.accesskey;"
type="radio"
name="playbackrate"
oncommand="gContextMenu.mediaCommand('playbackRate', 2.0);"/>
</menupopup>
</menu>
<menuitem id="context-media-loop"
label="&mediaLoop.label;"
accesskey="&mediaLoop.accesskey;"
type="checkbox"
oncommand="gContextMenu.mediaCommand('loop');"/>
<menuitem id="context-media-showcontrols"
label="&mediaShowControls.label;"
accesskey="&mediaShowControls.accesskey;"
oncommand="gContextMenu.mediaCommand('showcontrols');"/>
<menuitem id="context-media-hidecontrols"
label="&mediaHideControls.label;"
accesskey="&mediaHideControls.accesskey;"
oncommand="gContextMenu.mediaCommand('hidecontrols');"/>
<menuitem id="context-video-fullscreen"
accesskey="&videoFullScreen.accesskey;"
label="&videoFullScreen.label;"
oncommand="gContextMenu.mediaCommand('fullscreen');"/>
<menuitem id="context-leave-dom-fullscreen"
accesskey="&leaveDOMFullScreen.accesskey;"
label="&leaveDOMFullScreen.label;"
oncommand="gContextMenu.leaveDOMFullScreen();"/>
<menuseparator id="context-media-sep-commands"/>
<menuitem id="context-reloadimage"
label="&reloadImageCmd.label;"
accesskey="&reloadImageCmd.accesskey;"
oncommand="gContextMenu.reloadImage();"/>
<menuitem id="context-viewimage"
label="&viewImageCmd.label;"
accesskey="&viewImageCmd.accesskey;"
oncommand="gContextMenu.viewMedia(event);"
onclick="checkForMiddleClick(this, event);"/>
<menuitem id="context-viewvideo"
label="&viewVideoCmd.label;"
accesskey="&viewVideoCmd.accesskey;"
oncommand="gContextMenu.viewMedia(event);"
onclick="checkForMiddleClick(this, event);"/>
#ifdef CONTEXT_COPY_IMAGE_CONTENTS
<menuitem id="context-copyimage-contents"
label="&copyImageContentsCmd.label;"
accesskey="&copyImageContentsCmd.accesskey;"
oncommand="goDoCommand('cmd_copyImage');"/>
#endif
<menuitem id="context-copyimage"
label="&copyImageCmd.label;"
accesskey="&copyImageCmd.accesskey;"
oncommand="gContextMenu.copyMediaLocation();"/>
<menuitem id="context-copyvideourl"
label="&copyVideoURLCmd.label;"
accesskey="&copyVideoURLCmd.accesskey;"
oncommand="gContextMenu.copyMediaLocation();"/>
<menuitem id="context-copyaudiourl"
label="&copyAudioURLCmd.label;"
accesskey="&copyAudioURLCmd.accesskey;"
oncommand="gContextMenu.copyMediaLocation();"/>
<menuseparator id="context-sep-copyimage"/>
<menuitem id="context-saveimage"
label="&saveImageCmd.label;"
accesskey="&saveImageCmd.accesskey;"
oncommand="gContextMenu.saveMedia();"/>
<menuitem id="context-sendimage"
label="&emailImageCmd.label;"
accesskey="&emailImageCmd.accesskey;"
oncommand="gContextMenu.sendMedia();"/>
<menuitem id="context-setDesktopBackground"
label="&setDesktopBackgroundCmd.label;"
accesskey="&setDesktopBackgroundCmd.accesskey;"
oncommand="gContextMenu.setDesktopBackground();"/>
<menuitem id="context-viewimageinfo"
label="&viewImageInfoCmd.label;"
accesskey="&viewImageInfoCmd.accesskey;"
oncommand="gContextMenu.viewImageInfo();"/>
<menuitem id="context-viewimagedesc"
label="&viewImageDescCmd.label;"
accesskey="&viewImageDescCmd.accesskey;"
oncommand="gContextMenu.viewImageDesc(event);"
onclick="checkForMiddleClick(this, event);"/>
<menuitem id="context-savevideo"
label="&saveVideoCmd.label;"
accesskey="&saveVideoCmd.accesskey;"
oncommand="gContextMenu.saveMedia();"/>
<menuitem id="context-saveaudio"
label="&saveAudioCmd.label;"
accesskey="&saveAudioCmd.accesskey;"
oncommand="gContextMenu.saveMedia();"/>
<menuitem id="context-video-saveimage"
accesskey="&videoSaveImage.accesskey;"
label="&videoSaveImage.label;"
oncommand="gContextMenu.saveVideoFrameAsImage();"/>
<menuitem id="context-sendvideo"
label="&emailVideoCmd.label;"
accesskey="&emailVideoCmd.accesskey;"
oncommand="gContextMenu.sendMedia();"/>
<menu id="context-castvideo"
label="&castVideoCmd.label;"
accesskey="&castVideoCmd.accesskey;">
<menupopup id="context-castvideo-popup" onpopupshowing="gContextMenu.populateCastVideoMenu(this)"/>
</menu>
<menuitem id="context-sendaudio"
label="&emailAudioCmd.label;"
accesskey="&emailAudioCmd.accesskey;"
oncommand="gContextMenu.sendMedia();"/>
<menuitem id="context-ctp-play"
label="&playPluginCmd.label;"
accesskey="&playPluginCmd.accesskey;"
oncommand="gContextMenu.playPlugin();"/>
<menuitem id="context-ctp-hide"
label="&hidePluginCmd.label;"
accesskey="&hidePluginCmd.accesskey;"
oncommand="gContextMenu.hidePlugin();"/>
<menuseparator id="context-sep-ctp"/>
<menuitem id="context-savepage"
label="&savePageCmd.label;"
accesskey="&savePageCmd.accesskey2;"
oncommand="gContextMenu.savePageAs();"/>
<menuseparator id="context-sep-sendpagetodevice" hidden="true"/>
<menu id="context-sendpagetodevice"
label="&sendPageToDevice.label;"
accesskey="&sendPageToDevice.accesskey;"
hidden="true">
<menupopup id="context-sendpagetodevice-popup"
onpopupshowing="(() => { let browser = gBrowser || getPanelBrowser(); gFxAccounts.populateSendTabToDevicesMenu(event.target, browser.currentURI.spec, browser.contentTitle); })()"/>
</menu>
<menuseparator id="context-sep-viewbgimage"/>
<menuitem id="context-viewbgimage"
label="&viewBGImageCmd.label;"
accesskey="&viewBGImageCmd.accesskey;"
oncommand="gContextMenu.viewBGImage(event);"
onclick="checkForMiddleClick(this, event);"/>
<menuitem id="context-undo"
label="&undoCmd.label;"
accesskey="&undoCmd.accesskey;"
command="cmd_undo"/>
<menuseparator id="context-sep-undo"/>
<menuitem id="context-cut"
label="&cutCmd.label;"
accesskey="&cutCmd.accesskey;"
command="cmd_cut"/>
<menuitem id="context-copy"
label="&copyCmd.label;"
accesskey="&copyCmd.accesskey;"
command="cmd_copy"/>
<menuitem id="context-paste"
label="&pasteCmd.label;"
accesskey="&pasteCmd.accesskey;"
command="cmd_paste"/>
<menuitem id="context-delete"
label="&deleteCmd.label;"
accesskey="&deleteCmd.accesskey;"
command="cmd_delete"/>
<menuseparator id="context-sep-paste"/>
<menuitem id="context-selectall"
label="&selectAllCmd.label;"
accesskey="&selectAllCmd.accesskey;"
command="cmd_selectAll"/>
<menuseparator id="context-sep-selectall"/>
<menuitem id="context-keywordfield"
label="&keywordfield.label;"
accesskey="&keywordfield.accesskey;"
oncommand="AddKeywordForSearchField();"/>
<menuitem id="context-searchselect"
oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms);"/>
<menuseparator id="context-sep-sendlinktodevice" hidden="true"/>
<menu id="context-sendlinktodevice"
label="&sendLinkToDevice.label;"
accesskey="&sendLinkToDevice.accesskey;"
hidden="true">
<menupopup id="context-sendlinktodevice-popup"
onpopupshowing="gFxAccounts.populateSendTabToDevicesMenu(event.target, gContextMenu.linkURL, gContextMenu.linkTextStr);"/>
</menu>
<menuseparator id="frame-sep"/>
<menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
<menupopup>
<menuitem id="context-showonlythisframe"
label="&showOnlyThisFrameCmd.label;"
accesskey="&showOnlyThisFrameCmd.accesskey;"
oncommand="gContextMenu.showOnlyThisFrame();"/>
<menuitem id="context-openframeintab"
label="&openFrameCmdInTab.label;"
accesskey="&openFrameCmdInTab.accesskey;"
oncommand="gContextMenu.openFrameInTab();"/>
<menuitem id="context-openframe"
label="&openFrameCmd.label;"
accesskey="&openFrameCmd.accesskey;"
oncommand="gContextMenu.openFrame();"/>
<menuseparator id="open-frame-sep"/>
<menuitem id="context-reloadframe"
label="&reloadFrameCmd.label;"
accesskey="&reloadFrameCmd.accesskey;"
oncommand="gContextMenu.reloadFrame();"/>
<menuseparator/>
<menuitem id="context-bookmarkframe"
label="&bookmarkThisFrameCmd.label;"
accesskey="&bookmarkThisFrameCmd.accesskey;"
oncommand="gContextMenu.addBookmarkForFrame();"/>
<menuitem id="context-saveframe"
label="&saveFrameCmd.label;"
accesskey="&saveFrameCmd.accesskey;"
oncommand="gContextMenu.saveFrame();"/>
<menuseparator/>
<menuitem id="context-printframe"
label="&printFrameCmd.label;"
accesskey="&printFrameCmd.accesskey;"
oncommand="gContextMenu.printFrame();"/>
<menuseparator/>
<menuitem id="context-viewframesource"
label="&viewFrameSourceCmd.label;"
accesskey="&viewFrameSourceCmd.accesskey;"
oncommand="gContextMenu.viewFrameSource();"
observes="isFrameImage"/>
<menuitem id="context-viewframeinfo"
label="&viewFrameInfoCmd.label;"
accesskey="&viewFrameInfoCmd.accesskey;"
oncommand="gContextMenu.viewFrameInfo();"/>
</menupopup>
</menu>
<menuitem id="context-viewpartialsource-selection"
label="&viewPartialSourceForSelectionCmd.label;"
accesskey="&viewPartialSourceCmd.accesskey;"
oncommand="gContextMenu.viewPartialSource('selection');"
observes="isImage"/>
<menuitem id="context-viewpartialsource-mathml"
label="&viewPartialSourceForMathMLCmd.label;"
accesskey="&viewPartialSourceCmd.accesskey;"
oncommand="gContextMenu.viewPartialSource('mathml');"
observes="isImage"/>
<menuseparator id="context-sep-viewsource"/>
<menuitem id="context-viewsource"
label="&viewPageSourceCmd.label;"
accesskey="&viewPageSourceCmd.accesskey;"
oncommand="BrowserViewSource(gContextMenu.browser);"
observes="canViewSource"/>
<menuitem id="context-viewinfo"
label="&viewPageInfoCmd.label;"
accesskey="&viewPageInfoCmd.accesskey;"
oncommand="gContextMenu.viewInfo();"/>
<menuseparator id="spell-separator"/>
<menuitem id="spell-check-enabled"
label="&spellCheckToggle.label;"
type="checkbox"
accesskey="&spellCheckToggle.accesskey;"
oncommand="InlineSpellCheckerUI.toggleEnabled(window);"/>
<menuitem id="spell-add-dictionaries-main"
label="&spellAddDictionaries.label;"
accesskey="&spellAddDictionaries.accesskey;"
oncommand="gContextMenu.addDictionaries();"/>
<menu id="spell-dictionaries"
label="&spellDictionaries.label;"
accesskey="&spellDictionaries.accesskey;">
<menupopup id="spell-dictionaries-menu">
<menuseparator id="spell-language-separator"/>
<menuitem id="spell-add-dictionaries"
label="&spellAddDictionaries.label;"
accesskey="&spellAddDictionaries.accesskey;"
oncommand="gContextMenu.addDictionaries();"/>
</menupopup>
</menu>
<menuseparator hidden="true" id="context-sep-bidi"/>
<menuitem hidden="true" id="context-bidi-text-direction-toggle"
label="&bidiSwitchTextDirectionItem.label;"
accesskey="&bidiSwitchTextDirectionItem.accesskey;"
command="cmd_switchTextDirection"/>
<menuitem hidden="true" id="context-bidi-page-direction-toggle"
label="&bidiSwitchPageDirectionItem.label;"
accesskey="&bidiSwitchPageDirectionItem.accesskey;"
oncommand="gContextMenu.switchPageDirection();"/>
<menuseparator id="fill-login-separator" hidden="true"/>
<menu id="fill-login"
label="&fillLoginMenu.label;"
label-login="&fillLoginMenu.label;"
label-password="&fillPasswordMenu.label;"
label-username="&fillUsernameMenu.label;"
accesskey="&fillLoginMenu.accesskey;"
accesskey-login="&fillLoginMenu.accesskey;"
accesskey-password="&fillPasswordMenu.accesskey;"
accesskey-username="&fillUsernameMenu.accesskey;"
hidden="true">
<menupopup id="fill-login-popup">
<menuitem id="fill-login-no-logins"
label="&noLoginSuggestions.label;"
disabled="true"
hidden="true"/>
<menuseparator id="saved-logins-separator"/>
<menuitem id="fill-login-saved-passwords"
label="&viewSavedLogins.label;"
oncommand="gContextMenu.openPasswordManager();"/>
</menupopup>
</menu>
#ifdef MOZ_DEVTOOLS
<menuseparator id="inspect-separator" hidden="true"/>
<menuitem id="context-inspect"
hidden="true"
label="&inspectContextMenu.label;"
accesskey="&inspectContextMenu.accesskey;"
oncommand="gContextMenu.inspectNode();"/>
#endif
<menuseparator id="context-media-eme-separator" hidden="true"/>
<menuitem id="context-media-eme-learnmore"
class="menuitem-iconic"
hidden="true"
label="&emeLearnMoreContextMenu.label;"
accesskey="&emeLearnMoreContextMenu.accesskey;"
oncommand="gContextMenu.drmLearnMore(event);"
onclick="checkForMiddleClick(this, event);"/>

View file

@ -1,587 +0,0 @@
/* 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/. */
/**
* Tab previews utility, produces thumbnails
*/
var tabPreviews = {
init: function tabPreviews_init() {
if (this._selectedTab)
return;
this._selectedTab = gBrowser.selectedTab;
gBrowser.tabContainer.addEventListener("TabSelect", this, false);
gBrowser.tabContainer.addEventListener("SSTabRestored", this, false);
let screenManager = Cc["@mozilla.org/gfx/screenmanager;1"]
.getService(Ci.nsIScreenManager);
let left = {}, top = {}, width = {}, height = {};
screenManager.primaryScreen.GetRectDisplayPix(left, top, width, height);
this.aspectRatio = height.value / width.value;
},
get: function tabPreviews_get(aTab) {
let uri = aTab.linkedBrowser.currentURI.spec;
if (aTab.__thumbnail_lastURI &&
aTab.__thumbnail_lastURI != uri) {
aTab.__thumbnail = null;
aTab.__thumbnail_lastURI = null;
}
if (aTab.__thumbnail)
return aTab.__thumbnail;
if (aTab.getAttribute("pending") == "true") {
let img = new Image;
img.src = PageThumbs.getThumbnailURL(uri);
return img;
}
return this.capture(aTab, !aTab.hasAttribute("busy"));
},
capture: function tabPreviews_capture(aTab, aShouldCache) {
let browser = aTab.linkedBrowser;
let uri = browser.currentURI.spec;
let canvas = PageThumbs.createCanvas(window);
PageThumbs.shouldStoreThumbnail(browser, (aDoStore) => {
if (aDoStore && aShouldCache) {
PageThumbs.captureAndStore(browser, function () {
let img = new Image;
img.src = PageThumbs.getThumbnailURL(uri);
aTab.__thumbnail = img;
aTab.__thumbnail_lastURI = uri;
canvas.getContext("2d").drawImage(img, 0, 0);
});
} else {
PageThumbs.captureToCanvas(browser, canvas, () => {
if (aShouldCache) {
aTab.__thumbnail = canvas;
aTab.__thumbnail_lastURI = uri;
}
});
}
});
return canvas;
},
handleEvent: function tabPreviews_handleEvent(event) {
switch (event.type) {
case "TabSelect":
if (this._selectedTab &&
this._selectedTab.parentNode &&
!this._pendingUpdate) {
// Generate a thumbnail for the tab that was selected.
// The timeout keeps the UI snappy and prevents us from generating thumbnails
// for tabs that will be closed. During that timeout, don't generate other
// thumbnails in case multiple TabSelect events occur fast in succession.
this._pendingUpdate = true;
setTimeout(function (self, aTab) {
self._pendingUpdate = false;
if (aTab.parentNode &&
!aTab.hasAttribute("busy") &&
!aTab.hasAttribute("pending"))
self.capture(aTab, true);
}, 2000, this, this._selectedTab);
}
this._selectedTab = event.target;
break;
case "SSTabRestored":
this.capture(event.target, true);
break;
}
}
};
var tabPreviewPanelHelper = {
opening: function (host) {
host.panel.hidden = false;
var handler = this._generateHandler(host);
host.panel.addEventListener("popupshown", handler, false);
host.panel.addEventListener("popuphiding", handler, false);
host._prevFocus = document.commandDispatcher.focusedElement;
},
_generateHandler: function (host) {
var self = this;
return function (event) {
if (event.target == host.panel) {
host.panel.removeEventListener(event.type, arguments.callee, false);
self["_" + event.type](host);
}
};
},
_popupshown: function (host) {
if ("setupGUI" in host)
host.setupGUI();
},
_popuphiding: function (host) {
if ("suspendGUI" in host)
host.suspendGUI();
if (host._prevFocus) {
Services.focus.setFocus(host._prevFocus, Ci.nsIFocusManager.FLAG_NOSCROLL);
host._prevFocus = null;
} else
gBrowser.selectedBrowser.focus();
if (host.tabToSelect) {
gBrowser.selectedTab = host.tabToSelect;
host.tabToSelect = null;
}
}
};
/**
* Ctrl-Tab panel
*/
var ctrlTab = {
get panel () {
delete this.panel;
return this.panel = document.getElementById("ctrlTab-panel");
},
get showAllButton () {
delete this.showAllButton;
return this.showAllButton = document.getElementById("ctrlTab-showAll");
},
get previews () {
delete this.previews;
return this.previews = this.panel.getElementsByClassName("ctrlTab-preview");
},
get maxTabPreviews () {
delete this.maxTabPreviews;
return this.maxTabPreviews = this.previews.length - 1;
},
get canvasWidth () {
delete this.canvasWidth;
return this.canvasWidth = Math.ceil(screen.availWidth * .85 / this.maxTabPreviews);
},
get canvasHeight () {
delete this.canvasHeight;
return this.canvasHeight = Math.round(this.canvasWidth * tabPreviews.aspectRatio);
},
get keys () {
var keys = {};
["close", "find", "selectAll"].forEach(function (key) {
keys[key] = document.getElementById("key_" + key)
.getAttribute("key")
.toLocaleLowerCase().charCodeAt(0);
});
delete this.keys;
return this.keys = keys;
},
_selectedIndex: 0,
get selected () {
return this._selectedIndex < 0 ?
document.activeElement :
this.previews.item(this._selectedIndex);
},
get isOpen () {
return this.panel.state == "open" || this.panel.state == "showing" || this._timer;
},
get tabCount () {
return this.tabList.length;
},
get tabPreviewCount () {
return Math.min(this.maxTabPreviews, this.tabCount);
},
get tabList () {
return this._recentlyUsedTabs;
},
init: function ctrlTab_init() {
if (!this._recentlyUsedTabs) {
tabPreviews.init();
this._initRecentlyUsedTabs();
this._init(true);
}
},
uninit: function ctrlTab_uninit() {
this._recentlyUsedTabs = null;
this._init(false);
},
prefName: "browser.ctrlTab.previews",
readPref: function ctrlTab_readPref() {
var enable =
gPrefService.getBoolPref(this.prefName) &&
(!gPrefService.prefHasUserValue("browser.ctrlTab.disallowForScreenReaders") ||
!gPrefService.getBoolPref("browser.ctrlTab.disallowForScreenReaders"));
if (enable)
this.init();
else
this.uninit();
},
observe: function (aSubject, aTopic, aPrefName) {
this.readPref();
},
updatePreviews: function ctrlTab_updatePreviews() {
for (let i = 0; i < this.previews.length; i++)
this.updatePreview(this.previews[i], this.tabList[i]);
var showAllLabel = gNavigatorBundle.getString("ctrlTab.listAllTabs.label");
this.showAllButton.label =
PluralForm.get(this.tabCount, showAllLabel).replace("#1", this.tabCount);
this.showAllButton.hidden = !allTabs.canOpen;
},
updatePreview: function ctrlTab_updatePreview(aPreview, aTab) {
if (aPreview == this.showAllButton)
return;
aPreview._tab = aTab;
if (aPreview.firstChild)
aPreview.removeChild(aPreview.firstChild);
if (aTab) {
let canvasWidth = this.canvasWidth;
let canvasHeight = this.canvasHeight;
aPreview.appendChild(tabPreviews.get(aTab));
aPreview.setAttribute("label", aTab.label);
aPreview.setAttribute("tooltiptext", aTab.label);
aPreview.setAttribute("crop", aTab.crop);
aPreview.setAttribute("canvaswidth", canvasWidth);
aPreview.setAttribute("canvasstyle",
"max-width:" + canvasWidth + "px;" +
"min-width:" + canvasWidth + "px;" +
"max-height:" + canvasHeight + "px;" +
"min-height:" + canvasHeight + "px;");
if (aTab.image)
aPreview.setAttribute("image", aTab.image);
else
aPreview.removeAttribute("image");
aPreview.hidden = false;
} else {
aPreview.hidden = true;
aPreview.removeAttribute("label");
aPreview.removeAttribute("tooltiptext");
aPreview.removeAttribute("image");
}
},
advanceFocus: function ctrlTab_advanceFocus(aForward) {
let selectedIndex = Array.indexOf(this.previews, this.selected);
do {
selectedIndex += aForward ? 1 : -1;
if (selectedIndex < 0)
selectedIndex = this.previews.length - 1;
else if (selectedIndex >= this.previews.length)
selectedIndex = 0;
} while (this.previews[selectedIndex].hidden);
if (this._selectedIndex == -1) {
// Focus is already in the panel.
this.previews[selectedIndex].focus();
} else {
this._selectedIndex = selectedIndex;
}
if (this._timer) {
clearTimeout(this._timer);
this._timer = null;
this._openPanel();
}
},
_mouseOverFocus: function ctrlTab_mouseOverFocus(aPreview) {
if (this._trackMouseOver)
aPreview.focus();
},
pick: function ctrlTab_pick(aPreview) {
if (!this.tabCount)
return;
var select = (aPreview || this.selected);
if (select == this.showAllButton)
this.showAllTabs();
else
this.close(select._tab);
},
showAllTabs: function ctrlTab_showAllTabs(aPreview) {
this.close();
document.getElementById("Browser:ShowAllTabs").doCommand();
},
remove: function ctrlTab_remove(aPreview) {
if (aPreview._tab)
gBrowser.removeTab(aPreview._tab);
},
attachTab: function ctrlTab_attachTab(aTab, aPos) {
if (aTab.closing)
return;
if (aPos == 0)
this._recentlyUsedTabs.unshift(aTab);
else if (aPos)
this._recentlyUsedTabs.splice(aPos, 0, aTab);
else
this._recentlyUsedTabs.push(aTab);
},
detachTab: function ctrlTab_detachTab(aTab) {
var i = this._recentlyUsedTabs.indexOf(aTab);
if (i >= 0)
this._recentlyUsedTabs.splice(i, 1);
},
open: function ctrlTab_open() {
if (this.isOpen)
return;
document.addEventListener("keyup", this, true);
this.updatePreviews();
this._selectedIndex = 1;
// Add a slight delay before showing the UI, so that a quick
// "ctrl-tab" keypress just flips back to the MRU tab.
this._timer = setTimeout(function (self) {
self._timer = null;
self._openPanel();
}, 200, this);
},
_openPanel: function ctrlTab_openPanel() {
tabPreviewPanelHelper.opening(this);
this.panel.width = Math.min(screen.availWidth * .99,
this.canvasWidth * 1.25 * this.tabPreviewCount);
var estimateHeight = this.canvasHeight * 1.25 + 75;
this.panel.openPopupAtScreen(screen.availLeft + (screen.availWidth - this.panel.width) / 2,
screen.availTop + (screen.availHeight - estimateHeight) / 2,
false);
},
close: function ctrlTab_close(aTabToSelect) {
if (!this.isOpen)
return;
if (this._timer) {
clearTimeout(this._timer);
this._timer = null;
this.suspendGUI();
if (aTabToSelect)
gBrowser.selectedTab = aTabToSelect;
return;
}
this.tabToSelect = aTabToSelect;
this.panel.hidePopup();
},
setupGUI: function ctrlTab_setupGUI() {
this.selected.focus();
this._selectedIndex = -1;
// Track mouse movement after a brief delay so that the item that happens
// to be under the mouse pointer initially won't be selected unintentionally.
this._trackMouseOver = false;
setTimeout(function (self) {
if (self.isOpen)
self._trackMouseOver = true;
}, 0, this);
},
suspendGUI: function ctrlTab_suspendGUI() {
document.removeEventListener("keyup", this, true);
for (let preview of this.previews) {
this.updatePreview(preview, null);
}
},
onKeyPress: function ctrlTab_onKeyPress(event) {
var isOpen = this.isOpen;
if (isOpen) {
event.preventDefault();
event.stopPropagation();
}
switch (event.keyCode) {
case event.DOM_VK_TAB:
if (event.ctrlKey && !event.altKey && !event.metaKey) {
if (isOpen) {
this.advanceFocus(!event.shiftKey);
} else if (!event.shiftKey) {
event.preventDefault();
event.stopPropagation();
let tabs = gBrowser.visibleTabs;
if (tabs.length > 2) {
this.open();
} else if (tabs.length == 2) {
let index = tabs[0].selected ? 1 : 0;
gBrowser.selectedTab = tabs[index];
}
}
}
break;
default:
if (isOpen && event.ctrlKey) {
if (event.keyCode == event.DOM_VK_DELETE) {
this.remove(this.selected);
break;
}
switch (event.charCode) {
case this.keys.close:
this.remove(this.selected);
break;
case this.keys.find:
case this.keys.selectAll:
this.showAllTabs();
break;
}
}
}
},
removeClosingTabFromUI: function ctrlTab_removeClosingTabFromUI(aTab) {
if (this.tabCount == 2) {
this.close();
return;
}
this.updatePreviews();
if (this.selected.hidden)
this.advanceFocus(false);
if (this.selected == this.showAllButton)
this.advanceFocus(false);
// If the current tab is removed, another tab can steal our focus.
if (aTab.selected && this.panel.state == "open") {
setTimeout(function (selected) {
selected.focus();
}, 0, this.selected);
}
},
handleEvent: function ctrlTab_handleEvent(event) {
switch (event.type) {
case "SSWindowRestored":
this._initRecentlyUsedTabs();
break;
case "TabAttrModified":
// tab attribute modified (e.g. label, crop, busy, image, selected)
for (let i = this.previews.length - 1; i >= 0; i--) {
if (this.previews[i]._tab && this.previews[i]._tab == event.target) {
this.updatePreview(this.previews[i], event.target);
break;
}
}
break;
case "TabSelect":
this.detachTab(event.target);
this.attachTab(event.target, 0);
break;
case "TabOpen":
this.attachTab(event.target, 1);
break;
case "TabClose":
this.detachTab(event.target);
if (this.isOpen)
this.removeClosingTabFromUI(event.target);
break;
case "keypress":
this.onKeyPress(event);
break;
case "keyup":
if (event.keyCode == event.DOM_VK_CONTROL)
this.pick();
break;
case "popupshowing":
if (event.target.id == "menu_viewPopup")
document.getElementById("menu_showAllTabs").hidden = !allTabs.canOpen;
break;
}
},
filterForThumbnailExpiration: function (aCallback) {
// Save a few more thumbnails than we actually display, so that when tabs
// are closed, the previews we add instead still get thumbnails.
const extraThumbnails = 3;
const thumbnailCount = Math.min(this.tabPreviewCount + extraThumbnails,
this.tabCount);
let urls = [];
for (let i = 0; i < thumbnailCount; i++)
urls.push(this.tabList[i].linkedBrowser.currentURI.spec);
aCallback(urls);
},
_initRecentlyUsedTabs: function () {
this._recentlyUsedTabs =
Array.filter(gBrowser.tabs, tab => !tab.closing)
.sort((tab1, tab2) => tab2.lastAccessed - tab1.lastAccessed);
},
_init: function ctrlTab__init(enable) {
var toggleEventListener = enable ? "addEventListener" : "removeEventListener";
window[toggleEventListener]("SSWindowRestored", this, false);
var tabContainer = gBrowser.tabContainer;
tabContainer[toggleEventListener]("TabOpen", this, false);
tabContainer[toggleEventListener]("TabAttrModified", this, false);
tabContainer[toggleEventListener]("TabSelect", this, false);
tabContainer[toggleEventListener]("TabClose", this, false);
document[toggleEventListener]("keypress", this, false);
gBrowser.mTabBox.handleCtrlTab = !enable;
if (enable)
PageThumbs.addExpirationFilter(this);
else
PageThumbs.removeExpirationFilter(this);
// If we're not running, hide the "Show All Tabs" menu item,
// as Shift+Ctrl+Tab will be handled by the tab bar.
document.getElementById("menu_showAllTabs").hidden = !enable;
document.getElementById("menu_viewPopup")[toggleEventListener]("popupshowing", this);
// Also disable the <key> to ensure Shift+Ctrl+Tab never triggers
// Show All Tabs.
var key_showAllTabs = document.getElementById("key_showAllTabs");
if (enable)
key_showAllTabs.removeAttribute("disabled");
else
key_showAllTabs.setAttribute("disabled", "true");
}
};
/**
* All Tabs menu
*/
var allTabs = {
get toolbarButton() {
return document.getElementById("alltabs-button");
},
get canOpen() {
return isElementVisible(this.toolbarButton);
},
open: function allTabs_open() {
if (this.canOpen) {
// Without setTimeout, the menupopup won't stay open when invoking
// "View > Show All Tabs" and the menu bar auto-hides.
setTimeout(() => {
this.toolbarButton.open = true;
}, 0);
}
}
};

View file

@ -1,100 +0,0 @@
/* -*- 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/. */
/**
* Customization handler prepares this browser window for entering and exiting
* customization mode by handling customizationstarting and customizationending
* events.
*/
var CustomizationHandler = {
handleEvent: function(aEvent) {
switch (aEvent.type) {
case "customizationstarting":
this._customizationStarting();
break;
case "customizationchange":
this._customizationChange();
break;
case "customizationending":
this._customizationEnding(aEvent.detail);
break;
}
},
isCustomizing: function() {
return document.documentElement.hasAttribute("customizing");
},
_customizationStarting: function() {
// Disable the toolbar context menu items
let menubar = document.getElementById("main-menubar");
for (let childNode of menubar.childNodes)
childNode.setAttribute("disabled", true);
let cmd = document.getElementById("cmd_CustomizeToolbars");
cmd.setAttribute("disabled", "true");
UpdateUrlbarSearchSplitterState();
CombinedStopReload.uninit();
PlacesToolbarHelper.customizeStart();
DownloadsButton.customizeStart();
// The additional padding on the sides of the browser
// can cause the customize tab to get clipped.
let tabContainer = gBrowser.tabContainer;
if (tabContainer.getAttribute("overflow") == "true") {
let tabstrip = tabContainer.mTabstrip;
tabstrip.ensureElementIsVisible(gBrowser.selectedTab, true);
}
},
_customizationChange: function() {
PlacesToolbarHelper.customizeChange();
},
_customizationEnding: function(aDetails) {
// Update global UI elements that may have been added or removed
if (aDetails.changed) {
gURLBar = document.getElementById("urlbar");
gHomeButton.updateTooltip();
XULBrowserWindow.init();
if (AppConstants.platform != "macosx")
updateEditUIVisibility();
// Hacky: update the PopupNotifications' object's reference to the iconBox,
// if it already exists, since it may have changed if the URL bar was
// added/removed.
if (!window.__lookupGetter__("PopupNotifications")) {
PopupNotifications.iconBox =
document.getElementById("notification-popup-box");
}
}
PlacesToolbarHelper.customizeDone();
DownloadsButton.customizeDone();
// The url bar splitter state is dependent on whether stop/reload
// and the location bar are combined, so we need this ordering
CombinedStopReload.init();
UpdateUrlbarSearchSplitterState();
// Update the urlbar
URLBarSetURI();
XULBrowserWindow.asyncUpdateUI();
// Re-enable parts of the UI we disabled during the dialog
let menubar = document.getElementById("main-menubar");
for (let childNode of menubar.childNodes)
childNode.setAttribute("disabled", false);
let cmd = document.getElementById("cmd_CustomizeToolbars");
cmd.removeAttribute("disabled");
gBrowser.selectedBrowser.focus();
}
}

View file

@ -1,127 +0,0 @@
/* 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/. */
const LOGGER_NAME = "Toolkit.Telemetry";
const LOGGER_PREFIX = "DataNotificationInfoBar::";
/**
* Represents an info bar that shows a data submission notification.
*/
var gDataNotificationInfoBar = {
_OBSERVERS: [
"datareporting:notify-data-policy:request",
"datareporting:notify-data-policy:close",
],
_DATA_REPORTING_NOTIFICATION: "data-reporting",
get _notificationBox() {
delete this._notificationBox;
return this._notificationBox = document.getElementById("global-notificationbox");
},
get _log() {
let Log = Cu.import("resource://gre/modules/Log.jsm", {}).Log;
delete this._log;
return this._log = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
},
init: function() {
window.addEventListener("unload", () => {
for (let o of this._OBSERVERS) {
Services.obs.removeObserver(this, o);
}
}, false);
for (let o of this._OBSERVERS) {
Services.obs.addObserver(this, o, true);
}
},
_getDataReportingNotification: function (name=this._DATA_REPORTING_NOTIFICATION) {
return this._notificationBox.getNotificationWithValue(name);
},
_displayDataPolicyInfoBar: function (request) {
if (this._getDataReportingNotification()) {
return;
}
let brandBundle = document.getElementById("bundle_brand");
let appName = brandBundle.getString("brandShortName");
let vendorName = brandBundle.getString("vendorShortName");
let message = gNavigatorBundle.getFormattedString(
"dataReportingNotification.message",
[appName, vendorName]);
this._actionTaken = false;
let buttons = [{
label: gNavigatorBundle.getString("dataReportingNotification.button.label"),
accessKey: gNavigatorBundle.getString("dataReportingNotification.button.accessKey"),
popup: null,
callback: () => {
this._actionTaken = true;
window.openAdvancedPreferences("dataChoicesTab");
},
}];
this._log.info("Creating data reporting policy notification.");
this._notificationBox.appendNotification(
message,
this._DATA_REPORTING_NOTIFICATION,
null,
this._notificationBox.PRIORITY_INFO_HIGH,
buttons,
event => {
if (event == "removed") {
Services.obs.notifyObservers(null, "datareporting:notify-data-policy:close", null);
}
}
);
// It is important to defer calling onUserNotifyComplete() until we're
// actually sure the notification was displayed. If we ever called
// onUserNotifyComplete() without showing anything to the user, that
// would be very good for user choice. It may also have legal impact.
request.onUserNotifyComplete();
},
_clearPolicyNotification: function () {
let notification = this._getDataReportingNotification();
if (notification) {
this._log.debug("Closing notification.");
notification.close();
}
},
observe: function(subject, topic, data) {
switch (topic) {
case "datareporting:notify-data-policy:request":
let request = subject.wrappedJSObject.object;
try {
this._displayDataPolicyInfoBar(request);
} catch (ex) {
request.onUserNotifyFailed(ex);
return;
}
break;
case "datareporting:notify-data-policy:close":
// If this observer fires, it means something else took care of
// responding. Therefore, we don't need to do anything. So, we
// act like we took action and clear state.
this._actionTaken = true;
this._clearPolicyNotification();
break;
default:
}
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIObserver,
Ci.nsISupportsWeakReference,
]),
};

View file

@ -1,142 +0,0 @@
/* 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/. */
/**
* Listeners for the DevEdition theme. This adds an extra stylesheet
* to browser.xul if a pref is set and no other themes are applied.
*/
var DevEdition = {
_devtoolsThemePrefName: "devtools.theme",
styleSheetLocation: "chrome://browser/skin/devedition.css",
styleSheet: null,
initialized: false,
get isStyleSheetEnabled() {
return this.styleSheet && !this.styleSheet.sheet.disabled;
},
get isThemeCurrentlyApplied() {
let theme = LightweightThemeManager.currentTheme;
return theme && theme.id == "firefox-devedition@mozilla.org";
},
init: function () {
this.initialized = true;
Services.prefs.addObserver(this._devtoolsThemePrefName, this, false);
Services.obs.addObserver(this, "lightweight-theme-styling-update", false);
Services.obs.addObserver(this, "lightweight-theme-window-updated", false);
this._updateDevtoolsThemeAttribute();
if (this.isThemeCurrentlyApplied) {
this._toggleStyleSheet(true);
}
},
createStyleSheet: function() {
let styleSheetAttr = `href="${this.styleSheetLocation}" type="text/css"`;
this.styleSheet = document.createProcessingInstruction(
"xml-stylesheet", styleSheetAttr);
this.styleSheet.addEventListener("load", this);
document.insertBefore(this.styleSheet, document.documentElement);
this.styleSheet.sheet.disabled = true;
},
observe: function (subject, topic, data) {
if (topic == "lightweight-theme-styling-update") {
let newTheme = JSON.parse(data);
if (newTheme && newTheme.id == "firefox-devedition@mozilla.org") {
this._toggleStyleSheet(true);
} else {
this._toggleStyleSheet(false);
}
} else if (topic == "lightweight-theme-window-updated" && subject == window) {
this._updateLWTBrightness();
}
if (topic == "nsPref:changed" && data == this._devtoolsThemePrefName) {
this._updateDevtoolsThemeAttribute();
}
},
_inferBrightness: function() {
ToolbarIconColor.inferFromText();
// Get an inverted full screen button if the dark theme is applied.
if (this.isStyleSheetEnabled &&
document.documentElement.getAttribute("devtoolstheme") == "dark") {
document.documentElement.setAttribute("brighttitlebarforeground", "true");
} else {
document.documentElement.removeAttribute("brighttitlebarforeground");
}
},
_updateLWTBrightness() {
if (this.isThemeCurrentlyApplied) {
let devtoolsTheme = Services.prefs.getCharPref(this._devtoolsThemePrefName);
let textColor = devtoolsTheme == "dark" ? "bright" : "dark";
document.documentElement.setAttribute("lwthemetextcolor", textColor);
}
},
_updateDevtoolsThemeAttribute: function() {
// Set an attribute on root element to make it possible
// to change colors based on the selected devtools theme.
let devtoolsTheme = Services.prefs.getCharPref(this._devtoolsThemePrefName);
if (devtoolsTheme != "dark") {
devtoolsTheme = "light";
}
document.documentElement.setAttribute("devtoolstheme", devtoolsTheme);
this._updateLWTBrightness();
this._inferBrightness();
},
handleEvent: function(e) {
if (e.type === "load") {
this.styleSheet.removeEventListener("load", this);
this.refreshBrowserDisplay();
}
},
refreshBrowserDisplay: function() {
// Don't touch things on the browser if gBrowserInit.onLoad hasn't
// yet fired.
if (this.initialized) {
gBrowser.tabContainer._positionPinnedTabs();
this._inferBrightness();
}
},
_toggleStyleSheet: function(deveditionThemeEnabled) {
let wasEnabled = this.isStyleSheetEnabled;
if (deveditionThemeEnabled && !wasEnabled) {
// The stylesheet may not have been created yet if it wasn't
// needed on initial load. Make it now.
if (!this.styleSheet) {
this.createStyleSheet();
}
this.styleSheet.sheet.disabled = false;
this.refreshBrowserDisplay();
} else if (!deveditionThemeEnabled && wasEnabled) {
this.styleSheet.sheet.disabled = true;
this.refreshBrowserDisplay();
}
},
uninit: function () {
Services.prefs.removeObserver(this._devtoolsThemePrefName, this);
Services.obs.removeObserver(this, "lightweight-theme-styling-update", false);
Services.obs.removeObserver(this, "lightweight-theme-window-updated", false);
if (this.styleSheet) {
this.styleSheet.removeEventListener("load", this);
}
this.styleSheet = null;
}
};
// If the DevEdition theme is going to be applied in gBrowserInit.onLoad,
// then preload it now. This prevents a flash of unstyled content where the
// normal theme is applied while the DevEdition stylesheet is loading.
if (!AppConstants.RELEASE_OR_BETA &&
this != Services.appShell.hiddenDOMWindow && DevEdition.isThemeCurrentlyApplied) {
DevEdition.createStyleSheet();
}

View file

@ -1,25 +0,0 @@
<!DOCTYPE window [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" >
%browserDTD;
<!ENTITY % baseMenuDTD SYSTEM "chrome://browser/locale/baseMenuOverlay.dtd" >
%baseMenuDTD;
<!ENTITY % charsetDTD SYSTEM "chrome://global/locale/charsetMenu.dtd" >
%charsetDTD;
<!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd" >
%textcontextDTD;
<!ENTITY % customizeToolbarDTD SYSTEM "chrome://global/locale/customizeToolbar.dtd">
%customizeToolbarDTD;
<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
%placesDTD;
#ifdef MOZ_SAFE_BROWSING
<!ENTITY % safebrowsingDTD SYSTEM "chrome://browser/locale/safebrowsing/phishing-afterload-warning-message.dtd">
%safebrowsingDTD;
#endif
<!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
%aboutHomeDTD;
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
%syncBrandDTD;
]>

View file

@ -1,646 +0,0 @@
/* -*- 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/. */
XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
"resource://gre/modules/DeferredTask.jsm");
const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
const PREF_SHOW_FIRST_RUN_UI = "browser.feeds.showFirstRunUI";
const PREF_SELECTED_APP = "browser.feeds.handlers.application";
const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
const PREF_SELECTED_ACTION = "browser.feeds.handler";
const PREF_SELECTED_READER = "browser.feeds.handler.default";
const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";
const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";
const PREF_UPDATE_DELAY = 2000;
const SETTABLE_PREFS = new Set([
PREF_VIDEO_SELECTED_ACTION,
PREF_AUDIO_SELECTED_ACTION,
PREF_SELECTED_ACTION,
PREF_VIDEO_SELECTED_READER,
PREF_AUDIO_SELECTED_READER,
PREF_SELECTED_READER,
PREF_VIDEO_SELECTED_WEB,
PREF_AUDIO_SELECTED_WEB,
PREF_SELECTED_WEB
]);
const EXECUTABLE_PREFS = new Set([
PREF_SELECTED_APP,
PREF_VIDEO_SELECTED_APP,
PREF_AUDIO_SELECTED_APP
]);
const VALID_ACTIONS = new Set(["ask", "reader", "bookmarks"]);
const VALID_READERS = new Set(["web", "client", "default", "bookmarks"]);
XPCOMUtils.defineLazyPreferenceGetter(this, "SHOULD_LOG",
"feeds.log", false);
function LOG(str) {
if (SHOULD_LOG)
dump("*** Feeds: " + str + "\n");
}
function getPrefActionForType(t) {
switch (t) {
case Ci.nsIFeed.TYPE_VIDEO:
return PREF_VIDEO_SELECTED_ACTION;
case Ci.nsIFeed.TYPE_AUDIO:
return PREF_AUDIO_SELECTED_ACTION;
default:
return PREF_SELECTED_ACTION;
}
}
function getPrefReaderForType(t) {
switch (t) {
case Ci.nsIFeed.TYPE_VIDEO:
return PREF_VIDEO_SELECTED_READER;
case Ci.nsIFeed.TYPE_AUDIO:
return PREF_AUDIO_SELECTED_READER;
default:
return PREF_SELECTED_READER;
}
}
function getPrefWebForType(t) {
switch (t) {
case Ci.nsIFeed.TYPE_VIDEO:
return PREF_VIDEO_SELECTED_WEB;
case Ci.nsIFeed.TYPE_AUDIO:
return PREF_AUDIO_SELECTED_WEB;
default:
return PREF_SELECTED_WEB;
}
}
function getPrefAppForType(t) {
switch (t) {
case Ci.nsIFeed.TYPE_VIDEO:
return PREF_VIDEO_SELECTED_APP;
case Ci.nsIFeed.TYPE_AUDIO:
return PREF_AUDIO_SELECTED_APP;
default:
return PREF_SELECTED_APP;
}
}
/**
* Maps a feed type to a maybe-feed mimetype.
*/
function getMimeTypeForFeedType(aFeedType) {
switch (aFeedType) {
case Ci.nsIFeed.TYPE_VIDEO:
return TYPE_MAYBE_VIDEO_FEED;
case Ci.nsIFeed.TYPE_AUDIO:
return TYPE_MAYBE_AUDIO_FEED;
default:
return TYPE_MAYBE_FEED;
}
}
/**
* The Feed Handler object manages discovery of RSS/ATOM feeds in web pages
* and shows UI when they are discovered.
*/
var FeedHandler = {
_prefChangeCallback: null,
/** Called when the user clicks on the Subscribe to This Page... menu item,
* or when the user clicks the feed button when the page contains multiple
* feeds.
* Builds a menu of unique feeds associated with the page, and if there
* is only one, shows the feed inline in the browser window.
* @param container
* The feed list container (menupopup or subview) to be populated.
* @param isSubview
* Whether we're creating a subview (true) or menu (false/undefined)
* @return true if the menu/subview should be shown, false if there was only
* one feed and the feed should be shown inline in the browser
* window (do not show the menupopup/subview).
*/
buildFeedList(container, isSubview) {
let feeds = gBrowser.selectedBrowser.feeds;
if (!isSubview && feeds == null) {
// XXX hack -- menu opening depends on setting of an "open"
// attribute, and the menu refuses to open if that attribute is
// set (because it thinks it's already open). onpopupshowing gets
// called after the attribute is unset, and it doesn't get unset
// if we return false. so we unset it here; otherwise, the menu
// refuses to work past this point.
container.parentNode.removeAttribute("open");
return false;
}
for (let i = container.childNodes.length - 1; i >= 0; --i) {
let node = container.childNodes[i];
if (isSubview && node.localName == "label")
continue;
container.removeChild(node);
}
if (!feeds || feeds.length <= 1)
return false;
// Build the menu showing the available feed choices for viewing.
let itemNodeType = isSubview ? "toolbarbutton" : "menuitem";
for (let feedInfo of feeds) {
let item = document.createElement(itemNodeType);
let baseTitle = feedInfo.title || feedInfo.href;
item.setAttribute("label", baseTitle);
item.setAttribute("feed", feedInfo.href);
item.setAttribute("tooltiptext", feedInfo.href);
item.setAttribute("crop", "center");
let className = "feed-" + itemNodeType;
if (isSubview) {
className += " subviewbutton";
}
item.setAttribute("class", className);
container.appendChild(item);
}
return true;
},
/**
* Subscribe to a given feed. Called when
* 1. Page has a single feed and user clicks feed icon in location bar
* 2. Page has a single feed and user selects Subscribe menu item
* 3. Page has multiple feeds and user selects from feed icon popup (or subview)
* 4. Page has multiple feeds and user selects from Subscribe submenu
* @param href
* The feed to subscribe to. May be null, in which case the
* event target's feed attribute is examined.
* @param event
* The event this method is handling. Used to decide where
* to open the preview UI. (Optional, unless href is null)
*/
subscribeToFeed(href, event) {
// Just load the feed in the content area to either subscribe or show the
// preview UI
if (!href)
href = event.target.getAttribute("feed");
urlSecurityCheck(href, gBrowser.contentPrincipal,
Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
let feedURI = makeURI(href, document.characterSet);
// Use the feed scheme so X-Moz-Is-Feed will be set
// The value doesn't matter
if (/^https?$/.test(feedURI.scheme))
href = "feed:" + href;
this.loadFeed(href, event);
},
loadFeed(href, event) {
let feeds = gBrowser.selectedBrowser.feeds;
try {
openUILink(href, event, { ignoreAlt: true });
}
finally {
// We might default to a livebookmarks modal dialog,
// so reset that if the user happens to click it again
gBrowser.selectedBrowser.feeds = feeds;
}
},
get _feedMenuitem() {
delete this._feedMenuitem;
return this._feedMenuitem = document.getElementById("singleFeedMenuitemState");
},
get _feedMenupopup() {
delete this._feedMenupopup;
return this._feedMenupopup = document.getElementById("multipleFeedsMenuState");
},
/**
* Update the browser UI to show whether or not feeds are available when
* a page is loaded or the user switches tabs to a page that has feeds.
*/
updateFeeds() {
if (this._updateFeedTimeout)
clearTimeout(this._updateFeedTimeout);
let feeds = gBrowser.selectedBrowser.feeds;
let haveFeeds = feeds && feeds.length > 0;
let feedButton = document.getElementById("feed-button");
if (feedButton) {
if (haveFeeds) {
feedButton.removeAttribute("disabled");
} else {
feedButton.setAttribute("disabled", "true");
}
}
if (!haveFeeds) {
this._feedMenuitem.setAttribute("disabled", "true");
this._feedMenuitem.removeAttribute("hidden");
this._feedMenupopup.setAttribute("hidden", "true");
return;
}
if (feeds.length > 1) {
this._feedMenuitem.setAttribute("hidden", "true");
this._feedMenupopup.removeAttribute("hidden");
} else {
this._feedMenuitem.setAttribute("feed", feeds[0].href);
this._feedMenuitem.removeAttribute("disabled");
this._feedMenuitem.removeAttribute("hidden");
this._feedMenupopup.setAttribute("hidden", "true");
}
},
addFeed(link, browserForLink) {
if (!browserForLink.feeds)
browserForLink.feeds = [];
browserForLink.feeds.push({ href: link.href, title: link.title });
// If this addition was for the current browser, update the UI. For
// background browsers, we'll update on tab switch.
if (browserForLink == gBrowser.selectedBrowser) {
// Batch updates to avoid updating the UI for multiple onLinkAdded events
// fired within 100ms of each other.
if (this._updateFeedTimeout)
clearTimeout(this._updateFeedTimeout);
this._updateFeedTimeout = setTimeout(this.updateFeeds.bind(this), 100);
}
},
/**
* Get the human-readable display name of a file. This could be the
* application name.
* @param file
* A nsIFile to look up the name of
* @return The display name of the application represented by the file.
*/
_getFileDisplayName(file) {
switch (AppConstants.platform) {
case "win":
if (file instanceof Ci.nsILocalFileWin) {
try {
return file.getVersionInfoField("FileDescription");
} catch (e) {}
}
break;
case "macosx":
if (file instanceof Ci.nsILocalFileMac) {
try {
return file.bundleDisplayName;
} catch (e) {}
}
break;
}
return file.leafName;
},
_chooseClientApp(aTitle, aTypeName, aBrowser) {
const prefName = getPrefAppForType(aTypeName);
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
fp.init(window, aTitle, Ci.nsIFilePicker.modeOpen);
fp.appendFilters(Ci.nsIFilePicker.filterApps);
fp.open((aResult) => {
if (aResult == Ci.nsIFilePicker.returnOK) {
let selectedApp = fp.file;
if (selectedApp) {
// XXXben - we need to compare this with the running instance
// executable just don't know how to do that via script
// XXXmano TBD: can probably add this to nsIShellService
let appName = "";
switch (AppConstants.platform) {
case "win":
appName = AppConstants.MOZ_APP_NAME + ".exe";
break;
case "macosx":
appName = AppConstants.MOZ_MACBUNDLE_NAME;
break;
default:
appName = AppConstants.MOZ_APP_NAME + "-bin";
break;
}
if (fp.file.leafName != appName) {
Services.prefs.setComplexValue(prefName, Ci.nsILocalFile, selectedApp);
aBrowser.messageManager.sendAsyncMessage("FeedWriter:SetApplicationLauncherMenuItem",
{ name: this._getFileDisplayName(selectedApp),
type: "SelectedAppMenuItem" });
}
}
}
});
},
executeClientApp(aSpec, aTitle, aSubtitle, aFeedHandler) {
// aFeedHandler is either "default", indicating the system default reader, or a pref-name containing
// an nsILocalFile pointing to the feed handler's executable.
let clientApp = null;
if (aFeedHandler == "default") {
clientApp = Cc["@mozilla.org/browser/shell-service;1"]
.getService(Ci.nsIShellService)
.defaultFeedReader;
} else {
clientApp = Services.prefs.getComplexValue(aFeedHandler, Ci.nsILocalFile);
}
// For the benefit of applications that might know how to deal with more
// URLs than just feeds, send feed: URLs in the following format:
//
// http urls: replace scheme with feed, e.g.
// http://foo.com/index.rdf -> feed://foo.com/index.rdf
// other urls: prepend feed: scheme, e.g.
// https://foo.com/index.rdf -> feed:https://foo.com/index.rdf
let feedURI = NetUtil.newURI(aSpec);
if (feedURI.schemeIs("http")) {
feedURI.scheme = "feed";
aSpec = feedURI.spec;
} else {
aSpec = "feed:" + aSpec;
}
// Retrieving the shell service might fail on some systems, most
// notably systems where GNOME is not installed.
try {
let ss = Cc["@mozilla.org/browser/shell-service;1"]
.getService(Ci.nsIShellService);
ss.openApplicationWithURI(clientApp, aSpec);
} catch (e) {
// If we couldn't use the shell service, fallback to using a
// nsIProcess instance
let p = Cc["@mozilla.org/process/util;1"]
.createInstance(Ci.nsIProcess);
p.init(clientApp);
p.run(false, [aSpec], 1);
}
},
// nsISupports
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference]),
init() {
window.messageManager.addMessageListener("FeedWriter:ChooseClientApp", this);
window.messageManager.addMessageListener("FeedWriter:GetSubscriptionUI", this);
window.messageManager.addMessageListener("FeedWriter:SetFeedPrefsAndSubscribe", this);
window.messageManager.addMessageListener("FeedWriter:ShownFirstRun", this);
Services.ppmm.addMessageListener("FeedConverter:ExecuteClientApp", this);
const prefs = Services.prefs;
prefs.addObserver(PREF_SELECTED_ACTION, this, true);
prefs.addObserver(PREF_SELECTED_READER, this, true);
prefs.addObserver(PREF_SELECTED_WEB, this, true);
prefs.addObserver(PREF_VIDEO_SELECTED_ACTION, this, true);
prefs.addObserver(PREF_VIDEO_SELECTED_READER, this, true);
prefs.addObserver(PREF_VIDEO_SELECTED_WEB, this, true);
prefs.addObserver(PREF_AUDIO_SELECTED_ACTION, this, true);
prefs.addObserver(PREF_AUDIO_SELECTED_READER, this, true);
prefs.addObserver(PREF_AUDIO_SELECTED_WEB, this, true);
},
uninit() {
Services.ppmm.removeMessageListener("FeedConverter:ExecuteClientApp", this);
this._prefChangeCallback = null;
},
// nsIObserver
observe(subject, topic, data) {
if (topic == "nsPref:changed") {
LOG(`Pref changed ${data}`)
if (this._prefChangeCallback) {
this._prefChangeCallback.disarm();
}
// Multiple prefs are set at the same time, debounce to reduce noise
// This can happen in one feed and we want to message all feed pages
this._prefChangeCallback = new DeferredTask(() => {
this._prefChanged(data);
}, PREF_UPDATE_DELAY);
this._prefChangeCallback.arm();
}
},
_prefChanged(prefName) {
// Don't observe for PREF_*SELECTED_APP as user likely just picked one
// That is also handled by SetApplicationLauncherMenuItem call
// Rather than the others which happen on subscription
switch (prefName) {
case PREF_SELECTED_READER:
case PREF_SELECTED_WEB:
case PREF_VIDEO_SELECTED_READER:
case PREF_VIDEO_SELECTED_WEB:
case PREF_AUDIO_SELECTED_READER:
case PREF_AUDIO_SELECTED_WEB:
case PREF_SELECTED_ACTION:
case PREF_VIDEO_SELECTED_ACTION:
case PREF_AUDIO_SELECTED_ACTION:
const response = {
default: this._getReaderForType(Ci.nsIFeed.TYPE_FEED),
[Ci.nsIFeed.TYPE_AUDIO]: this._getReaderForType(Ci.nsIFeed.TYPE_AUDIO),
[Ci.nsIFeed.TYPE_VIDEO]: this._getReaderForType(Ci.nsIFeed.TYPE_VIDEO)
};
Services.mm.broadcastAsyncMessage("FeedWriter:PreferenceUpdated",
response);
break;
}
},
_initSubscriptionUIResponse(feedType) {
const wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
getService(Ci.nsIWebContentConverterService);
const handlersRaw = wccr.getContentHandlers(getMimeTypeForFeedType(feedType));
const handlers = [];
for (let handler of handlersRaw) {
LOG(`Handler found: ${handler}`);
handlers.push({
name: handler.name,
uri: handler.uri
});
}
let showFirstRunUI = true;
// eslint-disable-next-line mozilla/use-default-preference-values
try {
showFirstRunUI = Services.prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI);
} catch (ex) { }
const response = { handlers, showFirstRunUI };
let selectedClientApp;
const feedTypePref = getPrefAppForType(feedType);
try {
selectedClientApp = Services.prefs.getComplexValue(feedTypePref, Ci.nsILocalFile);
} catch (ex) {
// Just do nothing, then we won't bother populating
}
let defaultClientApp = null;
try {
// This can sometimes not exist
defaultClientApp = Cc["@mozilla.org/browser/shell-service;1"]
.getService(Ci.nsIShellService)
.defaultFeedReader;
} catch (ex) {
// Just do nothing, then we don't bother populating
}
if (selectedClientApp && selectedClientApp.exists()) {
if (defaultClientApp && selectedClientApp.path != defaultClientApp.path) {
// Only set the default menu item if it differs from the selected one
response.defaultMenuItem = this._getFileDisplayName(defaultClientApp);
}
response.selectedMenuItem = this._getFileDisplayName(selectedClientApp);
}
response.reader = this._getReaderForType(feedType);
return response;
},
_setPref(aPrefName, aPrefValue, aIsComplex = false) {
LOG(`FeedWriter._setPref ${aPrefName}`);
// Ensure we have a pref that is settable
if (aPrefName && SETTABLE_PREFS.has(aPrefName)) {
if (aIsComplex) {
const supportsString = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
supportsString.data = aPrefValue;
Services.prefs.setComplexValue(aPrefName, Ci.nsISupportsString, supportsString);
} else {
Services.prefs.setCharPref(aPrefName, aPrefValue);
}
} else {
LOG(`FeedWriter._setPref ${aPrefName} not allowed`);
}
},
_getReaderForType(feedType) {
let prefs = Services.prefs;
let handler = "bookmarks";
let url;
// eslint-disable-next-line mozilla/use-default-preference-values
try {
handler = prefs.getCharPref(getPrefReaderForType(feedType));
} catch (ex) { }
if (handler === "web") {
try {
url = prefs.getComplexValue(getPrefWebForType(feedType), Ci.nsISupportsString).data;
} catch (ex) {
LOG("FeedWriter._setSelectedHandler: invalid or no handler in prefs");
url = null;
}
}
const alwaysUse = this._getAlwaysUseState(feedType);
const action = prefs.getCharPref(getPrefActionForType(feedType));
return { handler, url, alwaysUse, action };
},
_getAlwaysUseState(feedType) {
try {
return Services.prefs.getCharPref(getPrefActionForType(feedType)) != "ask";
} catch (ex) { }
return false;
},
receiveMessage(msg) {
let handler;
switch (msg.name) {
case "FeedWriter:GetSubscriptionUI":
const response = this._initSubscriptionUIResponse(msg.data.feedType);
msg.target.messageManager
.sendAsyncMessage("FeedWriter:GetSubscriptionUIResponse",
response);
break;
case "FeedWriter:ChooseClientApp":
this._chooseClientApp(msg.data.title, msg.data.feedType, msg.target);
break;
case "FeedWriter:ShownFirstRun":
Services.prefs.setBoolPref(PREF_SHOW_FIRST_RUN_UI, false);
break;
case "FeedWriter:SetFeedPrefsAndSubscribe":
const settings = msg.data;
if (!settings.action || !VALID_ACTIONS.has(settings.action)) {
LOG(`Invalid action ${settings.action}`);
return;
}
if (!settings.reader || !VALID_READERS.has(settings.reader)) {
LOG(`Invalid reader ${settings.reader}`);
return;
}
const actionPref = getPrefActionForType(settings.feedType);
this._setPref(actionPref, settings.action);
const readerPref = getPrefReaderForType(settings.feedType);
this._setPref(readerPref, settings.reader);
handler = null;
switch (settings.reader) {
case "web":
// This is a web set URI by content using window.registerContentHandler()
// Lets make sure we know about it before setting it
const webPref = getPrefWebForType(settings.feedType);
let wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
getService(Ci.nsIWebContentConverterService);
// If the user provided an invalid web URL this function won't give us a reference
handler = wccr.getWebContentHandlerByURI(getMimeTypeForFeedType(settings.feedType), settings.uri);
if (handler) {
this._setPref(webPref, settings.uri, true);
if (settings.useAsDefault) {
wccr.setAutoHandler(getMimeTypeForFeedType(settings.feedType), handler);
}
msg.target.messageManager
.sendAsyncMessage("FeedWriter:SetFeedPrefsAndSubscribeResponse",
{ redirect: handler.getHandlerURI(settings.feedLocation) });
} else {
LOG(`No handler found for web ${settings.feedType} ${settings.uri}`);
}
break;
default:
const feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
getService(Ci.nsIFeedResultService);
feedService.addToClientReader(settings.feedLocation,
settings.feedTitle,
settings.feedSubtitle,
settings.feedType,
settings.reader);
}
break;
case "FeedConverter:ExecuteClientApp":
// Always check feedHandler is from a set array of executable prefs
if (EXECUTABLE_PREFS.has(msg.data.feedHandler)) {
this.executeClientApp(msg.data.spec, msg.data.title,
msg.data.subtitle, msg.data.feedHandler);
} else {
LOG(`FeedConverter:ExecuteClientApp - Will not exec ${msg.data.feedHandler}`);
}
break;
}
},
};

View file

@ -1,673 +0,0 @@
/* -*- 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 PointerlockFsWarning = {
_element: null,
_origin: null,
init: function() {
this.Timeout.prototype = {
start: function() {
this.cancel();
this._id = setTimeout(() => this._handle(), this._delay);
},
cancel: function() {
if (this._id) {
clearTimeout(this._id);
this._id = 0;
}
},
_handle: function() {
this._id = 0;
this._func();
},
get delay() {
return this._delay;
}
};
},
/**
* Timeout object for managing timeout request. If it is started when
* the previous call hasn't finished, it would automatically cancelled
* the previous one.
*/
Timeout: function(func, delay) {
this._id = 0;
this._func = func;
this._delay = delay;
},
showPointerLock: function(aOrigin) {
if (!document.fullscreen) {
let timeout = gPrefService.getIntPref("pointer-lock-api.warning.timeout");
this.show(aOrigin, "pointerlock-warning", timeout, 0);
}
},
showFullScreen: function(aOrigin) {
let timeout = gPrefService.getIntPref("full-screen-api.warning.timeout");
let delay = gPrefService.getIntPref("full-screen-api.warning.delay");
this.show(aOrigin, "fullscreen-warning", timeout, delay);
},
// Shows a warning that the site has entered fullscreen or
// pointer lock for a short duration.
show: function(aOrigin, elementId, timeout, delay) {
if (!this._element) {
this._element = document.getElementById(elementId);
// Setup event listeners
this._element.addEventListener("transitionend", this);
window.addEventListener("mousemove", this, true);
// The timeout to hide the warning box after a while.
this._timeoutHide = new this.Timeout(() => {
this._state = "hidden";
}, timeout);
// The timeout to show the warning box when the pointer is at the top
this._timeoutShow = new this.Timeout(() => {
this._state = "ontop";
this._timeoutHide.start();
}, delay);
}
// Set the strings on the warning UI.
if (aOrigin) {
this._origin = aOrigin;
}
let uri = BrowserUtils.makeURI(this._origin);
let host = null;
try {
host = uri.host;
} catch (e) { }
let textElem = this._element.querySelector(".pointerlockfswarning-domain-text");
if (!host) {
textElem.setAttribute("hidden", true);
} else {
textElem.removeAttribute("hidden");
let hostElem = this._element.querySelector(".pointerlockfswarning-domain");
// Document's principal's URI has a host. Display a warning including it.
let utils = {};
Cu.import("resource://gre/modules/DownloadUtils.jsm", utils);
hostElem.textContent = utils.DownloadUtils.getURIHost(uri.spec)[0];
}
this._element.dataset.identity =
gIdentityHandler.pointerlockFsWarningClassName;
// User should be allowed to explicitly disable
// the prompt if they really want.
if (this._timeoutHide.delay <= 0) {
return;
}
// Explicitly set the last state to hidden to avoid the warning
// box being hidden immediately because of mousemove.
this._state = "onscreen";
this._lastState = "hidden";
this._timeoutHide.start();
},
close: function() {
if (!this._element) {
return;
}
// Cancel any pending timeout
this._timeoutHide.cancel();
this._timeoutShow.cancel();
// Reset state of the warning box
this._state = "hidden";
this._element.setAttribute("hidden", true);
// Remove all event listeners
this._element.removeEventListener("transitionend", this);
window.removeEventListener("mousemove", this, true);
// Clear fields
this._element = null;
this._timeoutHide = null;
this._timeoutShow = null;
// Ensure focus switches away from the (now hidden) warning box.
// If the user clicked buttons in the warning box, it would have
// been focused, and any key events would be directed at the (now
// hidden) chrome document instead of the target document.
gBrowser.selectedBrowser.focus();
},
// State could be one of "onscreen", "ontop", "hiding", and
// "hidden". Setting the state to "onscreen" and "ontop" takes
// effect immediately, while setting it to "hidden" actually
// turns the state to "hiding" before the transition finishes.
_lastState: null,
_STATES: ["hidden", "ontop", "onscreen"],
get _state() {
for (let state of this._STATES) {
if (this._element.hasAttribute(state)) {
return state;
}
}
return "hiding";
},
set _state(newState) {
let currentState = this._state;
if (currentState == newState) {
return;
}
if (currentState != "hiding") {
this._lastState = currentState;
this._element.removeAttribute(currentState);
}
if (newState != "hidden") {
if (currentState != "hidden") {
this._element.setAttribute(newState, true);
} else {
// When the previous state is hidden, the display was none,
// thus no box was constructed. We need to wait for the new
// display value taking effect first, otherwise, there won't
// be any transition. Since requestAnimationFrame callback is
// generally triggered before any style flush and layout, we
// should wait for the second animation frame.
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (this._element) {
this._element.setAttribute(newState, true);
}
});
});
}
}
},
handleEvent: function(event) {
switch (event.type) {
case "mousemove": {
let state = this._state;
if (state == "hidden") {
// If the warning box is currently hidden, show it after
// a short delay if the pointer is at the top.
if (event.clientY != 0) {
this._timeoutShow.cancel();
} else if (this._timeoutShow.delay >= 0) {
this._timeoutShow.start();
}
} else {
let elemRect = this._element.getBoundingClientRect();
if (state == "hiding" && this._lastState != "hidden") {
// If we are on the hiding transition, and the pointer
// moved near the box, restore to the previous state.
if (event.clientY <= elemRect.bottom + 50) {
this._state = this._lastState;
this._timeoutHide.start();
}
} else if (state == "ontop" || this._lastState != "hidden") {
// State being "ontop" or the previous state not being
// "hidden" indicates this current warning box is shown
// in response to user's action. Hide it immediately when
// the pointer leaves that area.
if (event.clientY > elemRect.bottom + 50) {
this._state = "hidden";
this._timeoutHide.cancel();
}
}
}
break;
}
case "transitionend": {
if (this._state == "hiding") {
this._element.setAttribute("hidden", true);
}
break;
}
}
}
};
var PointerLock = {
init: function() {
window.messageManager.addMessageListener("PointerLock:Entered", this);
window.messageManager.addMessageListener("PointerLock:Exited", this);
},
receiveMessage: function(aMessage) {
switch (aMessage.name) {
case "PointerLock:Entered": {
PointerlockFsWarning.showPointerLock(aMessage.data.originNoSuffix);
break;
}
case "PointerLock:Exited": {
PointerlockFsWarning.close();
break;
}
}
}
};
var FullScreen = {
_MESSAGES: [
"DOMFullscreen:Request",
"DOMFullscreen:NewOrigin",
"DOMFullscreen:Exit",
"DOMFullscreen:Painted",
],
init: function() {
// called when we go into full screen, even if initiated by a web page script
window.addEventListener("fullscreen", this, true);
window.addEventListener("MozDOMFullscreen:Entered", this,
/* useCapture */ true,
/* wantsUntrusted */ false);
window.addEventListener("MozDOMFullscreen:Exited", this,
/* useCapture */ true,
/* wantsUntrusted */ false);
for (let type of this._MESSAGES) {
window.messageManager.addMessageListener(type, this);
}
if (window.fullScreen)
this.toggle();
},
uninit: function() {
for (let type of this._MESSAGES) {
window.messageManager.removeMessageListener(type, this);
}
this.cleanup();
},
toggle: function () {
var enterFS = window.fullScreen;
// Toggle the View:FullScreen command, which controls elements like the
// fullscreen menuitem, and menubars.
let fullscreenCommand = document.getElementById("View:FullScreen");
if (enterFS) {
fullscreenCommand.setAttribute("checked", enterFS);
} else {
fullscreenCommand.removeAttribute("checked");
}
if (AppConstants.platform == "macosx") {
// Make sure the menu items are adjusted.
document.getElementById("enterFullScreenItem").hidden = enterFS;
document.getElementById("exitFullScreenItem").hidden = !enterFS;
}
if (!this._fullScrToggler) {
this._fullScrToggler = document.getElementById("fullscr-toggler");
this._fullScrToggler.addEventListener("mouseover", this._expandCallback, false);
this._fullScrToggler.addEventListener("dragenter", this._expandCallback, false);
this._fullScrToggler.addEventListener("touchmove", this._expandCallback, {passive: true});
}
if (enterFS) {
gNavToolbox.setAttribute("inFullscreen", true);
document.documentElement.setAttribute("inFullscreen", true);
if (!document.fullscreenElement && this.useLionFullScreen)
document.documentElement.setAttribute("OSXLionFullscreen", true);
} else {
gNavToolbox.removeAttribute("inFullscreen");
document.documentElement.removeAttribute("inFullscreen");
document.documentElement.removeAttribute("OSXLionFullscreen");
}
if (!document.fullscreenElement)
this._updateToolbars(enterFS);
if (enterFS) {
document.addEventListener("keypress", this._keyToggleCallback, false);
document.addEventListener("popupshown", this._setPopupOpen, false);
document.addEventListener("popuphidden", this._setPopupOpen, false);
// In DOM fullscreen mode, we hide toolbars with CSS
if (!document.fullscreenElement)
this.hideNavToolbox(true);
}
else {
this.showNavToolbox(false);
// This is needed if they use the context menu to quit fullscreen
this._isPopupOpen = false;
this.cleanup();
// In TabsInTitlebar._update(), we cancel the appearance update on
// resize event for exiting fullscreen, since that happens before we
// change the UI here in the "fullscreen" event. Hence we need to
// call it here to ensure the appearance is properly updated. See
// TabsInTitlebar._update() and bug 1173768.
TabsInTitlebar.updateAppearance(true);
}
if (enterFS && !document.fullscreenElement) {
Services.telemetry.getHistogramById("FX_BROWSER_FULLSCREEN_USED")
.add(1);
}
},
exitDomFullScreen : function() {
document.exitFullscreen();
},
handleEvent: function (event) {
switch (event.type) {
case "fullscreen":
this.toggle();
break;
case "MozDOMFullscreen:Entered": {
// The event target is the element which requested the DOM
// fullscreen. If we were entering DOM fullscreen for a remote
// browser, the target would be `gBrowser` and the original
// target would be the browser which was the parameter of
// `remoteFrameFullscreenChanged` call. If the fullscreen
// request was initiated from an in-process browser, we need
// to get its corresponding browser here.
let browser;
if (event.target == gBrowser) {
browser = event.originalTarget;
} else {
let topWin = event.target.ownerGlobal.top;
browser = gBrowser.getBrowserForContentWindow(topWin);
}
TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
this.enterDomFullscreen(browser);
break;
}
case "MozDOMFullscreen:Exited":
TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
this.cleanupDomFullscreen();
break;
}
},
receiveMessage: function(aMessage) {
let browser = aMessage.target;
switch (aMessage.name) {
case "DOMFullscreen:Request": {
this._windowUtils.remoteFrameFullscreenChanged(browser);
break;
}
case "DOMFullscreen:NewOrigin": {
// Don't show the warning if we've already exited fullscreen.
if (document.fullscreen) {
PointerlockFsWarning.showFullScreen(aMessage.data.originNoSuffix);
}
break;
}
case "DOMFullscreen:Exit": {
this._windowUtils.remoteFrameFullscreenReverted();
break;
}
case "DOMFullscreen:Painted": {
Services.obs.notifyObservers(window, "fullscreen-painted", "");
TelemetryStopwatch.finish("FULLSCREEN_CHANGE_MS");
break;
}
}
},
enterDomFullscreen : function(aBrowser) {
if (!document.fullscreenElement) {
return;
}
// If we have a current pointerlock warning shown then hide it
// before transition.
PointerlockFsWarning.close();
// If it is a remote browser, send a message to ask the content
// to enter fullscreen state. We don't need to do so if it is an
// in-process browser, since all related document should have
// entered fullscreen state at this point.
// This should be done before the active tab check below to ensure
// that the content document handles the pending request. Doing so
// before the check is fine since we also check the activeness of
// the requesting document in content-side handling code.
if (this._isRemoteBrowser(aBrowser)) {
aBrowser.messageManager.sendAsyncMessage("DOMFullscreen:Entered");
}
// If we've received a fullscreen notification, we have to ensure that the
// element that's requesting fullscreen belongs to the browser that's currently
// active. If not, we exit fullscreen since the "full-screen document" isn't
// actually visible now.
if (!aBrowser || gBrowser.selectedBrowser != aBrowser ||
// The top-level window has lost focus since the request to enter
// full-screen was made. Cancel full-screen.
Services.focus.activeWindow != window) {
// This function is called synchronously in fullscreen change, so
// we have to avoid calling exitFullscreen synchronously here.
setTimeout(() => document.exitFullscreen(), 0);
return;
}
document.documentElement.setAttribute("inDOMFullscreen", true);
if (gFindBarInitialized) {
gFindBar.close(true);
}
// Exit DOM full-screen mode upon open, close, or change tab.
gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
gBrowser.tabContainer.addEventListener("TabClose", this.exitDomFullScreen);
gBrowser.tabContainer.addEventListener("TabSelect", this.exitDomFullScreen);
// Add listener to detect when the fullscreen window is re-focused.
// If a fullscreen window loses focus, we show a warning when the
// fullscreen window is refocused.
window.addEventListener("activate", this);
},
cleanup: function () {
if (!window.fullScreen) {
MousePosTracker.removeListener(this);
document.removeEventListener("keypress", this._keyToggleCallback, false);
document.removeEventListener("popupshown", this._setPopupOpen, false);
document.removeEventListener("popuphidden", this._setPopupOpen, false);
}
},
cleanupDomFullscreen: function () {
window.messageManager
.broadcastAsyncMessage("DOMFullscreen:CleanUp");
PointerlockFsWarning.close();
gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen);
gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen);
gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
window.removeEventListener("activate", this);
document.documentElement.removeAttribute("inDOMFullscreen");
},
_isRemoteBrowser: function (aBrowser) {
return gMultiProcessBrowser && aBrowser.getAttribute("remote") == "true";
},
get _windowUtils() {
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
},
getMouseTargetRect: function()
{
return this._mouseTargetRect;
},
// Event callbacks
_expandCallback: function()
{
FullScreen.showNavToolbox();
},
onMouseEnter: function()
{
FullScreen.hideNavToolbox();
},
_keyToggleCallback: function(aEvent)
{
// if we can use the keyboard (eg Ctrl+L or Ctrl+E) to open the toolbars, we
// should provide a way to collapse them too.
if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
FullScreen.hideNavToolbox();
}
// F6 is another shortcut to the address bar, but its not covered in OpenLocation()
else if (aEvent.keyCode == aEvent.DOM_VK_F6)
FullScreen.showNavToolbox();
},
// Checks whether we are allowed to collapse the chrome
_isPopupOpen: false,
_isChromeCollapsed: false,
_safeToCollapse: function () {
if (!gPrefService.getBoolPref("browser.fullscreen.autohide"))
return false;
// a popup menu is open in chrome: don't collapse chrome
if (this._isPopupOpen)
return false;
// On OS X Lion we don't want to hide toolbars.
if (this.useLionFullScreen)
return false;
// a textbox in chrome is focused (location bar anyone?): don't collapse chrome
if (document.commandDispatcher.focusedElement &&
document.commandDispatcher.focusedElement.ownerDocument == document &&
document.commandDispatcher.focusedElement.localName == "input") {
return false;
}
return true;
},
_setPopupOpen: function(aEvent)
{
// Popups should only veto chrome collapsing if they were opened when the chrome was not collapsed.
// Otherwise, they would not affect chrome and the user would expect the chrome to go away.
// e.g. we wouldn't want the autoscroll icon firing this event, so when the user
// toggles chrome when moving mouse to the top, it doesn't go away again.
if (aEvent.type == "popupshown" && !FullScreen._isChromeCollapsed &&
aEvent.target.localName != "tooltip" && aEvent.target.localName != "window")
FullScreen._isPopupOpen = true;
else if (aEvent.type == "popuphidden" && aEvent.target.localName != "tooltip" &&
aEvent.target.localName != "window") {
FullScreen._isPopupOpen = false;
// Try again to hide toolbar when we close the popup.
FullScreen.hideNavToolbox(true);
}
},
// Autohide helpers for the context menu item
getAutohide: function(aItem)
{
aItem.setAttribute("checked", gPrefService.getBoolPref("browser.fullscreen.autohide"));
},
setAutohide: function()
{
gPrefService.setBoolPref("browser.fullscreen.autohide", !gPrefService.getBoolPref("browser.fullscreen.autohide"));
// Try again to hide toolbar when we change the pref.
FullScreen.hideNavToolbox(true);
},
showNavToolbox: function(trackMouse = true) {
this._fullScrToggler.hidden = true;
gNavToolbox.removeAttribute("fullscreenShouldAnimate");
gNavToolbox.style.marginTop = "";
if (!this._isChromeCollapsed) {
return;
}
// Track whether mouse is near the toolbox
if (trackMouse && !this.useLionFullScreen) {
let rect = gBrowser.mPanelContainer.getBoundingClientRect();
this._mouseTargetRect = {
top: rect.top + 50,
bottom: rect.bottom,
left: rect.left,
right: rect.right
};
MousePosTracker.addListener(this);
}
this._isChromeCollapsed = false;
},
hideNavToolbox: function (aAnimate = false) {
if (this._isChromeCollapsed || !this._safeToCollapse())
return;
this._fullScrToggler.hidden = false;
if (aAnimate && gPrefService.getBoolPref("browser.fullscreen.animate")) {
gNavToolbox.setAttribute("fullscreenShouldAnimate", true);
// Hide the fullscreen toggler until the transition ends.
let listener = () => {
gNavToolbox.removeEventListener("transitionend", listener, true);
if (this._isChromeCollapsed)
this._fullScrToggler.hidden = false;
};
gNavToolbox.addEventListener("transitionend", listener, true);
this._fullScrToggler.hidden = true;
}
gNavToolbox.style.marginTop =
-gNavToolbox.getBoundingClientRect().height + "px";
this._isChromeCollapsed = true;
MousePosTracker.removeListener(this);
},
_updateToolbars: function (aEnterFS) {
for (let el of document.querySelectorAll("toolbar[fullscreentoolbar=true]")) {
if (aEnterFS) {
// Give the main nav bar and the tab bar the fullscreen context menu,
// otherwise remove context menu to prevent breakage
el.setAttribute("saved-context", el.getAttribute("context"));
if (el.id == "nav-bar" || el.id == "TabsToolbar")
el.setAttribute("context", "autohide-context");
else
el.removeAttribute("context");
// Set the inFullscreen attribute to allow specific styling
// in fullscreen mode
el.setAttribute("inFullscreen", true);
} else {
if (el.hasAttribute("saved-context")) {
el.setAttribute("context", el.getAttribute("saved-context"));
el.removeAttribute("saved-context");
}
el.removeAttribute("inFullscreen");
}
}
ToolbarIconColor.inferFromText();
// For Lion fullscreen, all fullscreen controls are hidden, don't
// bother to touch them. If we don't stop here, the following code
// could cause the native fullscreen button be shown unexpectedly.
// See bug 1165570.
if (this.useLionFullScreen) {
return;
}
var fullscreenctls = document.getElementById("window-controls");
var navbar = document.getElementById("nav-bar");
var ctlsOnTabbar = window.toolbar.visible;
if (fullscreenctls.parentNode == navbar && ctlsOnTabbar) {
fullscreenctls.removeAttribute("flex");
document.getElementById("TabsToolbar").appendChild(fullscreenctls);
}
else if (fullscreenctls.parentNode.id == "TabsToolbar" && !ctlsOnTabbar) {
fullscreenctls.setAttribute("flex", "1");
navbar.appendChild(fullscreenctls);
}
fullscreenctls.hidden = !aEnterFS;
}
};
XPCOMUtils.defineLazyGetter(FullScreen, "useLionFullScreen", function() {
// We'll only use OS X Lion full screen if we're
// * on OS X
// * on Lion or higher (Darwin 11+)
// * have fullscreenbutton="true"
return AppConstants.isPlatformAndVersionAtLeast("macosx", 11) &&
document.documentElement.getAttribute("fullscreenbutton") == "true";
});

View file

@ -1,526 +0,0 @@
/* 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/. */
/**
* Controls the "full zoom" setting and its site-specific preferences.
*/
var FullZoom = {
// Identifies the setting in the content prefs database.
name: "browser.content.full-zoom",
// browser.zoom.siteSpecific preference cache
_siteSpecificPref: undefined,
// browser.zoom.updateBackgroundTabs preference cache
updateBackgroundTabs: undefined,
// This maps the browser to monotonically increasing integer
// tokens. _browserTokenMap[browser] is increased each time the zoom is
// changed in the browser. See _getBrowserToken and _ignorePendingZoomAccesses.
_browserTokenMap: new WeakMap(),
// Stores initial locations if we receive onLocationChange
// events before we're initialized.
_initialLocations: new WeakMap(),
get siteSpecific() {
return this._siteSpecificPref;
},
// nsISupports
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
Ci.nsIObserver,
Ci.nsIContentPrefObserver,
Ci.nsISupportsWeakReference,
Ci.nsISupports]),
// Initialization & Destruction
init: function FullZoom_init() {
gBrowser.addEventListener("ZoomChangeUsingMouseWheel", this);
// Register ourselves with the service so we know when our pref changes.
this._cps2 = Cc["@mozilla.org/content-pref/service;1"].
getService(Ci.nsIContentPrefService2);
this._cps2.addObserverForName(this.name, this);
this._siteSpecificPref =
gPrefService.getBoolPref("browser.zoom.siteSpecific");
this.updateBackgroundTabs =
gPrefService.getBoolPref("browser.zoom.updateBackgroundTabs");
// Listen for changes to the browser.zoom branch so we can enable/disable
// updating background tabs and per-site saving and restoring of zoom levels.
gPrefService.addObserver("browser.zoom.", this, true);
// If we received onLocationChange events for any of the current browsers
// before we were initialized we want to replay those upon initialization.
for (let browser of gBrowser.browsers) {
if (this._initialLocations.has(browser)) {
this.onLocationChange(...this._initialLocations.get(browser), browser);
}
}
// This should be nulled after initialization.
this._initialLocations = null;
},
destroy: function FullZoom_destroy() {
gPrefService.removeObserver("browser.zoom.", this);
this._cps2.removeObserverForName(this.name, this);
gBrowser.removeEventListener("ZoomChangeUsingMouseWheel", this);
},
// Event Handlers
// nsIDOMEventListener
handleEvent: function FullZoom_handleEvent(event) {
switch (event.type) {
case "ZoomChangeUsingMouseWheel":
let browser = this._getTargetedBrowser(event);
this._ignorePendingZoomAccesses(browser);
this._applyZoomToPref(browser);
break;
}
},
// nsIObserver
observe: function (aSubject, aTopic, aData) {
switch (aTopic) {
case "nsPref:changed":
switch (aData) {
case "browser.zoom.siteSpecific":
this._siteSpecificPref =
gPrefService.getBoolPref("browser.zoom.siteSpecific");
break;
case "browser.zoom.updateBackgroundTabs":
this.updateBackgroundTabs =
gPrefService.getBoolPref("browser.zoom.updateBackgroundTabs");
break;
}
break;
}
},
// nsIContentPrefObserver
onContentPrefSet: function FullZoom_onContentPrefSet(aGroup, aName, aValue, aIsPrivate) {
this._onContentPrefChanged(aGroup, aValue, aIsPrivate);
},
onContentPrefRemoved: function FullZoom_onContentPrefRemoved(aGroup, aName, aIsPrivate) {
this._onContentPrefChanged(aGroup, undefined, aIsPrivate);
},
/**
* Appropriately updates the zoom level after a content preference has
* changed.
*
* @param aGroup The group of the changed preference.
* @param aValue The new value of the changed preference. Pass undefined to
* indicate the preference's removal.
*/
_onContentPrefChanged: function FullZoom__onContentPrefChanged(aGroup, aValue, aIsPrivate) {
if (this._isNextContentPrefChangeInternal) {
// Ignore changes that FullZoom itself makes. This works because the
// content pref service calls callbacks before notifying observers, and it
// does both in the same turn of the event loop.
delete this._isNextContentPrefChangeInternal;
return;
}
let browser = gBrowser.selectedBrowser;
if (!browser.currentURI)
return;
let ctxt = this._loadContextFromBrowser(browser);
let domain = this._cps2.extractDomain(browser.currentURI.spec);
if (aGroup) {
if (aGroup == domain && ctxt.usePrivateBrowsing == aIsPrivate)
this._applyPrefToZoom(aValue, browser);
return;
}
this._globalValue = aValue === undefined ? aValue :
this._ensureValid(aValue);
// If the current page doesn't have a site-specific preference, then its
// zoom should be set to the new global preference now that the global
// preference has changed.
let hasPref = false;
let token = this._getBrowserToken(browser);
this._cps2.getByDomainAndName(browser.currentURI.spec, this.name, ctxt, {
handleResult: function () { hasPref = true; },
handleCompletion: function () {
if (!hasPref && token.isCurrent)
this._applyPrefToZoom(undefined, browser);
}.bind(this)
});
},
// location change observer
/**
* Called when the location of a tab changes.
* When that happens, we need to update the current zoom level if appropriate.
*
* @param aURI
* A URI object representing the new location.
* @param aIsTabSwitch
* Whether this location change has happened because of a tab switch.
* @param aBrowser
* (optional) browser object displaying the document
*/
onLocationChange: function FullZoom_onLocationChange(aURI, aIsTabSwitch, aBrowser) {
let browser = aBrowser || gBrowser.selectedBrowser;
// If we haven't been initialized yet but receive an onLocationChange
// notification then let's store and replay it upon initialization.
if (this._initialLocations) {
this._initialLocations.set(browser, [aURI, aIsTabSwitch]);
return;
}
// Ignore all pending async zoom accesses in the browser. Pending accesses
// that started before the location change will be prevented from applying
// to the new location.
this._ignorePendingZoomAccesses(browser);
if (!aURI || (aIsTabSwitch && !this.siteSpecific)) {
this._notifyOnLocationChange(browser);
return;
}
// Avoid the cps roundtrip and apply the default/global pref.
if (aURI.spec == "about:blank") {
this._applyPrefToZoom(undefined, browser,
this._notifyOnLocationChange.bind(this, browser));
return;
}
// Media documents should always start at 1, and are not affected by prefs.
if (!aIsTabSwitch && browser.isSyntheticDocument) {
ZoomManager.setZoomForBrowser(browser, 1);
// _ignorePendingZoomAccesses already called above, so no need here.
this._notifyOnLocationChange(browser);
return;
}
// See if the zoom pref is cached.
let ctxt = this._loadContextFromBrowser(browser);
let pref = this._cps2.getCachedByDomainAndName(aURI.spec, this.name, ctxt);
if (pref) {
this._applyPrefToZoom(pref.value, browser,
this._notifyOnLocationChange.bind(this, browser));
return;
}
// It's not cached, so we have to asynchronously fetch it.
let value = undefined;
let token = this._getBrowserToken(browser);
this._cps2.getByDomainAndName(aURI.spec, this.name, ctxt, {
handleResult: function (resultPref) { value = resultPref.value; },
handleCompletion: function () {
if (!token.isCurrent) {
this._notifyOnLocationChange(browser);
return;
}
this._applyPrefToZoom(value, browser,
this._notifyOnLocationChange.bind(this, browser));
}.bind(this)
});
},
// update state of zoom type menu item
updateMenu: function FullZoom_updateMenu() {
var menuItem = document.getElementById("toggle_zoom");
menuItem.setAttribute("checked", !ZoomManager.useFullZoom);
},
// Setting & Pref Manipulation
/**
* Reduces the zoom level of the page in the current browser.
*/
reduce: function FullZoom_reduce() {
ZoomManager.reduce();
let browser = gBrowser.selectedBrowser;
this._ignorePendingZoomAccesses(browser);
this._applyZoomToPref(browser);
},
/**
* Enlarges the zoom level of the page in the current browser.
*/
enlarge: function FullZoom_enlarge() {
ZoomManager.enlarge();
let browser = gBrowser.selectedBrowser;
this._ignorePendingZoomAccesses(browser);
this._applyZoomToPref(browser);
},
/**
* Sets the zoom level for the given browser to the given floating
* point value, where 1 is the default zoom level.
*/
setZoom: function (value, browser = gBrowser.selectedBrowser) {
ZoomManager.setZoomForBrowser(browser, value);
this._ignorePendingZoomAccesses(browser);
this._applyZoomToPref(browser);
},
/**
* Sets the zoom level of the page in the given browser to the global zoom
* level.
*
* @return A promise which resolves when the zoom reset has been applied.
*/
reset: function FullZoom_reset(browser = gBrowser.selectedBrowser) {
let token = this._getBrowserToken(browser);
let result = this._getGlobalValue(browser).then(value => {
if (token.isCurrent) {
ZoomManager.setZoomForBrowser(browser, value === undefined ? 1 : value);
this._ignorePendingZoomAccesses(browser);
Services.obs.notifyObservers(browser, "browser-fullZoom:zoomReset", "");
}
});
this._removePref(browser);
return result;
},
/**
* Set the zoom level for a given browser.
*
* Per nsPresContext::setFullZoom, we can set the zoom to its current value
* without significant impact on performance, as the setting is only applied
* if it differs from the current setting. In fact getting the zoom and then
* checking ourselves if it differs costs more.
*
* And perhaps we should always set the zoom even if it was more expensive,
* since nsDocumentViewer::SetTextZoom claims that child documents can have
* a different text zoom (although it would be unusual), and it implies that
* those child text zooms should get updated when the parent zoom gets set,
* and perhaps the same is true for full zoom
* (although nsDocumentViewer::SetFullZoom doesn't mention it).
*
* So when we apply new zoom values to the browser, we simply set the zoom.
* We don't check first to see if the new value is the same as the current
* one.
*
* @param aValue The zoom level value.
* @param aBrowser The zoom is set in this browser. Required.
* @param aCallback If given, it's asynchronously called when complete.
*/
_applyPrefToZoom: function FullZoom__applyPrefToZoom(aValue, aBrowser, aCallback) {
if (!this.siteSpecific || gInPrintPreviewMode) {
this._executeSoon(aCallback);
return;
}
// The browser is sometimes half-destroyed because this method is called
// by content pref service callbacks, which themselves can be called at any
// time, even after browsers are closed.
if (!aBrowser.parentNode || aBrowser.isSyntheticDocument) {
this._executeSoon(aCallback);
return;
}
if (aValue !== undefined) {
ZoomManager.setZoomForBrowser(aBrowser, this._ensureValid(aValue));
this._ignorePendingZoomAccesses(aBrowser);
this._executeSoon(aCallback);
return;
}
let token = this._getBrowserToken(aBrowser);
this._getGlobalValue(aBrowser).then(value => {
if (token.isCurrent) {
ZoomManager.setZoomForBrowser(aBrowser, value === undefined ? 1 : value);
this._ignorePendingZoomAccesses(aBrowser);
}
this._executeSoon(aCallback);
});
},
/**
* Saves the zoom level of the page in the given browser to the content
* prefs store.
*
* @param browser The zoom of this browser will be saved. Required.
*/
_applyZoomToPref: function FullZoom__applyZoomToPref(browser) {
Services.obs.notifyObservers(browser, "browser-fullZoom:zoomChange", "");
if (!this.siteSpecific ||
gInPrintPreviewMode ||
browser.isSyntheticDocument)
return;
this._cps2.set(browser.currentURI.spec, this.name,
ZoomManager.getZoomForBrowser(browser),
this._loadContextFromBrowser(browser), {
handleCompletion: function () {
this._isNextContentPrefChangeInternal = true;
}.bind(this),
});
},
/**
* Removes from the content prefs store the zoom level of the given browser.
*
* @param browser The zoom of this browser will be removed. Required.
*/
_removePref: function FullZoom__removePref(browser) {
Services.obs.notifyObservers(browser, "browser-fullZoom:zoomReset", "");
if (browser.isSyntheticDocument)
return;
let ctxt = this._loadContextFromBrowser(browser);
this._cps2.removeByDomainAndName(browser.currentURI.spec, this.name, ctxt, {
handleCompletion: function () {
this._isNextContentPrefChangeInternal = true;
}.bind(this),
});
},
// Utilities
/**
* Returns the zoom change token of the given browser. Asynchronous
* operations that access the given browser's zoom should use this method to
* capture the token before starting and use token.isCurrent to determine if
* it's safe to access the zoom when done. If token.isCurrent is false, then
* after the async operation started, either the browser's zoom was changed or
* the browser was destroyed, and depending on what the operation is doing, it
* may no longer be safe to set and get its zoom.
*
* @param browser The token of this browser will be returned.
* @return An object with an "isCurrent" getter.
*/
_getBrowserToken: function FullZoom__getBrowserToken(browser) {
let map = this._browserTokenMap;
if (!map.has(browser))
map.set(browser, 0);
return {
token: map.get(browser),
get isCurrent() {
// At this point, the browser may have been destructed and unbound but
// its outer ID not removed from the map because outer-window-destroyed
// hasn't been received yet. In that case, the browser is unusable, it
// has no properties, so return false. Check for this case by getting a
// property, say, docShell.
return map.get(browser) === this.token && browser.parentNode;
},
};
},
/**
* Returns the browser that the supplied zoom event is associated with.
* @param event The ZoomChangeUsingMouseWheel event.
* @return The associated browser element, if one exists, otherwise null.
*/
_getTargetedBrowser: function FullZoom__getTargetedBrowser(event) {
let target = event.originalTarget;
// With remote content browsers, the event's target is the browser
// we're looking for.
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
if (target instanceof window.XULElement &&
target.localName == "browser" &&
target.namespaceURI == XUL_NS)
return target;
// With in-process content browsers, the event's target is the content
// document.
if (target.nodeType == Node.DOCUMENT_NODE)
return gBrowser.getBrowserForDocument(target);
throw new Error("Unexpected ZoomChangeUsingMouseWheel event source");
},
/**
* Increments the zoom change token for the given browser so that pending
* async operations know that it may be unsafe to access they zoom when they
* finish.
*
* @param browser Pending accesses in this browser will be ignored.
*/
_ignorePendingZoomAccesses: function FullZoom__ignorePendingZoomAccesses(browser) {
let map = this._browserTokenMap;
map.set(browser, (map.get(browser) || 0) + 1);
},
_ensureValid: function FullZoom__ensureValid(aValue) {
// Note that undefined is a valid value for aValue that indicates a known-
// not-to-exist value.
if (isNaN(aValue))
return 1;
if (aValue < ZoomManager.MIN)
return ZoomManager.MIN;
if (aValue > ZoomManager.MAX)
return ZoomManager.MAX;
return aValue;
},
/**
* Gets the global browser.content.full-zoom content preference.
*
* @param browser The browser pertaining to the zoom.
* @returns Promise<prefValue>
* Resolves to the preference value when done.
*/
_getGlobalValue: function FullZoom__getGlobalValue(browser) {
// * !("_globalValue" in this) => global value not yet cached.
// * this._globalValue === undefined => global value known not to exist.
// * Otherwise, this._globalValue is a number, the global value.
return new Promise(resolve => {
if ("_globalValue" in this) {
resolve(this._globalValue);
return;
}
let value = undefined;
this._cps2.getGlobal(this.name, this._loadContextFromBrowser(browser), {
handleResult: function (pref) { value = pref.value; },
handleCompletion: (reason) => {
this._globalValue = this._ensureValid(value);
resolve(this._globalValue);
}
});
});
},
/**
* Gets the load context from the given Browser.
*
* @param Browser The Browser whose load context will be returned.
* @return The nsILoadContext of the given Browser.
*/
_loadContextFromBrowser: function FullZoom__loadContextFromBrowser(browser) {
return browser.loadContext;
},
/**
* Asynchronously broadcasts "browser-fullZoom:location-change" so that
* listeners can be notified when the zoom levels on those pages change.
* The notification is always asynchronous so that observers are guaranteed a
* consistent behavior.
*/
_notifyOnLocationChange: function FullZoom__notifyOnLocationChange(browser) {
this._executeSoon(function () {
Services.obs.notifyObservers(browser, "browser-fullZoom:location-change", "");
});
},
_executeSoon: function FullZoom__executeSoon(callback) {
if (!callback)
return;
Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL);
},
};

View file

@ -1,459 +0,0 @@
/* 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 gFxAccounts = {
SYNC_MIGRATION_NOTIFICATION_TITLE: "fxa-migration",
_initialized: false,
_inCustomizationMode: false,
_cachedProfile: null,
get weave() {
delete this.weave;
return this.weave = Cc["@mozilla.org/weave/service;1"]
.getService(Ci.nsISupports)
.wrappedJSObject;
},
get topics() {
// Do all this dance to lazy-load FxAccountsCommon.
delete this.topics;
return this.topics = [
"weave:service:ready",
"weave:service:login:change",
"weave:service:setup-complete",
"weave:service:sync:error",
"weave:ui:login:error",
"fxa-migration:state-changed",
this.FxAccountsCommon.ONLOGIN_NOTIFICATION,
this.FxAccountsCommon.ONLOGOUT_NOTIFICATION,
this.FxAccountsCommon.ON_PROFILE_CHANGE_NOTIFICATION,
];
},
get panelUIFooter() {
delete this.panelUIFooter;
return this.panelUIFooter = document.getElementById("PanelUI-footer-fxa");
},
get panelUIStatus() {
delete this.panelUIStatus;
return this.panelUIStatus = document.getElementById("PanelUI-fxa-status");
},
get panelUIAvatar() {
delete this.panelUIAvatar;
return this.panelUIAvatar = document.getElementById("PanelUI-fxa-avatar");
},
get panelUILabel() {
delete this.panelUILabel;
return this.panelUILabel = document.getElementById("PanelUI-fxa-label");
},
get panelUIIcon() {
delete this.panelUIIcon;
return this.panelUIIcon = document.getElementById("PanelUI-fxa-icon");
},
get strings() {
delete this.strings;
return this.strings = Services.strings.createBundle(
"chrome://browser/locale/accounts.properties"
);
},
get loginFailed() {
// Referencing Weave.Service will implicitly initialize sync, and we don't
// want to force that - so first check if it is ready.
let service = Cc["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
if (!service.ready) {
return false;
}
// LOGIN_FAILED_LOGIN_REJECTED explicitly means "you must log back in".
// All other login failures are assumed to be transient and should go
// away by themselves, so aren't reflected here.
return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
},
get sendTabToDeviceEnabled() {
return Services.prefs.getBoolPref("services.sync.sendTabToDevice.enabled");
},
get remoteClients() {
return Weave.Service.clientsEngine.remoteClients
.sort((a, b) => a.name.localeCompare(b.name));
},
init: function () {
// Bail out if we're already initialized and for pop-up windows.
if (this._initialized || !window.toolbar.visible) {
return;
}
for (let topic of this.topics) {
Services.obs.addObserver(this, topic, false);
}
gNavToolbox.addEventListener("customizationstarting", this);
gNavToolbox.addEventListener("customizationending", this);
EnsureFxAccountsWebChannel();
this._initialized = true;
this.updateUI();
},
uninit: function () {
if (!this._initialized) {
return;
}
for (let topic of this.topics) {
Services.obs.removeObserver(this, topic);
}
this._initialized = false;
},
observe: function (subject, topic, data) {
switch (topic) {
case "fxa-migration:state-changed":
this.onMigrationStateChanged(data, subject);
break;
case this.FxAccountsCommon.ON_PROFILE_CHANGE_NOTIFICATION:
this._cachedProfile = null;
// Fallthrough intended
default:
this.updateUI();
break;
}
},
onMigrationStateChanged: function () {
// Since we nuked most of the migration code, this notification will fire
// once after legacy Sync has been disconnected (and should never fire
// again)
let nb = window.document.getElementById("global-notificationbox");
let msg = this.strings.GetStringFromName("autoDisconnectDescription")
let signInLabel = this.strings.GetStringFromName("autoDisconnectSignIn.label");
let signInAccessKey = this.strings.GetStringFromName("autoDisconnectSignIn.accessKey");
let learnMoreLink = this.fxaMigrator.learnMoreLink;
let buttons = [
{
label: signInLabel,
accessKey: signInAccessKey,
callback: () => {
this.openPreferences();
}
}
];
let fragment = document.createDocumentFragment();
let msgNode = document.createTextNode(msg);
fragment.appendChild(msgNode);
if (learnMoreLink) {
let link = document.createElement("label");
link.className = "text-link";
link.setAttribute("value", learnMoreLink.text);
link.href = learnMoreLink.href;
fragment.appendChild(link);
}
nb.appendNotification(fragment,
this.SYNC_MIGRATION_NOTIFICATION_TITLE,
undefined,
nb.PRIORITY_WARNING_LOW,
buttons);
// ensure the hamburger menu reflects the newly disconnected state.
this.updateAppMenuItem();
},
handleEvent: function (event) {
this._inCustomizationMode = event.type == "customizationstarting";
this.updateAppMenuItem();
},
updateUI: function () {
// It's possible someone signed in to FxA after seeing our notification
// about "Legacy Sync migration" (which now is actually "Legacy Sync
// auto-disconnect") so kill that notification if it still exists.
let nb = window.document.getElementById("global-notificationbox");
let n = nb.getNotificationWithValue(this.SYNC_MIGRATION_NOTIFICATION_TITLE);
if (n) {
nb.removeNotification(n, true);
}
this.updateAppMenuItem();
},
// Note that updateAppMenuItem() returns a Promise that's only used by tests.
updateAppMenuItem: function () {
let profileInfoEnabled = false;
try {
profileInfoEnabled = Services.prefs.getBoolPref("identity.fxaccounts.profile_image.enabled");
} catch (e) { }
// Bail out if FxA is disabled.
if (!this.weave.fxAccountsEnabled) {
return Promise.resolve();
}
this.panelUIFooter.hidden = false;
// Make sure the button is disabled in customization mode.
if (this._inCustomizationMode) {
this.panelUIStatus.setAttribute("disabled", "true");
this.panelUILabel.setAttribute("disabled", "true");
this.panelUIAvatar.setAttribute("disabled", "true");
this.panelUIIcon.setAttribute("disabled", "true");
} else {
this.panelUIStatus.removeAttribute("disabled");
this.panelUILabel.removeAttribute("disabled");
this.panelUIAvatar.removeAttribute("disabled");
this.panelUIIcon.removeAttribute("disabled");
}
let defaultLabel = this.panelUIStatus.getAttribute("defaultlabel");
let errorLabel = this.panelUIStatus.getAttribute("errorlabel");
let unverifiedLabel = this.panelUIStatus.getAttribute("unverifiedlabel");
// The localization string is for the signed in text, but it's the default text as well
let defaultTooltiptext = this.panelUIStatus.getAttribute("signedinTooltiptext");
let updateWithUserData = (userData) => {
// Window might have been closed while fetching data.
if (window.closed) {
return;
}
// Reset the button to its original state.
this.panelUILabel.setAttribute("label", defaultLabel);
this.panelUIStatus.setAttribute("tooltiptext", defaultTooltiptext);
this.panelUIFooter.removeAttribute("fxastatus");
this.panelUIFooter.removeAttribute("fxaprofileimage");
this.panelUIAvatar.style.removeProperty("list-style-image");
let showErrorBadge = false;
if (userData) {
// At this point we consider the user as logged-in (but still can be in an error state)
if (this.loginFailed) {
let tooltipDescription = this.strings.formatStringFromName("reconnectDescription", [userData.email], 1);
this.panelUIFooter.setAttribute("fxastatus", "error");
this.panelUILabel.setAttribute("label", errorLabel);
this.panelUIStatus.setAttribute("tooltiptext", tooltipDescription);
showErrorBadge = true;
} else if (!userData.verified) {
let tooltipDescription = this.strings.formatStringFromName("verifyDescription", [userData.email], 1);
this.panelUIFooter.setAttribute("fxastatus", "error");
this.panelUIFooter.setAttribute("unverified", "true");
this.panelUILabel.setAttribute("label", unverifiedLabel);
this.panelUIStatus.setAttribute("tooltiptext", tooltipDescription);
showErrorBadge = true;
} else {
this.panelUIFooter.setAttribute("fxastatus", "signedin");
this.panelUILabel.setAttribute("label", userData.email);
}
if (profileInfoEnabled) {
this.panelUIFooter.setAttribute("fxaprofileimage", "enabled");
}
}
if (showErrorBadge) {
gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_FXA, "fxa-needs-authentication");
} else {
gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_FXA);
}
}
let updateWithProfile = (profile) => {
if (profileInfoEnabled) {
if (profile.displayName) {
this.panelUILabel.setAttribute("label", profile.displayName);
}
if (profile.avatar) {
this.panelUIFooter.setAttribute("fxaprofileimage", "set");
let bgImage = "url(\"" + profile.avatar + "\")";
this.panelUIAvatar.style.listStyleImage = bgImage;
let img = new Image();
img.onerror = () => {
// Clear the image if it has trouble loading. Since this callback is asynchronous
// we check to make sure the image is still the same before we clear it.
if (this.panelUIAvatar.style.listStyleImage === bgImage) {
this.panelUIFooter.removeAttribute("fxaprofileimage");
this.panelUIAvatar.style.removeProperty("list-style-image");
}
};
img.src = profile.avatar;
}
}
}
return fxAccounts.getSignedInUser().then(userData => {
// userData may be null here when the user is not signed-in, but that's expected
updateWithUserData(userData);
// unverified users cause us to spew log errors fetching an OAuth token
// to fetch the profile, so don't even try in that case.
if (!userData || !userData.verified || !profileInfoEnabled) {
return null; // don't even try to grab the profile.
}
if (this._cachedProfile) {
return this._cachedProfile;
}
return fxAccounts.getSignedInUserProfile().catch(err => {
// Not fetching the profile is sad but the FxA logs will already have noise.
return null;
});
}).then(profile => {
if (!profile) {
return;
}
updateWithProfile(profile);
this._cachedProfile = profile; // Try to avoid fetching the profile on every UI update
}).catch(error => {
// This is most likely in tests, were we quickly log users in and out.
// The most likely scenario is a user logged out, so reflect that.
// Bug 995134 calls for better errors so we could retry if we were
// sure this was the failure reason.
this.FxAccountsCommon.log.error("Error updating FxA account info", error);
updateWithUserData(null);
});
},
onMenuPanelCommand: function () {
switch (this.panelUIFooter.getAttribute("fxastatus")) {
case "signedin":
this.openPreferences();
break;
case "error":
if (this.panelUIFooter.getAttribute("unverified")) {
this.openPreferences();
} else {
this.openSignInAgainPage("menupanel");
}
break;
default:
this.openPreferences();
break;
}
PanelUI.hide();
},
openPreferences: function () {
openPreferences("paneSync", { urlParams: { entrypoint: "menupanel" } });
},
openAccountsPage: function (action, urlParams={}) {
let params = new URLSearchParams();
if (action) {
params.set("action", action);
}
for (let name in urlParams) {
if (urlParams[name] !== undefined) {
params.set(name, urlParams[name]);
}
}
let url = "about:accounts?" + params;
switchToTabHavingURI(url, true, {
replaceQueryString: true
});
},
openSignInAgainPage: function (entryPoint) {
this.openAccountsPage("reauth", { entrypoint: entryPoint });
},
sendTabToDevice: function (url, clientId, title) {
Weave.Service.clientsEngine.sendURIToClientForDisplay(url, clientId, title);
},
populateSendTabToDevicesMenu: function (devicesPopup, url, title) {
// remove existing menu items
while (devicesPopup.hasChildNodes()) {
devicesPopup.removeChild(devicesPopup.firstChild);
}
const fragment = document.createDocumentFragment();
const onTargetDeviceCommand = (event) => {
const clientId = event.target.getAttribute("clientId");
const clients = clientId
? [clientId]
: this.remoteClients.map(client => client.id);
clients.forEach(clientId => this.sendTabToDevice(url, clientId, title));
}
function addTargetDevice(clientId, name) {
const targetDevice = document.createElement("menuitem");
targetDevice.addEventListener("command", onTargetDeviceCommand, true);
targetDevice.setAttribute("class", "sendtab-target");
targetDevice.setAttribute("clientId", clientId);
targetDevice.setAttribute("label", name);
fragment.appendChild(targetDevice);
}
const clients = this.remoteClients;
for (let client of clients) {
addTargetDevice(client.id, client.name);
}
// "All devices" menu item
if (clients.length > 1) {
const separator = document.createElement("menuseparator");
fragment.appendChild(separator);
const allDevicesLabel = this.strings.GetStringFromName("sendTabToAllDevices.menuitem");
addTargetDevice("", allDevicesLabel);
}
devicesPopup.appendChild(fragment);
},
updateTabContextMenu: function (aPopupMenu) {
if (!this.sendTabToDeviceEnabled) {
return;
}
const remoteClientPresent = this.remoteClients.length > 0;
["context_sendTabToDevice", "context_sendTabToDevice_separator"]
.forEach(id => { document.getElementById(id).hidden = !remoteClientPresent });
},
initPageContextMenu: function (contextMenu) {
if (!this.sendTabToDeviceEnabled) {
return;
}
const remoteClientPresent = this.remoteClients.length > 0;
// showSendLink and showSendPage are mutually exclusive
const showSendLink = remoteClientPresent
&& (contextMenu.onSaveableLink || contextMenu.onPlainTextLink);
const showSendPage = !showSendLink && remoteClientPresent
&& !(contextMenu.isContentSelected ||
contextMenu.onImage || contextMenu.onCanvas ||
contextMenu.onVideo || contextMenu.onAudio ||
contextMenu.onLink || contextMenu.onTextInput);
["context-sendpagetodevice", "context-sep-sendpagetodevice"]
.forEach(id => contextMenu.showItem(id, showSendPage));
["context-sendlinktodevice", "context-sep-sendlinktodevice"]
.forEach(id => contextMenu.showItem(id, showSendLink));
}
};
XPCOMUtils.defineLazyGetter(gFxAccounts, "FxAccountsCommon", function () {
return Cu.import("resource://gre/modules/FxAccountsCommon.js", {});
});
XPCOMUtils.defineLazyModuleGetter(gFxAccounts, "fxaMigrator",
"resource://services-sync/FxaMigrator.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "EnsureFxAccountsWebChannel",
"resource://gre/modules/FxAccountsWebChannel.jsm");

File diff suppressed because it is too large Load diff

View file

@ -1,367 +0,0 @@
/* -*- 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 gEMEHandler = {
get uiEnabled() {
#ifdef MOZ_EME
let emeUIEnabled = Services.prefs.getBoolPref("browser.eme.ui.enabled");
// Force-disable on WinXP:
if (navigator.platform.toLowerCase().startsWith("win")) {
emeUIEnabled = emeUIEnabled && parseFloat(Services.sysinfo.get("version")) >= 6;
}
return emeUIEnabled;
#else
return false;
#endif
},
ensureEMEEnabled: function(browser, keySystem) {
Services.prefs.setBoolPref("media.eme.enabled", true);
if (keySystem) {
if (keySystem.startsWith("com.adobe") &&
Services.prefs.getPrefType("media.gmp-eme-adobe.enabled") &&
!Services.prefs.getBoolPref("media.gmp-eme-adobe.enabled")) {
Services.prefs.setBoolPref("media.gmp-eme-adobe.enabled", true);
} else if (keySystem == "com.widevine.alpha" &&
Services.prefs.getPrefType("media.gmp-widevinecdm.enabled") &&
!Services.prefs.getBoolPref("media.gmp-widevinecdm.enabled")) {
Services.prefs.setBoolPref("media.gmp-widevinecdm.enabled", true);
}
}
browser.reload();
},
isKeySystemVisible: function(keySystem) {
if (!keySystem) {
return false;
}
if (keySystem.startsWith("com.adobe") &&
Services.prefs.getPrefType("media.gmp-eme-adobe.visible")) {
return Services.prefs.getBoolPref("media.gmp-eme-adobe.visible");
}
if (keySystem == "com.widevine.alpha" &&
Services.prefs.getPrefType("media.gmp-widevinecdm.visible")) {
return Services.prefs.getBoolPref("media.gmp-widevinecdm.visible");
}
return true;
},
getEMEDisabledFragment: function(msgId) {
let mainMessage = gNavigatorBundle.getString("emeNotifications.drmContentDisabled.message");
let [prefix, suffix] = mainMessage.split(/%(?:\$\d)?S/).map(s => document.createTextNode(s));
let text = gNavigatorBundle.getString("emeNotifications.drmContentDisabled.learnMoreLabel");
let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
let link = document.createElement("label");
link.className = "text-link";
link.setAttribute("href", baseURL + "drm-content");
link.textContent = text;
let fragment = document.createDocumentFragment();
[prefix, link, suffix].forEach(n => fragment.appendChild(n));
return fragment;
},
getMessageWithBrandName: function(notificationId) {
let msgId = "emeNotifications." + notificationId + ".message";
return gNavigatorBundle.getFormattedString(msgId, [this._brandShortName]);
},
receiveMessage: function({target: browser, data: data}) {
let parsedData;
try {
parsedData = JSON.parse(data);
} catch (ex) {
Cu.reportError("Malformed EME video message with data: " + data);
return;
}
let {status: status, keySystem: keySystem} = parsedData;
// Don't need to show if disabled or keysystem not visible.
if (!this.uiEnabled || !this.isKeySystemVisible(keySystem)) {
return;
}
let notificationId;
let buttonCallback;
// Notification message can be either a string or a DOM fragment.
let notificationMessage;
switch (status) {
case "available":
case "cdm-created":
// Only show the chain icon for proprietary CDMs. Clearkey is not one.
if (keySystem != "org.w3.clearkey") {
this.showPopupNotificationForSuccess(browser, keySystem);
}
// ... and bail!
return;
case "api-disabled":
case "cdm-disabled":
notificationId = "drmContentDisabled";
buttonCallback = gEMEHandler.ensureEMEEnabled.bind(gEMEHandler, browser, keySystem)
notificationMessage = this.getEMEDisabledFragment();
break;
case "cdm-insufficient-version":
notificationId = "drmContentCDMInsufficientVersion";
notificationMessage = this.getMessageWithBrandName(notificationId);
break;
case "cdm-not-installed":
notificationId = "drmContentCDMInstalling";
notificationMessage = this.getMessageWithBrandName(notificationId);
break;
case "cdm-not-supported":
// Not to pop up user-level notification because they cannot do anything
// about it.
return;
default:
Cu.reportError(new Error("Unknown message ('" + status + "') dealing with EME key request: " + data));
return;
}
// Now actually create the notification
let box = gBrowser.getNotificationBox(browser);
if (box.getNotificationWithValue(notificationId)) {
return;
}
let buttons = [];
if (buttonCallback) {
let msgPrefix = "emeNotifications." + notificationId + ".";
let btnLabelId = msgPrefix + "button.label";
let btnAccessKeyId = msgPrefix + "button.accesskey";
buttons.push({
label: gNavigatorBundle.getString(btnLabelId),
accessKey: gNavigatorBundle.getString(btnAccessKeyId),
callback: buttonCallback
});
}
let iconURL = "chrome://browser/skin/drm-icon.svg#chains-black";
box.appendNotification(notificationMessage, notificationId, iconURL,
box.PRIORITY_WARNING_MEDIUM, buttons);
},
showPopupNotificationForSuccess: function(browser, keySystem) {
// We're playing EME content! Remove any "we can't play because..." messages.
var box = gBrowser.getNotificationBox(browser);
["drmContentDisabled",
"drmContentCDMInstalling"
].forEach(function (value) {
var notification = box.getNotificationWithValue(value);
if (notification)
box.removeNotification(notification);
});
// Don't bother creating it if it's already there:
if (PopupNotifications.getNotification("drmContentPlaying", browser)) {
return;
}
let msgPrefix = "emeNotifications.drmContentPlaying.";
let msgId = msgPrefix + "message2";
let btnLabelId = msgPrefix + "button.label";
let btnAccessKeyId = msgPrefix + "button.accesskey";
let message = gNavigatorBundle.getFormattedString(msgId, [this._brandShortName]);
let anchorId = "eme-notification-icon";
let firstPlayPref = "browser.eme.ui.firstContentShown";
if (!Services.prefs.getPrefType(firstPlayPref) ||
!Services.prefs.getBoolPref(firstPlayPref)) {
document.getElementById(anchorId).setAttribute("firstplay", "true");
Services.prefs.setBoolPref(firstPlayPref, true);
} else {
document.getElementById(anchorId).removeAttribute("firstplay");
}
let mainAction = {
label: gNavigatorBundle.getString(btnLabelId),
accessKey: gNavigatorBundle.getString(btnAccessKeyId),
callback: function() { openPreferences("paneContent"); },
dismiss: true
};
let options = {
dismissed: true,
eventCallback: aTopic => aTopic == "swapping",
learnMoreURL: Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content",
};
PopupNotifications.show(browser, "drmContentPlaying", message, anchorId, mainAction, null, options);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener])
};
XPCOMUtils.defineLazyGetter(gEMEHandler, "_brandShortName", function() {
return document.getElementById("bundle_brand").getString("brandShortName");
});
const TELEMETRY_DDSTAT_SHOWN = 0;
const TELEMETRY_DDSTAT_SHOWN_FIRST = 1;
const TELEMETRY_DDSTAT_CLICKED = 2;
const TELEMETRY_DDSTAT_CLICKED_FIRST = 3;
const TELEMETRY_DDSTAT_SOLVED = 4;
let gDecoderDoctorHandler = {
getLabelForNotificationBox(type) {
if (type == "adobe-cdm-not-found" &&
AppConstants.platform == "win") {
if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
// We supply our own Learn More button so we don't need to populate the message here.
return gNavigatorBundle.getFormattedString("emeNotifications.drmContentDisabled.message", [""]);
}
return gNavigatorBundle.getString("decoder.noCodecs.message");
}
if (type == "adobe-cdm-not-activated" &&
AppConstants.platform == "win") {
if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
return gNavigatorBundle.getString("decoder.noCodecsXP.message");
}
if (!AppConstants.isPlatformAndVersionAtLeast("win", "6.1")) {
return gNavigatorBundle.getString("decoder.noCodecsVista.message");
}
return gNavigatorBundle.getString("decoder.noCodecs.message");
}
if (type == "platform-decoder-not-found") {
if (AppConstants.isPlatformAndVersionAtLeast("win", "6.1")) {
return gNavigatorBundle.getString("decoder.noHWAcceleration.message");
}
if (AppConstants.isPlatformAndVersionAtLeast("win", "6")) {
return gNavigatorBundle.getString("decoder.noHWAccelerationVista.message");
}
if (AppConstants.platform == "linux") {
return gNavigatorBundle.getString("decoder.noCodecsLinux.message");
}
}
if (type == "cannot-initialize-pulseaudio") {
return gNavigatorBundle.getString("decoder.noPulseAudio.message");
}
if (type == "unsupported-libavcodec" &&
AppConstants.platform == "linux") {
return gNavigatorBundle.getString("decoder.unsupportedLibavcodec.message");
}
return "";
},
getSumoForLearnHowButton(type) {
if (AppConstants.platform == "win") {
return "fix-video-audio-problems-firefox-windows";
}
if (type == "cannot-initialize-pulseaudio") {
return "fix-common-audio-and-video-issues";
}
return "";
},
receiveMessage({target: browser, data: data}) {
let box = gBrowser.getNotificationBox(browser);
let notificationId = "decoder-doctor-notification";
if (box.getNotificationWithValue(notificationId)) {
return;
}
let parsedData;
try {
parsedData = JSON.parse(data);
} catch (ex) {
Cu.reportError("Malformed Decoder Doctor message with data: " + data);
return;
}
// parsedData (the result of parsing the incoming 'data' json string)
// contains analysis information from Decoder Doctor:
// - 'type' is the type of issue, it determines which text to show in the
// infobar.
// - 'decoderDoctorReportId' is the Decoder Doctor issue identifier, to be
// used here as key for the telemetry (counting infobar displays,
// "Learn how" buttons clicks, and resolutions) and for the prefs used
// to store at-issue formats.
// - 'formats' contains a comma-separated list of formats (or key systems)
// that suffer the issue. These are kept in a pref, which the backend
// uses to later find when an issue is resolved.
// - 'isSolved' is true when the notification actually indicates the
// resolution of that issue, to be reported as telemetry.
let {type, isSolved, decoderDoctorReportId, formats} = parsedData;
type = type.toLowerCase();
// Error out early on invalid ReportId
if (!(/^\w+$/mi).test(decoderDoctorReportId)) {
return
}
let title = gDecoderDoctorHandler.getLabelForNotificationBox(type);
if (!title) {
return;
}
// We keep the list of formats in prefs for the sake of the decoder itself,
// which reads it to determine when issues get solved for these formats.
// (Writing prefs from e10s content is now allowed.)
let formatsPref = "media.decoder-doctor." + decoderDoctorReportId + ".formats";
let buttonClickedPref = "media.decoder-doctor." + decoderDoctorReportId + ".button-clicked";
let histogram =
Services.telemetry.getKeyedHistogramById("DECODER_DOCTOR_INFOBAR_STATS");
let formatsInPref = Services.prefs.getPrefType(formatsPref) &&
Services.prefs.getCharPref(formatsPref);
if (!isSolved) {
if (!formats) {
Cu.reportError("Malformed Decoder Doctor unsolved message with no formats");
return;
}
if (!formatsInPref) {
Services.prefs.setCharPref(formatsPref, formats);
histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_SHOWN_FIRST);
} else {
// Split existing formats into an array of strings.
let existing = formatsInPref.split(",").map(String.trim);
// Keep given formats that were not already recorded.
let newbies = formats.split(",").map(String.trim)
.filter(x => !existing.includes(x));
// And rewrite pref with the added new formats (if any).
if (newbies.length) {
Services.prefs.setCharPref(formatsPref,
existing.concat(newbies).join(", "));
}
}
histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_SHOWN);
let buttons = [];
let sumo = gDecoderDoctorHandler.getSumoForLearnHowButton(type);
if (sumo) {
buttons.push({
label: gNavigatorBundle.getString("decoder.noCodecs.button"),
accessKey: gNavigatorBundle.getString("decoder.noCodecs.accesskey"),
callback() {
let clickedInPref = Services.prefs.getPrefType(buttonClickedPref) &&
Services.prefs.getBoolPref(buttonClickedPref);
if (!clickedInPref) {
Services.prefs.setBoolPref(buttonClickedPref, true);
histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_CLICKED_FIRST);
}
histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_CLICKED);
let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
openUILinkIn(baseURL + sumo, "tab");
}
});
}
box.appendNotification(
title,
notificationId,
"", // This uses the info icon as specified below.
box.PRIORITY_INFO_LOW,
buttons
);
} else if (formatsInPref) {
// Issue is solved, and prefs haven't been cleared yet, meaning it's the
// first time we get this resolution -> Clear prefs and report telemetry.
Services.prefs.clearUserPref(formatsPref);
Services.prefs.clearUserPref(buttonClickedPref);
histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_SOLVED);
}
},
}
window.getGroupMessageManager("browsers").addMessageListener("DecoderDoctor:Notification", gDecoderDoctorHandler);
window.getGroupMessageManager("browsers").addMessageListener("EMEVideo:ContentMediaKeysRequest", gEMEHandler);
window.addEventListener("unload", function() {
window.getGroupMessageManager("browsers").removeMessageListener("EMEVideo:ContentMediaKeysRequest", gEMEHandler);
window.getGroupMessageManager("browsers").removeMessageListener("DecoderDoctor:Notification", gDecoderDoctorHandler);
}, false);

View file

@ -1,521 +0,0 @@
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# 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/.
<menubar id="main-menubar"
onpopupshowing="if (event.target.parentNode.parentNode == this &amp;&amp;
!('@mozilla.org/widget/nativemenuservice;1' in Cc))
this.setAttribute('openedwithkey',
event.target.parentNode.openedWithKey);"
style="border:0px;padding:0px;margin:0px;-moz-appearance:none">
<menu id="file-menu" label="&fileMenu.label;"
accesskey="&fileMenu.accesskey;">
<menupopup id="menu_FilePopup"
onpopupshowing="updateUserContextUIVisibility();">
<menuitem id="menu_newNavigatorTab"
label="&tabCmd.label;"
command="cmd_newNavigatorTab"
key="key_newNavigatorTab"
accesskey="&tabCmd.accesskey;"/>
<menu id="menu_newUserContext"
label="&newUserContext.label;"
accesskey="&newUserContext.accesskey;"
hidden="true">
<menupopup onpopupshowing="return createUserContextMenu(event);" />
</menu>
<menuitem id="menu_newNavigator"
label="&newNavigatorCmd.label;"
accesskey="&newNavigatorCmd.accesskey;"
key="key_newNavigator"
command="cmd_newNavigator"/>
<menuitem id="menu_newPrivateWindow"
label="&newPrivateWindow.label;"
accesskey="&newPrivateWindow.accesskey;"
command="Tools:PrivateBrowsing"
key="key_privatebrowsing"/>
#ifdef MAC_NON_BROWSER_WINDOW
<menuitem id="menu_openLocation"
label="&openLocationCmd.label;"
command="Browser:OpenLocation"
key="focusURLBar"/>
#endif
<menuitem id="menu_openFile"
label="&openFileCmd.label;"
command="Browser:OpenFile"
key="openFileKb"
accesskey="&openFileCmd.accesskey;"/>
<menuitem id="menu_close"
class="show-only-for-keyboard"
label="&closeCmd.label;"
key="key_close"
accesskey="&closeCmd.accesskey;"
command="cmd_close"/>
<menuitem id="menu_closeWindow"
class="show-only-for-keyboard"
hidden="true"
command="cmd_closeWindow"
key="key_closeWindow"
label="&closeWindow.label;"
accesskey="&closeWindow.accesskey;"/>
<menuseparator/>
<menuitem id="menu_savePage"
label="&savePageCmd.label;"
accesskey="&savePageCmd.accesskey;"
key="key_savePage"
command="Browser:SavePage"/>
<menuitem id="menu_sendLink"
label="&emailPageCmd.label;"
accesskey="&emailPageCmd.accesskey;"
command="Browser:SendLink"/>
<menuseparator/>
#if !defined(MOZ_WIDGET_GTK)
<menuitem id="menu_printSetup"
label="&printSetupCmd.label;"
accesskey="&printSetupCmd.accesskey;"
command="cmd_pageSetup"/>
#endif
#ifndef XP_MACOSX
<menuitem id="menu_printPreview"
label="&printPreviewCmd.label;"
accesskey="&printPreviewCmd.accesskey;"
command="cmd_printPreview"/>
#endif
<menuitem id="menu_print"
label="&printCmd.label;"
accesskey="&printCmd.accesskey;"
key="printKb"
command="cmd_print"/>
<menuseparator/>
<menuitem id="goOfflineMenuitem"
label="&goOfflineCmd.label;"
accesskey="&goOfflineCmd.accesskey;"
type="checkbox"
observes="workOfflineMenuitemState"
oncommand="BrowserOffline.toggleOfflineStatus();"/>
<menuitem id="menu_FileQuitItem"
#ifdef XP_WIN
label="&quitApplicationCmdWin2.label;"
accesskey="&quitApplicationCmdWin2.accesskey;"
#else
#ifdef XP_MACOSX
label="&quitApplicationCmdMac2.label;"
#else
label="&quitApplicationCmd.label;"
accesskey="&quitApplicationCmd.accesskey;"
#endif
#ifdef XP_UNIX
key="key_quitApplication"
#endif
#endif
command="cmd_quitApplication"/>
</menupopup>
</menu>
<menu id="edit-menu" label="&editMenu.label;"
accesskey="&editMenu.accesskey;">
<menupopup id="menu_EditPopup"
onpopupshowing="updateEditUIVisibility()"
onpopuphidden="updateEditUIVisibility()">
<menuitem id="menu_undo"
label="&undoCmd.label;"
key="key_undo"
accesskey="&undoCmd.accesskey;"
command="cmd_undo"/>
<menuitem id="menu_redo"
label="&redoCmd.label;"
key="key_redo"
accesskey="&redoCmd.accesskey;"
command="cmd_redo"/>
<menuseparator/>
<menuitem id="menu_cut"
label="&cutCmd.label;"
key="key_cut"
accesskey="&cutCmd.accesskey;"
command="cmd_cut"/>
<menuitem id="menu_copy"
label="&copyCmd.label;"
key="key_copy"
accesskey="&copyCmd.accesskey;"
command="cmd_copy"/>
<menuitem id="menu_paste"
label="&pasteCmd.label;"
key="key_paste"
accesskey="&pasteCmd.accesskey;"
command="cmd_paste"/>
<menuitem id="menu_delete"
label="&deleteCmd.label;"
key="key_delete"
accesskey="&deleteCmd.accesskey;"
command="cmd_delete"/>
<menuseparator/>
<menuitem id="menu_selectAll"
label="&selectAllCmd.label;"
key="key_selectAll"
accesskey="&selectAllCmd.accesskey;"
command="cmd_selectAll"/>
<menuseparator/>
<menuitem id="menu_find"
label="&findOnCmd.label;"
accesskey="&findOnCmd.accesskey;"
key="key_find"
command="cmd_find"/>
<menuitem id="menu_findAgain"
class="show-only-for-keyboard"
label="&findAgainCmd.label;"
accesskey="&findAgainCmd.accesskey;"
key="key_findAgain"
command="cmd_findAgain"/>
<menuseparator hidden="true" id="textfieldDirection-separator"/>
<menuitem id="textfieldDirection-swap"
command="cmd_switchTextDirection"
key="key_switchTextDirection"
label="&bidiSwitchTextDirectionItem.label;"
accesskey="&bidiSwitchTextDirectionItem.accesskey;"
hidden="true"/>
</menupopup>
</menu>
<menu id="view-menu" label="&viewMenu.label;"
accesskey="&viewMenu.accesskey;">
<menupopup id="menu_viewPopup"
onpopupshowing="updateCharacterEncodingMenuState();">
<menu id="viewToolbarsMenu"
label="&viewToolbarsMenu.label;"
accesskey="&viewToolbarsMenu.accesskey;">
<menupopup onpopupshowing="onViewToolbarsPopupShowing(event);">
<menuseparator/>
<menuitem id="menu_customizeToolbars"
label="&viewCustomizeToolbar.label;"
accesskey="&viewCustomizeToolbar.accesskey;"
command="cmd_CustomizeToolbars"/>
</menupopup>
</menu>
<menu id="viewSidebarMenuMenu"
label="&viewSidebarMenu.label;"
accesskey="&viewSidebarMenu.accesskey;">
<menupopup id="viewSidebarMenu">
<menuitem id="menu_bookmarksSidebar"
key="viewBookmarksSidebarKb"
observes="viewBookmarksSidebar"/>
<menuitem id="menu_historySidebar"
key="key_gotoHistory"
observes="viewHistorySidebar"
label="&historyButton.label;"/>
<menuitem id="menu_tabsSidebar"
observes="viewTabsSidebar"
label="&syncedTabs.sidebar.label;"/>
</menupopup>
</menu>
<menuseparator/>
<menu id="viewFullZoomMenu" label="&fullZoom.label;"
accesskey="&fullZoom.accesskey;"
onpopupshowing="FullZoom.updateMenu();">
<menupopup>
<menuitem id="menu_zoomEnlarge"
key="key_fullZoomEnlarge"
label="&fullZoomEnlargeCmd.label;"
accesskey="&fullZoomEnlargeCmd.accesskey;"
command="cmd_fullZoomEnlarge"/>
<menuitem id="menu_zoomReduce"
key="key_fullZoomReduce"
label="&fullZoomReduceCmd.label;"
accesskey="&fullZoomReduceCmd.accesskey;"
command="cmd_fullZoomReduce"/>
<menuseparator/>
<menuitem id="menu_zoomReset"
key="key_fullZoomReset"
label="&fullZoomResetCmd.label;"
accesskey="&fullZoomResetCmd.accesskey;"
command="cmd_fullZoomReset"/>
<menuseparator/>
<menuitem id="toggle_zoom"
label="&fullZoomToggleCmd.label;"
accesskey="&fullZoomToggleCmd.accesskey;"
type="checkbox"
command="cmd_fullZoomToggle"
checked="false"/>
</menupopup>
</menu>
<menu id="pageStyleMenu" label="&pageStyleMenu.label;"
accesskey="&pageStyleMenu.accesskey;" observes="isImage">
<menupopup onpopupshowing="gPageStyleMenu.fillPopup(this);">
<menuitem id="menu_pageStyleNoStyle"
label="&pageStyleNoStyle.label;"
accesskey="&pageStyleNoStyle.accesskey;"
oncommand="gPageStyleMenu.disableStyle();"
type="radio"/>
<menuitem id="menu_pageStylePersistentOnly"
label="&pageStylePersistentOnly.label;"
accesskey="&pageStylePersistentOnly.accesskey;"
oncommand="gPageStyleMenu.switchStyleSheet('');"
type="radio"
checked="true"/>
<menuseparator/>
</menupopup>
</menu>
#include browser-charsetmenu.inc
<menuseparator/>
#ifdef XP_MACOSX
<menuitem id="enterFullScreenItem"
accesskey="&enterFullScreenCmd.accesskey;"
label="&enterFullScreenCmd.label;"
key="key_fullScreen">
<observes element="View:FullScreen" attribute="oncommand"/>
<observes element="View:FullScreen" attribute="disabled"/>
</menuitem>
<menuitem id="exitFullScreenItem"
accesskey="&exitFullScreenCmd.accesskey;"
label="&exitFullScreenCmd.label;"
key="key_fullScreen"
hidden="true">
<observes element="View:FullScreen" attribute="oncommand"/>
<observes element="View:FullScreen" attribute="disabled"/>
</menuitem>
#else
<menuitem id="fullScreenItem"
accesskey="&fullScreenCmd.accesskey;"
label="&fullScreenCmd.label;"
key="key_fullScreen"
type="checkbox"
observes="View:FullScreen"/>
#endif
<menuitem id="menu_readerModeItem"
observes="View:ReaderView"
hidden="true"/>
<menuitem id="menu_showAllTabs"
hidden="true"
accesskey="&showAllTabsCmd.accesskey;"
label="&showAllTabsCmd.label;"
command="Browser:ShowAllTabs"
key="key_showAllTabs"/>
<menuseparator hidden="true" id="documentDirection-separator"/>
<menuitem id="documentDirection-swap"
hidden="true"
label="&bidiSwitchPageDirectionItem.label;"
accesskey="&bidiSwitchPageDirectionItem.accesskey;"
oncommand="gBrowser.selectedBrowser
.messageManager
.sendAsyncMessage('SwitchDocumentDirection');"/>
</menupopup>
</menu>
<menu id="history-menu"
label="&historyMenu.label;"
accesskey="&historyMenu.accesskey;">
<menupopup id="goPopup"
#ifndef XP_MACOSX
placespopup="true"
#endif
oncommand="this.parentNode._placesView._onCommand(event);"
onclick="checkForMiddleClick(this, event);"
onpopupshowing="if (!this.parentNode._placesView)
new HistoryMenu(event);"
tooltip="bhTooltip"
popupsinherittooltip="true">
<menuitem id="menu_showAllHistory"
label="&showAllHistoryCmd2.label;"
#ifndef XP_MACOSX
key="showAllHistoryKb"
#endif
command="Browser:ShowAllHistory"/>
<menuitem id="sanitizeItem"
label="&clearRecentHistory.label;"
key="key_sanitize"
command="Tools:Sanitize"/>
<menuseparator id="sanitizeSeparator"/>
<menuitem id="sync-tabs-menuitem"
class="syncTabsMenuItem"
label="&syncTabsMenu3.label;"
oncommand="BrowserOpenSyncTabs();"
hidden="true"/>
<menuitem id="historyRestoreLastSession"
label="&historyRestoreLastSession.label;"
command="Browser:RestoreLastSession"/>
<menu id="historyUndoMenu"
class="recentlyClosedTabsMenu"
label="&historyUndoMenu.label;"
disabled="true">
<menupopup id="historyUndoPopup"
#ifndef XP_MACOSX
placespopup="true"
#endif
onpopupshowing="document.getElementById('history-menu')._placesView.populateUndoSubmenu();"/>
</menu>
<menu id="historyUndoWindowMenu"
class="recentlyClosedWindowsMenu"
label="&historyUndoWindowMenu.label;"
disabled="true">
<menupopup id="historyUndoWindowPopup"
#ifndef XP_MACOSX
placespopup="true"
#endif
onpopupshowing="document.getElementById('history-menu')._placesView.populateUndoWindowSubmenu();"/>
</menu>
<menuseparator id="startHistorySeparator"
class="hide-if-empty-places-result"/>
</menupopup>
</menu>
<menu id="bookmarksMenu"
label="&bookmarksMenu.label;"
accesskey="&bookmarksMenu.accesskey;"
ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
ondragover="PlacesMenuDNDHandler.onDragOver(event);"
ondrop="PlacesMenuDNDHandler.onDrop(event);">
<menupopup id="bookmarksMenuPopup"
#ifndef XP_MACOSX
placespopup="true"
#endif
context="placesContext"
openInTabs="children"
oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
onpopupshowing="BookmarkingUI.onMainMenuPopupShowing(event);
if (!this.parentNode._placesView)
new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
tooltip="bhTooltip" popupsinherittooltip="true">
<menuitem id="bookmarksShowAll"
label="&showAllBookmarks2.label;"
command="Browser:ShowAllBookmarks"
key="manBookmarkKb"/>
<menuseparator id="organizeBookmarksSeparator"/>
<menuitem id="menu_bookmarkThisPage"
command="Browser:AddBookmarkAs"
observes="bookmarkThisPageBroadcaster"
key="addBookmarkAsKb"/>
<menuitem id="subscribeToPageMenuitem"
#ifndef XP_MACOSX
class="menuitem-iconic"
#endif
label="&subscribeToPageMenuitem.label;"
oncommand="return FeedHandler.subscribeToFeed(null, event);"
onclick="checkForMiddleClick(this, event);"
observes="singleFeedMenuitemState"/>
<menu id="subscribeToPageMenupopup"
#ifndef XP_MACOSX
class="menu-iconic"
#endif
label="&subscribeToPageMenupopup.label;"
observes="multipleFeedsMenuState">
<menupopup id="subscribeToPageSubmenuMenupopup"
onpopupshowing="return FeedHandler.buildFeedList(event.target);"
oncommand="return FeedHandler.subscribeToFeed(null, event);"
onclick="checkForMiddleClick(this, event);"/>
</menu>
<menuitem id="menu_bookmarkAllTabs"
label="&addCurPagesCmd.label;"
class="show-only-for-keyboard"
command="Browser:BookmarkAllTabs"
key="bookmarkAllTabsKb"/>
<menuseparator/>
<menuitem label="&recentBookmarks.label;"
id="menu_recentBookmarks"
disabled="true"/>
<menuseparator id="bookmarksToolbarSeparator"/>
<menu id="bookmarksToolbarFolderMenu"
class="menu-iconic bookmark-item"
label="&personalbarCmd.label;"
container="true">
<menupopup id="bookmarksToolbarFolderPopup"
#ifndef XP_MACOSX
placespopup="true"
#endif
context="placesContext"
onpopupshowing="if (!this.parentNode._placesView)
new PlacesMenu(event, 'place:folder=TOOLBAR');"/>
</menu>
<menu id="menu_unsortedBookmarks"
class="menu-iconic bookmark-item"
label="&otherBookmarksCmd.label;"
container="true">
<menupopup id="otherBookmarksFolderPopup"
#ifndef XP_MACOSX
placespopup="true"
#endif
context="placesContext"
onpopupshowing="if (!this.parentNode._placesView)
new PlacesMenu(event, 'place:folder=UNFILED_BOOKMARKS');"/>
</menu>
<menuseparator id="bookmarksMenuItemsSeparator"/>
<!-- Bookmarks menu items -->
</menupopup>
</menu>
<menu id="tools-menu"
label="&toolsMenu.label;"
accesskey="&toolsMenu.accesskey;"
onpopupshowing="mirrorShow(this)">
<menupopup id="menu_ToolsPopup"
# We have to use setTimeout() here to avoid a flickering menu bar when opening
# the Tools menu, see bug 970769. This can be removed once we got rid of the
# event loop spinning in Weave.Status._authManager.
onpopupshowing="setTimeout(() => gSyncUI.updateUI());"
>
<menuitem id="menu_openDownloads"
label="&downloads.label;"
accesskey="&downloads.accesskey;"
key="key_openDownloads"
command="Tools:Downloads"/>
<menuitem id="menu_openAddons"
label="&addons.label;"
accesskey="&addons.accesskey;"
key="key_openAddons"
command="Tools:Addons"/>
<!-- only one of sync-setup, sync-syncnowitem or sync-reauthitem will be showing at once -->
<menuitem id="sync-setup"
label="&syncSignIn.label;"
accesskey="&syncSignIn.accesskey;"
observes="sync-setup-state"
oncommand="gSyncUI.openSetup(null, 'menubar')"/>
<menuitem id="sync-syncnowitem"
label="&syncSyncNowItem.label;"
accesskey="&syncSyncNowItem.accesskey;"
observes="sync-syncnow-state"
oncommand="gSyncUI.doSync(event);"/>
<menuitem id="sync-reauthitem"
label="&syncReAuthItem.label;"
accesskey="&syncReAuthItem.accesskey;"
observes="sync-reauth-state"
oncommand="gSyncUI.openSignInAgainPage('menubar');"/>
<menuseparator id="devToolsSeparator"/>
<menu id="webDeveloperMenu"
label="&webDeveloperMenu.label;"
accesskey="&webDeveloperMenu.accesskey;">
<menupopup id="menuWebDeveloperPopup">
<menuitem id="menu_pageSource"
observes="devtoolsMenuBroadcaster_PageSource"
accesskey="&pageSourceCmd.accesskey;"/>
<menuitem id="javascriptConsole"
observes="devtoolsMenuBroadcaster_ErrorConsole"
accesskey="&errorConsoleCmd.accesskey;"/>
</menupopup>
</menu>
<menuitem id="menu_pageInfo"
accesskey="&pageInfoCmd.accesskey;"
label="&pageInfoCmd.label;"
#ifndef XP_WIN
key="key_viewInfo"
#endif
command="View:PageInfo"/>
<menu id="menu_mirrorTabCmd"
hidden="true"
accesskey="&mirrorTabCmd.accesskey;"
label="&mirrorTabCmd.label;">
<menupopup id="menu_mirrorTab-popup"
onpopupshowing="populateMirrorTabMenu(this)"/>
</menu>
<menuseparator id="prefSep"/>
<menuitem id="menu_preferences"
label="&preferencesCmd2.label;"
accesskey="&preferencesCmd2.accesskey;"
oncommand="openPreferences();"/>
</menupopup>
</menu>
#ifdef XP_MACOSX
<menu id="windowMenu" />
#endif
<menu id="helpMenu" />
</menubar>

File diff suppressed because it is too large Load diff

View file

@ -1,516 +0,0 @@
/* -*- 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 gPluginHandler = {
PREF_SESSION_PERSIST_MINUTES: "plugin.sessionPermissionNow.intervalInMinutes",
PREF_PERSISTENT_DAYS: "plugin.persistentPermissionAlways.intervalInDays",
MESSAGES: [
"PluginContent:ShowClickToPlayNotification",
"PluginContent:RemoveNotification",
"PluginContent:UpdateHiddenPluginUI",
"PluginContent:HideNotificationBar",
"PluginContent:InstallSinglePlugin",
"PluginContent:ShowPluginCrashedNotification",
"PluginContent:SubmitReport",
"PluginContent:LinkClickCallback",
],
init: function () {
const mm = window.messageManager;
for (let msg of this.MESSAGES) {
mm.addMessageListener(msg, this);
}
window.addEventListener("unload", this);
},
uninit: function () {
const mm = window.messageManager;
for (let msg of this.MESSAGES) {
mm.removeMessageListener(msg, this);
}
window.removeEventListener("unload", this);
},
handleEvent: function (event) {
if (event.type == "unload") {
this.uninit();
}
},
receiveMessage: function (msg) {
switch (msg.name) {
case "PluginContent:ShowClickToPlayNotification":
this.showClickToPlayNotification(msg.target, msg.data.plugins, msg.data.showNow,
msg.principal, msg.data.location);
break;
case "PluginContent:RemoveNotification":
this.removeNotification(msg.target, msg.data.name);
break;
case "PluginContent:UpdateHiddenPluginUI":
this.updateHiddenPluginUI(msg.target, msg.data.haveInsecure, msg.data.actions,
msg.principal, msg.data.location);
break;
case "PluginContent:HideNotificationBar":
this.hideNotificationBar(msg.target, msg.data.name);
break;
case "PluginContent:InstallSinglePlugin":
this.installSinglePlugin(msg.data.pluginInfo);
break;
case "PluginContent:ShowPluginCrashedNotification":
this.showPluginCrashedNotification(msg.target, msg.data.messageString,
msg.data.pluginID);
break;
case "PluginContent:SubmitReport":
// Nothing to do here
break;
case "PluginContent:LinkClickCallback":
switch (msg.data.name) {
case "managePlugins":
case "openHelpPage":
case "openPluginUpdatePage":
this[msg.data.name].call(this, msg.data.pluginTag);
break;
}
break;
default:
Cu.reportError("gPluginHandler did not expect to handle message " + msg.name);
break;
}
},
// Callback for user clicking on a disabled plugin
managePlugins: function () {
BrowserOpenAddonsMgr("addons://list/plugin");
},
// Callback for user clicking on the link in a click-to-play plugin
// (where the plugin has an update)
openPluginUpdatePage: function(pluginTag) {
let url = Services.blocklist.getPluginInfoURL(pluginTag);
if (!url) {
url = Services.blocklist.getPluginBlocklistURL(pluginTag);
}
openUILinkIn(url, "tab");
},
submitReport: function submitReport(runID, keyVals, submitURLOptIn) {
/*** STUB ***/
return;
},
// Callback for user clicking a "reload page" link
reloadPage: function (browser) {
browser.reload();
},
// Callback for user clicking the help icon
openHelpPage: function () {
openHelpLink("plugin-crashed", false);
},
_clickToPlayNotificationEventCallback: function PH_ctpEventCallback(event) {
if (event == "showing") {
Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_SHOWN")
.add(!this.options.primaryPlugin);
// Histograms always start at 0, even though our data starts at 1
let histogramCount = this.options.pluginData.size - 1;
if (histogramCount > 4) {
histogramCount = 4;
}
Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_PLUGIN_COUNT")
.add(histogramCount);
}
else if (event == "dismissed") {
// Once the popup is dismissed, clicking the icon should show the full
// list again
this.options.primaryPlugin = null;
}
},
/**
* Called from the plugin doorhanger to set the new permissions for a plugin
* and activate plugins if necessary.
* aNewState should be either "allownow" "allowalways" or "block"
*/
_updatePluginPermission: function (aNotification, aPluginInfo, aNewState) {
let permission;
let expireType;
let expireTime;
let histogram =
Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_USER_ACTION");
// Update the permission manager.
// Also update the current state of pluginInfo.fallbackType so that
// subsequent opening of the notification shows the current state.
switch (aNewState) {
case "allownow":
permission = Ci.nsIPermissionManager.ALLOW_ACTION;
expireType = Ci.nsIPermissionManager.EXPIRE_SESSION;
expireTime = Date.now() + Services.prefs.getIntPref(this.PREF_SESSION_PERSIST_MINUTES) * 60 * 1000;
histogram.add(0);
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
break;
case "allowalways":
permission = Ci.nsIPermissionManager.ALLOW_ACTION;
expireType = Ci.nsIPermissionManager.EXPIRE_TIME;
expireTime = Date.now() +
Services.prefs.getIntPref(this.PREF_PERSISTENT_DAYS) * 24 * 60 * 60 * 1000;
histogram.add(1);
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
break;
case "block":
permission = Ci.nsIPermissionManager.PROMPT_ACTION;
expireType = Ci.nsIPermissionManager.EXPIRE_NEVER;
expireTime = 0;
histogram.add(2);
switch (aPluginInfo.blocklistState) {
case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE:
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE;
break;
case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE:
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE;
break;
default:
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY;
}
break;
// In case a plugin has already been allowed in another tab, the "continue allowing" button
// shouldn't change any permissions but should run the plugin-enablement code below.
case "continue":
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
break;
default:
Cu.reportError(Error("Unexpected plugin state: " + aNewState));
return;
}
let browser = aNotification.browser;
if (aNewState != "continue") {
let principal = aNotification.options.principal;
Services.perms.addFromPrincipal(principal, aPluginInfo.permissionString,
permission, expireType, expireTime);
aPluginInfo.pluginPermissionType = expireType;
}
browser.messageManager.sendAsyncMessage("BrowserPlugins:ActivatePlugins", {
pluginInfo: aPluginInfo,
newState: aNewState,
});
},
showClickToPlayNotification: function (browser, plugins, showNow,
principal, location) {
// It is possible that we've received a message from the frame script to show
// a click to play notification for a principal that no longer matches the one
// that the browser's content now has assigned (ie, the browser has browsed away
// after the message was sent, but before the message was received). In that case,
// we should just ignore the message.
if (!principal.equals(browser.contentPrincipal)) {
return;
}
// Data URIs, when linked to from some page, inherit the principal of that
// page. That means that we also need to compare the actual locations to
// ensure we aren't getting a message from a Data URI that we're no longer
// looking at.
let receivedURI = BrowserUtils.makeURI(location);
if (!browser.documentURI.equalsExceptRef(receivedURI)) {
return;
}
let notification = PopupNotifications.getNotification("click-to-play-plugins", browser);
// If this is a new notification, create a pluginData map, otherwise append
let pluginData;
if (notification) {
pluginData = notification.options.pluginData;
} else {
pluginData = new Map();
}
for (var pluginInfo of plugins) {
if (pluginData.has(pluginInfo.permissionString)) {
continue;
}
// If a block contains an infoURL, we should always prefer that to the default
// URL that we construct in-product, even for other blocklist types.
let url = Services.blocklist.getPluginInfoURL(pluginInfo.pluginTag);
if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
if (!url) {
url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
}
}
else {
url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "clicktoplay";
}
pluginInfo.detailsLink = url;
pluginData.set(pluginInfo.permissionString, pluginInfo);
}
let primaryPluginPermission = null;
if (showNow) {
primaryPluginPermission = plugins[0].permissionString;
}
if (notification) {
// Don't modify the notification UI while it's on the screen, that would be
// jumpy and might allow clickjacking.
if (showNow) {
notification.options.primaryPlugin = primaryPluginPermission;
notification.reshow();
browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationShown");
}
return;
}
let options = {
dismissed: !showNow,
eventCallback: this._clickToPlayNotificationEventCallback,
primaryPlugin: primaryPluginPermission,
pluginData: pluginData,
principal: principal,
};
PopupNotifications.show(browser, "click-to-play-plugins",
"", "plugins-notification-icon",
null, null, options);
browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationShown");
},
removeNotification: function (browser, name) {
let notification = PopupNotifications.getNotification(name, browser);
if (notification)
PopupNotifications.remove(notification);
},
hideNotificationBar: function (browser, name) {
let notificationBox = gBrowser.getNotificationBox(browser);
let notification = notificationBox.getNotificationWithValue(name);
if (notification)
notificationBox.removeNotification(notification, true);
},
updateHiddenPluginUI: function (browser, haveInsecure, actions,
principal, location) {
let origin = principal.originNoSuffix;
// It is possible that we've received a message from the frame script to show
// the hidden plugin notification for a principal that no longer matches the one
// that the browser's content now has assigned (ie, the browser has browsed away
// after the message was sent, but before the message was received). In that case,
// we should just ignore the message.
if (!principal.equals(browser.contentPrincipal)) {
return;
}
// Data URIs, when linked to from some page, inherit the principal of that
// page. That means that we also need to compare the actual locations to
// ensure we aren't getting a message from a Data URI that we're no longer
// looking at.
let receivedURI = BrowserUtils.makeURI(location);
if (!browser.documentURI.equalsExceptRef(receivedURI)) {
return;
}
// Set up the icon
document.getElementById("plugins-notification-icon").classList.
toggle("plugin-blocked", haveInsecure);
// Now configure the notification bar
let notificationBox = gBrowser.getNotificationBox(browser);
function hideNotification() {
let n = notificationBox.getNotificationWithValue("plugin-hidden");
if (n) {
notificationBox.removeNotification(n, true);
}
}
// There are three different cases when showing an infobar:
// 1. A single type of plugin is hidden on the page. Show the UI for that
// plugin.
// 2a. Multiple types of plugins are hidden on the page. Show the multi-UI
// with the vulnerable styling.
// 2b. Multiple types of plugins are hidden on the page, but none are
// vulnerable. Show the nonvulnerable multi-UI.
function showNotification() {
let n = notificationBox.getNotificationWithValue("plugin-hidden");
if (n) {
// If something is already shown, just keep it
return;
}
Services.telemetry.getHistogramById("PLUGINS_INFOBAR_SHOWN").
add(true);
let message;
// Icons set directly cannot be manipulated using moz-image-region, so
// we use CSS classes instead.
let brand = document.getElementById("bundle_brand").getString("brandShortName");
if (actions.length == 1) {
let pluginInfo = actions[0];
let pluginName = pluginInfo.pluginName;
switch (pluginInfo.fallbackType) {
case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
message = gNavigatorBundle.getFormattedString(
"pluginActivateNew.message",
[pluginName, origin]);
break;
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
message = gNavigatorBundle.getFormattedString(
"pluginActivateOutdated.message",
[pluginName, origin, brand]);
break;
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
message = gNavigatorBundle.getFormattedString(
"pluginActivateVulnerable.message",
[pluginName, origin, brand]);
}
} else {
// Multi-plugin
message = gNavigatorBundle.getFormattedString(
"pluginActivateMultiple.message", [origin]);
}
let buttons = [
{
label: gNavigatorBundle.getString("pluginContinueBlocking.label"),
accessKey: gNavigatorBundle.getString("pluginContinueBlocking.accesskey"),
callback: function() {
Services.telemetry.getHistogramById("PLUGINS_INFOBAR_BLOCK").
add(true);
Services.perms.addFromPrincipal(principal,
"plugin-hidden-notification",
Services.perms.DENY_ACTION);
}
},
{
label: gNavigatorBundle.getString("pluginActivateTrigger.label"),
accessKey: gNavigatorBundle.getString("pluginActivateTrigger.accesskey"),
callback: function() {
Services.telemetry.getHistogramById("PLUGINS_INFOBAR_ALLOW").
add(true);
let curNotification =
PopupNotifications.getNotification("click-to-play-plugins",
browser);
if (curNotification) {
curNotification.reshow();
}
}
}
];
n = notificationBox.
appendNotification(message, "plugin-hidden", null,
notificationBox.PRIORITY_INFO_HIGH, buttons);
if (haveInsecure) {
n.classList.add('pluginVulnerable');
}
}
if (actions.length == 0) {
hideNotification();
} else {
let notificationPermission = Services.perms.testPermissionFromPrincipal(
principal, "plugin-hidden-notification");
if (notificationPermission == Ci.nsIPermissionManager.DENY_ACTION) {
hideNotification();
} else {
showNotification();
}
}
},
contextMenuCommand: function (browser, plugin, command) {
browser.messageManager.sendAsyncMessage("BrowserPlugins:ContextMenuCommand",
{ command: command }, { plugin: plugin });
},
// Crashed-plugin observer. Notified once per plugin crash, before events
// are dispatched to individual plugin instances.
NPAPIPluginCrashed : function(subject, topic, data) {
let propertyBag = subject;
if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
!(propertyBag instanceof Ci.nsIWritablePropertyBag2) ||
!propertyBag.hasKey("runID") ||
!propertyBag.hasKey("pluginName")) {
Cu.reportError("A NPAPI plugin crashed, but the properties of this plugin " +
"cannot be read.");
return;
}
let runID = propertyBag.getPropertyAsUint32("runID");
let uglyPluginName = propertyBag.getPropertyAsAString("pluginName");
let pluginName = BrowserUtils.makeNicePluginName(uglyPluginName);
let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
// If we don't have a minidumpID, we can't (or didn't) submit anything.
// This can happen if the plugin is killed from the task manager.
let state = "noSubmit";
let mm = window.getGroupMessageManager("browsers");
mm.broadcastAsyncMessage("BrowserPlugins:NPAPIPluginProcessCrashed",
{ pluginName, runID, state });
},
/**
* Shows a plugin-crashed notification bar for a browser that has had an
* invisiable NPAPI plugin crash, or a GMP plugin crash.
*
* @param browser
* The browser to show the notification for.
* @param messageString
* The string to put in the notification bar
* @param pluginID
* The unique-per-process identifier for the NPAPI plugin or GMP.
* For a GMP, this is the pluginID. For NPAPI plugins (where "pluginID"
* means something different), this is the runID.
*/
showPluginCrashedNotification: function (browser, messageString, pluginID) {
// If there's already an existing notification bar, don't do anything.
let notificationBox = gBrowser.getNotificationBox(browser);
let notification = notificationBox.getNotificationWithValue("plugin-crashed");
if (notification) {
return;
}
// Configure the notification bar
let priority = notificationBox.PRIORITY_WARNING_MEDIUM;
let iconURL = "chrome://mozapps/skin/plugins/notifyPluginCrashed.png";
let reloadLabel = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label");
let reloadKey = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey");
let buttons = [{
label: reloadLabel,
accessKey: reloadKey,
popup: null,
callback: function() { browser.reload(); },
}];
notification = notificationBox.appendNotification(messageString, "plugin-crashed",
iconURL, priority, buttons);
// Add the "learn more" link.
let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let link = notification.ownerDocument.createElementNS(XULNS, "label");
link.className = "text-link";
link.setAttribute("value", gNavigatorBundle.getString("crashedpluginsMessage.learnMore"));
let crashurl = formatURL("app.support.baseURL", true);
crashurl += "plugin-crashed-notificationbar";
link.href = crashurl;
let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
description.appendChild(link);
},
};
gPluginHandler.init();

View file

@ -1,84 +0,0 @@
/* -*- 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/. */
/**
* If the user has opted into blocking refresh and redirect attempts by
* default, this handles showing the notification to the user which
* gives them the option to let the refresh or redirect proceed.
*/
var RefreshBlocker = {
init() {
gBrowser.addEventListener("RefreshBlocked", this);
},
uninit() {
gBrowser.removeEventListener("RefreshBlocked", this);
},
handleEvent: function(event) {
if (event.type == "RefreshBlocked") {
this.block(event.originalTarget, event.detail);
}
},
/**
* Shows the blocked refresh / redirect notification for some browser.
*
* @param browser (<xul:browser>)
* The browser that had the refresh blocked. This will be the browser
* for which we'll show the notification on.
* @param data (object)
* An object with the following properties:
*
* URI (string)
* The URI that a page is attempting to refresh or redirect to.
*
* delay (int)
* The delay (in milliseconds) before the page was going to reload
* or redirect.
*
* sameURI (bool)
* true if we're refreshing the page. false if we're redirecting.
*
* outerWindowID (int)
* The outerWindowID of the frame that requested the refresh or
* redirect.
*/
block(browser, data) {
let brandBundle = document.getElementById("bundle_brand");
let brandShortName = brandBundle.getString("brandShortName");
let message =
gNavigatorBundle.getFormattedString(data.sameURI ? "refreshBlocked.refreshLabel"
: "refreshBlocked.redirectLabel",
[brandShortName]);
let notificationBox = gBrowser.getNotificationBox(browser);
let notification = notificationBox.getNotificationWithValue("refresh-blocked");
if (notification) {
notification.label = message;
} else {
let refreshButtonText =
gNavigatorBundle.getString("refreshBlocked.goButton");
let refreshButtonAccesskey =
gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
let buttons = [{
label: refreshButtonText,
accessKey: refreshButtonAccesskey,
callback: function (notification, button) {
if (browser.messageManager) {
browser.messageManager.sendAsyncMessage("RefreshBlocker:Refresh", data);
}
}
}];
notificationBox.appendNotification(message, "refresh-blocked",
"chrome://browser/skin/Info.png",
notificationBox.PRIORITY_INFO_MEDIUM,
buttons);
}
}
};

View file

@ -1,48 +0,0 @@
/* 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 gSafeBrowsing = {
setReportPhishingMenu: function() {
// In order to detect whether or not we're at the phishing warning
// page, we have to check the documentURI instead of the currentURI.
// This is because when the DocShell loads an error page, the
// currentURI stays at the original target, while the documentURI
// will point to the internal error page we loaded instead.
var docURI = gBrowser.selectedBrowser.documentURI;
var isPhishingPage =
docURI && docURI.spec.startsWith("about:blocked?e=deceptiveBlocked");
// Show/hide the appropriate menu item.
document.getElementById("menu_HelpPopup_reportPhishingtoolmenu")
.hidden = isPhishingPage;
document.getElementById("menu_HelpPopup_reportPhishingErrortoolmenu")
.hidden = !isPhishingPage;
var broadcasterId = isPhishingPage
? "reportPhishingErrorBroadcaster"
: "reportPhishingBroadcaster";
var broadcaster = document.getElementById(broadcasterId);
if (!broadcaster)
return;
// Now look at the currentURI to learn which page we were trying
// to browse to.
let uri = gBrowser.currentURI;
if (uri && (uri.schemeIs("http") || uri.schemeIs("https")))
broadcaster.removeAttribute("disabled");
else
broadcaster.setAttribute("disabled", true);
},
/**
* Used to report a phishing page or a false positive
* @param name String One of "Phish", "Error", "Malware" or "MalwareError"
* @return String the report phishing URL.
*/
getReportURL: function(name) {
return SafeBrowsing.getReportURL(name, gBrowser.currentURI);
}
}

View file

@ -1,379 +0,0 @@
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# 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/.
#ifdef XP_UNIX
#ifndef XP_MACOSX
#define XP_GNOME 1
#endif
#endif
<stringbundleset id="stringbundleset">
<stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
<stringbundle id="bundle_shell" src="chrome://browser/locale/shellservice.properties"/>
<stringbundle id="bundle_preferences" src="chrome://browser/locale/preferences/preferences.properties"/>
</stringbundleset>
<commandset id="mainCommandSet">
<command id="cmd_newNavigator" oncommand="OpenBrowserWindow()" reserved="true"/>
<command id="cmd_handleBackspace" oncommand="BrowserHandleBackspace();" />
<command id="cmd_handleShiftBackspace" oncommand="BrowserHandleShiftBackspace();" />
<command id="cmd_newNavigatorTab" oncommand="BrowserOpenTab(event);" reserved="true"/>
<command id="cmd_newNavigatorTabNoEvent" oncommand="BrowserOpenTab();" reserved="true"/>
<command id="Browser:OpenFile" oncommand="BrowserOpenFileWindow();"/>
<command id="Browser:SavePage" oncommand="saveBrowser(gBrowser.selectedBrowser);"/>
<command id="Browser:SendLink"
oncommand="MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);"/>
<command id="cmd_pageSetup" oncommand="PrintUtils.showPageSetup();"/>
<command id="cmd_print" oncommand="PrintUtils.printWindow(window.gBrowser.selectedBrowser.outerWindowID, window.gBrowser.selectedBrowser);"/>
<command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
<command id="cmd_close" oncommand="BrowserCloseTabOrWindow()" reserved="true"/>
<command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()" reserved="true"/>
<command id="cmd_toggleMute" oncommand="gBrowser.selectedTab.toggleMuteAudio()"/>
<command id="cmd_CustomizeToolbars" oncommand="BrowserCustomizeToolbar()"/>
<command id="cmd_quitApplication" oncommand="goQuitApplication()" reserved="true"/>
<commandset id="editMenuCommands"/>
<command id="View:PageSource" oncommand="BrowserViewSource(window.gBrowser.selectedBrowser);" observes="canViewSource"/>
<command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
<command id="View:FullScreen" oncommand="BrowserFullScreen();"/>
<command id="View:ReaderView" oncommand="ReaderParent.toggleReaderMode(event);"/>
<command id="cmd_find"
oncommand="gFindBar.onFindCommand();"
observes="isImage"/>
<command id="cmd_findAgain"
oncommand="gFindBar.onFindAgainCommand(false);"
observes="isImage"/>
<command id="cmd_findPrevious"
oncommand="gFindBar.onFindAgainCommand(true);"
observes="isImage"/>
#ifdef XP_MACOSX
<command id="cmd_findSelection" oncommand="gFindBar.onFindSelectionCommand();"/>
#endif
<!-- work-around bug 392512 -->
<command id="Browser:AddBookmarkAs"
oncommand="PlacesCommandHook.bookmarkCurrentPage(true, PlacesUtils.bookmarksMenuFolderId);"/>
<!-- The command disabled state must be manually updated through
PlacesCommandHook.updateBookmarkAllTabsCommand() -->
<command id="Browser:BookmarkAllTabs"
oncommand="PlacesCommandHook.bookmarkCurrentPages();"/>
<command id="Browser:Home" oncommand="BrowserHome();"/>
<command id="Browser:Back" oncommand="BrowserBack();" disabled="true"/>
<command id="Browser:BackOrBackDuplicate" oncommand="BrowserBack(event);" disabled="true">
<observes element="Browser:Back" attribute="disabled"/>
</command>
<command id="Browser:Forward" oncommand="BrowserForward();" disabled="true"/>
<command id="Browser:ForwardOrForwardDuplicate" oncommand="BrowserForward(event);" disabled="true">
<observes element="Browser:Forward" attribute="disabled"/>
</command>
<command id="Browser:Stop" oncommand="BrowserStop();" disabled="true"/>
<command id="Browser:Reload" oncommand="if (event.shiftKey) BrowserReloadSkipCache(); else BrowserReload()" disabled="true"/>
<command id="Browser:ReloadOrDuplicate" oncommand="BrowserReloadOrDuplicate(event)" disabled="true">
<observes element="Browser:Reload" attribute="disabled"/>
</command>
<command id="Browser:ReloadSkipCache" oncommand="BrowserReloadSkipCache()" disabled="true">
<observes element="Browser:Reload" attribute="disabled"/>
</command>
<command id="Browser:NextTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(1, true);" reserved="true"/>
<command id="Browser:PrevTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(-1, true);" reserved="true"/>
<command id="Browser:ShowAllTabs" oncommand="allTabs.open();"/>
<command id="cmd_fullZoomReduce" oncommand="FullZoom.reduce()"/>
<command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
<command id="cmd_fullZoomReset" oncommand="FullZoom.reset()"/>
<command id="cmd_fullZoomToggle" oncommand="ZoomManager.toggleZoom();"/>
<command id="cmd_gestureRotateLeft" oncommand="gGestureSupport.rotate(event.sourceEvent)"/>
<command id="cmd_gestureRotateRight" oncommand="gGestureSupport.rotate(event.sourceEvent)"/>
<command id="cmd_gestureRotateEnd" oncommand="gGestureSupport.rotateEnd()"/>
<command id="Browser:OpenLocation" oncommand="openLocation();"/>
<command id="Browser:RestoreLastSession" oncommand="restoreLastSession();" disabled="true"/>
<command id="Browser:NewUserContextTab" oncommand="openNewUserContextTab(event.sourceEvent);" reserved="true"/>
<command id="Browser:OpenAboutContainers" oncommand="openPreferences('paneContainers');"/>
<command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
<command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
<command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
<command id="Tools:ErrorConsole" oncommand="toJavaScriptConsole()"/>
<command id="Tools:Sanitize"
oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
<command id="Tools:PrivateBrowsing"
oncommand="OpenBrowserWindow({private: true});" reserved="true"/>
<command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
<command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
</commandset>
<commandset id="placesCommands">
<command id="Browser:ShowAllBookmarks"
oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"/>
<command id="Browser:ShowAllHistory"
oncommand="PlacesCommandHook.showPlacesOrganizer('History');"/>
</commandset>
<broadcasterset id="mainBroadcasterSet">
<broadcaster id="viewBookmarksSidebar" autoCheck="false" label="&bookmarksButton.label;"
type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/bookmarks/bookmarksPanel.xul"
oncommand="SidebarUI.toggle('viewBookmarksSidebar');"/>
<!-- for both places and non-places, the sidebar lives at
chrome://browser/content/history/history-panel.xul so there are no
problems when switching between versions -->
<broadcaster id="viewHistorySidebar" autoCheck="false" sidebartitle="&historyButton.label;"
type="checkbox" group="sidebar"
sidebarurl="chrome://browser/content/history/history-panel.xul"
oncommand="SidebarUI.toggle('viewHistorySidebar');"/>
<broadcaster id="viewWebPanelsSidebar" autoCheck="false"
type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/web-panels.xul"
oncommand="SidebarUI.toggle('viewWebPanelsSidebar');"/>
<broadcaster id="bookmarkThisPageBroadcaster"
label="&bookmarkThisPageCmd.label;"
bookmarklabel="&bookmarkThisPageCmd.label;"
editlabel="&editThisBookmarkCmd.label;"/>
<!-- popup blocking menu items -->
<broadcaster id="blockedPopupAllowSite"
accesskey="&allowPopups.accesskey;"
oncommand="gPopupBlockerObserver.toggleAllowPopupsForSite(event);"/>
<broadcaster id="blockedPopupEditSettings"
#ifdef XP_WIN
label="&editPopupSettings.label;"
#else
label="&editPopupSettingsUnix.label;"
#endif
accesskey="&editPopupSettings.accesskey;"
oncommand="gPopupBlockerObserver.editPopupSettings();"/>
<broadcaster id="blockedPopupDontShowMessage"
accesskey="&dontShowMessage.accesskey;"
type="checkbox"
oncommand="gPopupBlockerObserver.dontShowMessage();"/>
<broadcaster id="blockedPopupsSeparator"/>
<broadcaster id="isImage"/>
<broadcaster id="canViewSource"/>
<broadcaster id="isFrameImage"/>
<broadcaster id="singleFeedMenuitemState" disabled="true"/>
<broadcaster id="multipleFeedsMenuState" hidden="true"/>
<!-- Sync broadcasters -->
<!-- A broadcaster of a number of attributes suitable for "sync now" UI -
A 'syncstatus' attribute is set while actively syncing, and the label
attribute which changes from "sync now" to "syncing" etc. -->
<broadcaster id="sync-status"/>
<!-- broadcasters of the "hidden" attribute to reflect setup state for
menus -->
<broadcaster id="sync-setup-state"/>
<broadcaster id="sync-syncnow-state" hidden="true"/>
<broadcaster id="sync-reauth-state" hidden="true"/>
<broadcaster id="viewTabsSidebar" autoCheck="false" sidebartitle="&syncedTabs.sidebar.label;"
type="checkbox" group="sidebar"
sidebarurl="chrome://browser/content/syncedtabs/sidebar.xhtml"
oncommand="SidebarUI.toggle('viewTabsSidebar');"/>
<broadcaster id="workOfflineMenuitemState"/>
<broadcaster id="devtoolsMenuBroadcaster_ErrorConsole"
label="&errorConsoleCmd.label;"
command="Tools:ErrorConsole"/>
<broadcaster id="devtoolsMenuBroadcaster_PageSource"
label="&pageSourceCmd.label;"
key="key_viewSource"
command="View:PageSource">
<observes element="canViewSource" attribute="disabled"/>
</broadcaster>
</broadcasterset>
<keyset id="mainKeyset">
<key id="key_newNavigator"
key="&newNavigatorCmd.key;"
command="cmd_newNavigator"
modifiers="accel"/>
<key id="key_newNavigatorTab" key="&tabCmd.commandkey;" modifiers="accel" command="cmd_newNavigatorTabNoEvent"/>
<key id="focusURLBar" key="&openCmd.commandkey;" command="Browser:OpenLocation"
modifiers="accel"/>
#ifndef XP_MACOSX
<key id="focusURLBar2" key="&urlbar.accesskey;" command="Browser:OpenLocation"
modifiers="alt"/>
#endif
#
# Search Command Key Logic works like this:
#
# Unix: Ctrl+K (cross platform binding)
# Ctrl+J (in case of emacs Ctrl-K conflict)
# Mac: Cmd+K (cross platform binding)
# Cmd+Opt+F (platform convention)
# Win: Ctrl+K (cross platform binding)
# Ctrl+E (IE compat)
#
# We support Ctrl+K on all platforms now and advertise it in the menu since it is
# our standard - it is a "safe" choice since it is near no harmful keys like "W" as
# "E" is. People mourning the loss of Ctrl+K for emacs compat can switch their GTK
# system setting to use emacs emulation, and we should respect it. Focus-Search-Box
# is a fundamental keybinding and we are maintaining a XP binding so that it is easy
# for people to switch to Linux.
#
<key id="key_search" key="&searchFocus.commandkey;" command="Tools:Search" modifiers="accel"/>
#ifdef XP_MACOSX
<key id="key_search2" key="&findOnCmd.commandkey;" command="Tools:Search" modifiers="accel,alt"/>
#endif
#ifdef XP_WIN
<key id="key_search2" key="&searchFocus.commandkey2;" command="Tools:Search" modifiers="accel"/>
#endif
#ifdef XP_GNOME
<key id="key_search2" key="&searchFocusUnix.commandkey;" command="Tools:Search" modifiers="accel"/>
<key id="key_openDownloads" key="&downloadsUnix.commandkey;" command="Tools:Downloads" modifiers="accel,shift"/>
#else
<key id="key_openDownloads" key="&downloads.commandkey;" command="Tools:Downloads" modifiers="accel"/>
#endif
<key id="key_openAddons" key="&addons.commandkey;" command="Tools:Addons" modifiers="accel,shift"/>
<key id="openFileKb" key="&openFileCmd.commandkey;" command="Browser:OpenFile" modifiers="accel"/>
<key id="key_savePage" key="&savePageCmd.commandkey;" command="Browser:SavePage" modifiers="accel"/>
<key id="printKb" key="&printCmd.commandkey;" command="cmd_print" modifiers="accel"/>
<key id="key_close" key="&closeCmd.key;" command="cmd_close" modifiers="accel"/>
<key id="key_closeWindow" key="&closeCmd.key;" command="cmd_closeWindow" modifiers="accel,shift"/>
<key id="key_toggleMute" key="&toggleMuteCmd.key;" command="cmd_toggleMute" modifiers="control"/>
<key id="key_undo"
key="&undoCmd.key;"
modifiers="accel"/>
#ifdef XP_UNIX
<key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift"/>
#else
<key id="key_redo" key="&redoCmd.key;" modifiers="accel"/>
#endif
<key id="key_cut"
key="&cutCmd.key;"
modifiers="accel"/>
<key id="key_copy"
key="&copyCmd.key;"
modifiers="accel"/>
<key id="key_paste"
key="&pasteCmd.key;"
modifiers="accel"/>
<key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
<key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel"/>
<key keycode="VK_BACK" command="cmd_handleBackspace"/>
<key keycode="VK_BACK" command="cmd_handleShiftBackspace" modifiers="shift"/>
#ifndef XP_MACOSX
<key id="goBackKb" keycode="VK_LEFT" command="Browser:Back" modifiers="alt"/>
<key id="goForwardKb" keycode="VK_RIGHT" command="Browser:Forward" modifiers="alt"/>
#else
<key id="goBackKb" keycode="VK_LEFT" command="Browser:Back" modifiers="accel" />
<key id="goForwardKb" keycode="VK_RIGHT" command="Browser:Forward" modifiers="accel" />
#endif
#ifdef XP_UNIX
<key id="goBackKb2" key="&goBackCmd.commandKey;" command="Browser:Back" modifiers="accel"/>
<key id="goForwardKb2" key="&goForwardCmd.commandKey;" command="Browser:Forward" modifiers="accel"/>
#endif
<key id="goHome" keycode="VK_HOME" command="Browser:Home" modifiers="alt"/>
<key keycode="VK_F5" command="Browser:Reload"/>
#ifndef XP_MACOSX
<key id="showAllHistoryKb" key="&showAllHistoryCmd.commandkey;" command="Browser:ShowAllHistory" modifiers="accel,shift"/>
<key keycode="VK_F5" command="Browser:ReloadSkipCache" modifiers="accel"/>
<key id="key_fullScreen" keycode="VK_F11" command="View:FullScreen"/>
#else
<key id="key_fullScreen" key="&fullScreenCmd.macCommandKey;" command="View:FullScreen" modifiers="accel,control"/>
<key id="key_fullScreen_old" key="&fullScreenCmd.macCommandKey;" command="View:FullScreen" modifiers="accel,shift"/>
<key keycode="VK_F11" command="View:FullScreen"/>
#endif
<key id="key_toggleReaderMode" key="&toggleReaderMode.key;" command="View:ReaderView" modifiers="accel,alt" disabled="true"/>
<key key="&reloadCmd.commandkey;" command="Browser:Reload" modifiers="accel" id="key_reload"/>
<key key="&reloadCmd.commandkey;" command="Browser:ReloadSkipCache" modifiers="accel,shift"/>
<key id="key_viewSource" key="&pageSourceCmd.commandkey;" command="View:PageSource" modifiers="accel"/>
#ifndef XP_WIN
<key id="key_viewInfo" key="&pageInfoCmd.commandkey;" command="View:PageInfo" modifiers="accel"/>
#endif
<key id="key_find" key="&findOnCmd.commandkey;" command="cmd_find" modifiers="accel"/>
<key id="key_findAgain" key="&findAgainCmd.commandkey;" command="cmd_findAgain" modifiers="accel"/>
<key id="key_findPrevious" key="&findAgainCmd.commandkey;" command="cmd_findPrevious" modifiers="accel,shift"/>
#ifdef XP_MACOSX
<key id="key_findSelection" key="&findSelectionCmd.commandkey;" command="cmd_findSelection" modifiers="accel"/>
#endif
<key keycode="&findAgainCmd.commandkey2;" command="cmd_findAgain"/>
<key keycode="&findAgainCmd.commandkey2;" command="cmd_findPrevious" modifiers="shift"/>
<key id="addBookmarkAsKb" key="&bookmarkThisPageCmd.commandkey;" command="Browser:AddBookmarkAs" modifiers="accel"/>
# Accel+Shift+A-F are reserved on GTK
#ifndef MOZ_WIDGET_GTK
<key id="bookmarkAllTabsKb" key="&bookmarkThisPageCmd.commandkey;" oncommand="PlacesCommandHook.bookmarkCurrentPages();" modifiers="accel,shift"/>
<key id="manBookmarkKb" key="&bookmarksCmd.commandkey;" command="Browser:ShowAllBookmarks" modifiers="accel,shift"/>
#else
<key id="manBookmarkKb" key="&bookmarksGtkCmd.commandkey;" command="Browser:ShowAllBookmarks" modifiers="accel,shift"/>
#endif
<key id="viewBookmarksSidebarKb" key="&bookmarksCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
#ifdef XP_WIN
# Cmd+I is conventially mapped to Info on MacOS X, thus it should not be
# overridden for other purposes there.
<key id="viewBookmarksSidebarWinKb" key="&bookmarksWinCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
#endif
<key id="key_stop" keycode="VK_ESCAPE" command="Browser:Stop"/>
#ifdef XP_MACOSX
<key id="key_stop_mac" modifiers="accel" key="&stopCmd.macCommandKey;" command="Browser:Stop"/>
#endif
<key id="key_gotoHistory"
key="&historySidebarCmd.commandKey;"
#ifdef XP_MACOSX
modifiers="accel,shift"
#else
modifiers="accel"
#endif
command="viewHistorySidebar"/>
<key id="key_fullZoomReduce" key="&fullZoomReduceCmd.commandkey;" command="cmd_fullZoomReduce" modifiers="accel"/>
<key key="&fullZoomReduceCmd.commandkey2;" command="cmd_fullZoomReduce" modifiers="accel"/>
<key id="key_fullZoomEnlarge" key="&fullZoomEnlargeCmd.commandkey;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
<key key="&fullZoomEnlargeCmd.commandkey2;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
<key key="&fullZoomEnlargeCmd.commandkey3;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
<key id="key_fullZoomReset" key="&fullZoomResetCmd.commandkey;" command="cmd_fullZoomReset" modifiers="accel"/>
<key key="&fullZoomResetCmd.commandkey2;" command="cmd_fullZoomReset" modifiers="accel"/>
<key id="key_showAllTabs" command="Browser:ShowAllTabs" keycode="VK_TAB" modifiers="control,shift"/>
<key id="key_switchTextDirection" key="&bidiSwitchTextDirectionItem.commandkey;" command="cmd_switchTextDirection" modifiers="accel,shift" />
<key id="key_privatebrowsing" command="Tools:PrivateBrowsing" key="&privateBrowsingCmd.commandkey;" modifiers="accel,shift"/>
<key id="key_sanitize" command="Tools:Sanitize" keycode="VK_DELETE" modifiers="accel,shift"/>
#ifdef XP_MACOSX
<key id="key_sanitize_mac" command="Tools:Sanitize" keycode="VK_BACK" modifiers="accel,shift"/>
<key id="key_quitApplication" key="&quitApplicationCmdUnix.key;" modifiers="accel" reserved="true"/>
#elifdef XP_UNIX
<key id="key_quitApplication" key="&quitApplicationCmdUnix.key;" command="cmd_quitApplication" modifiers="accel"/>
#endif
#ifdef FULL_BROWSER_WINDOW
<key id="key_undoCloseTab" command="History:UndoCloseTab" key="&tabCmd.commandkey;" modifiers="accel,shift"/>
#endif
<key id="key_undoCloseWindow" command="History:UndoCloseWindow" key="&newNavigatorCmd.key;" modifiers="accel,shift"/>
#ifdef XP_GNOME
#define NUM_SELECT_TAB_MODIFIER alt
#else
#define NUM_SELECT_TAB_MODIFIER accel
#endif
#expand <key id="key_selectTab1" oncommand="gBrowser.selectTabAtIndex(0, event);" key="1" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab2" oncommand="gBrowser.selectTabAtIndex(1, event);" key="2" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab3" oncommand="gBrowser.selectTabAtIndex(2, event);" key="3" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab4" oncommand="gBrowser.selectTabAtIndex(3, event);" key="4" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab5" oncommand="gBrowser.selectTabAtIndex(4, event);" key="5" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab6" oncommand="gBrowser.selectTabAtIndex(5, event);" key="6" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab7" oncommand="gBrowser.selectTabAtIndex(6, event);" key="7" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab8" oncommand="gBrowser.selectTabAtIndex(7, event);" key="8" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectLastTab" oncommand="gBrowser.selectTabAtIndex(-1, event);" key="9" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
</keyset>
# Used by baseMenuOverlay
#ifdef XP_MACOSX
<commandset id="baseMenuCommandSet" />
#endif
<keyset id="baseMenuKeyset" />

View file

@ -1,337 +0,0 @@
/* 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/. */
/**
* SidebarUI controls showing and hiding the browser sidebar.
*
* @note
* Some of these methods take a commandID argument - we expect to find a
* xul:broadcaster element with the specified ID.
* The following attributes on that element may be used and/or modified:
* - id (required) the string to match commandID. The convention
* is to use this naming scheme: 'view<sidebar-name>Sidebar'.
* - sidebarurl (required) specifies the URL to load in this sidebar.
* - sidebartitle or label (in that order) specify the title to
* display on the sidebar.
* - checked indicates whether the sidebar is currently displayed.
* Note that toggleSidebar updates this attribute when
* it changes the sidebar's visibility.
* - group this attribute must be set to "sidebar".
*/
var SidebarUI = {
browser: null,
_box: null,
_title: null,
_splitter: null,
init() {
this._box = document.getElementById("sidebar-box");
this.browser = document.getElementById("sidebar");
this._title = document.getElementById("sidebar-title");
this._splitter = document.getElementById("sidebar-splitter");
if (!this.adoptFromWindow(window.opener)) {
let commandID = this._box.getAttribute("sidebarcommand");
if (commandID) {
let command = document.getElementById(commandID);
if (command) {
this._delayedLoad = true;
this._box.hidden = false;
this._splitter.hidden = false;
command.setAttribute("checked", "true");
} else {
// Remove the |sidebarcommand| attribute, because the element it
// refers to no longer exists, so we should assume this sidebar
// panel has been uninstalled. (249883)
this._box.removeAttribute("sidebarcommand");
}
}
}
},
uninit() {
let enumerator = Services.wm.getEnumerator(null);
enumerator.getNext();
if (!enumerator.hasMoreElements()) {
document.persist("sidebar-box", "sidebarcommand");
document.persist("sidebar-box", "width");
document.persist("sidebar-box", "src");
document.persist("sidebar-title", "value");
}
},
/**
* Try and adopt the status of the sidebar from another window.
* @param {Window} sourceWindow - Window to use as a source for sidebar status.
* @return true if we adopted the state, or false if the caller should
* initialize the state itself.
*/
adoptFromWindow(sourceWindow) {
// No source window, or it being closed, or not chrome, or in a different
// private-browsing context means we can't adopt.
if (!sourceWindow || sourceWindow.closed ||
!sourceWindow.document.documentURIObject.schemeIs("chrome") ||
PrivateBrowsingUtils.isWindowPrivate(window) != PrivateBrowsingUtils.isWindowPrivate(sourceWindow)) {
return false;
}
// If the opener had a sidebar, open the same sidebar in our window.
// The opener can be the hidden window too, if we're coming from the state
// where no windows are open, and the hidden window has no sidebar box.
let sourceUI = sourceWindow.SidebarUI;
if (!sourceUI || !sourceUI._box) {
// no source UI or no _box means we also can't adopt the state.
return false;
}
if (sourceUI._box.hidden) {
// just hidden means we have adopted the hidden state.
return true;
}
let commandID = sourceUI._box.getAttribute("sidebarcommand");
let commandElem = document.getElementById(commandID);
// dynamically generated sidebars will fail this check, but we still
// consider it adopted.
if (!commandElem) {
return true;
}
this._title.setAttribute("value",
sourceUI._title.getAttribute("value"));
this._box.setAttribute("width", sourceUI._box.boxObject.width);
this._box.setAttribute("sidebarcommand", commandID);
// Note: we're setting 'src' on this._box, which is a <vbox>, not on
// the <browser id="sidebar">. This lets us delay the actual load until
// delayedStartup().
this._box.setAttribute("src", sourceUI.browser.getAttribute("src"));
this._delayedLoad = true;
this._box.hidden = false;
this._splitter.hidden = false;
commandElem.setAttribute("checked", "true");
return true;
},
/**
* If loading a sidebar was delayed on startup, start the load now.
*/
startDelayedLoad() {
if (!this._delayedLoad) {
return;
}
this.browser.setAttribute("src", this._box.getAttribute("src"));
},
/**
* Fire a "SidebarFocused" event on the sidebar's |window| to give the sidebar
* a chance to adjust focus as needed. An additional event is needed, because
* we don't want to focus the sidebar when it's opened on startup or in a new
* window, only when the user opens the sidebar.
*/
_fireFocusedEvent() {
let event = new CustomEvent("SidebarFocused", {bubbles: true});
this.browser.contentWindow.dispatchEvent(event);
// Run the original function for backwards compatibility.
fireSidebarFocusedEvent();
},
/**
* True if the sidebar is currently open.
*/
get isOpen() {
return !this._box.hidden;
},
/**
* The ID of the current sidebar (ie, the ID of the broadcaster being used).
* This can be set even if the sidebar is hidden.
*/
get currentID() {
return this._box.getAttribute("sidebarcommand");
},
get title() {
return this._title.value;
},
set title(value) {
this._title.value = value;
},
/**
* Toggle the visibility of the sidebar. If the sidebar is hidden or is open
* with a different commandID, then the sidebar will be opened using the
* specified commandID. Otherwise the sidebar will be hidden.
*
* @param {string} commandID ID of the xul:broadcaster element to use.
* @return {Promise}
*/
toggle(commandID = this.currentID) {
if (this.isOpen && commandID == this.currentID) {
this.hide();
return Promise.resolve();
}
return this.show(commandID);
},
/**
* Show the sidebar, using the parameters from the specified broadcaster.
* @see SidebarUI note.
*
* @param {string} commandID ID of the xul:broadcaster element to use.
*/
show(commandID) {
return new Promise((resolve, reject) => {
let sidebarBroadcaster = document.getElementById(commandID);
if (!sidebarBroadcaster || sidebarBroadcaster.localName != "broadcaster") {
reject(new Error("Invalid sidebar broadcaster specified: " + commandID));
return;
}
if (this.isOpen && commandID != this.currentID) {
BrowserUITelemetry.countSidebarEvent(this.currentID, "hide");
}
let broadcasters = document.getElementsByAttribute("group", "sidebar");
for (let broadcaster of broadcasters) {
// skip elements that observe sidebar broadcasters and random
// other elements
if (broadcaster.localName != "broadcaster") {
continue;
}
if (broadcaster != sidebarBroadcaster) {
broadcaster.removeAttribute("checked");
} else {
sidebarBroadcaster.setAttribute("checked", "true");
}
}
this._box.hidden = false;
this._splitter.hidden = false;
this._box.setAttribute("sidebarcommand", sidebarBroadcaster.id);
let title = sidebarBroadcaster.getAttribute("sidebartitle");
if (!title) {
title = sidebarBroadcaster.getAttribute("label");
}
this._title.value = title;
let url = sidebarBroadcaster.getAttribute("sidebarurl");
this.browser.setAttribute("src", url); // kick off async load
// We set this attribute here in addition to setting it on the <browser>
// element itself, because the code in SidebarUI.uninit() persists this
// attribute, not the "src" of the <browser id="sidebar">. The reason it
// does that is that we want to delay sidebar load a bit when a browser
// window opens. See delayedStartup() and SidebarUI.startDelayedLoad().
this._box.setAttribute("src", url);
if (this.browser.contentDocument.location.href != url) {
let onLoad = event => {
this.browser.removeEventListener("load", onLoad, true);
// We're handling the 'load' event before it bubbles up to the usual
// (non-capturing) event handlers. Let it bubble up before firing the
// SidebarFocused event.
setTimeout(() => this._fireFocusedEvent(), 0);
// Run the original function for backwards compatibility.
sidebarOnLoad(event);
resolve();
};
this.browser.addEventListener("load", onLoad, true);
} else {
// Older code handled this case, so we do it too.
this._fireFocusedEvent();
resolve();
}
let selBrowser = gBrowser.selectedBrowser;
selBrowser.messageManager.sendAsyncMessage("Sidebar:VisibilityChange",
{commandID: commandID, isOpen: true}
);
BrowserUITelemetry.countSidebarEvent(commandID, "show");
});
},
/**
* Hide the sidebar.
*/
hide() {
if (!this.isOpen) {
return;
}
let commandID = this._box.getAttribute("sidebarcommand");
let sidebarBroadcaster = document.getElementById(commandID);
if (sidebarBroadcaster.getAttribute("checked") != "true") {
return;
}
// Replace the document currently displayed in the sidebar with about:blank
// so that we can free memory by unloading the page. We need to explicitly
// create a new content viewer because the old one doesn't get destroyed
// until about:blank has loaded (which does not happen as long as the
// element is hidden).
this.browser.setAttribute("src", "about:blank");
this.browser.docShell.createAboutBlankContentViewer(null);
sidebarBroadcaster.removeAttribute("checked");
this._box.setAttribute("sidebarcommand", "");
this._title.value = "";
this._box.hidden = true;
this._splitter.hidden = true;
let selBrowser = gBrowser.selectedBrowser;
selBrowser.focus();
selBrowser.messageManager.sendAsyncMessage("Sidebar:VisibilityChange",
{commandID: commandID, isOpen: false}
);
BrowserUITelemetry.countSidebarEvent(commandID, "hide");
},
};
/**
* This exists for backards compatibility - it will be called once a sidebar is
* ready, following any request to show it.
*
* @deprecated
*/
function fireSidebarFocusedEvent() {}
/**
* This exists for backards compatibility - it gets called when a sidebar has
* been loaded.
*
* @deprecated
*/
function sidebarOnLoad(event) {}
/**
* This exists for backards compatibility, and is equivilent to
* SidebarUI.toggle() without the forceOpen param. With forceOpen set to true,
* it is equalivent to SidebarUI.show().
*
* @deprecated
*/
function toggleSidebar(commandID, forceOpen = false) {
Deprecated.warning("toggleSidebar() is deprecated, please use SidebarUI.toggle() or SidebarUI.show() instead",
"https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Sidebar");
if (forceOpen) {
SidebarUI.show(commandID);
} else {
SidebarUI.toggle(commandID);
}
}

View file

@ -1,548 +0,0 @@
/* 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/. */
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
#ifdef MOZ_SERVICES_CLOUDSYNC
XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
"resource://gre/modules/CloudSync.jsm");
#endif
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
"resource://gre/modules/FxAccounts.jsm");
const MIN_STATUS_ANIMATION_DURATION = 1600;
// gSyncUI handles updating the tools menu and displaying notifications.
var gSyncUI = {
_obs: ["weave:service:sync:start",
"weave:service:sync:finish",
"weave:service:sync:error",
"weave:service:setup-complete",
"weave:service:login:start",
"weave:service:login:finish",
"weave:service:login:error",
"weave:service:logout:finish",
"weave:service:start-over",
"weave:service:start-over:finish",
"weave:ui:login:error",
"weave:ui:sync:error",
"weave:ui:sync:finish",
"weave:ui:clear-error",
"weave:engine:sync:finish"
],
_unloaded: false,
// The last sync start time. Used to calculate the leftover animation time
// once syncing completes (bug 1239042).
_syncStartTime: 0,
_syncAnimationTimer: 0,
init: function () {
Cu.import("resource://services-common/stringbundle.js");
// Proceed to set up the UI if Sync has already started up.
// Otherwise we'll do it when Sync is firing up.
if (this.weaveService.ready) {
this.initUI();
return;
}
// Sync isn't ready yet, but we can still update the UI with an initial
// state - we haven't called initUI() yet, but that's OK - that's more
// about observers for state changes, and will be called once Sync is
// ready to start sending notifications.
this.updateUI();
Services.obs.addObserver(this, "weave:service:ready", true);
Services.obs.addObserver(this, "quit-application", true);
// Remove the observer if the window is closed before the observer
// was triggered.
window.addEventListener("unload", function onUnload() {
gSyncUI._unloaded = true;
window.removeEventListener("unload", onUnload, false);
Services.obs.removeObserver(gSyncUI, "weave:service:ready");
Services.obs.removeObserver(gSyncUI, "quit-application");
if (Weave.Status.ready) {
gSyncUI._obs.forEach(function(topic) {
Services.obs.removeObserver(gSyncUI, topic);
});
}
}, false);
},
initUI: function SUI_initUI() {
// If this is a browser window?
if (gBrowser) {
this._obs.push("weave:notification:added");
}
this._obs.forEach(function(topic) {
Services.obs.addObserver(this, topic, true);
}, this);
// initial label for the sync buttons.
let broadcaster = document.getElementById("sync-status");
broadcaster.setAttribute("label", this._stringBundle.GetStringFromName("syncnow.label"));
this.maybeMoveSyncedTabsButton();
this.updateUI();
},
// Returns a promise that resolves with true if Sync needs to be configured,
// false otherwise.
_needsSetup() {
// If Sync is configured for FxAccounts then we do that promise-dance.
if (this.weaveService.fxAccountsEnabled) {
return fxAccounts.getSignedInUser().then(user => {
// We want to treat "account needs verification" as "needs setup".
return !(user && user.verified);
});
}
// We are using legacy sync - check that.
let firstSync = "";
try {
firstSync = Services.prefs.getCharPref("services.sync.firstSync");
} catch (e) { }
return Promise.resolve(Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
firstSync == "notReady");
},
// Returns a promise that resolves with true if the user currently signed in
// to Sync needs to be verified, false otherwise.
_needsVerification() {
// For callers who care about the distinction between "needs setup" and
// "needs verification"
if (this.weaveService.fxAccountsEnabled) {
return fxAccounts.getSignedInUser().then(user => {
// If there is no user, they can't be in a "needs verification" state.
if (!user) {
return false;
}
return !user.verified;
});
}
// Otherwise we are configured for legacy Sync, which has no verification
// concept.
return Promise.resolve(false);
},
// Note that we don't show login errors in a notification bar here, but do
// still need to track a login-failed state so the "Tools" menu updates
// with the correct state.
_loginFailed: function () {
// If Sync isn't already ready, we don't want to force it to initialize
// by referencing Weave.Status - and it isn't going to be accurate before
// Sync is ready anyway.
if (!this.weaveService.ready) {
this.log.debug("_loginFailed has sync not ready, so returning false");
return false;
}
this.log.debug("_loginFailed has sync state=${sync}",
{ sync: Weave.Status.login});
return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
},
// Kick off an update of the UI - does *not* return a promise.
updateUI() {
this._promiseUpdateUI().catch(err => {
this.log.error("updateUI failed", err);
})
},
// Updates the UI - returns a promise.
_promiseUpdateUI() {
return this._needsSetup().then(needsSetup => {
if (!gBrowser)
return Promise.resolve();
let loginFailed = this._loginFailed();
// Start off with a clean slate
document.getElementById("sync-reauth-state").hidden = true;
document.getElementById("sync-setup-state").hidden = true;
document.getElementById("sync-syncnow-state").hidden = true;
#ifdef MOZ_SERVICES_CLOUDSYNC
if (CloudSync && CloudSync.ready && CloudSync().adapters.count) {
document.getElementById("sync-syncnow-state").hidden = false;
} else if (loginFailed) {
#else
if (loginFailed) {
#endif
// unhiding this element makes the menubar show the login failure state.
document.getElementById("sync-reauth-state").hidden = false;
} else if (needsSetup) {
document.getElementById("sync-setup-state").hidden = false;
} else {
document.getElementById("sync-syncnow-state").hidden = false;
}
return this._updateSyncButtonsTooltip();
});
},
// Functions called by observers
onActivityStart() {
if (!gBrowser)
return;
this.log.debug("onActivityStart");
clearTimeout(this._syncAnimationTimer);
this._syncStartTime = Date.now();
let broadcaster = document.getElementById("sync-status");
broadcaster.setAttribute("syncstatus", "active");
broadcaster.setAttribute("label", this._stringBundle.GetStringFromName("syncing2.label"));
broadcaster.setAttribute("disabled", "true");
this.updateUI();
},
_updateSyncStatus() {
if (!gBrowser)
return;
let broadcaster = document.getElementById("sync-status");
broadcaster.removeAttribute("syncstatus");
broadcaster.removeAttribute("disabled");
broadcaster.setAttribute("label", this._stringBundle.GetStringFromName("syncnow.label"));
this.updateUI();
},
onActivityStop() {
if (!gBrowser)
return;
this.log.debug("onActivityStop");
let now = Date.now();
let syncDuration = now - this._syncStartTime;
if (syncDuration < MIN_STATUS_ANIMATION_DURATION) {
let animationTime = MIN_STATUS_ANIMATION_DURATION - syncDuration;
clearTimeout(this._syncAnimationTimer);
this._syncAnimationTimer = setTimeout(() => this._updateSyncStatus(), animationTime);
} else {
this._updateSyncStatus();
}
},
onLoginError: function SUI_onLoginError() {
this.log.debug("onLoginError: login=${login}, sync=${sync}", Weave.Status);
// We don't show any login errors here; browser-fxaccounts shows them in
// the hamburger menu.
this.updateUI();
},
onLogout: function SUI_onLogout() {
this.updateUI();
},
_getAppName: function () {
let brand = new StringBundle("chrome://branding/locale/brand.properties");
return brand.get("brandShortName");
},
// Commands
// doSync forces a sync - it *does not* return a promise as it is called
// via the various UI components.
doSync() {
this._needsSetup().then(needsSetup => {
if (!needsSetup) {
setTimeout(() => Weave.Service.errorHandler.syncAndReportErrors(), 0);
}
Services.obs.notifyObservers(null, "cloudsync:user-sync", null);
}).catch(err => {
this.log.error("Failed to force a sync", err);
});
},
// Handle clicking the toolbar button - which either opens the Sync setup
// pages or forces a sync now. Does *not* return a promise as it is called
// via the UI.
handleToolbarButton() {
this._needsSetup().then(needsSetup => {
if (needsSetup || this._loginFailed()) {
this.openSetup();
} else {
this.doSync();
}
}).catch(err => {
this.log.error("Failed to handle toolbar button command", err);
});
},
/**
* Invoke the Sync setup wizard.
*
* @param wizardType
* Indicates type of wizard to launch:
* null -- regular set up wizard
* "pair" -- pair a device first
* "reset" -- reset sync
* @param entryPoint
* Indicates the entrypoint from where this method was called.
*/
openSetup: function SUI_openSetup(wizardType, entryPoint = "syncbutton") {
if (this.weaveService.fxAccountsEnabled) {
this.openPrefs(entryPoint);
} else {
let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
if (win)
win.focus();
else {
window.openDialog("chrome://browser/content/sync/setup.xul",
"weaveSetup", "centerscreen,chrome,resizable=no",
wizardType);
}
}
},
// Open the legacy-sync device pairing UI. Note used for FxA Sync.
openAddDevice: function () {
if (!Weave.Utils.ensureMPUnlocked())
return;
let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
if (win)
win.focus();
else
window.openDialog("chrome://browser/content/sync/addDevice.xul",
"syncAddDevice", "centerscreen,chrome,resizable=no");
},
openPrefs: function (entryPoint) {
openPreferences("paneSync", { urlParams: { entrypoint: entryPoint } });
},
openSignInAgainPage: function (entryPoint = "syncbutton") {
gFxAccounts.openSignInAgainPage(entryPoint);
},
openSyncedTabsPanel() {
let placement = CustomizableUI.getPlacementOfWidget("sync-button");
let area = placement ? placement.area : CustomizableUI.AREA_NAVBAR;
let anchor = document.getElementById("sync-button") ||
document.getElementById("PanelUI-menu-button");
if (area == CustomizableUI.AREA_PANEL) {
// The button is in the panel, so we need to show the panel UI, then our
// subview.
PanelUI.show().then(() => {
PanelUI.showSubView("PanelUI-remotetabs", anchor, area);
}).catch(Cu.reportError);
} else {
// It is placed somewhere else - just try and show it.
PanelUI.showSubView("PanelUI-remotetabs", anchor, area);
}
},
/* After Sync is initialized we perform a once-only check for the sync
button being in "customize purgatory" and if so, move it to the panel.
This is done primarily for profiles created before SyncedTabs landed,
where the button defaulted to being in that purgatory.
We use a preference to ensure we only do it once, so people can still
customize it away and have it stick.
*/
maybeMoveSyncedTabsButton() {
const prefName = "browser.migrated-sync-button";
let migrated = false;
try {
migrated = Services.prefs.getBoolPref(prefName);
} catch (_) {}
if (migrated) {
return;
}
if (!CustomizableUI.getPlacementOfWidget("sync-button")) {
CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
}
Services.prefs.setBoolPref(prefName, true);
},
/* Update the tooltip for the sync-status broadcaster (which will update the
Sync Toolbar button and the Sync spinner in the FxA hamburger area.)
If Sync is configured, the tooltip is when the last sync occurred,
otherwise the tooltip reflects the fact that Sync needs to be
(re-)configured.
*/
_updateSyncButtonsTooltip: Task.async(function* () {
if (!gBrowser)
return;
let email;
try {
email = Services.prefs.getCharPref("services.sync.username");
} catch (ex) {}
let needsSetup = yield this._needsSetup();
let needsVerification = yield this._needsVerification();
let loginFailed = this._loginFailed();
// This is a little messy as the Sync buttons are 1/2 Sync related and
// 1/2 FxA related - so for some strings we use Sync strings, but for
// others we reach into gFxAccounts for strings.
let tooltiptext;
if (needsVerification) {
// "needs verification"
tooltiptext = gFxAccounts.strings.formatStringFromName("verifyDescription", [email], 1);
} else if (needsSetup) {
// "needs setup".
tooltiptext = this._stringBundle.GetStringFromName("signInToSync.description");
} else if (loginFailed) {
// "need to reconnect/re-enter your password"
tooltiptext = gFxAccounts.strings.formatStringFromName("reconnectDescription", [email], 1);
} else {
// Sync appears configured - format the "last synced at" time.
try {
let lastSync = new Date(Services.prefs.getCharPref("services.sync.lastSync"));
tooltiptext = this.formatLastSyncDate(lastSync);
}
catch (e) {
// pref doesn't exist (which will be the case until we've seen the
// first successful sync) or is invalid (which should be impossible!)
// Just leave tooltiptext as the empty string in these cases, which
// will cause the tooltip to be removed below.
}
}
// We've done all our promise-y work and ready to update the UI - make
// sure it hasn't been torn down since we started.
if (!gBrowser)
return;
let broadcaster = document.getElementById("sync-status");
if (broadcaster) {
if (tooltiptext) {
broadcaster.setAttribute("tooltiptext", tooltiptext);
} else {
broadcaster.removeAttribute("tooltiptext");
}
}
}),
formatLastSyncDate: function(date) {
let dateFormat;
let sixDaysAgo = (() => {
let date = new Date();
date.setDate(date.getDate() - 6);
date.setHours(0, 0, 0, 0);
return date;
})();
// It may be confusing for the user to see "Last Sync: Monday" when the last sync was a indeed a Monday but 3 weeks ago
if (date < sixDaysAgo) {
dateFormat = {month: 'long', day: 'numeric'};
} else {
dateFormat = {weekday: 'long', hour: 'numeric', minute: 'numeric'};
}
let lastSyncDateString = date.toLocaleDateString(undefined, dateFormat);
return this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDateString], 1);
},
onClientsSynced: function() {
let broadcaster = document.getElementById("sync-syncnow-state");
if (broadcaster) {
if (Weave.Service.clientsEngine.stats.numClients > 1) {
broadcaster.setAttribute("devices-status", "multi");
} else {
broadcaster.setAttribute("devices-status", "single");
}
}
},
observe: function SUI_observe(subject, topic, data) {
this.log.debug("observed", topic);
if (this._unloaded) {
Cu.reportError("SyncUI observer called after unload: " + topic);
return;
}
// Unwrap, just like Svc.Obs, but without pulling in that dependency.
if (subject && typeof subject == "object" &&
("wrappedJSObject" in subject) &&
("observersModuleSubjectWrapper" in subject.wrappedJSObject)) {
subject = subject.wrappedJSObject.object;
}
// First handle "activity" only.
switch (topic) {
case "weave:service:sync:start":
this.onActivityStart();
break;
case "weave:service:sync:finish":
case "weave:service:sync:error":
this.onActivityStop();
break;
}
// Now non-activity state (eg, enabled, errors, etc)
// Note that sync uses the ":ui:" notifications for errors because sync.
switch (topic) {
case "weave:ui:sync:finish":
// Do nothing.
break;
case "weave:ui:sync:error":
case "weave:service:setup-complete":
case "weave:service:login:finish":
case "weave:service:login:start":
case "weave:service:start-over":
this.updateUI();
break;
case "weave:ui:login:error":
case "weave:service:login:error":
this.onLoginError();
break;
case "weave:service:logout:finish":
this.onLogout();
break;
case "weave:service:start-over:finish":
this.updateUI();
break;
case "weave:service:ready":
this.initUI();
break;
case "weave:notification:added":
this.initNotifications();
break;
case "weave:engine:sync:finish":
if (data != "clients") {
return;
}
this.onClientsSynced();
break;
case "quit-application":
// Stop the animation timer on shutdown, since we can't update the UI
// after this.
clearTimeout(this._syncAnimationTimer);
break;
}
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIObserver,
Ci.nsISupportsWeakReference
])
};
XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
// XXXzpao these strings should probably be moved from /services to /browser... (bug 583381)
// but for now just make it work
return Cc["@mozilla.org/intl/stringbundle;1"].
getService(Ci.nsIStringBundleService).
createBundle("chrome://weave/locale/services/sync.properties");
});
XPCOMUtils.defineLazyGetter(gSyncUI, "log", function() {
return Log.repository.getLogger("browserwindow.syncui");
});
XPCOMUtils.defineLazyGetter(gSyncUI, "weaveService", function() {
return Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
});

View file

@ -1,37 +0,0 @@
<?xml version="1.0"?>
# -*- Mode: 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/.
<bindings id="tabPreviews"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="ctrlTab-preview" extends="chrome://global/content/bindings/button.xml#button-base">
<content pack="center">
<xul:stack>
<xul:vbox class="ctrlTab-preview-inner" align="center" pack="center"
xbl:inherits="width=canvaswidth">
<xul:hbox class="tabPreview-canvas" xbl:inherits="style=canvasstyle">
<children/>
</xul:hbox>
<xul:label xbl:inherits="value=label,crop" class="plain"/>
</xul:vbox>
<xul:hbox class="ctrlTab-favicon-container" xbl:inherits="hidden=noicon">
<xul:image class="ctrlTab-favicon" xbl:inherits="src=image"/>
</xul:hbox>
</xul:stack>
</content>
<handlers>
<handler event="mouseover" action="ctrlTab._mouseOverFocus(this);"/>
<handler event="command" action="ctrlTab.pick(this);"/>
<handler event="click" button="1" action="ctrlTab.remove(this);"/>
#ifdef XP_MACOSX
# Control+click is a right click on OS X
<handler event="click" button="2" action="ctrlTab.pick(this);"/>
#endif
</handlers>
</binding>
</bindings>

View file

@ -1,17 +0,0 @@
/* -*- 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/. */
// This file is used as a stub object for platforms which
// don't have CAN_DRAW_IN_TITLEBAR defined.
var TabsInTitlebar = {
init: function () {},
uninit: function () {},
allowedBy: function (condition, allow) {},
updateAppearance: function updateAppearance(aForce) {},
get enabled() {
return document.documentElement.getAttribute("tabsintitlebar") == "true";
},
};

View file

@ -1,307 +0,0 @@
/* -*- 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/. */
// Note: the file browser-tabsintitlebar-stub.js is used instead of
// this one on platforms which don't have CAN_DRAW_IN_TITLEBAR defined.
var TabsInTitlebar = {
init: function () {
if (this._initialized) {
return;
}
this._readPref();
Services.prefs.addObserver(this._prefName, this, false);
// We need to update the appearance of the titlebar when the menu changes
// from the active to the inactive state. We can't, however, rely on
// DOMMenuBarInactive, because the menu fires this event and then removes
// the inactive attribute after an event-loop spin.
//
// Because updating the appearance involves sampling the heights and margins
// of various elements, it's important that the layout be more or less
// settled before updating the titlebar. So instead of listening to
// DOMMenuBarActive and DOMMenuBarInactive, we use a MutationObserver to
// watch the "invalid" attribute directly.
let menu = document.getElementById("toolbar-menubar");
this._menuObserver = new MutationObserver(this._onMenuMutate);
this._menuObserver.observe(menu, {attributes: true});
this.onAreaReset = function(aArea) {
if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
this._update(true);
};
this.onWidgetAdded = this.onWidgetRemoved = function(aWidgetId, aArea) {
if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
this._update(true);
};
CustomizableUI.addListener(this);
addEventListener("resolutionchange", this, false);
this._initialized = true;
if (this._updateOnInit) {
// We don't need to call this with 'true', even if original calls
// (before init()) did, because this will be the first call and so
// we will update anyway.
this._update();
}
},
allowedBy: function (condition, allow) {
if (allow) {
if (condition in this._disallowed) {
delete this._disallowed[condition];
this._update(true);
}
} else if (!(condition in this._disallowed)) {
this._disallowed[condition] = null;
this._update(true);
}
},
updateAppearance: function updateAppearance(aForce) {
this._update(aForce);
},
get enabled() {
return document.documentElement.getAttribute("tabsintitlebar") == "true";
},
observe: function (subject, topic, data) {
if (topic == "nsPref:changed")
this._readPref();
},
handleEvent: function (aEvent) {
if (aEvent.type == "resolutionchange" && aEvent.target == window) {
this._update(true);
}
},
_onMenuMutate: function (aMutations) {
for (let mutation of aMutations) {
if (mutation.attributeName == "inactive" ||
mutation.attributeName == "autohide") {
TabsInTitlebar._update(true);
return;
}
}
},
_initialized: false,
_updateOnInit: false,
_disallowed: {},
_prefName: "browser.tabs.drawInTitlebar",
_lastSizeMode: null,
_readPref: function () {
this.allowedBy("pref",
Services.prefs.getBoolPref(this._prefName));
},
_update: function (aForce=false) {
let $ = id => document.getElementById(id);
let rect = ele => ele.getBoundingClientRect();
let verticalMargins = cstyle => parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);
if (window.fullScreen)
return;
// In some edgecases it is possible for this to fire before we've initialized.
// Don't run now, but don't forget to run it when we do initialize.
if (!this._initialized) {
this._updateOnInit = true;
return;
}
if (!aForce) {
// _update is called on resize events, because the window is not ready
// after sizemode events. However, we only care about the event when the
// sizemode is different from the last time we updated the appearance of
// the tabs in the titlebar.
let sizemode = document.documentElement.getAttribute("sizemode");
if (this._lastSizeMode == sizemode) {
return;
}
let oldSizeMode = this._lastSizeMode;
this._lastSizeMode = sizemode;
// Don't update right now if we are leaving fullscreen, since the UI is
// still changing in the consequent "fullscreen" event. Code there will
// call this function again when everything is ready.
// See browser-fullScreen.js: FullScreen.toggle and bug 1173768.
if (oldSizeMode == "fullscreen") {
return;
}
}
let allowed = (Object.keys(this._disallowed)).length == 0;
let titlebar = $("titlebar");
let titlebarContent = $("titlebar-content");
let menubar = $("toolbar-menubar");
if (allowed) {
// We set the tabsintitlebar attribute first so that our CSS for
// tabsintitlebar manifests before we do our measurements.
document.documentElement.setAttribute("tabsintitlebar", "true");
updateTitlebarDisplay();
// Try to avoid reflows in this code by calculating dimensions first and
// then later set the properties affecting layout together in a batch.
// Get the full height of the tabs toolbar:
let tabsToolbar = $("TabsToolbar");
let tabsStyles = window.getComputedStyle(tabsToolbar);
let fullTabsHeight = rect(tabsToolbar).height + verticalMargins(tabsStyles);
// Buttons first:
let captionButtonsBoxWidth = rect($("titlebar-buttonbox-container")).width;
let secondaryButtonsWidth, menuHeight, fullMenuHeight, menuStyles;
if (AppConstants.platform == "macosx") {
secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
// No need to look up the menubar stuff on OS X:
menuHeight = 0;
fullMenuHeight = 0;
} else {
// Otherwise, get the height and margins separately for the menubar
menuHeight = rect(menubar).height;
menuStyles = window.getComputedStyle(menubar);
fullMenuHeight = verticalMargins(menuStyles) + menuHeight;
}
// And get the height of what's in the titlebar:
let titlebarContentHeight = rect(titlebarContent).height;
// Begin setting CSS properties which will cause a reflow
// If the menubar is around (menuHeight is non-zero), try to adjust
// its full height (i.e. including margins) to match the titlebar,
// by changing the menubar's bottom padding
if (menuHeight) {
// Calculate the difference between the titlebar's height and that of the menubar
let menuTitlebarDelta = titlebarContentHeight - fullMenuHeight;
let paddingBottom;
// The titlebar is bigger:
if (menuTitlebarDelta > 0) {
fullMenuHeight += menuTitlebarDelta;
// If there is already padding on the menubar, we need to add that
// to the difference so the total padding is correct:
if ((paddingBottom = menuStyles.paddingBottom)) {
menuTitlebarDelta += parseFloat(paddingBottom);
}
menubar.style.paddingBottom = menuTitlebarDelta + "px";
// The menubar is bigger, but has bottom padding we can remove:
} else if (menuTitlebarDelta < 0 && (paddingBottom = menuStyles.paddingBottom)) {
let existingPadding = parseFloat(paddingBottom);
// menuTitlebarDelta is negative; work out what's left, but don't set negative padding:
let desiredPadding = Math.max(0, existingPadding + menuTitlebarDelta);
menubar.style.paddingBottom = desiredPadding + "px";
// We've changed the menu height now:
fullMenuHeight += desiredPadding - existingPadding;
}
}
// Next, we calculate how much we need to stretch the titlebar down to
// go all the way to the bottom of the tab strip, if necessary.
let tabAndMenuHeight = fullTabsHeight + fullMenuHeight;
if (tabAndMenuHeight > titlebarContentHeight) {
// We need to increase the titlebar content's outer height (ie including margins)
// to match the tab and menu height:
let extraMargin = tabAndMenuHeight - titlebarContentHeight;
if (AppConstants.platform != "macosx") {
titlebarContent.style.marginBottom = extraMargin + "px";
}
titlebarContentHeight += extraMargin;
} else {
titlebarContent.style.removeProperty("margin-bottom");
}
// Then add a negative margin to the titlebar, so that the following elements
// will overlap it by the lesser of the titlebar height or the tabstrip+menu.
let minTitlebarOrTabsHeight = Math.min(titlebarContentHeight, tabAndMenuHeight);
titlebar.style.marginBottom = "-" + minTitlebarOrTabsHeight + "px";
// Finally, size the placeholders:
if (AppConstants.platform == "macosx") {
this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
}
this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth);
} else {
document.documentElement.removeAttribute("tabsintitlebar");
updateTitlebarDisplay();
if (AppConstants.platform == "macosx") {
let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
}
// Reset the margins and padding that might have been modified:
titlebarContent.style.marginTop = "";
titlebarContent.style.marginBottom = "";
titlebar.style.marginBottom = "";
menubar.style.paddingBottom = "";
}
ToolbarIconColor.inferFromText();
if (CustomizationHandler.isCustomizing()) {
gCustomizeMode.updateLWTStyling();
}
},
_sizePlaceholder: function (type, width) {
Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='"+ type +"']"),
function (node) { node.width = width; });
},
uninit: function () {
this._initialized = false;
removeEventListener("resolutionchange", this);
Services.prefs.removeObserver(this._prefName, this);
this._menuObserver.disconnect();
CustomizableUI.removeListener(this);
}
};
function updateTitlebarDisplay() {
if (AppConstants.platform == "macosx") {
// OS X and the other platforms differ enough to necessitate this kind of
// special-casing. Like the other platforms where we CAN_DRAW_IN_TITLEBAR,
// we draw in the OS X titlebar when putting the tabs up there. However, OS X
// also draws in the titlebar when a lightweight theme is applied, regardless
// of whether or not the tabs are drawn in the titlebar.
if (TabsInTitlebar.enabled) {
document.documentElement.setAttribute("chromemargin-nonlwtheme", "0,-1,-1,-1");
document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
document.documentElement.removeAttribute("drawtitle");
} else {
// We set chromemargin-nonlwtheme to "" instead of removing it as a way of
// making sure that LightweightThemeConsumer doesn't take it upon itself to
// detect this value again if and when we do a lwtheme state change.
document.documentElement.setAttribute("chromemargin-nonlwtheme", "");
let isCustomizing = document.documentElement.hasAttribute("customizing");
let hasLWTheme = document.documentElement.hasAttribute("lwtheme");
let isPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
if ((!hasLWTheme || isCustomizing) && !isPrivate) {
document.documentElement.removeAttribute("chromemargin");
}
document.documentElement.setAttribute("drawtitle", "true");
}
} else if (TabsInTitlebar.enabled) {
// not OS X
document.documentElement.setAttribute("chromemargin", "0,2,2,2");
} else {
document.documentElement.removeAttribute("chromemargin");
}
}
function onTitlebarMaxClick() {
if (window.windowState == window.STATE_MAXIMIZED)
window.restore();
else
window.maximize();
}

View file

@ -1,142 +0,0 @@
/* 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/. */
/**
* Keeps thumbnails of open web pages up-to-date.
*/
var gBrowserThumbnails = {
/**
* Pref that controls whether we can store SSL content on disk
*/
PREF_DISK_CACHE_SSL: "browser.cache.disk_cache_ssl",
_captureDelayMS: 1000,
/**
* Used to keep track of disk_cache_ssl preference
*/
_sslDiskCacheEnabled: null,
/**
* Map of capture() timeouts assigned to their browsers.
*/
_timeouts: null,
/**
* List of tab events we want to listen for.
*/
_tabEvents: ["TabClose", "TabSelect"],
init: function Thumbnails_init() {
PageThumbs.addExpirationFilter(this);
gBrowser.addTabsProgressListener(this);
Services.prefs.addObserver(this.PREF_DISK_CACHE_SSL, this, false);
this._sslDiskCacheEnabled =
Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
this._tabEvents.forEach(function (aEvent) {
gBrowser.tabContainer.addEventListener(aEvent, this, false);
}, this);
this._timeouts = new WeakMap();
},
uninit: function Thumbnails_uninit() {
PageThumbs.removeExpirationFilter(this);
gBrowser.removeTabsProgressListener(this);
Services.prefs.removeObserver(this.PREF_DISK_CACHE_SSL, this);
this._tabEvents.forEach(function (aEvent) {
gBrowser.tabContainer.removeEventListener(aEvent, this, false);
}, this);
},
handleEvent: function Thumbnails_handleEvent(aEvent) {
switch (aEvent.type) {
case "scroll":
let browser = aEvent.currentTarget;
if (this._timeouts.has(browser))
this._delayedCapture(browser);
break;
case "TabSelect":
this._delayedCapture(aEvent.target.linkedBrowser);
break;
case "TabClose": {
this._clearTimeout(aEvent.target.linkedBrowser);
break;
}
}
},
observe: function Thumbnails_observe() {
this._sslDiskCacheEnabled =
Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
},
filterForThumbnailExpiration:
function Thumbnails_filterForThumbnailExpiration(aCallback) {
aCallback(this._topSiteURLs);
},
/**
* State change progress listener for all tabs.
*/
onStateChange: function Thumbnails_onStateChange(aBrowser, aWebProgress,
aRequest, aStateFlags, aStatus) {
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)
this._delayedCapture(aBrowser);
},
_capture: function Thumbnails_capture(aBrowser) {
// Only capture about:newtab top sites.
if (this._topSiteURLs.indexOf(aBrowser.currentURI.spec) == -1)
return;
this._shouldCapture(aBrowser, function (aResult) {
if (aResult) {
PageThumbs.captureAndStoreIfStale(aBrowser);
}
});
},
_delayedCapture: function Thumbnails_delayedCapture(aBrowser) {
if (this._timeouts.has(aBrowser))
clearTimeout(this._timeouts.get(aBrowser));
else
aBrowser.addEventListener("scroll", this, true);
let timeout = setTimeout(function () {
this._clearTimeout(aBrowser);
this._capture(aBrowser);
}.bind(this), this._captureDelayMS);
this._timeouts.set(aBrowser, timeout);
},
_shouldCapture: function Thumbnails_shouldCapture(aBrowser, aCallback) {
// Capture only if it's the currently selected tab.
if (aBrowser != gBrowser.selectedBrowser) {
aCallback(false);
return;
}
PageThumbs.shouldStoreThumbnail(aBrowser, aCallback);
},
get _topSiteURLs() {
return NewTabUtils.links.getLinks().reduce((urls, link) => {
if (link)
urls.push(link.url);
return urls;
}, []);
},
_clearTimeout: function Thumbnails_clearTimeout(aBrowser) {
if (this._timeouts.has(aBrowser)) {
aBrowser.removeEventListener("scroll", this, false);
clearTimeout(this._timeouts.get(aBrowser));
this._timeouts.delete(aBrowser);
}
}
};

View file

@ -1,177 +0,0 @@
/* 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 TrackingProtection = {
// If the user ignores the doorhanger, we stop showing it after some time.
PREF_ENABLED_GLOBALLY: "privacy.trackingprotection.enabled",
PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled",
enabledGlobally: false,
enabledInPrivateWindows: false,
container: null,
content: null,
icon: null,
activeTooltipText: null,
disabledTooltipText: null,
init() {
let $ = selector => document.querySelector(selector);
this.container = $("#tracking-protection-container");
this.content = $("#tracking-protection-content");
this.icon = $("#tracking-protection-icon");
this.updateEnabled();
Services.prefs.addObserver(this.PREF_ENABLED_GLOBALLY, this, false);
Services.prefs.addObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this, false);
this.activeTooltipText =
gNavigatorBundle.getString("trackingProtection.icon.activeTooltip");
this.disabledTooltipText =
gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip");
this.enabledHistogramAdd(this.enabledGlobally);
this.disabledPBMHistogramAdd(!this.enabledInPrivateWindows);
},
uninit() {
Services.prefs.removeObserver(this.PREF_ENABLED_GLOBALLY, this);
Services.prefs.removeObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this);
},
observe() {
this.updateEnabled();
},
get enabled() {
return this.enabledGlobally ||
(this.enabledInPrivateWindows &&
PrivateBrowsingUtils.isWindowPrivate(window));
},
updateEnabled() {
this.enabledGlobally =
Services.prefs.getBoolPref(this.PREF_ENABLED_GLOBALLY);
this.enabledInPrivateWindows =
Services.prefs.getBoolPref(this.PREF_ENABLED_IN_PRIVATE_WINDOWS);
this.container.hidden = !this.enabled;
},
enabledHistogramAdd(value) {
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
return;
}
Services.telemetry.getHistogramById("TRACKING_PROTECTION_ENABLED").add(value);
},
disabledPBMHistogramAdd(value) {
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
return;
}
Services.telemetry.getHistogramById("TRACKING_PROTECTION_PBM_DISABLED").add(value);
},
eventsHistogramAdd(value) {
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
return;
}
Services.telemetry.getHistogramById("TRACKING_PROTECTION_EVENTS").add(value);
},
shieldHistogramAdd(value) {
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
return;
}
Services.telemetry.getHistogramById("TRACKING_PROTECTION_SHIELD").add(value);
},
onSecurityChange(state, isSimulated) {
if (!this.enabled) {
return;
}
// Only animate the shield if the event was not fired directly from
// the tabbrowser (due to a browser change).
if (isSimulated) {
this.icon.removeAttribute("animate");
} else {
this.icon.setAttribute("animate", "true");
}
let isBlocking = state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT;
let isAllowing = state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT;
if (isBlocking) {
this.icon.setAttribute("tooltiptext", this.activeTooltipText);
this.icon.setAttribute("state", "blocked-tracking-content");
this.content.setAttribute("state", "blocked-tracking-content");
this.shieldHistogramAdd(2);
} else if (isAllowing) {
this.icon.setAttribute("tooltiptext", this.disabledTooltipText);
this.icon.setAttribute("state", "loaded-tracking-content");
this.content.setAttribute("state", "loaded-tracking-content");
this.shieldHistogramAdd(1);
} else {
this.icon.removeAttribute("tooltiptext");
this.icon.removeAttribute("state");
this.content.removeAttribute("state");
// We didn't show the shield
this.shieldHistogramAdd(0);
}
// Telemetry for state change.
this.eventsHistogramAdd(0);
},
disableForCurrentPage() {
// Convert document URI into the format used by
// nsChannelClassifier::ShouldEnableTrackingProtection.
// Any scheme turned into https is correct.
let normalizedUrl = Services.io.newURI(
"https://" + gBrowser.selectedBrowser.currentURI.hostPort,
null, null);
// Add the current host in the 'trackingprotection' consumer of
// the permission manager using a normalized URI. This effectively
// places this host on the tracking protection allowlist.
if (PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser)) {
PrivateBrowsingUtils.addToTrackingAllowlist(normalizedUrl);
} else {
Services.perms.add(normalizedUrl,
"trackingprotection", Services.perms.ALLOW_ACTION);
}
// Telemetry for disable protection.
this.eventsHistogramAdd(1);
// Hide the control center.
document.getElementById("identity-popup").hidePopup();
BrowserReload();
},
enableForCurrentPage() {
// Remove the current host from the 'trackingprotection' consumer
// of the permission manager. This effectively removes this host
// from the tracking protection allowlist.
let normalizedUrl = Services.io.newURI(
"https://" + gBrowser.selectedBrowser.currentURI.hostPort,
null, null);
if (PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser)) {
PrivateBrowsingUtils.removeFromTrackingAllowlist(normalizedUrl);
} else {
Services.perms.remove(normalizedUrl, "trackingprotection");
}
// Telemetry for enable protection.
this.eventsHistogramAdd(2);
// Hide the control center.
document.getElementById("identity-popup").hidePopup();
BrowserReload();
},
};

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,12 +0,0 @@
<stringbundleset id="stringbundleset"/>
<commandset id="mainCommandSet"/>
<commandset id="baseMenuCommandSet"/>
<commandset id="placesCommands"/>
<broadcasterset id="mainBroadcasterSet"/>
<keyset id="mainKeyset"/>
<keyset id="baseMenuKeyset"/>
<menubar id="main-menubar"/>

File diff suppressed because it is too large Load diff

View file

@ -1,161 +0,0 @@
/* 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/. */
.contentSearchSuggestionTable {
background-color: hsla(0,0%,100%,.99);
border: 1px solid hsla(0, 0%, 0%, .2);
border-top: none;
box-shadow: 0 5px 10px hsla(0, 0%, 0%, .1);
position: absolute;
left: 0;
z-index: 1001;
-moz-user-select: none;
cursor: default;
}
.contentSearchSuggestionTable:-moz-dir(rtl) {
left: auto;
right: 0;
}
.contentSearchSuggestionsList {
border-bottom: 1px solid hsl(0, 0%, 92%);
width: 100%;
height: 100%;
}
.contentSearchSuggestionTable,
.contentSearchSuggestionsList {
border-spacing: 0;
overflow: hidden;
padding: 0;
margin: 0;
text-align: start;
}
.contentSearchHeaderRow,
.contentSearchSuggestionRow {
margin: 0;
max-width: inherit;
padding: 0;
}
.contentSearchHeaderRow > td > img,
.contentSearchSuggestionRow > td > .historyIcon {
margin-inline-end: 8px;
margin-bottom: -3px;
}
.contentSearchSuggestionTable .historyIcon {
width: 16px;
height: 16px;
display: inline-block;
background-image: url("chrome://browser/skin/search-history-icon.svg#search-history-icon");
}
.contentSearchSuggestionRow.selected > td > .historyIcon {
background-image: url("chrome://browser/skin/search-history-icon.svg#search-history-icon-active");
}
.contentSearchHeader > img {
height: 16px;
width: 16px;
margin: 0;
padding: 0;
}
.contentSearchSuggestionRow.remote > td > .historyIcon {
visibility: hidden;
}
.contentSearchSuggestionRow.selected {
background-color: Highlight;
color: HighlightText;
}
.contentSearchHeader,
.contentSearchSuggestionEntry {
margin: 0;
max-width: inherit;
overflow: hidden;
padding: 4px 10px;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 75%;
}
.contentSearchHeader {
background-color: hsl(0, 0%, 97%);
color: #666;
border-bottom: 1px solid hsl(0, 0%, 92%);
}
.contentSearchSuggestionsContainer {
margin: 0;
padding: 0;
border-spacing: 0;
width: 100%;
}
.contentSearchSearchWithHeaderSearchText {
white-space: pre;
font-weight: bold;
}
.contentSearchOneOffItem {
-moz-appearance: none;
height: 32px;
margin: 0;
padding: 0;
border: none;
background: none;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAWCAYAAAABxvaqAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gofECQNNVW2/AAAABBJREFUGFdjOHPmzH8GehEA/KpKg9YTf4AAAAAASUVORK5CYII=');
background-repeat: no-repeat;
background-position: right center;
}
.contentSearchOneOffItem:-moz-dir(rtl) {
background-position: left center;
}
.contentSearchOneOffItem > img {
width: 16px;
height: 16px;
margin-bottom: -2px;
}
.contentSearchOneOffItem:not(.last-row) {
border-bottom: 1px solid hsl(0, 0%, 92%);
}
.contentSearchOneOffItem.end-of-row {
background-image: none;
}
.contentSearchOneOffItem.selected {
background-color: Highlight;
background-image: none;
}
.contentSearchOneOffsTable {
width: 100%;
}
.contentSearchSettingsButton {
margin: 0;
padding: 0;
height: 32px;
border: none;
border-top: 1px solid hsla(0, 0%, 0%, .08);
text-align: center;
width: 100%;
}
.contentSearchSettingsButton.selected {
background-color: hsl(0, 0%, 90%);
}
.contentSearchSettingsButton:active {
background-color: hsl(0, 0%, 85%);
}

View file

@ -1,915 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.ContentSearchUIController = (function () {
const MAX_DISPLAYED_SUGGESTIONS = 6;
const SUGGESTION_ID_PREFIX = "searchSuggestion";
const ONE_OFF_ID_PREFIX = "oneOff";
const HTML_NS = "http://www.w3.org/1999/xhtml";
/**
* Creates a new object that manages search suggestions and their UI for a text
* box.
*
* The UI consists of an html:table that's inserted into the DOM after the given
* text box and styled so that it appears as a dropdown below the text box.
*
* @param inputElement
* Search suggestions will be based on the text in this text box.
* Assumed to be an html:input. xul:textbox is untested but might work.
* @param tableParent
* The suggestion table is appended as a child to this element. Since
* the table is absolutely positioned and its top and left values are set
* to be relative to the top and left of the page, either the parent and
* all its ancestors should not be positioned elements (i.e., their
* positions should be "static"), or the parent's position should be the
* top left of the page.
* @param healthReportKey
* This will be sent with the search data for FHR to record the search.
* @param searchPurpose
* Sent with search data, see nsISearchEngine.getSubmission.
* @param idPrefix
* The IDs of elements created by the object will be prefixed with this
* string.
*/
function ContentSearchUIController(inputElement, tableParent, healthReportKey,
searchPurpose, idPrefix="") {
this.input = inputElement;
this._idPrefix = idPrefix;
this._healthReportKey = healthReportKey;
this._searchPurpose = searchPurpose;
let tableID = idPrefix + "searchSuggestionTable";
this.input.autocomplete = "off";
this.input.setAttribute("aria-autocomplete", "true");
this.input.setAttribute("aria-controls", tableID);
tableParent.appendChild(this._makeTable(tableID));
this.input.addEventListener("keypress", this);
this.input.addEventListener("input", this);
this.input.addEventListener("focus", this);
this.input.addEventListener("blur", this);
window.addEventListener("ContentSearchService", this);
this._stickyInputValue = "";
this._hideSuggestions();
this._getSearchEngines();
this._getStrings();
}
ContentSearchUIController.prototype = {
// The timeout (ms) of the remote suggestions. Corresponds to
// SearchSuggestionController.remoteTimeout. Uses
// SearchSuggestionController's default timeout if falsey.
remoteTimeout: undefined,
_oneOffButtons: [],
// Setting up the one off buttons causes an uninterruptible reflow. If we
// receive the list of engines while the newtab page is loading, this reflow
// may regress performance - so we set this flag and only set up the buttons
// if it's set when the suggestions table is actually opened.
_pendingOneOffRefresh: undefined,
get defaultEngine() {
return this._defaultEngine;
},
set defaultEngine(engine) {
if (this._defaultEngine && this._defaultEngine.icon) {
URL.revokeObjectURL(this._defaultEngine.icon);
}
let icon;
if (engine.iconBuffer) {
icon = this._getFaviconURIFromBuffer(engine.iconBuffer);
}
else {
icon = this._getImageURIForCurrentResolution(
"chrome://mozapps/skin/places/defaultFavicon.png");
}
this._defaultEngine = {
name: engine.name,
icon: icon,
};
this._updateDefaultEngineHeader();
if (engine && document.activeElement == this.input) {
this._speculativeConnect();
}
},
get engines() {
return this._engines;
},
set engines(val) {
this._engines = val;
this._pendingOneOffRefresh = true;
},
// The selectedIndex is the index of the element with the "selected" class in
// the list obtained by concatenating the suggestion rows, one-off buttons, and
// search settings button.
get selectedIndex() {
let allElts = [...this._suggestionsList.children,
...this._oneOffButtons,
document.getElementById("contentSearchSettingsButton")];
for (let i = 0; i < allElts.length; ++i) {
let elt = allElts[i];
if (elt.classList.contains("selected")) {
return i;
}
}
return -1;
},
set selectedIndex(idx) {
// Update the table's rows, and the input when there is a selection.
this._table.removeAttribute("aria-activedescendant");
this.input.removeAttribute("aria-activedescendant");
let allElts = [...this._suggestionsList.children,
...this._oneOffButtons,
document.getElementById("contentSearchSettingsButton")];
// If we are selecting a suggestion and a one-off is selected, don't deselect it.
let excludeIndex = idx < this.numSuggestions && this.selectedButtonIndex > -1 ?
this.numSuggestions + this.selectedButtonIndex : -1;
for (let i = 0; i < allElts.length; ++i) {
let elt = allElts[i];
let ariaSelectedElt = i < this.numSuggestions ? elt.firstChild : elt;
if (i == idx) {
elt.classList.add("selected");
ariaSelectedElt.setAttribute("aria-selected", "true");
this.input.setAttribute("aria-activedescendant", ariaSelectedElt.id);
}
else if (i != excludeIndex) {
elt.classList.remove("selected");
ariaSelectedElt.setAttribute("aria-selected", "false");
}
}
},
get selectedButtonIndex() {
let elts = [...this._oneOffButtons,
document.getElementById("contentSearchSettingsButton")];
for (let i = 0; i < elts.length; ++i) {
if (elts[i].classList.contains("selected")) {
return i;
}
}
return -1;
},
set selectedButtonIndex(idx) {
let elts = [...this._oneOffButtons,
document.getElementById("contentSearchSettingsButton")];
for (let i = 0; i < elts.length; ++i) {
let elt = elts[i];
if (i == idx) {
elt.classList.add("selected");
elt.setAttribute("aria-selected", "true");
}
else {
elt.classList.remove("selected");
elt.setAttribute("aria-selected", "false");
}
}
},
get selectedEngineName() {
let selectedElt = this._oneOffsTable.querySelector(".selected");
if (selectedElt) {
return selectedElt.engineName;
}
return this.defaultEngine.name;
},
get numSuggestions() {
return this._suggestionsList.children.length;
},
selectAndUpdateInput: function (idx) {
this.selectedIndex = idx;
let newValue = this.suggestionAtIndex(idx) || this._stickyInputValue;
// Setting the input value when the value has not changed commits the current
// IME composition, which we don't want to do.
if (this.input.value != newValue) {
this.input.value = newValue;
}
this._updateSearchWithHeader();
},
suggestionAtIndex: function (idx) {
let row = this._suggestionsList.children[idx];
return row ? row.textContent : null;
},
deleteSuggestionAtIndex: function (idx) {
// Only form history suggestions can be deleted.
if (this.isFormHistorySuggestionAtIndex(idx)) {
let suggestionStr = this.suggestionAtIndex(idx);
this._sendMsg("RemoveFormHistoryEntry", suggestionStr);
this._suggestionsList.children[idx].remove();
this.selectAndUpdateInput(-1);
}
},
isFormHistorySuggestionAtIndex: function (idx) {
let row = this._suggestionsList.children[idx];
return row && row.classList.contains("formHistory");
},
addInputValueToFormHistory: function () {
this._sendMsg("AddFormHistoryEntry", this.input.value);
},
handleEvent: function (event) {
this["_on" + event.type[0].toUpperCase() + event.type.substr(1)](event);
},
_onCommand: function(aEvent) {
if (this.selectedButtonIndex == this._oneOffButtons.length) {
// Settings button was selected.
this._sendMsg("ManageEngines");
return;
}
this.search(aEvent);
if (aEvent) {
aEvent.preventDefault();
}
},
search: function (aEvent) {
if (!this.defaultEngine) {
return; // Not initialized yet.
}
let searchText = this.input;
let searchTerms;
if (this._table.hidden ||
aEvent.originalTarget.id == "contentSearchDefaultEngineHeader" ||
aEvent instanceof KeyboardEvent) {
searchTerms = searchText.value;
}
else {
searchTerms = this.suggestionAtIndex(this.selectedIndex) || searchText.value;
}
// Send an event that will perform a search and Firefox Health Report will
// record that a search from the healthReportKey passed to the constructor.
let eventData = {
engineName: this.selectedEngineName,
searchString: searchTerms,
healthReportKey: this._healthReportKey,
searchPurpose: this._searchPurpose,
originalEvent: {
shiftKey: aEvent.shiftKey,
ctrlKey: aEvent.ctrlKey,
metaKey: aEvent.metaKey,
altKey: aEvent.altKey,
},
};
if ("button" in aEvent) {
eventData.originalEvent.button = aEvent.button;
}
if (this.suggestionAtIndex(this.selectedIndex)) {
eventData.selection = {
index: this.selectedIndex,
kind: undefined,
};
if (aEvent instanceof MouseEvent) {
eventData.selection.kind = "mouse";
} else if (aEvent instanceof KeyboardEvent) {
eventData.selection.kind = "key";
}
}
this._sendMsg("Search", eventData);
this.addInputValueToFormHistory();
},
_onInput: function () {
if (!this.input.value) {
this._stickyInputValue = "";
this._hideSuggestions();
}
else if (this.input.value != this._stickyInputValue) {
// Only fetch new suggestions if the input value has changed.
this._getSuggestions();
this.selectAndUpdateInput(-1);
}
this._updateSearchWithHeader();
},
_onKeypress: function (event) {
let selectedIndexDelta = 0;
let selectedSuggestionDelta = 0;
let selectedOneOffDelta = 0;
switch (event.keyCode) {
case event.DOM_VK_UP:
if (this._table.hidden) {
return;
}
if (event.getModifierState("Accel")) {
if (event.shiftKey) {
selectedSuggestionDelta = -1;
break;
}
this._cycleCurrentEngine(true);
break;
}
if (event.altKey) {
selectedOneOffDelta = -1;
break;
}
selectedIndexDelta = -1;
break;
case event.DOM_VK_DOWN:
if (this._table.hidden) {
this._getSuggestions();
return;
}
if (event.getModifierState("Accel")) {
if (event.shiftKey) {
selectedSuggestionDelta = 1;
break;
}
this._cycleCurrentEngine(false);
break;
}
if (event.altKey) {
selectedOneOffDelta = 1;
break;
}
selectedIndexDelta = 1;
break;
case event.DOM_VK_TAB:
if (this._table.hidden) {
return;
}
// Shift+tab when either the first or no one-off is selected, as well as
// tab when the settings button is selected, should change focus as normal.
if ((this.selectedButtonIndex <= 0 && event.shiftKey) ||
this.selectedButtonIndex == this._oneOffButtons.length && !event.shiftKey) {
return;
}
selectedOneOffDelta = event.shiftKey ? -1 : 1;
break;
case event.DOM_VK_RIGHT:
// Allow normal caret movement until the caret is at the end of the input.
if (this.input.selectionStart != this.input.selectionEnd ||
this.input.selectionEnd != this.input.value.length) {
return;
}
if (this.numSuggestions && this.selectedIndex >= 0 &&
this.selectedIndex < this.numSuggestions) {
this.input.value = this.suggestionAtIndex(this.selectedIndex);
this.input.setAttribute("selection-index", this.selectedIndex);
this.input.setAttribute("selection-kind", "key");
} else {
// If we didn't select anything, make sure to remove the attributes
// in case they were populated last time.
this.input.removeAttribute("selection-index");
this.input.removeAttribute("selection-kind");
}
this._stickyInputValue = this.input.value;
this._hideSuggestions();
return;
case event.DOM_VK_RETURN:
this._onCommand(event);
return;
case event.DOM_VK_DELETE:
if (this.selectedIndex >= 0) {
this.deleteSuggestionAtIndex(this.selectedIndex);
}
return;
case event.DOM_VK_ESCAPE:
if (!this._table.hidden) {
this._hideSuggestions();
}
return;
default:
return;
}
let currentIndex = this.selectedIndex;
if (selectedIndexDelta) {
let newSelectedIndex = currentIndex + selectedIndexDelta;
if (newSelectedIndex < -1) {
newSelectedIndex = this.numSuggestions + this._oneOffButtons.length;
}
// If are moving up from the first one off, we have to deselect the one off
// manually because the selectedIndex setter tries to exclude the selected
// one-off (which is desirable for accel+shift+up/down).
if (currentIndex == this.numSuggestions && selectedIndexDelta == -1) {
this.selectedButtonIndex = -1;
}
this.selectAndUpdateInput(newSelectedIndex);
}
else if (selectedSuggestionDelta) {
let newSelectedIndex;
if (currentIndex >= this.numSuggestions || currentIndex == -1) {
// No suggestion already selected, select the first/last one appropriately.
newSelectedIndex = selectedSuggestionDelta == 1 ?
0 : this.numSuggestions - 1;
}
else {
newSelectedIndex = currentIndex + selectedSuggestionDelta;
}
if (newSelectedIndex >= this.numSuggestions) {
newSelectedIndex = -1;
}
this.selectAndUpdateInput(newSelectedIndex);
}
else if (selectedOneOffDelta) {
let newSelectedIndex;
let currentButton = this.selectedButtonIndex;
if (currentButton == -1 || currentButton == this._oneOffButtons.length) {
// No one-off already selected, select the first/last one appropriately.
newSelectedIndex = selectedOneOffDelta == 1 ?
0 : this._oneOffButtons.length - 1;
}
else {
newSelectedIndex = currentButton + selectedOneOffDelta;
}
// Allow selection of the settings button via the tab key.
if (newSelectedIndex == this._oneOffButtons.length &&
event.keyCode != event.DOM_VK_TAB) {
newSelectedIndex = -1;
}
this.selectedButtonIndex = newSelectedIndex;
}
// Prevent the input's caret from moving.
event.preventDefault();
},
_currentEngineIndex: -1,
_cycleCurrentEngine: function (aReverse) {
if ((this._currentEngineIndex == this._engines.length - 1 && !aReverse) ||
(this._currentEngineIndex == 0 && aReverse)) {
return;
}
this._currentEngineIndex += aReverse ? -1 : 1;
let engineName = this._engines[this._currentEngineIndex].name;
this._sendMsg("SetCurrentEngine", engineName);
},
_onFocus: function () {
if (this._mousedown) {
return;
}
// When the input box loses focus to something in our table, we refocus it
// immediately. This causes the focus highlight to flicker, so we set a
// custom attribute which consumers should use for focus highlighting. This
// attribute is removed only when we do not immediately refocus the input
// box, thus eliminating flicker.
this.input.setAttribute("keepfocus", "true");
this._speculativeConnect();
},
_onBlur: function () {
if (this._mousedown) {
// At this point, this.input has lost focus, but a new element has not yet
// received it. If we re-focus this.input directly, the new element will
// steal focus immediately, so we queue it instead.
setTimeout(() => this.input.focus(), 0);
return;
}
this.input.removeAttribute("keepfocus");
this._hideSuggestions();
},
_onMousemove: function (event) {
let idx = this._indexOfTableItem(event.target);
if (idx >= this.numSuggestions) {
this.selectedButtonIndex = idx - this.numSuggestions;
return;
}
this.selectedIndex = idx;
},
_onMouseup: function (event) {
if (event.button == 2) {
return;
}
this._onCommand(event);
},
_onMouseout: function (event) {
// We only deselect one-off buttons and the settings button when they are
// moused out.
let idx = this._indexOfTableItem(event.originalTarget);
if (idx >= this.numSuggestions) {
this.selectedButtonIndex = -1;
}
},
_onClick: function (event) {
this._onMouseup(event);
},
_onContentSearchService: function (event) {
let methodName = "_onMsg" + event.detail.type;
if (methodName in this) {
this[methodName](event.detail.data);
}
},
_onMsgFocusInput: function (event) {
this.input.focus();
},
_onMsgSuggestions: function (suggestions) {
// Ignore the suggestions if their search string or engine doesn't match
// ours. Due to the async nature of message passing, this can easily happen
// when the user types quickly.
if (this._stickyInputValue != suggestions.searchString ||
this.defaultEngine.name != suggestions.engineName) {
return;
}
this._clearSuggestionRows();
// Position and size the table.
let { left } = this.input.getBoundingClientRect();
this._table.style.top = this.input.offsetHeight + "px";
this._table.style.minWidth = this.input.offsetWidth + "px";
this._table.style.maxWidth = (window.innerWidth - left - 40) + "px";
// Add the suggestions to the table.
let searchWords =
new Set(suggestions.searchString.trim().toLowerCase().split(/\s+/));
for (let i = 0; i < MAX_DISPLAYED_SUGGESTIONS; i++) {
let type, idx;
if (i < suggestions.formHistory.length) {
[type, idx] = ["formHistory", i];
}
else {
let j = i - suggestions.formHistory.length;
if (j < suggestions.remote.length) {
[type, idx] = ["remote", j];
}
else {
break;
}
}
this._suggestionsList.appendChild(
this._makeTableRow(type, suggestions[type][idx], i, searchWords));
}
if (this._table.hidden) {
this.selectedIndex = -1;
if (this._pendingOneOffRefresh) {
this._setUpOneOffButtons();
delete this._pendingOneOffRefresh;
}
this._currentEngineIndex =
this._engines.findIndex(aEngine => aEngine.name == this.defaultEngine.name);
this._table.hidden = false;
this.input.setAttribute("aria-expanded", "true");
this._originalDefaultEngine = {
name: this.defaultEngine.name,
icon: this.defaultEngine.icon,
};
}
},
_onMsgSuggestionsCancelled: function () {
if (!this._table.hidden) {
this._hideSuggestions();
}
},
_onMsgState: function (state) {
this.engines = state.engines;
// No point updating the default engine (and the header) if there's no change.
if (this.defaultEngine &&
this.defaultEngine.name == state.currentEngine.name &&
this.defaultEngine.icon == state.currentEngine.icon) {
return;
}
this.defaultEngine = state.currentEngine;
},
_onMsgCurrentState: function (state) {
this._onMsgState(state);
},
_onMsgCurrentEngine: function (engine) {
this.defaultEngine = engine;
this._pendingOneOffRefresh = true;
},
_onMsgStrings: function (strings) {
this._strings = strings;
this._updateDefaultEngineHeader();
this._updateSearchWithHeader();
document.getElementById("contentSearchSettingsButton").textContent =
this._strings.searchSettings;
this.input.setAttribute("placeholder", this._strings.searchPlaceholder);
},
_updateDefaultEngineHeader: function () {
let header = document.getElementById("contentSearchDefaultEngineHeader");
header.firstChild.setAttribute("src", this.defaultEngine.icon);
if (!this._strings) {
return;
}
while (header.firstChild.nextSibling) {
header.firstChild.nextSibling.remove();
}
header.appendChild(document.createTextNode(
this._strings.searchHeader.replace("%S", this.defaultEngine.name)));
},
_updateSearchWithHeader: function () {
if (!this._strings) {
return;
}
let searchWithHeader = document.getElementById("contentSearchSearchWithHeader");
if (this.input.value) {
searchWithHeader.innerHTML = this._strings.searchForSomethingWith;
searchWithHeader.querySelector('.contentSearchSearchWithHeaderSearchText').textContent = this.input.value;
} else {
searchWithHeader.textContent = this._strings.searchWithHeader;
}
},
_speculativeConnect: function () {
if (this.defaultEngine) {
this._sendMsg("SpeculativeConnect", this.defaultEngine.name);
}
},
_makeTableRow: function (type, suggestionStr, currentRow, searchWords) {
let row = document.createElementNS(HTML_NS, "tr");
row.dir = "auto";
row.classList.add("contentSearchSuggestionRow");
row.classList.add(type);
row.setAttribute("role", "presentation");
row.addEventListener("mousemove", this);
row.addEventListener("mouseup", this);
let entry = document.createElementNS(HTML_NS, "td");
let img = document.createElementNS(HTML_NS, "div");
img.setAttribute("class", "historyIcon");
entry.appendChild(img);
entry.classList.add("contentSearchSuggestionEntry");
entry.setAttribute("role", "option");
entry.id = this._idPrefix + SUGGESTION_ID_PREFIX + currentRow;
entry.setAttribute("aria-selected", "false");
let suggestionWords = suggestionStr.trim().toLowerCase().split(/\s+/);
for (let i = 0; i < suggestionWords.length; i++) {
let word = suggestionWords[i];
let wordSpan = document.createElementNS(HTML_NS, "span");
if (searchWords.has(word)) {
wordSpan.classList.add("typed");
}
wordSpan.textContent = word;
entry.appendChild(wordSpan);
if (i < suggestionWords.length - 1) {
entry.appendChild(document.createTextNode(" "));
}
}
row.appendChild(entry);
return row;
},
// Converts favicon array buffer into a data URI.
_getFaviconURIFromBuffer: function (buffer) {
let blob = new Blob([buffer]);
return URL.createObjectURL(blob);
},
// Adds "@2x" to the name of the given PNG url for "retina" screens.
_getImageURIForCurrentResolution: function (uri) {
if (window.devicePixelRatio > 1) {
return uri.replace(/\.png$/, "@2x.png");
}
return uri;
},
_getSearchEngines: function () {
this._sendMsg("GetState");
},
_getStrings: function () {
this._sendMsg("GetStrings");
},
_getSuggestions: function () {
this._stickyInputValue = this.input.value;
if (this.defaultEngine) {
this._sendMsg("GetSuggestions", {
engineName: this.defaultEngine.name,
searchString: this.input.value,
remoteTimeout: this.remoteTimeout,
});
}
},
_clearSuggestionRows: function() {
while (this._suggestionsList.firstElementChild) {
this._suggestionsList.firstElementChild.remove();
}
},
_hideSuggestions: function () {
this.input.setAttribute("aria-expanded", "false");
this.selectedIndex = -1;
this.selectedButtonIndex = -1;
this._currentEngineIndex = -1;
this._table.hidden = true;
},
_indexOfTableItem: function (elt) {
if (elt.classList.contains("contentSearchOneOffItem")) {
return this.numSuggestions + this._oneOffButtons.indexOf(elt);
}
if (elt.classList.contains("contentSearchSettingsButton")) {
return this.numSuggestions + this._oneOffButtons.length;
}
while (elt && elt.localName != "tr") {
elt = elt.parentNode;
}
if (!elt) {
throw new Error("Element is not a row");
}
return elt.rowIndex;
},
_makeTable: function (id) {
this._table = document.createElementNS(HTML_NS, "table");
this._table.id = id;
this._table.hidden = true;
this._table.classList.add("contentSearchSuggestionTable");
this._table.setAttribute("role", "presentation");
// When the search input box loses focus, we want to immediately give focus
// back to it if the blur was because the user clicked somewhere in the table.
// onBlur uses the _mousedown flag to detect this.
this._table.addEventListener("mousedown", () => { this._mousedown = true; });
document.addEventListener("mouseup", () => { delete this._mousedown; });
// Deselect the selected element on mouseout if it wasn't a suggestion.
this._table.addEventListener("mouseout", this);
// If a search is loaded in the same tab, ensure the suggestions dropdown
// is hidden immediately when the page starts loading and not when it first
// appears, in order to provide timely feedback to the user.
window.addEventListener("beforeunload", () => { this._hideSuggestions(); });
let headerRow = document.createElementNS(HTML_NS, "tr");
let header = document.createElementNS(HTML_NS, "td");
headerRow.setAttribute("class", "contentSearchHeaderRow");
header.setAttribute("class", "contentSearchHeader");
let iconImg = document.createElementNS(HTML_NS, "img");
header.appendChild(iconImg);
header.id = "contentSearchDefaultEngineHeader";
headerRow.appendChild(header);
headerRow.addEventListener("click", this);
this._table.appendChild(headerRow);
let row = document.createElementNS(HTML_NS, "tr");
row.setAttribute("class", "contentSearchSuggestionsContainer");
let cell = document.createElementNS(HTML_NS, "td");
cell.setAttribute("class", "contentSearchSuggestionsContainer");
this._suggestionsList = document.createElementNS(HTML_NS, "table");
this._suggestionsList.setAttribute("class", "contentSearchSuggestionsList");
cell.appendChild(this._suggestionsList);
row.appendChild(cell);
this._table.appendChild(row);
this._suggestionsList.setAttribute("role", "listbox");
this._oneOffsTable = document.createElementNS(HTML_NS, "table");
this._oneOffsTable.setAttribute("class", "contentSearchOneOffsTable");
this._oneOffsTable.classList.add("contentSearchSuggestionsContainer");
this._oneOffsTable.setAttribute("role", "group");
this._table.appendChild(this._oneOffsTable);
headerRow = document.createElementNS(HTML_NS, "tr");
header = document.createElementNS(HTML_NS, "td");
headerRow.setAttribute("class", "contentSearchHeaderRow");
header.setAttribute("class", "contentSearchHeader");
headerRow.appendChild(header);
header.id = "contentSearchSearchWithHeader";
this._oneOffsTable.appendChild(headerRow);
let button = document.createElementNS(HTML_NS, "button");
button.setAttribute("class", "contentSearchSettingsButton");
button.classList.add("contentSearchHeaderRow");
button.classList.add("contentSearchHeader");
button.id = "contentSearchSettingsButton";
button.addEventListener("click", this);
button.addEventListener("mousemove", this);
this._table.appendChild(button);
return this._table;
},
_setUpOneOffButtons: function () {
// Sometimes we receive a CurrentEngine message from the ContentSearch service
// before we've received a State message - i.e. before we have our engines.
if (!this._engines) {
return;
}
while (this._oneOffsTable.firstChild.nextSibling) {
this._oneOffsTable.firstChild.nextSibling.remove();
}
this._oneOffButtons = [];
let engines = this._engines.filter(aEngine => aEngine.name != this.defaultEngine.name)
.filter(aEngine => !aEngine.hidden);
if (!engines.length) {
this._oneOffsTable.hidden = true;
return;
}
const kDefaultButtonWidth = 49; // 48px + 1px border.
let rowWidth = this.input.offsetWidth - 2; // 2px border.
let enginesPerRow = Math.floor(rowWidth / kDefaultButtonWidth);
let buttonWidth = Math.floor(rowWidth / enginesPerRow);
let row = document.createElementNS(HTML_NS, "tr");
let cell = document.createElementNS(HTML_NS, "td");
row.setAttribute("class", "contentSearchSuggestionsContainer");
cell.setAttribute("class", "contentSearchSuggestionsContainer");
for (let i = 0; i < engines.length; ++i) {
let engine = engines[i];
if (i > 0 && i % enginesPerRow == 0) {
row.appendChild(cell);
this._oneOffsTable.appendChild(row);
row = document.createElementNS(HTML_NS, "tr");
cell = document.createElementNS(HTML_NS, "td");
row.setAttribute("class", "contentSearchSuggestionsContainer");
cell.setAttribute("class", "contentSearchSuggestionsContainer");
}
let button = document.createElementNS(HTML_NS, "button");
button.setAttribute("class", "contentSearchOneOffItem");
let img = document.createElementNS(HTML_NS, "img");
let uri;
if (engine.iconBuffer) {
uri = this._getFaviconURIFromBuffer(engine.iconBuffer);
}
else {
uri = this._getImageURIForCurrentResolution(
"chrome://browser/skin/search-engine-placeholder.png");
}
img.setAttribute("src", uri);
img.addEventListener("load", function imgLoad() {
img.removeEventListener("load", imgLoad);
URL.revokeObjectURL(uri);
});
button.appendChild(img);
button.style.width = buttonWidth + "px";
button.setAttribute("title", engine.name);
button.engineName = engine.name;
button.addEventListener("click", this);
button.addEventListener("mousemove", this);
if (engines.length - i <= enginesPerRow - (i % enginesPerRow)) {
button.classList.add("last-row");
}
if ((i + 1) % enginesPerRow == 0) {
button.classList.add("end-of-row");
}
button.id = ONE_OFF_ID_PREFIX + i;
cell.appendChild(button);
this._oneOffButtons.push(button);
}
row.appendChild(cell);
this._oneOffsTable.appendChild(row);
this._oneOffsTable.hidden = false;
},
_sendMsg: function (type, data=null) {
dispatchEvent(new CustomEvent("ContentSearchClient", {
detail: {
type: type,
data: data,
},
}));
},
};
return ContentSearchUIController;
})();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 751 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 731 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

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