summaryrefslogtreecommitdiffstats
path: root/mobile/android/chrome/content
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/chrome/content')
-rw-r--r--mobile/android/chrome/content/.eslintrc23
-rw-r--r--mobile/android/chrome/content/ActionBarHandler.js731
-rw-r--r--mobile/android/chrome/content/CastingApps.js850
-rw-r--r--mobile/android/chrome/content/ConsoleAPI.js96
-rw-r--r--mobile/android/chrome/content/EmbedRT.js82
-rw-r--r--mobile/android/chrome/content/FeedHandler.js120
-rw-r--r--mobile/android/chrome/content/Feedback.js64
-rw-r--r--mobile/android/chrome/content/FindHelper.js197
-rw-r--r--mobile/android/chrome/content/InputWidgetHelper.js98
-rw-r--r--mobile/android/chrome/content/Linkify.js108
-rw-r--r--mobile/android/chrome/content/MasterPassword.js67
-rw-r--r--mobile/android/chrome/content/MemoryObserver.js88
-rw-r--r--mobile/android/chrome/content/OfflineApps.js77
-rw-r--r--mobile/android/chrome/content/PermissionsHelper.js188
-rw-r--r--mobile/android/chrome/content/PluginHelper.js221
-rw-r--r--mobile/android/chrome/content/PresentationView.js63
-rw-r--r--mobile/android/chrome/content/PresentationView.xul15
-rw-r--r--mobile/android/chrome/content/PrintHelper.js73
-rw-r--r--mobile/android/chrome/content/Reader.js290
-rw-r--r--mobile/android/chrome/content/RemoteDebugger.js355
-rw-r--r--mobile/android/chrome/content/SelectHelper.js161
-rw-r--r--mobile/android/chrome/content/WebcompatReporter.js144
-rw-r--r--mobile/android/chrome/content/WebrtcUI.js302
-rw-r--r--mobile/android/chrome/content/about.js151
-rw-r--r--mobile/android/chrome/content/about.xhtml77
-rw-r--r--mobile/android/chrome/content/aboutAddons.js609
-rw-r--r--mobile/android/chrome/content/aboutAddons.xhtml63
-rw-r--r--mobile/android/chrome/content/aboutCertError.xhtml264
-rw-r--r--mobile/android/chrome/content/aboutDownloads.js373
-rw-r--r--mobile/android/chrome/content/aboutDownloads.xhtml62
-rw-r--r--mobile/android/chrome/content/aboutHealthReport.js192
-rw-r--r--mobile/android/chrome/content/aboutHealthReport.xhtml32
-rw-r--r--mobile/android/chrome/content/aboutHome.xhtml22
-rw-r--r--mobile/android/chrome/content/aboutLogins.js517
-rw-r--r--mobile/android/chrome/content/aboutLogins.xhtml90
-rw-r--r--mobile/android/chrome/content/aboutPrivateBrowsing.js32
-rw-r--r--mobile/android/chrome/content/aboutPrivateBrowsing.xhtml43
-rw-r--r--mobile/android/chrome/content/aboutRights.xhtml95
-rw-r--r--mobile/android/chrome/content/bindings/checkbox.xml75
-rw-r--r--mobile/android/chrome/content/bindings/settings.xml66
-rw-r--r--mobile/android/chrome/content/blockedSite.xhtml195
-rw-r--r--mobile/android/chrome/content/browser.css7
-rw-r--r--mobile/android/chrome/content/browser.js6961
-rw-r--r--mobile/android/chrome/content/browser.xul17
-rw-r--r--mobile/android/chrome/content/config.js673
-rw-r--r--mobile/android/chrome/content/config.xhtml86
-rw-r--r--mobile/android/chrome/content/content.js159
-rw-r--r--mobile/android/chrome/content/geckoview.js32
-rw-r--r--mobile/android/chrome/content/geckoview.xul16
-rw-r--r--mobile/android/chrome/content/healthreport-prefs.js6
-rw-r--r--mobile/android/chrome/content/languages.properties114
-rw-r--r--mobile/android/chrome/content/netError.xhtml406
52 files changed, 0 insertions, 15848 deletions
diff --git a/mobile/android/chrome/content/.eslintrc b/mobile/android/chrome/content/.eslintrc
deleted file mode 100644
index 32513189a..000000000
--- a/mobile/android/chrome/content/.eslintrc
+++ /dev/null
@@ -1,23 +0,0 @@
-globals:
- # TODO: Maybe this should be by file
- BrowserApp: false
- Cc: false
- Ci: false
- Cu: false
- NativeWindow: false
- PageActions: false
- ReaderMode: false
- SimpleServiceDiscovery: false
- TabMirror: false
- MediaPlayerApp: false
- RokuApp: false
- SearchEngines: false
- ConsoleAPI: true
- Point: false
- Rect: false
-
-rules:
- # Disabled stuff
- no-console: 0 # TODO: Can we use console?
- no-cond-assign: 0
- no-fallthrough: 0
diff --git a/mobile/android/chrome/content/ActionBarHandler.js b/mobile/android/chrome/content/ActionBarHandler.js
deleted file mode 100644
index 190021043..000000000
--- a/mobile/android/chrome/content/ActionBarHandler.js
+++ /dev/null
@@ -1,731 +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/. */
-"use strict";
-
-XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm");
-
-const PHONE_REGEX = /^\+?[0-9\s,-.\(\)*#pw]{1,30}$/; // Are we a phone #?
-
-
-/**
- * ActionBarHandler Object and methods. Interface between Gecko Text Selection code
- * (AccessibleCaret, etc) and the Mobile ActionBar UI.
- */
-var ActionBarHandler = {
- // Error codes returned from _init().
- START_TOUCH_ERROR: {
- NO_CONTENT_WINDOW: "No valid content Window found.",
- NONE: "",
- },
-
- _nextSelectionID: 1, // Next available.
- _selectionID: null, // Unique Selection ID, assigned each time we _init().
-
- _boundingClientRect: null, // Current selections boundingClientRect.
- _actionBarActions: null, // Most-recent set of actions sent to ActionBar.
-
- /**
- * Receive and act on AccessibleCarets caret state-change
- * (mozcaretstatechanged) events.
- */
- caretStateChangedHandler: function(e) {
- // Close an open ActionBar, if carets no longer logically visible.
- if (this._selectionID && !e.caretVisible) {
- this._uninit(false);
- return;
- }
-
- if (!this._selectionID && e.collapsed) {
- switch (e.reason) {
- case 'longpressonemptycontent':
- case 'taponcaret':
- // Show ActionBar when long pressing on an empty input or single
- // tapping on the caret.
- this._init(e.boundingClientRect);
- break;
-
- case 'updateposition':
- // Do not show ActionBar when single tapping on an non-empty editable
- // input.
- break;
-
- default:
- break;
- }
- return;
- }
-
- // Open a closed ActionBar if carets actually visible.
- if (!this._selectionID && e.caretVisuallyVisible) {
- this._init(e.boundingClientRect);
- return;
- }
-
- // Else, update an open ActionBar.
- if (this._selectionID) {
- if (!this._selectionHasChanged()) {
- // Still the same active selection.
- if (e.reason == 'presscaret' || e.reason == 'scroll') {
- // boundingClientRect doesn't matter since we are hiding the floating
- // toolbar.
- this._updateVisibility();
- } else {
- // Selection changes update boundingClientRect.
- this._boundingClientRect = e.boundingClientRect;
- let forceUpdate = e.reason == 'updateposition' || e.reason == 'releasecaret';
- this._sendActionBarActions(forceUpdate);
- }
- } else {
- // We've started a new selection entirely.
- this._uninit(false);
- this._init(e.boundingClientRect);
- }
- }
- },
-
- /**
- * ActionBarHandler notification observers.
- */
- observe: function(subject, topic, data) {
- switch (topic) {
- // User click an ActionBar button.
- case "TextSelection:Action": {
- if (!this._selectionID) {
- break;
- }
- for (let type in this.actions) {
- let action = this.actions[type];
- if (action.id == data) {
- action.action(this._targetElement, this._contentWindow);
- break;
- }
- }
- break;
- }
-
- // Provide selected text to FindInPageBar on request.
- case "TextSelection:Get": {
- Messaging.sendRequest({
- type: "TextSelection:Data",
- requestId: data,
- text: this._getSelectedText(),
- });
-
- this._uninit();
- break;
- }
-
- // User closed ActionBar by clicking "checkmark" button.
- case "TextSelection:End": {
- // End the requested selection only.
- if (this._selectionID == JSON.parse(data).selectionID) {
- this._uninit();
- }
- break;
- }
- }
- },
-
- /**
- * Called when Gecko AccessibleCaret becomes visible.
- */
- _init: function(boundingClientRect) {
- let [element, win] = this._getSelectionTargets();
- if (!win) {
- return this.START_TOUCH_ERROR.NO_CONTENT_WINDOW;
- }
-
- // Hold the ActionBar ID provided by Gecko.
- this._selectionID = this._nextSelectionID++;
- [this._targetElement, this._contentWindow] = [element, win];
- this._boundingClientRect = boundingClientRect;
-
- // Open the ActionBar, send it's actions list.
- Messaging.sendRequest({
- type: "TextSelection:ActionbarInit",
- selectionID: this._selectionID,
- });
- this._sendActionBarActions(true);
-
- return this.START_TOUCH_ERROR.NONE;
- },
-
- /**
- * Called when content is scrolled and handles are hidden.
- */
- _updateVisibility: function() {
- Messaging.sendRequest({
- type: "TextSelection:Visibility",
- selectionID: this._selectionID,
- });
- },
-
- /**
- * Determines the window containing the selection, and its
- * editable element if present.
- */
- _getSelectionTargets: function() {
- let [element, win] = [Services.focus.focusedElement, Services.focus.focusedWindow];
- if (!element) {
- // No focused editable.
- return [null, win];
- }
-
- // Return focused editable text element and its window.
- if (((element instanceof HTMLInputElement) && element.mozIsTextField(false)) ||
- (element instanceof HTMLTextAreaElement) ||
- element.isContentEditable) {
- return [element, win];
- }
-
- // Focused element can't contain text.
- return [null, win];
- },
-
- /**
- * The active Selection has changed, if the current focused element / win,
- * pair, or state of the win's designMode changes.
- */
- _selectionHasChanged: function() {
- let [element, win] = this._getSelectionTargets();
- return (this._targetElement !== element ||
- this._contentWindow !== win ||
- this._isInDesignMode(this._contentWindow) !== this._isInDesignMode(win));
- },
-
- /**
- * Called when Gecko AccessibleCaret becomes hidden,
- * ActionBar is closed by user "close" request, or as a result of object
- * methods such as SELECT_ALL, PASTE, etc.
- */
- _uninit: function(clearSelection = true) {
- // Bail if there's no active selection.
- if (!this._selectionID) {
- return;
- }
-
- // Close the ActionBar.
- Messaging.sendRequest({
- type: "TextSelection:ActionbarUninit",
- });
-
- // Clear the selection ID to complete the uninit(), but leave our reference
- // to selectionTargets (_targetElement, _contentWindow) in case we need
- // a final clearSelection().
- this._selectionID = null;
- this._boundingClientRect = null;
-
- // Clear selection required if triggered by self, or TextSelection icon
- // actions. If called by Gecko CaretStateChangedEvent,
- // visibility state is already correct.
- if (clearSelection) {
- this._clearSelection();
- }
- },
-
- /**
- * Final UI cleanup when Actionbar is closed by icon click, or where
- * we terminate selection state after before/after actionbar actions
- * (Cut, Copy, Paste, Search, Share, Call).
- */
- _clearSelection: function(element = this._targetElement, win = this._contentWindow) {
- // Commit edit compositions, and clear focus from editables.
- if (element) {
- let imeSupport = this._getEditor(element, win).QueryInterface(Ci.nsIEditorIMESupport);
- if (imeSupport.composing) {
- imeSupport.forceCompositionEnd();
- }
- element.blur();
- }
-
- // Remove Selection from non-editables and now-unfocused contentEditables.
- if (!element || element.isContentEditable) {
- this._getSelection().removeAllRanges();
- }
- },
-
- /**
- * Called to determine current ActionBar actions and send to TextSelection
- * handler. By default we only send if current action state differs from
- * the previous.
- * @param By default we only send an ActionBarStatus update message if
- * there is a change from the previous state. sendAlways can be
- * set by init() for example, where we want to always send the
- * current state.
- */
- _sendActionBarActions: function(sendAlways) {
- let actions = this._getActionBarActions();
-
- let actionCountUnchanged = this._actionBarActions &&
- actions.length === this._actionBarActions.length;
- let actionsMatch = actionCountUnchanged &&
- this._actionBarActions.every((e,i) => {
- return e.id === actions[i].id;
- });
-
- if (sendAlways || !actionsMatch) {
- Messaging.sendRequest({
- type: "TextSelection:ActionbarStatus",
- selectionID: this._selectionID,
- actions: actions,
- x: this._boundingClientRect.x,
- y: this._boundingClientRect.y,
- width: this._boundingClientRect.width,
- height: this._boundingClientRect.height
- });
- }
-
- this._actionBarActions = actions;
- },
-
- /**
- * Determine and return current ActionBar state.
- */
- _getActionBarActions: function(element = this._targetElement, win = this._contentWindow) {
- let actions = [];
-
- for (let type in this.actions) {
- let action = this.actions[type];
- if (action.selector.matches(element, win)) {
- let a = {
- id: action.id,
- label: this._getActionValue(action, "label", "", element),
- icon: this._getActionValue(action, "icon", "drawable://ic_status_logo", element),
- order: this._getActionValue(action, "order", 0, element),
- floatingOrder: this._getActionValue(action, "floatingOrder", 9, element),
- showAsAction: this._getActionValue(action, "showAsAction", true, element),
- };
- actions.push(a);
- }
- }
- actions.sort((a, b) => b.order - a.order);
-
- return actions;
- },
-
- /**
- * Provides a value from an action. If the action defines the value as a function,
- * we return the result of calling the function. Otherwise, we return the value
- * itself. If the value isn't defined for this action, will return a default.
- */
- _getActionValue: function(obj, name, defaultValue, element) {
- if (!(name in obj))
- return defaultValue;
-
- if (typeof obj[name] == "function")
- return obj[name](element);
-
- return obj[name];
- },
-
- /**
- * Actionbar callback methods.
- */
- actions: {
-
- SELECT_ALL: {
- id: "selectall_action",
- label: Strings.browser.GetStringFromName("contextmenu.selectAll"),
- icon: "drawable://ab_select_all",
- order: 5,
- floatingOrder: 5,
-
- selector: {
- matches: function(element, win) {
- // For editable, check its length. For default contentWindow, assume
- // true, else there'd been nothing to long-press to open ActionBar.
- return (element) ? element.textLength != 0 : true;
- },
- },
-
- action: function(element, win) {
- // Some Mobile keyboards such as SwiftKeyboard, provide auto-suggest
- // style highlights via composition selections in editables.
- if (element) {
- // If we have an active composition string, commit it, and
- // ensure proper element focus.
- let imeSupport = ActionBarHandler._getEditor(element, win).
- QueryInterface(Ci.nsIEditorIMESupport);
- if (imeSupport.composing) {
- element.blur();
- element.focus();
- }
- }
-
- // Close ActionBarHandler, then selectAll, and display handles.
- ActionBarHandler._getSelectAllController(element, win).selectAll();
- UITelemetry.addEvent("action.1", "actionbar", null, "select_all");
- },
- },
-
- CUT: {
- id: "cut_action",
- label: Strings.browser.GetStringFromName("contextmenu.cut"),
- icon: "drawable://ab_cut",
- order: 4,
- floatingOrder: 1,
-
- selector: {
- matches: function(element, win) {
- // Can cut from editable, or design-mode document.
- if (!element && !ActionBarHandler._isInDesignMode(win)) {
- return false;
- }
- // Don't allow "cut" from password fields.
- if (element instanceof Ci.nsIDOMHTMLInputElement &&
- !element.mozIsTextField(true)) {
- return false;
- }
- // Don't allow "cut" from disabled/readonly fields.
- if (element && (element.disabled || element.readOnly)) {
- return false;
- }
- // Allow if selected text exists.
- return (ActionBarHandler._getSelectedText().length > 0);
- },
- },
-
- action: function(element, win) {
- // First copy the selection text to the clipboard.
- let selectedText = ActionBarHandler._getSelectedText();
- let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
- getService(Ci.nsIClipboardHelper);
- clipboard.copyString(selectedText);
-
- let msg = Strings.browser.GetStringFromName("selectionHelper.textCopied");
- Snackbars.show(msg, Snackbars.LENGTH_LONG);
-
- // Then cut the selection text.
- ActionBarHandler._getSelection(element, win).deleteFromDocument();
-
- ActionBarHandler._uninit();
- UITelemetry.addEvent("action.1", "actionbar", null, "cut");
- },
- },
-
- COPY: {
- id: "copy_action",
- label: Strings.browser.GetStringFromName("contextmenu.copy"),
- icon: "drawable://ab_copy",
- order: 3,
- floatingOrder: 2,
-
- selector: {
- matches: function(element, win) {
- // Don't allow "copy" from password fields.
- if (element instanceof Ci.nsIDOMHTMLInputElement &&
- !element.mozIsTextField(true)) {
- return false;
- }
- // Allow if selected text exists.
- return (ActionBarHandler._getSelectedText().length > 0);
- },
- },
-
- action: function(element, win) {
- let selectedText = ActionBarHandler._getSelectedText();
- let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
- getService(Ci.nsIClipboardHelper);
- clipboard.copyString(selectedText);
-
- let msg = Strings.browser.GetStringFromName("selectionHelper.textCopied");
- Snackbars.show(msg, Snackbars.LENGTH_LONG);
-
- ActionBarHandler._uninit();
- UITelemetry.addEvent("action.1", "actionbar", null, "copy");
- },
- },
-
- PASTE: {
- id: "paste_action",
- label: Strings.browser.GetStringFromName("contextmenu.paste"),
- icon: "drawable://ab_paste",
- order: 2,
- floatingOrder: 3,
-
- selector: {
- matches: function(element, win) {
- // Can paste to editable, or design-mode document.
- if (!element && !ActionBarHandler._isInDesignMode(win)) {
- return false;
- }
- // Can't paste into disabled/readonly fields.
- if (element && (element.disabled || element.readOnly)) {
- return false;
- }
- // Can't paste if Clipboard empty.
- let flavors = ["text/unicode"];
- return Services.clipboard.hasDataMatchingFlavors(flavors, flavors.length,
- Ci.nsIClipboard.kGlobalClipboard);
- },
- },
-
- action: function(element, win) {
- // Paste the clipboard, then close the ActionBarHandler and ActionBar.
- ActionBarHandler._getEditor(element, win).
- paste(Ci.nsIClipboard.kGlobalClipboard);
- ActionBarHandler._uninit();
- UITelemetry.addEvent("action.1", "actionbar", null, "paste");
- },
- },
-
- CALL: {
- id: "call_action",
- label: Strings.browser.GetStringFromName("contextmenu.call"),
- icon: "drawable://phone",
- order: 1,
- floatingOrder: 0,
-
- selector: {
- matches: function(element, win) {
- return (ActionBarHandler._getSelectedPhoneNumber() != null);
- },
- },
-
- action: function(element, win) {
- BrowserApp.loadURI("tel:" +
- ActionBarHandler._getSelectedPhoneNumber());
-
- ActionBarHandler._uninit();
- UITelemetry.addEvent("action.1", "actionbar", null, "call");
- },
- },
-
- SEARCH: {
- id: "search_action",
- label: () => Strings.browser.formatStringFromName("contextmenu.search",
- [Services.search.defaultEngine.name], 1),
- icon: "drawable://ab_search",
- order: 1,
- floatingOrder: 6,
-
- selector: {
- matches: function(element, win) {
- // Allow if selected text exists.
- return (ActionBarHandler._getSelectedText().length > 0);
- },
- },
-
- action: function(element, win) {
- let selectedText = ActionBarHandler._getSelectedText();
- ActionBarHandler._uninit();
-
- // Set current tab as parent of new tab,
- // and set new tab as private if the parent is.
- let searchSubmission = Services.search.defaultEngine.getSubmission(selectedText);
- let parent = BrowserApp.selectedTab;
- let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(parent.browser);
- BrowserApp.addTab(searchSubmission.uri.spec,
- { parentId: parent.id,
- selected: true,
- isPrivate: isPrivate,
- }
- );
-
- UITelemetry.addEvent("action.1", "actionbar", null, "search");
- },
- },
-
- SEARCH_ADD: {
- id: "search_add_action",
- label: Strings.browser.GetStringFromName("contextmenu.addSearchEngine3"),
- icon: "drawable://ab_add_search_engine",
- order: 0,
- floatingOrder: 8,
-
- selector: {
- matches: function(element, win) {
- if(!(element instanceof HTMLInputElement)) {
- return false;
- }
- let form = element.form;
- if (!form || element.type == "password") {
- return false;
- }
-
- let method = form.method.toUpperCase();
- let canAddEngine = (method == "GET") ||
- (method == "POST" && (form.enctype != "text/plain" && form.enctype != "multipart/form-data"));
- if (!canAddEngine) {
- return false;
- }
-
- // If SearchEngine query finds it, then we don't want action to add displayed.
- if (SearchEngines.visibleEngineExists(element)) {
- return false;
- }
-
- return true;
- },
- },
-
- action: function(element, win) {
- UITelemetry.addEvent("action.1", "actionbar", null, "add_search_engine");
-
- // Engines are added asynch. If required, update SelectionUI on callback.
- SearchEngines.addEngine(element, (result) => {
- if (result) {
- ActionBarHandler._sendActionBarActions(true);
- }
- });
- },
- },
-
- SHARE: {
- id: "share_action",
- label: Strings.browser.GetStringFromName("contextmenu.share"),
- icon: "drawable://ic_menu_share",
- order: 0,
- floatingOrder: 4,
-
- selector: {
- matches: function(element, win) {
- if (!ParentalControls.isAllowed(ParentalControls.SHARE)) {
- return false;
- }
- // Allow if selected text exists.
- return (ActionBarHandler._getSelectedText().length > 0);
- },
- },
-
- action: function(element, win) {
- Messaging.sendRequest({
- type: "Share:Text",
- text: ActionBarHandler._getSelectedText(),
- });
-
- ActionBarHandler._uninit();
- UITelemetry.addEvent("action.1", "actionbar", null, "share");
- },
- },
- },
-
- /**
- * Provides UUID service for generating action ID's.
- */
- get _idService() {
- delete this._idService;
- return this._idService = Cc["@mozilla.org/uuid-generator;1"].
- getService(Ci.nsIUUIDGenerator);
- },
-
- /**
- * The targetElement holds an editable element containing a
- * selection or a caret.
- */
- get _targetElement() {
- if (this._targetElementRef)
- return this._targetElementRef.get();
- return null;
- },
-
- set _targetElement(element) {
- this._targetElementRef = Cu.getWeakReference(element);
- },
-
- /**
- * The contentWindow holds the selection, or the targetElement
- * if it's an editable.
- */
- get _contentWindow() {
- if (this._contentWindowRef)
- return this._contentWindowRef.get();
- return null;
- },
-
- set _contentWindow(aContentWindow) {
- this._contentWindowRef = Cu.getWeakReference(aContentWindow);
- },
-
- /**
- * If we have an active selection, is it part of a designMode document?
- */
- _isInDesignMode: function(win) {
- return this._selectionID && (win.document.designMode === "on");
- },
-
- /**
- * Provides the currently selected text, for either an editable,
- * or for the default contentWindow.
- */
- _getSelectedText: function() {
- // Can be called from FindInPageBar "TextSelection:Get", when there
- // is no active selection.
- if (!this._selectionID) {
- return "";
- }
-
- let selection = this._getSelection();
-
- // Textarea can contain LF, etc.
- if (this._targetElement instanceof Ci.nsIDOMHTMLTextAreaElement) {
- let flags = Ci.nsIDocumentEncoder.OutputPreformatted |
- Ci.nsIDocumentEncoder.OutputRaw;
- return selection.QueryInterface(Ci.nsISelectionPrivate).
- toStringWithFormat("text/plain", flags, 0);
- }
-
- // Return explicitly selected text.
- return selection.toString();
- },
-
- /**
- * Provides the nsISelection for either an editor, or from the
- * default window.
- */
- _getSelection: function(element = this._targetElement, win = this._contentWindow) {
- return (element instanceof Ci.nsIDOMNSEditableElement) ?
- this._getEditor(element).selection :
- win.getSelection();
- },
-
- /**
- * Returns an nsEditor or nsHTMLEditor.
- */
- _getEditor: function(element = this._targetElement, win = this._contentWindow) {
- if (element instanceof Ci.nsIDOMNSEditableElement) {
- return element.QueryInterface(Ci.nsIDOMNSEditableElement).editor;
- }
-
- return win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).
- QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIEditingSession).
- getEditorForWindow(win);
- },
-
- /**
- * Returns a selection controller.
- */
- _getSelectionController: function(element = this._targetElement, win = this._contentWindow) {
- if (element instanceof Ci.nsIDOMNSEditableElement) {
- return this._getEditor(element, win).selectionController;
- }
-
- return win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).
- QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsISelectionDisplay).
- QueryInterface(Ci.nsISelectionController);
- },
-
- /**
- * For selectAll(), provides the editor, or the default window selection Controller.
- */
- _getSelectAllController: function(element = this._targetElement, win = this._contentWindow) {
- let editor = this._getEditor(element, win);
- return (editor) ?
- editor : this._getSelectionController(element, win);
- },
-
- /**
- * Call / Phone Helper methods.
- */
- _getSelectedPhoneNumber: function() {
- let selectedText = this._getSelectedText().trim();
- return this._isPhoneNumber(selectedText) ?
- selectedText : null;
- },
-
- _isPhoneNumber: function(selectedText) {
- return (PHONE_REGEX.test(selectedText));
- },
-};
diff --git a/mobile/android/chrome/content/CastingApps.js b/mobile/android/chrome/content/CastingApps.js
deleted file mode 100644
index 76773c4d8..000000000
--- a/mobile/android/chrome/content/CastingApps.js
+++ /dev/null
@@ -1,850 +0,0 @@
-// -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
-/* 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";
-
-XPCOMUtils.defineLazyModuleGetter(this, "PageActions",
- "resource://gre/modules/PageActions.jsm");
-
-// Define service devices. We should consider moving these to their respective
-// JSM files, but we left them here to allow for better lazy JSM loading.
-var rokuDevice = {
- id: "roku:ecp",
- target: "roku:ecp",
- factory: function(aService) {
- Cu.import("resource://gre/modules/RokuApp.jsm");
- return new RokuApp(aService);
- },
- types: ["video/mp4"],
- extensions: ["mp4"]
-};
-
-var mediaPlayerDevice = {
- id: "media:router",
- target: "media:router",
- factory: function(aService) {
- Cu.import("resource://gre/modules/MediaPlayerApp.jsm");
- return new MediaPlayerApp(aService);
- },
- types: ["video/mp4", "video/webm", "application/x-mpegurl"],
- extensions: ["mp4", "webm", "m3u", "m3u8"],
- init: function() {
- Services.obs.addObserver(this, "MediaPlayer:Added", false);
- Services.obs.addObserver(this, "MediaPlayer:Changed", false);
- Services.obs.addObserver(this, "MediaPlayer:Removed", false);
- },
- observe: function(subject, topic, data) {
- if (topic === "MediaPlayer:Added") {
- let service = this.toService(JSON.parse(data));
- SimpleServiceDiscovery.addService(service);
- } else if (topic === "MediaPlayer:Changed") {
- let service = this.toService(JSON.parse(data));
- SimpleServiceDiscovery.updateService(service);
- } else if (topic === "MediaPlayer:Removed") {
- SimpleServiceDiscovery.removeService(data);
- }
- },
- toService: function(display) {
- // Convert the native data into something matching what is created in _processService()
- return {
- location: display.location,
- target: "media:router",
- friendlyName: display.friendlyName,
- uuid: display.uuid,
- manufacturer: display.manufacturer,
- modelName: display.modelName,
- mirror: display.mirror
- };
- }
-};
-
-var fxOSTVDevice = {
- id: "app://fling-player.gaiamobile.org",
- target: "app://fling-player.gaiamobile.org/index.html",
- factory: function(aService) {
- Cu.import("resource://gre/modules/PresentationApp.jsm");
- let request = new window.PresentationRequest(this.target);
- return new PresentationApp(aService, request);
- },
- init: function() {
- Services.obs.addObserver(this, "presentation-device-change", false);
- SimpleServiceDiscovery.addExternalDiscovery(this);
- },
- observe: function(subject, topic, data) {
- let device = subject.QueryInterface(Ci.nsIPresentationDevice);
- let service = this.toService(device);
- switch (data) {
- case "add":
- SimpleServiceDiscovery.addService(service);
- break;
- case "update":
- SimpleServiceDiscovery.updateService(service);
- break;
- case "remove":
- if(SimpleServiceDiscovery.findServiceForID(device.id)) {
- SimpleServiceDiscovery.removeService(device.id);
- }
- break;
- }
- },
- toService: function(device) {
- return {
- location: device.id,
- target: fxOSTVDevice.target,
- friendlyName: device.name,
- uuid: device.id,
- manufacturer: "Firefox OS TV",
- modelName: "Firefox OS TV",
- };
- },
- startDiscovery: function() {
- window.navigator.mozPresentationDeviceInfo.forceDiscovery();
-
- // need to update the lastPing time for known device.
- window.navigator.mozPresentationDeviceInfo.getAll()
- .then(function(devices) {
- for (let device of devices) {
- let service = fxOSTVDevice.toService(device);
- SimpleServiceDiscovery.addService(service);
- }
- });
- },
- stopDiscovery: function() {
- // do nothing
- },
- types: ["video/mp4", "video/webm"],
- extensions: ["mp4", "webm"],
-};
-
-var CastingApps = {
- _castMenuId: -1,
- mirrorStartMenuId: -1,
- mirrorStopMenuId: -1,
- _blocked: null,
- _bound: null,
- _interval: 120 * 1000, // 120 seconds
-
- init: function ca_init() {
- if (!this.isCastingEnabled()) {
- return;
- }
-
- // Register targets
- SimpleServiceDiscovery.registerDevice(rokuDevice);
-
- // MediaPlayerDevice will notify us any time the native device list changes.
- mediaPlayerDevice.init();
- SimpleServiceDiscovery.registerDevice(mediaPlayerDevice);
-
- // Presentation Device will notify us any time the available device list changes.
- if (window.PresentationRequest) {
- fxOSTVDevice.init();
- SimpleServiceDiscovery.registerDevice(fxOSTVDevice);
- }
-
- // Search for devices continuously
- SimpleServiceDiscovery.search(this._interval);
-
- this._castMenuId = NativeWindow.contextmenus.add(
- Strings.browser.GetStringFromName("contextmenu.sendToDevice"),
- this.filterCast,
- this.handleContextMenu.bind(this)
- );
-
- Services.obs.addObserver(this, "Casting:Play", false);
- Services.obs.addObserver(this, "Casting:Pause", false);
- Services.obs.addObserver(this, "Casting:Stop", false);
- Services.obs.addObserver(this, "Casting:Mirror", false);
- Services.obs.addObserver(this, "ssdp-service-found", false);
- Services.obs.addObserver(this, "ssdp-service-lost", false);
- Services.obs.addObserver(this, "application-background", false);
- Services.obs.addObserver(this, "application-foreground", false);
-
- BrowserApp.deck.addEventListener("TabSelect", this, true);
- BrowserApp.deck.addEventListener("pageshow", this, true);
- BrowserApp.deck.addEventListener("playing", this, true);
- BrowserApp.deck.addEventListener("ended", this, true);
- BrowserApp.deck.addEventListener("MozAutoplayMediaBlocked", this, true);
- // Note that the XBL binding is untrusted
- BrowserApp.deck.addEventListener("MozNoControlsVideoBindingAttached", this, true, true);
- },
-
- _mirrorStarted: function(stopMirrorCallback) {
- this.stopMirrorCallback = stopMirrorCallback;
- NativeWindow.menu.update(this.mirrorStartMenuId, { visible: false });
- NativeWindow.menu.update(this.mirrorStopMenuId, { visible: true });
- },
-
- serviceAdded: function(aService) {
- if (this.isMirroringEnabled() && aService.mirror && this.mirrorStartMenuId == -1) {
- this.mirrorStartMenuId = NativeWindow.menu.add({
- name: Strings.browser.GetStringFromName("casting.mirrorTab"),
- callback: function() {
- let callbackFunc = function(aService) {
- let app = SimpleServiceDiscovery.findAppForService(aService);
- if (app) {
- app.mirror(function() {}, window, BrowserApp.selectedTab.getViewport(), this._mirrorStarted.bind(this), window.BrowserApp.selectedBrowser.contentWindow);
- }
- }.bind(this);
-
- this.prompt(callbackFunc, aService => aService.mirror);
- }.bind(this),
- parent: NativeWindow.menu.toolsMenuID
- });
-
- this.mirrorStopMenuId = NativeWindow.menu.add({
- name: Strings.browser.GetStringFromName("casting.mirrorTabStop"),
- callback: function() {
- if (this.tabMirror) {
- this.tabMirror.stop();
- this.tabMirror = null;
- } else if (this.stopMirrorCallback) {
- this.stopMirrorCallback();
- this.stopMirrorCallback = null;
- }
- NativeWindow.menu.update(this.mirrorStartMenuId, { visible: true });
- NativeWindow.menu.update(this.mirrorStopMenuId, { visible: false });
- }.bind(this),
- });
- }
- if (this.mirrorStartMenuId != -1) {
- NativeWindow.menu.update(this.mirrorStopMenuId, { visible: false });
- }
- },
-
- serviceLost: function(aService) {
- if (aService.mirror && this.mirrorStartMenuId != -1) {
- let haveMirror = false;
- SimpleServiceDiscovery.services.forEach(function(service) {
- if (service.mirror) {
- haveMirror = true;
- }
- });
- if (!haveMirror) {
- NativeWindow.menu.remove(this.mirrorStartMenuId);
- this.mirrorStartMenuId = -1;
- }
- }
- },
-
- isCastingEnabled: function isCastingEnabled() {
- return Services.prefs.getBoolPref("browser.casting.enabled");
- },
-
- isMirroringEnabled: function isMirroringEnabled() {
- return Services.prefs.getBoolPref("browser.mirroring.enabled");
- },
-
- observe: function (aSubject, aTopic, aData) {
- switch (aTopic) {
- case "Casting:Play":
- if (this.session && this.session.remoteMedia.status == "paused") {
- this.session.remoteMedia.play();
- }
- break;
- case "Casting:Pause":
- if (this.session && this.session.remoteMedia.status == "started") {
- this.session.remoteMedia.pause();
- }
- break;
- case "Casting:Stop":
- if (this.session) {
- this.closeExternal();
- }
- break;
- case "Casting:Mirror":
- {
- Cu.import("resource://gre/modules/TabMirror.jsm");
- this.tabMirror = new TabMirror(aData, window);
- NativeWindow.menu.update(this.mirrorStartMenuId, { visible: false });
- NativeWindow.menu.update(this.mirrorStopMenuId, { visible: true });
- }
- break;
- case "ssdp-service-found":
- this.serviceAdded(SimpleServiceDiscovery.findServiceForID(aData));
- break;
- case "ssdp-service-lost":
- this.serviceLost(SimpleServiceDiscovery.findServiceForID(aData));
- break;
- case "application-background":
- // Turn off polling while in the background
- this._interval = SimpleServiceDiscovery.search(0);
- SimpleServiceDiscovery.stopSearch();
- break;
- case "application-foreground":
- // Turn polling on when app comes back to foreground
- SimpleServiceDiscovery.search(this._interval);
- break;
- }
- },
-
- handleEvent: function(aEvent) {
- switch (aEvent.type) {
- case "TabSelect": {
- let tab = BrowserApp.getTabForBrowser(aEvent.target);
- this._updatePageActionForTab(tab, aEvent);
- break;
- }
- case "pageshow": {
- let tab = BrowserApp.getTabForWindow(aEvent.originalTarget.defaultView);
- this._updatePageActionForTab(tab, aEvent);
- break;
- }
- case "playing":
- case "ended": {
- let video = aEvent.target;
- if (video instanceof HTMLVideoElement) {
- // If playing, send the <video>, but if ended we send nothing to shutdown the pageaction
- this._updatePageActionForVideo(aEvent.type === "playing" ? video : null);
- }
- break;
- }
- case "MozAutoplayMediaBlocked": {
- if (this._bound && this._bound.has(aEvent.target)) {
- aEvent.target.dispatchEvent(new CustomEvent("MozNoControlsBlockedVideo"));
- } else {
- if (!this._blocked) {
- this._blocked = new WeakMap;
- }
- this._blocked.set(aEvent.target, true);
- }
- break;
- }
- case "MozNoControlsVideoBindingAttached": {
- if (!this._bound) {
- this._bound = new WeakMap;
- }
- this._bound.set(aEvent.target, true);
- if (this._blocked && this._blocked.has(aEvent.target)) {
- this._blocked.delete(aEvent.target);
- aEvent.target.dispatchEvent(new CustomEvent("MozNoControlsBlockedVideo"));
- }
- break;
- }
- }
- },
-
- _sendEventToVideo: function _sendEventToVideo(aElement, aData) {
- let event = aElement.ownerDocument.createEvent("CustomEvent");
- event.initCustomEvent("media-videoCasting", false, true, JSON.stringify(aData));
- aElement.dispatchEvent(event);
- },
-
- handleVideoBindingAttached: function handleVideoBindingAttached(aTab, aEvent) {
- // Let's figure out if we have everything needed to cast a video. The binding
- // defaults to |false| so we only need to send an event if |true|.
- let video = aEvent.target;
- if (!(video instanceof HTMLVideoElement)) {
- return;
- }
-
- if (SimpleServiceDiscovery.services.length == 0) {
- return;
- }
-
- this.getVideo(video, 0, 0, (aBundle) => {
- // Let the binding know casting is allowed
- if (aBundle) {
- this._sendEventToVideo(aBundle.element, { allow: true });
- }
- });
- },
-
- handleVideoBindingCast: function handleVideoBindingCast(aTab, aEvent) {
- // The binding wants to start a casting session
- let video = aEvent.target;
- if (!(video instanceof HTMLVideoElement)) {
- return;
- }
-
- // Close an existing session first. closeExternal has checks for an exsting
- // session and handles remote and video binding shutdown.
- this.closeExternal();
-
- // Start the new session
- UITelemetry.addEvent("cast.1", "button", null);
- this.openExternal(video, 0, 0);
- },
-
- makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) {
- return Services.io.newURI(aURL, aOriginCharset, aBaseURI);
- },
-
- allowableExtension: function(aURI, aExtensions) {
- return (aURI instanceof Ci.nsIURL) && aExtensions.indexOf(aURI.fileExtension) != -1;
- },
-
- allowableMimeType: function(aType, aTypes) {
- return aTypes.indexOf(aType) != -1;
- },
-
- // This method will look at the aElement (or try to find a video at aX, aY) that has
- // a castable source. If found, aCallback will be called with a JSON meta bundle. If
- // no castable source was found, aCallback is called with null.
- getVideo: function(aElement, aX, aY, aCallback) {
- let extensions = SimpleServiceDiscovery.getSupportedExtensions();
- let types = SimpleServiceDiscovery.getSupportedMimeTypes();
-
- // Fast path: Is the given element a video element?
- if (aElement instanceof HTMLVideoElement) {
- // If we found a video element, no need to look further, even if no
- // castable video source is found.
- this._getVideo(aElement, types, extensions, aCallback);
- return;
- }
-
- // Maybe this is an overlay, with the video element under it.
- // Use the (x, y) location to guess at a <video> element.
-
- // The context menu system will keep walking up the DOM giving us a chance
- // to find an element we match. When it hits <html> things can go BOOM.
- try {
- let elements = aElement.ownerDocument.querySelectorAll("video");
- for (let element of elements) {
- // Look for a video element contained in the overlay bounds
- let rect = element.getBoundingClientRect();
- if (aY >= rect.top && aX >= rect.left && aY <= rect.bottom && aX <= rect.right) {
- // Once we find a <video> under the overlay, we check it and exit.
- this._getVideo(element, types, extensions, aCallback);
- return;
- }
- }
- } catch(e) {}
- },
-
- _getContentTypeForURI: function(aURI, aElement, aCallback) {
- let channel;
- try {
- let secFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
- if (aElement.crossOrigin) {
- secFlags = Ci.nsILoadInfo.SEC_REQUIRE_CORS_DATA_INHERITS;
- if (aElement.crossOrigin === "use-credentials") {
- secFlags |= Ci.nsILoadInfo.SEC_COOKIES_INCLUDE;
- }
- }
- channel = NetUtil.newChannel({
- uri: aURI,
- loadingNode: aElement,
- securityFlags: secFlags,
- contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_VIDEO
- });
- } catch(e) {
- aCallback(null);
- return;
- }
-
- let listener = {
- onStartRequest: function(request, context) {
- switch (channel.responseStatus) {
- case 301:
- case 302:
- case 303:
- request.cancel(0);
- let location = channel.getResponseHeader("Location");
- CastingApps._getContentTypeForURI(CastingApps.makeURI(location), aElement, aCallback);
- break;
- default:
- aCallback(channel.contentType);
- request.cancel(0);
- break;
- }
- },
- onStopRequest: function(request, context, statusCode) {},
- onDataAvailable: function(request, context, stream, offset, count) {}
- };
-
- if (channel) {
- channel.asyncOpen2(listener);
- } else {
- aCallback(null);
- }
- },
-
- // Because this method uses a callback, make sure we return ASAP if we know
- // we have a castable video source.
- _getVideo: function(aElement, aTypes, aExtensions, aCallback) {
- // Keep a list of URIs we need for an async mimetype check
- let asyncURIs = [];
-
- // Grab the poster attribute from the <video>
- let posterURL = aElement.poster;
-
- // First, look to see if the <video> has a src attribute
- let sourceURL = aElement.src;
-
- // If empty, try the currentSrc
- if (!sourceURL) {
- sourceURL = aElement.currentSrc;
- }
-
- if (sourceURL) {
- // Use the file extension to guess the mime type
- let sourceURI = this.makeURI(sourceURL, null, this.makeURI(aElement.baseURI));
- if (this.allowableExtension(sourceURI, aExtensions)) {
- aCallback({ element: aElement, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI});
- return;
- }
-
- if (aElement.type) {
- // Fast sync check
- if (this.allowableMimeType(aElement.type, aTypes)) {
- aCallback({ element: aElement, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI, type: aElement.type });
- return;
- }
- }
-
- // Delay the async check until we sync scan all possible URIs
- asyncURIs.push(sourceURI);
- }
-
- // Next, look to see if there is a <source> child element that meets
- // our needs
- let sourceNodes = aElement.getElementsByTagName("source");
- for (let sourceNode of sourceNodes) {
- let sourceURI = this.makeURI(sourceNode.src, null, this.makeURI(sourceNode.baseURI));
-
- // Using the type attribute is our ideal way to guess the mime type. Otherwise,
- // fallback to using the file extension to guess the mime type
- if (this.allowableExtension(sourceURI, aExtensions)) {
- aCallback({ element: aElement, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI, type: sourceNode.type });
- return;
- }
-
- if (sourceNode.type) {
- // Fast sync check
- if (this.allowableMimeType(sourceNode.type, aTypes)) {
- aCallback({ element: aElement, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI, type: sourceNode.type });
- return;
- }
- }
-
- // Delay the async check until we sync scan all possible URIs
- asyncURIs.push(sourceURI);
- }
-
- // Helper method that walks the array of possible URIs, fetching the mimetype as we go.
- // As soon as we find a good sourceURL, avoid firing the callback any further
- var _getContentTypeForURIs = (aURIs) => {
- // Do an async fetch to figure out the mimetype of the source video
- let sourceURI = aURIs.pop();
- this._getContentTypeForURI(sourceURI, aElement, (aType) => {
- if (this.allowableMimeType(aType, aTypes)) {
- // We found a supported mimetype.
- aCallback({ element: aElement, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI, type: aType });
- } else {
- // This URI was not a supported mimetype, so let's try the next, if we have more.
- if (aURIs.length > 0) {
- _getContentTypeForURIs(aURIs);
- } else {
- // We were not able to find a supported mimetype.
- aCallback(null);
- }
- }
- });
- }
-
- // If we didn't find a good URI directly, let's look using async methods.
- if (asyncURIs.length > 0) {
- _getContentTypeForURIs(asyncURIs);
- }
- },
-
- // This code depends on handleVideoBindingAttached setting mozAllowCasting
- // so we can quickly figure out if the video is castable
- isVideoCastable: function(aElement, aX, aY) {
- // Use the flag set when the <video> binding was created as the check
- if (aElement instanceof HTMLVideoElement) {
- return aElement.mozAllowCasting;
- }
-
- // This is called by the context menu system and the system will keep
- // walking up the DOM giving us a chance to find an element we match.
- // When it hits <html> things can go BOOM.
- try {
- // Maybe this is an overlay, with the video element under it
- // Use the (x, y) location to guess at a <video> element
- let elements = aElement.ownerDocument.querySelectorAll("video");
- for (let element of elements) {
- // Look for a video element contained in the overlay bounds
- let rect = element.getBoundingClientRect();
- if (aY >= rect.top && aX >= rect.left && aY <= rect.bottom && aX <= rect.right) {
- // Use the flag set when the <video> binding was created as the check
- return element.mozAllowCasting;
- }
- }
- } catch(e) {}
-
- return false;
- },
-
- filterCast: {
- matches: function(aElement, aX, aY) {
- // This behavior matches the pageaction: As long as a video is castable,
- // we can cast it, even if it's already being cast to a device.
- if (SimpleServiceDiscovery.services.length == 0)
- return false;
- return CastingApps.isVideoCastable(aElement, aX, aY);
- }
- },
-
- pageAction: {
- click: function() {
- // Since this is a pageaction, we use the selected browser
- let browser = BrowserApp.selectedBrowser;
- if (!browser) {
- return;
- }
-
- // Look for a castable <video> that is playing, and start casting it
- let videos = browser.contentDocument.querySelectorAll("video");
- for (let video of videos) {
- if (!video.paused && video.mozAllowCasting) {
- UITelemetry.addEvent("cast.1", "pageaction", null);
- CastingApps.openExternal(video, 0, 0);
- return;
- }
- }
- }
- },
-
- _findCastableVideo: function _findCastableVideo(aBrowser) {
- if (!aBrowser) {
- return null;
- }
-
- // Scan for a <video> being actively cast. Also look for a castable <video>
- // on the page.
- let castableVideo = null;
- let videos = aBrowser.contentDocument.querySelectorAll("video");
- for (let video of videos) {
- if (video.mozIsCasting) {
- // This <video> is cast-active. Break out of loop.
- return video;
- }
-
- if (!video.paused && video.mozAllowCasting) {
- // This <video> is cast-ready. Keep looking so cast-active could be found.
- castableVideo = video;
- }
- }
-
- // Could be null
- return castableVideo;
- },
-
- _updatePageActionForTab: function _updatePageActionForTab(aTab, aEvent) {
- // We only care about events on the selected tab
- if (aTab != BrowserApp.selectedTab) {
- return;
- }
-
- // Update the page action, scanning for a castable <video>
- this._updatePageAction();
- },
-
- _updatePageActionForVideo: function _updatePageActionForVideo(aVideo) {
- this._updatePageAction(aVideo);
- },
-
- _updatePageAction: function _updatePageAction(aVideo) {
- // Remove any exising pageaction first, in case state changes or we don't have
- // a castable video
- if (this.pageAction.id) {
- PageActions.remove(this.pageAction.id);
- delete this.pageAction.id;
- }
-
- if (!aVideo) {
- aVideo = this._findCastableVideo(BrowserApp.selectedBrowser);
- if (!aVideo) {
- return;
- }
- }
-
- // We only show pageactions if the <video> is from the selected tab
- if (BrowserApp.selectedTab != BrowserApp.getTabForWindow(aVideo.ownerDocument.defaultView.top)) {
- return;
- }
-
- // We check for two state here:
- // 1. The video is actively being cast
- // 2. The video is allowed to be cast and is currently playing
- // Both states have the same action: Show the cast page action
- if (aVideo.mozIsCasting) {
- this.pageAction.id = PageActions.add({
- title: Strings.browser.GetStringFromName("contextmenu.sendToDevice"),
- icon: "drawable://casting_active",
- clickCallback: this.pageAction.click,
- important: true
- });
- } else if (aVideo.mozAllowCasting) {
- this.pageAction.id = PageActions.add({
- title: Strings.browser.GetStringFromName("contextmenu.sendToDevice"),
- icon: "drawable://casting",
- clickCallback: this.pageAction.click,
- important: true
- });
- }
- },
-
- prompt: function(aCallback, aFilterFunc) {
- let items = [];
- let filteredServices = [];
- SimpleServiceDiscovery.services.forEach(function(aService) {
- let item = {
- label: aService.friendlyName,
- selected: false
- };
- if (!aFilterFunc || aFilterFunc(aService)) {
- filteredServices.push(aService);
- items.push(item);
- }
- });
-
- if (items.length == 0) {
- return;
- }
-
- let prompt = new Prompt({
- title: Strings.browser.GetStringFromName("casting.sendToDevice")
- }).setSingleChoiceItems(items).show(function(data) {
- let selected = data.button;
- let service = selected == -1 ? null : filteredServices[selected];
- if (aCallback)
- aCallback(service);
- });
- },
-
- handleContextMenu: function(aElement, aX, aY) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_cast");
- UITelemetry.addEvent("cast.1", "contextmenu", null);
- this.openExternal(aElement, aX, aY);
- },
-
- openExternal: function(aElement, aX, aY) {
- // Start a second screen media service
- this.getVideo(aElement, aX, aY, this._openExternal.bind(this));
- },
-
- _openExternal: function(aVideo) {
- if (!aVideo) {
- return;
- }
-
- function filterFunc(aService) {
- return this.allowableExtension(aVideo.sourceURI, aService.extensions) || this.allowableMimeType(aVideo.type, aService.types);
- }
-
- this.prompt(function(aService) {
- if (!aService)
- return;
-
- // Make sure we have a player app for the given service
- let app = SimpleServiceDiscovery.findAppForService(aService);
- if (!app)
- return;
-
- if (aVideo.element) {
- aVideo.title = aVideo.element.ownerDocument.defaultView.top.document.title;
-
- // If the video is currently playing on the device, pause it
- if (!aVideo.element.paused) {
- aVideo.element.pause();
- }
- }
-
- app.stop(function() {
- app.start(function(aStarted) {
- if (!aStarted) {
- dump("CastingApps: Unable to start app");
- return;
- }
-
- app.remoteMedia(function(aRemoteMedia) {
- if (!aRemoteMedia) {
- dump("CastingApps: Failed to create remotemedia");
- return;
- }
-
- this.session = {
- service: aService,
- app: app,
- remoteMedia: aRemoteMedia,
- data: {
- title: aVideo.title,
- source: aVideo.source,
- poster: aVideo.poster
- },
- videoRef: Cu.getWeakReference(aVideo.element)
- };
- }.bind(this), this);
- }.bind(this));
- }.bind(this));
- }.bind(this), filterFunc.bind(this));
- },
-
- closeExternal: function() {
- if (!this.session) {
- return;
- }
-
- this.session.remoteMedia.shutdown();
- this._shutdown();
- },
-
- _shutdown: function() {
- if (!this.session) {
- return;
- }
-
- this.session.app.stop();
- let video = this.session.videoRef.get();
- if (video) {
- this._sendEventToVideo(video, { active: false });
- this._updatePageAction();
- }
-
- delete this.session;
- },
-
- // RemoteMedia callback API methods
- onRemoteMediaStart: function(aRemoteMedia) {
- if (!this.session) {
- return;
- }
-
- aRemoteMedia.load(this.session.data);
- Messaging.sendRequest({ type: "Casting:Started", device: this.session.service.friendlyName });
-
- let video = this.session.videoRef.get();
- if (video) {
- this._sendEventToVideo(video, { active: true });
- this._updatePageAction(video);
- }
- },
-
- onRemoteMediaStop: function(aRemoteMedia) {
- Messaging.sendRequest({ type: "Casting:Stopped" });
- this._shutdown();
- },
-
- onRemoteMediaStatus: function(aRemoteMedia) {
- if (!this.session) {
- return;
- }
-
- let status = aRemoteMedia.status;
- switch (status) {
- case "started":
- Messaging.sendRequest({ type: "Casting:Playing" });
- break;
- case "paused":
- Messaging.sendRequest({ type: "Casting:Paused" });
- break;
- case "completed":
- this.closeExternal();
- break;
- }
- }
-};
diff --git a/mobile/android/chrome/content/ConsoleAPI.js b/mobile/android/chrome/content/ConsoleAPI.js
deleted file mode 100644
index 6ba4c1195..000000000
--- a/mobile/android/chrome/content/ConsoleAPI.js
+++ /dev/null
@@ -1,96 +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 ConsoleAPI = {
- observe: function observe(aMessage, aTopic, aData) {
- aMessage = aMessage.wrappedJSObject;
-
- let mappedArguments = Array.map(aMessage.arguments, this.formatResult, this);
- let joinedArguments = Array.join(mappedArguments, " ");
-
- if (aMessage.level == "error" || aMessage.level == "warn") {
- let flag = (aMessage.level == "error" ? Ci.nsIScriptError.errorFlag : Ci.nsIScriptError.warningFlag);
- let consoleMsg = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
- consoleMsg.init(joinedArguments, null, null, 0, 0, flag, "content javascript");
- Services.console.logMessage(consoleMsg);
- } else if (aMessage.level == "trace") {
- let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
- let args = aMessage.arguments;
- let filename = this.abbreviateSourceURL(args[0].filename);
- let functionName = args[0].functionName || bundle.GetStringFromName("stacktrace.anonymousFunction");
- let lineNumber = args[0].lineNumber;
-
- let body = bundle.formatStringFromName("stacktrace.outputMessage", [filename, functionName, lineNumber], 3);
- body += "\n";
- args.forEach(function(aFrame) {
- let functionName = aFrame.functionName || bundle.GetStringFromName("stacktrace.anonymousFunction");
- body += " " + aFrame.filename + " :: " + functionName + " :: " + aFrame.lineNumber + "\n";
- });
-
- Services.console.logStringMessage(body);
- } else if (aMessage.level == "time" && aMessage.arguments) {
- let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
- let body = bundle.formatStringFromName("timer.start", [aMessage.arguments.name], 1);
- Services.console.logStringMessage(body);
- } else if (aMessage.level == "timeEnd" && aMessage.arguments) {
- let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
- let body = bundle.formatStringFromName("timer.end", [aMessage.arguments.name, aMessage.arguments.duration], 2);
- Services.console.logStringMessage(body);
- } else if (["group", "groupCollapsed", "groupEnd"].indexOf(aMessage.level) != -1) {
- // Do nothing yet
- } else {
- Services.console.logStringMessage(joinedArguments);
- }
- },
-
- getResultType: function getResultType(aResult) {
- let type = aResult === null ? "null" : typeof aResult;
- if (type == "object" && aResult.constructor && aResult.constructor.name)
- type = aResult.constructor.name;
- return type.toLowerCase();
- },
-
- formatResult: function formatResult(aResult) {
- let output = "";
- let type = this.getResultType(aResult);
- switch (type) {
- case "string":
- case "boolean":
- case "date":
- case "error":
- case "number":
- case "regexp":
- output = aResult.toString();
- break;
- case "null":
- case "undefined":
- output = type;
- break;
- default:
- output = aResult.toString();
- break;
- }
-
- return output;
- },
-
- abbreviateSourceURL: function abbreviateSourceURL(aSourceURL) {
- // Remove any query parameters.
- let hookIndex = aSourceURL.indexOf("?");
- if (hookIndex > -1)
- aSourceURL = aSourceURL.substring(0, hookIndex);
-
- // Remove a trailing "/".
- if (aSourceURL[aSourceURL.length - 1] == "/")
- aSourceURL = aSourceURL.substring(0, aSourceURL.length - 1);
-
- // Remove all but the last path component.
- let slashIndex = aSourceURL.lastIndexOf("/");
- if (slashIndex > -1)
- aSourceURL = aSourceURL.substring(slashIndex + 1);
-
- return aSourceURL;
- }
-};
diff --git a/mobile/android/chrome/content/EmbedRT.js b/mobile/android/chrome/content/EmbedRT.js
deleted file mode 100644
index 8e35a3b63..000000000
--- a/mobile/android/chrome/content/EmbedRT.js
+++ /dev/null
@@ -1,82 +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";
-
-XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPI",
- "resource://gre/modules/Console.jsm");
-
-/*
- * Collection of methods and features specific to using a GeckoView instance.
- * The code is isolated from browser.js for code size and performance reasons.
- */
-var EmbedRT = {
- _scopes: {},
-
- observe: function(subject, topic, data) {
- switch(topic) {
- case "GeckoView:ImportScript":
- this.importScript(data);
- break;
- }
- },
-
- /*
- * Loads a script file into a sandbox and calls an optional load function
- */
- importScript: function(scriptURL) {
- if (scriptURL in this._scopes) {
- return;
- }
-
- let principal = Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
-
- let sandbox = new Cu.Sandbox(principal,
- {
- sandboxName: scriptURL,
- wantGlobalProperties: ["indexedDB"]
- }
- );
-
- sandbox["console"] = new ConsoleAPI({ consoleID: "script/" + scriptURL });
- sandbox["GeckoView"] = {
- sendRequest: function(data) {
- if (!data) {
- throw new Error("Invalid parameter: 'data' can't be null.");
- }
-
- let message = { type: "GeckoView:Message", data: data };
- Messaging.sendRequest(message);
- },
- sendRequestForResult: function(data) {
- if (!data) {
- throw new Error("Invalid parameter: 'data' can't be null.");
- }
-
- let message = { type: "GeckoView:Message", data: data };
- return Messaging.sendRequestForResult(message);
- }
- };
-
- // As we don't want our caller to control the JS version used for the
- // script file, we run loadSubScript within the context of the
- // sandbox with the latest JS version set explicitly.
- sandbox.__SCRIPT_URI_SPEC__ = scriptURL;
- Cu.evalInSandbox("Components.classes['@mozilla.org/moz/jssubscript-loader;1'].createInstance(Components.interfaces.mozIJSSubScriptLoader).loadSubScript(__SCRIPT_URI_SPEC__);", sandbox, "ECMAv5");
-
- this._scopes[scriptURL] = sandbox;
-
- if ("load" in sandbox) {
- let params = {
- window: window,
- resourceURI: scriptURL,
- };
-
- try {
- sandbox["load"](params);
- } catch(e) {
- dump("Exception calling 'load' method in script: " + scriptURL + "\n" + e);
- }
- }
- }
-};
diff --git a/mobile/android/chrome/content/FeedHandler.js b/mobile/android/chrome/content/FeedHandler.js
deleted file mode 100644
index 91d73ee8d..000000000
--- a/mobile/android/chrome/content/FeedHandler.js
+++ /dev/null
@@ -1,120 +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 FeedHandler = {
- PREF_CONTENTHANDLERS_BRANCH: "browser.contentHandlers.types.",
- TYPE_MAYBE_FEED: "application/vnd.mozilla.maybe.feed",
-
- _contentTypes: null,
-
- getContentHandlers: function fh_getContentHandlers(contentType) {
- if (!this._contentTypes)
- this.loadContentHandlers();
-
- if (!(contentType in this._contentTypes))
- return [];
-
- return this._contentTypes[contentType];
- },
-
- loadContentHandlers: function fh_loadContentHandlers() {
- this._contentTypes = {};
-
- let kids = Services.prefs.getBranch(this.PREF_CONTENTHANDLERS_BRANCH).getChildList("");
-
- // First get the numbers of the providers by getting all ###.uri prefs
- let nums = [];
- for (let i = 0; i < kids.length; i++) {
- let match = /^(\d+)\.uri$/.exec(kids[i]);
- if (!match)
- continue;
- else
- nums.push(match[1]);
- }
-
- // Sort them, to get them back in order
- nums.sort(function(a, b) { return a - b; });
-
- // Now register them
- for (let i = 0; i < nums.length; i++) {
- let branch = Services.prefs.getBranch(this.PREF_CONTENTHANDLERS_BRANCH + nums[i] + ".");
- let vals = branch.getChildList("");
- if (vals.length == 0)
- return;
-
- try {
- let type = branch.getCharPref("type");
- let uri = branch.getComplexValue("uri", Ci.nsIPrefLocalizedString).data;
- let title = branch.getComplexValue("title", Ci.nsIPrefLocalizedString).data;
-
- if (!(type in this._contentTypes))
- this._contentTypes[type] = [];
- this._contentTypes[type].push({ contentType: type, uri: uri, name: title });
- }
- catch(ex) {}
- }
- },
-
- observe: function fh_observe(aSubject, aTopic, aData) {
- if (aTopic === "Feeds:Subscribe") {
- let args = JSON.parse(aData);
- let tab = BrowserApp.getTabForId(args.tabId);
- if (!tab)
- return;
-
- let browser = tab.browser;
- let feeds = browser.feeds;
- if (feeds == null)
- return;
-
- // First, let's decide on which feed to subscribe
- let feedIndex = -1;
- if (feeds.length > 1) {
- let p = new Prompt({
- window: browser.contentWindow,
- title: Strings.browser.GetStringFromName("feedHandler.chooseFeed")
- }).setSingleChoiceItems(feeds.map(function(feed) {
- return { label: feed.title || feed.href }
- })).show((function(data) {
- feedIndex = data.button;
- if (feedIndex == -1)
- return;
-
- this.loadFeed(feeds[feedIndex], browser);
- }).bind(this));
- return;
- }
-
- this.loadFeed(feeds[0], browser);
- }
- },
-
- loadFeed: function fh_loadFeed(aFeed, aBrowser) {
- let feedURL = aFeed.href;
-
- // Next, we decide on which service to send the feed
- let handlers = this.getContentHandlers(this.TYPE_MAYBE_FEED);
- if (handlers.length == 0)
- return;
-
- // JSON for Prompt
- let p = new Prompt({
- window: aBrowser.contentWindow,
- title: Strings.browser.GetStringFromName("feedHandler.subscribeWith")
- }).setSingleChoiceItems(handlers.map(function(handler) {
- return { label: handler.name };
- })).show(function(data) {
- if (data.button == -1)
- return;
-
- // Merge the handler URL and the feed URL
- let readerURL = handlers[data.button].uri;
- readerURL = readerURL.replace(/%s/gi, encodeURIComponent(feedURL));
-
- // Open the resultant URL in a new tab
- BrowserApp.addTab(readerURL, { parentId: BrowserApp.selectedTab.id });
- });
- }
-};
diff --git a/mobile/android/chrome/content/Feedback.js b/mobile/android/chrome/content/Feedback.js
deleted file mode 100644
index 8727c46c3..000000000
--- a/mobile/android/chrome/content/Feedback.js
+++ /dev/null
@@ -1,64 +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 Feedback = {
-
- get _feedbackURL() {
- delete this._feedbackURL;
- return this._feedbackURL = Services.urlFormatter.formatURLPref("app.feedbackURL");
- },
-
- observe: function(aMessage, aTopic, aData) {
- if (aTopic !== "Feedback:Show") {
- return;
- }
-
- // Don't prompt for feedback in distribution builds.
- try {
- Services.prefs.getCharPref("distribution.id");
- return;
- } catch (e) {}
-
- let url = this._feedbackURL;
- let browser = BrowserApp.selectOrAddTab(url, { parentId: BrowserApp.selectedTab.id }).browser;
-
- browser.addEventListener("FeedbackClose", this, false, true);
- browser.addEventListener("FeedbackMaybeLater", this, false, true);
-
- // Dispatch a custom event to the page content when feedback is prompted by the browser.
- // This will be used by the page to determine it's being loaded directly by the browser,
- // instead of by the user visiting the page, e.g. through browser history.
- function loadListener(event) {
- browser.removeEventListener("DOMContentLoaded", loadListener, false);
- browser.contentDocument.dispatchEvent(new CustomEvent("FeedbackPrompted"));
- }
- browser.addEventListener("DOMContentLoaded", loadListener, false);
- },
-
- handleEvent: function(event) {
- if (!this._isAllowed(event.target)) {
- return;
- }
-
- switch (event.type) {
- case "FeedbackClose":
- // Do nothing.
- break;
-
- case "FeedbackMaybeLater":
- Messaging.sendRequest({ type: "Feedback:MaybeLater" });
- break;
- }
-
- let win = event.target.ownerDocument.defaultView.top;
- BrowserApp.closeTab(BrowserApp.getTabForWindow(win));
- },
-
- _isAllowed: function(node) {
- let uri = node.ownerDocument.documentURIObject;
- let feedbackURI = Services.io.newURI(this._feedbackURL, null, null);
- return uri.prePath === feedbackURI.prePath;
- }
-};
diff --git a/mobile/android/chrome/content/FindHelper.js b/mobile/android/chrome/content/FindHelper.js
deleted file mode 100644
index 037b182d6..000000000
--- a/mobile/android/chrome/content/FindHelper.js
+++ /dev/null
@@ -1,197 +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 FindHelper = {
- _finder: null,
- _targetTab: null,
- _initialViewport: null,
- _viewportChanged: false,
- _result: null,
-
- // Start of nsIObserver implementation.
-
- observe: function(aMessage, aTopic, aData) {
- switch(aTopic) {
- case "FindInPage:Opened": {
- this._findOpened();
- break;
- }
-
- case "Tab:Selected": {
- // Allow for page switching.
- this._uninit();
- break;
- }
-
- case "FindInPage:Closed":
- this._uninit();
- this._findClosed();
- break;
- }
- },
-
- /**
- * When the FindInPageBar opens/ becomes visible, it's time to:
- * 1. Add listeners for other message types sent from the FindInPageBar
- * 2. initialize the Finder instance, if necessary.
- */
- _findOpened: function() {
- Messaging.addListener(data => this.doFind(data), "FindInPage:Find");
- Messaging.addListener(data => this.findAgain(data, false), "FindInPage:Next");
- Messaging.addListener(data => this.findAgain(data, true), "FindInPage:Prev");
-
- // Initialize the finder component for the current page by performing a fake find.
- this._init();
- this._finder.requestMatchesCount("");
- },
-
- /**
- * Fetch the Finder instance from the active tabs' browser and start tracking
- * the active viewport.
- */
- _init: function() {
- // If there's no find in progress, start one.
- if (this._finder) {
- return;
- }
-
- this._targetTab = BrowserApp.selectedTab;
- try {
- this._finder = this._targetTab.browser.finder;
- } catch (e) {
- throw new Error("FindHelper: " + e + "\n" +
- "JS stack: \n" + (e.stack || Components.stack.formattedStack));
- }
-
- this._finder.addResultListener(this);
- this._initialViewport = JSON.stringify(this._targetTab.getViewport());
- this._viewportChanged = false;
- },
-
- /**
- * Detach from the Finder instance (so stop listening for messages) and stop
- * tracking the active viewport.
- */
- _uninit: function() {
- // If there's no find in progress, there's nothing to clean up.
- if (!this._finder) {
- return;
- }
-
- this._finder.removeSelection();
- this._finder.removeResultListener(this);
- this._finder = null;
- this._targetTab = null;
- this._initialViewport = null;
- this._viewportChanged = false;
- },
-
- /**
- * When the FindInPageBar closes, it's time to stop listening for its messages.
- */
- _findClosed: function() {
- Messaging.removeListener("FindInPage:Find");
- Messaging.removeListener("FindInPage:Next");
- Messaging.removeListener("FindInPage:Prev");
- },
-
- /**
- * Start an asynchronous find-in-page operation, using the current Finder
- * instance and request to count the amount of matches.
- * If no Finder instance is currently active, we'll lazily initialize it here.
- *
- * @param {String} searchString Word to search for in the current document
- * @return {Object} Echo of the current find action
- */
- doFind: function(searchString) {
- if (!this._finder) {
- this._init();
- }
-
- this._finder.fastFind(searchString, false);
- return { searchString, findBackwards: false };
- },
-
- /**
- * Restart the same find-in-page operation as before via `doFind()`. If we
- * haven't called `doFind()`, we simply kick off a regular find.
- *
- * @param {String} searchString Word to search for in the current document
- * @param {Boolean} findBackwards Direction to search in
- * @return {Object} Echo of the current find action
- */
- findAgain: function(searchString, findBackwards) {
- // This always happens if the user taps next/previous after re-opening the
- // search bar, and not only forces _init() but also an initial fastFind(STRING)
- // before any findAgain(DIRECTION).
- if (!this._finder) {
- return this.doFind(searchString);
- }
-
- this._finder.findAgain(findBackwards, false, false);
- return { searchString, findBackwards };
- },
-
- // Start of Finder.jsm listener implementation.
-
- /**
- * Pass along the count results to FindInPageBar for display. The result that
- * is sent to the FindInPageBar is augmented with the current find-in-page count
- * limit.
- *
- * @param {Object} result Result coming from the Finder instance that contains
- * the following properties:
- * - {Number} total The total amount of matches found
- * - {Number} current The index of current found range
- * in the document
- */
- onMatchesCountResult: function(result) {
- this._result = result;
-
- Messaging.sendRequest(Object.assign({
- type: "FindInPage:MatchesCountResult"
- }, this._result));
- },
-
- /**
- * When a find-in-page action finishes, this method is invoked. This is mainly
- * used at the moment to detect if the current viewport has changed, which might
- * be indicated by not finding a string in the current page.
- *
- * @param {Object} aData A dictionary, representing the find result, which
- * contains the following properties:
- * - {String} searchString Word that was searched for
- * in the current document
- * - {Number} result One of the following
- * Ci.nsITypeAheadFind.* result
- * indicators: FIND_FOUND,
- * FIND_NOTFOUND, FIND_WRAPPED,
- * FIND_PENDING
- * - {Boolean} findBackwards Whether the search direction
- * was backwards
- * - {Boolean} findAgain Whether the previous search
- * was repeated
- * - {Boolean} drawOutline Whether we may (re-)draw the
- * outline of a hyperlink
- * - {Boolean} linksOnly Whether links-only mode was
- * active
- */
- onFindResult: function(aData) {
- if (aData.result == Ci.nsITypeAheadFind.FIND_NOTFOUND) {
- if (this._viewportChanged) {
- if (this._targetTab != BrowserApp.selectedTab) {
- // this should never happen
- Cu.reportError("Warning: selected tab changed during find!");
- // fall through and restore viewport on the initial tab anyway
- }
- this._targetTab.sendViewportUpdate();
- }
- } else {
- // Disabled until bug 1014113 is fixed
- // ZoomHelper.zoomToRect(aData.rect);
- this._viewportChanged = true;
- }
- }
-};
diff --git a/mobile/android/chrome/content/InputWidgetHelper.js b/mobile/android/chrome/content/InputWidgetHelper.js
deleted file mode 100644
index cf66a263e..000000000
--- a/mobile/android/chrome/content/InputWidgetHelper.js
+++ /dev/null
@@ -1,98 +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 InputWidgetHelper = {
- _uiBusy: false,
-
- handleEvent: function(aEvent) {
- this.handleClick(aEvent.target);
- },
-
- handleClick: function(aTarget) {
- // if we're busy looking at a InputWidget we want to eat any clicks that
- // come to us, but not to process them
- if (this._uiBusy || !this.hasInputWidget(aTarget) || this._isDisabledElement(aTarget))
- return;
-
- this._uiBusy = true;
- this.show(aTarget);
- this._uiBusy = false;
- },
-
- show: function(aElement) {
- let type = aElement.getAttribute('type');
- let p = new Prompt({
- window: aElement.ownerDocument.defaultView,
- title: Strings.browser.GetStringFromName("inputWidgetHelper." + aElement.getAttribute('type')),
- buttons: [
- Strings.browser.GetStringFromName("inputWidgetHelper.set"),
- Strings.browser.GetStringFromName("inputWidgetHelper.clear"),
- Strings.browser.GetStringFromName("inputWidgetHelper.cancel")
- ],
- }).addDatePicker({
- value: aElement.value,
- type: type,
- min: aElement.min,
- max: aElement.max,
- }).show((function(data) {
- let changed = false;
- if (data.button == -1) {
- // This type is not supported with this android version.
- return;
- }
- if (data.button == 1) {
- // The user cleared the value.
- if (aElement.value != "") {
- aElement.value = "";
- changed = true;
- }
- } else if (data.button == 0) {
- // Commit the new value.
- if (aElement.value != data[type]) {
- aElement.value = data[type + "0"];
- changed = true;
- }
- }
- // Else the user canceled the input.
-
- if (changed)
- this.fireOnChange(aElement);
- }).bind(this));
- },
-
- hasInputWidget: function(aElement) {
- if (!(aElement instanceof HTMLInputElement))
- return false;
-
- let type = aElement.getAttribute('type');
- if (type == "date" || type == "datetime" || type == "datetime-local" ||
- type == "week" || type == "month" || type == "time") {
- return true;
- }
-
- return false;
- },
-
- fireOnChange: function(aElement) {
- let evt = aElement.ownerDocument.createEvent("Events");
- evt.initEvent("change", true, true, aElement.defaultView, 0,
- false, false,
- false, false, null);
- setTimeout(function() {
- aElement.dispatchEvent(evt);
- }, 0);
- },
-
- _isDisabledElement : function(aElement) {
- let currentElement = aElement;
- while (currentElement) {
- if (currentElement.disabled)
- return true;
-
- currentElement = currentElement.parentElement;
- }
- return false;
- }
-};
diff --git a/mobile/android/chrome/content/Linkify.js b/mobile/android/chrome/content/Linkify.js
deleted file mode 100644
index 3c757cc18..000000000
--- a/mobile/android/chrome/content/Linkify.js
+++ /dev/null
@@ -1,108 +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 LINKIFY_TIMEOUT = 0;
-
-function Linkifier() {
- this._linkifyTimer = null;
- this._phoneRegex = /(?:\s|^)[\+]?(\(?\d{1,8}\)?)?([- ]+\(?\d{1,8}\)?)+( ?(x|ext) ?\d{1,3})?(?:\s|$)/g;
-}
-
-Linkifier.prototype = {
- _buildAnchor : function(aDoc, aNumberText) {
- let anchorNode = aDoc.createElement("a");
- let cleanedText = "";
- for (let i = 0; i < aNumberText.length; i++) {
- let c = aNumberText.charAt(i);
- if ((c >= '0' && c <= '9') || c == '+') //assuming there is only the leading '+'.
- cleanedText += c;
- }
- anchorNode.setAttribute("href", "tel:" + cleanedText);
- let nodeText = aDoc.createTextNode(aNumberText);
- anchorNode.appendChild(nodeText);
- return anchorNode;
- },
-
- _linkifyNodeNumbers : function(aNodeToProcess, aDoc) {
- let parent = aNodeToProcess.parentNode;
- let nodeText = aNodeToProcess.nodeValue;
-
- // Replacing the original text node with a sequence of
- // |text before number|anchor with number|text after number nodes.
- // Each step a couple of (optional) text node and anchor node are appended.
- let anchorNode = null;
- let m = null;
- let startIndex = 0;
- let prevNode = null;
- while (m = this._phoneRegex.exec(nodeText)) {
- anchorNode = this._buildAnchor(aDoc, nodeText.substr(m.index, m[0].length));
-
- let textExistsBeforeNumber = (m.index > startIndex);
- let nodeToAdd = null;
- if (textExistsBeforeNumber)
- nodeToAdd = aDoc.createTextNode(nodeText.substr(startIndex, m.index - startIndex));
- else
- nodeToAdd = anchorNode;
-
- if (!prevNode) // first time, need to replace the whole node with the first new one.
- parent.replaceChild(nodeToAdd, aNodeToProcess);
- else
- parent.insertBefore(nodeToAdd, prevNode.nextSibling); //inserts after.
-
- if (textExistsBeforeNumber) // if we added the text node before the anchor, we still need to add the anchor node.
- parent.insertBefore(anchorNode, nodeToAdd.nextSibling);
-
- // next nodes need to be appended to this node.
- prevNode = anchorNode;
- startIndex = m.index + m[0].length;
- }
-
- // if some text is remaining after the last anchor.
- if (startIndex > 0 && startIndex < nodeText.length) {
- let lastNode = aDoc.createTextNode(nodeText.substr(startIndex));
- parent.insertBefore(lastNode, prevNode.nextSibling);
- return lastNode;
- }
- return anchorNode;
- },
-
- linkifyNumbers: function(aDoc) {
- // Removing any installed timer in case the page has changed and a previous timer is still running.
- if (this._linkifyTimer) {
- clearTimeout(this._linkifyTimer);
- this._linkifyTimer = null;
- }
-
- let filterNode = function (node) {
- if (node.parentNode.tagName != 'A' &&
- node.parentNode.tagName != 'SCRIPT' &&
- node.parentNode.tagName != 'NOSCRIPT' &&
- node.parentNode.tagName != 'STYLE' &&
- node.parentNode.tagName != 'APPLET' &&
- node.parentNode.tagName != 'TEXTAREA')
- return NodeFilter.FILTER_ACCEPT;
- else
- return NodeFilter.FILTER_REJECT;
- }
-
- let nodeWalker = aDoc.createTreeWalker(aDoc.body, NodeFilter.SHOW_TEXT, filterNode, false);
- let parseNode = function() {
- let node = nodeWalker.nextNode();
- if (!node) {
- this._linkifyTimer = null;
- return;
- }
- let lastAddedNode = this._linkifyNodeNumbers(node, aDoc);
- // we assign a different timeout whether the node was processed or not.
- if (lastAddedNode) {
- nodeWalker.currentNode = lastAddedNode;
- this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT);
- } else {
- this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT);
- }
- }.bind(this);
-
- this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT);
- }
-};
diff --git a/mobile/android/chrome/content/MasterPassword.js b/mobile/android/chrome/content/MasterPassword.js
deleted file mode 100644
index d85fa928d..000000000
--- a/mobile/android/chrome/content/MasterPassword.js
+++ /dev/null
@@ -1,67 +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";
-
-XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm");
-
-var MasterPassword = {
- pref: "privacy.masterpassword.enabled",
- _tokenName: "",
-
- get _secModuleDB() {
- delete this._secModuleDB;
- return this._secModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(Ci.nsIPKCS11ModuleDB);
- },
-
- get _pk11DB() {
- delete this._pk11DB;
- return this._pk11DB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(Ci.nsIPK11TokenDB);
- },
-
- get enabled() {
- let slot = this._secModuleDB.findSlotByName(this._tokenName);
- if (slot) {
- let status = slot.status;
- return status != Ci.nsIPKCS11Slot.SLOT_UNINITIALIZED && status != Ci.nsIPKCS11Slot.SLOT_READY;
- }
- return false;
- },
-
- setPassword: function setPassword(aPassword) {
- try {
- let status;
- let slot = this._secModuleDB.findSlotByName(this._tokenName);
- if (slot)
- status = slot.status;
- else
- return false;
-
- let token = this._pk11DB.findTokenByName(this._tokenName);
-
- if (status == Ci.nsIPKCS11Slot.SLOT_UNINITIALIZED)
- token.initPassword(aPassword);
- else if (status == Ci.nsIPKCS11Slot.SLOT_READY)
- token.changePassword("", aPassword);
-
- return true;
- } catch(e) {
- dump("MasterPassword.setPassword: " + e);
- }
- return false;
- },
-
- removePassword: function removePassword(aOldPassword) {
- try {
- let token = this._pk11DB.getInternalKeyToken();
- if (token.checkPassword(aOldPassword)) {
- token.changePassword(aOldPassword, "");
- return true;
- }
- } catch(e) {
- dump("MasterPassword.removePassword: " + e + "\n");
- }
- Snackbars.show(Strings.browser.GetStringFromName("masterPassword.incorrect"), Snackbars.LENGTH_LONG);
- return false;
- }
-};
diff --git a/mobile/android/chrome/content/MemoryObserver.js b/mobile/android/chrome/content/MemoryObserver.js
deleted file mode 100644
index 2bb3ae842..000000000
--- a/mobile/android/chrome/content/MemoryObserver.js
+++ /dev/null
@@ -1,88 +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 MemoryObserver = {
- observe: function mo_observe(aSubject, aTopic, aData) {
- if (aTopic == "memory-pressure") {
- if (aData != "heap-minimize") {
- this.handleLowMemory();
- }
- // The JS engine would normally GC on this notification, but since we
- // disabled that in favor of this method (bug 669346), we should gc here.
- // See bug 784040 for when this code was ported from XUL to native Fennec.
- this.gc();
- } else if (aTopic == "Memory:Dump") {
- this.dumpMemoryStats(aData);
- }
- },
-
- handleLowMemory: function() {
- // do things to reduce memory usage here
- if (!Services.prefs.getBoolPref("browser.tabs.disableBackgroundZombification")) {
- let tabs = BrowserApp.tabs;
- let selected = BrowserApp.selectedTab;
- for (let i = 0; i < tabs.length; i++) {
- if (tabs[i] != selected && !tabs[i].playingAudio) {
- this.zombify(tabs[i]);
- }
- }
- }
-
- // Change some preferences temporarily for only this session
- let defaults = Services.prefs.getDefaultBranch(null);
-
- // Reduce the amount of decoded image data we keep around
- defaults.setIntPref("image.mem.max_decoded_image_kb", 0);
-
- // Stop using the bfcache
- if (!Services.prefs.getBoolPref("browser.sessionhistory.bfcacheIgnoreMemoryPressure")) {
- defaults.setIntPref("browser.sessionhistory.max_total_viewers", 0);
- }
- },
-
- zombify: function(tab) {
- let browser = tab.browser;
- let data = browser.__SS_data;
- let extra = browser.__SS_extdata;
-
- // Notify any interested parties (e.g. the session store)
- // that the original tab object is going to be destroyed
- let evt = document.createEvent("UIEvents");
- evt.initUIEvent("TabPreZombify", true, false, window, null);
- browser.dispatchEvent(evt);
-
- // We need this data to correctly create and position the new browser
- // If this browser is already a zombie, fallback to the session data
- let currentURL = browser.__SS_restore ? data.entries[0].url : browser.currentURI.spec;
- let sibling = browser.nextSibling;
- let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(browser);
-
- tab.destroy();
- tab.create(currentURL, { sibling: sibling, zombifying: true, delayLoad: true, isPrivate: isPrivate });
-
- // Reattach session store data and flag this browser so it is restored on select
- browser = tab.browser;
- browser.__SS_data = data;
- browser.__SS_extdata = extra;
- browser.__SS_restore = true;
- browser.setAttribute("pending", "true");
-
- // Notify the session store to reattach its listeners to the new tab object
- evt = document.createEvent("UIEvents");
- evt.initUIEvent("TabPostZombify", true, false, window, null);
- browser.dispatchEvent(evt);
- },
-
- gc: function() {
- window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).garbageCollect();
- Cu.forceGC();
- },
-
- dumpMemoryStats: function(aLabel) {
- let memDumper = Cc["@mozilla.org/memory-info-dumper;1"].getService(Ci.nsIMemoryInfoDumper);
- memDumper.dumpMemoryInfoToTempDir(aLabel, /* anonymize = */ false,
- /* minimize = */ false);
- },
-};
diff --git a/mobile/android/chrome/content/OfflineApps.js b/mobile/android/chrome/content/OfflineApps.js
deleted file mode 100644
index e11b3c645..000000000
--- a/mobile/android/chrome/content/OfflineApps.js
+++ /dev/null
@@ -1,77 +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 OfflineApps = {
- offlineAppRequested: function(aContentWindow) {
- if (!Services.prefs.getBoolPref("browser.offline-apps.notify"))
- return;
-
- let tab = BrowserApp.getTabForWindow(aContentWindow);
- let currentURI = aContentWindow.document.documentURIObject;
-
- // Don't bother showing UI if the user has already made a decision
- if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
- return;
-
- try {
- if (Services.prefs.getBoolPref("offline-apps.allow_by_default")) {
- // All pages can use offline capabilities, no need to ask the user
- return;
- }
- } catch(e) {
- // This pref isn't set by default, ignore failures
- }
-
- let host = currentURI.asciiHost;
- let notificationID = "offline-app-requested-" + host;
-
- let strings = Strings.browser;
- let buttons = [{
- label: strings.GetStringFromName("offlineApps.dontAllow2"),
- callback: function(aChecked) {
- if (aChecked)
- OfflineApps.disallowSite(aContentWindow.document);
- }
- },
- {
- label: strings.GetStringFromName("offlineApps.allow"),
- callback: function() {
- OfflineApps.allowSite(aContentWindow.document);
- },
- positive: true
- }];
-
- let requestor = BrowserApp.manifest ? "'" + BrowserApp.manifest.name + "'" : host;
- let message = strings.formatStringFromName("offlineApps.ask", [requestor], 1);
- let options = { checkbox: Strings.browser.GetStringFromName("offlineApps.dontAskAgain") };
- NativeWindow.doorhanger.show(message, notificationID, buttons, tab.id, options);
- },
-
- allowSite: function(aDocument) {
- Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.ALLOW_ACTION);
-
- // When a site is enabled while loading, manifest resources will
- // start fetching immediately. This one time we need to do it
- // ourselves.
- this._startFetching(aDocument);
- },
-
- disallowSite: function(aDocument) {
- Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.DENY_ACTION);
- },
-
- _startFetching: function(aDocument) {
- if (!aDocument.documentElement)
- return;
-
- let manifest = aDocument.documentElement.getAttribute("manifest");
- if (!manifest)
- return;
-
- let manifestURI = Services.io.newURI(manifest, aDocument.characterSet, aDocument.documentURIObject);
- let updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].getService(Ci.nsIOfflineCacheUpdateService);
- updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject, aDocument.nodePrincipal, window);
- }
-};
diff --git a/mobile/android/chrome/content/PermissionsHelper.js b/mobile/android/chrome/content/PermissionsHelper.js
deleted file mode 100644
index ad1eb760a..000000000
--- a/mobile/android/chrome/content/PermissionsHelper.js
+++ /dev/null
@@ -1,188 +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 PermissionsHelper = {
- _permissonTypes: ["password", "geolocation", "popup", "indexedDB",
- "offline-app", "desktop-notification", "plugins", "native-intent",
- "flyweb-publish-server"],
- _permissionStrings: {
- "password": {
- label: "password.logins",
- allowed: "password.save",
- denied: "password.dontSave"
- },
- "geolocation": {
- label: "geolocation.location",
- allowed: "geolocation.allow",
- denied: "geolocation.dontAllow"
- },
- "flyweb-publish-server": {
- label: "flyWebPublishServer.publishServer",
- allowed: "flyWebPublishServer.allow",
- denied: "flyWebPublishServer.dontAllow"
- },
- "popup": {
- label: "blockPopups.label2",
- allowed: "popup.show",
- denied: "popup.dontShow"
- },
- "indexedDB": {
- label: "offlineApps.offlineData",
- allowed: "offlineApps.allow",
- denied: "offlineApps.dontAllow2"
- },
- "offline-app": {
- label: "offlineApps.offlineData",
- allowed: "offlineApps.allow",
- denied: "offlineApps.dontAllow2"
- },
- "desktop-notification": {
- label: "desktopNotification.notifications",
- allowed: "desktopNotification2.allow",
- denied: "desktopNotification2.dontAllow"
- },
- "plugins": {
- label: "clickToPlayPlugins.plugins",
- allowed: "clickToPlayPlugins.activate",
- denied: "clickToPlayPlugins.dontActivate"
- },
- "native-intent": {
- label: "helperapps.openWithList2",
- allowed: "helperapps.always",
- denied: "helperapps.never"
- }
- },
-
- observe: function observe(aSubject, aTopic, aData) {
- let uri = BrowserApp.selectedBrowser.currentURI;
- let check = false;
-
- switch (aTopic) {
- case "Permissions:Check":
- check = true;
- case "Permissions:Get":
- let permissions = [];
- for (let i = 0; i < this._permissonTypes.length; i++) {
- let type = this._permissonTypes[i];
- let value = this.getPermission(uri, type);
-
- // Only add the permission if it was set by the user
- if (value == Services.perms.UNKNOWN_ACTION)
- continue;
-
- if (check) {
- Messaging.sendRequest({
- type: "Permissions:CheckResult",
- hasPermissions: true
- });
- return;
- }
- // Get the strings that correspond to the permission type
- let typeStrings = this._permissionStrings[type];
- let label = Strings.browser.GetStringFromName(typeStrings["label"]);
-
- // Get the key to look up the appropriate string entity
- let valueKey = value == Services.perms.ALLOW_ACTION ?
- "allowed" : "denied";
- let valueString = Strings.browser.GetStringFromName(typeStrings[valueKey]);
-
- permissions.push({
- type: type,
- setting: label,
- value: valueString
- });
- }
-
- if (check) {
- Messaging.sendRequest({
- type: "Permissions:CheckResult",
- hasPermissions: false
- });
- return;
- }
-
- // Keep track of permissions, so we know which ones to clear
- this._currentPermissions = permissions;
-
- Messaging.sendRequest({
- type: "Permissions:Data",
- permissions: permissions
- });
- break;
-
- case "Permissions:Clear":
- // An array of the indices of the permissions we want to clear
- let permissionsToClear = JSON.parse(aData);
- let privacyContext = BrowserApp.selectedBrowser.docShell
- .QueryInterface(Ci.nsILoadContext);
-
- for (let i = 0; i < permissionsToClear.length; i++) {
- let indexToClear = permissionsToClear[i];
- let permissionType = this._currentPermissions[indexToClear]["type"];
- this.clearPermission(uri, permissionType, privacyContext);
- }
- break;
- }
- },
-
- /**
- * Gets the permission value stored for a specified permission type.
- *
- * @param aType
- * The permission type string stored in permission manager.
- * e.g. "geolocation", "indexedDB", "popup"
- *
- * @return A permission value defined in nsIPermissionManager.
- */
- getPermission: function getPermission(aURI, aType) {
- // Password saving isn't a nsIPermissionManager permission type, so handle
- // it seperately.
- if (aType == "password") {
- // By default, login saving is enabled, so if it is disabled, the
- // user selected the never remember option
- if (!Services.logins.getLoginSavingEnabled(aURI.prePath))
- return Services.perms.DENY_ACTION;
-
- // Check to see if the user ever actually saved a login
- if (Services.logins.countLogins(aURI.prePath, "", ""))
- return Services.perms.ALLOW_ACTION;
-
- return Services.perms.UNKNOWN_ACTION;
- }
-
- // Geolocation consumers use testExactPermission
- if (aType == "geolocation")
- return Services.perms.testExactPermission(aURI, aType);
-
- return Services.perms.testPermission(aURI, aType);
- },
-
- /**
- * Clears a user-set permission value for the site given a permission type.
- *
- * @param aType
- * The permission type string stored in permission manager.
- * e.g. "geolocation", "indexedDB", "popup"
- */
- clearPermission: function clearPermission(aURI, aType, aContext) {
- // Password saving isn't a nsIPermissionManager permission type, so handle
- // it seperately.
- if (aType == "password") {
- // Get rid of exisiting stored logings
- let logins = Services.logins.findLogins({}, aURI.prePath, "", "");
- for (let i = 0; i < logins.length; i++) {
- Services.logins.removeLogin(logins[i]);
- }
- // Re-set login saving to enabled
- Services.logins.setLoginSavingEnabled(aURI.prePath, true);
- } else {
- Services.perms.remove(aURI, aType);
- // Clear content prefs set in ContentPermissionPrompt.js
- Cc["@mozilla.org/content-pref/service;1"]
- .getService(Ci.nsIContentPrefService2)
- .removeByDomainAndName(aURI.spec, aType + ".request.remember", aContext);
- }
- }
-};
diff --git a/mobile/android/chrome/content/PluginHelper.js b/mobile/android/chrome/content/PluginHelper.js
deleted file mode 100644
index 59d87fa7c..000000000
--- a/mobile/android/chrome/content/PluginHelper.js
+++ /dev/null
@@ -1,221 +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 PluginHelper = {
- showDoorHanger: function(aTab) {
- if (!aTab.browser)
- return;
-
- // Even though we may not end up showing a doorhanger, this flag
- // lets us know that we've tried to show a doorhanger.
- aTab.shouldShowPluginDoorhanger = false;
-
- let uri = aTab.browser.currentURI;
-
- // If the user has previously set a plugins permission for this website,
- // either play or don't play the plugins instead of showing a doorhanger.
- let permValue = Services.perms.testPermission(uri, "plugins");
- if (permValue != Services.perms.UNKNOWN_ACTION) {
- if (permValue == Services.perms.ALLOW_ACTION)
- PluginHelper.playAllPlugins(aTab.browser.contentWindow);
-
- return;
- }
-
- let message = Strings.browser.formatStringFromName("clickToPlayPlugins.message2",
- [uri.host], 1);
- let buttons = [
- {
- label: Strings.browser.GetStringFromName("clickToPlayPlugins.dontActivate"),
- callback: function(aChecked) {
- // If the user checked "Don't ask again", make a permanent exception
- if (aChecked)
- Services.perms.add(uri, "plugins", Ci.nsIPermissionManager.DENY_ACTION);
-
- // Other than that, do nothing
- }
- },
- {
- label: Strings.browser.GetStringFromName("clickToPlayPlugins.activate"),
- callback: function(aChecked) {
- // If the user checked "Don't ask again", make a permanent exception
- if (aChecked)
- Services.perms.add(uri, "plugins", Ci.nsIPermissionManager.ALLOW_ACTION);
-
- PluginHelper.playAllPlugins(aTab.browser.contentWindow);
- },
- positive: true
- }
- ];
-
- // Add a checkbox with a "Don't ask again" message if the uri contains a
- // host. Adding a permanent exception will fail if host is not present.
- let options = uri.host ? { checkbox: Strings.browser.GetStringFromName("clickToPlayPlugins.dontAskAgain") } : {};
-
- NativeWindow.doorhanger.show(message, "ask-to-play-plugins", buttons, aTab.id, options);
- },
-
- delayAndShowDoorHanger: function(aTab) {
- // To avoid showing the doorhanger if there are also visible plugin
- // overlays on the page, delay showing the doorhanger to check if
- // visible plugins get added in the near future.
- if (!aTab.pluginDoorhangerTimeout) {
- aTab.pluginDoorhangerTimeout = setTimeout(function() {
- if (this.shouldShowPluginDoorhanger) {
- PluginHelper.showDoorHanger(this);
- }
- }.bind(aTab), 500);
- }
- },
-
- playAllPlugins: function(aContentWindow) {
- let cwu = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
- // XXX not sure if we should enable plugins for the parent documents...
- let plugins = cwu.plugins;
- if (!plugins || !plugins.length)
- return;
-
- plugins.forEach(this.playPlugin);
- },
-
- playPlugin: function(plugin) {
- let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
- if (!objLoadingContent.activated)
- objLoadingContent.playPlugin();
- },
-
- getPluginPreference: function getPluginPreference() {
- let pluginDisable = Services.prefs.getBoolPref("plugin.disable");
- if (pluginDisable)
- return "0";
-
- let state = Services.prefs.getIntPref("plugin.default.state");
- return state == Ci.nsIPluginTag.STATE_CLICKTOPLAY ? "2" : "1";
- },
-
- setPluginPreference: function setPluginPreference(aValue) {
- switch (aValue) {
- case "0": // Enable Plugins = No
- Services.prefs.setBoolPref("plugin.disable", true);
- Services.prefs.clearUserPref("plugin.default.state");
- break;
- case "1": // Enable Plugins = Yes
- Services.prefs.clearUserPref("plugin.disable");
- Services.prefs.setIntPref("plugin.default.state", Ci.nsIPluginTag.STATE_ENABLED);
- break;
- case "2": // Enable Plugins = Tap to Play (default)
- Services.prefs.clearUserPref("plugin.disable");
- Services.prefs.clearUserPref("plugin.default.state");
- break;
- }
- },
-
- // Copied from /browser/base/content/browser.js
- isTooSmall : function (plugin, overlay) {
- // Is the <object>'s size too small to hold what we want to show?
- let pluginRect = plugin.getBoundingClientRect();
- // XXX bug 446693. The text-shadow on the submitted-report text at
- // the bottom causes scrollHeight to be larger than it should be.
- let overflows = (overlay.scrollWidth > pluginRect.width) ||
- (overlay.scrollHeight - 5 > pluginRect.height);
-
- return overflows;
- },
-
- getPluginMimeType: function (plugin) {
- var tagMimetype = plugin.actualType;
-
- if (tagMimetype == "") {
- tagMimetype = plugin.type;
- }
-
- return tagMimetype;
- },
-
- handlePluginBindingAttached: function (aTab, aEvent) {
- let plugin = aEvent.target;
- let doc = plugin.ownerDocument;
- let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
- if (!overlay || overlay._bindingHandled) {
- return;
- }
- overlay._bindingHandled = true;
-
- let eventType = PluginHelper._getBindingType(plugin);
- if (!eventType) {
- // Not all bindings have handlers
- return;
- }
-
- switch (eventType) {
- case "PluginClickToPlay": {
- // Check if plugins have already been activated for this page, or if
- // the user has set a permission to always play plugins on the site
- if (aTab.clickToPlayPluginsActivated ||
- Services.perms.testPermission(aTab.browser.currentURI, "plugins") ==
- Services.perms.ALLOW_ACTION) {
- PluginHelper.playPlugin(plugin);
- return;
- }
-
- // If the plugin is hidden, or if the overlay is too small, show a
- // doorhanger notification
- if (PluginHelper.isTooSmall(plugin, overlay)) {
- PluginHelper.delayAndShowDoorHanger(aTab);
- } else {
- // There's a large enough visible overlay that we don't need to show
- // the doorhanger.
- aTab.shouldShowPluginDoorhanger = false;
- overlay.classList.add("visible");
- }
-
- // Add click to play listener to the overlay
- overlay.addEventListener("click", function(e) {
- if (!e.isTrusted)
- return;
- e.preventDefault();
- let win = e.target.ownerDocument.defaultView.top;
- let tab = BrowserApp.getTabForWindow(win);
- tab.clickToPlayPluginsActivated = true;
- PluginHelper.playAllPlugins(win);
-
- NativeWindow.doorhanger.hide("ask-to-play-plugins", tab.id);
- }, true);
-
- // Add handlers for over- and underflow in case the plugin gets resized
- plugin.addEventListener("overflow", function(event) {
- overlay.classList.remove("visible");
- PluginHelper.delayAndShowDoorHanger(aTab);
- });
- plugin.addEventListener("underflow", function(event) {
- // This is also triggered if only one dimension underflows,
- // the other dimension might still overflow
- if (!PluginHelper.isTooSmall(plugin, overlay)) {
- overlay.classList.add("visible");
- }
- });
-
- break;
- }
- }
- },
-
- // Helper to get the binding handler type from a plugin object
- _getBindingType: function(plugin) {
- if (!(plugin instanceof Ci.nsIObjectLoadingContent))
- return null;
-
- switch (plugin.pluginFallbackType) {
- case Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED:
- return "PluginNotFound";
- case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
- return "PluginClickToPlay";
- default:
- // Not all states map to a handler
- return null;
- }
- }
-};
diff --git a/mobile/android/chrome/content/PresentationView.js b/mobile/android/chrome/content/PresentationView.js
deleted file mode 100644
index 4f7e02870..000000000
--- a/mobile/android/chrome/content/PresentationView.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/* -*- Mode: tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-const TOPIC_PRESENTATION_VIEW_READY = "presentation-view-ready";
-const TOPIC_PRESENTATION_RECEIVER_LAUNCH = "presentation-receiver:launch";
-const TOPIC_PRESENTATION_RECEIVER_LAUNCH_RESPONSE = "presentation-receiver:launch:response";
-
-// globals Services
-Cu.import("resource://gre/modules/Services.jsm");
-
-function log(str) {
- // dump("-*- PresentationView.js -*-: " + str + "\n");
-}
-
-let PresentationView = {
- _id: null,
-
- startup: function startup() {
- // use hash as the ID of this top level window
- this._id = window.location.hash.substr(1);
-
- // Listen "presentation-receiver:launch" sent from
- // PresentationRequestUIGlue.
- Services.obs.addObserver(this,TOPIC_PRESENTATION_RECEIVER_LAUNCH, false);
-
- // Notify PresentationView is ready.
- Services.obs.notifyObservers(null, TOPIC_PRESENTATION_VIEW_READY, this._id);
- },
-
- stop: function stop() {
- Services.obs.removeObserver(this, TOPIC_PRESENTATION_RECEIVER_LAUNCH);
- },
-
- observe: function observe(aSubject, aTopic, aData) {
- log("Got observe: aTopic=" + aTopic);
-
- let requestData = JSON.parse(aData);
- if (this._id != requestData.windowId) {
- return;
- }
-
- let browser = document.getElementById("content");
- browser.setAttribute("mozpresentation", requestData.url);
- try {
- browser.loadURI(requestData.url);
- Services.obs.notifyObservers(browser,
- TOPIC_PRESENTATION_RECEIVER_LAUNCH_RESPONSE,
- JSON.stringify({ result: "success",
- requestId: requestData.requestId }));
- } catch (e) {
- Services.obs.notifyObservers(null,
- TOPIC_PRESENTATION_RECEIVER_LAUNCH_RESPONSE,
- JSON.stringify({ result: "error",
- reason: e.message }));
- }
- }
-};
diff --git a/mobile/android/chrome/content/PresentationView.xul b/mobile/android/chrome/content/PresentationView.xul
deleted file mode 100644
index 00440453c..000000000
--- a/mobile/android/chrome/content/PresentationView.xul
+++ /dev/null
@@ -1,15 +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/. -->
-
-<window id="presentation-window"
- onload="PresentationView.startup();"
- onunload="PresentationView.stop();"
- windowtype="navigator:browser"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
- <browser id="content" type="content-targetable" src="about:blank" flex="1"/>
-
- <script type="application/javascript" src="chrome://browser/content/PresentationView.js"/>
-</window>
diff --git a/mobile/android/chrome/content/PrintHelper.js b/mobile/android/chrome/content/PrintHelper.js
deleted file mode 100644
index 9b071ee92..000000000
--- a/mobile/android/chrome/content/PrintHelper.js
+++ /dev/null
@@ -1,73 +0,0 @@
-// -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
-/* 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";
-
-XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm");
-
-var PrintHelper = {
- init: function() {
- Services.obs.addObserver(this, "Print:PDF", false);
- },
-
- observe: function (aSubject, aTopic, aData) {
- let browser = BrowserApp.selectedBrowser;
-
- switch (aTopic) {
- case "Print:PDF":
- Messaging.handleRequest(aTopic, aData, (data) => {
- return this.generatePDF(browser);
- });
- break;
- }
- },
-
- generatePDF: function(aBrowser) {
- // Create the final destination file location
- let fileName = ContentAreaUtils.getDefaultFileName(aBrowser.contentTitle, aBrowser.currentURI, null, null);
- fileName = fileName.trim() + ".pdf";
-
- let file = Services.dirsvc.get("TmpD", Ci.nsIFile);
- file.append(fileName);
- file.createUnique(file.NORMAL_FILE_TYPE, parseInt("666", 8));
-
- let printSettings = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(Ci.nsIPrintSettingsService).newPrintSettings;
- printSettings.printSilent = true;
- printSettings.showPrintProgress = false;
- printSettings.printBGImages = false;
- printSettings.printBGColors = false;
- printSettings.printToFile = true;
- printSettings.toFileName = file.path;
- printSettings.printFrameType = Ci.nsIPrintSettings.kFramesAsIs;
- printSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
-
- let webBrowserPrint = aBrowser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebBrowserPrint);
-
- return new Promise((resolve, reject) => {
- webBrowserPrint.print(printSettings, {
- onStateChange: function(webProgress, request, stateFlags, status) {
- // We get two STATE_START calls, one for STATE_IS_DOCUMENT and one for STATE_IS_NETWORK
- if (stateFlags & Ci.nsIWebProgressListener.STATE_START && stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
- // Let the user know something is happening. Generating the PDF can take some time.
- Snackbars.show(Strings.browser.GetStringFromName("alertPrintjobToast"), Snackbars.LENGTH_LONG);
- }
-
- // We get two STATE_STOP calls, one for STATE_IS_DOCUMENT and one for STATE_IS_NETWORK
- if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP && stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
- if (Components.isSuccessCode(status)) {
- // Send the details to Java
- resolve({ file: file.path, title: fileName });
- } else {
- reject();
- }
- }
- },
- onProgressChange: function () {},
- onLocationChange: function () {},
- onStatusChange: function () {},
- onSecurityChange: function () {},
- });
- });
- }
-};
diff --git a/mobile/android/chrome/content/Reader.js b/mobile/android/chrome/content/Reader.js
deleted file mode 100644
index d0f3d7801..000000000
--- a/mobile/android/chrome/content/Reader.js
+++ /dev/null
@@ -1,290 +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/. */
-
-"use strict";
-
-XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm");
-
-/*globals MAX_URI_LENGTH, MAX_TITLE_LENGTH */
-
-var Reader = {
- // These values should match those defined in BrowserContract.java.
- STATUS_UNFETCHED: 0,
- STATUS_FETCH_FAILED_TEMPORARY: 1,
- STATUS_FETCH_FAILED_PERMANENT: 2,
- STATUS_FETCH_FAILED_UNSUPPORTED_FORMAT: 3,
- STATUS_FETCHED_ARTICLE: 4,
-
- get _hasUsedToolbar() {
- delete this._hasUsedToolbar;
- return this._hasUsedToolbar = Services.prefs.getBoolPref("reader.has_used_toolbar");
- },
-
- /**
- * BackPressListener (listeners / ReaderView Ids).
- */
- _backPressListeners: [],
- _backPressViewIds: [],
-
- /**
- * Set a backPressListener for this tabId / ReaderView Id pair.
- */
- _addBackPressListener: function(tabId, viewId, listener) {
- this._backPressListeners[tabId] = listener;
- this._backPressViewIds[viewId] = tabId;
- },
-
- /**
- * Remove a backPressListener for this ReaderView Id.
- */
- _removeBackPressListener: function(viewId) {
- let tabId = this._backPressViewIds[viewId];
- if (tabId != undefined) {
- this._backPressListeners[tabId] = null;
- delete this._backPressViewIds[viewId];
- }
- },
-
- /**
- * If the requested tab has a backPress listener, return its results, else false.
- */
- onBackPress: function(tabId) {
- let listener = this._backPressListeners[tabId];
- return { handled: (listener ? listener() : false) };
- },
-
- observe: function Reader_observe(aMessage, aTopic, aData) {
- switch (aTopic) {
- case "Reader:RemoveFromCache": {
- ReaderMode.removeArticleFromCache(aData).catch(e => Cu.reportError("Error removing article from cache: " + e));
- break;
- }
-
- case "Reader:AddToCache": {
- let tab = BrowserApp.getTabForId(aData);
- if (!tab) {
- throw new Error("No tab for tabID = " + aData + " when trying to save reader view article");
- }
-
- // If the article is coming from reader mode, we must have fetched it already.
- this._getArticleData(tab.browser).then((article) => {
- ReaderMode.storeArticleInCache(article);
- }).catch(e => Cu.reportError("Error storing article in cache: " + e));
- break;
- }
- }
- },
-
- receiveMessage: function(message) {
- switch (message.name) {
- case "Reader:ArticleGet":
- this._getArticle(message.data.url).then((article) => {
- // Make sure the target browser is still alive before trying to send data back.
- if (message.target.messageManager) {
- message.target.messageManager.sendAsyncMessage("Reader:ArticleData", { article: article });
- }
- }, e => {
- if (e && e.newURL) {
- message.target.loadURI("about:reader?url=" + encodeURIComponent(e.newURL));
- }
- });
- break;
-
- // On DropdownClosed in ReaderView, we cleanup / clear existing BackPressListener.
- case "Reader:DropdownClosed": {
- this._removeBackPressListener(message.data);
- break;
- }
-
- // On DropdownOpened in ReaderView, we add BackPressListener to handle a subsequent BACK request.
- case "Reader:DropdownOpened": {
- let tabId = BrowserApp.selectedTab.id;
- this._addBackPressListener(tabId, message.data, () => {
- // User hit BACK key while ReaderView has the banner font-dropdown opened.
- // Close it and return prevent-default.
- if (message.target.messageManager) {
- message.target.messageManager.sendAsyncMessage("Reader:CloseDropdown");
- return true;
- }
- // We can assume ReaderView banner's font-dropdown doesn't need to be closed.
- return false;
- });
-
- break;
- }
-
- case "Reader:FaviconRequest": {
- Messaging.sendRequestForResult({
- type: "Reader:FaviconRequest",
- url: message.data.url
- }).then(data => {
- message.target.messageManager.sendAsyncMessage("Reader:FaviconReturn", JSON.parse(data));
- });
- break;
- }
-
- case "Reader:SystemUIVisibility":
- Messaging.sendRequest({
- type: "SystemUI:Visibility",
- visible: message.data.visible
- });
- break;
-
- case "Reader:ToolbarHidden":
- if (!this._hasUsedToolbar) {
- Snackbars.show(Strings.browser.GetStringFromName("readerMode.toolbarTip"), Snackbars.LENGTH_LONG);
- Services.prefs.setBoolPref("reader.has_used_toolbar", true);
- this._hasUsedToolbar = true;
- }
- break;
-
- case "Reader:UpdateReaderButton": {
- let tab = BrowserApp.getTabForBrowser(message.target);
- tab.browser.isArticle = message.data.isArticle;
- this.updatePageAction(tab);
- break;
- }
- }
- },
-
- pageAction: {
- readerModeCallback: function(browser) {
- let url = browser.currentURI.spec;
- if (url.startsWith("about:reader")) {
- UITelemetry.addEvent("action.1", "button", null, "reader_exit");
- } else {
- UITelemetry.addEvent("action.1", "button", null, "reader_enter");
- }
- browser.messageManager.sendAsyncMessage("Reader:ToggleReaderMode");
- },
- },
-
- updatePageAction: function(tab) {
- if (!tab.getActive()) {
- return;
- }
-
- if (this.pageAction.id) {
- PageActions.remove(this.pageAction.id);
- delete this.pageAction.id;
- }
-
- let showPageAction = (icon, title) => {
- this.pageAction.id = PageActions.add({
- icon: icon,
- title: title,
- clickCallback: () => this.pageAction.readerModeCallback(browser),
- important: true
- });
- };
-
- let browser = tab.browser;
- if (browser.currentURI.spec.startsWith("about:reader")) {
- showPageAction("drawable://reader_active", Strings.reader.GetStringFromName("readerView.close"));
- // Only start a reader session if the viewer is in the foreground. We do
- // not track background reader viewers.
- UITelemetry.startSession("reader.1", null);
- return;
- }
-
- // Only stop a reader session if the foreground viewer is not visible.
- UITelemetry.stopSession("reader.1", "", null);
-
- if (browser.isArticle) {
- showPageAction("drawable://reader", Strings.reader.GetStringFromName("readerView.enter"));
- UITelemetry.addEvent("show.1", "button", null, "reader_available");
- } else {
- UITelemetry.addEvent("show.1", "button", null, "reader_unavailable");
- }
- },
-
- /**
- * Gets an article for a given URL. This method will download and parse a document
- * if it does not find the article in the cache.
- *
- * @param url The article URL.
- * @return {Promise}
- * @resolves JS object representing the article, or null if no article is found.
- */
- _getArticle: Task.async(function* (url) {
- // First try to find a parsed article in the cache.
- let article = yield ReaderMode.getArticleFromCache(url);
- if (article) {
- return article;
- }
-
- // Article hasn't been found in the cache, we need to
- // download the page and parse the article out of it.
- return yield ReaderMode.downloadAndParseDocument(url).catch(e => {
- if (e && e.newURL) {
- // Pass up the error so we can navigate the browser in question to the new URL:
- throw e;
- }
- Cu.reportError("Error downloading and parsing document: " + e);
- return null;
- });
- }),
-
- _getArticleData: function(browser) {
- return new Promise((resolve, reject) => {
- if (browser == null) {
- reject("_getArticleData needs valid browser");
- }
-
- let mm = browser.messageManager;
- let listener = (message) => {
- mm.removeMessageListener("Reader:StoredArticleData", listener);
- resolve(message.data.article);
- };
- mm.addMessageListener("Reader:StoredArticleData", listener);
- mm.sendAsyncMessage("Reader:GetStoredArticleData");
- });
- },
-
-
- /**
- * Migrates old indexedDB reader mode cache to new JSON cache.
- */
- migrateCache: Task.async(function* () {
- let cacheDB = yield new Promise((resolve, reject) => {
- let request = window.indexedDB.open("about:reader", 1);
- request.onsuccess = event => resolve(event.target.result);
- request.onerror = event => reject(request.error);
-
- // If there is no DB to migrate, don't do anything.
- request.onupgradeneeded = event => resolve(null);
- });
-
- if (!cacheDB) {
- return;
- }
-
- let articles = yield new Promise((resolve, reject) => {
- let articles = [];
-
- let transaction = cacheDB.transaction(cacheDB.objectStoreNames);
- let store = transaction.objectStore(cacheDB.objectStoreNames[0]);
-
- let request = store.openCursor();
- request.onsuccess = event => {
- let cursor = event.target.result;
- if (!cursor) {
- resolve(articles);
- } else {
- articles.push(cursor.value);
- cursor.continue();
- }
- };
- request.onerror = event => reject(request.error);
- });
-
- for (let article of articles) {
- yield ReaderMode.storeArticleInCache(article);
- }
-
- // Delete the database.
- window.indexedDB.deleteDatabase("about:reader");
- }),
-};
diff --git a/mobile/android/chrome/content/RemoteDebugger.js b/mobile/android/chrome/content/RemoteDebugger.js
deleted file mode 100644
index a5a3a43de..000000000
--- a/mobile/android/chrome/content/RemoteDebugger.js
+++ /dev/null
@@ -1,355 +0,0 @@
-// -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
-/* 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/. */
-/* globals DebuggerServer */
-"use strict";
-
-XPCOMUtils.defineLazyGetter(this, "DebuggerServer", () => {
- let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
- let { DebuggerServer } = require("devtools/server/main");
- return DebuggerServer;
-});
-
-var RemoteDebugger = {
- init() {
- USBRemoteDebugger.init();
- WiFiRemoteDebugger.init();
- },
-
- get isAnyEnabled() {
- return USBRemoteDebugger.isEnabled || WiFiRemoteDebugger.isEnabled;
- },
-
- /**
- * Prompt the user to accept or decline the incoming connection.
- *
- * @param session object
- * The session object will contain at least the following fields:
- * {
- * authentication,
- * client: {
- * host,
- * port
- * },
- * server: {
- * host,
- * port
- * }
- * }
- * Specific authentication modes may include additional fields. Check
- * the different |allowConnection| methods in
- * devtools/shared/security/auth.js.
- * @return An AuthenticationResult value.
- * A promise that will be resolved to the above is also allowed.
- */
- allowConnection(session) {
- if (this._promptingForAllow) {
- // Don't stack connection prompts if one is already open
- return DebuggerServer.AuthenticationResult.DENY;
- }
-
- if (!session.server.port) {
- this._promptingForAllow = this._promptForUSB(session);
- } else {
- this._promptingForAllow = this._promptForTCP(session);
- }
- this._promptingForAllow.then(() => this._promptingForAllow = null);
-
- return this._promptingForAllow;
- },
-
- _promptForUSB(session) {
- if (session.authentication !== 'PROMPT') {
- // This dialog is not prepared for any other authentication method at
- // this time.
- return DebuggerServer.AuthenticationResult.DENY;
- }
-
- return new Promise(resolve => {
- let title = Strings.browser.GetStringFromName("remoteIncomingPromptTitle");
- let msg = Strings.browser.GetStringFromName("remoteIncomingPromptUSB");
- let allow = Strings.browser.GetStringFromName("remoteIncomingPromptAllow");
- let deny = Strings.browser.GetStringFromName("remoteIncomingPromptDeny");
-
- // Make prompt. Note: button order is in reverse.
- let prompt = new Prompt({
- window: null,
- hint: "remotedebug",
- title: title,
- message: msg,
- buttons: [ allow, deny ],
- priority: 1
- });
-
- prompt.show(data => {
- let result = data.button;
- if (result === 0) {
- resolve(DebuggerServer.AuthenticationResult.ALLOW);
- } else {
- resolve(DebuggerServer.AuthenticationResult.DENY);
- }
- });
- });
- },
-
- _promptForTCP(session) {
- if (session.authentication !== 'OOB_CERT' || !session.client.cert) {
- // This dialog is not prepared for any other authentication method at
- // this time.
- return DebuggerServer.AuthenticationResult.DENY;
- }
-
- return new Promise(resolve => {
- let title = Strings.browser.GetStringFromName("remoteIncomingPromptTitle");
- let msg = Strings.browser.formatStringFromName("remoteIncomingPromptTCP", [
- session.client.host,
- session.client.port
- ], 2);
- let scan = Strings.browser.GetStringFromName("remoteIncomingPromptScan");
- let scanAndRemember = Strings.browser.GetStringFromName("remoteIncomingPromptScanAndRemember");
- let deny = Strings.browser.GetStringFromName("remoteIncomingPromptDeny");
-
- // Make prompt. Note: button order is in reverse.
- let prompt = new Prompt({
- window: null,
- hint: "remotedebug",
- title: title,
- message: msg,
- buttons: [ scan, scanAndRemember, deny ],
- priority: 1
- });
-
- prompt.show(data => {
- let result = data.button;
- if (result === 0) {
- resolve(DebuggerServer.AuthenticationResult.ALLOW);
- } else if (result === 1) {
- resolve(DebuggerServer.AuthenticationResult.ALLOW_PERSIST);
- } else {
- resolve(DebuggerServer.AuthenticationResult.DENY);
- }
- });
- });
- },
-
- /**
- * During OOB_CERT authentication, the user must transfer some data through
- * some out of band mechanism from the client to the server to authenticate
- * the devices.
- *
- * This implementation instructs Fennec to invoke a QR decoder and return the
- * the data it contains back here.
- *
- * @return An object containing:
- * * sha256: hash(ClientCert)
- * * k : K(random 128-bit number)
- * A promise that will be resolved to the above is also allowed.
- */
- receiveOOB() {
- if (this._receivingOOB) {
- return this._receivingOOB;
- }
-
- this._receivingOOB = Messaging.sendRequestForResult({
- type: "DevToolsAuth:Scan"
- }).then(data => {
- return JSON.parse(data);
- }, () => {
- let title = Strings.browser.GetStringFromName("remoteQRScanFailedPromptTitle");
- let msg = Strings.browser.GetStringFromName("remoteQRScanFailedPromptMessage");
- let ok = Strings.browser.GetStringFromName("remoteQRScanFailedPromptOK");
- let prompt = new Prompt({
- window: null,
- hint: "remotedebug",
- title: title,
- message: msg,
- buttons: [ ok ],
- priority: 1
- });
- prompt.show();
- });
-
- this._receivingOOB.then(() => this._receivingOOB = null);
-
- return this._receivingOOB;
- },
-
- initServer: function() {
- if (DebuggerServer.initialized) {
- return;
- }
-
- DebuggerServer.init();
-
- // Add browser and Fennec specific actors
- DebuggerServer.addBrowserActors();
- DebuggerServer.registerModule("resource://gre/modules/dbg-browser-actors.js");
-
- // Allow debugging of chrome for any process
- DebuggerServer.allowChromeProcess = true;
- }
-};
-
-RemoteDebugger.allowConnection =
- RemoteDebugger.allowConnection.bind(RemoteDebugger);
-RemoteDebugger.receiveOOB =
- RemoteDebugger.receiveOOB.bind(RemoteDebugger);
-
-var USBRemoteDebugger = {
-
- init() {
- Services.prefs.addObserver("devtools.", this, false);
-
- if (this.isEnabled) {
- this.start();
- }
- },
-
- observe(subject, topic, data) {
- if (topic != "nsPref:changed") {
- return;
- }
-
- switch (data) {
- case "devtools.remote.usb.enabled":
- Services.prefs.setBoolPref("devtools.debugger.remote-enabled",
- RemoteDebugger.isAnyEnabled);
- if (this.isEnabled) {
- this.start();
- } else {
- this.stop();
- }
- break;
-
- case "devtools.debugger.remote-port":
- case "devtools.debugger.unix-domain-socket":
- if (this.isEnabled) {
- this.stop();
- this.start();
- }
- break;
- }
- },
-
- get isEnabled() {
- return Services.prefs.getBoolPref("devtools.remote.usb.enabled");
- },
-
- start: function() {
- if (this._listener) {
- return;
- }
-
- RemoteDebugger.initServer();
-
- let portOrPath =
- Services.prefs.getCharPref("devtools.debugger.unix-domain-socket") ||
- Services.prefs.getIntPref("devtools.debugger.remote-port");
-
- try {
- dump("Starting USB debugger on " + portOrPath);
- let AuthenticatorType = DebuggerServer.Authenticators.get("PROMPT");
- let authenticator = new AuthenticatorType.Server();
- authenticator.allowConnection = RemoteDebugger.allowConnection;
- this._listener = DebuggerServer.createListener();
- this._listener.portOrPath = portOrPath;
- this._listener.authenticator = authenticator;
- this._listener.open();
- } catch (e) {
- dump("Unable to start USB debugger server: " + e);
- }
- },
-
- stop: function() {
- if (!this._listener) {
- return;
- }
-
- try {
- this._listener.close();
- this._listener = null;
- } catch (e) {
- dump("Unable to stop USB debugger server: " + e);
- }
- }
-
-};
-
-var WiFiRemoteDebugger = {
-
- init() {
- Services.prefs.addObserver("devtools.", this, false);
-
- if (this.isEnabled) {
- this.start();
- }
- },
-
- observe(subject, topic, data) {
- if (topic != "nsPref:changed") {
- return;
- }
-
- switch (data) {
- case "devtools.remote.wifi.enabled":
- Services.prefs.setBoolPref("devtools.debugger.remote-enabled",
- RemoteDebugger.isAnyEnabled);
- // Allow remote debugging on non-local interfaces when WiFi debug is
- // enabled
- // TODO: Bug 1034411: Lock down to WiFi interface only
- Services.prefs.setBoolPref("devtools.debugger.force-local",
- !this.isEnabled);
- if (this.isEnabled) {
- this.start();
- } else {
- this.stop();
- }
- break;
- }
- },
-
- get isEnabled() {
- return Services.prefs.getBoolPref("devtools.remote.wifi.enabled");
- },
-
- start: function() {
- if (this._listener) {
- return;
- }
-
- RemoteDebugger.initServer();
-
- try {
- dump("Starting WiFi debugger");
- let AuthenticatorType = DebuggerServer.Authenticators.get("OOB_CERT");
- let authenticator = new AuthenticatorType.Server();
- authenticator.allowConnection = RemoteDebugger.allowConnection;
- authenticator.receiveOOB = RemoteDebugger.receiveOOB;
- this._listener = DebuggerServer.createListener();
- this._listener.portOrPath = -1 /* any available port */;
- this._listener.authenticator = authenticator;
- this._listener.discoverable = true;
- this._listener.encryption = true;
- this._listener.open();
- let port = this._listener.port;
- dump("Started WiFi debugger on " + port);
- } catch (e) {
- dump("Unable to start WiFi debugger server: " + e);
- }
- },
-
- stop: function() {
- if (!this._listener) {
- return;
- }
-
- try {
- this._listener.close();
- this._listener = null;
- } catch (e) {
- dump("Unable to stop WiFi debugger server: " + e);
- }
- }
-
-};
diff --git a/mobile/android/chrome/content/SelectHelper.js b/mobile/android/chrome/content/SelectHelper.js
deleted file mode 100644
index 41d0193d4..000000000
--- a/mobile/android/chrome/content/SelectHelper.js
+++ /dev/null
@@ -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/. */
-"use strict";
-
-var SelectHelper = {
- _uiBusy: false,
-
- handleEvent: function(event) {
- this.handleClick(event.target);
- },
-
- handleClick: function(target) {
- // if we're busy looking at a select we want to eat any clicks that
- // come to us, but not to process them
- if (this._uiBusy || !this._isMenu(target) || this._isDisabledElement(target)) {
- return;
- }
-
- this._uiBusy = true;
- this.show(target);
- this._uiBusy = false;
- },
-
- // This is a callback function to be provided to prompt.show(callBack).
- // It will update which Option elements in a Select have been selected
- // or unselected and fire the onChange event.
- _promptCallBack: function(data, element) {
- let selected = data.list;
-
- if (element instanceof Ci.nsIDOMXULMenuListElement) {
- if (element.selectedIndex != selected[0]) {
- element.selectedIndex = selected[0];
- this.fireOnCommand(element);
- }
- } else if (element instanceof HTMLSelectElement) {
- let changed = false;
- let i = 0; // The index for the element from `data.list` that we are currently examining.
- this.forVisibleOptions(element, function(node) {
- if (node.selected && selected.indexOf(i) == -1) {
- changed = true;
- node.selected = false;
- } else if (!node.selected && selected.indexOf(i) != -1) {
- changed = true;
- node.selected = true;
- }
- i++;
- });
-
- if (changed) {
- this.fireOnChange(element);
- }
- }
- },
-
- show: function(element) {
- let list = this.getListForElement(element);
- let p = new Prompt({
- window: element.ownerDocument.defaultView
- });
-
- if (element.multiple) {
- p.addButton({
- label: Strings.browser.GetStringFromName("selectHelper.closeMultipleSelectDialog")
- }).setMultiChoiceItems(list);
- } else {
- p.setSingleChoiceItems(list);
- }
-
- p.show((data) => {
- this._promptCallBack(data,element)
- });
- },
-
- _isMenu: function(element) {
- return (element instanceof HTMLSelectElement || element instanceof Ci.nsIDOMXULMenuListElement);
- },
-
- // Return a list of Option elements within a Select excluding
- // any that were not visible.
- getListForElement: function(element) {
- let index = 0;
- let items = [];
- this.forVisibleOptions(element, function(node, options,parent) {
- let item = {
- label: node.text || node.label,
- header: options.isGroup,
- disabled: node.disabled,
- id: index,
- selected: node.selected,
- };
-
- if (parent) {
- item.child = true;
- item.disabled = item.disabled || parent.disabled;
- }
- items.push(item);
- index++;
- });
- return items;
- },
-
- // Apply a function to all visible Option elements in a Select
- forVisibleOptions: function(element, aFunction, parent = null) {
- if (element instanceof Ci.nsIDOMXULMenuListElement) {
- element = element.menupopup;
- }
- let children = element.children;
- let numChildren = children.length;
-
-
- // if there are no children in this select, we add a dummy row so that at least something appears
- if (numChildren == 0) {
- aFunction.call(this, {label: ""}, {isGroup: false}, parent);
- }
-
- for (let i = 0; i < numChildren; i++) {
- let child = children[i];
- let style = window.getComputedStyle(child, null);
- if (style.display !== "none") {
- if (child instanceof HTMLOptionElement ||
- child instanceof Ci.nsIDOMXULSelectControlItemElement) {
- aFunction.call(this, child, {isGroup: false}, parent);
- } else if (child instanceof HTMLOptGroupElement) {
- aFunction.call(this, child, {isGroup: true});
- this.forVisibleOptions(child, aFunction, child);
- }
- }
- }
- },
-
- fireOnChange: function(element) {
- let event = element.ownerDocument.createEvent("Events");
- event.initEvent("change", true, true, element.defaultView, 0,
- false, false, false, false, null);
- setTimeout(function() {
- element.dispatchEvent(event);
- }, 0);
- },
-
- fireOnCommand: function(element) {
- let event = element.ownerDocument.createEvent("XULCommandEvent");
- event.initCommandEvent("command", true, true, element.defaultView, 0,
- false, false, false, false, null);
- setTimeout(function() {
- element.dispatchEvent(event);
- }, 0);
- },
-
- _isDisabledElement : function(element) {
- let currentElement = element;
- while (currentElement) {
- // Must test with === in case a form has a field named "disabled". See bug 1263589.
- if (currentElement.disabled === true) {
- return true;
- }
- currentElement = currentElement.parentElement;
- }
- return false;
- }
-};
diff --git a/mobile/android/chrome/content/WebcompatReporter.js b/mobile/android/chrome/content/WebcompatReporter.js
deleted file mode 100644
index 66aefdda0..000000000
--- a/mobile/android/chrome/content/WebcompatReporter.js
+++ /dev/null
@@ -1,144 +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 { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
- "resource://gre/modules/PrivateBrowsingUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm");
-
-var WebcompatReporter = {
- menuItem: null,
- menuItemEnabled: null,
- init: function() {
- Services.obs.addObserver(this, "DesktopMode:Change", false);
- Services.obs.addObserver(this, "chrome-document-global-created", false);
- Services.obs.addObserver(this, "content-document-global-created", false);
-
- let visible = true;
- if ("@mozilla.org/parental-controls-service;1" in Cc) {
- let pc = Cc["@mozilla.org/parental-controls-service;1"].createInstance(Ci.nsIParentalControlsService);
- visible = !pc.parentalControlsEnabled;
- }
-
- this.addMenuItem(visible);
- },
-
- observe: function(subject, topic, data) {
- if (topic == "content-document-global-created" || topic == "chrome-document-global-created") {
- let win = subject;
- let currentURI = win.document.documentURI;
-
- // Ignore non top-level documents
- if (currentURI !== win.top.location.href) {
- return;
- }
-
- if (!this.menuItemEnabled && this.isReportableUrl(currentURI)) {
- NativeWindow.menu.update(this.menuItem, {enabled: true});
- this.menuItemEnabled = true;
- } else if (this.menuItemEnabled && !this.isReportableUrl(currentURI)) {
- NativeWindow.menu.update(this.menuItem, {enabled: false});
- this.menuItemEnabled = false;
- }
- } else if (topic === "DesktopMode:Change") {
- let args = JSON.parse(data);
- let tab = BrowserApp.getTabForId(args.tabId);
- let currentURI = tab.browser.currentURI.spec;
- if (args.desktopMode && this.isReportableUrl(currentURI)) {
- this.reportDesktopModePrompt(tab);
- }
- }
- },
-
- addMenuItem: function(visible) {
- this.menuItem = NativeWindow.menu.add({
- name: this.strings.GetStringFromName("webcompat.menu.name"),
- callback: () => {
- Promise.resolve(BrowserApp.selectedTab).then(this.getScreenshot)
- .then(this.reportIssue)
- .catch(Cu.reportError);
- },
- enabled: false,
- visible: visible,
- });
- },
-
- getScreenshot: (tab) => {
- return new Promise((resolve) => {
- try {
- let win = tab.window;
- let dpr = win.devicePixelRatio;
- let canvas = win.document.createElement("canvas");
- let ctx = canvas.getContext("2d");
- // Grab the visible viewport coordinates
- let x = win.document.documentElement.scrollLeft;
- let y = win.document.documentElement.scrollTop;
- let w = win.innerWidth;
- let h = win.innerHeight;
- // Scale according to devicePixelRatio and coordinates
- canvas.width = dpr * w;
- canvas.height = dpr * h;
- ctx.scale(dpr, dpr);
- ctx.drawWindow(win, x, y, w, h, '#ffffff');
- let screenshot = canvas.toDataURL();
- resolve({tab: tab, data: screenshot});
- } catch (e) {
- // drawWindow can fail depending on memory or surface size. Rather than reject here,
- // we resolve the URL so the user can continue to file an issue without a screenshot.
- Cu.reportError("WebCompatReporter: getting a screenshot failed: " + e);
- resolve({tab: tab});
- }
- });
- },
-
- isReportableUrl: function(url) {
- return url && !(url.startsWith("about") ||
- url.startsWith("chrome") ||
- url.startsWith("file") ||
- url.startsWith("resource"));
- },
-
- reportDesktopModePrompt: function(tab) {
- let message = this.strings.GetStringFromName("webcompat.reportDesktopMode.message");
- let options = {
- action: {
- label: this.strings.GetStringFromName("webcompat.reportDesktopModeYes.label"),
- callback: () => this.reportIssue({tab: tab})
- }
- };
- Snackbars.show(message, Snackbars.LENGTH_LONG, options);
- },
-
- reportIssue: (tabData) => {
- return new Promise((resolve) => {
- const WEBCOMPAT_ORIGIN = "https://webcompat.com";
- let url = tabData.tab.browser.currentURI.spec
- let webcompatURL = `${WEBCOMPAT_ORIGIN}/issues/new?url=${url}`;
-
- if (tabData.data && typeof tabData.data === "string") {
- BrowserApp.deck.addEventListener("DOMContentLoaded", function sendDataToTab(event) {
- BrowserApp.deck.removeEventListener("DOMContentLoaded", sendDataToTab, false);
-
- if (event.target.defaultView.location.origin === WEBCOMPAT_ORIGIN) {
- // Waive Xray vision so event.origin is not chrome://browser on the other side.
- let win = Cu.waiveXrays(event.target.defaultView);
- win.postMessage(tabData.data, WEBCOMPAT_ORIGIN);
- }
- }, false);
- }
-
- let isPrivateTab = PrivateBrowsingUtils.isBrowserPrivate(tabData.tab.browser);
- BrowserApp.addTab(webcompatURL, {parentId: tabData.tab.id, isPrivate: isPrivateTab});
- resolve();
- });
- }
-};
-
-XPCOMUtils.defineLazyGetter(WebcompatReporter, "strings", function() {
- return Services.strings.createBundle("chrome://browser/locale/webcompatReporter.properties");
-});
diff --git a/mobile/android/chrome/content/WebrtcUI.js b/mobile/android/chrome/content/WebrtcUI.js
deleted file mode 100644
index 475d05bd2..000000000
--- a/mobile/android/chrome/content/WebrtcUI.js
+++ /dev/null
@@ -1,302 +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.EXPORTED_SYMBOLS = ["WebrtcUI"];
-
-XPCOMUtils.defineLazyModuleGetter(this, "Notifications", "resource://gre/modules/Notifications.jsm");
-XPCOMUtils.defineLazyServiceGetter(this, "ParentalControls", "@mozilla.org/parental-controls-service;1", "nsIParentalControlsService");
-XPCOMUtils.defineLazyModuleGetter(this, "RuntimePermissions", "resource://gre/modules/RuntimePermissions.jsm");
-
-var WebrtcUI = {
- _notificationId: null,
-
- // Add-ons can override stock permission behavior by doing:
- //
- // var stockObserve = WebrtcUI.observe;
- //
- // webrtcUI.observe = function(aSubject, aTopic, aData) {
- // switch (aTopic) {
- // case "PeerConnection:request": {
- // // new code.
- // break;
- // ...
- // default:
- // return stockObserve.call(this, aSubject, aTopic, aData);
- //
- // See browser/modules/webrtcUI.jsm for details.
-
- observe: function(aSubject, aTopic, aData) {
- if (aTopic === "getUserMedia:request") {
- RuntimePermissions
- .waitForPermissions(this._determineNeededRuntimePermissions(aSubject))
- .then((permissionGranted) => {
- if (permissionGranted) {
- WebrtcUI.handleGumRequest(aSubject, aTopic, aData);
- } else {
- Services.obs.notifyObservers(null, "getUserMedia:response:deny", aSubject.callID);
- }});
- } else if (aTopic === "PeerConnection:request") {
- this.handlePCRequest(aSubject, aTopic, aData);
- } else if (aTopic === "recording-device-events") {
- switch (aData) {
- case "shutdown":
- case "starting":
- this.notify();
- break;
- }
- } else if (aTopic === "VideoCapture:Paused") {
- if (this._notificationId) {
- Notifications.cancel(this._notificationId);
- this._notificationId = null;
- }
- } else if (aTopic === "VideoCapture:Resumed") {
- this.notify();
- }
- },
-
- notify: function() {
- let windows = MediaManagerService.activeMediaCaptureWindows;
- let count = windows.length;
- let msg = {};
- if (count == 0) {
- if (this._notificationId) {
- Notifications.cancel(this._notificationId);
- this._notificationId = null;
- }
- } else {
- let notificationOptions = {
- title: Strings.brand.GetStringFromName("brandShortName"),
- when: null, // hide the date row
- light: [0xFF9500FF, 1000, 1000],
- ongoing: true
- };
-
- let cameraActive = false;
- let audioActive = false;
- for (let i = 0; i < count; i++) {
- let win = windows.queryElementAt(i, Ci.nsIDOMWindow);
- let hasAudio = {};
- let hasVideo = {};
- MediaManagerService.mediaCaptureWindowState(win, hasVideo, hasAudio);
- if (hasVideo.value) cameraActive = true;
- if (hasAudio.value) audioActive = true;
- }
-
- if (cameraActive && audioActive) {
- notificationOptions.message = Strings.browser.GetStringFromName("getUserMedia.sharingCameraAndMicrophone.message2");
- notificationOptions.icon = "drawable:alert_mic_camera";
- } else if (cameraActive) {
- notificationOptions.message = Strings.browser.GetStringFromName("getUserMedia.sharingCamera.message2");
- notificationOptions.icon = "drawable:alert_camera";
- } else if (audioActive) {
- notificationOptions.message = Strings.browser.GetStringFromName("getUserMedia.sharingMicrophone.message2");
- notificationOptions.icon = "drawable:alert_mic";
- } else {
- // somethings wrong. lets throw
- throw "Couldn't find any cameras or microphones being used"
- }
-
- if (this._notificationId)
- Notifications.update(this._notificationId, notificationOptions);
- else
- this._notificationId = Notifications.create(notificationOptions);
- if (count > 1)
- msg.count = count;
- }
- },
-
- handlePCRequest: function handlePCRequest(aSubject, aTopic, aData) {
- aSubject = aSubject.wrappedJSObject;
- let { callID } = aSubject;
- // Also available: windowID, isSecure, innerWindowID. For contentWindow do:
- //
- // let contentWindow = Services.wm.getOuterWindowWithId(windowID);
-
- Services.obs.notifyObservers(null, "PeerConnection:response:allow", callID);
- },
-
- handleGumRequest: function handleGumRequest(aSubject, aTopic, aData) {
- let constraints = aSubject.getConstraints();
- let contentWindow = Services.wm.getOuterWindowWithId(aSubject.windowID);
-
- contentWindow.navigator.mozGetUserMediaDevices(
- constraints,
- function (devices) {
- if (!ParentalControls.isAllowed(ParentalControls.CAMERA_MICROPHONE)) {
- Services.obs.notifyObservers(null, "getUserMedia:response:deny", aSubject.callID);
- WebrtcUI.showBlockMessage(devices);
- return;
- }
-
- WebrtcUI.prompt(contentWindow, aSubject.callID, constraints.audio,
- constraints.video, devices);
- },
- function (error) {
- Cu.reportError(error);
- },
- aSubject.innerWindowID,
- aSubject.callID);
- },
-
- getDeviceButtons: function(audioDevices, videoDevices, aCallID, aUri) {
- return [{
- label: Strings.browser.GetStringFromName("getUserMedia.denyRequest.label"),
- callback: function() {
- Services.obs.notifyObservers(null, "getUserMedia:response:deny", aCallID);
- }
- },
- {
- label: Strings.browser.GetStringFromName("getUserMedia.shareRequest.label"),
- callback: function(checked /* ignored */, inputs) {
- let allowedDevices = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
-
- let audioId = 0;
- if (inputs && inputs.audioDevice != undefined)
- audioId = inputs.audioDevice;
- if (audioDevices[audioId])
- allowedDevices.appendElement(audioDevices[audioId], /*weak =*/ false);
-
- let videoId = 0;
- if (inputs && inputs.videoSource != undefined)
- videoId = inputs.videoSource;
- if (videoDevices[videoId]) {
- allowedDevices.appendElement(videoDevices[videoId], /*weak =*/ false);
- let perms = Services.perms;
- // Although the lifetime is "session" it will be removed upon
- // use so it's more of a one-shot.
- perms.add(aUri, "MediaManagerVideo", perms.ALLOW_ACTION, perms.EXPIRE_SESSION);
- }
-
- Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", aCallID);
- },
- positive: true
- }];
- },
-
- _determineNeededRuntimePermissions: function(aSubject) {
- let permissions = [];
-
- let constraints = aSubject.getConstraints();
- if (constraints.video) {
- permissions.push(RuntimePermissions.CAMERA);
- }
- if (constraints.audio) {
- permissions.push(RuntimePermissions.RECORD_AUDIO);
- }
-
- return permissions;
- },
-
- // Get a list of string names for devices. Ensures that none of the strings are blank
- _getList: function(aDevices, aType) {
- let defaultCount = 0;
- return aDevices.map(function(device) {
- // if this is a Camera input, convert the name to something readable
- let res = /Camera\ \d+,\ Facing (front|back)/.exec(device.name);
- if (res)
- return Strings.browser.GetStringFromName("getUserMedia." + aType + "." + res[1] + "Camera");
-
- if (device.name.startsWith("&") && device.name.endsWith(";"))
- return Strings.browser.GetStringFromName(device.name.substring(1, device.name.length -1));
-
- if (device.name.trim() == "") {
- defaultCount++;
- return Strings.browser.formatStringFromName("getUserMedia." + aType + ".default", [defaultCount], 1);
- }
- return device.name
- }, this);
- },
-
- _addDevicesToOptions: function(aDevices, aType, aOptions) {
- if (aDevices.length) {
-
- // Filter out empty items from the list
- let list = this._getList(aDevices, aType);
-
- if (list.length > 0) {
- aOptions.inputs.push({
- id: aType,
- type: "menulist",
- label: Strings.browser.GetStringFromName("getUserMedia." + aType + ".prompt"),
- values: list
- });
-
- }
- }
- },
-
- showBlockMessage: function(aDevices) {
- let microphone = false;
- let camera = false;
-
- for (let device of aDevices) {
- device = device.QueryInterface(Ci.nsIMediaDevice);
- if (device.type == "audio") {
- microphone = true;
- } else if (device.type == "video") {
- camera = true;
- }
- }
-
- let message;
- if (microphone && !camera) {
- message = Strings.browser.GetStringFromName("getUserMedia.blockedMicrophoneAccess");
- } else if (camera && !microphone) {
- message = Strings.browser.GetStringFromName("getUserMedia.blockedCameraAccess");
- } else {
- message = Strings.browser.GetStringFromName("getUserMedia.blockedCameraAndMicrophoneAccess");
- }
-
- NativeWindow.doorhanger.show(message, "webrtc-blocked", [], BrowserApp.selectedTab.id, {});
- },
-
- prompt: function prompt(aContentWindow, aCallID, aAudioRequested,
- aVideoRequested, aDevices) {
- let audioDevices = [];
- let videoDevices = [];
- for (let device of aDevices) {
- device = device.QueryInterface(Ci.nsIMediaDevice);
- switch (device.type) {
- case "audio":
- if (aAudioRequested)
- audioDevices.push(device);
- break;
- case "video":
- if (aVideoRequested)
- videoDevices.push(device);
- break;
- }
- }
-
- let requestType;
- if (audioDevices.length && videoDevices.length)
- requestType = "CameraAndMicrophone";
- else if (audioDevices.length)
- requestType = "Microphone";
- else if (videoDevices.length)
- requestType = "Camera";
- else
- return;
-
- let uri = aContentWindow.document.documentURIObject;
- let host = uri.host;
- let requestor = BrowserApp.manifest ? "'" + BrowserApp.manifest.name + "'" : host;
- let message = Strings.browser.formatStringFromName("getUserMedia.share" + requestType + ".message", [ requestor ], 1);
-
- let options = { inputs: [] };
- if (videoDevices.length > 1 || audioDevices.length > 0) {
- // videoSource is both the string used for l10n lookup and the object that will be returned
- this._addDevicesToOptions(videoDevices, "videoSource", options);
- }
-
- if (audioDevices.length > 1 || videoDevices.length > 0) {
- this._addDevicesToOptions(audioDevices, "audioDevice", options);
- }
-
- let buttons = this.getDeviceButtons(audioDevices, videoDevices, aCallID, uri);
-
- NativeWindow.doorhanger.show(message, "webrtc-request", buttons, BrowserApp.selectedTab.id, options, "WEBRTC");
- }
-}
diff --git a/mobile/android/chrome/content/about.js b/mobile/android/chrome/content/about.js
deleted file mode 100644
index 8c9acdf8a..000000000
--- a/mobile/android/chrome/content/about.js
+++ /dev/null
@@ -1,151 +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 Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils, Cr = Components.results;
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-function init() {
- // Include the build date and a warning about Telemetry
- // if this is an "a#" (nightly or aurora) build
-#expand const version = "__MOZ_APP_VERSION_DISPLAY__";
- if (/a\d+$/.test(version)) {
- let buildID = Services.appinfo.appBuildID;
- let buildDate = buildID.slice(0, 4) + "-" + buildID.slice(4, 6) + "-" + buildID.slice(6, 8);
- let br = document.createElement("br");
- let versionPara = document.getElementById("version");
- versionPara.appendChild(br);
- let date = document.createTextNode("(" + buildDate + ")");
- versionPara.appendChild(date);
- document.getElementById("telemetry").hidden = false;
- }
-
- // Include the Distribution information if available
- try {
- let distroId = Services.prefs.getCharPref("distribution.id");
- if (distroId) {
- let distroVersion = Services.prefs.getCharPref("distribution.version");
- let distroIdField = document.getElementById("distributionID");
- distroIdField.textContent = distroId + " - " + distroVersion;
- distroIdField.hidden = false;
-
- let distroAbout = Services.prefs.getComplexValue("distribution.about", Ci.nsISupportsString);
- let distroField = document.getElementById("distributionAbout");
- distroField.textContent = distroAbout;
- distroField.hidden = false;
- }
- } catch (e) {
- // Pref is unset
- }
-
- // get URLs from prefs
- try {
- let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
-
- let links = [
- {id: "releaseNotesURL", pref: "app.releaseNotesURL"},
- {id: "supportURL", pref: "app.supportURL"},
- {id: "faqURL", pref: "app.faqURL"},
- {id: "privacyURL", pref: "app.privacyURL"},
- {id: "creditsURL", pref: "app.creditsURL"},
- ];
-
- links.forEach(function(link) {
- let url = formatter.formatURLPref(link.pref);
- let element = document.getElementById(link.id);
- element.setAttribute("href", url);
- });
- } catch (ex) {}
-
-#ifdef MOZ_UPDATER
- let Updater = {
- update: null,
-
- init: function() {
- Services.obs.addObserver(this, "Update:CheckResult", false);
- },
-
- observe: function(aSubject, aTopic, aData) {
- if (aTopic == "Update:CheckResult") {
- showUpdateMessage(aData);
- }
- },
- };
-
- Updater.init();
-
- function checkForUpdates() {
- showCheckingMessage();
-
- Services.androidBridge.handleGeckoMessage({ type: "Update:Check" });
- }
-
- function downloadUpdate() {
- Services.androidBridge.handleGeckoMessage({ type: "Update:Download" });
- }
-
- function installUpdate() {
- showCheckAction();
-
- Services.androidBridge.handleGeckoMessage({ type: "Update:Install" });
- }
-
- let updateLink = document.getElementById("updateLink");
- let checkingSpan = document.getElementById("update-message-checking");
- let noneSpan = document.getElementById("update-message-none");
- let foundSpan = document.getElementById("update-message-found");
- let downloadingSpan = document.getElementById("update-message-downloading");
- let downloadedSpan = document.getElementById("update-message-downloaded");
-
- updateLink.onclick = checkForUpdates;
- foundSpan.onclick = downloadUpdate;
- downloadedSpan.onclick = installUpdate;
-
- function showCheckAction() {
- checkingSpan.style.display = "none";
- noneSpan.style.display = "none";
- foundSpan.style.display = "none";
- downloadingSpan.style.display = "none";
- downloadedSpan.style.display = "none";
- updateLink.style.display = "block";
- }
-
- function showCheckingMessage() {
- updateLink.style.display = "none";
- noneSpan.style.display = "none";
- foundSpan.style.display = "none";
- downloadingSpan.style.display = "none";
- downloadedSpan.style.display = "none";
- checkingSpan.style.display = "block";
- }
-
- function showUpdateMessage(aResult) {
- updateLink.style.display = "none";
- checkingSpan.style.display = "none";
- noneSpan.style.display = "none";
- foundSpan.style.display = "none";
- downloadingSpan.style.display = "none";
- downloadedSpan.style.display = "none";
-
- // the aResult values come from mobile/android/base/UpdateServiceHelper.java
- switch (aResult) {
- case "NOT_AVAILABLE":
- noneSpan.style.display = "block";
- setTimeout(showCheckAction, 2000);
- break;
- case "AVAILABLE":
- foundSpan.style.display = "block";
- break;
- case "DOWNLOADING":
- downloadingSpan.style.display = "block";
- break;
- case "DOWNLOADED":
- downloadedSpan.style.display = "block";
- break;
- }
- }
-#endif
-}
-
-document.addEventListener("DOMContentLoaded", init, false);
diff --git a/mobile/android/chrome/content/about.xhtml b/mobile/android/chrome/content/about.xhtml
deleted file mode 100644
index 8a4c28357..000000000
--- a/mobile/android/chrome/content/about.xhtml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!DOCTYPE html [
-<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
-%brandDTD;
-<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
-%globalDTD;
-<!ENTITY % fennecDTD SYSTEM "chrome://browser/locale/about.dtd">
-%fennecDTD;
-]>
-
-<!-- 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>
- <meta name="viewport" content="width=480; initial-scale=.6667; user-scalable=no"/>
- <title>&aboutPage.title;</title>
- <link rel="stylesheet" href="chrome://browser/skin/aboutPage.css" type="text/css"/>
- <link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
-</head>
-
-<body dir="&locale.dir;">
- <div id="header">
- <div id="wordmark"></div>
-#expand <p id="version">__MOZ_APP_VERSION_DISPLAY__</p>
- </div>
-
- <div id="banner">
- <div id="logo"/>
-#ifdef MOZ_UPDATER
- <div id="updateBox">
- <a id="updateLink" href="">&aboutPage.checkForUpdates.link;</a>
- <span id="update-message-checking">&aboutPage.checkForUpdates.checking;</span>
- <span id="update-message-none">&aboutPage.checkForUpdates.none;</span>
- <span id="update-message-found">&aboutPage.checkForUpdates.available2;</span>
- <span id="update-message-downloading">&aboutPage.checkForUpdates.downloading;</span>
- <span id="update-message-downloaded">&aboutPage.checkForUpdates.downloaded2;</span>
- </div>
-#endif
-
- <div id="messages">
- <p id="distributionAbout" hidden="true"/>
- <p id="distributionID" hidden="true"/>
- <p id="telemetry" hidden="true">
- &aboutPage.warningVersion;
-#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
- &aboutPage.telemetryStart;<a href="https://www.mozilla.org/">&aboutPage.telemetryMozillaLink;</a>&aboutPage.telemetryEnd;
-#endif
- </p>
- </div>
-
- </div>
-
- <ul id="aboutLinks">
- <div class="top-border"></div>
- <li><a id="faqURL">&aboutPage.faq.label;</a></li>
- <li><a id="supportURL">&aboutPage.support.label;</a></li>
- <li><a id="privacyURL">&aboutPage.privacyPolicy.label;</a></li>
- <li><a href="about:rights">&aboutPage.rights.label;</a></li>
- <li><a id="releaseNotesURL">&aboutPage.relNotes.label;</a></li>
- <li><a id="creditsURL">&aboutPage.credits.label;</a></li>
- <li><a href="about:license">&aboutPage.license.label;</a></li>
- <div class="bottom-border"></div>
- </ul>
-
-#ifdef RELEASE_OR_BETA
- <div id="aboutDetails">
- <p>&aboutPage.logoTrademark;</p>
- </div>
-#endif
-
- <script type="application/javascript;version=1.8" src="chrome://browser/content/about.js" />
-
-</body>
-</html>
diff --git a/mobile/android/chrome/content/aboutAddons.js b/mobile/android/chrome/content/aboutAddons.js
deleted file mode 100644
index becf56a32..000000000
--- a/mobile/android/chrome/content/aboutAddons.js
+++ /dev/null
@@ -1,609 +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";
-
-/*globals gChromeWin */
-
-var Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
-
-Cu.import("resource://gre/modules/Services.jsm")
-Cu.import("resource://gre/modules/AddonManager.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const AMO_ICON = "chrome://browser/skin/images/amo-logo.png";
-
-var gStringBundle = Services.strings.createBundle("chrome://browser/locale/aboutAddons.properties");
-
-XPCOMUtils.defineLazyGetter(window, "gChromeWin", function() {
- return window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShellTreeItem)
- .rootTreeItem
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindow)
- .QueryInterface(Ci.nsIDOMChromeWindow);
-});
-XPCOMUtils.defineLazyModuleGetter(window, "Preferences",
- "resource://gre/modules/Preferences.jsm");
-
-var ContextMenus = {
- target: null,
-
- init: function() {
- document.addEventListener("contextmenu", this, false);
-
- document.getElementById("contextmenu-enable").addEventListener("click", ContextMenus.enable.bind(this), false);
- document.getElementById("contextmenu-disable").addEventListener("click", ContextMenus.disable.bind(this), false);
- document.getElementById("contextmenu-uninstall").addEventListener("click", ContextMenus.uninstall.bind(this), false);
-
- // XXX - Hack to fix bug 985867 for now
- document.addEventListener("touchstart", function() { });
- },
-
- handleEvent: function(event) {
- // store the target of context menu events so that we know which app to act on
- this.target = event.target;
- while (!this.target.hasAttribute("contextmenu")) {
- this.target = this.target.parentNode;
- }
-
- if (!this.target) {
- document.getElementById("contextmenu-enable").setAttribute("hidden", "true");
- document.getElementById("contextmenu-disable").setAttribute("hidden", "true");
- document.getElementById("contextmenu-uninstall").setAttribute("hidden", "true");
- return;
- }
-
- let addon = this.target.addon;
- if (addon.scope == AddonManager.SCOPE_APPLICATION) {
- document.getElementById("contextmenu-uninstall").setAttribute("hidden", "true");
- } else {
- document.getElementById("contextmenu-uninstall").removeAttribute("hidden");
- }
-
- // Hide the enable/disable context menu items if the add-on was disabled by
- // Firefox (e.g. unsigned or blocklisted add-on).
- if (addon.appDisabled) {
- document.getElementById("contextmenu-enable").setAttribute("hidden", "true");
- document.getElementById("contextmenu-disable").setAttribute("hidden", "true");
- return;
- }
-
- let enabled = this.target.getAttribute("isDisabled") != "true";
- if (enabled) {
- document.getElementById("contextmenu-enable").setAttribute("hidden", "true");
- document.getElementById("contextmenu-disable").removeAttribute("hidden");
- } else {
- document.getElementById("contextmenu-enable").removeAttribute("hidden");
- document.getElementById("contextmenu-disable").setAttribute("hidden", "true");
- }
- },
-
- enable: function(event) {
- Addons.setEnabled(true, this.target.addon);
- this.target = null;
- },
-
- disable: function (event) {
- Addons.setEnabled(false, this.target.addon);
- this.target = null;
- },
-
- uninstall: function (event) {
- Addons.uninstall(this.target.addon);
- this.target = null;
- }
-}
-
-function init() {
- window.addEventListener("popstate", onPopState, false);
-
- AddonManager.addInstallListener(Addons);
- AddonManager.addAddonListener(Addons);
- Addons.init();
- showList();
- ContextMenus.init();
-}
-
-
-function uninit() {
- AddonManager.removeInstallListener(Addons);
- AddonManager.removeAddonListener(Addons);
-}
-
-function openLink(url) {
- let BrowserApp = gChromeWin.BrowserApp;
- BrowserApp.addTab(url, { selected: true, parentId: BrowserApp.selectedTab.id });
-}
-
-function onPopState(aEvent) {
- // Called when back/forward is used to change the state of the page
- if (aEvent.state) {
- // Show the detail page for an addon
- Addons.showDetails(Addons._getElementForAddon(aEvent.state.id));
- } else {
- // Clear any previous detail addon
- let detailItem = document.querySelector("#addons-details > .addon-item");
- detailItem.addon = null;
-
- showList();
- }
-}
-
-function showList() {
- // Hide the detail page and show the list
- let details = document.querySelector("#addons-details");
- details.style.display = "none";
- let list = document.querySelector("#addons-list");
- list.style.display = "block";
- document.documentElement.removeAttribute("details");
-}
-
-var Addons = {
- _restartCount: 0,
-
- _createItem: function _createItem(aAddon) {
- let outer = document.createElement("div");
- outer.setAttribute("addonID", aAddon.id);
- outer.className = "addon-item list-item";
- outer.setAttribute("role", "button");
- outer.setAttribute("contextmenu", "addonmenu");
- outer.addEventListener("click", function() {
- this.showDetails(outer);
- history.pushState({ id: aAddon.id }, document.title);
- }.bind(this), true);
-
- let img = document.createElement("img");
- img.className = "icon";
- img.setAttribute("src", aAddon.iconURL || AMO_ICON);
- outer.appendChild(img);
-
- let inner = document.createElement("div");
- inner.className = "inner";
-
- let details = document.createElement("div");
- details.className = "details";
- inner.appendChild(details);
-
- let titlePart = document.createElement("div");
- titlePart.textContent = aAddon.name;
- titlePart.className = "title";
- details.appendChild(titlePart);
-
- let versionPart = document.createElement("div");
- versionPart.textContent = aAddon.version;
- versionPart.className = "version";
- details.appendChild(versionPart);
-
- if ("description" in aAddon) {
- let descPart = document.createElement("div");
- descPart.textContent = aAddon.description;
- descPart.className = "description";
- inner.appendChild(descPart);
- }
-
- outer.appendChild(inner);
- return outer;
- },
-
- _createBrowseItem: function _createBrowseItem() {
- let outer = document.createElement("div");
- outer.className = "addon-item list-item";
- outer.setAttribute("role", "button");
- outer.addEventListener("click", function(event) {
- try {
- let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
- openLink(formatter.formatURLPref("extensions.getAddons.browseAddons"));
- } catch (e) {
- Cu.reportError(e);
- }
- }, true);
-
- let img = document.createElement("img");
- img.className = "icon";
- img.setAttribute("src", AMO_ICON);
- outer.appendChild(img);
-
- let inner = document.createElement("div");
- inner.className = "inner";
-
- let title = document.createElement("div");
- title.id = "browse-title";
- title.className = "title";
- title.textContent = gStringBundle.GetStringFromName("addons.browseAll");
- inner.appendChild(title);
-
- outer.appendChild(inner);
- return outer;
- },
-
- _createItemForAddon: function _createItemForAddon(aAddon) {
- let appManaged = (aAddon.scope == AddonManager.SCOPE_APPLICATION);
- let opType = this._getOpTypeForOperations(aAddon.pendingOperations);
- let updateable = (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE) > 0;
- let uninstallable = (aAddon.permissions & AddonManager.PERM_CAN_UNINSTALL) > 0;
-
- // TODO(matt): Add support for OPTIONS_TYPE_INLINE_BROWSER once bug 1302504 lands.
- let optionsURL;
- switch (aAddon.optionsType) {
- case AddonManager.OPTIONS_TYPE_INLINE:
- optionsURL = aAddon.optionsURL || "";
- break;
- default:
- optionsURL = "";
- }
-
- let blocked = "";
- switch(aAddon.blocklistState) {
- case Ci.nsIBlocklistService.STATE_BLOCKED:
- blocked = "blocked";
- break;
- case Ci.nsIBlocklistService.STATE_SOFTBLOCKED:
- blocked = "softBlocked";
- break;
- case Ci.nsIBlocklistService.STATE_OUTDATED:
- blocked = "outdated";
- break;
- }
-
- let item = this._createItem(aAddon);
- item.setAttribute("isDisabled", !aAddon.isActive);
- item.setAttribute("isUnsigned", aAddon.signedState <= AddonManager.SIGNEDSTATE_MISSING);
- item.setAttribute("opType", opType);
- item.setAttribute("updateable", updateable);
- if (blocked)
- item.setAttribute("blockedStatus", blocked);
- item.setAttribute("optionsURL", optionsURL);
- item.addon = aAddon;
-
- return item;
- },
-
- _getElementForAddon: function(aKey) {
- let list = document.getElementById("addons-list");
- let element = list.querySelector("div[addonID=\"" + CSS.escape(aKey) + "\"]");
- return element;
- },
-
- init: function init() {
- let self = this;
- AddonManager.getAllAddons(function(aAddons) {
- // Clear all content before filling the addons
- let list = document.getElementById("addons-list");
- list.innerHTML = "";
-
- aAddons.sort(function(a,b) {
- return a.name.localeCompare(b.name);
- });
- for (let i=0; i<aAddons.length; i++) {
- // Don't create item for system add-ons.
- if (aAddons[i].isSystem)
- continue;
-
- let item = self._createItemForAddon(aAddons[i]);
- list.appendChild(item);
- }
-
- // Add a "Browse all Firefox Add-ons" item to the bottom of the list.
- let browseItem = self._createBrowseItem();
- list.appendChild(browseItem);
- });
-
- document.getElementById("uninstall-btn").addEventListener("click", Addons.uninstallCurrent.bind(this), false);
- document.getElementById("cancel-btn").addEventListener("click", Addons.cancelUninstall.bind(this), false);
- document.getElementById("disable-btn").addEventListener("click", Addons.disable.bind(this), false);
- document.getElementById("enable-btn").addEventListener("click", Addons.enable.bind(this), false);
-
- document.getElementById("unsigned-learn-more").addEventListener("click", function() {
- openLink(Services.urlFormatter.formatURLPref("app.support.baseURL") + "unsigned-addons");
- }, false);
- },
-
- _getOpTypeForOperations: function _getOpTypeForOperations(aOperations) {
- if (aOperations & AddonManager.PENDING_UNINSTALL)
- return "needs-uninstall";
- if (aOperations & AddonManager.PENDING_ENABLE)
- return "needs-enable";
- if (aOperations & AddonManager.PENDING_DISABLE)
- return "needs-disable";
- return "";
- },
-
- showDetails: function showDetails(aListItem) {
- // This function removes and returns the text content of aNode without
- // removing any child elements. Removing the text nodes ensures any XBL
- // bindings apply properly.
- function stripTextNodes(aNode) {
- var text = "";
- for (var i = 0; i < aNode.childNodes.length; i++) {
- if (aNode.childNodes[i].nodeType != document.ELEMENT_NODE) {
- text += aNode.childNodes[i].textContent;
- aNode.removeChild(aNode.childNodes[i--]);
- } else {
- text += stripTextNodes(aNode.childNodes[i]);
- }
- }
- return text;
- }
-
- let detailItem = document.querySelector("#addons-details > .addon-item");
- detailItem.setAttribute("isDisabled", aListItem.getAttribute("isDisabled"));
- detailItem.setAttribute("isUnsigned", aListItem.getAttribute("isUnsigned"));
- detailItem.setAttribute("opType", aListItem.getAttribute("opType"));
- detailItem.setAttribute("optionsURL", aListItem.getAttribute("optionsURL"));
- let addon = detailItem.addon = aListItem.addon;
-
- let favicon = document.querySelector("#addons-details > .addon-item .icon");
- favicon.setAttribute("src", addon.iconURL || AMO_ICON);
-
- detailItem.querySelector(".title").textContent = addon.name;
- detailItem.querySelector(".version").textContent = addon.version;
- detailItem.querySelector(".description-full").textContent = addon.description;
- detailItem.querySelector(".status-uninstalled").textContent =
- gStringBundle.formatStringFromName("addonStatus.uninstalled", [addon.name], 1);
-
- let enableBtn = document.getElementById("enable-btn");
- if (addon.appDisabled) {
- enableBtn.setAttribute("disabled", "true");
- } else {
- enableBtn.removeAttribute("disabled");
- }
-
- let uninstallBtn = document.getElementById("uninstall-btn");
- if (addon.scope == AddonManager.SCOPE_APPLICATION) {
- uninstallBtn.setAttribute("disabled", "true");
- } else {
- uninstallBtn.removeAttribute("disabled");
- }
-
- let box = document.querySelector("#addons-details > .addon-item .options-box");
- box.innerHTML = "";
-
- // Retrieve the extensions preferences
- try {
- let optionsURL = aListItem.getAttribute("optionsURL");
- let xhr = new XMLHttpRequest();
- xhr.open("GET", optionsURL, true);
- xhr.onload = function(e) {
- if (xhr.responseXML) {
- // Only allow <setting> for now
- let settings = xhr.responseXML.querySelectorAll(":root > setting");
- if (settings.length > 0) {
- for (let i = 0; i < settings.length; i++) {
- var setting = settings[i];
- var desc = stripTextNodes(setting).trim();
- if (!setting.hasAttribute("desc")) {
- setting.setAttribute("desc", desc);
- }
- box.appendChild(setting);
- }
- // Send an event so add-ons can prepopulate any non-preference based
- // settings
- let event = document.createEvent("Events");
- event.initEvent("AddonOptionsLoad", true, false);
- window.dispatchEvent(event);
- } else {
- // Reset the options URL to hide the options header if there are no
- // valid settings to show.
- detailItem.setAttribute("optionsURL", "");
- }
-
- // Also send a notification to match the behavior of desktop Firefox
- let id = aListItem.getAttribute("addonID");
- Services.obs.notifyObservers(document, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED, id);
- }
- }
- xhr.send(null);
- } catch (e) { }
-
- let list = document.querySelector("#addons-list");
- list.style.display = "none";
- let details = document.querySelector("#addons-details");
- details.style.display = "block";
- document.documentElement.setAttribute("details", "true");
- },
-
- setEnabled: function setEnabled(aValue, aAddon) {
- let detailItem = document.querySelector("#addons-details > .addon-item");
- let addon = aAddon || detailItem.addon;
- if (!addon)
- return;
-
- let listItem = this._getElementForAddon(addon.id);
-
- let opType;
- if (addon.type == "theme") {
- if (aValue) {
- // We can have only one theme enabled, so disable the current one if any
- let list = document.getElementById("addons-list");
- let item = list.firstElementChild;
- while (item) {
- if (item.addon && (item.addon.type == "theme") && (item.addon.isActive)) {
- item.addon.userDisabled = true;
- item.setAttribute("isDisabled", true);
- break;
- }
- item = item.nextSibling;
- }
- }
- addon.userDisabled = !aValue;
- } else if (addon.type == "locale") {
- addon.userDisabled = !aValue;
- } else {
- addon.userDisabled = !aValue;
- opType = this._getOpTypeForOperations(addon.pendingOperations);
-
- if ((addon.pendingOperations & AddonManager.PENDING_ENABLE) ||
- (addon.pendingOperations & AddonManager.PENDING_DISABLE)) {
- this.showRestart();
- } else if (listItem && /needs-(enable|disable)/.test(listItem.getAttribute("opType"))) {
- this.hideRestart();
- }
- }
-
- if (addon == detailItem.addon) {
- detailItem.setAttribute("isDisabled", !aValue);
- if (opType)
- detailItem.setAttribute("opType", opType);
- else
- detailItem.removeAttribute("opType");
- }
-
- // Sync to the list item
- if (listItem) {
- listItem.setAttribute("isDisabled", !aValue);
- if (opType)
- listItem.setAttribute("opType", opType);
- else
- listItem.removeAttribute("opType");
- }
- },
-
- enable: function enable() {
- this.setEnabled(true);
- },
-
- disable: function disable() {
- this.setEnabled(false);
- },
-
- uninstallCurrent: function uninstallCurrent() {
- let detailItem = document.querySelector("#addons-details > .addon-item");
-
- let addon = detailItem.addon;
- if (!addon)
- return;
-
- this.uninstall(addon);
- },
-
- uninstall: function uninstall(aAddon) {
- if (!aAddon) {
- return;
- }
-
- let listItem = this._getElementForAddon(aAddon.id);
- aAddon.uninstall();
-
- if (aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
- this.showRestart();
-
- // A disabled addon doesn't need a restart so it has no pending ops and
- // can't be cancelled
- let opType = this._getOpTypeForOperations(aAddon.pendingOperations);
- if (!aAddon.isActive && opType == "")
- opType = "needs-uninstall";
-
- detailItem.setAttribute("opType", opType);
- listItem.setAttribute("opType", opType);
- }
- },
-
- cancelUninstall: function ev_cancelUninstall() {
- let detailItem = document.querySelector("#addons-details > .addon-item");
- let addon = detailItem.addon;
- if (!addon)
- return;
-
- addon.cancelUninstall();
- this.hideRestart();
-
- let opType = this._getOpTypeForOperations(addon.pendingOperations);
- detailItem.setAttribute("opType", opType);
-
- let listItem = this._getElementForAddon(addon.id);
- listItem.setAttribute("opType", opType);
- },
-
- showRestart: function showRestart() {
- this._restartCount++;
- gChromeWin.XPInstallObserver.showRestartPrompt();
- },
-
- hideRestart: function hideRestart() {
- this._restartCount--;
- if (this._restartCount == 0)
- gChromeWin.XPInstallObserver.hideRestartPrompt();
- },
-
- onEnabled: function(aAddon) {
- let listItem = this._getElementForAddon(aAddon.id);
- if (!listItem)
- return;
-
- // Reload the details to pick up any options now that it's enabled.
- listItem.setAttribute("optionsURL", aAddon.optionsURL || "");
- let detailItem = document.querySelector("#addons-details > .addon-item");
- if (aAddon == detailItem.addon)
- this.showDetails(listItem);
- },
-
- onInstallEnded: function(aInstall, aAddon) {
- let needsRestart = false;
- if (aInstall.existingAddon && (aInstall.existingAddon.pendingOperations & AddonManager.PENDING_UPGRADE))
- needsRestart = true;
- else if (aAddon.pendingOperations & AddonManager.PENDING_INSTALL)
- needsRestart = true;
-
- let list = document.getElementById("addons-list");
- let element = this._getElementForAddon(aAddon.id);
- if (!element) {
- element = this._createItemForAddon(aAddon);
- list.insertBefore(element, list.firstElementChild);
- }
-
- if (needsRestart)
- element.setAttribute("opType", "needs-restart");
- },
-
- onInstalled: function(aAddon) {
- let list = document.getElementById("addons-list");
- let element = this._getElementForAddon(aAddon.id);
- if (!element) {
- element = this._createItemForAddon(aAddon);
-
- // Themes aren't considered active on install, so set existing as disabled, and new one enabled.
- if (aAddon.type == "theme") {
- let item = list.firstElementChild;
- while (item) {
- if (item.addon && (item.addon.type == "theme")) {
- item.setAttribute("isDisabled", true);
- }
- item = item.nextSibling;
- }
- element.setAttribute("isDisabled", false);
- }
-
- list.insertBefore(element, list.firstElementChild);
- }
- },
-
- onUninstalled: function(aAddon) {
- let list = document.getElementById("addons-list");
- let element = this._getElementForAddon(aAddon.id);
- list.removeChild(element);
-
- // Go back if we're in the detail view of the add-on that was uninstalled.
- let detailItem = document.querySelector("#addons-details > .addon-item");
- if (detailItem.addon.id == aAddon.id) {
- history.back();
- }
- },
-
- onInstallFailed: function(aInstall) {
- },
-
- onDownloadProgress: function xpidm_onDownloadProgress(aInstall) {
- },
-
- onDownloadFailed: function(aInstall) {
- },
-
- onDownloadCancelled: function(aInstall) {
- }
-}
-
-window.addEventListener("load", init, false);
-window.addEventListener("unload", uninit, false);
diff --git a/mobile/android/chrome/content/aboutAddons.xhtml b/mobile/android/chrome/content/aboutAddons.xhtml
deleted file mode 100644
index 42d9cfa9c..000000000
--- a/mobile/android/chrome/content/aboutAddons.xhtml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
- "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
-<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
-%brandDTD;
-<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
-%globalDTD;
-<!ENTITY % aboutDTD SYSTEM "chrome://browser/locale/aboutAddons.dtd" >
-%aboutDTD;
-]>
-
-<!-- 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>&aboutAddons.title2;</title>
- <meta name="viewport" content="width=device-width; user-scalable=0" />
- <link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
- <link rel="stylesheet" href="chrome://browser/skin/aboutBase.css" type="text/css"/>
- <link rel="stylesheet" href="chrome://browser/skin/aboutAddons.css" type="text/css"/>
-</head>
-
-<body dir="&locale.dir;">
- <menu type="context" id="addonmenu">
- <menuitem id="contextmenu-enable" label="&addonAction.enable;"></menuitem>
- <menuitem id="contextmenu-disable" label="&addonAction.disable;" ></menuitem>
- <menuitem id="contextmenu-uninstall" label="&addonAction.uninstall;" ></menuitem>
- </menu>
-
- <div id="addons-header" class="header">
- <div>&aboutAddons.header2;</div>
- </div>
- <div id="addons-list" class="list">
- </div>
-
- <div id="addons-details" class="list">
- <div class="addon-item list-item">
- <img class="icon"/>
- <div class="inner">
- <div class="details">
- <div class="title"></div><div class="version"></div>
- </div>
- <div class="description-full"></div>
- <div class="options-header">&aboutAddons.options;</div>
- <div class="options-box"></div>
- </div>
- <div class="warn-unsigned">&addonUnsigned.message; <a id="unsigned-learn-more">&addonUnsigned.learnMore;</a></div>
- <div class="status status-uninstalled show-on-uninstall"></div>
- <div class="buttons">
- <button id="enable-btn" class="show-on-disable hide-on-enable hide-on-uninstall" >&addonAction.enable;</button>
- <button id="disable-btn" class="show-on-enable hide-on-disable hide-on-uninstall" >&addonAction.disable;</button>
- <button id="uninstall-btn" class="hide-on-uninstall" >&addonAction.uninstall;</button>
- <button id="cancel-btn" class="show-on-uninstall" >&addonAction.undo;</button>
- </div>
- </div>
- </div>
-
- <script type="application/javascript;version=1.8" src="chrome://browser/content/aboutAddons.js"></script>
-</body>
-</html>
diff --git a/mobile/android/chrome/content/aboutCertError.xhtml b/mobile/android/chrome/content/aboutCertError.xhtml
deleted file mode 100644
index c5922e2fe..000000000
--- a/mobile/android/chrome/content/aboutCertError.xhtml
+++ /dev/null
@@ -1,264 +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 % certerrorDTD
- SYSTEM "chrome://browser/locale/aboutCertError.dtd">
- %certerrorDTD;
-]>
-
-<!-- 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>&certerror.pagetitle;</title>
- <meta name="viewport" content="width=device-width; user-scalable=false" />
- <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all" />
- <!-- This page currently uses the same favicon as neterror.xhtml.
- If the location of the favicon is changed for both pages, the
- FAVICON_ERRORPAGE_URL symbol in toolkit/components/places/src/nsFaviconService.h
- should be updated. If this page starts using a different favicon
- than neterrorm nsFaviconService->SetAndLoadFaviconForPage
- should be updated to ignore this one as well. -->
- <link rel="icon" type="image/png" id="favicon" sizes="64x64" href="chrome://browser/skin/images/certerror-warning.png"/>
-
- <script type="application/javascript"><![CDATA[
- // Error url MUST be formatted like this:
- // about:certerror?e=error&u=url&d=desc
-
- // 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 getCSSClass()
- {
- var url = document.documentURI;
- var matches = url.match(/s\=([^&]+)\&/);
- // s is optional, if no match just return nothing
- if (!matches || matches.length < 2)
- return "";
-
- // parenthetical match is the second entry
- return decodeURIComponent(matches[1]);
- }
-
- function getDescription()
- {
- var url = document.documentURI;
- var desc = url.search(/d\=/);
-
- // desc == -1 if not found; if so, return an empty string
- // instead of what would turn out to be portions of the URI
- if (desc == -1)
- return "";
-
- return decodeURIComponent(url.slice(desc + 2));
- }
-
- function initPage()
- {
- // Replace the "#1" string in the intro with the hostname. Trickier
- // than it might seem since we want to preserve the <b> tags, but
- // not allow for any injection by just using innerHTML. Instead,
- // just find the right target text node.
- var intro = document.getElementById('introContentP1');
- function replaceWithHost(node) {
- if (node.textContent == "#1")
- node.textContent = location.host;
- else
- for(var i = 0; i < node.childNodes.length; i++)
- replaceWithHost(node.childNodes[i]);
- };
- replaceWithHost(intro);
-
- if (getCSSClass() == "expertBadCert") {
- toggle('technicalContent');
- toggle('expertContent');
- }
-
- // 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 (getCSSClass() == "badStsCert" || window != top)
- document.getElementById("expertContent").setAttribute("hidden", "true");
-
- var tech = document.getElementById("technicalContentText");
- if (tech)
- tech.textContent = getDescription();
-
- addDomainErrorLinks();
- }
-
- /* 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 sd = document.getElementById("technicalContentText");
- 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)));
- }
-
- // Then 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")
- toggle("technicalContent");
- }
-
- 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);
- }
-
- function toggle(id) {
- var el = document.getElementById(id);
- if (el.hasAttribute("collapsed"))
- el.removeAttribute("collapsed");
- else
- el.setAttribute("collapsed", true);
- }
- ]]></script>
- </head>
-
- <body id="errorPage" class="certerror" dir="&locale.dir;">
-
- <!-- PAGE CONTAINER (for styling purposes only) -->
- <div id="errorPageContainer">
-
- <!-- Error Title -->
- <div id="errorTitle">
- <h1 class="errorTitleText">&certerror.longpagetitle;</h1>
- </div>
-
- <!-- LONG CONTENT (the section most likely to require scrolling) -->
- <div id="errorLongContent">
- <div id="introContent">
- <p id="introContentP1">&certerror.introPara1;</p>
- </div>
-
- <div id="whatShouldIDoContent">
- <h2>&certerror.whatShouldIDo.heading;</h2>
- <div id="whatShouldIDoContentText">
- <p>&certerror.whatShouldIDo.content;</p>
- <button id="getMeOutOfHereButton">&certerror.getMeOutOfHere.label;</button>
- </div>
- </div>
-
- <!-- The following sections can be unhidden by default by setting the
- "browser.xul.error_pages.expert_bad_cert" pref to true -->
- <div id="technicalContent" collapsed="true">
- <h2 class="expander" onclick="toggle('technicalContent');" id="technicalContentHeading">&certerror.technical.heading;</h2>
- <p id="technicalContentText"/>
- </div>
-
- <div id="expertContent" collapsed="true">
- <h2 class="expander" onclick="toggle('expertContent');" id="expertContentHeading">&certerror.expert.heading;</h2>
- <div>
- <p>&certerror.expert.content;</p>
- <p>&certerror.expert.contentPara2;</p>
- <button id="temporaryExceptionButton">&certerror.addTemporaryException.label;</button>
- <button id="permanentExceptionButton">&certerror.addPermanentException.label;</button>
- </div>
- </div>
- </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>
diff --git a/mobile/android/chrome/content/aboutDownloads.js b/mobile/android/chrome/content/aboutDownloads.js
deleted file mode 100644
index add0a48e6..000000000
--- a/mobile/android/chrome/content/aboutDownloads.js
+++ /dev/null
@@ -1,373 +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 Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
-
-XPCOMUtils.defineLazyModuleGetter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils", "resource://gre/modules/DownloadUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
-
-var gStrings = Services.strings.createBundle("chrome://browser/locale/aboutDownloads.properties");
-XPCOMUtils.defineLazyGetter(this, "strings",
- () => Services.strings.createBundle("chrome://browser/locale/aboutDownloads.properties"));
-
-function deleteDownload(download) {
- download.finalize(true).then(null, Cu.reportError);
- OS.File.remove(download.target.path).then(null, ex => {
- if (!(ex instanceof OS.File.Error && ex.becauseNoSuchFile)) {
- Cu.reportError(ex);
- }
- });
-}
-
-var contextMenu = {
- _items: [],
- _targetDownload: null,
-
- init: function () {
- let element = document.getElementById("downloadmenu");
- element.addEventListener("click",
- event => event.download = this._targetDownload,
- true);
- this._items = [
- new ContextMenuItem("open",
- download => download.succeeded,
- download => download.launch().then(null, Cu.reportError)),
- new ContextMenuItem("retry",
- download => download.error ||
- (download.canceled && !download.hasPartialData),
- download => download.start().then(null, Cu.reportError)),
- new ContextMenuItem("remove",
- download => download.stopped,
- download => {
- Downloads.getList(Downloads.ALL)
- .then(list => list.remove(download))
- .then(null, Cu.reportError);
- deleteDownload(download);
- }),
- new ContextMenuItem("pause",
- download => !download.stopped && download.hasPartialData,
- download => download.cancel().then(null, Cu.reportError)),
- new ContextMenuItem("resume",
- download => download.canceled && download.hasPartialData,
- download => download.start().then(null, Cu.reportError)),
- new ContextMenuItem("cancel",
- download => !download.stopped ||
- (download.canceled && download.hasPartialData),
- download => {
- download.cancel().then(null, Cu.reportError);
- download.removePartialData().then(null, Cu.reportError);
- }),
- // following menu item is a global action
- new ContextMenuItem("removeall",
- () => downloadLists.finished.length > 0,
- () => downloadLists.removeFinished())
- ];
- },
-
- addContextMenuEventListener: function (element) {
- element.addEventListener("contextmenu", this.onContextMenu.bind(this));
- },
-
- onContextMenu: function (event) {
- let target = event.target;
- while (target && !target.download) {
- target = target.parentNode;
- }
- if (!target) {
- Cu.reportError("No download found for context menu target");
- event.preventDefault();
- return;
- }
-
- // capture the target download for menu items to use in a click event
- this._targetDownload = target.download;
- for (let item of this._items) {
- item.updateVisibility(target.download);
- }
- }
-};
-
-function ContextMenuItem(name, isVisible, action) {
- this.element = document.getElementById("contextmenu-" + name);
- this.isVisible = isVisible;
-
- this.element.addEventListener("click", event => action(event.download));
-}
-
-ContextMenuItem.prototype = {
- updateVisibility: function (download) {
- this.element.hidden = !this.isVisible(download);
- }
-};
-
-function DownloadListView(type, listElementId) {
- this.listElement = document.getElementById(listElementId);
- contextMenu.addContextMenuEventListener(this.listElement);
-
- this.items = new Map();
-
- Downloads.getList(type)
- .then(list => list.addView(this))
- .then(null, Cu.reportError);
-
- window.addEventListener("unload", event => {
- Downloads.getList(type)
- .then(list => list.removeView(this))
- .then(null, Cu.reportError);
- });
-}
-
-DownloadListView.prototype = {
- get finished() {
- let finished = [];
- for (let download of this.items.keys()) {
- if (download.stopped && (!download.hasPartialData || download.error)) {
- finished.push(download);
- }
- }
-
- return finished;
- },
-
- insertOrMoveItem: function (item) {
- var compare = (a, b) => {
- // active downloads always before stopped downloads
- if (a.stopped != b.stopped) {
- return b.stopped ? -1 : 1
- }
- // most recent downloads first
- return b.startTime - a.startTime;
- };
-
- let insertLocation = this.listElement.firstChild;
- while (insertLocation && compare(item.download, insertLocation.download) > 0) {
- insertLocation = insertLocation.nextElementSibling;
- }
- this.listElement.insertBefore(item.element, insertLocation);
- },
-
- onDownloadAdded: function (download) {
- let item = new DownloadItem(download);
- this.items.set(download, item);
- this.insertOrMoveItem(item);
- },
-
- onDownloadChanged: function (download) {
- let item = this.items.get(download);
- if (!item) {
- Cu.reportError("No DownloadItem found for download");
- return;
- }
-
- if (item.stateChanged) {
- this.insertOrMoveItem(item);
- }
-
- item.onDownloadChanged();
- },
-
- onDownloadRemoved: function (download) {
- let item = this.items.get(download);
- if (!item) {
- Cu.reportError("No DownloadItem found for download");
- return;
- }
-
- this.items.delete(download);
- this.listElement.removeChild(item.element);
-
- Messaging.sendRequest({
- type: "Download:Remove",
- path: download.target.path
- });
- }
-};
-
-var downloadLists = {
- init: function () {
- this.publicDownloads = new DownloadListView(Downloads.PUBLIC, "public-downloads-list");
- this.privateDownloads = new DownloadListView(Downloads.PRIVATE, "private-downloads-list");
- },
-
- get finished() {
- return this.publicDownloads.finished.concat(this.privateDownloads.finished);
- },
-
- removeFinished: function () {
- let finished = this.finished;
- if (finished.length == 0) {
- return;
- }
-
- let title = strings.GetStringFromName("downloadAction.deleteAll");
- let messageForm = strings.GetStringFromName("downloadMessage.deleteAll");
- let message = PluralForm.get(finished.length, messageForm).replace("#1", finished.length);
-
- if (Services.prompt.confirm(null, title, message)) {
- Downloads.getList(Downloads.ALL)
- .then(list => {
- for (let download of finished) {
- list.remove(download).then(null, Cu.reportError);
- deleteDownload(download);
- }
- }, Cu.reportError);
- }
- }
-};
-
-function DownloadItem(download) {
- this._download = download;
- this._updateFromDownload();
-
- this._domain = DownloadUtils.getURIHost(download.source.url)[0];
- this._fileName = this._htmlEscape(OS.Path.basename(download.target.path));
- this._iconUrl = "moz-icon://" + this._fileName + "?size=64";
- this._startDate = this._htmlEscape(DownloadUtils.getReadableDates(download.startTime)[0]);
-
- this._element = this.createElement();
-}
-
-const kDownloadStatePropertyNames = [
- "stopped",
- "succeeded",
- "canceled",
- "error",
- "startTime"
-];
-
-DownloadItem.prototype = {
- _htmlEscape : function (s) {
- s = s.replace(/&/g, "&amp;");
- s = s.replace(/>/g, "&gt;");
- s = s.replace(/</g, "&lt;");
- s = s.replace(/"/g, "&quot;");
- s = s.replace(/'/g, "&apos;");
- return s;
- },
-
- _updateFromDownload: function () {
- this._state = {};
- kDownloadStatePropertyNames.forEach(
- name => this._state[name] = this._download[name],
- this);
- },
-
- get stateChanged() {
- return kDownloadStatePropertyNames.some(
- name => this._state[name] != this._download[name],
- this);
- },
-
- get download() {
- return this._download;
- },
- get element() {
- return this._element;
- },
-
- createElement: function() {
- let template = document.getElementById("download-item");
- // TODO: use this once <template> is working
- // let element = document.importNode(template.content, true);
-
- // simulate a <template> node...
- let element = template.cloneNode(true);
- element.removeAttribute("id");
- element.removeAttribute("style");
-
- // launch the download if clicked
- element.addEventListener("click", this.onClick.bind(this));
-
- // set download as an expando property for the context menu
- element.download = this.download;
-
- // fill in template placeholders
- this.updateElement(element);
-
- return element;
- },
-
- updateElement: function (element) {
- element.querySelector(".date").textContent = this.startDate;
- element.querySelector(".domain").textContent = this.domain;
- element.querySelector(".icon").src = this.iconUrl;
- element.querySelector(".size").textContent = this.size;
- element.querySelector(".state").textContent = this.stateDescription;
- element.querySelector(".title").setAttribute("value", this.fileName);
- },
-
- onClick: function (event) {
- if (this.download.succeeded) {
- this.download.launch().then(null, Cu.reportError);
- }
- },
-
- onDownloadChanged: function () {
- this._updateFromDownload();
- this.updateElement(this.element);
- },
-
- // template properties below
- get domain() {
- return this._domain;
- },
- get fileName() {
- return this._fileName;
- },
- get id() {
- return this._id;
- },
- get iconUrl() {
- return this._iconUrl;
- },
-
- get size() {
- if (this.download.succeeded && this.download.target.exists) {
- return DownloadUtils.convertByteUnits(this.download.target.size).join("");
- } else if (this.download.hasProgress) {
- return DownloadUtils.convertByteUnits(this.download.totalBytes).join("");
- }
- return strings.GetStringFromName("downloadState.unknownSize");
- },
-
- get startDate() {
- return this._startDate;
- },
-
- get stateDescription() {
- let name;
- if (this.download.error) {
- name = "downloadState.failed";
- } else if (this.download.canceled) {
- if (this.download.hasPartialData) {
- name = "downloadState.paused";
- } else {
- name = "downloadState.canceled";
- }
- } else if (!this.download.stopped) {
- if (this.download.currentBytes > 0) {
- name = "downloadState.downloading";
- } else {
- name = "downloadState.starting";
- }
- }
-
- if (name) {
- return strings.GetStringFromName(name);
- }
- return "";
- }
-};
-
-window.addEventListener("DOMContentLoaded", event => {
- contextMenu.init();
- downloadLists.init()
-}); \ No newline at end of file
diff --git a/mobile/android/chrome/content/aboutDownloads.xhtml b/mobile/android/chrome/content/aboutDownloads.xhtml
deleted file mode 100644
index 6b9025694..000000000
--- a/mobile/android/chrome/content/aboutDownloads.xhtml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
- "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
-<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
-%brandDTD;
-<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
-%globalDTD;
-<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/aboutDownloads.dtd" >
-%downloadsDTD;
-]>
-
-<!-- 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"
- xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-<head>
- <title>&aboutDownloads.title;</title>
- <meta name="viewport" content="width=device-width; user-scalable=0" />
- <link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
- <link rel="stylesheet" href="chrome://browser/skin/aboutBase.css" type="text/css"/>
- <link rel="stylesheet" href="chrome://browser/skin/aboutDownloads.css" type="text/css"/>
-</head>
-
-<body dir="&locale.dir;">
- <menu type="context" id="downloadmenu">
- <menuitem id="contextmenu-open" label="&aboutDownloads.open;"></menuitem>
- <menuitem id="contextmenu-retry" label="&aboutDownloads.retry;"></menuitem>
- <menuitem id="contextmenu-remove" label="&aboutDownloads.remove;"></menuitem>
- <menuitem id="contextmenu-pause" label="&aboutDownloads.pause;"></menuitem>
- <menuitem id="contextmenu-resume" label="&aboutDownloads.resume;"></menuitem>
- <menuitem id="contextmenu-cancel" label="&aboutDownloads.cancel;"></menuitem>
- <menuitem id="contextmenu-removeall" label="&aboutDownloads.removeAll;"></menuitem>
- </menu>
-
- <!--template id="download-item"-->
- <li id="download-item" class="list-item" role="button" contextmenu="downloadmenu" style="display: none">
- <img class="icon" src=""/>
- <div class="details">
- <div class="row">
- <!-- This is a hack so that we can crop this label in its center -->
- <xul:label class="title" crop="center" value=""/>
- <div class="date"></div>
- </div>
- <div class="size"></div>
- <div class="domain"></div>
- <div class="state"></div>
- </div>
- </li>
- <!--/template-->
-
- <div class="header">
- <div>&aboutDownloads.header;</div>
- </div>
- <ul id="private-downloads-list" class="list"></ul>
- <ul id="public-downloads-list" class="list"></ul>
- <span id="no-downloads-indicator">&aboutDownloads.empty;</span>
- <script type="application/javascript;version=1.8" src="chrome://browser/content/aboutDownloads.js"/>
-</body>
-</html>
diff --git a/mobile/android/chrome/content/aboutHealthReport.js b/mobile/android/chrome/content/aboutHealthReport.js
deleted file mode 100644
index 070eb821d..000000000
--- a/mobile/android/chrome/content/aboutHealthReport.js
+++ /dev/null
@@ -1,192 +0,0 @@
-// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
-/* 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/Messaging.jsm");
-Cu.import("resource://gre/modules/SharedPreferences.jsm");
-
-// Name of Android SharedPreference controlling whether to upload
-// health reports.
-const PREF_UPLOAD_ENABLED = "android.not_a_preference.healthreport.uploadEnabled";
-
-// Name of Gecko Pref specifying report content location.
-const PREF_REPORTURL = "datareporting.healthreport.about.reportUrl";
-
-// Monotonically increasing wrapper API version number.
-const WRAPPER_VERSION = 1;
-
-const EVENT_HEALTH_REQUEST = "HealthReport:Request";
-const EVENT_HEALTH_RESPONSE = "HealthReport:Response";
-
-// about:healthreport prefs are stored in Firefox's default Android
-// SharedPreferences.
-var sharedPrefs = SharedPreferences.forApp();
-
-var healthReportWrapper = {
- init: function () {
- let iframe = document.getElementById("remote-report");
- iframe.addEventListener("load", healthReportWrapper.initRemotePage, false);
- let report = this._getReportURI();
- iframe.src = report.spec;
- console.log("AboutHealthReport: loading content from " + report.spec);
-
- sharedPrefs.addObserver(PREF_UPLOAD_ENABLED, this, false);
- Services.obs.addObserver(this, EVENT_HEALTH_RESPONSE, false);
- },
-
- observe: function (subject, topic, data) {
- if (topic == PREF_UPLOAD_ENABLED) {
- this.updatePrefState();
- } else if (topic == EVENT_HEALTH_RESPONSE) {
- this.updatePayload(data);
- }
- },
-
- uninit: function () {
- sharedPrefs.removeObserver(PREF_UPLOAD_ENABLED, this);
- Services.obs.removeObserver(this, EVENT_HEALTH_RESPONSE);
- },
-
- _getReportURI: function () {
- let url = Services.urlFormatter.formatURLPref(PREF_REPORTURL);
- // This handles URLs that already have query parameters.
- let uri = Services.io.newURI(url, null, null).QueryInterface(Ci.nsIURL);
- uri.query += ((uri.query != "") ? "&v=" : "v=") + WRAPPER_VERSION;
- return uri;
- },
-
- onOptIn: function () {
- console.log("AboutHealthReport: page sent opt-in command.");
- sharedPrefs.setBoolPref(PREF_UPLOAD_ENABLED, true);
- this.updatePrefState();
- },
-
- onOptOut: function () {
- console.log("AboutHealthReport: page sent opt-out command.");
- sharedPrefs.setBoolPref(PREF_UPLOAD_ENABLED, false);
- this.updatePrefState();
- },
-
- updatePrefState: function () {
- console.log("AboutHealthReport: sending pref state to page.");
- try {
- let prefs = {
- enabled: sharedPrefs.getBoolPref(PREF_UPLOAD_ENABLED),
- };
- this.injectData("prefs", prefs);
- } catch (e) {
- this.reportFailure(this.ERROR_PREFS_FAILED);
- }
- },
-
- refreshPayload: function () {
- console.log("AboutHealthReport: page requested fresh payload.");
- Messaging.sendRequest({
- type: EVENT_HEALTH_REQUEST,
- });
- },
-
- updatePayload: function (data) {
- healthReportWrapper.injectData("payload", data);
- // Data is supposed to be a string, so the length should be
- // defined. Just in case, we do this after injecting the data.
- console.log("AboutHealthReport: sending payload to page " +
- "(" + typeof(data) + " of length " + data.length + ").");
- },
-
- 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);
- },
-
- showSettings: function () {
- console.log("AboutHealthReport: showing settings.");
- Messaging.sendRequest({
- type: "Settings:Show",
- resource: "preferences_vendor",
- });
- },
-
- launchUpdater: function () {
- console.log("AboutHealthReport: launching updater.");
- Messaging.sendRequest({
- type: "Updater:Launch",
- });
- },
-
- handleRemoteCommand: function (evt) {
- switch (evt.detail.command) {
- case "DisableDataSubmission":
- this.onOptOut();
- break;
- case "EnableDataSubmission":
- this.onOptIn();
- break;
- case "RequestCurrentPrefs":
- this.updatePrefState();
- break;
- case "RequestCurrentPayload":
- this.refreshPayload();
- break;
- case "ShowSettings":
- this.showSettings();
- break;
- case "LaunchUpdater":
- this.launchUpdater();
- 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.injectData("begin", null);
- },
-
- // 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", healthReportWrapper.init.bind(healthReportWrapper), false);
-window.addEventListener("unload", healthReportWrapper.uninit.bind(healthReportWrapper), false);
diff --git a/mobile/android/chrome/content/aboutHealthReport.xhtml b/mobile/android/chrome/content/aboutHealthReport.xhtml
deleted file mode 100644
index 73dae0380..000000000
--- a/mobile/android/chrome/content/aboutHealthReport.xhtml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
- "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
-<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
-%brandDTD;
-<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
-%globalDTD;
-<!ENTITY % aboutHealthReportDTD SYSTEM "chrome://browser/locale/aboutHealthReport.dtd" >
-%aboutHealthReportDTD;
-]>
-
-<!-- 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>
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- <title>&abouthealth.pagetitle;</title>
- <link rel="icon" type="image/png" sizes="64x64"
- href="chrome://branding/content/favicon64.png" />
- <link rel="stylesheet"
- href="chrome://browser/skin/aboutHealthReport.css"
- type="text/css" />
- <script type="text/javascript;version=1.8"
- src="chrome://browser/content/aboutHealthReport.js" />
- </head>
- <body>
- <iframe id="remote-report"/>
- </body>
-</html>
diff --git a/mobile/android/chrome/content/aboutHome.xhtml b/mobile/android/chrome/content/aboutHome.xhtml
deleted file mode 100644
index 692eac2ec..000000000
--- a/mobile/android/chrome/content/aboutHome.xhtml
+++ /dev/null
@@ -1,22 +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 % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
- %brandDTD;
- <!ENTITY % abouthomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
- %abouthomeDTD;
-]>
-
-<!-- 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>&abouthome.title;</title>
-</head>
-<body>
-</body>
-</html>
diff --git a/mobile/android/chrome/content/aboutLogins.js b/mobile/android/chrome/content/aboutLogins.js
deleted file mode 100644
index b3d003875..000000000
--- a/mobile/android/chrome/content/aboutLogins.js
+++ /dev/null
@@ -1,517 +0,0 @@
-/* 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 Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
-
-Cu.import("resource://services-common/utils.js"); /*global: CommonUtils */
-Cu.import("resource://gre/modules/Messaging.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyGetter(window, "gChromeWin", () =>
- window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShellTreeItem)
- .rootTreeItem
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindow)
- .QueryInterface(Ci.nsIDOMChromeWindow));
-
-XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Prompt",
- "resource://gre/modules/Prompt.jsm");
-
-var debug = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.d.bind(null, "AboutLogins");
-
-var gStringBundle = Services.strings.createBundle("chrome://browser/locale/aboutLogins.properties");
-
-function copyStringShowSnackbar(string, notifyString) {
- try {
- let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
- clipboard.copyString(string);
- Snackbars.show(notifyString, Snackbars.LENGTH_LONG);
- } catch (e) {
- debug("Error copying from about:logins");
- Snackbars.show(gStringBundle.GetStringFromName("loginsDetails.copyFailed"), Snackbars.LENGTH_LONG);
- }
-}
-
-// Delay filtering while typing in MS
-const FILTER_DELAY = 500;
-
-var Logins = {
- _logins: [],
- _filterTimer: null,
- _selectedLogin: null,
-
- // Load the logins list, displaying interstitial UI (see
- // #logins-list-loading-body) while loading. There are careful
- // jank-avoiding measures taken in this function; be careful when
- // modifying it!
- //
- // Returns a Promise that resolves to the list of logins, ordered by
- // hostname.
- _promiseLogins: function() {
- let contentBody = document.getElementById("content-body");
- let emptyBody = document.getElementById("empty-body");
- let filterIcon = document.getElementById("filter-button");
-
- let showSpinner = () => {
- this._toggleListBody(true);
- emptyBody.classList.add("hidden");
- };
-
- let getAllLogins = () => {
- let logins = [];
- try {
- logins = Services.logins.getAllLogins();
- } catch(e) {
- // It's likely that the Master Password was not entered; give
- // a hint to the next person.
- throw new Error("Possible Master Password permissions error: " + e.toString());
- }
-
- logins.sort((a, b) => a.hostname.localeCompare(b.hostname));
-
- return logins;
- };
-
- let hideSpinner = (logins) => {
- this._toggleListBody(false);
-
- if (!logins.length) {
- contentBody.classList.add("hidden");
- filterIcon.classList.add("hidden");
- emptyBody.classList.remove("hidden");
- } else {
- contentBody.classList.remove("hidden");
- emptyBody.classList.add("hidden");
- }
-
- return logins;
- };
-
- // Return a promise that is resolved after a paint.
- let waitForPaint = () => {
- // We're changing 'display'. We need to wait for the new value to take
- // effect; otherwise, we'll block and never paint a change. Since
- // requestAnimationFrame callback is generally triggered *before* any
- // style flush and layout, we wait for two animation frames. This
- // approach was cribbed from
- // https://dxr.mozilla.org/mozilla-central/rev/5abe3c4deab94270440422c850bbeaf512b1f38d/browser/base/content/browser-fullScreen.js?offset=0#469.
- return new Promise(function(resolve, reject) {
- requestAnimationFrame(() => {
- requestAnimationFrame(() => {
- resolve();
- });
- });
- });
- };
-
- // getAllLogins janks the main-thread. We need to paint before that jank;
- // by throwing the janky load onto the next tick, we paint the spinner; the
- // spinner is CSS animated off-main-thread.
- return Promise.resolve()
- .then(showSpinner)
- .then(waitForPaint)
- .then(getAllLogins)
- .then(hideSpinner);
- },
-
- // Reload the logins list, displaying interstitial UI while loading.
- // Update the stored and displayed list upon completion.
- _reloadList: function() {
- this._promiseLogins()
- .then((logins) => {
- this._logins = logins;
- this._loadList(logins);
- })
- .catch((e) => {
- // There's no way to recover from errors, sadly. Log and make
- // it obvious that something is up.
- this._logins = [];
- debug("Failed to _reloadList!");
- Cu.reportError(e);
- });
- },
-
- _toggleListBody: function(isLoading) {
- let contentBody = document.getElementById("content-body");
- let loadingBody = document.getElementById("logins-list-loading-body");
-
- if (isLoading) {
- contentBody.classList.add("hidden");
- loadingBody.classList.remove("hidden");
- } else {
- loadingBody.classList.add("hidden");
- contentBody.classList.remove("hidden");
- }
- },
-
- init: function () {
- window.addEventListener("popstate", this , false);
-
- Services.obs.addObserver(this, "passwordmgr-storage-changed", false);
- document.getElementById("update-btn").addEventListener("click", this._onSaveEditLogin.bind(this), false);
- document.getElementById("password-btn").addEventListener("click", this._onPasswordBtn.bind(this), false);
-
- let filterInput = document.getElementById("filter-input");
- let filterContainer = document.getElementById("filter-input-container");
-
- filterInput.addEventListener("input", (event) => {
- // Stop any in-progress filter timer
- if (this._filterTimer) {
- clearTimeout(this._filterTimer);
- this._filterTimer = null;
- }
-
- // Start a new timer
- this._filterTimer = setTimeout(() => {
- this._filter(event);
- }, FILTER_DELAY);
- }, false);
-
- filterInput.addEventListener("blur", (event) => {
- filterContainer.setAttribute("hidden", true);
- });
-
- document.getElementById("filter-button").addEventListener("click", (event) => {
- filterContainer.removeAttribute("hidden");
- filterInput.focus();
- }, false);
-
- document.getElementById("filter-clear").addEventListener("click", (event) => {
- // Stop any in-progress filter timer
- if (this._filterTimer) {
- clearTimeout(this._filterTimer);
- this._filterTimer = null;
- }
-
- filterInput.blur();
- filterInput.value = "";
- this._loadList(this._logins);
- }, false);
-
- this._showList();
-
- this._updatePasswordBtn(true);
-
- this._reloadList();
- },
-
- uninit: function () {
- Services.obs.removeObserver(this, "passwordmgr-storage-changed");
- window.removeEventListener("popstate", this, false);
- },
-
- _loadList: function (logins) {
- let list = document.getElementById("logins-list");
- let newList = list.cloneNode(false);
-
- logins.forEach(login => {
- let item = this._createItemForLogin(login);
- newList.appendChild(item);
- });
-
- list.parentNode.replaceChild(newList, list);
- },
-
- _showList: function () {
- let loginsListPage = document.getElementById("logins-list-page");
- loginsListPage.classList.remove("hidden");
-
- let editLoginPage = document.getElementById("edit-login-page");
- editLoginPage.classList.add("hidden");
-
- // If the Show/Hide password button has been flipped, reset it
- if (this._isPasswordBtnInHideMode()) {
- this._updatePasswordBtn(true);
- }
- },
-
- _onPopState: function (event) {
- // Called when back/forward is used to change the state of the page
- if (event.state) {
- this._showEditLoginDialog(event.state.id);
- } else {
- this._selectedLogin = null;
- this._showList();
- }
- },
- _showEditLoginDialog: function (login) {
- let listPage = document.getElementById("logins-list-page");
- listPage.classList.add("hidden");
-
- let editLoginPage = document.getElementById("edit-login-page");
- editLoginPage.classList.remove("hidden");
-
- let usernameField = document.getElementById("username");
- usernameField.value = login.username;
- let passwordField = document.getElementById("password");
- passwordField.value = login.password;
- let domainField = document.getElementById("hostname");
- domainField.value = login.hostname;
-
- let img = document.getElementById("favicon");
- this._loadFavicon(img, login.hostname);
-
- let headerText = document.getElementById("edit-login-header-text");
- if (login.hostname && (login.hostname != "")) {
- headerText.textContent = login.hostname;
- }
- else {
- headerText.textContent = gStringBundle.GetStringFromName("editLogin.fallbackTitle");
- }
-
- passwordField.addEventListener("input", (event) => {
- let newPassword = passwordField.value;
- let updateBtn = document.getElementById("update-btn");
-
- if (newPassword === "") {
- updateBtn.disabled = true;
- updateBtn.classList.add("disabled-btn");
- } else if ((newPassword !== "") && (updateBtn.disabled === true)) {
- updateBtn.disabled = false;
- updateBtn.classList.remove("disabled-btn");
- }
- }, false);
- },
-
- _onSaveEditLogin: function() {
- let newUsername = document.getElementById("username").value;
- let newPassword = document.getElementById("password").value;
- let newDomain = document.getElementById("hostname").value;
- let origUsername = this._selectedLogin.username;
- let origPassword = this._selectedLogin.password;
- let origDomain = this._selectedLogin.hostname;
-
- try {
- if ((newUsername === origUsername) &&
- (newPassword === origPassword) &&
- (newDomain === origDomain) ) {
- Snackbars.show(gStringBundle.GetStringFromName("editLogin.saved1"), Snackbars.LENGTH_LONG);
- this._showList();
- return;
- }
-
- let logins = Services.logins.findLogins({}, origDomain, origDomain, null);
-
- for (let i = 0; i < logins.length; i++) {
- if (logins[i].username == origUsername) {
- let clone = logins[i].clone();
- clone.username = newUsername;
- clone.password = newPassword;
- clone.hostname = newDomain;
- Services.logins.removeLogin(logins[i]);
- Services.logins.addLogin(clone);
- break;
- }
- }
- } catch (e) {
- Snackbars.show(gStringBundle.GetStringFromName("editLogin.couldNotSave"), Snackbars.LENGTH_LONG);
- return;
- }
- Snackbars.show(gStringBundle.GetStringFromName("editLogin.saved1"), Snackbars.LENGTH_LONG);
- this._showList();
- },
-
- _onPasswordBtn: function () {
- this._updatePasswordBtn(this._isPasswordBtnInHideMode());
- },
-
- _updatePasswordBtn: function (aShouldShow) {
- let passwordField = document.getElementById("password");
- let button = document.getElementById("password-btn");
- let show = gStringBundle.GetStringFromName("password-btn.show");
- let hide = gStringBundle.GetStringFromName("password-btn.hide");
- if (aShouldShow) {
- passwordField.type = "password";
- button.textContent = show;
- button.classList.remove("password-btn-hide");
- } else {
- passwordField.type = "text";
- button.textContent= hide;
- button.classList.add("password-btn-hide");
- }
- },
-
- _isPasswordBtnInHideMode: function () {
- let button = document.getElementById("password-btn");
- return button.classList.contains("password-btn-hide");
- },
-
- _showPassword: function(password) {
- let passwordPrompt = new Prompt({
- window: window,
- message: password,
- buttons: [
- gStringBundle.GetStringFromName("loginsDialog.copy"),
- gStringBundle.GetStringFromName("loginsDialog.cancel") ]
- }).show((data) => {
- switch (data.button) {
- case 0:
- // Corresponds to "Copy password" button.
- copyStringShowSnackbar(password, gStringBundle.GetStringFromName("loginsDetails.passwordCopied"));
- }
- });
- },
-
- _onLoginClick: function (event) {
- let loginItem = event.currentTarget;
- let login = loginItem.login;
- if (!login) {
- debug("No login!");
- return;
- }
-
- let prompt = new Prompt({
- window: window,
- });
- let menuItems = [
- { label: gStringBundle.GetStringFromName("loginsMenu.showPassword") },
- { label: gStringBundle.GetStringFromName("loginsMenu.copyPassword") },
- { label: gStringBundle.GetStringFromName("loginsMenu.copyUsername") },
- { label: gStringBundle.GetStringFromName("loginsMenu.editLogin") },
- { label: gStringBundle.GetStringFromName("loginsMenu.delete") }
- ];
-
- prompt.setSingleChoiceItems(menuItems);
- prompt.show((data) => {
- // Switch on indices of buttons, as they were added when creating login item.
- switch (data.button) {
- case 0:
- this._showPassword(login.password);
- break;
- case 1:
- copyStringShowSnackbar(login.password, gStringBundle.GetStringFromName("loginsDetails.passwordCopied"));
- break;
- case 2:
- copyStringShowSnackbar(login.username, gStringBundle.GetStringFromName("loginsDetails.usernameCopied"));
- break;
- case 3:
- this._selectedLogin = login;
- this._showEditLoginDialog(login);
- history.pushState({ id: login.guid }, document.title);
- break;
- case 4:
- let confirmPrompt = new Prompt({
- window: window,
- message: gStringBundle.GetStringFromName("loginsDialog.confirmDelete"),
- buttons: [
- gStringBundle.GetStringFromName("loginsDialog.confirm"),
- gStringBundle.GetStringFromName("loginsDialog.cancel") ]
- });
- confirmPrompt.show((data) => {
- switch (data.button) {
- case 0:
- // Corresponds to "confirm" button.
- Services.logins.removeLogin(login);
- }
- });
- }
- });
- },
-
- _loadFavicon: function (aImg, aHostname) {
- // Load favicon from cache.
- Messaging.sendRequestForResult({
- type: "Favicon:CacheLoad",
- url: aHostname,
- }).then(function(faviconUrl) {
- aImg.style.backgroundImage= "url('" + faviconUrl + "')";
- aImg.style.visibility = "visible";
- }, function(data) {
- debug("Favicon cache failure : " + data);
- aImg.style.visibility = "visible";
- });
- },
-
- _createItemForLogin: function (login) {
- let loginItem = document.createElement("div");
-
- loginItem.setAttribute("loginID", login.guid);
- loginItem.className = "login-item list-item";
-
- loginItem.addEventListener("click", this, true);
-
- // Create item icon.
- let img = document.createElement("div");
- img.className = "icon";
-
- this._loadFavicon(img, login.hostname);
- loginItem.appendChild(img);
-
- // Create item details.
- let inner = document.createElement("div");
- inner.className = "inner";
-
- let details = document.createElement("div");
- details.className = "details";
- inner.appendChild(details);
-
- let titlePart = document.createElement("div");
- titlePart.className = "hostname";
- titlePart.textContent = login.hostname;
- details.appendChild(titlePart);
-
- let versionPart = document.createElement("div");
- versionPart.textContent = login.httpRealm;
- versionPart.className = "realm";
- details.appendChild(versionPart);
-
- let descPart = document.createElement("div");
- descPart.textContent = login.username;
- descPart.className = "username";
- inner.appendChild(descPart);
-
- loginItem.appendChild(inner);
- loginItem.login = login;
- return loginItem;
- },
-
- handleEvent: function (event) {
- switch (event.type) {
- case "popstate": {
- this._onPopState(event);
- break;
- }
- case "click": {
- this._onLoginClick(event);
- break;
- }
- }
- },
-
- observe: function (subject, topic, data) {
- switch(topic) {
- case "passwordmgr-storage-changed": {
- this._reloadList();
- break;
- }
- }
- },
-
- _filter: function(event) {
- let value = event.target.value.toLowerCase();
- let logins = this._logins.filter((login) => {
- if (login.hostname.toLowerCase().indexOf(value) != -1) {
- return true;
- }
- if (login.username &&
- login.username.toLowerCase().indexOf(value) != -1) {
- return true;
- }
- if (login.httpRealm &&
- login.httpRealm.toLowerCase().indexOf(value) != -1) {
- return true;
- }
- return false;
- });
-
- this._loadList(logins);
- }
-};
-
-window.addEventListener("load", Logins.init.bind(Logins), false);
-window.addEventListener("unload", Logins.uninit.bind(Logins), false);
diff --git a/mobile/android/chrome/content/aboutLogins.xhtml b/mobile/android/chrome/content/aboutLogins.xhtml
deleted file mode 100644
index 02225d43a..000000000
--- a/mobile/android/chrome/content/aboutLogins.xhtml
+++ /dev/null
@@ -1,90 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
-"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
-<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
-%globalDTD;
-<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
-%brandDTD;
-<!ENTITY % aboutDTD SYSTEM "chrome://browser/locale/aboutLogins.dtd" >
-%aboutDTD;
-]>
-<!-- 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>&aboutLogins.title;</title>
- <meta name="viewport" content="width=device-width; user-scalable=0" />
- <link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
- <link rel="stylesheet" href="chrome://browser/skin/spinner.css" type="text/css"/>
- <link rel="stylesheet" href="chrome://browser/skin/aboutBase.css" type="text/css"/>
- <link rel="stylesheet" href="chrome://browser/skin/aboutLogins.css" type="text/css"/>
- <script type="application/javascript;version=1.8" src="chrome://browser/content/aboutLogins.js"></script>
- </head>
- <body dir="&locale.dir;">
-
- <div id="logins-list-page">
- <div id="logins-header" class="header">
- <div>&aboutLogins.title;</div>
- <ul class="toolbar-buttons">
- <li id="filter-button"></li>
- </ul>
- </div>
- <div id="content-body">
- <div id="logins-list" class="list"/>
- <div id="filter-input-container" hidden="true">
- <input id="filter-input" type="search"/>
- <div id="filter-clear"/>
- </div>
- </div>
- <div id="logins-list-loading-body" class="hidden">
- <div id="loading-img-container">
-
- <div id="spinner" class="mui-refresh-main">
- <div class="mui-refresh-wrapper">
- <div class="mui-spinner-wrapper">
- <div class="mui-spinner-main">
- <div class="mui-spinner-left">
- <div class="mui-half-circle-left" />
- </div>
- <div class="mui-spinner-right">
- <div class="mui-half-circle-right" />
- </div>
- </div>
- </div>
- </div>
- </div>
-
- </div>
- </div>
- <div id="empty-body" class="hidden">
- <div id="empty-obj-text-container">
- <object type="image/svg+xml" id="empty-icon" data="chrome://browser/skin/images/icon_key_emptypage.svg"/>
- <div class="empty-text">&aboutLogins.emptyLoginText;</div>
- <div class="empty-hint">&aboutLogins.emptyLoginHint;</div>
- </div>
- </div>
- </div>
-
- <div id="edit-login-page" class="hidden">
- <div id="edit-login-header" class="header">
- <div id="edit-login-header-text"/>
- </div>
- <div class="edit-login-div">
- <div id="favicon" class="edit-login-icon"/>
- <input type="text" name="hostname" id="hostname" class="edit-login-input"/>
- </div>
- <div class="edit-login-div">
- <input type="text" name="username" id="username" class="edit-login-input" autocomplete="off"/>
- </div>
- <div class="edit-login-div">
- <input type="password" id="password" name="password" value="password" class="edit-login-input" />
- <button id="password-btn"></button>
- </div>
- <div class="edit-login-div">
- <button id="update-btn" class="update-button">&aboutLogins.update;</button>
- </div>
- </div>
-
- </body>
-</html>
diff --git a/mobile/android/chrome/content/aboutPrivateBrowsing.js b/mobile/android/chrome/content/aboutPrivateBrowsing.js
deleted file mode 100644
index 782abfb5d..000000000
--- a/mobile/android/chrome/content/aboutPrivateBrowsing.js
+++ /dev/null
@@ -1,32 +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 Ci = Components.interfaces;
-var Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
-
-XPCOMUtils.defineLazyGetter(window, "gChromeWin", () =>
- window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShellTreeItem)
- .rootTreeItem
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindow)
- .QueryInterface(Ci.nsIDOMChromeWindow));
-
-document.addEventListener("DOMContentLoaded", function() {
- let BrowserApp = gChromeWin.BrowserApp;
-
- if (!PrivateBrowsingUtils.isContentWindowPrivate(window)) {
- document.body.setAttribute("class", "normal");
- document.getElementById("newPrivateTabLink").addEventListener("click", function() {
- BrowserApp.addTab("about:privatebrowsing", { selected: true, parentId: BrowserApp.selectedTab.id, isPrivate: true });
- }, false);
- }
- }, false);
diff --git a/mobile/android/chrome/content/aboutPrivateBrowsing.xhtml b/mobile/android/chrome/content/aboutPrivateBrowsing.xhtml
deleted file mode 100644
index 7075bd11e..000000000
--- a/mobile/android/chrome/content/aboutPrivateBrowsing.xhtml
+++ /dev/null
@@ -1,43 +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 % privatebrowsingpageDTD SYSTEM "chrome://browser/locale/aboutPrivateBrowsing.dtd">
- %privatebrowsingpageDTD;
-]>
-
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <title>&privatebrowsingpage.title;</title>
- <meta name="viewport" content="width=device-width, initial-scale=1; user-scalable=no"/>
- <link rel="stylesheet" href="chrome://browser/skin/aboutPrivateBrowsing.css" type="text/css" media="all"/>
- <link rel="icon" type="image/png" href="chrome://branding/content/favicon32.png" />
- <script type="application/javascript;version=1.8" src="chrome://browser/content/aboutPrivateBrowsing.js"></script>
- </head>
-
- <body class="private">
- <img class="showPrivate masq" src="chrome://browser/skin/images/privatebrowsing-mask-and-shield.svg" />
- <img class="showNormal masq" src="chrome://browser/skin/images/privatebrowsing-mask.png" />
-
- <h1 class="showPrivate">&privatebrowsingpage.title;<br />&privatebrowsingpage.title.private;</h1>
- <h1 class="showNormal">&privatebrowsingpage.title.normal1;</h1>
-
- <div class="contentSection">
- <p class="showPrivate">&privatebrowsingpage.description.trackingProtection;<br /><br />&privatebrowsingpage.description.privateDetails;</p>
- <p class="showNormal">&privatebrowsingpage.description.normal2;</p>
-
- <p class="showPrivate"><a href="https://support.mozilla.org/kb/private-browsing-firefox-android">&privatebrowsingpage.link.private;</a></p>
- <p class="showNormal"><a href="#" id="newPrivateTabLink">&privatebrowsingpage.link.normal;</a></p>
- </div>
-
- </body>
-</html>
diff --git a/mobile/android/chrome/content/aboutRights.xhtml b/mobile/android/chrome/content/aboutRights.xhtml
deleted file mode 100644
index 8172788f4..000000000
--- a/mobile/android/chrome/content/aboutRights.xhtml
+++ /dev/null
@@ -1,95 +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 % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
- %brandDTD;
- <!ENTITY % aboutRightsDTD SYSTEM "chrome://global/locale/aboutRights.dtd">
- %aboutRightsDTD;
-]>
-
-<!-- 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>&rights.pagetitle;</title>
- <link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css"/>
-</head>
-
-<body id="your-rights" dir="&rights.locale-direction;" class="aboutPageWideContainer">
-
-<h1>&rights.intro-header;</h1>
-
-<p>&rights.intro;</p>
-
-<ul>
- <li>&rights.intro-point1a;<a href="https://www.mozilla.org/MPL/">&rights.intro-point1b;</a>&rights.intro-point1c;</li>
- <!-- Point 2 discusses Mozilla trademarks, and isn't needed when the build is unbranded.
- Point 3 discusses privacy policy, unbranded builds get a placeholder (for the vendor to replace)
- Point 4 discusses web service terms, unbranded builds gets a placeholder (for the vendor to replace) -->
- <li>&rights.intro-point2-a;<a href="https://www.mozilla.org/foundation/trademarks/policy.html">&rights.intro-point2-b;</a>&rights.intro-point2-c;</li>
- <li>&rights.intro-point2.5;</li>
- <li>&rights2.intro-point3a;<a href="https://www.mozilla.org/privacy/firefox/">&rights2.intro-point3b;</a>&rights.intro-point3c;</li>
- <li>&rights2.intro-point4a;<a href="about:rights#webservices" onclick="showServices();">&rights.intro-point4b;</a>&rights.intro-point4c;</li>
-</ul>
-
-<div id="webservices-container">
- <a name="webservices"/>
- <h3>&rights2.webservices-header;</h3>
-
- <p>&rights2.webservices-a;<a href="about:rights#disabling-webservices" onclick="showDisablingServices();">&rights2.webservices-b;</a>&rights3.webservices-c;</p>
-
- <div id="disabling-webservices-container" style="margin-left:40px;">
- <a name="disabling-webservices"/>
- <!-- Safe Browsing cannot be disabled in Firefox Mobile; these instructions show how to do it on desktop
- <p><strong>&rights.safebrowsing-a;</strong>&rights.safebrowsing-b;</p>
- <ul>
- <li>&rights.safebrowsing-term1;</li>
- <li>&rights.safebrowsing-term2;</li>
- <li>&rights2.safebrowsing-term3;</li>
- <li>&rights.safebrowsing-term4;</li>
- </ul>
- -->
-
- <p><strong>&rights.locationawarebrowsing-a;</strong>&rights.locationawarebrowsing-b;</p>
- <ul>
- <li>&rights.locationawarebrowsing-term1a;<code>&rights.locationawarebrowsing-term1b;</code></li>
- <li>&rights.locationawarebrowsing-term2;</li>
- <li>&rights.locationawarebrowsing-term3;</li>
- <li>&rights.locationawarebrowsing-term4;</li>
- </ul>
- </div>
-
- <ol>
- <!-- Terms only apply to official builds, unbranded builds get a placeholder. -->
- <li>&rights2.webservices-term1;</li>
- <li>&rights.webservices-term2;</li>
- <li>&rights2.webservices-term3;</li>
- <li><strong>&rights.webservices-term4;</strong></li>
- <li><strong>&rights.webservices-term5;</strong></li>
- <li>&rights.webservices-term6;</li>
- <li>&rights.webservices-term7;</li>
- </ol>
-</div>
-
-<script type="application/javascript"><![CDATA[
- var servicesDiv = document.getElementById("webservices-container");
- servicesDiv.style.display = "none";
-
- function showServices() {
- servicesDiv.style.display = "";
- }
-
- var disablingServicesDiv = document.getElementById("disabling-webservices-container");
- disablingServicesDiv.style.display = "none";
-
- function showDisablingServices() {
- disablingServicesDiv.style.display = "";
- }
-]]></script>
-
-</body>
-</html>
diff --git a/mobile/android/chrome/content/bindings/checkbox.xml b/mobile/android/chrome/content/bindings/checkbox.xml
deleted file mode 100644
index ec5c7828b..000000000
--- a/mobile/android/chrome/content/bindings/checkbox.xml
+++ /dev/null
@@ -1,75 +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 bindings [
- <!ENTITY % checkboxDTD SYSTEM "chrome://browser/locale/checkbox.dtd">
- %checkboxDTD;
-]>
-
-<bindings
- xmlns="http://www.mozilla.org/xbl"
- xmlns:xbl="http://www.mozilla.org/xbl"
- xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
- <binding id="checkbox-radio" display="xul:box" extends="chrome://global/content/bindings/checkbox.xml#checkbox-baseline">
- <content>
- <xul:radiogroup anonid="group" xbl:inherits="disabled">
- <xul:radio anonid="on" class="checkbox-radio-on" label="&checkbox.yes.label;" xbl:inherits="label=onlabel"/>
- <xul:radio anonid="off" class="checkbox-radio-off" label="&checkbox.no.label;" xbl:inherits="label=offlabel"/>
- </xul:radiogroup>
- </content>
- <implementation>
- <constructor><![CDATA[
- this.setChecked(this.checked);
- ]]></constructor>
-
- <field name="_group">
- document.getAnonymousElementByAttribute(this, "anonid", "group");
- </field>
-
- <field name="_on">
- document.getAnonymousElementByAttribute(this, "anonid", "on");
- </field>
-
- <field name="_off">
- document.getAnonymousElementByAttribute(this, "anonid", "off");
- </field>
-
- <property name="onlabel"
- onget="return this._on.label"
- onset="this._on.label=val"/>
-
- <property name="offlabel"
- onget="return this._off.label"
- onset="this._off.label=val"/>
-
- <method name="setChecked">
- <parameter name="aValue"/>
- <body>
- <![CDATA[
- var change = (aValue != this.checked);
- if (aValue) {
- this.setAttribute("checked", "true");
- this._group.selectedItem = this._on;
- }
- else {
- this.removeAttribute("checked");
- this._group.selectedItem = this._off;
- }
-
- if (change) {
- var event = document.createEvent("Events");
- event.initEvent("CheckboxStateChange", true, true);
- this.dispatchEvent(event);
- }
- return aValue;
- ]]>
- </body>
- </method>
- </implementation>
- </binding>
-
-</bindings>
diff --git a/mobile/android/chrome/content/bindings/settings.xml b/mobile/android/chrome/content/bindings/settings.xml
deleted file mode 100644
index 0019e9d3b..000000000
--- a/mobile/android/chrome/content/bindings/settings.xml
+++ /dev/null
@@ -1,66 +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/. -->
-
-
-<bindings
- xmlns="http://www.mozilla.org/xbl"
- xmlns:xbl="http://www.mozilla.org/xbl"
- xmlns:html="http://www.w3.org/1999/xhtml"
- xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
- <binding id="setting-fulltoggle-bool" extends="chrome://mozapps/content/extensions/setting.xml#setting-bool">
- <handlers>
- <handler event="command" button="0" phase="capturing">
- <![CDATA[
- event.stopPropagation();
- ]]>
- </handler>
- <handler event="click" button="0" phase="capturing">
- <![CDATA[
- event.stopPropagation();
- this.input.checked = !this.input.checked;
- this.inputChanged();
- this.fireEvent("oncommand");
- ]]>
- </handler>
- </handlers>
- </binding>
-
- <binding id="setting-fulltoggle-boolint" extends="chrome://mozapps/content/extensions/setting.xml#setting-boolint">
- <handlers>
- <handler event="command" button="0" phase="capturing">
- <![CDATA[
- event.stopPropagation();
- ]]>
- </handler>
- <handler event="click" button="0" phase="capturing">
- <![CDATA[
- event.stopPropagation();
- this.input.checked = !this.input.checked;
- this.inputChanged();
- this.fireEvent("oncommand");
- ]]>
- </handler>
- </handlers>
- </binding>
-
- <binding id="setting-fulltoggle-localized-bool" extends="chrome://mozapps/content/extensions/setting.xml#setting-localized-bool">
- <handlers>
- <handler event="command" button="0" phase="capturing">
- <![CDATA[
- event.stopPropagation();
- ]]>
- </handler>
- <handler event="click" button="0" phase="capturing">
- <![CDATA[
- event.stopPropagation();
- this.input.checked = !this.input.checked;
- this.inputChanged();
- this.fireEvent("oncommand");
- ]]>
- </handler>
- </handlers>
- </binding>
-</bindings>
diff --git a/mobile/android/chrome/content/blockedSite.xhtml b/mobile/android/chrome/content/blockedSite.xhtml
deleted file mode 100644
index 5f04edbef..000000000
--- a/mobile/android/chrome/content/blockedSite.xhtml
+++ /dev/null
@@ -1,195 +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/phishing.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>
- <meta name="viewport" content="width=device-width; user-scalable=false" />
- <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all" />
- <link rel="icon" type="image/png" id="favicon" sizes="64x64" href="chrome://browser/skin/images/blocked-warning.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 (/^view-source\:/.test(url))
- 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);
- }
-
- if (!getOverride()) {
- var btn = document.getElementById("ignoreWarningButton");
- if (btn) {
- btn.parentNode.removeChild(btn);
- }
- }
-
- // Set sitename
- document.getElementById(error + "_sitename").textContent = getHostString();
- document.title = document.getElementById("errorTitleText_" + error)
- .innerHTML;
-
- // Inform the test harness that we're done loading the page
- var event = new CustomEvent("AboutBlockedLoaded");
- document.dispatchEvent(event);
- }
- ]]></script>
- </head>
-
- <body id="errorPage" class="blockedsite" dir="&locale.dir;">
-
- <div id="errorPageContainer">
-
- <!-- Error Title -->
- <div id="errorTitle">
- <h1 id="errorTitleText_phishing" class="errorTitleText">&safeb.blocked.phishingPage.title3;</h1>
- <h1 id="errorTitleText_malware" class="errorTitleText">&safeb.blocked.malwarePage.title;</h1>
- <h1 id="errorTitleText_unwanted" class="errorTitleText">&safeb.blocked.unwantedPage.title;</h1>
- </div>
-
- <div id="errorLongContent">
-
- <!-- Short Description -->
- <div id="errorShortDesc">
- <p id="errorShortDescText_phishing">&safeb.blocked.phishingPage.shortDesc3;</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.longDesc3;</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">
- <!-- Commands handled in browser.js -->
- <button id="getMeOutButton">&safeb.palm.accept.label;</button>
- <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>
diff --git a/mobile/android/chrome/content/browser.css b/mobile/android/chrome/content/browser.css
deleted file mode 100644
index fdc35d961..000000000
--- a/mobile/android/chrome/content/browser.css
+++ /dev/null
@@ -1,7 +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/. */
-
-browser[remote="true"] {
- -moz-binding: url("chrome://global/content/bindings/remote-browser.xml#remote-browser");
-} \ No newline at end of file
diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js
deleted file mode 100644
index 535f7e607..000000000
--- a/mobile/android/chrome/content/browser.js
+++ /dev/null
@@ -1,6961 +0,0 @@
-// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
-/* 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 Cc = Components.classes;
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-var Cr = Components.results;
-
-Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/AddonManager.jsm");
-Cu.import("resource://gre/modules/AsyncPrefs.jsm");
-Cu.import("resource://gre/modules/DelayedInit.jsm");
-
-if (AppConstants.ACCESSIBILITY) {
- XPCOMUtils.defineLazyModuleGetter(this, "AccessFu",
- "resource://gre/modules/accessibility/AccessFu.jsm");
-}
-
-XPCOMUtils.defineLazyModuleGetter(this, "SpatialNavigation",
- "resource://gre/modules/SpatialNavigation.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "DownloadNotifications",
- "resource://gre/modules/DownloadNotifications.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
- "resource://gre/modules/FileUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "JNI",
- "resource://gre/modules/JNI.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
- "resource://gre/modules/UITelemetry.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
- "resource://gre/modules/PluralForm.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
- "resource://gre/modules/Downloads.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Messaging",
- "resource://gre/modules/Messaging.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "UserAgentOverrides",
- "resource://gre/modules/UserAgentOverrides.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
- "resource://gre/modules/LoginManagerContent.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerParent",
- "resource://gre/modules/LoginManagerParent.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
-
-#ifdef MOZ_SAFE_BROWSING
- XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
- "resource://gre/modules/SafeBrowsing.jsm");
-#endif
-
-XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
- "resource://gre/modules/BrowserUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
- "resource://gre/modules/PrivateBrowsingUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Sanitizer",
- "resource://gre/modules/Sanitizer.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Prompt",
- "resource://gre/modules/Prompt.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "HelperApps",
- "resource://gre/modules/HelperApps.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "SSLExceptions",
- "resource://gre/modules/SSLExceptions.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
- "resource://gre/modules/FormHistory.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
- "@mozilla.org/uuid-generator;1",
- "nsIUUIDGenerator");
-
-XPCOMUtils.defineLazyServiceGetter(this, "Profiler",
- "@mozilla.org/tools/profiler;1",
- "nsIProfiler");
-
-XPCOMUtils.defineLazyModuleGetter(this, "SimpleServiceDiscovery",
- "resource://gre/modules/SimpleServiceDiscovery.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
- "resource://gre/modules/CharsetMenu.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "NetErrorHelper",
- "resource://gre/modules/NetErrorHelper.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PermissionsUtils",
- "resource://gre/modules/PermissionsUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
- "resource://gre/modules/Preferences.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "SharedPreferences",
- "resource://gre/modules/SharedPreferences.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Notifications",
- "resource://gre/modules/Notifications.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "RuntimePermissions", "resource://gre/modules/RuntimePermissions.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "WebsiteMetadata", "resource://gre/modules/WebsiteMetadata.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "FontEnumerator",
- "@mozilla.org/gfx/fontenumerator;1",
- "nsIFontEnumerator");
-
-var lazilyLoadedBrowserScripts = [
- ["SelectHelper", "chrome://browser/content/SelectHelper.js"],
- ["InputWidgetHelper", "chrome://browser/content/InputWidgetHelper.js"],
- ["MasterPassword", "chrome://browser/content/MasterPassword.js"],
- ["PluginHelper", "chrome://browser/content/PluginHelper.js"],
- ["OfflineApps", "chrome://browser/content/OfflineApps.js"],
- ["Linkifier", "chrome://browser/content/Linkify.js"],
- ["CastingApps", "chrome://browser/content/CastingApps.js"],
- ["RemoteDebugger", "chrome://browser/content/RemoteDebugger.js"],
-];
-if (!AppConstants.RELEASE_OR_BETA) {
- lazilyLoadedBrowserScripts.push(
- ["WebcompatReporter", "chrome://browser/content/WebcompatReporter.js"]);
-}
-
-lazilyLoadedBrowserScripts.forEach(function (aScript) {
- let [name, script] = aScript;
- XPCOMUtils.defineLazyGetter(window, name, function() {
- let sandbox = {};
- Services.scriptloader.loadSubScript(script, sandbox);
- return sandbox[name];
- });
-});
-
-var lazilyLoadedObserverScripts = [
- ["MemoryObserver", ["memory-pressure", "Memory:Dump"], "chrome://browser/content/MemoryObserver.js"],
- ["ConsoleAPI", ["console-api-log-event"], "chrome://browser/content/ConsoleAPI.js"],
- ["FindHelper", ["FindInPage:Opened", "FindInPage:Closed", "Tab:Selected"], "chrome://browser/content/FindHelper.js"],
- ["PermissionsHelper", ["Permissions:Check", "Permissions:Get", "Permissions:Clear"], "chrome://browser/content/PermissionsHelper.js"],
- ["FeedHandler", ["Feeds:Subscribe"], "chrome://browser/content/FeedHandler.js"],
- ["Feedback", ["Feedback:Show"], "chrome://browser/content/Feedback.js"],
- ["EmbedRT", ["GeckoView:ImportScript"], "chrome://browser/content/EmbedRT.js"],
- ["Reader", ["Reader:AddToCache", "Reader:RemoveFromCache"], "chrome://browser/content/Reader.js"],
- ["PrintHelper", ["Print:PDF"], "chrome://browser/content/PrintHelper.js"],
-];
-
-lazilyLoadedObserverScripts.push(
-["ActionBarHandler", ["TextSelection:Get", "TextSelection:Action", "TextSelection:End"],
- "chrome://browser/content/ActionBarHandler.js"]
-);
-
-if (AppConstants.MOZ_WEBRTC) {
- lazilyLoadedObserverScripts.push(
- ["WebrtcUI", ["getUserMedia:request",
- "PeerConnection:request",
- "recording-device-events",
- "VideoCapture:Paused",
- "VideoCapture:Resumed"], "chrome://browser/content/WebrtcUI.js"])
-}
-
-lazilyLoadedObserverScripts.forEach(function (aScript) {
- let [name, notifications, script] = aScript;
- XPCOMUtils.defineLazyGetter(window, name, function() {
- let sandbox = {};
- Services.scriptloader.loadSubScript(script, sandbox);
- return sandbox[name];
- });
- let observer = (s, t, d) => {
- Services.obs.removeObserver(observer, t);
- Services.obs.addObserver(window[name], t, false);
- window[name].observe(s, t, d); // Explicitly notify new observer
- };
- notifications.forEach((notification) => {
- Services.obs.addObserver(observer, notification, false);
- });
-});
-
-// Lazily-loaded browser scripts that use message listeners.
-[
- ["Reader", [
- ["Reader:AddToCache", false],
- ["Reader:RemoveFromCache", false],
- ["Reader:ArticleGet", false],
- ["Reader:DropdownClosed", true], // 'true' allows us to survive mid-air cycle-collection.
- ["Reader:DropdownOpened", false],
- ["Reader:FaviconRequest", false],
- ["Reader:ToolbarHidden", false],
- ["Reader:SystemUIVisibility", false],
- ["Reader:UpdateReaderButton", false],
- ], "chrome://browser/content/Reader.js"],
-].forEach(aScript => {
- let [name, messages, script] = aScript;
- XPCOMUtils.defineLazyGetter(window, name, function() {
- let sandbox = {};
- Services.scriptloader.loadSubScript(script, sandbox);
- return sandbox[name];
- });
-
- let mm = window.getGroupMessageManager("browsers");
- let listener = (message) => {
- mm.removeMessageListener(message.name, listener);
- let listenAfterClose = false;
- for (let [name, laClose] of messages) {
- if (message.name === name) {
- listenAfterClose = laClose;
- break;
- }
- }
-
- mm.addMessageListener(message.name, window[name], listenAfterClose);
- window[name].receiveMessage(message);
- };
-
- messages.forEach((message) => {
- let [name, listenAfterClose] = message;
- mm.addMessageListener(name, listener, listenAfterClose);
- });
-});
-
-// Lazily-loaded JS modules that use observer notifications
-[
- ["Home", ["HomeBanner:Get", "HomePanels:Get", "HomePanels:Authenticate", "HomePanels:RefreshView",
- "HomePanels:Installed", "HomePanels:Uninstalled"], "resource://gre/modules/Home.jsm"],
-].forEach(module => {
- let [name, notifications, resource] = module;
- XPCOMUtils.defineLazyModuleGetter(this, name, resource);
- let observer = (s, t, d) => {
- Services.obs.removeObserver(observer, t);
- Services.obs.addObserver(this[name], t, false);
- this[name].observe(s, t, d); // Explicitly notify new observer
- };
- notifications.forEach(notification => {
- Services.obs.addObserver(observer, notification, false);
- });
-});
-
-XPCOMUtils.defineLazyServiceGetter(this, "Haptic",
- "@mozilla.org/widget/hapticfeedback;1", "nsIHapticFeedback");
-
-XPCOMUtils.defineLazyServiceGetter(this, "ParentalControls",
- "@mozilla.org/parental-controls-service;1", "nsIParentalControlsService");
-
-XPCOMUtils.defineLazyServiceGetter(this, "DOMUtils",
- "@mozilla.org/inspector/dom-utils;1", "inIDOMUtils");
-
-XPCOMUtils.defineLazyServiceGetter(window, "URIFixup",
- "@mozilla.org/docshell/urifixup;1", "nsIURIFixup");
-
-if (AppConstants.MOZ_WEBRTC) {
- XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
- "@mozilla.org/mediaManagerService;1", "nsIMediaManagerService");
-}
-
-XPCOMUtils.defineLazyModuleGetter(this, "Log",
- "resource://gre/modules/AndroidLog.jsm", "AndroidLog");
-
-// Define the "dump" function as a binding of the Log.d function so it specifies
-// the "debug" priority and a log tag.
-function dump(msg) {
- Log.d("Browser", msg);
-}
-
-const kStateActive = 0x00000001; // :active pseudoclass for elements
-
-const kXLinkNamespace = "http://www.w3.org/1999/xlink";
-
-function fuzzyEquals(a, b) {
- return (Math.abs(a - b) < 1e-6);
-}
-
-XPCOMUtils.defineLazyGetter(this, "ContentAreaUtils", function() {
- let ContentAreaUtils = {};
- Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", ContentAreaUtils);
- return ContentAreaUtils;
-});
-
-XPCOMUtils.defineLazyModuleGetter(this, "Rect", "resource://gre/modules/Geometry.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Point", "resource://gre/modules/Geometry.jsm");
-
-function resolveGeckoURI(aURI) {
- if (!aURI)
- throw "Can't resolve an empty uri";
-
- if (aURI.startsWith("chrome://")) {
- let registry = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci["nsIChromeRegistry"]);
- return registry.convertChromeURL(Services.io.newURI(aURI, null, null)).spec;
- } else if (aURI.startsWith("resource://")) {
- let handler = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
- return handler.resolveURI(Services.io.newURI(aURI, null, null));
- }
- return aURI;
-}
-
-/**
- * Cache of commonly used string bundles.
- */
-var Strings = {
- init: function () {
- XPCOMUtils.defineLazyGetter(Strings, "brand", () => Services.strings.createBundle("chrome://branding/locale/brand.properties"));
- XPCOMUtils.defineLazyGetter(Strings, "browser", () => Services.strings.createBundle("chrome://browser/locale/browser.properties"));
- XPCOMUtils.defineLazyGetter(Strings, "reader", () => Services.strings.createBundle("chrome://global/locale/aboutReader.properties"));
- },
-
- flush: function () {
- Services.strings.flushBundles();
- this.init();
- },
-};
-
-Strings.init();
-
-const kFormHelperModeDisabled = 0;
-const kFormHelperModeEnabled = 1;
-const kFormHelperModeDynamic = 2; // disabled on tablets
-const kMaxHistoryListSize = 50;
-
-function InitLater(fn, object, name) {
- return DelayedInit.schedule(fn, object, name, 15000 /* 15s max wait */);
-}
-
-var BrowserApp = {
- _tabs: [],
- _selectedTab: null,
-
- get isTablet() {
- let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
- delete this.isTablet;
- return this.isTablet = sysInfo.get("tablet");
- },
-
- get isOnLowMemoryPlatform() {
- let memory = Cc["@mozilla.org/xpcom/memory-service;1"].getService(Ci.nsIMemory);
- delete this.isOnLowMemoryPlatform;
- return this.isOnLowMemoryPlatform = memory.isLowMemoryPlatform();
- },
-
- deck: null,
-
- startup: function startup() {
- window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess();
- dump("zerdatime " + Date.now() + " - browser chrome startup finished.");
- Services.obs.notifyObservers(this.browser, "BrowserChrome:Ready", null);
-
- this.deck = document.getElementById("browsers");
-
- BrowserEventHandler.init();
-
- ViewportHandler.init();
-
- Services.androidBridge.browserApp = this;
-
- Services.obs.addObserver(this, "Locale:OS", false);
- Services.obs.addObserver(this, "Locale:Changed", false);
- Services.obs.addObserver(this, "Tab:Load", false);
- Services.obs.addObserver(this, "Tab:Selected", false);
- Services.obs.addObserver(this, "Tab:Closed", false);
- Services.obs.addObserver(this, "Session:Back", false);
- Services.obs.addObserver(this, "Session:Forward", false);
- Services.obs.addObserver(this, "Session:Navigate", false);
- Services.obs.addObserver(this, "Session:Reload", false);
- Services.obs.addObserver(this, "Session:Stop", false);
- Services.obs.addObserver(this, "SaveAs:PDF", false);
- Services.obs.addObserver(this, "Browser:Quit", false);
- Services.obs.addObserver(this, "ScrollTo:FocusedInput", false);
- Services.obs.addObserver(this, "Sanitize:ClearData", false);
- Services.obs.addObserver(this, "FullScreen:Exit", false);
- Services.obs.addObserver(this, "Passwords:Init", false);
- Services.obs.addObserver(this, "FormHistory:Init", false);
- Services.obs.addObserver(this, "android-get-pref", false);
- Services.obs.addObserver(this, "android-set-pref", false);
- Services.obs.addObserver(this, "gather-telemetry", false);
- Services.obs.addObserver(this, "keyword-search", false);
- Services.obs.addObserver(this, "sessionstore-state-purge-complete", false);
- Services.obs.addObserver(this, "Fonts:Reload", false);
- Services.obs.addObserver(this, "Vibration:Request", false);
-
- Messaging.addListener(this.getHistory.bind(this), "Session:GetHistory");
-
- window.addEventListener("fullscreen", function() {
- Messaging.sendRequest({
- type: window.fullScreen ? "ToggleChrome:Hide" : "ToggleChrome:Show"
- });
- }, false);
-
- window.addEventListener("fullscreenchange", (e) => {
- // This event gets fired on the document and its entire ancestor chain
- // of documents. When enabling fullscreen, it is fired on the top-level
- // document first and goes down; when disabling the order is reversed
- // (per spec). This means the last event on enabling will be for the innermost
- // document, which will have fullscreenElement set correctly.
- let doc = e.target;
- Messaging.sendRequest({
- type: doc.fullscreenElement ? "DOMFullScreen:Start" : "DOMFullScreen:Stop",
- rootElement: doc.fullscreenElement == doc.documentElement
- });
-
- if (this.fullscreenTransitionTab) {
- // Tab selection has changed during a fullscreen transition, handle it now.
- let tab = this.fullscreenTransitionTab;
- this.fullscreenTransitionTab = null;
- this._handleTabSelected(tab);
- }
- }, false);
-
- NativeWindow.init();
- FormAssistant.init();
- IndexedDB.init();
- XPInstallObserver.init();
- CharacterEncoding.init();
- ActivityObserver.init();
- RemoteDebugger.init();
- UserAgentOverrides.init();
- DesktopUserAgent.init();
- Distribution.init();
- Tabs.init();
- SearchEngines.init();
- Experiments.init();
-
- // XXX maybe we don't do this if the launch was kicked off from external
- Services.io.offline = false;
-
- // Broadcast a UIReady message so add-ons know we are finished with startup
- let event = document.createEvent("Events");
- event.initEvent("UIReady", true, false);
- window.dispatchEvent(event);
-
- if (this._startupStatus) {
- this.onAppUpdated();
- }
-
- if (!ParentalControls.isAllowed(ParentalControls.INSTALL_EXTENSION)) {
- // Disable extension installs
- Services.prefs.setIntPref("extensions.enabledScopes", 1);
- Services.prefs.setIntPref("extensions.autoDisableScopes", 1);
- Services.prefs.setBoolPref("xpinstall.enabled", false);
- } else if (ParentalControls.parentalControlsEnabled) {
- Services.prefs.clearUserPref("extensions.enabledScopes");
- Services.prefs.clearUserPref("extensions.autoDisableScopes");
- Services.prefs.setBoolPref("xpinstall.enabled", true);
- }
-
- if (ParentalControls.parentalControlsEnabled) {
- let isBlockListEnabled = ParentalControls.isAllowed(ParentalControls.BLOCK_LIST);
- Services.prefs.setBoolPref("browser.safebrowsing.forbiddenURIs.enabled", isBlockListEnabled);
- Services.prefs.setBoolPref("browser.safebrowsing.allowOverride", !isBlockListEnabled);
-
- let isTelemetryEnabled = ParentalControls.isAllowed(ParentalControls.TELEMETRY);
- Services.prefs.setBoolPref("toolkit.telemetry.enabled", isTelemetryEnabled);
-
- let isHealthReportEnabled = ParentalControls.isAllowed(ParentalControls.HEALTH_REPORT);
- SharedPreferences.forApp().setBoolPref("android.not_a_preference.healthreport.uploadEnabled", isHealthReportEnabled);
- }
-
- let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
- if (sysInfo.get("version") < 16) {
- let defaults = Services.prefs.getDefaultBranch(null);
- defaults.setBoolPref("media.autoplay.enabled", false);
- }
-
- InitLater(() => {
- // The order that context menu items are added is important
- // Make sure the "Open in App" context menu item appears at the bottom of the list
- this.initContextMenu();
- ExternalApps.init();
- }, NativeWindow, "contextmenus");
-
- if (AppConstants.ACCESSIBILITY) {
- InitLater(() => AccessFu.attach(window), window, "AccessFu");
- }
-
- // Don't delay loading content.js because when we restore reader mode tabs,
- // we require the reader mode scripts in content.js right away.
- let mm = window.getGroupMessageManager("browsers");
- mm.loadFrameScript("chrome://browser/content/content.js", true);
-
- // Notify Java that Gecko has loaded.
- Messaging.sendRequest({ type: "Gecko:Ready" });
-
- this.deck.addEventListener("DOMContentLoaded", function BrowserApp_delayedStartup() {
- BrowserApp.deck.removeEventListener("DOMContentLoaded", BrowserApp_delayedStartup, false);
-
- InitLater(() => Cu.import("resource://gre/modules/NotificationDB.jsm"));
- InitLater(() => Cu.import("resource://gre/modules/PresentationDeviceInfoManager.jsm"));
-
- InitLater(() => Services.obs.notifyObservers(window, "browser-delayed-startup-finished", ""));
- InitLater(() => Messaging.sendRequest({ type: "Gecko:DelayedStartup" }));
-
- if (!AppConstants.RELEASE_OR_BETA) {
- InitLater(() => WebcompatReporter.init());
- }
-
- // Collect telemetry data.
- // We do this at startup because we want to move away from "gather-telemetry" (bug 1127907)
- InitLater(() => {
- Telemetry.addData("FENNEC_TRACKING_PROTECTION_STATE", parseInt(BrowserApp.getTrackingProtectionState()));
- Telemetry.addData("ZOOMED_VIEW_ENABLED", Services.prefs.getBoolPref("ui.zoomedview.enabled"));
- });
-
- InitLater(() => LightWeightThemeWebInstaller.init());
- InitLater(() => SpatialNavigation.init(BrowserApp.deck, null), window, "SpatialNavigation");
- InitLater(() => CastingApps.init(), window, "CastingApps");
- InitLater(() => Services.search.init(), Services, "search");
- InitLater(() => DownloadNotifications.init(), window, "DownloadNotifications");
-
-#ifdef MOZ_SAFE_BROWSING
- // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
- InitLater(() => SafeBrowsing.init(), window, "SafeBrowsing");
-#endif
-
- InitLater(() => Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager));
- InitLater(() => LoginManagerParent.init(), window, "LoginManagerParent");
-
- }, false);
-
- // Pass caret StateChanged events to ActionBarHandler.
- window.addEventListener("mozcaretstatechanged", e => {
- ActionBarHandler.caretStateChangedHandler(e);
- }, /* useCapture = */ true, /* wantsUntrusted = */ false);
- },
-
- get _startupStatus() {
- delete this._startupStatus;
-
- let savedMilestone = null;
- try {
- savedMilestone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone");
- } catch (e) {
- }
- let ourMilestone = AppConstants.MOZ_APP_VERSION;
- this._startupStatus = "";
- if (ourMilestone != savedMilestone) {
- Services.prefs.setCharPref("browser.startup.homepage_override.mstone", ourMilestone);
- this._startupStatus = savedMilestone ? "upgrade" : "new";
- }
-
- return this._startupStatus;
- },
-
- /**
- * Pass this a locale string, such as "fr" or "es_ES".
- */
- setLocale: function (locale) {
- console.log("browser.js: requesting locale set: " + locale);
- Messaging.sendRequest({ type: "Locale:Set", locale: locale });
- },
-
- initContextMenu: function () {
- // We pass a thunk in place of a raw label string. This allows the
- // context menu to automatically accommodate locale changes without
- // having to be rebuilt.
- let stringGetter = name => () => Strings.browser.GetStringFromName(name);
-
- // TODO: These should eventually move into more appropriate classes
- NativeWindow.contextmenus.add(stringGetter("contextmenu.openInNewTab"),
- NativeWindow.contextmenus.linkOpenableNonPrivateContext,
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_open_new_tab");
- UITelemetry.addEvent("loadurl.1", "contextmenu", null);
-
- let url = NativeWindow.contextmenus._getLinkURL(aTarget);
- ContentAreaUtils.urlSecurityCheck(url, aTarget.ownerDocument.nodePrincipal);
- let tab = BrowserApp.addTab(url, { selected: false, parentId: BrowserApp.selectedTab.id });
-
- let newtabStrings = Strings.browser.GetStringFromName("newtabpopup.opened");
- let label = PluralForm.get(1, newtabStrings).replace("#1", 1);
- let buttonLabel = Strings.browser.GetStringFromName("newtabpopup.switch");
-
- Snackbars.show(label, Snackbars.LENGTH_LONG, {
- action: {
- label: buttonLabel,
- callback: () => { BrowserApp.selectTab(tab); },
- }
- });
- });
-
- let showOpenInPrivateTab = true;
- if ("@mozilla.org/parental-controls-service;1" in Cc) {
- let pc = Cc["@mozilla.org/parental-controls-service;1"].createInstance(Ci.nsIParentalControlsService);
- showOpenInPrivateTab = pc.isAllowed(Ci.nsIParentalControlsService.PRIVATE_BROWSING);
- }
-
- if (showOpenInPrivateTab) {
- NativeWindow.contextmenus.add(stringGetter("contextmenu.openInPrivateTab"),
- NativeWindow.contextmenus.linkOpenableContext,
- function (aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_open_new_tab");
- UITelemetry.addEvent("loadurl.1", "contextmenu", null);
-
- let url = NativeWindow.contextmenus._getLinkURL(aTarget);
- ContentAreaUtils.urlSecurityCheck(url, aTarget.ownerDocument.nodePrincipal);
- let tab = BrowserApp.addTab(url, {selected: false, parentId: BrowserApp.selectedTab.id, isPrivate: true});
-
- let newtabStrings = Strings.browser.GetStringFromName("newprivatetabpopup.opened");
- let label = PluralForm.get(1, newtabStrings).replace("#1", 1);
- let buttonLabel = Strings.browser.GetStringFromName("newtabpopup.switch");
- Snackbars.show(label, Snackbars.LENGTH_LONG, {
- action: {
- label: buttonLabel,
- callback: () => { BrowserApp.selectTab(tab); },
- }
- });
- });
- }
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.copyLink"),
- NativeWindow.contextmenus.linkCopyableContext,
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_copy_link");
-
- let url = NativeWindow.contextmenus._getLinkURL(aTarget);
- NativeWindow.contextmenus._copyStringToDefaultClipboard(url);
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.copyEmailAddress"),
- NativeWindow.contextmenus.emailLinkContext,
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_copy_email");
-
- let url = NativeWindow.contextmenus._getLinkURL(aTarget);
- let emailAddr = NativeWindow.contextmenus._stripScheme(url);
- NativeWindow.contextmenus._copyStringToDefaultClipboard(emailAddr);
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.copyPhoneNumber"),
- NativeWindow.contextmenus.phoneNumberLinkContext,
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_copy_phone");
-
- let url = NativeWindow.contextmenus._getLinkURL(aTarget);
- let phoneNumber = NativeWindow.contextmenus._stripScheme(url);
- NativeWindow.contextmenus._copyStringToDefaultClipboard(phoneNumber);
- });
-
- NativeWindow.contextmenus.add({
- label: stringGetter("contextmenu.shareLink"),
- order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1, // Show above HTML5 menu items
- selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.linkShareableContext),
- showAsActions: function(aElement) {
- return {
- title: aElement.textContent.trim() || aElement.title.trim(),
- uri: NativeWindow.contextmenus._getLinkURL(aElement),
- };
- },
- icon: "drawable://ic_menu_share",
- callback: function(aTarget) {
- // share.1 telemetry is handled in Java via PromptList
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_link");
- }
- });
-
- NativeWindow.contextmenus.add({
- label: stringGetter("contextmenu.shareEmailAddress"),
- order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1,
- selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.emailLinkContext),
- showAsActions: function(aElement) {
- let url = NativeWindow.contextmenus._getLinkURL(aElement);
- let emailAddr = NativeWindow.contextmenus._stripScheme(url);
- let title = aElement.textContent || aElement.title;
- return {
- title: title,
- uri: emailAddr,
- };
- },
- icon: "drawable://ic_menu_share",
- callback: function(aTarget) {
- // share.1 telemetry is handled in Java via PromptList
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_email");
- }
- });
-
- NativeWindow.contextmenus.add({
- label: stringGetter("contextmenu.sharePhoneNumber"),
- order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1,
- selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.phoneNumberLinkContext),
- showAsActions: function(aElement) {
- let url = NativeWindow.contextmenus._getLinkURL(aElement);
- let phoneNumber = NativeWindow.contextmenus._stripScheme(url);
- let title = aElement.textContent || aElement.title;
- return {
- title: title,
- uri: phoneNumber,
- };
- },
- icon: "drawable://ic_menu_share",
- callback: function(aTarget) {
- // share.1 telemetry is handled in Java via PromptList
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_phone");
- }
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.addToContacts"),
- NativeWindow.contextmenus._disableRestricted("ADD_CONTACT", NativeWindow.contextmenus.emailLinkContext),
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_email");
-
- let url = NativeWindow.contextmenus._getLinkURL(aTarget);
- Messaging.sendRequest({
- type: "Contact:Add",
- email: url
- });
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.addToContacts"),
- NativeWindow.contextmenus._disableRestricted("ADD_CONTACT", NativeWindow.contextmenus.phoneNumberLinkContext),
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_phone");
-
- let url = NativeWindow.contextmenus._getLinkURL(aTarget);
- Messaging.sendRequest({
- type: "Contact:Add",
- phone: url
- });
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.bookmarkLink"),
- NativeWindow.contextmenus._disableRestricted("BOOKMARK", NativeWindow.contextmenus.linkBookmarkableContext),
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_bookmark");
- UITelemetry.addEvent("save.1", "contextmenu", null, "bookmark");
-
- let url = NativeWindow.contextmenus._getLinkURL(aTarget);
- let title = aTarget.textContent || aTarget.title || url;
- Messaging.sendRequest({
- type: "Bookmark:Insert",
- url: url,
- title: title
- });
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.playMedia"),
- NativeWindow.contextmenus.mediaContext("media-paused"),
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_play");
- aTarget.play();
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.pauseMedia"),
- NativeWindow.contextmenus.mediaContext("media-playing"),
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_pause");
- aTarget.pause();
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.showControls2"),
- NativeWindow.contextmenus.mediaContext("media-hidingcontrols"),
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_controls_media");
- aTarget.setAttribute("controls", true);
- });
-
- NativeWindow.contextmenus.add({
- label: stringGetter("contextmenu.shareMedia"),
- order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1,
- selector: NativeWindow.contextmenus._disableRestricted(
- "SHARE", NativeWindow.contextmenus.videoContext()),
- showAsActions: function(aElement) {
- let url = (aElement.currentSrc || aElement.src);
- let title = aElement.textContent || aElement.title;
- return {
- title: title,
- uri: url,
- type: "video/*",
- };
- },
- icon: "drawable://ic_menu_share",
- callback: function(aTarget) {
- // share.1 telemetry is handled in Java via PromptList
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_media");
- }
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.fullScreen"),
- NativeWindow.contextmenus.videoContext("not-fullscreen"),
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_fullscreen");
- aTarget.requestFullscreen();
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.mute"),
- NativeWindow.contextmenus.mediaContext("media-unmuted"),
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_mute");
- aTarget.muted = true;
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.unmute"),
- NativeWindow.contextmenus.mediaContext("media-muted"),
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_unmute");
- aTarget.muted = false;
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.viewImage"),
- NativeWindow.contextmenus.imageLocationCopyableContext,
- function(aTarget) {
- let url = aTarget.src;
- ContentAreaUtils.urlSecurityCheck(url, aTarget.ownerDocument.nodePrincipal,
- Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_view_image");
- UITelemetry.addEvent("loadurl.1", "contextmenu", null);
- BrowserApp.selectedBrowser.loadURI(url);
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.copyImageLocation"),
- NativeWindow.contextmenus.imageLocationCopyableContext,
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_copy_image");
-
- let url = aTarget.src;
- NativeWindow.contextmenus._copyStringToDefaultClipboard(url);
- });
-
- NativeWindow.contextmenus.add({
- label: stringGetter("contextmenu.shareImage"),
- selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.imageShareableContext),
- order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1, // Show above HTML5 menu items
- showAsActions: function(aTarget) {
- let doc = aTarget.ownerDocument;
- let imageCache = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
- .getImgCacheForDocument(doc);
- let props = imageCache.findEntryProperties(aTarget.currentURI, doc);
- let src = aTarget.src;
- return {
- title: src,
- uri: src,
- type: "image/*",
- };
- },
- icon: "drawable://ic_menu_share",
- menu: true,
- callback: function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_image");
- }
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.saveImage"),
- NativeWindow.contextmenus.imageSaveableContext,
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_save_image");
- UITelemetry.addEvent("save.1", "contextmenu", null, "image");
-
- RuntimePermissions.waitForPermissions(RuntimePermissions.WRITE_EXTERNAL_STORAGE).then(function(permissionGranted) {
- if (!permissionGranted) {
- return;
- }
-
- ContentAreaUtils.saveImageURL(aTarget.currentURI.spec, null, "SaveImageTitle",
- false, true, aTarget.ownerDocument.documentURIObject,
- aTarget.ownerDocument);
- });
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.setImageAs"),
- NativeWindow.contextmenus._disableRestricted("SET_IMAGE", NativeWindow.contextmenus.imageSaveableContext),
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_background_image");
-
- let src = aTarget.src;
- Messaging.sendRequest({
- type: "Image:SetAs",
- url: src
- });
- });
-
- NativeWindow.contextmenus.add(
- function(aTarget) {
- if (aTarget instanceof HTMLVideoElement) {
- // If a video element is zero width or height, its essentially
- // an HTMLAudioElement.
- if (aTarget.videoWidth == 0 || aTarget.videoHeight == 0 )
- return Strings.browser.GetStringFromName("contextmenu.saveAudio");
- return Strings.browser.GetStringFromName("contextmenu.saveVideo");
- } else if (aTarget instanceof HTMLAudioElement) {
- return Strings.browser.GetStringFromName("contextmenu.saveAudio");
- }
- return Strings.browser.GetStringFromName("contextmenu.saveVideo");
- }, NativeWindow.contextmenus.mediaSaveableContext,
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_save_media");
- UITelemetry.addEvent("save.1", "contextmenu", null, "media");
-
- let url = aTarget.currentSrc || aTarget.src;
- let filePickerTitleKey = (aTarget instanceof HTMLVideoElement &&
- (aTarget.videoWidth != 0 && aTarget.videoHeight != 0))
- ? "SaveVideoTitle" : "SaveAudioTitle";
- // Skipped trying to pull MIME type out of cache for now
- ContentAreaUtils.internalSave(url, null, null, null, null, false,
- filePickerTitleKey, null, aTarget.ownerDocument.documentURIObject,
- aTarget.ownerDocument, true, null);
- });
-
- NativeWindow.contextmenus.add(stringGetter("contextmenu.showImage"),
- NativeWindow.contextmenus.imageBlockingPolicyContext,
- function(aTarget) {
- UITelemetry.addEvent("action.1", "contextmenu", null, "web_show_image");
- aTarget.setAttribute("data-ctv-show", "true");
- aTarget.setAttribute("src", aTarget.getAttribute("data-ctv-src"));
-
- // Shows a snackbar to unblock all images if browser.image_blocking.enabled is enabled.
- let blockedImgs = aTarget.ownerDocument.querySelectorAll("[data-ctv-src]");
- if (blockedImgs.length == 0) {
- return;
- }
- let message = Strings.browser.GetStringFromName("imageblocking.downloadedImage");
- Snackbars.show(message, Snackbars.LENGTH_LONG, {
- action: {
- label: Strings.browser.GetStringFromName("imageblocking.showAllImages"),
- callback: () => {
- UITelemetry.addEvent("action.1", "toast", null, "web_show_all_image");
- for (let i = 0; i < blockedImgs.length; ++i) {
- blockedImgs[i].setAttribute("data-ctv-show", "true");
- blockedImgs[i].setAttribute("src", blockedImgs[i].getAttribute("data-ctv-src"));
- }
- },
- }
- });
- });
- },
-
- onAppUpdated: function() {
- // initialize the form history and passwords databases on upgrades
- Services.obs.notifyObservers(null, "FormHistory:Init", "");
- Services.obs.notifyObservers(null, "Passwords:Init", "");
-
- if (this._startupStatus === "upgrade") {
- this._migrateUI();
- }
- },
-
- _migrateUI: function() {
- const UI_VERSION = 3;
- let currentUIVersion = 0;
- try {
- currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
- } catch(ex) {}
- if (currentUIVersion >= UI_VERSION) {
- return;
- }
-
- if (currentUIVersion < 1) {
- // Migrate user-set "plugins.click_to_play" pref. See bug 884694.
- // Because the default value is true, a user-set pref means that the pref was set to false.
- if (Services.prefs.prefHasUserValue("plugins.click_to_play")) {
- Services.prefs.setIntPref("plugin.default.state", Ci.nsIPluginTag.STATE_ENABLED);
- Services.prefs.clearUserPref("plugins.click_to_play");
- }
-
- // Migrate the "privacy.donottrackheader.value" pref. See bug 1042135.
- if (Services.prefs.prefHasUserValue("privacy.donottrackheader.value")) {
- // Make sure the doNotTrack value conforms to the conversion from
- // three-state to two-state. (This reverts a setting of "please track me"
- // to the default "don't say anything").
- if (Services.prefs.getBoolPref("privacy.donottrackheader.enabled") &&
- (Services.prefs.getIntPref("privacy.donottrackheader.value") != 1)) {
- Services.prefs.clearUserPref("privacy.donottrackheader.enabled");
- }
-
- // This pref has been removed, so always clear it.
- Services.prefs.clearUserPref("privacy.donottrackheader.value");
- }
-
- // Set the search activity default pref on app upgrade if it has not been set already.
- if (!Services.prefs.prefHasUserValue("searchActivity.default.migrated")) {
- Services.prefs.setBoolPref("searchActivity.default.migrated", true);
- SearchEngines.migrateSearchActivityDefaultPref();
- }
-
- Reader.migrateCache().catch(e => Cu.reportError("Error migrating Reader cache: " + e));
-
- // We removed this pref from user visible settings, so we should reset it.
- // Power users can go into about:config to re-enable this if they choose.
- if (Services.prefs.prefHasUserValue("nglayout.debug.paint_flashing")) {
- Services.prefs.clearUserPref("nglayout.debug.paint_flashing");
- }
- }
-
- if (currentUIVersion < 2) {
- let name;
- if (Services.prefs.prefHasUserValue("browser.search.defaultenginename")) {
- name = Services.prefs.getCharPref("browser.search.defaultenginename");
- }
- if (!name && Services.prefs.prefHasUserValue("browser.search.defaultenginename.US")) {
- name = Services.prefs.getCharPref("browser.search.defaultenginename.US");
- }
- if (name) {
- Services.search.init(() => {
- let engine = Services.search.getEngineByName(name);
- if (engine) {
- Services.search.defaultEngine = engine;
- Services.obs.notifyObservers(null, "default-search-engine-migrated", "");
- }
- });
- }
- }
-
- if (currentUIVersion < 3) {
- const kOldSafeBrowsingPref = "browser.safebrowsing.enabled";
- // Default value is set to true, a user pref means that the pref was
- // set to false.
- if (Services.prefs.prefHasUserValue(kOldSafeBrowsingPref) &&
- !Services.prefs.getBoolPref(kOldSafeBrowsingPref)) {
- Services.prefs.setBoolPref("browser.safebrowsing.phishing.enabled",
- false);
- // Should just remove support for the pref entirely, even if it's
- // only in about:config
- Services.prefs.clearUserPref(kOldSafeBrowsingPref);
- }
- }
-
- // Update the migration version.
- Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
- },
-
- // This function returns false during periods where the browser displayed document is
- // different from the browser content document, so user actions and some kinds of viewport
- // updates should be ignored. This period starts when we start loading a new page or
- // switch tabs, and ends when the new browser content document has been drawn and handed
- // off to the compositor.
- isBrowserContentDocumentDisplayed: function() {
- try {
- if (!Services.androidBridge.isContentDocumentDisplayed(window))
- return false;
- } catch (e) {
- return false;
- }
-
- let tab = this.selectedTab;
- if (!tab)
- return false;
- return tab.contentDocumentIsDisplayed;
- },
-
- contentDocumentChanged: function() {
- window.top.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).isFirstPaint = true;
- Services.androidBridge.contentDocumentChanged(window);
- },
-
- get tabs() {
- return this._tabs;
- },
-
- set selectedTab(aTab) {
- if (this._selectedTab == aTab)
- return;
-
- if (this._selectedTab) {
- this._selectedTab.setActive(false);
- }
-
- this._selectedTab = aTab;
- if (!aTab)
- return;
-
- aTab.setActive(true);
- this.contentDocumentChanged();
- this.deck.selectedPanel = aTab.browser;
- // Focus the browser so that things like selection will be styled correctly.
- aTab.browser.focus();
- },
-
- get selectedBrowser() {
- if (this._selectedTab)
- return this._selectedTab.browser;
- return null;
- },
-
- getTabForId: function getTabForId(aId) {
- let tabs = this._tabs;
- for (let i=0; i < tabs.length; i++) {
- if (tabs[i].id == aId)
- return tabs[i];
- }
- return null;
- },
-
- getTabForBrowser: function getTabForBrowser(aBrowser) {
- let tabs = this._tabs;
- for (let i = 0; i < tabs.length; i++) {
- if (tabs[i].browser == aBrowser)
- return tabs[i];
- }
- return null;
- },
-
- getTabForWindow: function getTabForWindow(aWindow) {
- let tabs = this._tabs;
- for (let i = 0; i < tabs.length; i++) {
- if (tabs[i].browser.contentWindow == aWindow)
- return tabs[i];
- }
- return null;
- },
-
- getBrowserForWindow: function getBrowserForWindow(aWindow) {
- let tabs = this._tabs;
- for (let i = 0; i < tabs.length; i++) {
- if (tabs[i].browser.contentWindow == aWindow)
- return tabs[i].browser;
- }
- return null;
- },
-
- getBrowserForDocument: function getBrowserForDocument(aDocument) {
- let tabs = this._tabs;
- for (let i = 0; i < tabs.length; i++) {
- if (tabs[i].browser.contentDocument == aDocument)
- return tabs[i].browser;
- }
- return null;
- },
-
- loadURI: function loadURI(aURI, aBrowser, aParams) {
- aBrowser = aBrowser || this.selectedBrowser;
- if (!aBrowser)
- return;
-
- aParams = aParams || {};
-
- let flags = "flags" in aParams ? aParams.flags : Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
- let postData = ("postData" in aParams && aParams.postData) ? aParams.postData : null;
- let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null;
- let charset = "charset" in aParams ? aParams.charset : null;
-
- let tab = this.getTabForBrowser(aBrowser);
- if (tab) {
- if ("userRequested" in aParams) tab.userRequested = aParams.userRequested;
- tab.isSearch = ("isSearch" in aParams) ? aParams.isSearch : false;
- }
-
- try {
- aBrowser.loadURIWithFlags(aURI, flags, referrerURI, charset, postData);
- } catch(e) {
- if (tab) {
- let message = {
- type: "Content:LoadError",
- tabID: tab.id
- };
- Messaging.sendRequest(message);
- dump("Handled load error: " + e)
- }
- }
- },
-
- addTab: function addTab(aURI, aParams) {
- aParams = aParams || {};
-
- let newTab = new Tab(aURI, aParams);
-
- if (typeof aParams.tabIndex == "number") {
- this._tabs.splice(aParams.tabIndex, 0, newTab);
- } else {
- this._tabs.push(newTab);
- }
-
- let selected = "selected" in aParams ? aParams.selected : true;
- if (selected)
- this.selectedTab = newTab;
-
- let pinned = "pinned" in aParams ? aParams.pinned : false;
- if (pinned) {
- let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
- ss.setTabValue(newTab, "appOrigin", aURI);
- }
-
- let evt = document.createEvent("UIEvents");
- evt.initUIEvent("TabOpen", true, false, window, null);
- newTab.browser.dispatchEvent(evt);
-
- return newTab;
- },
-
- // Use this method to close a tab from JS. This method sends a message
- // to Java to close the tab in the Java UI (we'll get a Tab:Closed message
- // back from Java when that happens).
- closeTab: function closeTab(aTab) {
- if (!aTab) {
- Cu.reportError("Error trying to close tab (tab doesn't exist)");
- return;
- }
-
- let message = {
- type: "Tab:Close",
- tabID: aTab.id
- };
- Messaging.sendRequest(message);
- },
-
- // Calling this will update the state in BrowserApp after a tab has been
- // closed in the Java UI.
- _handleTabClosed: function _handleTabClosed(aTab, aShowUndoSnackbar) {
- if (aTab == this.selectedTab)
- this.selectedTab = null;
-
- let tabIndex = this._tabs.indexOf(aTab);
-
- let evt = document.createEvent("UIEvents");
- evt.initUIEvent("TabClose", true, false, window, tabIndex);
- aTab.browser.dispatchEvent(evt);
-
- if (aShowUndoSnackbar) {
- // Get a title for the undo close snackbar. Fall back to the URL if there is no title.
- let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
- let closedTabData = ss.getClosedTabs(window)[0];
-
- let message;
- let title = closedTabData.entries[closedTabData.index - 1].title;
- let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(aTab.browser);
-
- if (isPrivate) {
- message = Strings.browser.GetStringFromName("privateClosedMessage.message");
- } else if (title) {
- message = Strings.browser.formatStringFromName("undoCloseToast.message", [title], 1);
- } else {
- message = Strings.browser.GetStringFromName("undoCloseToast.messageDefault");
- }
-
- Snackbars.show(message, Snackbars.LENGTH_LONG, {
- action: {
- label: Strings.browser.GetStringFromName("undoCloseToast.action2"),
- callback: function() {
- UITelemetry.addEvent("undo.1", "toast", null, "closetab");
- ss.undoCloseTab(window, closedTabData);
- }
- }
- });
- }
-
- aTab.destroy();
- this._tabs.splice(tabIndex, 1);
- },
-
- // Use this method to select a tab from JS. This method sends a message
- // to Java to select the tab in the Java UI (we'll get a Tab:Selected message
- // back from Java when that happens).
- selectTab: function selectTab(aTab) {
- if (!aTab) {
- Cu.reportError("Error trying to select tab (tab doesn't exist)");
- return;
- }
-
- // There's nothing to do if the tab is already selected
- if (aTab == this.selectedTab)
- return;
-
- let doc = this.selectedBrowser.contentDocument;
- if (doc.fullscreenElement) {
- // We'll finish the tab selection once the fullscreen transition has ended,
- // remember the new tab for this.
- this.fullscreenTransitionTab = aTab;
- doc.exitFullscreen();
- }
-
- let message = {
- type: "Tab:Select",
- tabID: aTab.id
- };
- Messaging.sendRequest(message);
- },
-
- /**
- * Gets an open tab with the given URL.
- *
- * @param aURL URL to look for
- * @param aOptions Options for the search. Currently supports:
- ** @option startsWith a Boolean indicating whether to search for a tab who's url starts with the
- * requested url. Useful if you want to ignore hash codes on the end of a url. For instance
- * to have about:downloads match about:downloads#123.
- * @return the tab with the given URL, or null if no such tab exists
- */
- getTabWithURL: function getTabWithURL(aURL, aOptions) {
- aOptions = aOptions || {};
- let uri = Services.io.newURI(aURL, null, null);
- for (let i = 0; i < this._tabs.length; ++i) {
- let tab = this._tabs[i];
- if (aOptions.startsWith) {
- if (tab.browser.currentURI.spec.startsWith(aURL)) {
- return tab;
- }
- } else {
- if (tab.browser.currentURI.equals(uri)) {
- return tab;
- }
- }
- }
- return null;
- },
-
- /**
- * If a tab with the given URL already exists, that tab is selected.
- * Otherwise, a new tab is opened with the given URL.
- *
- * @param aURL URL to open
- * @param aParam Options used if a tab is created
- * @param aFlags Options for the search. Currently supports:
- ** @option startsWith a Boolean indicating whether to search for a tab who's url starts with the
- * requested url. Useful if you want to ignore hash codes on the end of a url. For instance
- * to have about:downloads match about:downloads#123.
- */
- selectOrAddTab: function selectOrAddTab(aURL, aParams, aFlags) {
- let tab = this.getTabWithURL(aURL, aFlags);
- if (tab == null) {
- tab = this.addTab(aURL, aParams);
- } else {
- this.selectTab(tab);
- }
-
- return tab;
- },
-
- // This method updates the state in BrowserApp after a tab has been selected
- // in the Java UI.
- _handleTabSelected: function _handleTabSelected(aTab) {
- if (this.fullscreenTransitionTab) {
- // Defer updating to "fullscreenchange" if tab selection happened during
- // a fullscreen transition.
- return;
- }
- this.selectedTab = aTab;
-
- let evt = document.createEvent("UIEvents");
- evt.initUIEvent("TabSelect", true, false, window, null);
- aTab.browser.dispatchEvent(evt);
- },
-
- quit: function quit(aClear = { sanitize: {}, dontSaveSession: false }) {
- // Notify all windows that an application quit has been requested.
- let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
- Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
-
- // Quit aborted.
- if (cancelQuit.data) {
- return;
- }
-
- Services.obs.notifyObservers(null, "quit-application-proceeding", null);
-
- // Tell session store to forget about this window
- if (aClear.dontSaveSession) {
- let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
- ss.removeWindow(window);
- }
-
- BrowserApp.sanitize(aClear.sanitize, function() {
- let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
- appStartup.quit(Ci.nsIAppStartup.eForceQuit);
- }, true);
- },
-
- saveAsPDF: function saveAsPDF(aBrowser) {
- RuntimePermissions.waitForPermissions(RuntimePermissions.WRITE_EXTERNAL_STORAGE).then(function(permissionGranted) {
- if (!permissionGranted) {
- return;
- }
-
- Task.spawn(function* () {
- let fileName = ContentAreaUtils.getDefaultFileName(aBrowser.contentTitle, aBrowser.currentURI, null, null);
- fileName = fileName.trim() + ".pdf";
-
- let downloadsDir = yield Downloads.getPreferredDownloadsDirectory();
- let file = OS.Path.join(downloadsDir, fileName);
-
- // Force this to have a unique name.
- let openedFile = yield OS.File.openUnique(file, { humanReadable: true });
- file = openedFile.path;
- yield openedFile.file.close();
-
- let download = yield Downloads.createDownload({
- source: aBrowser.contentWindow,
- target: file,
- saver: "pdf",
- startTime: Date.now(),
- });
-
- let list = yield Downloads.getList(download.source.isPrivate ? Downloads.PRIVATE : Downloads.PUBLIC)
- yield list.add(download);
- yield download.start();
- });
- });
- },
-
- // These values come from pref_tracking_protection_entries in arrays.xml.
- PREF_TRACKING_PROTECTION_ENABLED: "2",
- PREF_TRACKING_PROTECTION_ENABLED_PB: "1",
- PREF_TRACKING_PROTECTION_DISABLED: "0",
-
- /**
- * Returns the current state of the tracking protection pref.
- * (0 = Disabled, 1 = Enabled in PB, 2 = Enabled)
- */
- getTrackingProtectionState: function() {
- if (Services.prefs.getBoolPref("privacy.trackingprotection.enabled")) {
- return this.PREF_TRACKING_PROTECTION_ENABLED;
- }
- if (Services.prefs.getBoolPref("privacy.trackingprotection.pbmode.enabled")) {
- return this.PREF_TRACKING_PROTECTION_ENABLED_PB;
- }
- return this.PREF_TRACKING_PROTECTION_DISABLED;
- },
-
- sanitize: function (aItems, callback, aShutdown) {
- let success = true;
- var promises = [];
-
- for (let key in aItems) {
- if (!aItems[key])
- continue;
-
- key = key.replace("private.data.", "");
-
- switch (key) {
- case "cookies_sessions":
- promises.push(Sanitizer.clearItem("cookies"));
- promises.push(Sanitizer.clearItem("sessions"));
- break;
- default:
- promises.push(Sanitizer.clearItem(key));
- }
- }
-
- Promise.all(promises).then(function() {
- Messaging.sendRequest({
- type: "Sanitize:Finished",
- success: true,
- shutdown: aShutdown === true
- });
-
- if (callback) {
- callback();
- }
- }).catch(function(err) {
- Messaging.sendRequest({
- type: "Sanitize:Finished",
- error: err,
- success: false,
- shutdown: aShutdown === true
- });
-
- if (callback) {
- callback();
- }
- })
- },
-
- getFocusedInput: function(aBrowser, aOnlyInputElements = false) {
- if (!aBrowser)
- return null;
-
- let doc = aBrowser.contentDocument;
- if (!doc)
- return null;
-
- let focused = doc.activeElement;
- while (focused instanceof HTMLFrameElement || focused instanceof HTMLIFrameElement) {
- doc = focused.contentDocument;
- focused = doc.activeElement;
- }
-
- if (focused instanceof HTMLInputElement &&
- (focused.mozIsTextField(false) || focused.type === "number")) {
- return focused;
- }
-
- if (aOnlyInputElements)
- return null;
-
- if (focused && (focused instanceof HTMLTextAreaElement || focused.isContentEditable)) {
-
- if (focused instanceof HTMLBodyElement) {
- // we are putting focus into a contentEditable frame. scroll the frame into
- // view instead of the contentEditable document contained within, because that
- // results in a better user experience
- focused = focused.ownerDocument.defaultView.frameElement;
- }
- return focused;
- }
- return null;
- },
-
- scrollToFocusedInput: function(aBrowser, aAllowZoom = true) {
- let formHelperMode = Services.prefs.getIntPref("formhelper.mode");
- if (formHelperMode == kFormHelperModeDisabled)
- return;
-
- let dwu = aBrowser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
- if (!dwu) {
- return;
- }
-
- let apzFlushDone = function() {
- Services.obs.removeObserver(apzFlushDone, "apz-repaints-flushed", false);
- dwu.zoomToFocusedInput();
- };
-
- let paintDone = function() {
- window.removeEventListener("MozAfterPaint", paintDone, false);
- if (dwu.flushApzRepaints()) {
- Services.obs.addObserver(apzFlushDone, "apz-repaints-flushed", false);
- } else {
- apzFlushDone();
- }
- };
-
- let gotResizeWindow = false;
- let resizeWindow = function(e) {
- gotResizeWindow = true;
- aBrowser.contentWindow.removeEventListener("resize", resizeWindow, false);
- if (dwu.isMozAfterPaintPending) {
- window.addEventListener("MozAfterPaint", paintDone, false);
- } else {
- paintDone();
- }
- }
-
- aBrowser.contentWindow.addEventListener("resize", resizeWindow, false);
-
- // The "resize" event sometimes fails to fire, so set a timer to catch that case
- // and unregister the event listener. See Bug 1253469
- setTimeout(function(e) {
- if (!gotResizeWindow) {
- aBrowser.contentWindow.removeEventListener("resize", resizeWindow, false);
- dwu.zoomToFocusedInput();
- }
- }, 500);
- },
-
- getUALocalePref: function () {
- try {
- return Services.prefs.getComplexValue("general.useragent.locale", Ci.nsIPrefLocalizedString).data;
- } catch (e) {
- try {
- return Services.prefs.getCharPref("general.useragent.locale");
- } catch (ee) {
- return undefined;
- }
- }
- },
-
- getOSLocalePref: function () {
- try {
- return Services.prefs.getCharPref("intl.locale.os");
- } catch (e) {
- return undefined;
- }
- },
-
- setLocalizedPref: function (pref, value) {
- let pls = Cc["@mozilla.org/pref-localizedstring;1"]
- .createInstance(Ci.nsIPrefLocalizedString);
- pls.data = value;
- Services.prefs.setComplexValue(pref, Ci.nsIPrefLocalizedString, pls);
- },
-
- observe: function(aSubject, aTopic, aData) {
- let browser = this.selectedBrowser;
-
- switch (aTopic) {
-
- case "Session:Back":
- browser.goBack();
- break;
-
- case "Session:Forward":
- browser.goForward();
- break;
-
- case "Session:Navigate":
- let index = JSON.parse(aData);
- let webNav = BrowserApp.selectedTab.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
- let historySize = webNav.sessionHistory.count;
-
- if (index < 0) {
- index = 0;
- Log.e("Browser", "Negative index truncated to zero");
- } else if (index >= historySize) {
- Log.e("Browser", "Incorrect index " + index + " truncated to " + historySize - 1);
- index = historySize - 1;
- }
-
- browser.gotoIndex(index);
- break;
-
- case "Session:Reload": {
- let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
-
- // Check to see if this is a message to enable/disable mixed content blocking.
- if (aData) {
- let data = JSON.parse(aData);
-
- if (data.bypassCache) {
- flags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE |
- Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY;
- }
-
- if (data.contentType === "tracking") {
- // Convert document URI into the format used by
- // nsChannelClassifier::ShouldEnableTrackingProtection
- // (any scheme turned into https is correct)
- let normalizedUrl = Services.io.newURI("https://" + browser.currentURI.hostPort, null, null);
- if (data.allowContent) {
- // 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 white list.
- if (PrivateBrowsingUtils.isBrowserPrivate(browser)) {
- PrivateBrowsingUtils.addToTrackingAllowlist(normalizedUrl);
- } else {
- Services.perms.add(normalizedUrl, "trackingprotection", Services.perms.ALLOW_ACTION);
- Telemetry.addData("TRACKING_PROTECTION_EVENTS", 1);
- }
- } else {
- // Remove the current host from the 'trackingprotection' consumer
- // of the permission manager. This effectively removes this host
- // from the tracking protection white list (any list actually).
- if (PrivateBrowsingUtils.isBrowserPrivate(browser)) {
- PrivateBrowsingUtils.removeFromTrackingAllowlist(normalizedUrl);
- } else {
- Services.perms.remove(normalizedUrl, "trackingprotection");
- Telemetry.addData("TRACKING_PROTECTION_EVENTS", 2);
- }
- }
- }
- }
-
- // Try to use the session history to reload so that framesets are
- // handled properly. If the window has no session history, fall back
- // to using the web navigation's reload method.
- let webNav = browser.webNavigation;
- try {
- let sh = webNav.sessionHistory;
- if (sh)
- webNav = sh.QueryInterface(Ci.nsIWebNavigation);
- } catch (e) {}
- webNav.reload(flags);
- break;
- }
-
- case "Session:Stop":
- browser.stop();
- break;
-
- case "Tab:Load": {
- let data = JSON.parse(aData);
- let url = data.url;
- let flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP
- | Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
-
- // Pass LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL to prevent any loads from
- // inheriting the currently loaded document's principal.
- if (data.userEntered) {
- flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
- }
-
- let delayLoad = ("delayLoad" in data) ? data.delayLoad : false;
- let params = {
- selected: ("selected" in data) ? data.selected : !delayLoad,
- parentId: ("parentId" in data) ? data.parentId : -1,
- flags: flags,
- tabID: data.tabID,
- isPrivate: (data.isPrivate === true),
- pinned: (data.pinned === true),
- delayLoad: (delayLoad === true),
- desktopMode: (data.desktopMode === true)
- };
-
- params.userRequested = url;
-
- if (data.engine) {
- let engine = Services.search.getEngineByName(data.engine);
- if (engine) {
- let submission = engine.getSubmission(url);
- url = submission.uri.spec;
- params.postData = submission.postData;
- params.isSearch = true;
- }
- }
-
- if (data.newTab) {
- this.addTab(url, params);
- } else {
- if (data.tabId) {
- // Use a specific browser instead of the selected browser, if it exists
- let specificBrowser = this.getTabForId(data.tabId).browser;
- if (specificBrowser)
- browser = specificBrowser;
- }
- this.loadURI(url, browser, params);
- }
- break;
- }
-
- case "Tab:Selected":
- this._handleTabSelected(this.getTabForId(parseInt(aData)));
- break;
-
- case "Tab:Closed": {
- let data = JSON.parse(aData);
- this._handleTabClosed(this.getTabForId(data.tabId), data.showUndoToast);
- break;
- }
-
- case "keyword-search":
- // This event refers to a search via the URL bar, not a bookmarks
- // keyword search. Note that this code assumes that the user can only
- // perform a keyword search on the selected tab.
- this.selectedTab.isSearch = true;
-
- // Don't store queries in private browsing mode.
- let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(this.selectedTab.browser);
- let query = isPrivate ? "" : aData;
-
- let engine = aSubject.QueryInterface(Ci.nsISearchEngine);
- Messaging.sendRequest({
- type: "Search:Keyword",
- identifier: engine.identifier,
- name: engine.name,
- query: query
- });
- break;
-
- case "Browser:Quit":
- // Add-ons like QuitNow and CleanQuit provide aData as an empty-string ("").
- // Pass undefined to invoke the methods default parms.
- this.quit(aData ? JSON.parse(aData) : undefined);
- break;
-
- case "SaveAs:PDF":
- this.saveAsPDF(browser);
- break;
-
- case "ScrollTo:FocusedInput":
- // these messages come from a change in the viewable area and not user interaction
- // we allow scrolling to the selected input, but not zooming the page
- this.scrollToFocusedInput(browser, false);
- break;
-
- case "Sanitize:ClearData":
- this.sanitize(JSON.parse(aData));
- break;
-
- case "FullScreen:Exit":
- browser.contentDocument.exitFullscreen();
- break;
-
- case "Passwords:Init": {
- let storage = Cc["@mozilla.org/login-manager/storage/mozStorage;1"].
- getService(Ci.nsILoginManagerStorage);
- storage.initialize();
- Services.obs.removeObserver(this, "Passwords:Init");
- break;
- }
-
- case "FormHistory:Init": {
- // Force creation/upgrade of formhistory.sqlite
- FormHistory.count({});
- Services.obs.removeObserver(this, "FormHistory:Init");
- break;
- }
-
- case "android-get-pref": {
- // These pref names are not "real" pref names. They are used in the
- // setting menu, and these are passed when initializing the setting
- // menu. aSubject is a nsIWritableVariant to hold the pref value.
- aSubject.QueryInterface(Ci.nsIWritableVariant);
-
- switch (aData) {
- // The plugin pref is actually two separate prefs, so
- // we need to handle it differently
- case "plugin.enable":
- aSubject.setAsAString(PluginHelper.getPluginPreference());
- break;
-
- // Handle master password
- case "privacy.masterpassword.enabled":
- aSubject.setAsBool(MasterPassword.enabled);
- break;
-
- case "privacy.trackingprotection.state": {
- aSubject.setAsAString(this.getTrackingProtectionState());
- break;
- }
-
- // Crash reporter submit pref must be fetched from nsICrashReporter
- // service.
- case "datareporting.crashreporter.submitEnabled":
- let crashReporterBuilt = "nsICrashReporter" in Ci &&
- Services.appinfo instanceof Ci.nsICrashReporter;
- if (crashReporterBuilt) {
- aSubject.setAsBool(Services.appinfo.submitReports);
- }
- break;
- }
- break;
- }
-
- case "android-set-pref": {
- // Pseudo-prefs. aSubject is an nsIWritableVariant that holds the pref
- // value. Set to empty to signal the pref was handled.
- aSubject.QueryInterface(Ci.nsIWritableVariant);
- let value = aSubject.QueryInterface(Ci.nsIVariant);
-
- switch (aData) {
- // The plugin pref is actually two separate prefs, so we need to
- // handle it differently.
- case "plugin.enable":
- PluginHelper.setPluginPreference(value);
- aSubject.setAsEmpty();
- break;
-
- // MasterPassword pref is not real, we just need take action and leave
- case "privacy.masterpassword.enabled":
- if (MasterPassword.enabled) {
- MasterPassword.removePassword(value);
- } else {
- MasterPassword.setPassword(value);
- }
- aSubject.setAsEmpty();
- break;
-
- // "privacy.trackingprotection.state" is not a "real" pref name, but
- // it's used in the setting menu. By default
- // "privacy.trackingprotection.pbmode.enabled" is true, and
- // "privacy.trackingprotection.enabled" is false.
- case "privacy.trackingprotection.state": {
- switch (value) {
- // Tracking protection disabled.
- case this.PREF_TRACKING_PROTECTION_DISABLED:
- Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled", false);
- Services.prefs.setBoolPref("privacy.trackingprotection.enabled", false);
- break;
- // Tracking protection only in private browsing,
- case this.PREF_TRACKING_PROTECTION_ENABLED_PB:
- Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled", true);
- Services.prefs.setBoolPref("privacy.trackingprotection.enabled", false);
- break;
- // Tracking protection everywhere.
- case this.PREF_TRACKING_PROTECTION_ENABLED:
- Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled", true);
- Services.prefs.setBoolPref("privacy.trackingprotection.enabled", true);
- break;
- }
- aSubject.setAsEmpty();
- break;
- }
-
- // Crash reporter preference is in a service; set and return.
- case "datareporting.crashreporter.submitEnabled":
- let crashReporterBuilt = "nsICrashReporter" in Ci &&
- Services.appinfo instanceof Ci.nsICrashReporter;
- if (crashReporterBuilt) {
- Services.appinfo.submitReports = value;
- aSubject.setAsEmpty();
- }
- break;
- }
- break;
- }
-
- case "sessionstore-state-purge-complete":
- Messaging.sendRequest({ type: "Session:StatePurged" });
- break;
-
- case "gather-telemetry":
- Messaging.sendRequest({ type: "Telemetry:Gather" });
- break;
-
- case "Locale:OS":
- // We know the system locale. We use this for generating Accept-Language headers.
- console.log("Locale:OS: " + aData);
- let currentOSLocale = this.getOSLocalePref();
- if (currentOSLocale == aData) {
- break;
- }
-
- console.log("New OS locale.");
-
- // Ensure that this choice is immediately persisted, because
- // Gecko won't be told again if it forgets.
- Services.prefs.setCharPref("intl.locale.os", aData);
- Services.prefs.savePrefFile(null);
-
- let appLocale = this.getUALocalePref();
-
- this.computeAcceptLanguages(aData, appLocale);
- break;
-
- case "Locale:Changed":
- if (aData) {
- // The value provided to Locale:Changed should be a BCP47 language tag
- // understood by Gecko -- for example, "es-ES" or "de".
- console.log("Locale:Changed: " + aData);
-
- // We always write a localized pref, even though sometimes the value is a char pref.
- // (E.g., on desktop single-locale builds.)
- this.setLocalizedPref("general.useragent.locale", aData);
- } else {
- // Resetting.
- console.log("Switching to system locale.");
- Services.prefs.clearUserPref("general.useragent.locale");
- }
-
- Services.prefs.setBoolPref("intl.locale.matchOS", !aData);
-
- // Ensure that this choice is immediately persisted, because
- // Gecko won't be told again if it forgets.
- Services.prefs.savePrefFile(null);
-
- // Blow away the string cache so that future lookups get the
- // correct locale.
- Strings.flush();
-
- // Make sure we use the right Accept-Language header.
- let osLocale;
- try {
- // This should never not be set at this point, but better safe than sorry.
- osLocale = Services.prefs.getCharPref("intl.locale.os");
- } catch (e) {
- }
-
- this.computeAcceptLanguages(osLocale, aData);
- break;
-
- case "Fonts:Reload":
- FontEnumerator.updateFontList();
- break;
-
- case "Vibration:Request":
- if (aSubject instanceof Navigator) {
- let navigator = aSubject;
- let buttons = [
- {
- label: Strings.browser.GetStringFromName("vibrationRequest.denyButton"),
- callback: function() {
- navigator.setVibrationPermission(false);
- }
- },
- {
- label: Strings.browser.GetStringFromName("vibrationRequest.allowButton"),
- callback: function() {
- navigator.setVibrationPermission(true);
- },
- positive: true
- }
- ];
- let message = Strings.browser.GetStringFromName("vibrationRequest.message");
- let options = {};
- NativeWindow.doorhanger.show(message, "vibration-request", buttons,
- BrowserApp.selectedTab.id, options, "VIBRATION");
- }
- break;
-
- default:
- dump('BrowserApp.observe: unexpected topic "' + aTopic + '"\n');
- break;
-
- }
- },
-
- /**
- * Set intl.accept_languages accordingly.
- *
- * After Bug 881510 this will also accept a real Accept-Language choice as
- * input; all Accept-Language logic lives here.
- *
- * osLocale should never be null, but this method is safe regardless.
- * appLocale may explicitly be null.
- */
- computeAcceptLanguages(osLocale, appLocale) {
- let defaultBranch = Services.prefs.getDefaultBranch(null);
- let defaultAccept = defaultBranch.getComplexValue("intl.accept_languages", Ci.nsIPrefLocalizedString).data;
- console.log("Default intl.accept_languages = " + defaultAccept);
-
- // A guard for potential breakage. Bug 438031.
- // This should not be necessary, because we're reading from the default branch,
- // but better safe than sorry.
- if (defaultAccept && defaultAccept.startsWith("chrome://")) {
- defaultAccept = null;
- } else {
- // Ensure lowercase everywhere so we can compare.
- defaultAccept = defaultAccept.toLowerCase();
- }
-
- if (appLocale) {
- appLocale = appLocale.toLowerCase();
- }
-
- if (osLocale) {
- osLocale = osLocale.toLowerCase();
- }
-
- // Eliminate values if they're present in the default.
- let chosen;
- if (defaultAccept) {
- // intl.accept_languages is a comma-separated list, with no q-value params. Those
- // are added when the header is generated.
- chosen = defaultAccept.split(",")
- .map(String.trim)
- .filter((x) => (x != appLocale && x != osLocale));
- } else {
- chosen = [];
- }
-
- if (osLocale) {
- chosen.unshift(osLocale);
- }
-
- if (appLocale && appLocale != osLocale) {
- chosen.unshift(appLocale);
- }
-
- let result = chosen.join(",");
- console.log("Setting intl.accept_languages to " + result);
- this.setLocalizedPref("intl.accept_languages", result);
- },
-
- // nsIAndroidBrowserApp
- get selectedTab() {
- return this._selectedTab;
- },
-
- // nsIAndroidBrowserApp
- getBrowserTab: function(tabId) {
- return this.getTabForId(tabId);
- },
-
- getUITelemetryObserver: function() {
- return UITelemetry;
- },
-
- // This method will return a list of history items and toIndex based on the action provided from the fromIndex to toIndex,
- // optionally selecting selIndex (if fromIndex <= selIndex <= toIndex)
- getHistory: function(data) {
- let action = data.action;
- let webNav = BrowserApp.getTabForId(data.tabId).window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
- let historyIndex = webNav.sessionHistory.index;
- let historySize = webNav.sessionHistory.count;
- let canGoBack = webNav.canGoBack;
- let canGoForward = webNav.canGoForward;
- let listitems = [];
- let fromIndex = 0;
- let toIndex = historySize - 1;
- let selIndex = historyIndex;
-
- if (action == "BACK" && canGoBack) {
- fromIndex = Math.max(historyIndex - kMaxHistoryListSize, 0);
- toIndex = historyIndex;
- selIndex = historyIndex;
- } else if (action == "FORWARD" && canGoForward) {
- fromIndex = historyIndex;
- toIndex = Math.min(historySize - 1, historyIndex + kMaxHistoryListSize);
- selIndex = historyIndex;
- } else if (action == "ALL" && (canGoBack || canGoForward)){
- fromIndex = historyIndex - kMaxHistoryListSize / 2;
- toIndex = historyIndex + kMaxHistoryListSize / 2;
- if (fromIndex < 0) {
- toIndex -= fromIndex;
- }
-
- if (toIndex > historySize - 1) {
- fromIndex -= toIndex - (historySize - 1);
- toIndex = historySize - 1;
- }
-
- fromIndex = Math.max(fromIndex, 0);
- selIndex = historyIndex;
- } else {
- // return empty list immediately.
- return {
- "historyItems": listitems,
- "toIndex": toIndex
- };
- }
-
- let browser = this.selectedBrowser;
- let hist = browser.sessionHistory;
- for (let i = toIndex; i >= fromIndex; i--) {
- let entry = hist.getEntryAtIndex(i, false);
- let item = {
- title: entry.title || entry.URI.spec,
- url: entry.URI.spec,
- selected: (i == selIndex)
- };
- listitems.push(item);
- }
-
- return {
- "historyItems": listitems,
- "toIndex": toIndex
- };
- },
-};
-
-var NativeWindow = {
- init: function() {
- Services.obs.addObserver(this, "Menu:Clicked", false);
- Services.obs.addObserver(this, "Doorhanger:Reply", false);
- this.contextmenus.init();
- },
-
- loadDex: function(zipFile, implClass) {
- Messaging.sendRequest({
- type: "Dex:Load",
- zipfile: zipFile,
- impl: implClass || "Main"
- });
- },
-
- unloadDex: function(zipFile) {
- Messaging.sendRequest({
- type: "Dex:Unload",
- zipfile: zipFile
- });
- },
-
- menu: {
- _callbacks: [],
- _menuId: 1,
- toolsMenuID: -1,
- add: function() {
- let options;
- if (arguments.length == 1) {
- options = arguments[0];
- } else if (arguments.length == 3) {
- Log.w("Browser", "This menu addon API has been deprecated. Instead, use the options object API.");
- options = {
- name: arguments[0],
- callback: arguments[2]
- };
- } else {
- throw "Incorrect number of parameters";
- }
-
- options.type = "Menu:Add";
- options.id = this._menuId;
-
- Messaging.sendRequest(options);
- this._callbacks[this._menuId] = options.callback;
- this._menuId++;
- return this._menuId - 1;
- },
-
- remove: function(aId) {
- Messaging.sendRequest({ type: "Menu:Remove", id: aId });
- },
-
- update: function(aId, aOptions) {
- if (!aOptions)
- return;
-
- Messaging.sendRequest({
- type: "Menu:Update",
- id: aId,
- options: aOptions
- });
- }
- },
-
- doorhanger: {
- _callbacks: {},
- _callbacksId: 0,
- _promptId: 0,
-
- /**
- * @param aOptions
- * An options JavaScript object holding additional properties for the
- * notification. The following properties are currently supported:
- * persistence: An integer. The notification will not automatically
- * dismiss for this many page loads. If persistence is set
- * to -1, the doorhanger will never automatically dismiss.
- * persistWhileVisible:
- * A boolean. If true, a visible notification will always
- * persist across location changes.
- * timeout: A time in milliseconds. The notification will not
- * automatically dismiss before this time.
- *
- * checkbox: A string to appear next to a checkbox under the notification
- * message. The button callback functions will be called with
- * the checked state as an argument.
- *
- * actionText: An object that specifies a clickable string, a type of action,
- * and a bundle blob for the consumer to create a click action.
- * { text: <text>,
- * type: <type>,
- * bundle: <blob-object> }
- *
- * @param aCategory
- * Doorhanger type to display (e.g., LOGIN)
- */
- show: function(aMessage, aValue, aButtons, aTabID, aOptions, aCategory) {
- if (aButtons == null) {
- aButtons = [];
- }
-
- if (aButtons.length > 2) {
- console.log("Doorhanger can have a maximum of two buttons!");
- aButtons.length = 2;
- }
-
- aButtons.forEach((function(aButton) {
- this._callbacks[this._callbacksId] = { cb: aButton.callback, prompt: this._promptId };
- aButton.callback = this._callbacksId;
- this._callbacksId++;
- }).bind(this));
-
- this._promptId++;
- let json = {
- type: "Doorhanger:Add",
- message: aMessage,
- value: aValue,
- buttons: aButtons,
- // use the current tab if none is provided
- tabID: aTabID || BrowserApp.selectedTab.id,
- options: aOptions || {},
- category: aCategory
- };
- Messaging.sendRequest(json);
- },
-
- hide: function(aValue, aTabID) {
- Messaging.sendRequest({
- type: "Doorhanger:Remove",
- value: aValue,
- tabID: aTabID
- });
- }
- },
-
- observe: function(aSubject, aTopic, aData) {
- if (aTopic == "Menu:Clicked") {
- if (this.menu._callbacks[aData])
- this.menu._callbacks[aData]();
- } else if (aTopic == "Doorhanger:Reply") {
- let data = JSON.parse(aData);
- let reply_id = data["callback"];
-
- if (this.doorhanger._callbacks[reply_id]) {
- // Pass the value of the optional checkbox to the callback
- let checked = data["checked"];
- this.doorhanger._callbacks[reply_id].cb(checked, data.inputs);
-
- let prompt = this.doorhanger._callbacks[reply_id].prompt;
- for (let id in this.doorhanger._callbacks) {
- if (this.doorhanger._callbacks[id].prompt == prompt) {
- delete this.doorhanger._callbacks[id];
- }
- }
- }
- }
- },
-
- contextmenus: {
- items: {}, // a list of context menu items that we may show
- DEFAULT_HTML5_ORDER: -1, // Sort order for HTML5 context menu items
-
- init: function() {
- // Accessing "NativeWindow.contextmenus" initializes context menus if needed.
- BrowserApp.deck.addEventListener(
- "contextmenu", (e) => NativeWindow.contextmenus.show(e), false);
- },
-
- add: function() {
- let args;
- if (arguments.length == 1) {
- args = arguments[0];
- } else if (arguments.length == 3) {
- args = {
- label : arguments[0],
- selector: arguments[1],
- callback: arguments[2]
- };
- } else {
- throw "Incorrect number of parameters";
- }
-
- if (!args.label)
- throw "Menu items must have a name";
-
- let cmItem = new ContextMenuItem(args);
- this.items[cmItem.id] = cmItem;
- return cmItem.id;
- },
-
- remove: function(aId) {
- delete this.items[aId];
- },
-
- // Although we do not use this ourselves anymore, add-ons may still
- // need it as it has been documented, so we shouldn't remove it.
- SelectorContext: function(aSelector) {
- return {
- matches: function(aElt) {
- if (aElt.matches)
- return aElt.matches(aSelector);
- return false;
- }
- };
- },
-
- linkOpenableNonPrivateContext: {
- matches: function linkOpenableNonPrivateContextMatches(aElement) {
- let doc = aElement.ownerDocument;
- if (!doc || PrivateBrowsingUtils.isContentWindowPrivate(doc.defaultView)) {
- return false;
- }
-
- return NativeWindow.contextmenus.linkOpenableContext.matches(aElement);
- }
- },
-
- linkOpenableContext: {
- matches: function linkOpenableContextMatches(aElement) {
- let uri = NativeWindow.contextmenus._getLink(aElement);
- if (uri) {
- let scheme = uri.scheme;
- let dontOpen = /^(javascript|mailto|news|snews|tel)$/;
- return (scheme && !dontOpen.test(scheme));
- }
- return false;
- }
- },
-
- linkCopyableContext: {
- matches: function linkCopyableContextMatches(aElement) {
- let uri = NativeWindow.contextmenus._getLink(aElement);
- if (uri) {
- let scheme = uri.scheme;
- let dontCopy = /^(mailto|tel)$/;
- return (scheme && !dontCopy.test(scheme));
- }
- return false;
- }
- },
-
- linkShareableContext: {
- matches: function linkShareableContextMatches(aElement) {
- let uri = NativeWindow.contextmenus._getLink(aElement);
- if (uri) {
- let scheme = uri.scheme;
- let dontShare = /^(about|chrome|file|javascript|mailto|resource|tel)$/;
- return (scheme && !dontShare.test(scheme));
- }
- return false;
- }
- },
-
- linkBookmarkableContext: {
- matches: function linkBookmarkableContextMatches(aElement) {
- let uri = NativeWindow.contextmenus._getLink(aElement);
- if (uri) {
- let scheme = uri.scheme;
- let dontBookmark = /^(mailto|tel)$/;
- return (scheme && !dontBookmark.test(scheme));
- }
- return false;
- }
- },
-
- emailLinkContext: {
- matches: function emailLinkContextMatches(aElement) {
- let uri = NativeWindow.contextmenus._getLink(aElement);
- if (uri)
- return uri.schemeIs("mailto");
- return false;
- }
- },
-
- phoneNumberLinkContext: {
- matches: function phoneNumberLinkContextMatches(aElement) {
- let uri = NativeWindow.contextmenus._getLink(aElement);
- if (uri)
- return uri.schemeIs("tel");
- return false;
- }
- },
-
- imageLocationCopyableContext: {
- matches: function imageLinkCopyableContextMatches(aElement) {
- if (aElement instanceof Ci.nsIDOMHTMLImageElement) {
- // The image is blocked by Tap-to-load Images
- if (aElement.hasAttribute("data-ctv-src") && !aElement.hasAttribute("data-ctv-show")) {
- return false;
- }
- }
- return (aElement instanceof Ci.nsIImageLoadingContent && aElement.currentURI);
- }
- },
-
- imageSaveableContext: {
- matches: function imageSaveableContextMatches(aElement) {
- if (aElement instanceof Ci.nsIDOMHTMLImageElement) {
- // The image is blocked by Tap-to-load Images
- if (aElement.hasAttribute("data-ctv-src") && !aElement.hasAttribute("data-ctv-show")) {
- return false;
- }
- }
- if (aElement instanceof Ci.nsIImageLoadingContent && aElement.currentURI) {
- // The image must be loaded to allow saving
- let request = aElement.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
- return (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE));
- }
- return false;
- }
- },
-
- imageShareableContext: {
- matches: function imageShareableContextMatches(aElement) {
- let imgSrc = '';
- if (aElement instanceof Ci.nsIDOMHTMLImageElement) {
- imgSrc = aElement.src;
- } else if (aElement instanceof Ci.nsIImageLoadingContent &&
- aElement.currentURI &&
- aElement.currentURI.spec) {
- imgSrc = aElement.currentURI.spec;
- }
-
- // In order to share an image, we need to pass the image src over IPC via an Intent (in
- // `ApplicationPackageManager.queryIntentActivities`). However, the transaction has a 1MB limit
- // (shared by all transactions in progress) - otherwise we crash! (bug 1243305)
- // https://developer.android.com/reference/android/os/TransactionTooLargeException.html
- //
- // The transaction limit is 1MB and we arbitrarily choose to cap this transaction at 1/4 of that = 250,000 bytes.
- // In Java, a UTF-8 character is 1-4 bytes so, 250,000 bytes / 4 bytes/char = 62,500 char
- let MAX_IMG_SRC_LEN = 62500;
- let isTooLong = imgSrc.length >= MAX_IMG_SRC_LEN;
- return !isTooLong && this.NativeWindow.contextmenus.imageSaveableContext.matches(aElement);
- }.bind(this)
- },
-
- mediaSaveableContext: {
- matches: function mediaSaveableContextMatches(aElement) {
- return (aElement instanceof HTMLVideoElement ||
- aElement instanceof HTMLAudioElement);
- }
- },
-
- imageBlockingPolicyContext: {
- matches: function imageBlockingPolicyContextMatches(aElement) {
- if (aElement instanceof Ci.nsIDOMHTMLImageElement && aElement.getAttribute("data-ctv-src")) {
- // Only show the menuitem if we are blocking the image
- if (aElement.getAttribute("data-ctv-show") == "true") {
- return false;
- }
- return true;
- }
- return false;
- }
- },
-
- mediaContext: function(aMode) {
- return {
- matches: function(aElt) {
- if (aElt instanceof Ci.nsIDOMHTMLMediaElement) {
- let hasError = aElt.error != null || aElt.networkState == aElt.NETWORK_NO_SOURCE;
- if (hasError)
- return false;
-
- let paused = aElt.paused || aElt.ended;
- if (paused && aMode == "media-paused")
- return true;
- if (!paused && aMode == "media-playing")
- return true;
- let controls = aElt.controls;
- if (!controls && aMode == "media-hidingcontrols")
- return true;
-
- let muted = aElt.muted;
- if (muted && aMode == "media-muted")
- return true;
- else if (!muted && aMode == "media-unmuted")
- return true;
- }
- return false;
- }
- };
- },
-
- videoContext: function(aMode) {
- return {
- matches: function(aElt) {
- if (aElt instanceof HTMLVideoElement) {
- if (!aMode) {
- return true;
- }
- var isFullscreen = aElt.ownerDocument.fullscreenElement == aElt;
- if (aMode == "not-fullscreen") {
- return !isFullscreen;
- }
- if (aMode == "fullscreen") {
- return isFullscreen;
- }
- }
- return false;
- }
- };
- },
-
- /* Holds a WeakRef to the original target element this context menu was shown for.
- * Most API's will have to walk up the tree from this node to find the correct element
- * to act on
- */
- get _target() {
- if (this._targetRef)
- return this._targetRef.get();
- return null;
- },
-
- set _target(aTarget) {
- if (aTarget)
- this._targetRef = Cu.getWeakReference(aTarget);
- else this._targetRef = null;
- },
-
- get defaultContext() {
- delete this.defaultContext;
- return this.defaultContext = Strings.browser.GetStringFromName("browser.menu.context.default");
- },
-
- /* Gets menuitems for an arbitrary node
- * Parameters:
- * element - The element to look at. If this element has a contextmenu attribute, the
- * corresponding contextmenu will be used.
- */
- _getHTMLContextMenuItemsForElement: function(element) {
- let htmlMenu = element.contextMenu;
- if (!htmlMenu) {
- return [];
- }
-
- htmlMenu.QueryInterface(Components.interfaces.nsIHTMLMenu);
- htmlMenu.sendShowEvent();
-
- return this._getHTMLContextMenuItemsForMenu(htmlMenu, element);
- },
-
- /* Add a menuitem for an HTML <menu> node
- * Parameters:
- * menu - The <menu> element to iterate through for menuitems
- * target - The target element these context menu items are attached to
- */
- _getHTMLContextMenuItemsForMenu: function(menu, target) {
- let items = [];
- for (let i = 0; i < menu.childNodes.length; i++) {
- let elt = menu.childNodes[i];
- if (!elt.label)
- continue;
-
- items.push(new HTMLContextMenuItem(elt, target));
- }
-
- return items;
- },
-
- // Searches the current list of menuitems to show for any that match this id
- _findMenuItem: function(aId) {
- if (!this.menus) {
- return null;
- }
-
- for (let context in this.menus) {
- let menu = this.menus[context];
- for (let i = 0; i < menu.length; i++) {
- if (menu[i].id === aId) {
- return menu[i];
- }
- }
- }
- return null;
- },
-
- // Returns true if there are any context menu items to show
- _shouldShow: function() {
- for (let context in this.menus) {
- let menu = this.menus[context];
- if (menu.length > 0) {
- return true;
- }
- }
- return false;
- },
-
- /* Returns a label to be shown in a tabbed ui if there are multiple "contexts". For instance, if this
- * is an image inside an <a> tag, we may have a "link" context and an "image" one.
- */
- _getContextType: function(element) {
- // For anchor nodes, we try to use the scheme to pick a string
- if (element instanceof Ci.nsIDOMHTMLAnchorElement) {
- let uri = this.makeURI(this._getLinkURL(element));
- try {
- return Strings.browser.GetStringFromName("browser.menu.context." + uri.scheme);
- } catch(ex) { }
- }
-
- // Otherwise we try the nodeName
- try {
- return Strings.browser.GetStringFromName("browser.menu.context." + element.nodeName.toLowerCase());
- } catch(ex) { }
-
- // Fallback to the default
- return this.defaultContext;
- },
-
- // Adds context menu items added through the add-on api
- _getNativeContextMenuItems: function(element, x, y) {
- let res = [];
- for (let itemId of Object.keys(this.items)) {
- let item = this.items[itemId];
-
- if (!this._findMenuItem(item.id) && item.matches(element, x, y)) {
- res.push(item);
- }
- }
-
- return res;
- },
-
- /* Checks if there are context menu items to show, and if it finds them
- * sends a contextmenu event to content. We also send showing events to
- * any html5 context menus we are about to show, and fire some local notifications
- * for chrome consumers to do lazy menuitem construction
- */
- show: function(event) {
- // Android Long-press / contextmenu event provides clientX/Y data. This is not provided
- // by mochitest: test_browserElement_inproc_ContextmenuEvents.html.
- if (!event.clientX || !event.clientY) {
- return;
- }
-
- // If the event was already defaultPrevented by somebody (web content, or
- // some other part of gecko), then don't do anything with it.
- if (event.defaultPrevented) {
- return;
- }
-
- // Use the highlighted element for the context menu target. When accessibility is
- // enabled, elements may not be highlighted so use the event target instead.
- this._target = BrowserEventHandler._highlightElement || event.target;
- if (!this._target) {
- return;
- }
-
- // Try to build a list of contextmenu items. If successful, actually show the
- // native context menu by passing the list to Java.
- this._buildMenu(event.clientX, event.clientY);
- if (this._shouldShow()) {
- BrowserEventHandler._cancelTapHighlight();
-
- // Consume / preventDefault the event, and show the contextmenu.
- event.preventDefault();
- this._innerShow(this._target, event.clientX, event.clientY);
- this._target = null;
-
- return;
- }
-
- // If no context-menu for long-press event, it may be meant to trigger text-selection.
- this.menus = null;
- Services.obs.notifyObservers(
- {target: this._target, x: event.clientX, y: event.clientY}, "context-menu-not-shown", "");
- },
-
- // Returns a title for a context menu. If no title attribute exists, will fall back to looking for a url
- _getTitle: function(node) {
- if (node.hasAttribute && node.hasAttribute("title")) {
- return node.getAttribute("title");
- }
- return this._getUrl(node);
- },
-
- // Returns a url associated with a node
- _getUrl: function(node) {
- if ((node instanceof Ci.nsIDOMHTMLAnchorElement && node.href) ||
- (node instanceof Ci.nsIDOMHTMLAreaElement && node.href)) {
- return this._getLinkURL(node);
- } else if (node instanceof Ci.nsIImageLoadingContent && node.currentURI) {
- // The image is blocked by Tap-to-load Images
- let originalURL = node.getAttribute("data-ctv-src");
- if (originalURL) {
- return originalURL;
- }
- return node.currentURI.spec;
- } else if (node instanceof Ci.nsIDOMHTMLMediaElement) {
- let srcUrl = node.currentSrc || node.src;
- // If URL prepended with blob or mediasource, we'll remove it.
- return srcUrl.replace(/^(?:blob|mediasource):/, '');
- }
-
- return "";
- },
-
- // Adds an array of menuitems to the current list of items to show, in the correct context
- _addMenuItems: function(items, context) {
- if (!this.menus[context]) {
- this.menus[context] = [];
- }
- this.menus[context] = this.menus[context].concat(items);
- },
-
- /* Does the basic work of building a context menu to show. Will combine HTML and Native
- * context menus items, as well as sorting menuitems into different menus based on context.
- */
- _buildMenu: function(x, y) {
- // now walk up the tree and for each node look for any context menu items that apply
- let element = this._target;
-
- // this.menus holds a hashmap of "contexts" to menuitems associated with that context
- // For instance, if the user taps an image inside a link, we'll have something like:
- // {
- // link: [ ContextMenuItem, ContextMenuItem ]
- // image: [ ContextMenuItem, ContextMenuItem ]
- // }
- this.menus = {};
-
- while (element) {
- let context = this._getContextType(element);
-
- // First check for any html5 context menus that might exist...
- var items = this._getHTMLContextMenuItemsForElement(element);
- if (items.length > 0) {
- this._addMenuItems(items, context);
- }
-
- // then check for any context menu items registered in the ui.
- items = this._getNativeContextMenuItems(element, x, y);
- if (items.length > 0) {
- this._addMenuItems(items, context);
- }
-
- // walk up the tree and find more items to show
- element = element.parentNode;
- }
- },
-
- // Walks the DOM tree to find a title from a node
- _findTitle: function(node) {
- let title = "";
- while(node && !title) {
- title = this._getTitle(node);
- node = node.parentNode;
- }
- return title;
- },
-
- /* Reformats the list of menus to show into an object that can be sent to Prompt.jsm
- * If there is one menu, will return a flat array of menuitems. If there are multiple
- * menus, will return an array with appropriate tabs/items inside it. i.e. :
- * [
- * { label: "link", items: [...] },
- * { label: "image", items: [...] }
- * ]
- */
- _reformatList: function(target) {
- let contexts = Object.keys(this.menus);
-
- if (contexts.length === 1) {
- // If there's only one context, we'll only show a single flat single select list
- return this._reformatMenuItems(target, this.menus[contexts[0]]);
- }
-
- // If there are multiple contexts, we'll only show a tabbed ui with multiple lists
- return this._reformatListAsTabs(target, this.menus);
- },
-
- /* Reformats the list of menus to show into an object that can be sent to Prompt.jsm's
- * addTabs method. i.e. :
- * { link: [...], image: [...] } becomes
- * [ { label: "link", items: [...] } ]
- *
- * Also reformats items and resolves any parmaeters that aren't known until display time
- * (for instance Helper app menu items adjust their title to reflect what Helper App can be used for this link).
- */
- _reformatListAsTabs: function(target, menus) {
- let itemArray = [];
-
- // Sort the keys so that "link" is always first
- let contexts = Object.keys(this.menus);
- contexts.sort((context1, context2) => {
- if (context1 === this.defaultContext) {
- return -1;
- } else if (context2 === this.defaultContext) {
- return 1;
- }
- return 0;
- });
-
- contexts.forEach(context => {
- itemArray.push({
- label: context,
- items: this._reformatMenuItems(target, menus[context])
- });
- });
-
- return itemArray;
- },
-
- /* Reformats an array of ContextMenuItems into an array that can be handled by Prompt.jsm. Also reformats items
- * and resolves any parmaeters that aren't known until display time
- * (for instance Helper app menu items adjust their title to reflect what Helper App can be used for this link).
- */
- _reformatMenuItems: function(target, menuitems) {
- let itemArray = [];
-
- for (let i = 0; i < menuitems.length; i++) {
- let t = target;
- while(t) {
- if (menuitems[i].matches(t)) {
- let val = menuitems[i].getValue(t);
-
- // hidden menu items will return null from getValue
- if (val) {
- itemArray.push(val);
- break;
- }
- }
-
- t = t.parentNode;
- }
- }
-
- return itemArray;
- },
-
- // Called where we're finally ready to actually show the contextmenu. Sorts the items and shows a prompt.
- _innerShow: function(target, x, y) {
- Haptic.performSimpleAction(Haptic.LongPress);
-
- // spin through the tree looking for a title for this context menu
- let title = this._findTitle(target);
-
- for (let context in this.menus) {
- let menu = this.menus[context];
- menu.sort((a,b) => {
- if (a.order === b.order) {
- return 0;
- }
- return (a.order > b.order) ? 1 : -1;
- });
- }
-
- let useTabs = Object.keys(this.menus).length > 1;
- let prompt = new Prompt({
- window: target.ownerDocument.defaultView,
- title: useTabs ? undefined : title
- });
-
- let items = this._reformatList(target);
- if (useTabs) {
- prompt.addTabs({
- id: "tabs",
- items: items
- });
- } else {
- prompt.setSingleChoiceItems(items);
- }
-
- prompt.show(this._promptDone.bind(this, target, x, y, items));
- },
-
- // Called when the contextmenu prompt is closed
- _promptDone: function(target, x, y, items, data) {
- if (data.button == -1) {
- // Prompt was cancelled, or an ActionView was used.
- return;
- }
-
- let selectedItemId;
- if (data.tabs) {
- let menu = items[data.tabs.tab];
- selectedItemId = menu.items[data.tabs.item].id;
- } else {
- selectedItemId = items[data.list[0]].id
- }
-
- let selectedItem = this._findMenuItem(selectedItemId);
- this.menus = null;
-
- if (!selectedItem || !selectedItem.matches || !selectedItem.callback) {
- return;
- }
-
- // for menuitems added using the native UI, pass the dom element that matched that item to the callback
- while (target) {
- if (selectedItem.matches(target, x, y)) {
- selectedItem.callback(target, x, y);
- break;
- }
- target = target.parentNode;
- }
- },
-
- // XXX - These are stolen from Util.js, we should remove them if we bring it back
- makeURLAbsolute: function makeURLAbsolute(base, url) {
- // Note: makeURI() will throw if url is not a valid URI
- return this.makeURI(url, null, this.makeURI(base)).spec;
- },
-
- makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) {
- return Services.io.newURI(aURL, aOriginCharset, aBaseURI);
- },
-
- _getLink: function(aElement) {
- if (aElement.nodeType == Ci.nsIDOMNode.ELEMENT_NODE &&
- ((aElement instanceof Ci.nsIDOMHTMLAnchorElement && aElement.href) ||
- (aElement instanceof Ci.nsIDOMHTMLAreaElement && aElement.href) ||
- aElement instanceof Ci.nsIDOMHTMLLinkElement ||
- aElement.getAttributeNS(kXLinkNamespace, "type") == "simple")) {
- try {
- let url = this._getLinkURL(aElement);
- return Services.io.newURI(url, null, null);
- } catch (e) {}
- }
- return null;
- },
-
- _disableRestricted: function _disableRestricted(restriction, selector) {
- return {
- matches: function _disableRestrictedMatches(aElement, aX, aY) {
- if (!ParentalControls.isAllowed(ParentalControls[restriction])) {
- return false;
- }
-
- return selector.matches(aElement, aX, aY);
- }
- };
- },
-
- _getLinkURL: function ch_getLinkURL(aLink) {
- let href = aLink.href;
- if (href)
- return href;
-
- href = aLink.getAttribute("href") ||
- aLink.getAttributeNS(kXLinkNamespace, "href");
- if (!href || !href.match(/\S/)) {
- // Without this we try to save as the current doc,
- // for example, HTML case also throws if empty
- throw "Empty href";
- }
-
- return this.makeURLAbsolute(aLink.baseURI, href);
- },
-
- _copyStringToDefaultClipboard: function(aString) {
- let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
- clipboard.copyString(aString);
- Snackbars.show(Strings.browser.GetStringFromName("selectionHelper.textCopied"), Snackbars.LENGTH_LONG);
- },
-
- _stripScheme: function(aString) {
- let index = aString.indexOf(":");
- return aString.slice(index + 1);
- }
- }
-};
-
-XPCOMUtils.defineLazyModuleGetter(this, "PageActions",
- "resource://gre/modules/PageActions.jsm");
-
-// These alias to the old, deprecated NativeWindow interfaces
-[
- ["pageactions", "resource://gre/modules/PageActions.jsm", "PageActions"],
- ["toast", "resource://gre/modules/Snackbars.jsm", "Snackbars"]
-].forEach(item => {
- let [name, script, exprt] = item;
-
- XPCOMUtils.defineLazyGetter(NativeWindow, name, () => {
- var err = Strings.browser.formatStringFromName("nativeWindow.deprecated", ["NativeWindow." + name, script], 2);
- Cu.reportError(err);
-
- let sandbox = {};
- Cu.import(script, sandbox);
- return sandbox[exprt];
- });
-});
-
-var LightWeightThemeWebInstaller = {
- init: function sh_init() {
- let temp = {};
- Cu.import("resource://gre/modules/LightweightThemeConsumer.jsm", temp);
- let theme = new temp.LightweightThemeConsumer(document);
- BrowserApp.deck.addEventListener("InstallBrowserTheme", this, false, true);
- BrowserApp.deck.addEventListener("PreviewBrowserTheme", this, false, true);
- BrowserApp.deck.addEventListener("ResetBrowserThemePreview", this, false, true);
-
- if (ParentalControls.parentalControlsEnabled &&
- !this._manager.currentTheme &&
- ParentalControls.isAllowed(ParentalControls.DEFAULT_THEME)) {
- // We are using the DEFAULT_THEME restriction to differentiate between restricted profiles & guest mode - Bug 1199596
- this._installParentalControlsTheme();
- }
- },
-
- handleEvent: function (event) {
- switch (event.type) {
- case "InstallBrowserTheme":
- case "PreviewBrowserTheme":
- case "ResetBrowserThemePreview":
- // ignore requests from background tabs
- if (event.target.ownerDocument.defaultView.top != content)
- return;
- }
-
- switch (event.type) {
- case "InstallBrowserTheme":
- this._installRequest(event);
- break;
- case "PreviewBrowserTheme":
- this._preview(event);
- break;
- case "ResetBrowserThemePreview":
- this._resetPreview(event);
- break;
- case "pagehide":
- 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;
- },
-
- _installParentalControlsTheme: function() {
- let mgr = this._manager;
- let parentalControlsTheme = {
- "headerURL": "resource://android/assets/parental_controls_theme.png",
- "name": "Parental Controls Theme",
- "id": "parental-controls-theme@mozilla.org"
- };
-
- mgr.addBuiltInTheme(parentalControlsTheme);
- mgr.themeChanged(parentalControlsTheme);
- },
-
- _installRequest: function (event) {
- let node = event.target;
- let data = this._getThemeFromNode(node);
- if (!data)
- return;
-
- if (this._isAllowed(node)) {
- this._install(data);
- return;
- }
-
- let allowButtonText = Strings.browser.GetStringFromName("lwthemeInstallRequest.allowButton");
- let message = Strings.browser.formatStringFromName("lwthemeInstallRequest.message", [node.ownerDocument.location.hostname], 1);
- let buttons = [{
- label: allowButtonText,
- callback: function () {
- LightWeightThemeWebInstaller._install(data);
- },
- positive: true
- }];
-
- NativeWindow.doorhanger.show(message, "Personas", buttons, BrowserApp.selectedTab.id);
- },
-
- _install: function (newLWTheme) {
- this._manager.currentTheme = newLWTheme;
- },
-
- _previewWindow: null,
- _preview: function (event) {
- if (!this._isAllowed(event.target))
- return;
- let data = this._getThemeFromNode(event.target);
- if (!data)
- return;
- this._resetPreview();
-
- this._previewWindow = event.target.ownerDocument.defaultView;
- this._previewWindow.addEventListener("pagehide", this, true);
- BrowserApp.deck.addEventListener("TabSelect", this, false);
- this._manager.previewTheme(data);
- },
-
- _resetPreview: function (event) {
- if (!this._previewWindow ||
- event && !this._isAllowed(event.target))
- return;
-
- this._previewWindow.removeEventListener("pagehide", this, true);
- this._previewWindow = null;
- BrowserApp.deck.removeEventListener("TabSelect", this, false);
-
- this._manager.resetPreview();
- },
-
- _isAllowed: function (node) {
- // Make sure the whitelist has been imported to permissions
- PermissionsUtils.importFromPrefs("xpinstall.", "install");
-
- let pm = Services.perms;
-
- let uri = node.ownerDocument.documentURIObject;
- if (!uri.schemeIs("https")) {
- return false;
- }
-
- return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
- },
-
- _getThemeFromNode: function (node) {
- return this._manager.parseTheme(node.getAttribute("data-browsertheme"), node.baseURI);
- }
-};
-
-var DesktopUserAgent = {
- DESKTOP_UA: null,
- TCO_DOMAIN: "t.co",
- TCO_REPLACE: / Gecko.*/,
-
- init: function ua_init() {
- Services.obs.addObserver(this, "DesktopMode:Change", false);
- UserAgentOverrides.addComplexOverride(this.onRequest.bind(this));
-
- // See https://developer.mozilla.org/en/Gecko_user_agent_string_reference
- this.DESKTOP_UA = Cc["@mozilla.org/network/protocol;1?name=http"]
- .getService(Ci.nsIHttpProtocolHandler).userAgent
- .replace(/Android \d.+?; [a-zA-Z]+/, "X11; Linux x86_64")
- .replace(/Gecko\/[0-9\.]+/, "Gecko/20100101");
- },
-
- onRequest: function(channel, defaultUA) {
- if (AppConstants.NIGHTLY_BUILD && this.TCO_DOMAIN == channel.URI.host) {
- // Force the referrer
- channel.referrer = channel.URI;
-
- // Send a bot-like UA to t.co to get a real redirect. We strip off the
- // "Gecko/x.y Firefox/x.y" part
- return defaultUA.replace(this.TCO_REPLACE, "");
- }
-
- let channelWindow = this._getWindowForRequest(channel);
- let tab = BrowserApp.getTabForWindow(channelWindow);
- if (tab) {
- return this.getUserAgentForTab(tab);
- }
-
- return null;
- },
-
- getUserAgentForWindow: function ua_getUserAgentForWindow(aWindow) {
- let tab = BrowserApp.getTabForWindow(aWindow.top);
- if (tab) {
- return this.getUserAgentForTab(tab);
- }
-
- return null;
- },
-
- getUserAgentForTab: function ua_getUserAgentForTab(aTab) {
- // Send desktop UA if "Request Desktop Site" is enabled.
- if (aTab.desktopMode) {
- return this.DESKTOP_UA;
- }
-
- return null;
- },
-
- _getRequestLoadContext: function ua_getRequestLoadContext(aRequest) {
- if (aRequest && aRequest.notificationCallbacks) {
- try {
- return aRequest.notificationCallbacks.getInterface(Ci.nsILoadContext);
- } catch (ex) { }
- }
-
- if (aRequest && aRequest.loadGroup && aRequest.loadGroup.notificationCallbacks) {
- try {
- return aRequest.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
- } catch (ex) { }
- }
-
- return null;
- },
-
- _getWindowForRequest: function ua_getWindowForRequest(aRequest) {
- let loadContext = this._getRequestLoadContext(aRequest);
- if (loadContext) {
- try {
- return loadContext.associatedWindow;
- } catch (e) {
- // loadContext.associatedWindow can throw when there's no window
- }
- }
- return null;
- },
-
- observe: function ua_observe(aSubject, aTopic, aData) {
- if (aTopic === "DesktopMode:Change") {
- let args = JSON.parse(aData);
- let tab = BrowserApp.getTabForId(args.tabId);
- if (tab) {
- tab.reloadWithMode(args.desktopMode);
- }
- }
- }
-};
-
-
-function nsBrowserAccess() {
-}
-
-nsBrowserAccess.prototype = {
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow]),
-
- _getBrowser: function _getBrowser(aURI, aOpener, aWhere, aFlags) {
- let isExternal = !!(aFlags & Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
- if (isExternal && aURI && aURI.schemeIs("chrome"))
- return null;
-
- let loadflags = isExternal ?
- Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
- Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
- if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
- if (isExternal) {
- aWhere = Services.prefs.getIntPref("browser.link.open_external");
- } else {
- aWhere = Services.prefs.getIntPref("browser.link.open_newwindow");
- }
- }
-
- Services.io.offline = false;
-
- let referrer;
- if (aOpener) {
- try {
- let location = aOpener.location;
- referrer = Services.io.newURI(location, null, null);
- } catch(e) { }
- }
-
- let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
- let pinned = false;
-
- if (aURI && aWhere == Ci.nsIBrowserDOMWindow.OPEN_SWITCHTAB) {
- pinned = true;
- let spec = aURI.spec;
- let tabs = BrowserApp.tabs;
- for (let i = 0; i < tabs.length; i++) {
- let appOrigin = ss.getTabValue(tabs[i], "appOrigin");
- if (appOrigin == spec) {
- let tab = tabs[i];
- BrowserApp.selectTab(tab);
- return tab.browser;
- }
- }
- }
-
- // If OPEN_SWITCHTAB was not handled above, we need to open a new tab,
- // along with other OPEN_ values that create a new tab.
- let newTab = (aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW ||
- aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB ||
- aWhere == Ci.nsIBrowserDOMWindow.OPEN_SWITCHTAB);
- let isPrivate = false;
-
- if (newTab) {
- let parentId = -1;
- if (!isExternal && aOpener) {
- let parent = BrowserApp.getTabForWindow(aOpener.top);
- if (parent) {
- parentId = parent.id;
- isPrivate = PrivateBrowsingUtils.isBrowserPrivate(parent.browser);
- }
- }
-
- let openerWindow = (aFlags & Ci.nsIBrowserDOMWindow.OPEN_NO_OPENER) ? null : aOpener;
- // BrowserApp.addTab calls loadURIWithFlags with the appropriate params
- let tab = BrowserApp.addTab(aURI ? aURI.spec : "about:blank", { flags: loadflags,
- referrerURI: referrer,
- external: isExternal,
- parentId: parentId,
- opener: openerWindow,
- selected: true,
- isPrivate: isPrivate,
- pinned: pinned });
-
- return tab.browser;
- }
-
- // OPEN_CURRENTWINDOW and illegal values
- let browser = BrowserApp.selectedBrowser;
- if (aURI && browser) {
- browser.loadURIWithFlags(aURI.spec, loadflags, referrer, null, null);
- }
-
- return browser;
- },
-
- openURI: function browser_openURI(aURI, aOpener, aWhere, aFlags) {
- let browser = this._getBrowser(aURI, aOpener, aWhere, aFlags);
- return browser ? browser.contentWindow : null;
- },
-
- openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aFlags) {
- let browser = this._getBrowser(aURI, null, aWhere, aFlags);
- return browser ? browser.QueryInterface(Ci.nsIFrameLoaderOwner) : null;
- },
-
- isTabContentWindow: function(aWindow) {
- return BrowserApp.getBrowserForWindow(aWindow) != null;
- },
-
- canClose() {
- return BrowserUtils.canCloseWindow(window);
- },
-};
-
-
-function Tab(aURL, aParams) {
- this.filter = null;
- this.browser = null;
- this.id = 0;
- this.lastTouchedAt = Date.now();
- this._zoom = 1.0;
- this._drawZoom = 1.0;
- this._restoreZoom = false;
- this.userScrollPos = { x: 0, y: 0 };
- this.contentDocumentIsDisplayed = true;
- this.pluginDoorhangerTimeout = null;
- this.shouldShowPluginDoorhanger = true;
- this.clickToPlayPluginsActivated = false;
- this.desktopMode = false;
- this.originalURI = null;
- this.hasTouchListener = false;
- this.playingAudio = false;
-
- this.create(aURL, aParams);
-}
-
-/*
- * Sanity limit for URIs passed to UI code.
- *
- * 2000 is the typical industry limit, largely due to older IE versions.
- *
- * We use 25000, so we'll allow almost any value through.
- *
- * Still, this truncation doesn't affect history, so this is only a practical
- * concern in two ways: the truncated value is used when editing URIs, and as
- * the key for favicon fetches.
- */
-const MAX_URI_LENGTH = 25000;
-
-/*
- * Similar restriction for titles. This is only a display concern.
- */
-const MAX_TITLE_LENGTH = 255;
-
-/**
- * Ensure that a string is of a sane length.
- */
-function truncate(text, max) {
- if (!text || !max) {
- return text;
- }
-
- if (text.length <= max) {
- return text;
- }
-
- return text.slice(0, max) + "…";
-}
-
-Tab.prototype = {
- create: function(aURL, aParams) {
- if (this.browser)
- return;
-
- aParams = aParams || {};
-
- this.browser = document.createElement("browser");
- this.browser.setAttribute("type", "content-targetable");
- this.browser.setAttribute("messagemanagergroup", "browsers");
-
- if (Preferences.get("browser.tabs.remote.force-enable", false)) {
- this.browser.setAttribute("remote", "true");
- }
-
- this.browser.permanentKey = {};
-
- // Check if we have a "parent" window which we need to set as our opener
- if ("opener" in aParams) {
- this.browser.presetOpenerWindow(aParams.opener);
- }
-
- // Make sure the previously selected panel remains selected. The selected panel of a deck is
- // not stable when panels are added.
- let selectedPanel = BrowserApp.deck.selectedPanel;
- BrowserApp.deck.insertBefore(this.browser, aParams.sibling || null);
- BrowserApp.deck.selectedPanel = selectedPanel;
-
- let attrs = {};
- if (BrowserApp.manifestUrl) {
- let appsService = Cc["@mozilla.org/AppsService;1"].getService(Ci.nsIAppsService);
- let manifest = appsService.getAppByManifestURL(BrowserApp.manifestUrl);
- if (manifest) {
- let app = manifest.QueryInterface(Ci.mozIApplication);
- this.browser.docShell.frameType = Ci.nsIDocShell.FRAME_TYPE_APP;
- attrs['appId'] = app.localId;
- }
- }
-
- // Must be called after appendChild so the docShell has been created.
- this.setActive(false);
-
- let isPrivate = ("isPrivate" in aParams) && aParams.isPrivate;
- if (isPrivate) {
- attrs['privateBrowsingId'] = 1;
- }
-
- this.browser.docShell.setOriginAttributes(attrs);
-
- // Set the new docShell load flags based on network state.
- if (Tabs.useCache) {
- this.browser.docShell.defaultLoadFlags |= Ci.nsIRequest.LOAD_FROM_CACHE;
- }
-
- this.browser.stop();
-
- // Only set tab uri if uri is valid
- let uri = null;
- let title = aParams.title || aURL;
- try {
- uri = Services.io.newURI(aURL, null, null).spec;
- } catch (e) {}
-
- // When the tab is stubbed from Java, there's a window between the stub
- // creation and the tab creation in Gecko where the stub could be removed
- // or the selected tab can change (which is easiest to hit during startup).
- // To prevent these races, we need to differentiate between tab stubs from
- // Java and new tabs from Gecko.
- let stub = false;
-
- if (!aParams.zombifying) {
- if ("tabID" in aParams) {
- this.id = aParams.tabID;
- stub = true;
- } else {
- let jenv = JNI.GetForThread();
- let jTabs = JNI.LoadClass(jenv, "org.mozilla.gecko.Tabs", {
- static_methods: [
- { name: "getNextTabId", sig: "()I" }
- ],
- });
- this.id = jTabs.getNextTabId();
- JNI.UnloadClasses(jenv);
- }
-
- this.desktopMode = ("desktopMode" in aParams) ? aParams.desktopMode : false;
-
- let message = {
- type: "Tab:Added",
- tabID: this.id,
- uri: truncate(uri, MAX_URI_LENGTH),
- parentId: ("parentId" in aParams) ? aParams.parentId : -1,
- tabIndex: ("tabIndex" in aParams) ? aParams.tabIndex : -1,
- external: ("external" in aParams) ? aParams.external : false,
- selected: ("selected" in aParams || aParams.cancelEditMode === true) ? aParams.selected : true,
- cancelEditMode: aParams.cancelEditMode === true,
- title: truncate(title, MAX_TITLE_LENGTH),
- delayLoad: aParams.delayLoad || false,
- desktopMode: this.desktopMode,
- isPrivate: isPrivate,
- stub: stub
- };
- Messaging.sendRequest(message);
- }
-
- let flags = Ci.nsIWebProgress.NOTIFY_STATE_ALL |
- Ci.nsIWebProgress.NOTIFY_LOCATION |
- Ci.nsIWebProgress.NOTIFY_SECURITY;
- this.filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"].createInstance(Ci.nsIWebProgress);
- this.filter.addProgressListener(this, flags)
- this.browser.addProgressListener(this.filter, flags);
- this.browser.sessionHistory.addSHistoryListener(this);
-
- this.browser.addEventListener("DOMContentLoaded", this, true);
- this.browser.addEventListener("DOMFormHasPassword", this, true);
- this.browser.addEventListener("DOMInputPasswordAdded", this, true);
- this.browser.addEventListener("DOMLinkAdded", this, true);
- this.browser.addEventListener("DOMLinkChanged", this, true);
- this.browser.addEventListener("DOMMetaAdded", this, false);
- this.browser.addEventListener("DOMTitleChanged", this, true);
- this.browser.addEventListener("DOMAudioPlaybackStarted", this, true);
- this.browser.addEventListener("DOMAudioPlaybackStopped", this, true);
- this.browser.addEventListener("DOMWindowClose", this, true);
- this.browser.addEventListener("DOMWillOpenModalDialog", this, true);
- this.browser.addEventListener("DOMAutoComplete", this, true);
- this.browser.addEventListener("blur", this, true);
- this.browser.addEventListener("pageshow", this, true);
- this.browser.addEventListener("MozApplicationManifest", this, true);
- this.browser.addEventListener("TabPreZombify", this, true);
-
- // Note that the XBL binding is untrusted
- this.browser.addEventListener("PluginBindingAttached", this, true, true);
- this.browser.addEventListener("VideoBindingAttached", this, true, true);
- this.browser.addEventListener("VideoBindingCast", this, true, true);
-
- Services.obs.addObserver(this, "before-first-paint", false);
- Services.obs.addObserver(this, "media-playback", false);
- Services.obs.addObserver(this, "media-playback-resumed", false);
-
- // Always intialise new tabs with basic session store data to avoid
- // problems with functions that always expect it to be present
- this.browser.__SS_data = {
- entries: [{
- url: aURL,
- title: truncate(title, MAX_TITLE_LENGTH)
- }],
- index: 1,
- desktopMode: this.desktopMode,
- isPrivate: isPrivate
- };
-
- if (aParams.delayLoad) {
- // If this is a zombie tab, mark the browser for delay loading, which will
- // restore the tab when selected using the session data added above
- this.browser.__SS_restore = true;
- } else {
- let flags = "flags" in aParams ? aParams.flags : Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
- let postData = ("postData" in aParams && aParams.postData) ? aParams.postData.value : null;
- let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null;
- let charset = "charset" in aParams ? aParams.charset : null;
-
- // The search term the user entered to load the current URL
- this.userRequested = "userRequested" in aParams ? aParams.userRequested : "";
- this.isSearch = "isSearch" in aParams ? aParams.isSearch : false;
-
- try {
- this.browser.loadURIWithFlags(aURL, flags, referrerURI, charset, postData);
- } catch(e) {
- let message = {
- type: "Content:LoadError",
- tabID: this.id
- };
- Messaging.sendRequest(message);
- dump("Handled load error: " + e);
- }
- }
- },
-
- /**
- * Reloads the tab with the desktop mode setting.
- */
- reloadWithMode: function (aDesktopMode) {
- // notify desktopmode for PIDOMWindow
- let win = this.browser.contentWindow;
- let dwi = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
- dwi.setDesktopModeViewport(aDesktopMode);
-
- // Set desktop mode for tab and send change to Java
- if (this.desktopMode != aDesktopMode) {
- this.desktopMode = aDesktopMode;
- Messaging.sendRequest({
- type: "DesktopMode:Changed",
- desktopMode: aDesktopMode,
- tabID: this.id
- });
- }
-
- // Only reload the page for http/https schemes
- let currentURI = this.browser.currentURI;
- if (!currentURI.schemeIs("http") && !currentURI.schemeIs("https"))
- return;
-
- let url = currentURI.spec;
- // We need LOAD_FLAGS_BYPASS_CACHE here since we're changing the User-Agent
- // string, and servers typically don't use the Vary: User-Agent header, so
- // not doing this means that we'd get some of the previously cached content.
- let flags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE |
- Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
- if (this.originalURI && !this.originalURI.equals(currentURI)) {
- // We were redirected; reload the original URL
- url = this.originalURI.spec;
- }
-
- this.browser.docShell.loadURI(url, flags, null, null, null);
- },
-
- destroy: function() {
- if (!this.browser)
- return;
-
- this.browser.removeProgressListener(this.filter);
- this.filter.removeProgressListener(this);
- this.filter = null;
- this.browser.sessionHistory.removeSHistoryListener(this);
-
- this.browser.removeEventListener("DOMContentLoaded", this, true);
- this.browser.removeEventListener("DOMFormHasPassword", this, true);
- this.browser.removeEventListener("DOMInputPasswordAdded", this, true);
- this.browser.removeEventListener("DOMLinkAdded", this, true);
- this.browser.removeEventListener("DOMLinkChanged", this, true);
- this.browser.removeEventListener("DOMMetaAdded", this, false);
- this.browser.removeEventListener("DOMTitleChanged", this, true);
- this.browser.removeEventListener("DOMAudioPlaybackStarted", this, true);
- this.browser.removeEventListener("DOMAudioPlaybackStopped", this, true);
- this.browser.removeEventListener("DOMWindowClose", this, true);
- this.browser.removeEventListener("DOMWillOpenModalDialog", this, true);
- this.browser.removeEventListener("DOMAutoComplete", this, true);
- this.browser.removeEventListener("blur", this, true);
- this.browser.removeEventListener("pageshow", this, true);
- this.browser.removeEventListener("MozApplicationManifest", this, true);
- this.browser.removeEventListener("TabPreZombify", this, true);
-
- this.browser.removeEventListener("PluginBindingAttached", this, true, true);
- this.browser.removeEventListener("VideoBindingAttached", this, true, true);
- this.browser.removeEventListener("VideoBindingCast", this, true, true);
-
- Services.obs.removeObserver(this, "before-first-paint");
- Services.obs.removeObserver(this, "media-playback", false);
- Services.obs.removeObserver(this, "media-playback-resumed", false);
-
- // Make sure the previously selected panel remains selected. The selected panel of a deck is
- // not stable when panels are removed.
- let selectedPanel = BrowserApp.deck.selectedPanel;
- BrowserApp.deck.removeChild(this.browser);
- BrowserApp.deck.selectedPanel = selectedPanel;
-
- this.browser = null;
- },
-
- // This should be called to update the browser when the tab gets selected/unselected
- setActive: function setActive(aActive) {
- if (!this.browser || !this.browser.docShell)
- return;
-
- this.lastTouchedAt = Date.now();
-
- if (aActive) {
- this.browser.setAttribute("type", "content-primary");
- this.browser.focus();
- this.browser.docShellIsActive = true;
- Reader.updatePageAction(this);
- ExternalApps.updatePageAction(this.browser.currentURI, this.browser.contentDocument);
- } else {
- this.browser.setAttribute("type", "content-targetable");
- this.browser.docShellIsActive = false;
- this.browser.blur();
- }
- },
-
- getActive: function getActive() {
- return this.browser.docShellIsActive;
- },
-
- // These constants are used to prioritize high quality metadata over low quality data, so that
- // we can collect data as we find meta tags, and replace low quality metadata with higher quality
- // matches. For instance a msApplicationTile icon is a better tile image than an og:image tag.
- METADATA_GOOD_MATCH: 10,
- METADATA_NORMAL_MATCH: 1,
-
- addMetadata: function(type, value, quality = 1) {
- if (!this.metatags) {
- this.metatags = {
- url: this.browser.currentURI.specIgnoringRef
- };
- }
-
- if (type == "touchIconList") {
- if (!this.metatags['touchIconList']) {
- this.metatags['touchIconList'] = {};
- }
- this.metatags.touchIconList[quality] = value;
- } else if (!this.metatags[type] || this.metatags[type + "_quality"] < quality) {
- this.metatags[type] = value;
- this.metatags[type + "_quality"] = quality;
- }
- },
-
- sanitizeRelString: function(linkRel) {
- // Sanitize the rel string
- let list = [];
- if (linkRel) {
- list = linkRel.toLowerCase().split(/\s+/);
- let hash = {};
- list.forEach(function(value) { hash[value] = true; });
- list = [];
- for (let rel in hash)
- list.push("[" + rel + "]");
- }
- return list;
- },
-
- makeFaviconMessage: function(eventTarget) {
- // We want to get the largest icon size possible for our UI.
- let maxSize = 0;
-
- // We use the sizes attribute if available
- // see http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#rel-icon
- if (eventTarget.hasAttribute("sizes")) {
- let sizes = eventTarget.getAttribute("sizes").toLowerCase();
-
- if (sizes == "any") {
- // Since Java expects an integer, use -1 to represent icons with sizes="any"
- maxSize = -1;
- } else {
- let tokens = sizes.split(" ");
- tokens.forEach(function(token) {
- // TODO: check for invalid tokens
- let [w, h] = token.split("x");
- maxSize = Math.max(maxSize, Math.max(w, h));
- });
- }
- }
- return {
- type: "Link:Favicon",
- tabID: this.id,
- href: resolveGeckoURI(eventTarget.href),
- size: maxSize,
- mime: eventTarget.getAttribute("type") || ""
- };
- },
-
- makeFeedMessage: function(eventTarget, targetType) {
- try {
- // urlSecurityCeck will throw if things are not OK
- ContentAreaUtils.urlSecurityCheck(eventTarget.href,
- eventTarget.ownerDocument.nodePrincipal,
- Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
-
- if (!this.browser.feeds)
- this.browser.feeds = [];
-
- this.browser.feeds.push({
- href: eventTarget.href,
- title: eventTarget.title,
- type: targetType
- });
-
- return {
- type: "Link:Feed",
- tabID: this.id
- };
- } catch (e) {
- return null;
- }
- },
-
- sendOpenSearchMessage: function(eventTarget) {
- let type = eventTarget.type && eventTarget.type.toLowerCase();
- // Replace all starting or trailing spaces or spaces before "*;" globally w/ "".
- type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
-
- // Check that type matches opensearch.
- let isOpenSearch = (type == "application/opensearchdescription+xml");
- if (isOpenSearch && eventTarget.title && /^(?:https?|ftp):/i.test(eventTarget.href)) {
- Services.search.init(() => {
- let visibleEngines = Services.search.getVisibleEngines();
- // NOTE: Engines are currently identified by name, but this can be changed
- // when Engines are identified by URL (see bug 335102).
- if (visibleEngines.some(function(e) {
- return e.name == eventTarget.title;
- })) {
- // This engine is already present, do nothing.
- return null;
- }
-
- if (this.browser.engines) {
- // This engine has already been handled, do nothing.
- if (this.browser.engines.some(function(e) {
- return e.url == eventTarget.href;
- })) {
- return null;
- }
- } else {
- this.browser.engines = [];
- }
-
- // Get favicon.
- let iconURL = eventTarget.ownerDocument.documentURIObject.prePath + "/favicon.ico";
-
- let newEngine = {
- title: eventTarget.title,
- url: eventTarget.href,
- iconURL: iconURL
- };
-
- this.browser.engines.push(newEngine);
-
- // Don't send a message to display engines if we've already handled an engine.
- if (this.browser.engines.length > 1)
- return null;
-
- // Broadcast message that this tab contains search engines that should be visible.
- Messaging.sendRequest({
- type: "Link:OpenSearch",
- tabID: this.id,
- visible: true
- });
- });
- }
- },
-
- handleEvent: function(aEvent) {
- switch (aEvent.type) {
- case "DOMContentLoaded": {
- let target = aEvent.originalTarget;
-
- // ignore on frames and other documents
- if (target != this.browser.contentDocument)
- return;
-
- // Sample the background color of the page and pass it along. (This is used to draw the
- // checkerboard.) Right now we don't detect changes in the background color after this
- // event fires; it's not clear that doing so is worth the effort.
- var backgroundColor = null;
- try {
- let { contentDocument, contentWindow } = this.browser;
- let computedStyle = contentWindow.getComputedStyle(contentDocument.body);
- backgroundColor = computedStyle.backgroundColor;
- } catch (e) {
- // Ignore. Catching and ignoring exceptions here ensures that Talos succeeds.
- }
-
- let docURI = target.documentURI;
- let errorType = "";
- if (docURI.startsWith("about:certerror")) {
- errorType = "certerror";
- }
- else if (docURI.startsWith("about:blocked")) {
- errorType = "blocked";
- }
- else if (docURI.startsWith("about:neterror")) {
- let error = docURI.search(/e\=/);
- let duffUrl = docURI.search(/\&u\=/);
- let errorExtra = decodeURIComponent(docURI.slice(error + 2, duffUrl));
- // Here is a list of errorExtra types (et_*)
- // http://mxr.mozilla.org/mozilla-central/source/mobile/android/chrome/content/netError.xhtml#287
- UITelemetry.addEvent("neterror.1", "content", null, errorExtra);
- errorType = "neterror";
- }
-
- // Attach a listener to watch for "click" events bubbling up from error
- // pages and other similar page. This lets us fix bugs like 401575 which
- // require error page UI to do privileged things, without letting error
- // pages have any privilege themselves.
- if (docURI.startsWith("about:neterror")) {
- NetErrorHelper.attachToBrowser(this.browser);
- }
-
- Messaging.sendRequest({
- type: "DOMContentLoaded",
- tabID: this.id,
- bgColor: backgroundColor,
- errorType: errorType,
- metadata: this.metatags,
- });
-
- // Reset isSearch so that the userRequested term will be erased on next page load
- this.metatags = null;
-
- if (docURI.startsWith("about:certerror") || docURI.startsWith("about:blocked")) {
- this.browser.addEventListener("click", ErrorPageEventHandler, true);
- let listener = function() {
- this.browser.removeEventListener("click", ErrorPageEventHandler, true);
- this.browser.removeEventListener("pagehide", listener, true);
- }.bind(this);
-
- this.browser.addEventListener("pagehide", listener, true);
- }
-
- if (AppConstants.NIGHTLY_BUILD || AppConstants.MOZ_ANDROID_ACTIVITY_STREAM) {
- WebsiteMetadata.parseAsynchronously(this.browser.contentDocument);
- }
-
- break;
- }
-
- case "DOMFormHasPassword": {
- LoginManagerContent.onDOMFormHasPassword(aEvent,
- this.browser.contentWindow);
-
- // Send logins for this hostname to Java.
- let hostname = aEvent.target.baseURIObject.prePath;
- let foundLogins = Services.logins.findLogins({}, hostname, "", "");
- if (foundLogins.length > 0) {
- let displayHost = IdentityHandler.getEffectiveHost();
- let title = { text: displayHost, resource: hostname };
- let selectObj = { title: title, logins: foundLogins };
- Messaging.sendRequest({ type: "Doorhanger:Logins", data: selectObj });
- }
- break;
- }
-
- case "DOMInputPasswordAdded": {
- LoginManagerContent.onDOMInputPasswordAdded(aEvent,
- this.browser.contentWindow);
- }
-
- case "DOMMetaAdded":
- let target = aEvent.originalTarget;
- let browser = BrowserApp.getBrowserForDocument(target.ownerDocument);
-
- switch (target.name) {
- case "msapplication-TileImage":
- this.addMetadata("tileImage", browser.currentURI.resolve(target.content), this.METADATA_GOOD_MATCH);
- break;
- case "msapplication-TileColor":
- this.addMetadata("tileColor", target.content, this.METADATA_GOOD_MATCH);
- break;
- }
-
- break;
-
- case "DOMLinkAdded":
- case "DOMLinkChanged": {
- let jsonMessage = null;
- let target = aEvent.originalTarget;
- if (!target.href || target.disabled)
- return;
-
- // Ignore on frames and other documents
- if (target.ownerDocument != this.browser.contentDocument)
- return;
-
- // Sanitize rel link
- let list = this.sanitizeRelString(target.rel);
- if (list.indexOf("[icon]") != -1) {
- jsonMessage = this.makeFaviconMessage(target);
- } else if (list.indexOf("[apple-touch-icon]") != -1 ||
- list.indexOf("[apple-touch-icon-precomposed]") != -1) {
- jsonMessage = this.makeFaviconMessage(target);
- jsonMessage['type'] = 'Link:Touchicon';
- this.addMetadata("touchIconList", jsonMessage.href, jsonMessage.size);
- } else if (list.indexOf("[alternate]") != -1 && aEvent.type == "DOMLinkAdded") {
- let type = target.type.toLowerCase().replace(/^\s+|\s*(?:;.*)?$/g, "");
- let isFeed = (type == "application/rss+xml" || type == "application/atom+xml");
-
- if (!isFeed)
- return;
-
- jsonMessage = this.makeFeedMessage(target, type);
- } else if (list.indexOf("[search]") != -1 && aEvent.type == "DOMLinkAdded") {
- this.sendOpenSearchMessage(target);
- }
- if (!jsonMessage)
- return;
-
- Messaging.sendRequest(jsonMessage);
- break;
- }
-
- case "DOMTitleChanged": {
- if (!aEvent.isTrusted)
- return;
-
- // ignore on frames and other documents
- if (aEvent.originalTarget != this.browser.contentDocument)
- return;
-
- Messaging.sendRequest({
- type: "DOMTitleChanged",
- tabID: this.id,
- title: truncate(aEvent.target.title, MAX_TITLE_LENGTH)
- });
- break;
- }
-
- case "TabPreZombify": {
- if (!this.playingAudio) {
- return;
- }
- // Fall through to the DOMAudioPlayback events, so the
- // audio playback indicator gets reset upon zombification.
- }
- case "DOMAudioPlaybackStarted":
- case "DOMAudioPlaybackStopped": {
- if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") ||
- !aEvent.isTrusted) {
- return;
- }
-
- let browser = aEvent.originalTarget;
- if (browser != this.browser) {
- return;
- }
-
- this.playingAudio = aEvent.type === "DOMAudioPlaybackStarted";
-
- Messaging.sendRequest({
- type: "Tab:AudioPlayingChange",
- tabID: this.id,
- isAudioPlaying: this.playingAudio
- });
- return;
- }
-
- case "DOMWindowClose": {
- if (!aEvent.isTrusted)
- return;
-
- // Find the relevant tab, and close it from Java
- if (this.browser.contentWindow == aEvent.target) {
- aEvent.preventDefault();
-
- Messaging.sendRequest({
- type: "Tab:Close",
- tabID: this.id
- });
- }
- break;
- }
-
- case "DOMWillOpenModalDialog": {
- if (!aEvent.isTrusted)
- return;
-
- // We're about to open a modal dialog, make sure the opening
- // tab is brought to the front.
- let tab = BrowserApp.getTabForWindow(aEvent.target.top);
- BrowserApp.selectTab(tab);
- break;
- }
-
- case "DOMAutoComplete":
- case "blur": {
- LoginManagerContent.onUsernameInput(aEvent);
- break;
- }
-
- case "PluginBindingAttached": {
- PluginHelper.handlePluginBindingAttached(this, aEvent);
- break;
- }
-
- case "VideoBindingAttached": {
- CastingApps.handleVideoBindingAttached(this, aEvent);
- break;
- }
-
- case "VideoBindingCast": {
- CastingApps.handleVideoBindingCast(this, aEvent);
- break;
- }
-
- case "MozApplicationManifest": {
- OfflineApps.offlineAppRequested(aEvent.originalTarget.defaultView);
- break;
- }
-
- case "pageshow": {
- LoginManagerContent.onPageShow(aEvent, this.browser.contentWindow);
-
- // The rest of this only handles pageshow for the top-level document.
- if (aEvent.originalTarget.defaultView != this.browser.contentWindow)
- return;
-
- let target = aEvent.originalTarget;
- let docURI = target.documentURI;
- if (!docURI.startsWith("about:neterror") && !this.isSearch) {
- // If this wasn't an error page and the user isn't search, don't retain the typed entry
- this.userRequested = "";
- }
-
- Messaging.sendRequest({
- type: "Content:PageShow",
- tabID: this.id,
- userRequested: this.userRequested,
- fromCache: Tabs.useCache
- });
-
- this.isSearch = false;
-
- if (!aEvent.persisted && Services.prefs.getBoolPref("browser.ui.linkify.phone")) {
- if (!this._linkifier)
- this._linkifier = new Linkifier();
- this._linkifier.linkifyNumbers(this.browser.contentWindow.document);
- }
-
- // Update page actions for helper apps.
- let uri = this.browser.currentURI;
- if (BrowserApp.selectedTab == this) {
- if (ExternalApps.shouldCheckUri(uri)) {
- ExternalApps.updatePageAction(uri, this.browser.contentDocument);
- } else {
- ExternalApps.clearPageAction();
- }
- }
- }
- }
- },
-
- onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
- let contentWin = aWebProgress.DOMWindow;
- if (contentWin != contentWin.top)
- return;
-
- // Filter optimization: Only really send NETWORK state changes to Java listener
- if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
- if (AppConstants.NIGHTLY_BUILD && (aStateFlags & Ci.nsIWebProgressListener.STATE_START)) {
- Profiler.AddMarker("Load start: " + aRequest.QueryInterface(Ci.nsIChannel).originalURI.spec);
- } else if (AppConstants.NIGHTLY_BUILD && (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) && !aWebProgress.isLoadingDocument) {
- Profiler.AddMarker("Load stop: " + aRequest.QueryInterface(Ci.nsIChannel).originalURI.spec);
- }
-
- if ((aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) && aWebProgress.isLoadingDocument) {
- // We may receive a document stop event while a document is still loading
- // (such as when doing URI fixup). Don't notify Java UI in these cases.
- return;
- }
-
- // Clear page-specific opensearch engines and feeds for a new request.
- if (aStateFlags & Ci.nsIWebProgressListener.STATE_START && aRequest && aWebProgress.isTopLevel) {
- this.browser.engines = null;
- this.browser.feeds = null;
- }
-
- // true if the page loaded successfully (i.e., no 404s or other errors)
- let success = false;
- let uri = "";
- try {
- // Remember original URI for UA changes on redirected pages
- this.originalURI = aRequest.QueryInterface(Components.interfaces.nsIChannel).originalURI;
-
- if (this.originalURI != null)
- uri = this.originalURI.spec;
- } catch (e) { }
- try {
- success = aRequest.QueryInterface(Components.interfaces.nsIHttpChannel).requestSucceeded;
- } catch (e) {
- // If the request does not handle the nsIHttpChannel interface, use nsIRequest's success
- // status. Used for local files. See bug 948849.
- success = aRequest.status == 0;
- }
-
- // Check to see if we restoring the content from a previous presentation (session)
- // since there should be no real network activity
- let restoring = (aStateFlags & Ci.nsIWebProgressListener.STATE_RESTORING) > 0;
-
- let message = {
- type: "Content:StateChange",
- tabID: this.id,
- uri: truncate(uri, MAX_URI_LENGTH),
- state: aStateFlags,
- restoring: restoring,
- success: success
- };
- Messaging.sendRequest(message);
- }
- },
-
- onLocationChange: function(aWebProgress, aRequest, aLocationURI, aFlags) {
- let contentWin = aWebProgress.DOMWindow;
-
- // Browser webapps may load content inside iframes that can not reach across the app/frame boundary
- // i.e. even though the page is loaded in an iframe window.top != webapp
- // Make cure this window is a top level tab before moving on.
- if (BrowserApp.getBrowserForWindow(contentWin) == null)
- return;
-
- this._hostChanged = true;
-
- let fixedURI = aLocationURI;
- try {
- fixedURI = URIFixup.createExposableURI(aLocationURI);
- } catch (ex) { }
-
- // In restricted profiles, we refuse to let you open various urls.
- if (!ParentalControls.isAllowed(ParentalControls.BROWSE, fixedURI)) {
- aRequest.cancel(Cr.NS_BINDING_ABORTED);
-
- this.browser.docShell.displayLoadError(Cr.NS_ERROR_UNKNOWN_PROTOCOL, fixedURI, null);
- }
-
- let contentType = contentWin.document.contentType;
-
- // If fixedURI matches browser.lastURI, we assume this isn't a real location
- // change but rather a spurious addition like a wyciwyg URI prefix. See Bug 747883.
- // Note that we have to ensure fixedURI is not the same as aLocationURI so we
- // don't false-positive page reloads as spurious additions.
- let sameDocument = (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) != 0 ||
- ((this.browser.lastURI != null) && fixedURI.equals(this.browser.lastURI) && !fixedURI.equals(aLocationURI));
- this.browser.lastURI = fixedURI;
-
- // Let the reader logic know about same document changes because we won't get a DOMContentLoaded
- // or pageshow event, but we'll still want to update the reader view button to account for this change.
- // This mirrors the desktop logic in TabsProgressListener.
- if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
- this.browser.messageManager.sendAsyncMessage("Reader:PushState", {isArticle: this.browser.isArticle});
- }
-
- // Reset state of click-to-play plugin notifications.
- clearTimeout(this.pluginDoorhangerTimeout);
- this.pluginDoorhangerTimeout = null;
- this.shouldShowPluginDoorhanger = true;
- this.clickToPlayPluginsActivated = false;
-
- let documentURI = contentWin.document.documentURIObject.spec;
-
- // If reader mode, get the base domain for the original url.
- let strippedURI = this._stripAboutReaderURL(documentURI);
-
- // Borrowed from desktop Firefox: http://hg.mozilla.org/mozilla-central/annotate/72835344333f/browser/base/content/urlbarBindings.xml#l236
- let matchedURL = strippedURI.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
- let baseDomain = "";
- if (matchedURL) {
- var domain = "";
- [, , domain] = matchedURL;
-
- try {
- baseDomain = Services.eTLD.getBaseDomainFromHost(domain);
- if (!domain.endsWith(baseDomain)) {
- // getBaseDomainFromHost converts its resultant to ACE.
- let IDNService = Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService);
- baseDomain = IDNService.convertACEtoUTF8(baseDomain);
- }
- } catch (e) {}
- }
-
- // If we are navigating to a new location with a different host,
- // clear any URL origin that might have been pinned to this tab.
- let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
- let appOrigin = ss.getTabValue(this, "appOrigin");
- if (appOrigin) {
- let originHost = "";
- try {
- originHost = Services.io.newURI(appOrigin, null, null).host;
- } catch (e if (e.result == Cr.NS_ERROR_FAILURE)) {
- // NS_ERROR_FAILURE can be thrown by nsIURI.host if the URI scheme does not possess a host - in this case
- // we just act as if we have an empty host.
- }
- if (originHost != aLocationURI.host) {
- // Note: going 'back' will not make this tab pinned again
- ss.deleteTabValue(this, "appOrigin");
- }
- }
-
- // Update the page actions URI for helper apps.
- if (BrowserApp.selectedTab == this) {
- ExternalApps.updatePageActionUri(fixedURI);
- }
-
- let webNav = contentWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
-
- let message = {
- type: "Content:LocationChange",
- tabID: this.id,
- uri: truncate(fixedURI.spec, MAX_URI_LENGTH),
- userRequested: this.userRequested || "",
- baseDomain: baseDomain,
- contentType: (contentType ? contentType : ""),
- sameDocument: sameDocument,
-
- historyIndex: webNav.sessionHistory.index,
- historySize: webNav.sessionHistory.count,
- canGoBack: webNav.canGoBack,
- canGoForward: webNav.canGoForward,
- };
-
- Messaging.sendRequest(message);
-
- if (!sameDocument) {
- // XXX This code assumes that this is the earliest hook we have at which
- // browser.contentDocument is changed to the new document we're loading
- this.contentDocumentIsDisplayed = false;
- this.hasTouchListener = false;
- Services.obs.notifyObservers(this.browser, "Session:NotifyLocationChange", null);
- }
- },
-
- _stripAboutReaderURL: function (url) {
- return ReaderMode.getOriginalUrl(url) || url;
- },
-
- // Properties used to cache security state used to update the UI
- _state: null,
- _hostChanged: false, // onLocationChange will flip this bit
-
- onSecurityChange: function(aWebProgress, aRequest, aState) {
- // Don't need to do anything if the data we use to update the UI hasn't changed
- if (this._state == aState && !this._hostChanged)
- return;
-
- this._state = aState;
- this._hostChanged = false;
-
- let identity = IdentityHandler.checkIdentity(aState, this.browser);
-
- let message = {
- type: "Content:SecurityChange",
- tabID: this.id,
- identity: identity
- };
-
- Messaging.sendRequest(message);
- },
-
- onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {
- // Note: aWebProgess and aRequest will be NULL since we are filtering webprogress
- // notifications using nsBrowserStatusFilter.
- },
-
- onStatusChange: function(aBrowser, aWebProgress, aRequest, aStatus, aMessage) {
- // Note: aWebProgess and aRequest will be NULL since we are filtering webprogress
- // notifications using nsBrowserStatusFilter.
- },
-
- _getGeckoZoom: function() {
- let res = {};
- let cwu = this.browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
- cwu.getResolution(res);
- let zoom = res.value * window.devicePixelRatio;
- return zoom;
- },
-
- saveSessionZoom: function(aZoom) {
- let cwu = this.browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
- cwu.setResolutionAndScaleTo(aZoom / window.devicePixelRatio);
- },
-
- restoredSessionZoom: function() {
- let cwu = this.browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
-
- if (this._restoreZoom && cwu.isResolutionSet) {
- return this._getGeckoZoom();
- }
- return null;
- },
-
- _updateZoomFromHistoryEvent: function(aHistoryEventName) {
- // Restore zoom only when moving in session history, not for new page loads.
- this._restoreZoom = aHistoryEventName !== "New";
- },
-
- OnHistoryNewEntry: function(aUri) {
- this._updateZoomFromHistoryEvent("New");
- },
-
- OnHistoryGoBack: function(aUri) {
- this._updateZoomFromHistoryEvent("Back");
- return true;
- },
-
- OnHistoryGoForward: function(aUri) {
- this._updateZoomFromHistoryEvent("Forward");
- return true;
- },
-
- OnHistoryReload: function(aUri, aFlags) {
- // we don't do anything with this, so don't propagate it
- // for now anyway
- return true;
- },
-
- OnHistoryGotoIndex: function(aIndex, aUri) {
- this._updateZoomFromHistoryEvent("Goto");
- return true;
- },
-
- OnHistoryPurge: function(aNumEntries) {
- this._updateZoomFromHistoryEvent("Purge");
- return true;
- },
-
- OnHistoryReplaceEntry: function(aIndex) {
- // we don't do anything with this, so don't propogate it
- // for now anyway.
- },
-
- ShouldNotifyMediaPlaybackChange: function(activeState) {
- // If the media is active, we would check it's duration, because we don't
- // want to show the media control interface for the short sound which
- // duration is smaller than the threshold. The basic unit is second.
- // Note : the streaming format's duration is infinite.
- if (activeState === "inactive") {
- return true;
- }
-
- const mediaDurationThreshold = 1.0;
-
- let audioElements = this.browser.contentDocument.getElementsByTagName("audio");
- for each (let audio in audioElements) {
- if (!audio.paused && audio.duration < mediaDurationThreshold) {
- return false;
- }
- }
-
- let videoElements = this.browser.contentDocument.getElementsByTagName("video");
- for each (let video in videoElements) {
- if (!video.paused && video.duration < mediaDurationThreshold) {
- return false;
- }
- }
-
- return true;
- },
-
- observe: function(aSubject, aTopic, aData) {
- switch (aTopic) {
- case "before-first-paint":
- // Is it on the top level?
- let contentDocument = aSubject;
- if (contentDocument == this.browser.contentDocument) {
- if (BrowserApp.selectedTab == this) {
- BrowserApp.contentDocumentChanged();
- }
- this.contentDocumentIsDisplayed = true;
-
- if (contentDocument instanceof Ci.nsIImageDocument) {
- contentDocument.shrinkToFit();
- }
- }
- break;
-
- case "media-playback":
- case "media-playback-resumed":
- if (!aSubject) {
- return;
- }
-
- let winId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
- if (this.browser.outerWindowID != winId) {
- return;
- }
-
- if (!this.ShouldNotifyMediaPlaybackChange(aData)) {
- return;
- }
-
- let status;
- if (aTopic == "media-playback") {
- status = (aData === "inactive") ? "end" : "start";
- } else if (aTopic == "media-playback-resumed") {
- status = "resume";
- }
-
- Messaging.sendRequest({
- type: "Tab:MediaPlaybackChange",
- tabID: this.id,
- status: status
- });
- break;
- }
- },
-
- // nsIBrowserTab
- get window() {
- if (!this.browser)
- return null;
- return this.browser.contentWindow;
- },
-
- get scale() {
- return this._zoom;
- },
-
- QueryInterface: XPCOMUtils.generateQI([
- Ci.nsIWebProgressListener,
- Ci.nsISHistoryListener,
- Ci.nsIObserver,
- Ci.nsISupportsWeakReference,
- Ci.nsIBrowserTab
- ])
-};
-
-var BrowserEventHandler = {
- init: function init() {
- this._clickInZoomedView = false;
- Services.obs.addObserver(this, "Gesture:SingleTap", false);
- Services.obs.addObserver(this, "Gesture:ClickInZoomedView", false);
-
- BrowserApp.deck.addEventListener("touchend", this, true);
-
- BrowserApp.deck.addEventListener("DOMUpdatePageReport", PopupBlockerObserver.onUpdatePageReport, false);
- BrowserApp.deck.addEventListener("MozMouseHittest", this, true);
- BrowserApp.deck.addEventListener("OpenMediaWithExternalApp", this, true);
-
- InitLater(() => BrowserApp.deck.addEventListener("click", InputWidgetHelper, true));
- InitLater(() => BrowserApp.deck.addEventListener("click", SelectHelper, true));
-
- // ReaderViews support backPress listeners.
- Messaging.addListener(() => {
- return Reader.onBackPress(BrowserApp.selectedTab.id);
- }, "Browser:OnBackPressed");
- },
-
- handleEvent: function(aEvent) {
- switch (aEvent.type) {
- case 'touchend':
- if (this._inCluster) {
- aEvent.preventDefault();
- }
- break;
- case 'MozMouseHittest':
- this._handleRetargetedTouchStart(aEvent);
- break;
- case 'OpenMediaWithExternalApp': {
- let mediaSrc = aEvent.target.currentSrc || aEvent.target.src;
- let uuid = uuidgen.generateUUID().toString();
- Services.androidBridge.handleGeckoMessage({
- type: "Video:Play",
- uri: mediaSrc,
- uuid: uuid
- });
- break;
- }
- }
- },
-
- _handleRetargetedTouchStart: function(aEvent) {
- // we should only get this called just after a new touchstart with a single
- // touch point.
- if (!BrowserApp.isBrowserContentDocumentDisplayed() || aEvent.defaultPrevented) {
- return;
- }
-
- let target = aEvent.target;
- if (!target) {
- return;
- }
-
- this._inCluster = aEvent.hitCluster;
- if (this._inCluster) {
- return; // No highlight for a cluster of links
- }
-
- let uri = this._getLinkURI(target);
- if (uri) {
- try {
- Services.io.QueryInterface(Ci.nsISpeculativeConnect).speculativeConnect(uri, null);
- } catch (e) {}
- }
- this._doTapHighlight(target);
- },
-
- _getLinkURI: function(aElement) {
- if (aElement.nodeType == Ci.nsIDOMNode.ELEMENT_NODE &&
- ((aElement instanceof Ci.nsIDOMHTMLAnchorElement && aElement.href) ||
- (aElement instanceof Ci.nsIDOMHTMLAreaElement && aElement.href))) {
- try {
- return Services.io.newURI(aElement.href, null, null);
- } catch (e) {}
- }
- return null;
- },
-
- observe: function(aSubject, aTopic, aData) {
- // the remaining events are all dependent on the browser content document being the
- // same as the browser displayed document. if they are not the same, we should ignore
- // the event.
- if (BrowserApp.isBrowserContentDocumentDisplayed()) {
- this.handleUserEvent(aTopic, aData);
- }
- },
-
- handleUserEvent: function(aTopic, aData) {
- switch (aTopic) {
-
- case "Gesture:ClickInZoomedView":
- this._clickInZoomedView = true;
- break;
-
- case "Gesture:SingleTap": {
- let focusedElement = BrowserApp.getFocusedInput(BrowserApp.selectedBrowser);
- let data = JSON.parse(aData);
- let {x, y} = data;
-
- if (this._inCluster && this._clickInZoomedView != true) {
- // If there is a focused element, the display of the zoomed view won't remove the focus.
- // In this case, the form assistant linked to the focused element will never be closed.
- // To avoid this situation, the focus is moved and the form assistant is closed.
- if (focusedElement) {
- try {
- Services.focus.moveFocus(BrowserApp.selectedBrowser.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0);
- } catch(e) {
- Cu.reportError(e);
- }
- Messaging.sendRequest({ type: "FormAssist:Hide" });
- }
- this._clusterClicked(x, y);
- } else {
- if (this._clickInZoomedView != true) {
- this._closeZoomedView();
- }
- }
- this._clickInZoomedView = false;
- this._cancelTapHighlight();
- break;
- }
-
- default:
- dump('BrowserEventHandler.handleUserEvent: unexpected topic "' + aTopic + '"');
- break;
- }
- },
-
- _closeZoomedView: function() {
- Messaging.sendRequest({
- type: "Gesture:CloseZoomedView"
- });
- },
-
- _clusterClicked: function(aX, aY) {
- Messaging.sendRequest({
- type: "Gesture:clusteredLinksClicked",
- clickPosition: {
- x: aX,
- y: aY
- }
- });
- },
-
- _highlightElement: null,
-
- _doTapHighlight: function _doTapHighlight(aElement) {
- this._highlightElement = aElement;
- },
-
- _cancelTapHighlight: function _cancelTapHighlight() {
- if (!this._highlightElement)
- return;
-
- this._highlightElement = null;
- }
-};
-
-const ElementTouchHelper = {
- getBoundingContentRect: function(aElement) {
- if (!aElement)
- return {x: 0, y: 0, w: 0, h: 0};
-
- let document = aElement.ownerDocument;
- while (document.defaultView.frameElement)
- document = document.defaultView.frameElement.ownerDocument;
-
- let cwu = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
- let scrollX = {}, scrollY = {};
- cwu.getScrollXY(false, scrollX, scrollY);
-
- let r = aElement.getBoundingClientRect();
-
- // step out of iframes and frames, offsetting scroll values
- for (let frame = aElement.ownerDocument.defaultView; frame.frameElement && frame != content; frame = frame.parent) {
- // adjust client coordinates' origin to be top left of iframe viewport
- let rect = frame.frameElement.getBoundingClientRect();
- let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth;
- let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth;
- scrollX.value += rect.left + parseInt(left);
- scrollY.value += rect.top + parseInt(top);
- }
-
- return {x: r.left + scrollX.value,
- y: r.top + scrollY.value,
- w: r.width,
- h: r.height };
- }
-};
-
-var ErrorPageEventHandler = {
- handleEvent: function(aEvent) {
- switch (aEvent.type) {
- case "click": {
- // Don't trust synthetic events
- if (!aEvent.isTrusted)
- return;
-
- let target = aEvent.originalTarget;
- let errorDoc = target.ownerDocument;
-
- // If the event came from an ssl error page, it is probably either the "Add
- // Exception…" or "Get me out of here!" button
- if (errorDoc.documentURI.startsWith("about:certerror?e=nssBadCert")) {
- let perm = errorDoc.getElementById("permanentExceptionButton");
- let temp = errorDoc.getElementById("temporaryExceptionButton");
- if (target == temp || target == perm) {
- // Handle setting an cert exception and reloading the page
- try {
- // Add a new SSL exception for this URL
- let uri = Services.io.newURI(errorDoc.location.href, null, null);
- let sslExceptions = new SSLExceptions();
-
- if (target == perm)
- sslExceptions.addPermanentException(uri, errorDoc.defaultView);
- else
- sslExceptions.addTemporaryException(uri, errorDoc.defaultView);
- } catch (e) {
- dump("Failed to set cert exception: " + e + "\n");
- }
- errorDoc.location.reload();
- } else if (target == errorDoc.getElementById("getMeOutOfHereButton")) {
- errorDoc.location = "about:home";
- }
- } else if (errorDoc.documentURI.startsWith("about:blocked")) {
- // The event came from a button on a malware/phishing block page
- // First check whether it's malware, phishing or unwanted, so that we
- // can use the right strings/links
- let isIframe = (errorDoc.defaultView.parent === errorDoc.defaultView);
- let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
-
- if (target == errorDoc.getElementById("getMeOutButton")) {
- errorDoc.location = "about:home";
- } else if (target == errorDoc.getElementById("reportButton")) {
- // This is the "Why is this site blocked" button. We redirect
- // to the generic page describing phishing/malware protection.
- let url = Services.urlFormatter.formatURLPref("app.support.baseURL");
- BrowserApp.selectedBrowser.loadURI(url + "phishing-malware");
- } else if (target == errorDoc.getElementById("ignoreWarningButton") &&
- Services.prefs.getBoolPref("browser.safebrowsing.allowOverride")) {
- // Allow users to override and continue through to the site,
- let webNav = BrowserApp.selectedBrowser.docShell.QueryInterface(Ci.nsIWebNavigation);
- let location = BrowserApp.selectedBrowser.contentWindow.location;
- webNav.loadURI(location, Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER, null, null, null);
-
- // ....but add a notify bar as a reminder, so that they don't lose
- // track after, e.g., tab switching.
- NativeWindow.doorhanger.show(Strings.browser.GetStringFromName("safeBrowsingDoorhanger"), "safebrowsing-warning", [], BrowserApp.selectedTab.id);
- }
- }
- break;
- }
- }
- }
-};
-
-var FormAssistant = {
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
-
- // Used to keep track of the element that corresponds to the current
- // autocomplete suggestions
- _currentInputElement: null,
-
- // The value of the currently focused input
- _currentInputValue: null,
-
- // Whether we're in the middle of an autocomplete
- _doingAutocomplete: false,
-
- // Keep track of whether or not an invalid form has been submitted
- _invalidSubmit: false,
-
- init: function() {
- Services.obs.addObserver(this, "FormAssist:AutoComplete", false);
- Services.obs.addObserver(this, "FormAssist:Hidden", false);
- Services.obs.addObserver(this, "FormAssist:Remove", false);
- Services.obs.addObserver(this, "invalidformsubmit", false);
- Services.obs.addObserver(this, "PanZoom:StateChange", false);
-
- // We need to use a capturing listener for focus events
- BrowserApp.deck.addEventListener("focus", this, true);
- BrowserApp.deck.addEventListener("blur", this, true);
- BrowserApp.deck.addEventListener("click", this, true);
- BrowserApp.deck.addEventListener("input", this, false);
- BrowserApp.deck.addEventListener("pageshow", this, false);
- },
-
- observe: function(aSubject, aTopic, aData) {
- switch (aTopic) {
- case "PanZoom:StateChange":
- // If the user is just touching the screen and we haven't entered a pan or zoom state yet do nothing
- if (aData == "TOUCHING" || aData == "WAITING_LISTENERS")
- break;
- if (aData == "NOTHING") {
- // only look for input elements, not contentEditable or multiline text areas
- let focused = BrowserApp.getFocusedInput(BrowserApp.selectedBrowser, true);
- if (!focused)
- break;
-
- if (this._showValidationMessage(focused))
- break;
- let checkResultsClick = hasResults => {
- if (!hasResults) {
- this._hideFormAssistPopup();
- }
- };
- this._showAutoCompleteSuggestions(focused, checkResultsClick);
- } else {
- // temporarily hide the form assist popup while we're panning or zooming the page
- this._hideFormAssistPopup();
- }
- break;
- case "FormAssist:AutoComplete":
- if (!this._currentInputElement)
- break;
-
- let editableElement = this._currentInputElement.QueryInterface(Ci.nsIDOMNSEditableElement);
-
- this._doingAutocomplete = true;
-
- // If we have an active composition string, commit it before sending
- // the autocomplete event with the text that will replace it.
- try {
- let imeEditor = editableElement.editor.QueryInterface(Ci.nsIEditorIMESupport);
- if (imeEditor.composing)
- imeEditor.forceCompositionEnd();
- } catch (e) {}
-
- editableElement.setUserInput(aData);
- this._currentInputValue = aData;
-
- let event = this._currentInputElement.ownerDocument.createEvent("Events");
- event.initEvent("DOMAutoComplete", true, true);
- this._currentInputElement.dispatchEvent(event);
-
- this._doingAutocomplete = false;
-
- break;
-
- case "FormAssist:Hidden":
- this._currentInputElement = null;
- break;
-
- case "FormAssist:Remove":
- if (!this._currentInputElement) {
- break;
- }
-
- FormHistory.update({
- op: "remove",
- fieldname: this._currentInputElement.name,
- value: aData
- });
- break;
- }
- },
-
- notifyInvalidSubmit: function notifyInvalidSubmit(aFormElement, aInvalidElements) {
- if (!aInvalidElements.length)
- return;
-
- // Ignore this notificaiton if the current tab doesn't contain the invalid element
- let currentElement = aInvalidElements.queryElementAt(0, Ci.nsISupports);
- if (BrowserApp.selectedBrowser.contentDocument !=
- currentElement.ownerDocument.defaultView.top.document)
- return;
-
- this._invalidSubmit = true;
-
- // Our focus listener will show the element's validation message
- currentElement.focus();
- },
-
- handleEvent: function(aEvent) {
- switch (aEvent.type) {
- case "focus": {
- let currentElement = aEvent.target;
-
- // Only show a validation message on focus.
- this._showValidationMessage(currentElement);
- break;
- }
-
- case "blur": {
- this._currentInputValue = null;
- break;
- }
-
- case "click": {
- let currentElement = aEvent.target;
-
- // Prioritize a form validation message over autocomplete suggestions
- // when the element is first focused (a form validation message will
- // only be available if an invalid form was submitted)
- if (this._showValidationMessage(currentElement))
- break;
-
- let checkResultsClick = hasResults => {
- if (!hasResults) {
- this._hideFormAssistPopup();
- }
- };
-
- this._showAutoCompleteSuggestions(currentElement, checkResultsClick);
- break;
- }
-
- case "input": {
- let currentElement = aEvent.target;
-
- // If this element isn't focused, we're already in middle of an
- // autocomplete, or its value hasn't changed, don't show the
- // autocomplete popup.
- if (currentElement !== BrowserApp.getFocusedInput(BrowserApp.selectedBrowser) ||
- this._doingAutocomplete ||
- currentElement.value === this._currentInputValue) {
- break;
- }
-
- this._currentInputValue = currentElement.value;
-
- // Since we can only show one popup at a time, prioritze autocomplete
- // suggestions over a form validation message
- let checkResultsInput = hasResults => {
- if (hasResults)
- return;
-
- if (this._showValidationMessage(currentElement))
- return;
-
- // If we're not showing autocomplete suggestions, hide the form assist popup
- this._hideFormAssistPopup();
- };
-
- this._showAutoCompleteSuggestions(currentElement, checkResultsInput);
- break;
- }
-
- // Reset invalid submit state on each pageshow
- case "pageshow": {
- if (!this._invalidSubmit)
- return;
-
- let selectedBrowser = BrowserApp.selectedBrowser;
- if (selectedBrowser) {
- let selectedDocument = selectedBrowser.contentDocument;
- let target = aEvent.originalTarget;
- if (target == selectedDocument || target.ownerDocument == selectedDocument)
- this._invalidSubmit = false;
- }
- break;
- }
- }
- },
-
- // We only want to show autocomplete suggestions for certain elements
- _isAutoComplete: function _isAutoComplete(aElement) {
- if (!(aElement instanceof HTMLInputElement) || aElement.readOnly || aElement.disabled ||
- (aElement.getAttribute("type") == "password") ||
- (aElement.hasAttribute("autocomplete") &&
- aElement.getAttribute("autocomplete").toLowerCase() == "off"))
- return false;
-
- return true;
- },
-
- // Retrieves autocomplete suggestions for an element from the form autocomplete service.
- // aCallback(array_of_suggestions) is called when results are available.
- _getAutoCompleteSuggestions: function _getAutoCompleteSuggestions(aSearchString, aElement, aCallback) {
- // Cache the form autocomplete service for future use
- if (!this._formAutoCompleteService) {
- this._formAutoCompleteService = Cc["@mozilla.org/satchel/form-autocomplete;1"]
- .getService(Ci.nsIFormAutoComplete);
- }
-
- let resultsAvailable = function (results) {
- let suggestions = [];
- for (let i = 0; i < results.matchCount; i++) {
- let value = results.getValueAt(i);
-
- // Do not show the value if it is the current one in the input field
- if (value == aSearchString)
- continue;
-
- // Supply a label and value, since they can differ for datalist suggestions
- suggestions.push({ label: value, value: value });
- }
- aCallback(suggestions);
- };
-
- this._formAutoCompleteService.autoCompleteSearchAsync(aElement.name || aElement.id,
- aSearchString, aElement, null,
- null, resultsAvailable);
- },
-
- /**
- * (Copied from mobile/xul/chrome/content/forms.js)
- * This function is similar to getListSuggestions from
- * components/satchel/src/nsInputListAutoComplete.js but sadly this one is
- * used by the autocomplete.xml binding which is not in used in fennec
- */
- _getListSuggestions: function _getListSuggestions(aElement) {
- if (!(aElement instanceof HTMLInputElement) || !aElement.list)
- return [];
-
- let suggestions = [];
- let filter = !aElement.hasAttribute("mozNoFilter");
- let lowerFieldValue = aElement.value.toLowerCase();
-
- let options = aElement.list.options;
- let length = options.length;
- for (let i = 0; i < length; i++) {
- let item = options.item(i);
-
- let label = item.value;
- if (item.label)
- label = item.label;
- else if (item.text)
- label = item.text;
-
- if (filter && !(label.toLowerCase().includes(lowerFieldValue)) )
- continue;
- suggestions.push({ label: label, value: item.value });
- }
-
- return suggestions;
- },
-
- // Retrieves autocomplete suggestions for an element from the form autocomplete service
- // and sends the suggestions to the Java UI, along with element position data. As
- // autocomplete queries are asynchronous, calls aCallback when done with a true
- // argument if results were found and false if no results were found.
- _showAutoCompleteSuggestions: function _showAutoCompleteSuggestions(aElement, aCallback) {
- if (!this._isAutoComplete(aElement)) {
- aCallback(false);
- return;
- }
- if (this._isDisabledElement(aElement)) {
- aCallback(false);
- return;
- }
-
- let isEmpty = (aElement.value.length === 0);
-
- let resultsAvailable = autoCompleteSuggestions => {
- // On desktop, we show datalist suggestions below autocomplete suggestions,
- // without duplicates removed.
- let listSuggestions = this._getListSuggestions(aElement);
- let suggestions = autoCompleteSuggestions.concat(listSuggestions);
-
- // Return false if there are no suggestions to show
- if (!suggestions.length) {
- aCallback(false);
- return;
- }
-
- Messaging.sendRequest({
- type: "FormAssist:AutoComplete",
- suggestions: suggestions,
- rect: ElementTouchHelper.getBoundingContentRect(aElement),
- isEmpty: isEmpty,
- });
-
- // Keep track of input element so we can fill it in if the user
- // selects an autocomplete suggestion
- this._currentInputElement = aElement;
- aCallback(true);
- };
-
- this._getAutoCompleteSuggestions(aElement.value, aElement, resultsAvailable);
- },
-
- // Only show a validation message if the user submitted an invalid form,
- // there's a non-empty message string, and the element is the correct type
- _isValidateable: function _isValidateable(aElement) {
- if (!this._invalidSubmit ||
- !aElement.validationMessage ||
- !(aElement instanceof HTMLInputElement ||
- aElement instanceof HTMLTextAreaElement ||
- aElement instanceof HTMLSelectElement ||
- aElement instanceof HTMLButtonElement))
- return false;
-
- return true;
- },
-
- // Sends a validation message and position data for an element to the Java UI.
- // Returns true if there's a validation message to show, false otherwise.
- _showValidationMessage: function _sendValidationMessage(aElement) {
- if (!this._isValidateable(aElement))
- return false;
-
- Messaging.sendRequest({
- type: "FormAssist:ValidationMessage",
- validationMessage: aElement.validationMessage,
- rect: ElementTouchHelper.getBoundingContentRect(aElement)
- });
-
- return true;
- },
-
- _hideFormAssistPopup: function _hideFormAssistPopup() {
- Messaging.sendRequest({ type: "FormAssist:Hide" });
- },
-
- _isDisabledElement : function(aElement) {
- let currentElement = aElement;
- while (currentElement) {
- if(currentElement.disabled)
- return true;
-
- currentElement = currentElement.parentElement;
- }
- return false;
- }
-};
-
-var XPInstallObserver = {
- init: function() {
- Services.obs.addObserver(this, "addon-install-origin-blocked", false);
- Services.obs.addObserver(this, "addon-install-disabled", false);
- Services.obs.addObserver(this, "addon-install-blocked", false);
- Services.obs.addObserver(this, "addon-install-started", false);
- Services.obs.addObserver(this, "xpi-signature-changed", false);
- Services.obs.addObserver(this, "browser-delayed-startup-finished", false);
-
- AddonManager.addInstallListener(this);
- },
-
- observe: function(aSubject, aTopic, aData) {
- let installInfo, tab, host;
- if (aSubject && aSubject instanceof Ci.amIWebInstallInfo) {
- installInfo = aSubject;
- tab = BrowserApp.getTabForBrowser(installInfo.browser);
- if (installInfo.originatingURI) {
- host = installInfo.originatingURI.host;
- }
- }
-
- let strings = Strings.browser;
- let brandShortName = Strings.brand.GetStringFromName("brandShortName");
-
- switch (aTopic) {
- case "addon-install-started":
- Snackbars.show(strings.GetStringFromName("alertAddonsDownloading"), Snackbars.LENGTH_LONG);
- break;
- case "addon-install-disabled": {
- if (!tab)
- return;
-
- let enabled = true;
- try {
- enabled = Services.prefs.getBoolPref("xpinstall.enabled");
- } catch (e) {}
-
- let buttons, message, callback;
- if (!enabled) {
- message = strings.GetStringFromName("xpinstallDisabledMessageLocked");
- buttons = [strings.GetStringFromName("unsignedAddonsDisabled.dismiss")];
- callback: (data) => {};
- } else {
- message = strings.formatStringFromName("xpinstallDisabledMessage2", [brandShortName, host], 2);
- buttons = [
- strings.GetStringFromName("xpinstallDisabledButton"),
- strings.GetStringFromName("unsignedAddonsDisabled.dismiss")
- ];
- callback: (data) => {
- if (data.button === 1) {
- Services.prefs.setBoolPref("xpinstall.enabled", true)
- }
- };
- }
-
- new Prompt({
- title: Strings.browser.GetStringFromName("addonError.titleError"),
- message: message,
- buttons: buttons
- }).show(callback);
- break;
- }
- case "addon-install-blocked": {
- if (!tab)
- return;
-
- let message;
- if (host) {
- // We have a host which asked for the install.
- message = strings.formatStringFromName("xpinstallPromptWarning2", [brandShortName, host], 2);
- } else {
- // Without a host we address the add-on as the initiator of the install.
- let addon = null;
- if (installInfo.installs.length > 0) {
- addon = installInfo.installs[0].name;
- }
- if (addon) {
- // We have an addon name, show the regular message.
- message = strings.formatStringFromName("xpinstallPromptWarningLocal", [brandShortName, addon], 2);
- } else {
- // We don't have an addon name, show an alternative message.
- message = strings.formatStringFromName("xpinstallPromptWarningDirect", [brandShortName], 1);
- }
- }
-
- let buttons = [
- strings.GetStringFromName("xpinstallPromptAllowButton"),
- strings.GetStringFromName("unsignedAddonsDisabled.dismiss")
- ];
- new Prompt({
- title: Strings.browser.GetStringFromName("addonError.titleBlocked"),
- message: message,
- buttons: buttons
- }).show((data) => {
- if (data.button === 0) {
- // Kick off the install
- installInfo.install();
- }
- });
- break;
- }
- case "addon-install-origin-blocked": {
- if (!tab)
- return;
-
- new Prompt({
- title: Strings.browser.GetStringFromName("addonError.titleBlocked"),
- message: strings.formatStringFromName("xpinstallPromptWarningDirect", [brandShortName], 1),
- buttons: [strings.GetStringFromName("unsignedAddonsDisabled.dismiss")]
- }).show((data) => {});
- break;
- }
- case "xpi-signature-changed": {
- if (JSON.parse(aData).disabled.length) {
- this._notifyUnsignedAddonsDisabled();
- }
- break;
- }
- case "browser-delayed-startup-finished": {
- let disabledAddons = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_DISABLED);
- for (let id of disabledAddons) {
- if (AddonManager.getAddonByID(id).signedState <= AddonManager.SIGNEDSTATE_MISSING) {
- this._notifyUnsignedAddonsDisabled();
- break;
- }
- }
- break;
- }
- }
- },
-
- _notifyUnsignedAddonsDisabled: function() {
- new Prompt({
- window: window,
- title: Strings.browser.GetStringFromName("unsignedAddonsDisabled.title"),
- message: Strings.browser.GetStringFromName("unsignedAddonsDisabled.message"),
- buttons: [
- Strings.browser.GetStringFromName("unsignedAddonsDisabled.viewAddons"),
- Strings.browser.GetStringFromName("unsignedAddonsDisabled.dismiss")
- ]
- }).show((data) => {
- if (data.button === 0) {
- // TODO: Open about:addons to show only unsigned add-ons?
- BrowserApp.selectOrAddTab("about:addons", { parentId: BrowserApp.selectedTab.id });
- }
- });
- },
-
- onInstallEnded: function(aInstall, aAddon) {
- // Don't create a notification for distribution add-ons.
- if (Distribution.pendingAddonInstalls.has(aInstall)) {
- Distribution.pendingAddonInstalls.delete(aInstall);
- return;
- }
-
- let needsRestart = false;
- if (aInstall.existingAddon && (aInstall.existingAddon.pendingOperations & AddonManager.PENDING_UPGRADE))
- needsRestart = true;
- else if (aAddon.pendingOperations & AddonManager.PENDING_INSTALL)
- needsRestart = true;
-
- if (needsRestart) {
- this.showRestartPrompt();
- } else {
- // Display completion message for new installs or updates not done Automatically
- if (!aInstall.existingAddon || !AddonManager.shouldAutoUpdate(aInstall.existingAddon)) {
- let message = Strings.browser.GetStringFromName("alertAddonsInstalledNoRestart.message");
- Snackbars.show(message, Snackbars.LENGTH_LONG, {
- action: {
- label: Strings.browser.GetStringFromName("alertAddonsInstalledNoRestart.action2"),
- callback: () => {
- UITelemetry.addEvent("show.1", "toast", null, "addons");
- BrowserApp.selectOrAddTab("about:addons", { parentId: BrowserApp.selectedTab.id });
- },
- }
- });
- }
- }
- },
-
- onInstallFailed: function(aInstall) {
- this._showErrorMessage(aInstall);
- },
-
- onDownloadProgress: function(aInstall) {},
-
- onDownloadFailed: function(aInstall) {
- this._showErrorMessage(aInstall);
- },
-
- onDownloadCancelled: function(aInstall) {},
-
- _showErrorMessage: function(aInstall) {
- // Don't create a notification for distribution add-ons.
- if (Distribution.pendingAddonInstalls.has(aInstall)) {
- Cu.reportError("Error installing distribution add-on: " + aInstall.addon.id);
- Distribution.pendingAddonInstalls.delete(aInstall);
- return;
- }
-
- let host = (aInstall.originatingURI instanceof Ci.nsIStandardURL) && aInstall.originatingURI.host;
- if (!host) {
- host = (aInstall.sourceURI instanceof Ci.nsIStandardURL) && aInstall.sourceURI.host;
- }
-
- let error = (host || aInstall.error == 0) ? "addonError" : "addonLocalError";
- if (aInstall.error < 0) {
- error += aInstall.error;
- } else if (aInstall.addon && aInstall.addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) {
- error += "Blocklisted";
- } else {
- error += "Incompatible";
- }
-
- let msg = Strings.browser.GetStringFromName(error);
- // TODO: formatStringFromName
- msg = msg.replace("#1", aInstall.name);
- if (host) {
- msg = msg.replace("#2", host);
- }
- msg = msg.replace("#3", Strings.brand.GetStringFromName("brandShortName"));
- msg = msg.replace("#4", Services.appinfo.version);
-
- if (aInstall.error == AddonManager.ERROR_SIGNEDSTATE_REQUIRED) {
- new Prompt({
- window: window,
- title: Strings.browser.GetStringFromName("addonError.titleBlocked"),
- message: msg,
- buttons: [Strings.browser.GetStringFromName("addonError.learnMore")]
- }).show((data) => {
- if (data.button === 0) {
- let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "unsigned-addons";
- BrowserApp.addTab(url, { parentId: BrowserApp.selectedTab.id });
- }
- });
- } else {
- Services.prompt.alert(null, Strings.browser.GetStringFromName("addonError.titleError"), msg);
- }
- },
-
- showRestartPrompt: function() {
- let buttons = [{
- label: Strings.browser.GetStringFromName("notificationRestart.button"),
- callback: function() {
- // Notify all windows that an application quit has been requested
- let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
- Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
-
- // If nothing aborted, quit the app
- if (cancelQuit.data == false) {
- Services.obs.notifyObservers(null, "quit-application-proceeding", null);
- let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
- appStartup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
- }
- },
- positive: true
- }];
-
- let message = Strings.browser.GetStringFromName("notificationRestart.normal");
- NativeWindow.doorhanger.show(message, "addon-app-restart", buttons, BrowserApp.selectedTab.id, { persistence: -1 });
- },
-
- hideRestartPrompt: function() {
- NativeWindow.doorhanger.hide("addon-app-restart", BrowserApp.selectedTab.id);
- }
-};
-
-var ViewportHandler = {
- init: function init() {
- Services.obs.addObserver(this, "Window:Resize", false);
- },
-
- observe: function(aSubject, aTopic, aData) {
- if (aTopic == "Window:Resize" && aData) {
- let scrollChange = JSON.parse(aData);
- let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
- windowUtils.setNextPaintSyncId(scrollChange.id);
- }
- }
-};
-
-/**
- * Handler for blocked popups, triggered by DOMUpdatePageReport events in browser.xml
- */
-var PopupBlockerObserver = {
- onUpdatePageReport: function onUpdatePageReport(aEvent) {
- let browser = BrowserApp.selectedBrowser;
- if (aEvent.originalTarget != browser)
- return;
-
- if (!browser.pageReport)
- return;
-
- let result = Services.perms.testExactPermission(BrowserApp.selectedBrowser.currentURI, "popup");
- if (result == Ci.nsIPermissionManager.DENY_ACTION)
- return;
-
- // Only show the notification again if we've not already shown it. Since
- // notifications are per-browser, we don't need to worry about re-adding
- // it.
- if (!browser.pageReport.reported) {
- if (Services.prefs.getBoolPref("privacy.popups.showBrowserMessage")) {
- let brandShortName = Strings.brand.GetStringFromName("brandShortName");
- let popupCount = browser.pageReport.length;
-
- let strings = Strings.browser;
- let message = PluralForm.get(popupCount, strings.GetStringFromName("popup.message"))
- .replace("#1", brandShortName)
- .replace("#2", popupCount);
-
- let buttons = [
- {
- label: strings.GetStringFromName("popup.dontShow"),
- callback: function(aChecked) {
- if (aChecked)
- PopupBlockerObserver.allowPopupsForSite(false);
- }
- },
- {
- label: strings.GetStringFromName("popup.show"),
- callback: function(aChecked) {
- // Set permission before opening popup windows
- if (aChecked)
- PopupBlockerObserver.allowPopupsForSite(true);
-
- PopupBlockerObserver.showPopupsForSite();
- },
- positive: true
- }
- ];
-
- let options = { checkbox: Strings.browser.GetStringFromName("popup.dontAskAgain") };
- NativeWindow.doorhanger.show(message, "popup-blocked", buttons, null, options);
- }
- // Record the fact that we've reported this blocked popup, so we don't
- // show it again.
- browser.pageReport.reported = true;
- }
- },
-
- allowPopupsForSite: function allowPopupsForSite(aAllow) {
- let currentURI = BrowserApp.selectedBrowser.currentURI;
- Services.perms.add(currentURI, "popup", aAllow
- ? Ci.nsIPermissionManager.ALLOW_ACTION
- : Ci.nsIPermissionManager.DENY_ACTION);
- dump("Allowing popups for: " + currentURI);
- },
-
- showPopupsForSite: function showPopupsForSite() {
- let uri = BrowserApp.selectedBrowser.currentURI;
- let pageReport = BrowserApp.selectedBrowser.pageReport;
- if (pageReport) {
- for (let i = 0; i < pageReport.length; ++i) {
- let popupURIspec = pageReport[i].popupWindowURIspec;
-
- // Sometimes the popup URI that we get back from the pageReport
- // isn't useful (for instance, netscape.com's popup URI ends up
- // being "http://www.netscape.com", which isn't really the URI of
- // the popup they're trying to show). This isn't going to be
- // useful to the user, so we won't create a menu item for it.
- if (popupURIspec == "" || popupURIspec == "about:blank" || popupURIspec == uri.spec)
- continue;
-
- let popupFeatures = pageReport[i].popupWindowFeatures;
- let popupName = pageReport[i].popupWindowName;
-
- let parent = BrowserApp.selectedTab;
- let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(parent.browser);
- BrowserApp.addTab(popupURIspec, { parentId: parent.id, isPrivate: isPrivate });
- }
- }
- }
-};
-
-
-var IndexedDB = {
- _permissionsPrompt: "indexedDB-permissions-prompt",
- _permissionsResponse: "indexedDB-permissions-response",
-
- init: function IndexedDB_init() {
- Services.obs.addObserver(this, this._permissionsPrompt, false);
- },
-
- observe: function IndexedDB_observe(subject, topic, data) {
- if (topic != this._permissionsPrompt) {
- throw new Error("Unexpected topic!");
- }
-
- let requestor = subject.QueryInterface(Ci.nsIInterfaceRequestor);
-
- let browser = requestor.getInterface(Ci.nsIDOMNode);
- let tab = BrowserApp.getTabForBrowser(browser);
- if (!tab)
- return;
-
- let host = browser.currentURI.asciiHost;
-
- let strings = Strings.browser;
-
- let message, responseTopic;
- if (topic == this._permissionsPrompt) {
- message = strings.formatStringFromName("offlineApps.ask", [host], 1);
- responseTopic = this._permissionsResponse;
- }
-
- const firstTimeoutDuration = 300000; // 5 minutes
-
- let timeoutId;
-
- let notificationID = responseTopic + host;
- let observer = requestor.getInterface(Ci.nsIObserver);
-
- // This will be set to the result of PopupNotifications.show() below, or to
- // the result of PopupNotifications.getNotification() if this is a
- // quotaCancel notification.
- let notification;
-
- function timeoutNotification() {
- // Remove the notification.
- NativeWindow.doorhanger.hide(notificationID, tab.id);
-
- // Clear all of our timeout stuff. We may be called directly, not just
- // when the timeout actually elapses.
- clearTimeout(timeoutId);
-
- // And tell the page that the popup timed out.
- observer.observe(null, responseTopic, Ci.nsIPermissionManager.UNKNOWN_ACTION);
- }
-
- let buttons = [
- {
- label: strings.GetStringFromName("offlineApps.dontAllow2"),
- callback: function(aChecked) {
- clearTimeout(timeoutId);
- let action = aChecked ? Ci.nsIPermissionManager.DENY_ACTION : Ci.nsIPermissionManager.UNKNOWN_ACTION;
- observer.observe(null, responseTopic, action);
- }
- },
- {
- label: strings.GetStringFromName("offlineApps.allow"),
- callback: function() {
- clearTimeout(timeoutId);
- observer.observe(null, responseTopic, Ci.nsIPermissionManager.ALLOW_ACTION);
- },
- positive: true
- }];
-
- let options = { checkbox: Strings.browser.GetStringFromName("offlineApps.dontAskAgain") };
- NativeWindow.doorhanger.show(message, notificationID, buttons, tab.id, options);
-
- // Set the timeoutId after the popup has been created, and use the long
- // timeout value. If the user doesn't notice the popup after this amount of
- // time then it is most likely not visible and we want to alert the page.
- timeoutId = setTimeout(timeoutNotification, firstTimeoutDuration);
- }
-};
-
-var CharacterEncoding = {
- _charsets: [],
-
- init: function init() {
- Services.obs.addObserver(this, "CharEncoding:Get", false);
- Services.obs.addObserver(this, "CharEncoding:Set", false);
- InitLater(() => this.sendState());
- },
-
- observe: function observe(aSubject, aTopic, aData) {
- switch (aTopic) {
- case "CharEncoding:Get":
- this.getEncoding();
- break;
- case "CharEncoding:Set":
- this.setEncoding(aData);
- break;
- }
- },
-
- sendState: function sendState() {
- let showCharEncoding = "false";
- try {
- showCharEncoding = Services.prefs.getComplexValue("browser.menu.showCharacterEncoding", Ci.nsIPrefLocalizedString).data;
- } catch (e) { /* Optional */ }
-
- Messaging.sendRequest({
- type: "CharEncoding:State",
- visible: showCharEncoding
- });
- },
-
- getEncoding: function getEncoding() {
- function infoToCharset(info) {
- return { code: info.value, title: info.label };
- }
-
- if (!this._charsets.length) {
- let data = CharsetMenu.getData();
-
- // In the desktop UI, the pinned charsets are shown above the rest.
- let pinnedCharsets = data.pinnedCharsets.map(infoToCharset);
- let otherCharsets = data.otherCharsets.map(infoToCharset)
-
- this._charsets = pinnedCharsets.concat(otherCharsets);
- }
-
- // Look for the index of the selected charset. Default to -1 if the
- // doc charset isn't found in the list of available charsets.
- let docCharset = BrowserApp.selectedBrowser.contentDocument.characterSet;
- let selected = -1;
- let charsetCount = this._charsets.length;
-
- for (let i = 0; i < charsetCount; i++) {
- if (this._charsets[i].code === docCharset) {
- selected = i;
- break;
- }
- }
-
- Messaging.sendRequest({
- type: "CharEncoding:Data",
- charsets: this._charsets,
- selected: selected
- });
- },
-
- setEncoding: function setEncoding(aEncoding) {
- let browser = BrowserApp.selectedBrowser;
- browser.docShell.gatherCharsetMenuTelemetry();
- browser.docShell.charset = aEncoding;
- browser.reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
- }
-};
-
-var IdentityHandler = {
- // No trusted identity information. No site identity icon is shown.
- IDENTITY_MODE_UNKNOWN: "unknown",
-
- // Domain-Validation SSL CA-signed domain verification (DV).
- IDENTITY_MODE_IDENTIFIED: "identified",
-
- // Extended-Validation SSL CA-signed identity information (EV). A more rigorous validation process.
- IDENTITY_MODE_VERIFIED: "verified",
-
- // Part of the product's UI (built in about: pages)
- IDENTITY_MODE_CHROMEUI: "chromeUI",
-
- // The following mixed content modes are only used if "security.mixed_content.block_active_content"
- // is enabled. Our Java frontend coalesces them into one indicator.
-
- // No mixed content information. No mixed content icon is shown.
- MIXED_MODE_UNKNOWN: "unknown",
-
- // Blocked active mixed content.
- MIXED_MODE_CONTENT_BLOCKED: "blocked",
-
- // Loaded active mixed content.
- MIXED_MODE_CONTENT_LOADED: "loaded",
-
- // The following tracking content modes are only used if tracking protection
- // is enabled. Our Java frontend coalesces them into one indicator.
-
- // No tracking content information. No tracking content icon is shown.
- TRACKING_MODE_UNKNOWN: "unknown",
-
- // Blocked active tracking content. Shield icon is shown, with a popup option to load content.
- TRACKING_MODE_CONTENT_BLOCKED: "tracking_content_blocked",
-
- // Loaded active tracking content. Yellow triangle icon is shown.
- TRACKING_MODE_CONTENT_LOADED: "tracking_content_loaded",
-
- // Cache the most recent SSLStatus and Location seen in getIdentityStrings
- _lastStatus : null,
- _lastLocation : null,
-
- /**
- * Helper to parse out the important parts of _lastStatus (of the SSL cert in
- * particular) for use in constructing identity UI strings
- */
- getIdentityData : function() {
- let result = {};
- let status = this._lastStatus.QueryInterface(Components.interfaces.nsISSLStatus);
- let cert = status.serverCert;
-
- // Human readable name of Subject
- result.subjectOrg = cert.organization;
-
- // SubjectName fields, broken up for individual access
- if (cert.subjectName) {
- result.subjectNameFields = {};
- cert.subjectName.split(",").forEach(function(v) {
- let field = v.split("=");
- this[field[0]] = field[1];
- }, result.subjectNameFields);
-
- // Call out city, state, and country specifically
- result.city = result.subjectNameFields.L;
- result.state = result.subjectNameFields.ST;
- result.country = result.subjectNameFields.C;
- }
-
- // Human readable name of Certificate Authority
- result.caOrg = cert.issuerOrganization || cert.issuerCommonName;
- result.cert = cert;
-
- return result;
- },
-
- /**
- * Determines the identity mode corresponding to the icon we show in the urlbar.
- */
- getIdentityMode: function getIdentityMode(aState, uri) {
- if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
- return this.IDENTITY_MODE_VERIFIED;
- }
-
- if (aState & Ci.nsIWebProgressListener.STATE_IS_SECURE) {
- return this.IDENTITY_MODE_IDENTIFIED;
- }
-
- // We also allow "about:" by allowing the selector to be empty (i.e. '(|.....|...|...)'
- let whitelist = /^about:($|about|accounts|addons|buildconfig|cache|config|crashes|devices|downloads|fennec|firefox|feedback|healthreport|home|license|logins|logo|memory|mozilla|networking|plugins|privatebrowsing|rights|serviceworkers|support|telemetry|webrtc)($|\?)/i;
- if (uri.schemeIs("about") && whitelist.test(uri.spec)) {
- return this.IDENTITY_MODE_CHROMEUI;
- }
-
- return this.IDENTITY_MODE_UNKNOWN;
- },
-
- getMixedDisplayMode: function getMixedDisplayMode(aState) {
- if (aState & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT) {
- return this.MIXED_MODE_CONTENT_LOADED;
- }
-
- if (aState & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_DISPLAY_CONTENT) {
- return this.MIXED_MODE_CONTENT_BLOCKED;
- }
-
- return this.MIXED_MODE_UNKNOWN;
- },
-
- getMixedActiveMode: function getActiveDisplayMode(aState) {
- // Only show an indicator for loaded mixed content if the pref to block it is enabled
- if ((aState & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT) &&
- !Services.prefs.getBoolPref("security.mixed_content.block_active_content")) {
- return this.MIXED_MODE_CONTENT_LOADED;
- }
-
- if (aState & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT) {
- return this.MIXED_MODE_CONTENT_BLOCKED;
- }
-
- return this.MIXED_MODE_UNKNOWN;
- },
-
- getTrackingMode: function getTrackingMode(aState, aBrowser) {
- if (aState & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT) {
- this.shieldHistogramAdd(aBrowser, 2);
- return this.TRACKING_MODE_CONTENT_BLOCKED;
- }
-
- // Only show an indicator for loaded tracking content if the pref to block it is enabled
- let tpEnabled = Services.prefs.getBoolPref("privacy.trackingprotection.enabled") ||
- (Services.prefs.getBoolPref("privacy.trackingprotection.pbmode.enabled") &&
- PrivateBrowsingUtils.isBrowserPrivate(aBrowser));
-
- if ((aState & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT) && tpEnabled) {
- this.shieldHistogramAdd(aBrowser, 1);
- return this.TRACKING_MODE_CONTENT_LOADED;
- }
-
- this.shieldHistogramAdd(aBrowser, 0);
- return this.TRACKING_MODE_UNKNOWN;
- },
-
- shieldHistogramAdd: function(browser, value) {
- if (PrivateBrowsingUtils.isBrowserPrivate(browser)) {
- return;
- }
- Telemetry.addData("TRACKING_PROTECTION_SHIELD", value);
- },
-
- /**
- * Determine the identity of the page being displayed by examining its SSL cert
- * (if available). Return the data needed to update the UI.
- */
- checkIdentity: function checkIdentity(aState, aBrowser) {
- this._lastStatus = aBrowser.securityUI
- .QueryInterface(Components.interfaces.nsISSLStatusProvider)
- .SSLStatus;
-
- // Don't pass in the actual location object, since it can cause us to
- // hold on to the window object too long. Just pass in the fields we
- // care about. (bug 424829)
- let locationObj = {};
- try {
- let location = aBrowser.contentWindow.location;
- locationObj.host = location.host;
- locationObj.hostname = location.hostname;
- locationObj.port = location.port;
- locationObj.origin = location.origin;
- } catch (ex) {
- // Can sometimes throw if the URL being visited has no host/hostname,
- // e.g. about:blank. The _state for these pages means we won't need these
- // properties anyways, though.
- }
- this._lastLocation = locationObj;
-
- let uri = aBrowser.currentURI;
- try {
- uri = Services.uriFixup.createExposableURI(uri);
- } catch (e) {}
-
- let identityMode = this.getIdentityMode(aState, uri);
- let mixedDisplay = this.getMixedDisplayMode(aState);
- let mixedActive = this.getMixedActiveMode(aState);
- let trackingMode = this.getTrackingMode(aState, aBrowser);
- let result = {
- origin: locationObj.origin,
- mode: {
- identity: identityMode,
- mixed_display: mixedDisplay,
- mixed_active: mixedActive,
- tracking: trackingMode
- }
- };
-
- // Don't show identity data for pages with an unknown identity or if any
- // mixed content is loaded (mixed display content is loaded by default).
- // We also return for CHROMEUI pages since they don't have any certificate
- // information to load either. result.secure specifically refers to connection
- // security, which is irrelevant for about: pages, as they're loaded locally.
- if (identityMode == this.IDENTITY_MODE_UNKNOWN ||
- identityMode == this.IDENTITY_MODE_CHROMEUI ||
- aState & Ci.nsIWebProgressListener.STATE_IS_BROKEN) {
- result.secure = false;
- return result;
- }
-
- result.secure = true;
-
- result.host = this.getEffectiveHost();
-
- let iData = this.getIdentityData();
- result.verifier = Strings.browser.formatStringFromName("identity.identified.verifier", [iData.caOrg], 1);
-
- // If the cert is identified, then we can populate the results with credentials
- if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
- result.owner = iData.subjectOrg;
-
- // Build an appropriate supplemental block out of whatever location data we have
- let supplemental = "";
- if (iData.city) {
- supplemental += iData.city + "\n";
- }
- if (iData.state && iData.country) {
- supplemental += Strings.browser.formatStringFromName("identity.identified.state_and_country", [iData.state, iData.country], 2);
- result.country = iData.country;
- } else if (iData.state) { // State only
- supplemental += iData.state;
- } else if (iData.country) { // Country only
- supplemental += iData.country;
- result.country = iData.country;
- }
- result.supplemental = supplemental;
-
- return result;
- }
-
- // Cache the override service the first time we need to check it
- if (!this._overrideService)
- this._overrideService = Cc["@mozilla.org/security/certoverride;1"].getService(Ci.nsICertOverrideService);
-
- // Check whether this site is a security exception. XPConnect does the right
- // thing here in terms of converting _lastLocation.port from string to int, but
- // the overrideService doesn't like undefined ports, so make sure we have
- // something in the default case (bug 432241).
- // .hostname can return an empty string in some exceptional cases -
- // hasMatchingOverride does not handle that, so avoid calling it.
- // Updating the tooltip value in those cases isn't critical.
- // FIXME: Fixing bug 646690 would probably makes this check unnecessary
- if (this._lastLocation.hostname &&
- this._overrideService.hasMatchingOverride(this._lastLocation.hostname,
- (this._lastLocation.port || 443),
- iData.cert, {}, {}))
- result.verifier = Strings.browser.GetStringFromName("identity.identified.verified_by_you");
-
- return result;
- },
-
- /**
- * Attempt to provide proper IDN treatment for host names
- */
- getEffectiveHost: function getEffectiveHost() {
- if (!this._IDNService)
- this._IDNService = Cc["@mozilla.org/network/idn-service;1"]
- .getService(Ci.nsIIDNService);
- try {
- return this._IDNService.convertToDisplayIDN(this._uri.host, {});
- } catch (e) {
- // If something goes wrong (e.g. hostname is an IP address) just fail back
- // to the full domain.
- return this._lastLocation.hostname;
- }
- }
-};
-
-var SearchEngines = {
- _contextMenuId: null,
- PREF_SUGGEST_ENABLED: "browser.search.suggest.enabled",
- PREF_SUGGEST_PROMPTED: "browser.search.suggest.prompted",
-
- // Shared preference key used for search activity default engine.
- PREF_SEARCH_ACTIVITY_ENGINE_KEY: "search.engines.defaultname",
-
- init: function init() {
- Services.obs.addObserver(this, "SearchEngines:Add", false);
- Services.obs.addObserver(this, "SearchEngines:GetVisible", false);
- Services.obs.addObserver(this, "SearchEngines:Remove", false);
- Services.obs.addObserver(this, "SearchEngines:RestoreDefaults", false);
- Services.obs.addObserver(this, "SearchEngines:SetDefault", false);
- Services.obs.addObserver(this, "browser-search-engine-modified", false);
- },
-
- // Fetch list of search engines. all ? All engines : Visible engines only.
- _handleSearchEnginesGetVisible: function _handleSearchEnginesGetVisible(rv, all) {
- if (!Components.isSuccessCode(rv)) {
- Cu.reportError("Could not initialize search service, bailing out.");
- return;
- }
-
- let engineData = Services.search.getVisibleEngines({});
-
- // Our Java UI assumes that the default engine is the first item in the array,
- // so we need to make sure that's the case.
- if (engineData[0] !== Services.search.defaultEngine) {
- engineData = engineData.filter(engine => engine !== Services.search.defaultEngine);
- engineData.unshift(Services.search.defaultEngine);
- }
-
- let searchEngines = engineData.map(function (engine) {
- return {
- name: engine.name,
- identifier: engine.identifier,
- iconURI: (engine.iconURI ? engine.iconURI.spec : null),
- hidden: engine.hidden
- };
- });
-
- let suggestTemplate = null;
- let suggestEngine = null;
-
- // Check to see if the default engine supports search suggestions. We only need to check
- // the default engine because we only show suggestions for the default engine in the UI.
- let engine = Services.search.defaultEngine;
- if (engine.supportsResponseType("application/x-suggestions+json")) {
- suggestEngine = engine.name;
- suggestTemplate = engine.getSubmission("__searchTerms__", "application/x-suggestions+json").uri.spec;
- }
-
- // By convention, the currently configured default engine is at position zero in searchEngines.
- Messaging.sendRequest({
- type: "SearchEngines:Data",
- searchEngines: searchEngines,
- suggest: {
- engine: suggestEngine,
- template: suggestTemplate,
- enabled: Services.prefs.getBoolPref(this.PREF_SUGGEST_ENABLED),
- prompted: Services.prefs.getBoolPref(this.PREF_SUGGEST_PROMPTED)
- }
- });
-
- // Send a speculative connection to the default engine.
- Services.search.defaultEngine.speculativeConnect({window: window});
- },
-
- // Helper method to extract the engine name from a JSON. Simplifies the observe function.
- _extractEngineFromJSON: function _extractEngineFromJSON(aData) {
- let data = JSON.parse(aData);
- return Services.search.getEngineByName(data.engine);
- },
-
- observe: function observe(aSubject, aTopic, aData) {
- let engine;
- switch(aTopic) {
- case "SearchEngines:Add":
- this.displaySearchEnginesList(aData);
- break;
- case "SearchEngines:GetVisible":
- Services.search.init(this._handleSearchEnginesGetVisible.bind(this));
- break;
- case "SearchEngines:Remove":
- // Make sure the engine isn't hidden before removing it, to make sure it's
- // visible if the user later re-adds it (works around bug 341833)
- engine = this._extractEngineFromJSON(aData);
- engine.hidden = false;
- Services.search.removeEngine(engine);
- break;
- case "SearchEngines:RestoreDefaults":
- // Un-hides all default engines.
- Services.search.restoreDefaultEngines();
- break;
- case "SearchEngines:SetDefault":
- engine = this._extractEngineFromJSON(aData);
- // Move the new default search engine to the top of the search engine list.
- Services.search.moveEngine(engine, 0);
- Services.search.defaultEngine = engine;
- break;
- case "browser-search-engine-modified":
- if (aData == "engine-default") {
- this._setSearchActivityDefaultPref(aSubject.QueryInterface(Ci.nsISearchEngine));
- }
- break;
- default:
- dump("Unexpected message type observed: " + aTopic);
- break;
- }
- },
-
- migrateSearchActivityDefaultPref: function migrateSearchActivityDefaultPref() {
- Services.search.init(() => this._setSearchActivityDefaultPref(Services.search.defaultEngine));
- },
-
- // Updates the search activity pref when the default engine changes.
- _setSearchActivityDefaultPref: function _setSearchActivityDefaultPref(engine) {
- SharedPreferences.forApp().setCharPref(this.PREF_SEARCH_ACTIVITY_ENGINE_KEY, engine.name);
- },
-
- // Display context menu listing names of the search engines available to be added.
- displaySearchEnginesList: function displaySearchEnginesList(aData) {
- let data = JSON.parse(aData);
- let tab = BrowserApp.getTabForId(data.tabId);
-
- if (!tab)
- return;
-
- let browser = tab.browser;
- let engines = browser.engines;
-
- let p = new Prompt({
- window: browser.contentWindow
- }).setSingleChoiceItems(engines.map(function(e) {
- return { label: e.title };
- })).show((function(data) {
- if (data.button == -1)
- return;
-
- this.addOpenSearchEngine(engines[data.button]);
- engines.splice(data.button, 1);
-
- if (engines.length < 1) {
- // Broadcast message that there are no more add-able search engines.
- let newEngineMessage = {
- type: "Link:OpenSearch",
- tabID: tab.id,
- visible: false
- };
-
- Messaging.sendRequest(newEngineMessage);
- }
- }).bind(this));
- },
-
- addOpenSearchEngine: function addOpenSearchEngine(engine) {
- Services.search.addEngine(engine.url, Ci.nsISearchEngine.DATA_XML, engine.iconURL, false, {
- onSuccess: function() {
- // Display a toast confirming addition of new search engine.
- Snackbars.show(Strings.browser.formatStringFromName("alertSearchEngineAddedToast", [engine.title], 1), Snackbars.LENGTH_LONG);
- },
-
- onError: function(aCode) {
- let errorMessage;
- if (aCode == 2) {
- // Engine is a duplicate.
- errorMessage = "alertSearchEngineDuplicateToast";
-
- } else {
- // Unknown failure. Display general error message.
- errorMessage = "alertSearchEngineErrorToast";
- }
-
- Snackbars.show(Strings.browser.formatStringFromName(errorMessage, [engine.title], 1), Snackbars.LENGTH_LONG);
- }
- });
- },
-
- /**
- * Build and return an array of sorted form data / Query Parameters
- * for an element in a submission form.
- *
- * @param element
- * A valid submission element of a form.
- */
- _getSortedFormData: function(element) {
- let formData = [];
-
- for (let formElement of element.form.elements) {
- if (!formElement.type) {
- continue;
- }
-
- // Make this text field a generic search parameter.
- if (element == formElement) {
- formData.push({ name: formElement.name, value: "{searchTerms}" });
- continue;
- }
-
- // Add other form elements as parameters.
- switch (formElement.type.toLowerCase()) {
- case "checkbox":
- case "radio":
- if (!formElement.checked) {
- break;
- }
- case "text":
- case "hidden":
- case "textarea":
- formData.push({ name: escape(formElement.name), value: escape(formElement.value) });
- break;
-
- case "select-one": {
- for (let option of formElement.options) {
- if (option.selected) {
- formData.push({ name: escape(formElement.name), value: escape(formElement.value) });
- break;
- }
- }
- }
- }
- };
-
- // Return valid, pre-sorted queryParams.
- return formData.filter(a => a.name && a.value).sort((a, b) => {
- // nsIBrowserSearchService.hasEngineWithURL() ensures sort, but this helps.
- if (a.name > b.name) {
- return 1;
- }
- if (b.name > a.name) {
- return -1;
- }
-
- if (a.value > b.value) {
- return 1;
- }
- if (b.value > a.value) {
- return -1;
- }
-
- return 0;
- });
- },
-
- /**
- * Check if any search engines already handle an EngineURL of type
- * URLTYPE_SEARCH_HTML, matching this request-method, formURL, and queryParams.
- */
- visibleEngineExists: function(element) {
- let formData = this._getSortedFormData(element);
-
- let form = element.form;
- let method = form.method.toUpperCase();
-
- let charset = element.ownerDocument.characterSet;
- let docURI = Services.io.newURI(element.ownerDocument.URL, charset, null);
- let formURL = Services.io.newURI(form.getAttribute("action"), charset, docURI).spec;
-
- return Services.search.hasEngineWithURL(method, formURL, formData);
- },
-
- /**
- * Adds a new search engine to the BrowserSearchService, based on its provided element. Prompts for an engine
- * name, and appends a simple version-number in case of collision with an existing name.
- *
- * @return callback to handle success value. Currently used for ActionBarHandler.js and UI updates.
- */
- addEngine: function addEngine(aElement, resultCallback) {
- let form = aElement.form;
- let charset = aElement.ownerDocument.characterSet;
- let docURI = Services.io.newURI(aElement.ownerDocument.URL, charset, null);
- let formURL = Services.io.newURI(form.getAttribute("action"), charset, docURI).spec;
- let method = form.method.toUpperCase();
- let formData = this._getSortedFormData(aElement);
-
- // prompt user for name of search engine
- let promptTitle = Strings.browser.GetStringFromName("contextmenu.addSearchEngine3");
- let title = { value: (aElement.ownerDocument.title || docURI.host) };
- if (!Services.prompt.prompt(null, promptTitle, null, title, null, {})) {
- if (resultCallback) {
- resultCallback(false);
- };
- return;
- }
-
- // fetch the favicon for this page
- let dbFile = FileUtils.getFile("ProfD", ["browser.db"]);
- let mDBConn = Services.storage.openDatabase(dbFile);
- let stmts = [];
- stmts[0] = mDBConn.createStatement("SELECT favicon FROM history_with_favicons WHERE url = ?");
- stmts[0].bindByIndex(0, docURI.spec);
- let favicon = null;
-
- Services.search.init(function addEngine_cb(rv) {
- if (!Components.isSuccessCode(rv)) {
- Cu.reportError("Could not initialize search service, bailing out.");
- if (resultCallback) {
- resultCallback(false);
- };
- return;
- }
-
- mDBConn.executeAsync(stmts, stmts.length, {
- handleResult: function (results) {
- let bytes = results.getNextRow().getResultByName("favicon");
- if (bytes && bytes.length) {
- favicon = "data:image/x-icon;base64," + btoa(String.fromCharCode.apply(null, bytes));
- }
- },
- handleCompletion: function (reason) {
- // if there's already an engine with this name, add a number to
- // make the name unique (e.g., "Google" becomes "Google 2")
- let name = title.value;
- for (let i = 2; Services.search.getEngineByName(name); i++)
- name = title.value + " " + i;
-
- Services.search.addEngineWithDetails(name, favicon, null, null, method, formURL);
- Snackbars.show(Strings.browser.formatStringFromName("alertSearchEngineAddedToast", [name], 1), Snackbars.LENGTH_LONG);
-
- let engine = Services.search.getEngineByName(name);
- engine.wrappedJSObject._queryCharset = charset;
- formData.forEach(param => { engine.addParam(param.name, param.value, null); });
-
- if (resultCallback) {
- return resultCallback(true);
- };
- }
- });
- });
- }
-};
-
-var ActivityObserver = {
- init: function ao_init() {
- Services.obs.addObserver(this, "application-background", false);
- Services.obs.addObserver(this, "application-foreground", false);
- },
-
- observe: function ao_observe(aSubject, aTopic, aData) {
- let isForeground = false;
- let tab = BrowserApp.selectedTab;
-
- UITelemetry.addEvent("show.1", "system", null, aTopic);
-
- switch (aTopic) {
- case "application-background" :
- let doc = (tab ? tab.browser.contentDocument : null);
- if (doc && doc.fullscreenElement) {
- doc.exitFullscreen();
- }
- isForeground = false;
- break;
- case "application-foreground" :
- isForeground = true;
- break;
- }
-
- if (tab && tab.getActive() != isForeground) {
- tab.setActive(isForeground);
- }
- }
-};
-
-var Telemetry = {
- addData: function addData(aHistogramId, aValue) {
- let histogram = Services.telemetry.getHistogramById(aHistogramId);
- histogram.add(aValue);
- },
-};
-
-var Experiments = {
- // Enable malware download protection (bug 936041)
- MALWARE_DOWNLOAD_PROTECTION: "malware-download-protection",
-
- // Try to load pages from disk cache when network is offline (bug 935190)
- OFFLINE_CACHE: "offline-cache",
-
- init() {
- Messaging.sendRequestForResult({
- type: "Experiments:GetActive"
- }).then(experiments => {
- let names = JSON.parse(experiments);
- for (let name of names) {
- switch (name) {
- case this.MALWARE_DOWNLOAD_PROTECTION: {
- // Apply experiment preferences on the default branch. This allows
- // us to avoid migrating user prefs when experiments are enabled/disabled,
- // and it also allows users to override these prefs in about:config.
- let defaults = Services.prefs.getDefaultBranch(null);
- defaults.setBoolPref("browser.safebrowsing.downloads.enabled", true);
- defaults.setBoolPref("browser.safebrowsing.downloads.remote.enabled", true);
- continue;
- }
-
- case this.OFFLINE_CACHE: {
- let defaults = Services.prefs.getDefaultBranch(null);
- defaults.setBoolPref("browser.tabs.useCache", true);
- continue;
- }
- }
- }
- });
- },
-
- setOverride(name, isEnabled) {
- Messaging.sendRequest({
- type: "Experiments:SetOverride",
- name: name,
- isEnabled: isEnabled
- });
- },
-
- clearOverride(name) {
- Messaging.sendRequest({
- type: "Experiments:ClearOverride",
- name: name
- });
- }
-};
-
-var ExternalApps = {
- _contextMenuId: null,
-
- // extend _getLink to pickup html5 media links.
- _getMediaLink: function(aElement) {
- let uri = NativeWindow.contextmenus._getLink(aElement);
- if (uri == null && aElement.nodeType == Ci.nsIDOMNode.ELEMENT_NODE && (aElement instanceof Ci.nsIDOMHTMLMediaElement)) {
- try {
- let mediaSrc = aElement.currentSrc || aElement.src;
- uri = ContentAreaUtils.makeURI(mediaSrc, null, null);
- } catch (e) {}
- }
- return uri;
- },
-
- init: function helper_init() {
- this._contextMenuId = NativeWindow.contextmenus.add(function(aElement) {
- let uri = null;
- var node = aElement;
- while (node && !uri) {
- uri = ExternalApps._getMediaLink(node);
- node = node.parentNode;
- }
- let apps = [];
- if (uri)
- apps = HelperApps.getAppsForUri(uri);
-
- return apps.length == 1 ? Strings.browser.formatStringFromName("helperapps.openWithApp2", [apps[0].name], 1) :
- Strings.browser.GetStringFromName("helperapps.openWithList2");
- }, this.filter, this.openExternal);
- },
-
- filter: {
- matches: function(aElement) {
- let uri = ExternalApps._getMediaLink(aElement);
- let apps = [];
- if (uri) {
- apps = HelperApps.getAppsForUri(uri);
- }
- return apps.length > 0;
- }
- },
-
- openExternal: function(aElement) {
- if (aElement.pause) {
- aElement.pause();
- }
- let uri = ExternalApps._getMediaLink(aElement);
- HelperApps.launchUri(uri);
- },
-
- shouldCheckUri: function(uri) {
- if (!(uri.schemeIs("http") || uri.schemeIs("https") || uri.schemeIs("file"))) {
- return false;
- }
-
- return true;
- },
-
- updatePageAction: function updatePageAction(uri, contentDocument) {
- HelperApps.getAppsForUri(uri, { filterHttp: true }, (apps) => {
- this.clearPageAction();
- if (apps.length > 0)
- this._setUriForPageAction(uri, apps, contentDocument);
- });
- },
-
- updatePageActionUri: function updatePageActionUri(uri) {
- this._pageActionUri = uri;
- },
-
- _getMediaContentElement(contentDocument) {
- if (!contentDocument.contentType.startsWith("video/") &&
- !contentDocument.contentType.startsWith("audio/")) {
- return null;
- }
-
- let element = contentDocument.activeElement;
-
- if (element instanceof HTMLBodyElement) {
- element = element.firstChild;
- }
-
- if (element instanceof HTMLMediaElement) {
- return element;
- }
-
- return null;
- },
-
- _setUriForPageAction: function setUriForPageAction(uri, apps, contentDocument) {
- this.updatePageActionUri(uri);
-
- // If the pageaction is already added, simply update the URI to be launched when 'onclick' is triggered.
- if (this._pageActionId != undefined)
- return;
-
- let mediaElement = this._getMediaContentElement(contentDocument);
-
- this._pageActionId = PageActions.add({
- title: Strings.browser.GetStringFromName("openInApp.pageAction"),
- icon: "drawable://icon_openinapp",
-
- clickCallback: () => {
- UITelemetry.addEvent("launch.1", "pageaction", null, "helper");
-
- let wasPlaying = mediaElement && !mediaElement.paused && !mediaElement.ended;
- if (wasPlaying) {
- mediaElement.pause();
- }
-
- if (apps.length > 1) {
- // Use the HelperApps prompt here to filter out any Http handlers
- HelperApps.prompt(apps, {
- title: Strings.browser.GetStringFromName("openInApp.pageAction"),
- buttons: [
- Strings.browser.GetStringFromName("openInApp.ok"),
- Strings.browser.GetStringFromName("openInApp.cancel")
- ]
- }, (result) => {
- if (result.button != 0) {
- if (wasPlaying) {
- mediaElement.play();
- }
-
- return;
- }
- apps[result.icongrid0].launch(this._pageActionUri);
- });
- } else {
- apps[0].launch(this._pageActionUri);
- }
- }
- });
- },
-
- clearPageAction: function clearPageAction() {
- if(!this._pageActionId)
- return;
-
- PageActions.remove(this._pageActionId);
- delete this._pageActionId;
- },
-};
-
-var Distribution = {
- // File used to store campaign data
- _file: null,
-
- _preferencesJSON: null,
-
- init: function dc_init() {
- Services.obs.addObserver(this, "Distribution:Changed", false);
- Services.obs.addObserver(this, "Distribution:Set", false);
- Services.obs.addObserver(this, "prefservice:after-app-defaults", false);
- Services.obs.addObserver(this, "Campaign:Set", false);
-
- // Look for file outside the APK:
- // /data/data/org.mozilla.xxx/distribution.json
- this._file = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
- this._file.append("distribution.json");
- this.readJSON(this._file, this.update);
- },
-
- observe: function dc_observe(aSubject, aTopic, aData) {
- switch (aTopic) {
- case "Distribution:Changed":
- // Re-init the search service.
- try {
- Services.search._asyncReInit();
- } catch (e) {
- console.log("Unable to reinit search service.");
- }
- // Fall through.
-
- case "Distribution:Set":
- if (aData) {
- try {
- this._preferencesJSON = JSON.parse(aData);
- } catch (e) {
- console.log("Invalid distribution JSON.");
- }
- }
- // Reload the default prefs so we can observe "prefservice:after-app-defaults"
- Services.prefs.QueryInterface(Ci.nsIObserver).observe(null, "reload-default-prefs", null);
- this.installDistroAddons();
- break;
-
- case "prefservice:after-app-defaults":
- this.getPrefs();
- break;
-
- case "Campaign:Set": {
- // Update the prefs for this session
- try {
- this.update(JSON.parse(aData));
- } catch (ex) {
- Cu.reportError("Distribution: Could not parse JSON: " + ex);
- return;
- }
-
- // Asynchronously copy the data to the file.
- let array = new TextEncoder().encode(aData);
- OS.File.writeAtomic(this._file.path, array, { tmpPath: this._file.path + ".tmp" });
- break;
- }
- }
- },
-
- update: function dc_update(aData) {
- // Force the distribution preferences on the default branch
- let defaults = Services.prefs.getDefaultBranch(null);
- defaults.setCharPref("distribution.id", aData.id);
- defaults.setCharPref("distribution.version", aData.version);
- },
-
- getPrefs: function dc_getPrefs() {
- if (this._preferencesJSON) {
- this.applyPrefs(this._preferencesJSON);
- this._preferencesJSON = null;
- return;
- }
-
- // Get the distribution directory, and bail if it doesn't exist.
- let file = FileUtils.getDir("XREAppDist", [], false);
- if (!file.exists())
- return;
-
- file.append("preferences.json");
- this.readJSON(file, this.applyPrefs);
- },
-
- applyPrefs: function dc_applyPrefs(aData) {
- // Check for required Global preferences
- let global = aData["Global"];
- if (!(global && global["id"] && global["version"] && global["about"])) {
- Cu.reportError("Distribution: missing or incomplete Global preferences");
- return;
- }
-
- // Force the distribution preferences on the default branch
- let defaults = Services.prefs.getDefaultBranch(null);
- defaults.setCharPref("distribution.id", global["id"]);
- defaults.setCharPref("distribution.version", global["version"]);
-
- let locale = BrowserApp.getUALocalePref();
- let aboutString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
- aboutString.data = global["about." + locale] || global["about"];
- defaults.setComplexValue("distribution.about", Ci.nsISupportsString, aboutString);
-
- let prefs = aData["Preferences"];
- for (let key in prefs) {
- try {
- let value = prefs[key];
- switch (typeof value) {
- case "boolean":
- defaults.setBoolPref(key, value);
- break;
- case "number":
- defaults.setIntPref(key, value);
- break;
- case "string":
- case "undefined":
- defaults.setCharPref(key, value);
- break;
- }
- } catch (e) { /* ignore bad prefs and move on */ }
- }
-
- // Apply a lightweight theme if necessary
- if (prefs && prefs["lightweightThemes.selectedThemeID"]) {
- Services.obs.notifyObservers(null, "lightweight-theme-apply", "");
- }
-
- let localizedString = Cc["@mozilla.org/pref-localizedstring;1"].createInstance(Ci.nsIPrefLocalizedString);
- let localizeablePrefs = aData["LocalizablePreferences"];
- for (let key in localizeablePrefs) {
- try {
- let value = localizeablePrefs[key];
- value = value.replace(/%LOCALE%/g, locale);
- localizedString.data = "data:text/plain," + key + "=" + value;
- defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedString);
- } catch (e) { /* ignore bad prefs and move on */ }
- }
-
- let localizeablePrefsOverrides = aData["LocalizablePreferences." + locale];
- for (let key in localizeablePrefsOverrides) {
- try {
- let value = localizeablePrefsOverrides[key];
- localizedString.data = "data:text/plain," + key + "=" + value;
- defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedString);
- } catch (e) { /* ignore bad prefs and move on */ }
- }
-
- Messaging.sendRequest({ type: "Distribution:Set:OK" });
- },
-
- // aFile is an nsIFile
- // aCallback takes the parsed JSON object as a parameter
- readJSON: function dc_readJSON(aFile, aCallback) {
- Task.spawn(function() {
- let bytes = yield OS.File.read(aFile.path);
- let raw = new TextDecoder().decode(bytes) || "";
-
- try {
- aCallback(JSON.parse(raw));
- } catch (e) {
- Cu.reportError("Distribution: Could not parse JSON: " + e);
- }
- }).then(null, function onError(reason) {
- if (!(reason instanceof OS.File.Error && reason.becauseNoSuchFile)) {
- Cu.reportError("Distribution: Could not read from " + aFile.leafName + " file");
- }
- });
- },
-
- // Track pending installs so we can avoid showing notifications for them.
- pendingAddonInstalls: new Set(),
-
- installDistroAddons: Task.async(function* () {
- const PREF_ADDONS_INSTALLED = "distribution.addonsInstalled";
- try {
- let installed = Services.prefs.getBoolPref(PREF_ADDONS_INSTALLED);
- if (installed) {
- return;
- }
- } catch (e) {
- Services.prefs.setBoolPref(PREF_ADDONS_INSTALLED, true);
- }
-
- let distroPath;
- try {
- distroPath = FileUtils.getDir("XREAppDist", ["extensions"]).path;
-
- let info = yield OS.File.stat(distroPath);
- if (!info.isDir) {
- return;
- }
- } catch (e) {
- return;
- }
-
- let it = new OS.File.DirectoryIterator(distroPath);
- try {
- yield it.forEach(entry => {
- // Only support extensions that are zipped in .xpi files.
- if (entry.isDir || !entry.name.endsWith(".xpi")) {
- dump("Ignoring distribution add-on that isn't an XPI: " + entry.path);
- return;
- }
-
- new Promise((resolve, reject) => {
- AddonManager.getInstallForFile(new FileUtils.File(entry.path), resolve);
- }).then(install => {
- let id = entry.name.substring(0, entry.name.length - 4);
- if (install.addon.id !== id) {
- Cu.reportError("File entry " + entry.path + " contains an add-on with an incorrect ID");
- return;
- }
- this.pendingAddonInstalls.add(install);
- install.install();
- }).catch(e => {
- Cu.reportError("Error installing distribution add-on: " + entry.path + ": " + e);
- });
- });
- } finally {
- it.close();
- }
- })
-};
-
-var Tabs = {
- _enableTabExpiration: false,
- _useCache: false,
- _domains: new Set(),
-
- init: function() {
- // On low-memory platforms, always allow tab expiration. On high-mem
- // platforms, allow it to be turned on once we hit a low-mem situation.
- if (BrowserApp.isOnLowMemoryPlatform) {
- this._enableTabExpiration = true;
- } else {
- Services.obs.addObserver(this, "memory-pressure", false);
- }
-
- // Watch for opportunities to pre-connect to high probability targets.
- Services.obs.addObserver(this, "Session:Prefetch", false);
-
- // Track the network connection so we can efficiently use the cache
- // for possible offline rendering.
- Services.obs.addObserver(this, "network:link-status-changed", false);
- let network = Cc["@mozilla.org/network/network-link-service;1"].getService(Ci.nsINetworkLinkService);
- this.useCache = !network.isLinkUp;
-
- BrowserApp.deck.addEventListener("pageshow", this, false);
- BrowserApp.deck.addEventListener("TabOpen", this, false);
- },
-
- observe: function(aSubject, aTopic, aData) {
- switch (aTopic) {
- case "memory-pressure":
- if (aData != "heap-minimize") {
- // We received a low-memory related notification. This will enable
- // expirations.
- this._enableTabExpiration = true;
- Services.obs.removeObserver(this, "memory-pressure");
- } else {
- // Use "heap-minimize" as a trigger to expire the most stale tab.
- this.expireLruTab();
- }
- break;
- case "Session:Prefetch":
- if (aData) {
- try {
- let uri = Services.io.newURI(aData, null, null);
- if (uri && !this._domains.has(uri.host)) {
- Services.io.QueryInterface(Ci.nsISpeculativeConnect).speculativeConnect(uri, null);
- this._domains.add(uri.host);
- }
- } catch (e) {}
- }
- break;
- case "network:link-status-changed":
- if (["down", "unknown", "up"].indexOf(aData) == -1) {
- return;
- }
- this.useCache = (aData === "down");
- break;
- }
- },
-
- handleEvent: function(aEvent) {
- switch (aEvent.type) {
- case "pageshow":
- // Clear the domain cache whenever a page is loaded into any browser.
- this._domains.clear();
-
- break;
- case "TabOpen":
- // Use opening a new tab as a trigger to expire the most stale tab.
- this.expireLruTab();
- break;
- }
- },
-
- // Manage the most-recently-used list of tabs. Each tab has a timestamp
- // associated with it that indicates when it was last touched.
- expireLruTab: function() {
- if (!this._enableTabExpiration) {
- return false;
- }
- let expireTimeMs = Services.prefs.getIntPref("browser.tabs.expireTime") * 1000;
- if (expireTimeMs < 0) {
- // This behaviour is disabled.
- return false;
- }
- let tabs = BrowserApp.tabs;
- let selected = BrowserApp.selectedTab;
- let lruTab = null;
- // Find the least recently used non-zombie tab.
- for (let i = 0; i < tabs.length; i++) {
- if (tabs[i] == selected ||
- tabs[i].browser.__SS_restore ||
- tabs[i].playingAudio) {
- // This tab is selected, is already a zombie, or is currently playing
- // audio, skip it.
- continue;
- }
- if (lruTab == null || tabs[i].lastTouchedAt < lruTab.lastTouchedAt) {
- lruTab = tabs[i];
- }
- }
- // If the tab was last touched more than browser.tabs.expireTime seconds ago,
- // zombify it.
- if (lruTab) {
- if (Date.now() - lruTab.lastTouchedAt > expireTimeMs) {
- MemoryObserver.zombify(lruTab);
- return true;
- }
- }
- return false;
- },
-
- get useCache() {
- if (!Services.prefs.getBoolPref("browser.tabs.useCache")) {
- return false;
- }
- return this._useCache;
- },
-
- set useCache(aUseCache) {
- if (!Services.prefs.getBoolPref("browser.tabs.useCache")) {
- return;
- }
-
- if (this._useCache == aUseCache) {
- return;
- }
-
- BrowserApp.tabs.forEach(function(tab) {
- if (tab.browser && tab.browser.docShell) {
- if (aUseCache) {
- tab.browser.docShell.defaultLoadFlags |= Ci.nsIRequest.LOAD_FROM_CACHE;
- } else {
- tab.browser.docShell.defaultLoadFlags &= ~Ci.nsIRequest.LOAD_FROM_CACHE;
- }
- }
- });
- this._useCache = aUseCache;
- },
-
- // For debugging
- dump: function(aPrefix) {
- let tabs = BrowserApp.tabs;
- for (let i = 0; i < tabs.length; i++) {
- dump(aPrefix + " | " + "Tab [" + tabs[i].browser.contentWindow.location.href + "]: lastTouchedAt:" + tabs[i].lastTouchedAt + ", zombie:" + tabs[i].browser.__SS_restore);
- }
- },
-};
-
-function ContextMenuItem(args) {
- this.id = uuidgen.generateUUID().toString();
- this.args = args;
-}
-
-ContextMenuItem.prototype = {
- get order() {
- return this.args.order || 0;
- },
-
- matches: function(elt, x, y) {
- return this.args.selector.matches(elt, x, y);
- },
-
- callback: function(elt) {
- this.args.callback(elt);
- },
-
- addVal: function(name, elt, defaultValue) {
- if (!(name in this.args))
- return defaultValue;
-
- if (typeof this.args[name] == "function")
- return this.args[name](elt);
-
- return this.args[name];
- },
-
- getValue: function(elt) {
- return {
- id: this.id,
- label: this.addVal("label", elt),
- showAsActions: this.addVal("showAsActions", elt),
- icon: this.addVal("icon", elt),
- isGroup: this.addVal("isGroup", elt, false),
- inGroup: this.addVal("inGroup", elt, false),
- disabled: this.addVal("disabled", elt, false),
- selected: this.addVal("selected", elt, false),
- isParent: this.addVal("isParent", elt, false),
- };
- }
-}
-
-function HTMLContextMenuItem(elt, target) {
- ContextMenuItem.call(this, { });
-
- this.menuElementRef = Cu.getWeakReference(elt);
- this.targetElementRef = Cu.getWeakReference(target);
-}
-
-HTMLContextMenuItem.prototype = Object.create(ContextMenuItem.prototype, {
- order: {
- value: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER
- },
-
- matches: {
- value: function(target) {
- let t = this.targetElementRef.get();
- return t === target;
- },
- },
-
- callback: {
- value: function(target) {
- let elt = this.menuElementRef.get();
- if (!elt) {
- return;
- }
-
- // If this is a menu item, show a new context menu with the submenu in it
- if (elt instanceof Ci.nsIDOMHTMLMenuElement) {
- try {
- NativeWindow.contextmenus.menus = {};
-
- let elt = this.menuElementRef.get();
- let target = this.targetElementRef.get();
- if (!elt) {
- return;
- }
-
- var items = NativeWindow.contextmenus._getHTMLContextMenuItemsForMenu(elt, target);
- // This menu will always only have one context, but we still make sure its the "right" one.
- var context = NativeWindow.contextmenus._getContextType(target);
- if (items.length > 0) {
- NativeWindow.contextmenus._addMenuItems(items, context);
- }
-
- } catch(ex) {
- Cu.reportError(ex);
- }
- } else {
- // otherwise just click the menu item
- elt.click();
- }
- },
- },
-
- getValue: {
- value: function(target) {
- let elt = this.menuElementRef.get();
- if (!elt) {
- return null;
- }
-
- if (elt.hasAttribute("hidden")) {
- return null;
- }
-
- return {
- id: this.id,
- icon: elt.icon,
- label: elt.label,
- disabled: elt.disabled,
- menu: elt instanceof Ci.nsIDOMHTMLMenuElement
- };
- }
- },
-});
-
diff --git a/mobile/android/chrome/content/browser.xul b/mobile/android/chrome/content/browser.xul
deleted file mode 100644
index 8072a7a1c..000000000
--- a/mobile/android/chrome/content/browser.xul
+++ /dev/null
@@ -1,17 +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/. -->
-
-<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
-
-<window id="main-window"
- onload="BrowserApp.startup();"
- windowtype="navigator:browser"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
- <script type="application/javascript" src="chrome://browser/content/browser.js"/>
-
- <deck id="browsers" flex="1"/>
-
-</window>
diff --git a/mobile/android/chrome/content/config.js b/mobile/android/chrome/content/config.js
deleted file mode 100644
index 2c868f175..000000000
--- a/mobile/android/chrome/content/config.js
+++ /dev/null
@@ -1,673 +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, manager: Cm, utils: Cu} = Components;
-Cu.import("resource://gre/modules/Services.jsm");
-
-const VKB_ENTER_KEY = 13; // User press of VKB enter key
-const INITIAL_PAGE_DELAY = 500; // Initial pause on program start for scroll alignment
-const PREFS_BUFFER_MAX = 30; // Max prefs buffer size for getPrefsBuffer()
-const PAGE_SCROLL_TRIGGER = 200; // Triggers additional getPrefsBuffer() on user scroll-to-bottom
-const FILTER_CHANGE_TRIGGER = 200; // Delay between responses to filterInput changes
-const INNERHTML_VALUE_DELAY = 100; // Delay before providing prefs innerHTML value
-
-var gStringBundle = Services.strings.createBundle("chrome://browser/locale/config.properties");
-var gClipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
-
-
-/* ============================== NewPrefDialog ==============================
- *
- * New Preference Dialog Object and methods
- *
- * Implements User Interfaces for creation of a single(new) Preference setting
- *
- */
-var NewPrefDialog = {
-
- _prefsShield: null,
-
- _newPrefsDialog: null,
- _newPrefItem: null,
- _prefNameInputElt: null,
- _prefTypeSelectElt: null,
-
- _booleanValue: null,
- _booleanToggle: null,
- _stringValue: null,
- _intValue: null,
-
- _positiveButton: null,
-
- get type() {
- return this._prefTypeSelectElt.value;
- },
-
- set type(aType) {
- this._prefTypeSelectElt.value = aType;
- switch(this._prefTypeSelectElt.value) {
- case "boolean":
- this._prefTypeSelectElt.selectedIndex = 0;
- break;
- case "string":
- this._prefTypeSelectElt.selectedIndex = 1;
- break;
- case "int":
- this._prefTypeSelectElt.selectedIndex = 2;
- break;
- }
-
- this._newPrefItem.setAttribute("typestyle", aType);
- },
-
- // Init the NewPrefDialog
- init: function AC_init() {
- this._prefsShield = document.getElementById("prefs-shield");
-
- this._newPrefsDialog = document.getElementById("new-pref-container");
- this._newPrefItem = document.getElementById("new-pref-item");
- this._prefNameInputElt = document.getElementById("new-pref-name");
- this._prefTypeSelectElt = document.getElementById("new-pref-type");
-
- this._booleanValue = document.getElementById("new-pref-value-boolean");
- this._stringValue = document.getElementById("new-pref-value-string");
- this._intValue = document.getElementById("new-pref-value-int");
-
- this._positiveButton = document.getElementById("positive-button");
- },
-
- // Called to update positive button to display text ("Create"/"Change), and enabled/disabled status
- // As new pref name is initially displayed, re-focused, or modifed during user input
- _updatePositiveButton: function AC_updatePositiveButton(aPrefName) {
- this._positiveButton.textContent = gStringBundle.GetStringFromName("newPref.createButton");
- this._positiveButton.setAttribute("disabled", true);
- if (aPrefName == "") {
- return;
- }
-
- // If item already in list, it's being changed, else added
- let item = AboutConfig._list.filter(i => { return i.name == aPrefName });
- if (item.length) {
- this._positiveButton.textContent = gStringBundle.GetStringFromName("newPref.changeButton");
- } else {
- this._positiveButton.removeAttribute("disabled");
- }
- },
-
- // When we want to cancel/hide an existing, or show a new pref dialog
- toggleShowHide: function AC_toggleShowHide() {
- if (this._newPrefsDialog.classList.contains("show")) {
- this.hide();
- } else {
- this._show();
- }
- },
-
- // When we want to show the new pref dialog / shield the prefs list
- _show: function AC_show() {
- this._newPrefsDialog.classList.add("show");
- this._prefsShield.setAttribute("shown", true);
-
- // Initial default field values
- this._prefNameInputElt.value = "";
- this._updatePositiveButton(this._prefNameInputElt.value);
-
- this.type = "boolean";
- this._booleanValue.value = "false";
- this._stringValue.value = "";
- this._intValue.value = "";
-
- this._prefNameInputElt.focus();
-
- window.addEventListener("keypress", this.handleKeypress, false);
- },
-
- // When we want to cancel/hide the new pref dialog / un-shield the prefs list
- hide: function AC_hide() {
- this._newPrefsDialog.classList.remove("show");
- this._prefsShield.removeAttribute("shown");
-
- window.removeEventListener("keypress", this.handleKeypress, false);
- },
-
- // Watch user key input so we can provide Enter key action, commit input values
- handleKeypress: function AC_handleKeypress(aEvent) {
- // Close our VKB on new pref enter key press
- if (aEvent.keyCode == VKB_ENTER_KEY)
- aEvent.target.blur();
- },
-
- // New prefs create dialog only allows creating a non-existing preference, doesn't allow for
- // Changing an existing one on-the-fly, tap existing/displayed line item pref for that
- create: function AC_create(aEvent) {
- if (this._positiveButton.getAttribute("disabled") == "true") {
- return;
- }
-
- switch(this.type) {
- case "boolean":
- Services.prefs.setBoolPref(this._prefNameInputElt.value, (this._booleanValue.value == "true") ? true : false);
- break;
- case "string":
- Services.prefs.setCharPref(this._prefNameInputElt.value, this._stringValue.value);
- break;
- case "int":
- Services.prefs.setIntPref(this._prefNameInputElt.value, this._intValue.value);
- break;
- }
-
- // Ensure pref adds flushed to disk immediately
- Services.prefs.savePrefFile(null);
-
- this.hide();
- },
-
- // Display proper positive button text/state on new prefs name input focus
- focusName: function AC_focusName(aEvent) {
- this._updatePositiveButton(aEvent.target.value);
- },
-
- // Display proper positive button text/state as user changes new prefs name
- updateName: function AC_updateName(aEvent) {
- this._updatePositiveButton(aEvent.target.value);
- },
-
- // In new prefs dialog, bool prefs are <input type="text">, as they aren't yet tied to an
- // Actual Services.prefs.*etBoolPref()
- toggleBoolValue: function AC_toggleBoolValue() {
- this._booleanValue.value = (this._booleanValue.value == "true" ? "false" : "true");
- }
-}
-
-
-/* ============================== AboutConfig ==============================
- *
- * Main AboutConfig object and methods
- *
- * Implements User Interfaces for maintenance of a list of Preference settings
- *
- */
-var AboutConfig = {
-
- contextMenuLINode: null,
- filterInput: null,
- _filterPrevInput: null,
- _filterChangeTimer: null,
- _prefsContainer: null,
- _loadingContainer: null,
- _list: null,
-
- // Init the main AboutConfig dialog
- init: function AC_init() {
- this.filterInput = document.getElementById("filter-input");
- this._prefsContainer = document.getElementById("prefs-container");
- this._loadingContainer = document.getElementById("loading-container");
-
- let list = Services.prefs.getChildList("");
- this._list = list.sort().map( function AC_getMapPref(aPref) {
- return new Pref(aPref);
- }, this);
-
- // Support filtering about:config via a ?filter=<string> param
- let match = /[?&]filter=([^&]+)/i.exec(window.location.href);
- if (match) {
- this.filterInput.value = decodeURIComponent(match[1]);
- }
-
- // Display the current prefs list (retains searchFilter value)
- this.bufferFilterInput();
-
- // Setup the prefs observers
- Services.prefs.addObserver("", this, false);
- },
-
- // Uninit the main AboutConfig dialog
- uninit: function AC_uninit() {
- // Remove the prefs observer
- Services.prefs.removeObserver("", this);
- },
-
- // Clear the filterInput value, to display the entire list
- clearFilterInput: function AC_clearFilterInput() {
- this.filterInput.value = "";
- this.bufferFilterInput();
- },
-
- // Buffer down rapid changes in filterInput value from keyboard
- bufferFilterInput: function AC_bufferFilterInput() {
- if (this._filterChangeTimer) {
- clearTimeout(this._filterChangeTimer);
- }
-
- this._filterChangeTimer = setTimeout((function() {
- this._filterChangeTimer = null;
- // Display updated prefs list when filterInput value settles
- this._displayNewList();
- }).bind(this), FILTER_CHANGE_TRIGGER);
- },
-
- // Update displayed list when filterInput value changes
- _displayNewList: function AC_displayNewList() {
- // This survives the search filter value past a page refresh
- this.filterInput.setAttribute("value", this.filterInput.value);
-
- // Don't start new filter search if same as last
- if (this.filterInput.value == this._filterPrevInput) {
- return;
- }
- this._filterPrevInput = this.filterInput.value;
-
- // Clear list item selection / context menu, prefs list, get first buffer, set scrolling on
- this.selected = "";
- this._clearPrefsContainer();
- this._addMorePrefsToContainer();
- window.onscroll = this.onScroll.bind(this);
-
- // Pause for screen to settle, then ensure at top
- setTimeout((function() {
- window.scrollTo(0, 0);
- }).bind(this), INITIAL_PAGE_DELAY);
- },
-
- // Clear the displayed preferences list
- _clearPrefsContainer: function AC_clearPrefsContainer() {
- // Quick clear the prefsContainer list
- let empty = this._prefsContainer.cloneNode(false);
- this._prefsContainer.parentNode.replaceChild(empty, this._prefsContainer);
- this._prefsContainer = empty;
-
- // Quick clear the prefs li.HTML list
- this._list.forEach(function(item) {
- delete item.li;
- });
- },
-
- // Get a small manageable block of prefs items, and add them to the displayed list
- _addMorePrefsToContainer: function AC_addMorePrefsToContainer() {
- // Create filter regex
- let filterExp = this.filterInput.value ?
- new RegExp(this.filterInput.value, "i") : null;
-
- // Get a new block for the display list
- let prefsBuffer = [];
- for (let i = 0; i < this._list.length && prefsBuffer.length < PREFS_BUFFER_MAX; i++) {
- if (!this._list[i].li && this._list[i].test(filterExp)) {
- prefsBuffer.push(this._list[i]);
- }
- }
-
- // Add the new block to the displayed list
- for (let i = 0; i < prefsBuffer.length; i++) {
- this._prefsContainer.appendChild(prefsBuffer[i].getOrCreateNewLINode());
- }
-
- // Determine if anything left to add later by scrolling
- let anotherPrefsBufferRemains = false;
- for (let i = 0; i < this._list.length; i++) {
- if (!this._list[i].li && this._list[i].test(filterExp)) {
- anotherPrefsBufferRemains = true;
- break;
- }
- }
-
- if (anotherPrefsBufferRemains) {
- // If still more could be displayed, show the throbber
- this._loadingContainer.style.display = "block";
- } else {
- // If no more could be displayed, hide the throbber, and stop noticing scroll events
- this._loadingContainer.style.display = "none";
- window.onscroll = null;
- }
- },
-
- // If scrolling at the bottom, maybe add some more entries
- onScroll: function AC_onScroll(aEvent) {
- if (this._prefsContainer.scrollHeight - (window.pageYOffset + window.innerHeight) < PAGE_SCROLL_TRIGGER) {
- if (!this._filterChangeTimer) {
- this._addMorePrefsToContainer();
- }
- }
- },
-
-
- // Return currently selected list item node
- get selected() {
- return document.querySelector(".pref-item.selected");
- },
-
- // Set list item node as selected
- set selected(aSelection) {
- let currentSelection = this.selected;
- if (aSelection == currentSelection) {
- return;
- }
-
- // Clear any previous selection
- if (currentSelection) {
- currentSelection.classList.remove("selected");
- currentSelection.removeEventListener("keypress", this.handleKeypress, false);
- }
-
- // Set any current selection
- if (aSelection) {
- aSelection.classList.add("selected");
- aSelection.addEventListener("keypress", this.handleKeypress, false);
- }
- },
-
- // Watch user key input so we can provide Enter key action, commit input values
- handleKeypress: function AC_handleKeypress(aEvent) {
- if (aEvent.keyCode == VKB_ENTER_KEY)
- aEvent.target.blur();
- },
-
- // Return the target list item node of an action event
- getLINodeForEvent: function AC_getLINodeForEvent(aEvent) {
- let node = aEvent.target;
- while (node && node.nodeName != "li") {
- node = node.parentNode;
- }
-
- return node;
- },
-
- // Return a pref of a list item node
- _getPrefForNode: function AC_getPrefForNode(aNode) {
- let pref = aNode.getAttribute("name");
-
- return new Pref(pref);
- },
-
- // When list item name or value are tapped
- selectOrToggleBoolPref: function AC_selectOrToggleBoolPref(aEvent) {
- let node = this.getLINodeForEvent(aEvent);
-
- // If not already selected, just do so
- if (this.selected != node) {
- this.selected = node;
- return;
- }
-
- // If already selected, and value is boolean, toggle it
- let pref = this._getPrefForNode(node);
- if (pref.type != Services.prefs.PREF_BOOL) {
- return;
- }
-
- this.toggleBoolPref(aEvent);
- },
-
- // When finalizing list input values due to blur
- setIntOrStringPref: function AC_setIntOrStringPref(aEvent) {
- let node = this.getLINodeForEvent(aEvent);
-
- // Skip if locked
- let pref = this._getPrefForNode(node);
- if (pref.locked) {
- return;
- }
-
- // Boolean inputs blur to remove focus from "button"
- if (pref.type == Services.prefs.PREF_BOOL) {
- return;
- }
-
- // String and Int inputs change / commit on blur
- pref.value = aEvent.target.value;
- },
-
- // When we reset a pref to it's default value (note resetting a user created pref will delete it)
- resetDefaultPref: function AC_resetDefaultPref(aEvent) {
- let node = this.getLINodeForEvent(aEvent);
-
- // If not already selected, do so
- if (this.selected != node) {
- this.selected = node;
- }
-
- // Reset will handle any locked condition
- let pref = this._getPrefForNode(node);
- pref.reset();
-
- // Ensure pref reset flushed to disk immediately
- Services.prefs.savePrefFile(null);
- },
-
- // When we want to toggle a bool pref
- toggleBoolPref: function AC_toggleBoolPref(aEvent) {
- let node = this.getLINodeForEvent(aEvent);
-
- // Skip if locked, or not boolean
- let pref = this._getPrefForNode(node);
- if (pref.locked) {
- return;
- }
-
- // Toggle, and blur to remove field focus
- pref.value = !pref.value;
- aEvent.target.blur();
- },
-
- // When Int inputs have their Up or Down arrows toggled
- incrOrDecrIntPref: function AC_incrOrDecrIntPref(aEvent, aInt) {
- let node = this.getLINodeForEvent(aEvent);
-
- // Skip if locked
- let pref = this._getPrefForNode(node);
- if (pref.locked) {
- return;
- }
-
- pref.value += aInt;
- },
-
- // Observe preference changes
- observe: function AC_observe(aSubject, aTopic, aPrefName) {
- let pref = new Pref(aPrefName);
-
- // Ignore uninteresting changes, and avoid "private" preferences
- if (aTopic != "nsPref:changed") {
- return;
- }
-
- // If pref type invalid, refresh display as user reset/removed an item from the list
- if (pref.type == Services.prefs.PREF_INVALID) {
- document.location.reload();
- return;
- }
-
- // If pref onscreen, update in place.
- let item = document.querySelector(".pref-item[name=\"" + CSS.escape(pref.name) + "\"]");
- if (item) {
- item.setAttribute("value", pref.value);
- let input = item.querySelector("input");
- input.setAttribute("value", pref.value);
- input.value = pref.value;
-
- pref.default ?
- item.querySelector(".reset").setAttribute("disabled", "true") :
- item.querySelector(".reset").removeAttribute("disabled");
- return;
- }
-
- // If pref not already in list, refresh display as it's being added
- let anyWhere = this._list.filter(i => { return i.name == pref.name });
- if (!anyWhere.length) {
- document.location.reload();
- }
- },
-
- // Quick context menu helpers for about:config
- clipboardCopy: function AC_clipboardCopy(aField) {
- let pref = this._getPrefForNode(this.contextMenuLINode);
- if (aField == 'name') {
- gClipboardHelper.copyString(pref.name);
- } else {
- gClipboardHelper.copyString(pref.value);
- }
- }
-}
-
-
-/* ============================== Pref ==============================
- *
- * Individual Preference object / methods
- *
- * Defines a Pref object, a document list item tied to Preferences Services
- * And the methods by which they interact.
- *
- */
-function Pref(aName) {
- this.name = aName;
-}
-
-Pref.prototype = {
- get type() {
- return Services.prefs.getPrefType(this.name);
- },
-
- get value() {
- switch (this.type) {
- case Services.prefs.PREF_BOOL:
- return Services.prefs.getBoolPref(this.name);
- case Services.prefs.PREF_INT:
- return Services.prefs.getIntPref(this.name);
- case Services.prefs.PREF_STRING:
- default:
- return Services.prefs.getCharPref(this.name);
- }
-
- },
- set value(aPrefValue) {
- switch (this.type) {
- case Services.prefs.PREF_BOOL:
- Services.prefs.setBoolPref(this.name, aPrefValue);
- break;
- case Services.prefs.PREF_INT:
- Services.prefs.setIntPref(this.name, aPrefValue);
- break;
- case Services.prefs.PREF_STRING:
- default:
- Services.prefs.setCharPref(this.name, aPrefValue);
- }
-
- // Ensure pref change flushed to disk immediately
- Services.prefs.savePrefFile(null);
- },
-
- get default() {
- return !Services.prefs.prefHasUserValue(this.name);
- },
-
- get locked() {
- return Services.prefs.prefIsLocked(this.name);
- },
-
- reset: function AC_reset() {
- Services.prefs.clearUserPref(this.name);
- },
-
- test: function AC_test(aValue) {
- return aValue ? aValue.test(this.name) : true;
- },
-
- // Get existing or create new LI node for the pref
- getOrCreateNewLINode: function AC_getOrCreateNewLINode() {
- if (!this.li) {
- this.li = document.createElement("li");
-
- this.li.className = "pref-item";
- this.li.setAttribute("name", this.name);
-
- // Click callback to ensure list item selected even on no-action tap events
- this.li.addEventListener("click",
- function(aEvent) {
- AboutConfig.selected = AboutConfig.getLINodeForEvent(aEvent);
- },
- false
- );
-
- // Contextmenu callback to identify selected list item
- this.li.addEventListener("contextmenu",
- function(aEvent) {
- AboutConfig.contextMenuLINode = AboutConfig.getLINodeForEvent(aEvent);
- },
- false
- );
-
- this.li.setAttribute("contextmenu", "prefs-context-menu");
-
- // Create list item outline, bind to object actions
- this.li.innerHTML =
- "<div class='pref-name' " +
- "onclick='AboutConfig.selectOrToggleBoolPref(event);'>" +
- this.name +
- "</div>" +
- "<div class='pref-item-line'>" +
- "<input class='pref-value' value='' " +
- "onblur='AboutConfig.setIntOrStringPref(event);' " +
- "onclick='AboutConfig.selectOrToggleBoolPref(event);'>" +
- "</input>" +
- "<div class='pref-button reset' " +
- "onclick='AboutConfig.resetDefaultPref(event);'>" +
- gStringBundle.GetStringFromName("pref.resetButton") +
- "</div>" +
- "<div class='pref-button toggle' " +
- "onclick='AboutConfig.toggleBoolPref(event);'>" +
- gStringBundle.GetStringFromName("pref.toggleButton") +
- "</div>" +
- "<div class='pref-button up' " +
- "onclick='AboutConfig.incrOrDecrIntPref(event, 1);'>" +
- "</div>" +
- "<div class='pref-button down' " +
- "onclick='AboutConfig.incrOrDecrIntPref(event, -1);'>" +
- "</div>" +
- "</div>";
-
- // Delay providing the list item values, until the LI is returned and added to the document
- setTimeout(this._valueSetup.bind(this), INNERHTML_VALUE_DELAY);
- }
-
- return this.li;
- },
-
- // Initialize list item object values
- _valueSetup: function AC_valueSetup() {
-
- this.li.setAttribute("type", this.type);
- this.li.setAttribute("value", this.value);
-
- let valDiv = this.li.querySelector(".pref-value");
- valDiv.value = this.value;
-
- switch(this.type) {
- case Services.prefs.PREF_BOOL:
- valDiv.setAttribute("type", "button");
- this.li.querySelector(".up").setAttribute("disabled", true);
- this.li.querySelector(".down").setAttribute("disabled", true);
- break;
- case Services.prefs.PREF_STRING:
- valDiv.setAttribute("type", "text");
- this.li.querySelector(".up").setAttribute("disabled", true);
- this.li.querySelector(".down").setAttribute("disabled", true);
- this.li.querySelector(".toggle").setAttribute("disabled", true);
- break;
- case Services.prefs.PREF_INT:
- valDiv.setAttribute("type", "number");
- this.li.querySelector(".toggle").setAttribute("disabled", true);
- break;
- }
-
- this.li.setAttribute("default", this.default);
- if (this.default) {
- this.li.querySelector(".reset").setAttribute("disabled", true);
- }
-
- if (this.locked) {
- valDiv.setAttribute("disabled", this.locked);
- this.li.querySelector(".pref-name").setAttribute("locked", true);
- }
- }
-}
-
diff --git a/mobile/android/chrome/content/config.xhtml b/mobile/android/chrome/content/config.xhtml
deleted file mode 100644
index fd40bb517..000000000
--- a/mobile/android/chrome/content/config.xhtml
+++ /dev/null
@@ -1,86 +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 html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
- "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
-<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
-%globalDTD;
-<!ENTITY % configDTD SYSTEM "chrome://browser/locale/config.dtd">
-%configDTD;
-]>
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-
-<head>
- <meta name="viewport" content="width=device-width; user-scalable=0" />
-
- <link rel="stylesheet" href="chrome://browser/skin/config.css" type="text/css"/>
- <script type="text/javascript;version=1.8" src="chrome://browser/content/config.js"></script>
-</head>
-
-<body dir="&locale.dir;" onload="NewPrefDialog.init(); AboutConfig.init();"
- onunload="AboutConfig.uninit();">
-
- <div class="toolbar">
- <div class="toolbar-container">
- <div id="new-pref-toggle-button" onclick="NewPrefDialog.toggleShowHide();"/>
-
- <div class="toolbar-item" id="filter-container">
- <div id="filter-search-button"/>
- <input id="filter-input" type="search" placeholder="&toolbar.searchPlaceholder;" value=""
- oninput="AboutConfig.bufferFilterInput();"/>
- <div id="filter-input-clear-button" onclick="AboutConfig.clearFilterInput();"/>
- </div>
- </div>
- </div>
-
- <div id="content" ontouchstart="AboutConfig.filterInput.blur();">
-
- <div id="new-pref-container">
- <li class="pref-item" id="new-pref-item">
- <div class="pref-item-line">
- <input class="pref-name" id="new-pref-name" type="text" placeholder="&newPref.namePlaceholder;"
- onfocus="NewPrefDialog.focusName(event);"
- oninput="NewPrefDialog.updateName(event);"/>
- <select class="pref-value" id="new-pref-type" onchange="NewPrefDialog.type = event.target.value;">
- <option value="boolean">&newPref.valueBoolean;</option>
- <option value="string">&newPref.valueString;</option>
- <option value="int">&newPref.valueInteger;</option>
- </select>
- </div>
-
- <div class="pref-item-line" id="new-pref-line-boolean">
- <input class="pref-value" id="new-pref-value-boolean" disabled="disabled"/>
- <div class="pref-button toggle" onclick="NewPrefDialog.toggleBoolValue();">&newPref.toggleButton;</div>
- </div>
-
- <div class="pref-item-line">
- <input class="pref-value" id="new-pref-value-string" placeholder="&newPref.stringPlaceholder;"/>
- <input class="pref-value" id="new-pref-value-int" placeholder="&newPref.numberPlaceholder;" type="number"/>
- </div>
-
- <div class="pref-item-line">
- <div class="pref-button cancel" id="negative-button" onclick="NewPrefDialog.hide();">&newPref.cancelButton;</div>
- <div class="pref-button create" id="positive-button" onclick="NewPrefDialog.create(event);"></div>
- </div>
- </li>
- </div>
-
- <div id="prefs-shield"></div>
-
- <ul id="prefs-container"/>
-
- <ul id="loading-container"><li></li></ul>
-
- </div>
-
- <menu type="context" id="prefs-context-menu">
- <menuitem label="&contextMenu.copyPrefName;" onclick="AboutConfig.clipboardCopy('name');"></menuitem>
- <menuitem label="&contextMenu.copyPrefValue;" onclick="AboutConfig.clipboardCopy('value');"></menuitem>
- </menu>
-
-</body>
-</html>
diff --git a/mobile/android/chrome/content/content.js b/mobile/android/chrome/content/content.js
deleted file mode 100644
index 7cac22bd1..000000000
--- a/mobile/android/chrome/content/content.js
+++ /dev/null
@@ -1,159 +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 { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/ExtensionContent.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "AboutReader", "resource://gre/modules/AboutReader.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent", "resource://gre/modules/LoginManagerContent.jsm");
-
-var dump = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.d.bind(null, "Content");
-
-var global = this;
-
-// This is copied from desktop's tab-content.js. See bug 1153485 about sharing this code somehow.
-var AboutReaderListener = {
-
- _articlePromise: null,
-
- _isLeavingReaderMode: false,
-
- init: function() {
- addEventListener("AboutReaderContentLoaded", this, false, true);
- addEventListener("DOMContentLoaded", this, false);
- addEventListener("pageshow", this, false);
- addEventListener("pagehide", this, false);
- addMessageListener("Reader:ToggleReaderMode", this);
- addMessageListener("Reader:PushState", this);
- },
-
- receiveMessage: function(message) {
- switch (message.name) {
- case "Reader:ToggleReaderMode":
- let url = content.document.location.href;
- if (!this.isAboutReader) {
- this._articlePromise = ReaderMode.parseDocument(content.document).catch(Cu.reportError);
- ReaderMode.enterReaderMode(docShell, content);
- } else {
- this._isLeavingReaderMode = true;
- ReaderMode.leaveReaderMode(docShell, content);
- }
- break;
-
- case "Reader:PushState":
- this.updateReaderButton(!!(message.data && message.data.isArticle));
- break;
- }
- },
-
- get isAboutReader() {
- return content.document.documentURI.startsWith("about:reader");
- },
-
- handleEvent: function(aEvent) {
- if (aEvent.originalTarget.defaultView != content) {
- return;
- }
-
- switch (aEvent.type) {
- case "AboutReaderContentLoaded":
- if (!this.isAboutReader) {
- return;
- }
-
- // If we are restoring multiple reader mode tabs during session restore, duplicate "DOMContentLoaded"
- // events may be fired for the visible tab. The inital "DOMContentLoaded" may be received before the
- // document body is available, so we avoid instantiating an AboutReader object, expecting that a
- // valid message will follow. See bug 925983.
- if (content.document.body) {
- new AboutReader(global, content, this._articlePromise);
- this._articlePromise = null;
- }
- break;
-
- case "pagehide":
- // this._isLeavingReaderMode is used here to keep the Reader Mode icon
- // visible in the location bar when transitioning from reader-mode page
- // back to the source page.
- sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: this._isLeavingReaderMode });
- if (this._isLeavingReaderMode) {
- this._isLeavingReaderMode = false;
- }
- break;
-
- case "pageshow":
- // If a page is loaded from the bfcache, we won't get a "DOMContentLoaded"
- // event, so we need to rely on "pageshow" in this case.
- if (aEvent.persisted) {
- this.updateReaderButton();
- }
- break;
- case "DOMContentLoaded":
- this.updateReaderButton();
- break;
- }
- },
- updateReaderButton: function(forceNonArticle) {
- if (!ReaderMode.isEnabledForParseOnLoad || this.isAboutReader ||
- !(content.document instanceof content.HTMLDocument) ||
- content.document.mozSyntheticDocument) {
- return;
- }
-
- this.scheduleReadabilityCheckPostPaint(forceNonArticle);
- },
-
- cancelPotentialPendingReadabilityCheck: function() {
- if (this._pendingReadabilityCheck) {
- removeEventListener("MozAfterPaint", this._pendingReadabilityCheck);
- delete this._pendingReadabilityCheck;
- }
- },
-
- scheduleReadabilityCheckPostPaint: function(forceNonArticle) {
- if (this._pendingReadabilityCheck) {
- // We need to stop this check before we re-add one because we don't know
- // if forceNonArticle was true or false last time.
- this.cancelPotentialPendingReadabilityCheck();
- }
- this._pendingReadabilityCheck = this.onPaintWhenWaitedFor.bind(this, forceNonArticle);
- addEventListener("MozAfterPaint", this._pendingReadabilityCheck);
- },
-
- onPaintWhenWaitedFor: function(forceNonArticle, event) {
- // In non-e10s, we'll get called for paints other than ours, and so it's
- // possible that this page hasn't been laid out yet, in which case we
- // should wait until we get an event that does relate to our layout. We
- // determine whether any of our content got painted by checking if there
- // are any painted rects.
- if (!event.clientRects.length) {
- return;
- }
-
- this.cancelPotentialPendingReadabilityCheck();
-
- // Only send updates when there are articles; there's no point updating with
- // |false| all the time.
- if (ReaderMode.isProbablyReaderable(content.document)) {
- sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
- } else if (forceNonArticle) {
- sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
- }
- },
-};
-AboutReaderListener.init();
-
-addMessageListener("RemoteLogins:fillForm", function(message) {
- LoginManagerContent.receiveMessage(message, content);
-});
-
-ExtensionContent.init(this);
-addEventListener("unload", () => {
- ExtensionContent.uninit(this);
-});
diff --git a/mobile/android/chrome/content/geckoview.js b/mobile/android/chrome/content/geckoview.js
deleted file mode 100644
index b4685a8d3..000000000
--- a/mobile/android/chrome/content/geckoview.js
+++ /dev/null
@@ -1,32 +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 Cc = Components.classes;
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-var Cr = Components.results;
-
-Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Log",
- "resource://gre/modules/AndroidLog.jsm", "AndroidLog");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Messaging",
- "resource://gre/modules/Messaging.jsm", "Messaging");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Services",
- "resource://gre/modules/Services.jsm", "Services");
-
-function dump(msg) {
- Log.d("View", msg);
-}
-
-function startup() {
- dump("zerdatime " + Date.now() + " - geckoivew chrome startup finished.");
-
- // Notify Java that Gecko has loaded.
- Messaging.sendRequest({ type: "Gecko:Ready" });
-}
diff --git a/mobile/android/chrome/content/geckoview.xul b/mobile/android/chrome/content/geckoview.xul
deleted file mode 100644
index a3d4d1290..000000000
--- a/mobile/android/chrome/content/geckoview.xul
+++ /dev/null
@@ -1,16 +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/. -->
-
-<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
-
-<window id="main-window"
- onload="startup();"
- windowtype="navigator:browser"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
- <browser id="content" type="content-primary" src="https://mozilla.com" flex="1" remote="true"/>
-
- <script type="application/javascript" src="chrome://browser/content/geckoview.js"/>
-</window>
diff --git a/mobile/android/chrome/content/healthreport-prefs.js b/mobile/android/chrome/content/healthreport-prefs.js
deleted file mode 100644
index 5c4a50d38..000000000
--- a/mobile/android/chrome/content/healthreport-prefs.js
+++ /dev/null
@@ -1,6 +0,0 @@
-#filter substitution
-/* 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/. */
-
-pref("datareporting.healthreport.about.reportUrl", "https://fhr.cdn.mozilla.net/%LOCALE%/mobile/");
diff --git a/mobile/android/chrome/content/languages.properties b/mobile/android/chrome/content/languages.properties
deleted file mode 100644
index 53d30d125..000000000
--- a/mobile/android/chrome/content/languages.properties
+++ /dev/null
@@ -1,114 +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/.
-
-# LOCALIZATION NOTE: do not localize
-af=Afrikaans
-ak=Akan
-ar=عربي
-as=অসমীয়া
-ast-ES=Asturianu
-be=Беларуская
-bg=Български
-bn-BD=বাংলা (বাংলাদেশ)
-bn-IN=বাংলা (ভারত)
-br-FR=Brezhoneg
-ca=català
-ca-valencia=català (valencià)
-cs=Čeština
-cy=Cymraeg
-da=Dansk
-de=Deutsch
-de-AT=Deutsch (Österreich)
-de-CH=Deutsch (Schweiz)
-de-DE=Deutsch (Deutschland)
-el=Ελληνικά
-en-AU=English (Australian)
-en-CA=English (Canadian)
-en-GB=English (British)
-en-NZ=English (New Zealand)
-en-US=English (US)
-en-ZA=English (South African)
-eo=Esperanto
-es-AR=Español (de Argentina)
-es-CL=Español (de Chile)
-es-ES=Español (de España)
-es-MX=Español (de México)
-et=Eesti keel
-eu=Euskara
-fa=فارسی
-fi=suomi
-fr=Français
-fur-IT=Furlan
-fy-NL=Frysk
-ga-IE=Gaeilge
-gl=Galego
-gu-IN=ગુજરાતી
-he=עברית
-hi=हिन्दी
-hi-IN=हिन्दी (भारत)
-hr=Hrvatski
-hsb=Hornjoserbsce
-hu=Magyar
-hy-AM=Հայերեն
-id=Bahasa Indonesia
-is=íslenska
-it=Italiano
-ja=日本語
-ka=ქართული
-kk=Қазақ
-kn=ಕನ್ನಡ
-ko=한국어
-ku=Kurdî
-la=Latina
-lt=lietuvių
-lv=Latviešu
-mg=Malagasy
-mi=Māori (Aotearoa)
-mk=Македонски
-ml=മലയാളം
-mn=Монгол
-mr=मराठी
-nb-NO=Norsk bokmål
-ne-NP=नेपाली
-nl=Nederlands
-nn-NO=Norsk nynorsk
-nr=isiNdebele Sepumalanga
-nso=Sepedi
-oc=occitan (lengadocian)
-or=ଓଡ଼ିଆ
-pa-IN=ਪੰਜਾਬੀ
-pl=Polski
-pt-BR=Português (do Brasil)
-pt-PT=Português (Europeu)
-rm=rumantsch
-ro=română
-ru=Русский
-rw=Ikinyarwanda
-si=සිංහල
-sk=slovenčina
-sl=slovensko
-sq=Shqip
-sr=Српски
-sr-Latn=Srpski
-ss=Siswati
-st=Sesotho
-sv-SE=Svenska
-ta=தமிழ்
-ta-IN=தமிழ் (இந்தியா)
-ta-LK=தமிழ் (இலங்கை)
-te=తెలుగు
-th=ไทย
-tn=Setswana
-tr=Türkçe
-ts=Mutsonga
-tt-RU=Tatarça
-uk=Українська
-ur=اُردو
-ve=Tshivenḓa
-vi=Tiếng Việt
-wo=Wolof
-xh=isiXhosa
-zh-CN=中文 (简体)
-zh-TW=正體中文 (繁體)
-zu=isiZulu
diff --git a/mobile/android/chrome/content/netError.xhtml b/mobile/android/chrome/content/netError.xhtml
deleted file mode 100644
index f4c727c06..000000000
--- a/mobile/android/chrome/content/netError.xhtml
+++ /dev/null
@@ -1,406 +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>
- <meta name="viewport" content="width=device-width; user-scalable=false;" />
- <title>&loadError.label;</title>
- <link rel="stylesheet" href="chrome://global/skin/netError.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" sizes="64x64" href="chrome://browser/skin/images/errorpage-warning.png"/>
-
- <script type="application/javascript"><![CDATA[
- // Error url MUST be formatted like this:
- // moz-neterror:page?e=error&u=url&d=desc
- //
- // or optionally, to specify an alternate CSS class to allow for
- // custom styling and favicon:
- //
- // moz-neterror:page?e=error&u=url&s=classname&d=desc
-
- // 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 getCSSClass()
- {
- var url = document.documentURI;
- var matches = url.match(/s\=([^&]+)\&/);
- // s is optional, if no match just return nothing
- if (!matches || matches.length < 2)
- return "";
-
- // parenthetical match is the second entry
- return decodeURIComponent(matches[1]);
- }
-
- function getDescription()
- {
- var url = document.documentURI;
- var desc = url.search(/d\=/);
-
- // desc == -1 if not found; if so, return an empty string
- // instead of what would turn out to be portions of the URI
- if (desc == -1)
- return "";
-
- return decodeURIComponent(url.slice(desc + 2));
- }
-
- 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.
- }
- }
-
- function initPage()
- {
- var err = getErrorCode();
-
- // 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");
- }
-
- var title = document.getElementsByClassName("errorTitleText")[0];
- if (title)
- {
- title.parentNode.replaceChild(errTitle, title);
- // change id to the replaced child's id so styling works
- errTitle.classList.add("errorTitleText");
- }
-
- var sd = document.getElementById("errorShortDescText");
- if (sd)
- sd.textContent = getDescription();
-
- var ld = document.getElementById("errorLongDesc");
- if (ld)
- {
- ld.parentNode.replaceChild(errDesc, ld);
- // change id to the replaced child's id so styling works
- errDesc.id = "errorLongDesc";
- }
-
- // 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 (className == "expertBadCert") {
- showSecuritySection();
- }
-
- if (err == "remoteXUL") {
- // Remove the "Try again" button for remote XUL errors given that
- // it is useless.
- document.getElementById("errorTryAgain").style.display = "none";
- }
-
- if (err == "cspBlocked") {
- // Remove the "Try again" button for CSP violations, since it's
- // almost certainly useless. (Bug 553180)
- document.getElementById("errorTryAgain").style.display = "none";
- }
-
- if (err == "nssBadCert") {
- // Remove the "Try again" button for security exceptions, since it's
- // almost certainly useless.
- document.getElementById("errorTryAgain").style.display = "none";
- document.getElementById("errorPage").setAttribute("class", "certerror");
- }
- else {
- // Remove the override block for non-certificate errors. CSS-hiding
- // isn't good enough here, because of bug 39098
- var secOverride = document.getElementById("securityOverrideDiv");
- secOverride.parentNode.removeChild(secOverride);
- }
-
- 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 showSecuritySection() {
- // Swap link out, content in
- document.getElementById('securityOverrideContent').style.display = '';
- document.getElementById('securityOverrideLink').style.display = 'none';
- }
-
- /* 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 sd = document.getElementById("errorShortDescText");
- 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)));
- }
-
- // 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;
- }
-
- 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 id="errorPage" dir="&locale.dir;">
-
- <!-- 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_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>
-
- <!-- Since Fennec not yet have offline mode, change the title to
- connectionFailure to prevent confusion -->
- <h1 id="et_netOffline">&connectionFailure.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">&nssBadCert.title;</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_dnsNotFound">&dnsNotFound.longDesc4;</div>
- <div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
- <div id="ed_fileAccessDenied">&fileAccessDenied.longDesc;</div>
- <div id="ed_malformedURI">&malformedURI.longDesc2;</div>
- <div id="ed_unknownProtocolFound">&unknownProtocolFound.longDesc;</div>
- <div id="ed_connectionFailure">&connectionFailure.longDesc2;</div>
- <div id="ed_netTimeout">&netTimeout.longDesc2;</div>
- <div id="ed_redirectLoop">&redirectLoop.longDesc;</div>
- <div id="ed_unknownSocketType">&unknownSocketType.longDesc;</div>
- <div id="ed_netReset">&netReset.longDesc2;</div>
- <div id="ed_notCached">&notCached.longDesc;</div>
-
- <!-- Change longDesc from netOffline to connectionFailure,
- suggesting user to check their wifi/cell_data connection -->
- <div id="ed_netOffline">&connectionFailure.longDesc2;</div>
-
- <div id="ed_netInterrupt">&netInterrupt.longDesc2;</div>
- <div id="ed_deniedPortAccess">&deniedPortAccess.longDesc;</div>
- <div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc3;</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">&nssBadCert.longDesc2;</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.longDesc;</div>
- <div id="ed_weakCryptoUsed">&weakCryptoUsed.longDesc;</div>
- <div id="ed_inadequateSecurityError">&inadequateSecurityError.longDesc;</div>
- </div>
- </div>
-
- <!-- PAGE CONTAINER (for styling purposes only) -->
- <div id="errorPageContainer">
-
- <!-- Error Title -->
- <div id="errorTitle">
- <h1 class="errorTitleText" />
- </div>
-
- <!-- LONG CONTENT (the section most likely to require scrolling) -->
- <div id="errorLongContent">
-
- <!-- Short Description -->
- <div id="errorShortDesc">
- <p id="errorShortDescText" />
- </div>
-
- <!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
- <div id="errorLongDesc" />
-
- <!-- Override section - For ssl errors only. Removed on init for other
- error types. -->
- <div id="securityOverrideDiv">
- <a id="securityOverrideLink" href="javascript:showSecuritySection();" >&securityOverride.linkText;</a>
- <div id="securityOverrideContent" style="display: none;">&securityOverride.warningContent;</div>
- </div>
- </div>
-
- <!-- Retry Button -->
- <button id="errorTryAgain" onclick="retryThis(this);">&retry.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>