diff options
Diffstat (limited to 'mobile/android/components')
48 files changed, 0 insertions, 7580 deletions
diff --git a/mobile/android/components/AboutRedirector.js b/mobile/android/components/AboutRedirector.js deleted file mode 100644 index df50864dd..000000000 --- a/mobile/android/components/AboutRedirector.js +++ /dev/null @@ -1,132 +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 Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; - -Cu.import("resource://gre/modules/AppConstants.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -var modules = { - // about: - "": { - uri: "chrome://browser/content/about.xhtml", - privileged: true - }, - - // about:fennec and about:firefox are aliases for about:, - // but hidden from about:about - fennec: { - uri: "chrome://browser/content/about.xhtml", - privileged: true, - hide: true - }, - get firefox() { - return this.fennec - }, - - // about:blank has some bad loading behavior we can avoid, if we use an alias - empty: { - uri: "about:blank", - privileged: false, - hide: true - }, - - rights: { - uri: "chrome://browser/content/aboutRights.xhtml", - privileged: false - }, - blocked: { - uri: "chrome://browser/content/blockedSite.xhtml", - privileged: false, - hide: true - }, - certerror: { - uri: "chrome://browser/content/aboutCertError.xhtml", - privileged: false, - hide: true - }, - home: { - uri: "chrome://browser/content/aboutHome.xhtml", - privileged: false - }, - downloads: { - uri: "chrome://browser/content/aboutDownloads.xhtml", - privileged: true - }, - reader: { - uri: "chrome://global/content/reader/aboutReader.html", - privileged: false, - hide: true - }, - feedback: { - uri: "chrome://browser/content/aboutFeedback.xhtml", - privileged: true - }, - privatebrowsing: { - uri: "chrome://browser/content/aboutPrivateBrowsing.xhtml", - privileged: true - }, - logins: { - uri: "chrome://browser/content/aboutLogins.xhtml", - privileged: true - }, - accounts: { - uri: "chrome://browser/content/aboutAccounts.xhtml", - privileged: true - }, -}; - -if (AppConstants.MOZ_SERVICES_HEALTHREPORT) { - modules['healthreport'] = { - uri: "chrome://browser/content/aboutHealthReport.xhtml", - privileged: true - }; -} - -function AboutRedirector() {} -AboutRedirector.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]), - classID: Components.ID("{322ba47e-7047-4f71-aebf-cb7d69325cd9}"), - - _getModuleInfo: function (aURI) { - let moduleName = aURI.path.replace(/[?#].*/, "").toLowerCase(); - return modules[moduleName]; - }, - - // nsIAboutModule - getURIFlags: function(aURI) { - let flags; - let moduleInfo = this._getModuleInfo(aURI); - if (moduleInfo.hide) - flags = Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT; - - return flags | Ci.nsIAboutModule.ALLOW_SCRIPT; - }, - - newChannel: function(aURI, aLoadInfo) { - let moduleInfo = this._getModuleInfo(aURI); - - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - - var newURI = ios.newURI(moduleInfo.uri, null, null); - - var channel = ios.newChannelFromURIWithLoadInfo(newURI, aLoadInfo); - - if (!moduleInfo.privileged) { - // Setting the owner to null means that we'll go through the normal - // path in GetChannelPrincipal and create a codebase principal based - // on the channel's originalURI - channel.owner = null; - } - - channel.originalURI = aURI; - - return channel; - } -}; - -const components = [AboutRedirector]; -this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); diff --git a/mobile/android/components/AddonUpdateService.js b/mobile/android/components/AddonUpdateService.js deleted file mode 100644 index b2c4732c3..000000000 --- a/mobile/android/components/AddonUpdateService.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/. */ - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate", - "resource://gre/modules/AddonManager.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository", - "resource://gre/modules/addons/AddonRepository.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "GMPInstallManager", - "resource://gre/modules/GMPInstallManager.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", - "resource://gre/modules/Messaging.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); - -// ----------------------------------------------------------------------- -// Add-on auto-update management service -// ----------------------------------------------------------------------- - -const PREF_ADDON_UPDATE_ENABLED = "extensions.autoupdate.enabled"; -const PREF_ADDON_UPDATE_INTERVAL = "extensions.autoupdate.interval"; - -var gNeedsRestart = false; - -function AddonUpdateService() {} - -AddonUpdateService.prototype = { - classDescription: "Add-on auto-update management", - classID: Components.ID("{93c8824c-9b87-45ae-bc90-5b82a1e4d877}"), - - QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback]), - - notify: function aus_notify(aTimer) { - if (aTimer && !Services.prefs.getBoolPref(PREF_ADDON_UPDATE_ENABLED, true)) - return; - - // If we already auto-upgraded and installed new versions, ignore this check - if (gNeedsRestart) - return; - - AddonManagerPrivate.backgroundUpdateCheck(); - - let gmp = new GMPInstallManager(); - gmp.simpleCheckAndInstall().then(null, () => {}); - - let interval = 1000 * Services.prefs.getIntPref(PREF_ADDON_UPDATE_INTERVAL, 86400); - Messaging.sendRequest({ - type: "Gecko:ScheduleRun", - action: "update-addons", - trigger: interval, - interval: interval, - }); - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AddonUpdateService]); - diff --git a/mobile/android/components/BlocklistPrompt.js b/mobile/android/components/BlocklistPrompt.js deleted file mode 100644 index ce7b8e011..000000000 --- a/mobile/android/components/BlocklistPrompt.js +++ /dev/null @@ -1,61 +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 Ci = Components.interfaces; -const Cu = Components.utils; -const Cc = Components.classes; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -// ----------------------------------------------------------------------- -// BlocklistPrompt Service -// ----------------------------------------------------------------------- - - -function BlocklistPrompt() { } - -BlocklistPrompt.prototype = { - prompt: function(aAddons, aCount) { - let win = Services.wm.getMostRecentWindow("navigator:browser"); - if (win.ExtensionsView.visible) { - win.ExtensionsView.showRestart("blocked"); - } else { - let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); - let notifyBox = win.getNotificationBox(); - let restartCallback = function(aNotification, aDescription) { - // Notify all windows that an application quit has been requested - var 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) { - let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup); - appStartup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit); - } - }; - - let buttons = [{accessKey: null, - label: bundle.GetStringFromName("notificationRestart.button"), - callback: restartCallback}]; - notifyBox.appendNotification(bundle.GetStringFromName("notificationRestart.blocked"), - "blocked-add-on", - "", - "PRIORITY_CRITICAL_HIGH", - buttons); - } - // Disable softblocked items automatically - for (let i = 0; i < aAddons.length; i++) { - if (aAddons[i].item instanceof Ci.nsIPluginTag) - aAddons[i].item.disabled = true; - else - aAddons[i].item.userDisabled = true; - } - }, - classID: Components.ID("{4e6ea350-b09a-11df-94e2-0800200c9a66}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIBlocklistPrompt]) -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([BlocklistPrompt]); - diff --git a/mobile/android/components/BrowserCLH.js b/mobile/android/components/BrowserCLH.js deleted file mode 100644 index 4cbf03554..000000000 --- a/mobile/android/components/BrowserCLH.js +++ /dev/null @@ -1,47 +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 Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -function BrowserCLH() {} - -BrowserCLH.prototype = { - /** - * Register resource://android as the APK root. - * - * Consumers can access Android assets using resource://android/assets/FILENAME. - */ - setResourceSubstitutions: function () { - let registry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci["nsIChromeRegistry"]); - // Like jar:jar:file:///data/app/org.mozilla.fennec-2.apk!/assets/omni.ja!/chrome/chrome/content/aboutHome.xhtml - let url = registry.convertChromeURL(Services.io.newURI("chrome://browser/content/aboutHome.xhtml", null, null)).spec; - // Like jar:file:///data/app/org.mozilla.fennec-2.apk!/ - url = url.substring(4, url.indexOf("!/") + 2); - - let protocolHandler = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler); - protocolHandler.setSubstitution("android", Services.io.newURI(url, null, null)); - }, - - observe: function (subject, topic, data) { - switch (topic) { - case "app-startup": - this.setResourceSubstitutions(); - break; - } - }, - - // QI - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), - - // XPCOMUtils factory - classID: Components.ID("{be623d20-d305-11de-8a39-0800200c9a66}") -}; - -var components = [ BrowserCLH ]; -this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); diff --git a/mobile/android/components/ColorPicker.js b/mobile/android/components/ColorPicker.js deleted file mode 100644 index 7d478da80..000000000 --- a/mobile/android/components/ColorPicker.js +++ /dev/null @@ -1,55 +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 Ci = Components.interfaces; -const Cu = Components.utils; -const Cc = Components.classes; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Prompt", - "resource://gre/modules/Prompt.jsm"); - -function ColorPicker() { -} - -ColorPicker.prototype = { - _initial: 0, - _domWin: null, - _title: "", - - get strings() { - if (!this._strings) { - this._strings = Services.strings.createBundle("chrome://browser/locale/browser.properties"); - } - return this._strings; - }, - - init: function(aParent, aTitle, aInitial) { - this._domWin = aParent; - this._initial = aInitial; - this._title = aTitle; - }, - - open: function(aCallback) { - let p = new Prompt({ title: this._title, - buttons: [ - this.strings.GetStringFromName("inputWidgetHelper.set"), - this.strings.GetStringFromName("inputWidgetHelper.cancel") - ] }) - .addColorPicker({ value: this._initial }) - .show((data) => { - if (data.button == 0) - aCallback.done(data.color0); - else - aCallback.done(this._initial); - }); - }, - - classID: Components.ID("{430b987f-bb9f-46a3-99a5-241749220b29}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIColorPicker]) -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ColorPicker]); diff --git a/mobile/android/components/ContentDispatchChooser.js b/mobile/android/components/ContentDispatchChooser.js deleted file mode 100644 index b28e356e0..000000000 --- a/mobile/android/components/ContentDispatchChooser.js +++ /dev/null @@ -1,83 +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 Ci = Components.interfaces; -const Cu = Components.utils; -const Cc = Components.classes; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); - -function ContentDispatchChooser() {} - -ContentDispatchChooser.prototype = -{ - classID: Components.ID("5a072a22-1e66-4100-afc1-07aed8b62fc5"), - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentDispatchChooser]), - - get protoSvc() { - if (!this._protoSvc) { - this._protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].getService(Ci.nsIExternalProtocolService); - } - return this._protoSvc; - }, - - _getChromeWin: function getChromeWin() { - try { - return Services.wm.getMostRecentWindow("navigator:browser"); - } catch (e) { - throw Cr.NS_ERROR_FAILURE; - } - }, - - ask: function ask(aHandler, aWindowContext, aURI, aReason) { - let window = null; - try { - if (aWindowContext) - window = aWindowContext.getInterface(Ci.nsIDOMWindow); - } catch (e) { /* it's OK to not have a window */ } - - // The current list is based purely on the scheme. Redo the query using the url to get more - // specific results. - aHandler = this.protoSvc.getProtocolHandlerInfoFromOS(aURI.spec, {}); - - // The first handler in the set is the Android Application Chooser (which will fall back to a default if one is set) - // If we have more than one option, let the OS handle showing a list (if needed). - if (aHandler.possibleApplicationHandlers.length > 1) { - aHandler.launchWithURI(aURI, aWindowContext); - } else { - // xpcshell tests do not have an Android Bridge but we require Android - // Bridge when using Messaging so we guard against this case. xpcshell - // tests also do not have a window, so we use this state to guard. - let win = this._getChromeWin(); - if (!win) { - return; - } - - let msg = { - type: "Intent:OpenNoHandler", - uri: aURI.spec, - }; - - Messaging.sendRequestForResult(msg).then(() => { - // Java opens an app on success: take no action. - }, (uri) => { - // We couldn't open this. If this was from a click, it's likely that we just - // want this to fail silently. If the user entered this on the address bar, though, - // we want to show the neterror page. - - let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - let millis = dwu.millisSinceLastUserInput; - if (millis > 0 && millis >= 1000) { - window.location.href = uri; - } - }); - } - }, -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentDispatchChooser]); diff --git a/mobile/android/components/ContentPermissionPrompt.js b/mobile/android/components/ContentPermissionPrompt.js deleted file mode 100644 index fd13ce26b..000000000 --- a/mobile/android/components/ContentPermissionPrompt.js +++ /dev/null @@ -1,146 +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 Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; -const Cc = Components.classes; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -const kEntities = { - "contacts": "contacts", - "desktop-notification": "desktopNotification2", - "geolocation": "geolocation", - "flyweb-publish-server": "flyWebPublishServer", -}; - -// For these types, prompt for permission if action is unknown. -const PROMPT_FOR_UNKNOWN = [ - "desktop-notification", - "geolocation", - "flyweb-publish-server", -]; - -function ContentPermissionPrompt() {} - -ContentPermissionPrompt.prototype = { - classID: Components.ID("{C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5}"), - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]), - - handleExistingPermission: function handleExistingPermission(request, type, denyUnknown) { - let result = Services.perms.testExactPermissionFromPrincipal(request.principal, type); - if (result == Ci.nsIPermissionManager.ALLOW_ACTION) { - request.allow(); - return true; - } - - if (result == Ci.nsIPermissionManager.DENY_ACTION) { - request.cancel(); - return true; - } - - if (denyUnknown && result == Ci.nsIPermissionManager.UNKNOWN_ACTION) { - request.cancel(); - return true; - } - - return false; - }, - - getChromeWindow: function getChromeWindow(aWindow) { - let chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow) - .QueryInterface(Ci.nsIDOMChromeWindow); - return chromeWin; - }, - - getChromeForRequest: function getChromeForRequest(request) { - if (request.window) { - let requestingWindow = request.window.top; - return this.getChromeWindow(requestingWindow).wrappedJSObject; - } - return request.element.ownerDocument.defaultView; - }, - - prompt: function(request) { - let isApp = request.principal.appId !== Ci.nsIScriptSecurityManager.NO_APP_ID && request.principal.appId !== Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID; - - // Only allow exactly one permission rquest here. - let types = request.types.QueryInterface(Ci.nsIArray); - if (types.length != 1) { - request.cancel(); - return; - } - let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); - - // Returns true if the request was handled - let access = (perm.access && perm.access !== "unused") ? - (perm.type + "-" + perm.access) : perm.type; - if (this.handleExistingPermission(request, access, - /* denyUnknown */ isApp || PROMPT_FOR_UNKNOWN.indexOf(perm.type) < 0)) - return; - - let chromeWin = this.getChromeForRequest(request); - let tab = chromeWin.BrowserApp.getTabForWindow(request.window.top); - if (!tab) - return; - - let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); - let entityName = kEntities[perm.type]; - - let buttons = [{ - label: browserBundle.GetStringFromName(entityName + ".dontAllow"), - callback: function(aChecked) { - // If the user checked "Don't ask again" or this is a desktopNotification, make a permanent exception - if (aChecked || entityName == "desktopNotification2") - Services.perms.addFromPrincipal(request.principal, access, Ci.nsIPermissionManager.DENY_ACTION); - - request.cancel(); - } - }, - { - label: browserBundle.GetStringFromName(entityName + ".allow"), - callback: function(aChecked) { - // If the user checked "Don't ask again" or this is a desktopNotification, make a permanent exception - if (aChecked || entityName == "desktopNotification2") { - Services.perms.addFromPrincipal(request.principal, access, Ci.nsIPermissionManager.ALLOW_ACTION); - } else if (isApp) { - // Otherwise allow the permission for the current session if the request comes from an app - Services.perms.addFromPrincipal(request.principal, access, Ci.nsIPermissionManager.ALLOW_ACTION, Ci.nsIPermissionManager.EXPIRE_SESSION); - } - - request.allow(); - }, - positive: true - }]; - - let requestor = chromeWin.BrowserApp.manifest ? "'" + chromeWin.BrowserApp.manifest.name + "'" : request.principal.URI.host; - let message = browserBundle.formatStringFromName(entityName + ".ask", [requestor], 1); - // desktopNotification doesn't have a checkbox - let options; - if (entityName == "desktopNotification2") { - options = { - link: { - label: browserBundle.GetStringFromName("doorhanger.learnMore"), - url: "https://www.mozilla.org/firefox/push/" - } - }; - } else { - options = { checkbox: browserBundle.GetStringFromName(entityName + ".dontAskAgain") }; - } - - chromeWin.NativeWindow.doorhanger.show(message, entityName + request.principal.URI.host, buttons, tab.id, options, entityName.toUpperCase()); - } -}; - - -//module initialization -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentPermissionPrompt]); diff --git a/mobile/android/components/DirectoryProvider.js b/mobile/android/components/DirectoryProvider.js deleted file mode 100644 index 5d0f7974c..000000000 --- a/mobile/android/components/DirectoryProvider.js +++ /dev/null @@ -1,214 +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 Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; - -Cu.import("resource://gre/modules/AppConstants.jsm"); -Cu.import("resource://gre/modules/FileUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "JNI", "resource://gre/modules/JNI.jsm"); - -// ----------------------------------------------------------------------- -// Directory Provider for special browser folders and files -// ----------------------------------------------------------------------- - -const NS_APP_CACHE_PARENT_DIR = "cachePDir"; -const NS_APP_SEARCH_DIR = "SrchPlugns"; -const NS_APP_SEARCH_DIR_LIST = "SrchPluginsDL"; -const NS_APP_DISTRIBUTION_SEARCH_DIR_LIST = "SrchPluginsDistDL"; -const NS_APP_USER_SEARCH_DIR = "UsrSrchPlugns"; -const NS_XPCOM_CURRENT_PROCESS_DIR = "XCurProcD"; -const XRE_APP_DISTRIBUTION_DIR = "XREAppDist"; -const XRE_UPDATE_ROOT_DIR = "UpdRootD"; -const ENVVAR_UPDATE_DIR = "UPDATES_DIRECTORY"; -const WEBAPPS_DIR = "webappsDir"; - -const SYSTEM_DIST_PATH = `/system/${AppConstants.ANDROID_PACKAGE_NAME}/distribution`; - -function DirectoryProvider() {} - -DirectoryProvider.prototype = { - classID: Components.ID("{ef0f7a87-c1ee-45a8-8d67-26f586e46a4b}"), - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider, - Ci.nsIDirectoryServiceProvider2]), - - getFile: function(prop, persistent) { - if (prop == NS_APP_CACHE_PARENT_DIR) { - let dirsvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); - let profile = dirsvc.get("ProfD", Ci.nsIFile); - return profile; - } else if (prop == WEBAPPS_DIR) { - // returns the folder that should hold the webapps database file - // For fennec we will store that in the root profile folder so that all - // webapps can easily access it - let dirsvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); - let profile = dirsvc.get("ProfD", Ci.nsIFile); - return profile.parent; - } else if (prop == XRE_APP_DISTRIBUTION_DIR) { - let distributionDirectories = this._getDistributionDirectories(); - for (let i = 0; i < distributionDirectories.length; i++) { - if (distributionDirectories[i].exists()) { - return distributionDirectories[i]; - } - } - // Fallback: Return default data distribution directory - return FileUtils.getDir(NS_XPCOM_CURRENT_PROCESS_DIR, ["distribution"], false); - } else if (prop == XRE_UPDATE_ROOT_DIR) { - let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); - if (env.exists(ENVVAR_UPDATE_DIR)) { - let path = env.get(ENVVAR_UPDATE_DIR); - if (path) { - return new FileUtils.File(path); - } - } - return new FileUtils.File(env.get("DOWNLOADS_DIRECTORY")); - } - - // We are retuning null to show failure instead for throwing an error. The - // interface is called quite a bit and throwing an error is noisy. Returning - // null works with the way the interface is called [see bug 529077] - return null; - }, - - /** - * Appends the distribution-specific search engine directories to the array. - * The distribution directory structure is as follows: - * - * \- distribution/ - * \- searchplugins/ - * |- common/ - * \- locale/ - * |- <locale 1>/ - * ... - * \- <locale N>/ - * - * Common engines are loaded for all locales. If there is no locale directory for - * the current locale, there is a pref: "distribution.searchplugins.defaultLocale", - * which specifies a default locale to use. - */ - _appendDistroSearchDirs: function(array) { - let distro = this.getFile(XRE_APP_DISTRIBUTION_DIR); - if (!distro.exists()) - return; - - let searchPlugins = distro.clone(); - searchPlugins.append("searchplugins"); - if (!searchPlugins.exists()) - return; - - let commonPlugins = searchPlugins.clone(); - commonPlugins.append("common"); - if (commonPlugins.exists()) - array.push(commonPlugins); - - let localePlugins = searchPlugins.clone(); - localePlugins.append("locale"); - if (!localePlugins.exists()) - return; - - let curLocale = ""; - try { - curLocale = Services.prefs.getComplexValue("general.useragent.locale", Ci.nsIPrefLocalizedString).data; - } catch (e) { - try { - curLocale = Services.prefs.getCharPref("general.useragent.locale"); - } catch (ee) { - } - } - - if (curLocale) { - let curLocalePlugins = localePlugins.clone(); - curLocalePlugins.append(curLocale); - if (curLocalePlugins.exists()) { - array.push(curLocalePlugins); - return; - } - } - - // We didn't append the locale dir - try the default one. - try { - let defLocale = Services.prefs.getCharPref("distribution.searchplugins.defaultLocale"); - let defLocalePlugins = localePlugins.clone(); - defLocalePlugins.append(defLocale); - if (defLocalePlugins.exists()) - array.push(defLocalePlugins); - } catch(e) { - } - }, - - getFiles: function(prop) { - if (prop != NS_APP_SEARCH_DIR_LIST && - prop != NS_APP_DISTRIBUTION_SEARCH_DIR_LIST) - return null; - - let result = []; - - if (prop == NS_APP_DISTRIBUTION_SEARCH_DIR_LIST) { - this._appendDistroSearchDirs(result); - } - else { - /** - * We want to preserve the following order, since the search service - * loads engines in first-loaded-wins order. - * - distro search plugin locations (loaded separately by the search - * service) - * - user search plugin locations (profile) - * - app search plugin location (shipped engines) - */ - let appUserSearchDir = FileUtils.getDir(NS_APP_USER_SEARCH_DIR, [], false); - if (appUserSearchDir.exists()) - result.push(appUserSearchDir); - - let appSearchDir = FileUtils.getDir(NS_APP_SEARCH_DIR, [], false); - if (appSearchDir.exists()) - result.push(appSearchDir); - } - - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]), - hasMoreElements: function() { - return result.length > 0; - }, - getNext: function() { - return result.shift(); - } - }; - }, - - _getDistributionDirectories: function() { - let directories = []; - let jenv = null; - - try { - jenv = JNI.GetForThread(); - - let jDistribution = JNI.LoadClass(jenv, "org.mozilla.gecko.distribution.Distribution", { - static_methods: [ - { name: "getDistributionDirectories", sig: "()[Ljava/lang/String;" } - ], - }); - - let jDirectories = jDistribution.getDistributionDirectories(); - - for (let i = 0; i < jDirectories.length; i++) { - directories.push(new FileUtils.File( - JNI.ReadString(jenv, jDirectories.get(i)) - )); - } - } finally { - if (jenv) { - JNI.UnloadClasses(jenv); - } - } - - return directories; - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DirectoryProvider]); diff --git a/mobile/android/components/FilePicker.js b/mobile/android/components/FilePicker.js deleted file mode 100644 index 2de81ca46..000000000 --- a/mobile/android/components/FilePicker.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/. */ - -const Ci = Components.interfaces; -const Cu = Components.utils; -const Cc = Components.classes; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/FileUtils.jsm"); - -Cu.importGlobalProperties(['File']); - -function FilePicker() { -} - -FilePicker.prototype = { - _mimeTypeFilter: 0, - _extensionsFilter: "", - _defaultString: "", - _domWin: null, - _defaultExtension: null, - _displayDirectory: null, - _filePath: null, - _promptActive: false, - _filterIndex: 0, - _addToRecentDocs: false, - _title: "", - - init: function(aParent, aTitle, aMode) { - this._domWin = aParent; - this._mode = aMode; - this._title = aTitle; - Services.obs.addObserver(this, "FilePicker:Result", false); - - let idService = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); - this.guid = idService.generateUUID().toString(); - - if (aMode != Ci.nsIFilePicker.modeOpen && aMode != Ci.nsIFilePicker.modeOpenMultiple) - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - }, - - appendFilters: function(aFilterMask) { - if (aFilterMask & Ci.nsIFilePicker.filterAudio) { - this._mimeTypeFilter = "audio/*"; - return; - } - - if (aFilterMask & Ci.nsIFilePicker.filterImages) { - this._mimeTypeFilter = "image/*"; - return; - } - - if (aFilterMask & Ci.nsIFilePicker.filterVideo) { - this._mimeTypeFilter = "video/*"; - return; - } - - if (aFilterMask & Ci.nsIFilePicker.filterAll) { - this._mimeTypeFilter = "*/*"; - return; - } - - /* From BaseFilePicker.cpp */ - if (aFilterMask & Ci.nsIFilePicker.filterHTML) { - this.appendFilter("*.html; *.htm; *.shtml; *.xhtml"); - } - if (aFilterMask & Ci.nsIFilePicker.filterText) { - this.appendFilter("*.txt; *.text"); - } - - if (aFilterMask & Ci.nsIFilePicker.filterXML) { - this.appendFilter("*.xml"); - } - - if (aFilterMask & Ci.nsIFilePicker.xulFilter) { - this.appendFilter("*.xul"); - } - - if (aFilterMask & Ci.nsIFilePicker.xulFilter) { - this.appendFilter("..apps"); - } - }, - - appendFilter: function(title, filter) { - if (this._extensionsFilter) - this._extensionsFilter += ", "; - this._extensionsFilter += filter; - }, - - get defaultString() { - return this._defaultString; - }, - - set defaultString(defaultString) { - this._defaultString = defaultString; - }, - - get defaultExtension() { - return this._defaultExtension; - }, - - set defaultExtension(defaultExtension) { - this._defaultExtension = defaultExtension; - }, - - get filterIndex() { - return this._filterIndex; - }, - - set filterIndex(val) { - this._filterIndex = val; - }, - - get displayDirectory() { - return this._displayDirectory; - }, - - set displayDirectory(dir) { - this._displayDirectory = dir; - }, - - get file() { - if (!this._filePath) { - return null; - } - - return new FileUtils.File(this._filePath); - }, - - get fileURL() { - let file = this.getFile(); - return Services.io.newFileURI(file); - }, - - get files() { - return this.getEnumerator([this.file], function(file) { - return file; - }); - }, - - // We don't support directory selection yet. - get domFileOrDirectory() { - let f = this.file; - if (!f) { - return null; - } - - let win = this._domWin; - if (win) { - let utils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - return utils.wrapDOMFile(f); - } - - return File.createFromNsIFile(f); - }, - - get domFileOrDirectoryEnumerator() { - let win = this._domWin; - return this.getEnumerator([this.file], function(file) { - if (win) { - let utils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - return utils.wrapDOMFile(file); - } - - return File.createFromNsIFile(file); - }); - }, - - get addToRecentDocs() { - return this._addToRecentDocs; - }, - - set addToRecentDocs(val) { - this._addToRecentDocs = val; - }, - - get mode() { - return this._mode; - }, - - show: function() { - if (this._domWin) { - this.fireDialogEvent(this._domWin, "DOMWillOpenModalDialog"); - let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - winUtils.enterModalState(); - } - - this._promptActive = true; - this._sendMessage(); - - let thread = Services.tm.currentThread; - while (this._promptActive) - thread.processNextEvent(true); - delete this._promptActive; - - if (this._domWin) { - let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - winUtils.leaveModalState(); - this.fireDialogEvent(this._domWin, "DOMModalDialogClosed"); - } - - if (this._filePath) - return Ci.nsIFilePicker.returnOK; - - return Ci.nsIFilePicker.returnCancel; - }, - - open: function(callback) { - this._callback = callback; - this._sendMessage(); - }, - - _sendMessage: function() { - let msg = { - type: "FilePicker:Show", - guid: this.guid, - title: this._title, - }; - - // Knowing the window lets us destroy any temp files when the tab is closed - // Other consumers of the file picker may have to either wait for Android - // to clean up the temp dir (not guaranteed) or clean up after themselves. - let win = Services.wm.getMostRecentWindow('navigator:browser'); - let tab = win.BrowserApp.getTabForWindow(this._domWin.top) - if (tab) { - msg.tabId = tab.id; - } - - if (!this._extensionsFilter && !this._mimeTypeFilter) { - // If neither filters is set show anything we can. - msg.mode = "mimeType"; - msg.mimeType = "*/*"; - } else if (this._extensionsFilter) { - msg.mode = "extension"; - msg.extensions = this._extensionsFilter; - } else { - msg.mode = "mimeType"; - msg.mimeType = this._mimeTypeFilter; - } - - this.sendMessageToJava(msg); - }, - - sendMessageToJava: function(aMsg) { - Services.androidBridge.handleGeckoMessage(aMsg); - }, - - observe: function(aSubject, aTopic, aData) { - let data = JSON.parse(aData); - if (data.guid != this.guid) - return; - - this._filePath = null; - if (data.file) - this._filePath = data.file; - - this._promptActive = false; - - if (this._callback) { - this._callback.done(this._filePath ? Ci.nsIFilePicker.returnOK : Ci.nsIFilePicker.returnCancel); - } - delete this._callback; - }, - - getEnumerator: function(files, mapFunction) { - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]), - mFiles: files, - mIndex: 0, - hasMoreElements: function() { - return (this.mIndex < this.mFiles.length); - }, - getNext: function() { - if (this.mIndex >= this.mFiles.length) { - throw Components.results.NS_ERROR_FAILURE; - } - return mapFunction(this.mFiles[this.mIndex++]); - } - }; - }, - - fireDialogEvent: function(aDomWin, aEventName) { - // accessing the document object can throw if this window no longer exists. See bug 789888. - try { - if (!aDomWin.document) - return; - let event = aDomWin.document.createEvent("Events"); - event.initEvent(aEventName, true, true); - let winUtils = aDomWin.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - winUtils.dispatchEventToChromeOnly(aDomWin, event); - } catch(ex) { - } - }, - - classID: Components.ID("{18a4e042-7c7c-424b-a583-354e68553a7f}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIFilePicker, Ci.nsIObserver]) -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FilePicker]); diff --git a/mobile/android/components/HelperAppDialog.js b/mobile/android/components/HelperAppDialog.js deleted file mode 100644 index f127fb0b3..000000000 --- a/mobile/android/components/HelperAppDialog.js +++ /dev/null @@ -1,373 +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/. */ - -/*globals ContentAreaUtils */ - -const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; - -const APK_MIME_TYPE = "application/vnd.android.package-archive"; - -const OMA_DOWNLOAD_DESCRIPTOR_MIME_TYPE = "application/vnd.oma.dd+xml"; -const OMA_DRM_MESSAGE_MIME = "application/vnd.oma.drm.message"; -const OMA_DRM_CONTENT_MIME = "application/vnd.oma.drm.content"; -const OMA_DRM_RIGHTS_MIME = "application/vnd.oma.drm.rights+wbxml"; - -const PREF_BD_USEDOWNLOADDIR = "browser.download.useDownloadDir"; -const URI_GENERIC_ICON_DOWNLOAD = "drawable://alert_download"; - -Cu.import("resource://gre/modules/Downloads.jsm"); -Cu.import("resource://gre/modules/FileUtils.jsm"); -Cu.import("resource://gre/modules/HelperApps.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/NetUtil.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "RuntimePermissions", "resource://gre/modules/RuntimePermissions.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm"); - -// ----------------------------------------------------------------------- -// HelperApp Launcher Dialog -// ----------------------------------------------------------------------- - -XPCOMUtils.defineLazyGetter(this, "ContentAreaUtils", function() { - let ContentAreaUtils = {}; - Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", ContentAreaUtils); - return ContentAreaUtils; -}); - -function HelperAppLauncherDialog() { } - -HelperAppLauncherDialog.prototype = { - classID: Components.ID("{e9d277a0-268a-4ec2-bb8c-10fdf3e44611}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog]), - - /** - * Returns false if `url` represents a local or special URL that we don't - * wish to ever download. - * - * Returns true otherwise. - */ - _canDownload: function (url, alreadyResolved=false) { - // The common case. - if (url.schemeIs("http") || - url.schemeIs("https") || - url.schemeIs("ftp")) { - return true; - } - - // The less-common opposite case. - if (url.schemeIs("chrome") || - url.schemeIs("jar") || - url.schemeIs("resource") || - url.schemeIs("wyciwyg") || - url.schemeIs("file")) { - return false; - } - - // For all other URIs, try to resolve them to an inner URI, and check that. - if (!alreadyResolved) { - let innerURI = NetUtil.newChannel({ - uri: url, - loadUsingSystemPrincipal: true - }).URI; - - if (!url.equals(innerURI)) { - return this._canDownload(innerURI, true); - } - } - - // Anything else is fine to download. - return true; - }, - - /** - * Returns true if `launcher` represents a download for which we wish - * to prompt. - */ - _shouldPrompt: function (launcher) { - let mimeType = this._getMimeTypeFromLauncher(launcher); - - // Straight equality: nsIMIMEInfo normalizes. - return APK_MIME_TYPE == mimeType || OMA_DOWNLOAD_DESCRIPTOR_MIME_TYPE == mimeType; - }, - - /** - * Returns true if `launcher` represents a download for which we wish to - * offer a "Save to disk" option. - */ - _shouldAddSaveToDiskIntent: function(launcher) { - let mimeType = this._getMimeTypeFromLauncher(launcher); - - // We can't handle OMA downloads. So don't even try. (Bug 1219078) - return mimeType != OMA_DOWNLOAD_DESCRIPTOR_MIME_TYPE; - }, - - /** - * Returns true if `launcher`represents a download that should not be handled by Firefox - * or a third-party app and instead be forwarded to Android's download manager. - */ - _shouldForwardToAndroidDownloadManager: function(aLauncher) { - let forwardDownload = Services.prefs.getBoolPref('browser.download.forward_oma_android_download_manager'); - if (!forwardDownload) { - return false; - } - - let mimeType = aLauncher.MIMEInfo.MIMEType; - if (!mimeType) { - mimeType = ContentAreaUtils.getMIMETypeForURI(aLauncher.source) || ""; - } - - return [ - OMA_DOWNLOAD_DESCRIPTOR_MIME_TYPE, - OMA_DRM_MESSAGE_MIME, - OMA_DRM_CONTENT_MIME, - OMA_DRM_RIGHTS_MIME - ].indexOf(mimeType) != -1; - }, - - show: function hald_show(aLauncher, aContext, aReason) { - if (!this._canDownload(aLauncher.source)) { - this._refuseDownload(aLauncher); - return; - } - - if (this._shouldForwardToAndroidDownloadManager(aLauncher)) { - Task.spawn(function* () { - try { - let hasPermission = yield RuntimePermissions.waitForPermissions(RuntimePermissions.WRITE_EXTERNAL_STORAGE); - if (hasPermission) { - this._downloadWithAndroidDownloadManager(aLauncher); - aLauncher.cancel(Cr.NS_BINDING_ABORTED); - } - } finally { - } - }.bind(this)).catch(Cu.reportError); - return; - } - - let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); - - let defaultHandler = new Object(); - let apps = HelperApps.getAppsForUri(aLauncher.source, { - mimeType: aLauncher.MIMEInfo.MIMEType, - }); - - if (this._shouldAddSaveToDiskIntent(aLauncher)) { - // Add a fake intent for save to disk at the top of the list. - apps.unshift({ - name: bundle.GetStringFromName("helperapps.saveToDisk"), - packageName: "org.mozilla.gecko.Download", - iconUri: "drawable://icon", - selected: true, // Default to download for files - launch: function() { - // Reset the preferredAction here. - aLauncher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.saveToDisk; - aLauncher.saveToDisk(null, false); - return true; - } - }); - } - - // We do not handle this download and there are no apps that want to do it - if (apps.length === 0) { - this._refuseDownload(aLauncher); - return; - } - - let callback = function(app) { - aLauncher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.useHelperApp; - if (!app.launch(aLauncher.source)) { - // Once the app is done we need to get rid of the temp file. This shouldn't - // get run in the saveToDisk case. - aLauncher.cancel(Cr.NS_BINDING_ABORTED); - } - } - - // See if the user already marked something as the default for this mimetype, - // and if that app is still installed. - let preferredApp = this._getPreferredApp(aLauncher); - if (preferredApp) { - let pref = apps.filter(function(app) { - return app.packageName === preferredApp; - }); - - if (pref.length > 0) { - callback(pref[0]); - return; - } - } - - // If there's only one choice, and we don't want to prompt, go right ahead - // and choose that app automatically. - if (!this._shouldPrompt(aLauncher) && (apps.length === 1)) { - callback(apps[0]); - return; - } - - // Otherwise, let's go through the prompt. - HelperApps.prompt(apps, { - title: bundle.GetStringFromName("helperapps.pick"), - buttons: [ - bundle.GetStringFromName("helperapps.alwaysUse"), - bundle.GetStringFromName("helperapps.useJustOnce") - ], - // Tapping an app twice should choose "Just once". - doubleTapButton: 1 - }, (data) => { - if (data.button < 0) { - return; - } - - callback(apps[data.icongrid0]); - - if (data.button === 0) { - this._setPreferredApp(aLauncher, apps[data.icongrid0]); - } - }); - }, - - _refuseDownload: function(aLauncher) { - aLauncher.cancel(Cr.NS_BINDING_ABORTED); - - Services.console.logStringMessage("Refusing download of non-downloadable file."); - - let bundle = Services.strings.createBundle("chrome://browser/locale/handling.properties"); - let failedText = bundle.GetStringFromName("download.blocked"); - - Snackbars.show(failedText, Snackbars.LENGTH_LONG); - }, - - _downloadWithAndroidDownloadManager(aLauncher) { - let mimeType = aLauncher.MIMEInfo.MIMEType; - if (!mimeType) { - mimeType = ContentAreaUtils.getMIMETypeForURI(aLauncher.source) || ""; - } - - Messaging.sendRequest({ - 'type': 'Download:AndroidDownloadManager', - 'uri': aLauncher.source.spec, - 'mimeType': mimeType, - 'filename': aLauncher.suggestedFileName - }); - }, - - _getPrefName: function getPrefName(mimetype) { - return "browser.download.preferred." + mimetype.replace("\\", "."); - }, - - _getMimeTypeFromLauncher: function (launcher) { - let mime = launcher.MIMEInfo.MIMEType; - if (!mime) - mime = ContentAreaUtils.getMIMETypeForURI(launcher.source) || ""; - return mime; - }, - - _getPreferredApp: function getPreferredApp(launcher) { - let mime = this._getMimeTypeFromLauncher(launcher); - if (!mime) - return; - - try { - return Services.prefs.getCharPref(this._getPrefName(mime)); - } catch(ex) { - Services.console.logStringMessage("Error getting pref for " + mime + "."); - } - return null; - }, - - _setPreferredApp: function setPreferredApp(launcher, app) { - let mime = this._getMimeTypeFromLauncher(launcher); - if (!mime) - return; - - if (app) - Services.prefs.setCharPref(this._getPrefName(mime), app.packageName); - else - Services.prefs.clearUserPref(this._getPrefName(mime)); - }, - - promptForSaveToFileAsync: function (aLauncher, aContext, aDefaultFile, - aSuggestedFileExt, aForcePrompt) { - Task.spawn(function* () { - let file = null; - try { - let hasPermission = yield RuntimePermissions.waitForPermissions(RuntimePermissions.WRITE_EXTERNAL_STORAGE); - if (hasPermission) { - // If we do have the STORAGE permission then pick the public downloads directory as destination - // for this file. Without the permission saveDestinationAvailable(null) will be called which - // will effectively cancel the download. - let preferredDir = yield Downloads.getPreferredDownloadsDirectory(); - file = this.validateLeafName(new FileUtils.File(preferredDir), - aDefaultFile, aSuggestedFileExt); - } - } finally { - // The file argument will be null in case any exception occurred. - aLauncher.saveDestinationAvailable(file); - } - }.bind(this)).catch(Cu.reportError); - }, - - validateLeafName: function hald_validateLeafName(aLocalFile, aLeafName, aFileExt) { - if (!(aLocalFile && this.isUsableDirectory(aLocalFile))) - return null; - - // Remove any leading periods, since we don't want to save hidden files - // automatically. - aLeafName = aLeafName.replace(/^\.+/, ""); - - if (aLeafName == "") - aLeafName = "unnamed" + (aFileExt ? "." + aFileExt : ""); - aLocalFile.append(aLeafName); - - this.makeFileUnique(aLocalFile); - return aLocalFile; - }, - - makeFileUnique: function hald_makeFileUnique(aLocalFile) { - try { - // Note - this code is identical to that in - // toolkit/content/contentAreaUtils.js. - // If you are updating this code, update that code too! We can't share code - // here since this is called in a js component. - let collisionCount = 0; - while (aLocalFile.exists()) { - collisionCount++; - if (collisionCount == 1) { - // Append "(2)" before the last dot in (or at the end of) the filename - // special case .ext.gz etc files so we don't wind up with .tar(2).gz - if (aLocalFile.leafName.match(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i)) - aLocalFile.leafName = aLocalFile.leafName.replace(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i, "(2)$&"); - else - aLocalFile.leafName = aLocalFile.leafName.replace(/(\.[^\.]*)?$/, "(2)$&"); - } - else { - // replace the last (n) in the filename with (n+1) - aLocalFile.leafName = aLocalFile.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount+1) + ")"); - } - } - aLocalFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600); - } - catch (e) { - dump("*** exception in validateLeafName: " + e + "\n"); - - if (e.result == Cr.NS_ERROR_FILE_ACCESS_DENIED) - throw e; - - if (aLocalFile.leafName == "" || aLocalFile.isDirectory()) { - aLocalFile.append("unnamed"); - if (aLocalFile.exists()) - aLocalFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600); - } - } - }, - - isUsableDirectory: function hald_isUsableDirectory(aDirectory) { - return aDirectory.exists() && aDirectory.isDirectory() && aDirectory.isWritable(); - }, -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HelperAppLauncherDialog]); diff --git a/mobile/android/components/ImageBlockingPolicy.js b/mobile/android/components/ImageBlockingPolicy.js deleted file mode 100644 index 2444bda06..000000000 --- a/mobile/android/components/ImageBlockingPolicy.js +++ /dev/null @@ -1,125 +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 { classes: Cc, interfaces: Ci, manager: Cm, utils: Cu, results: Cr } = Components; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Timer.jsm"); - -//////////////////////////////////////////////////////////////////////////////// -//// Constants - -//// SVG placeholder image for blocked image content -const PLACEHOLDER_IMG = "chrome://browser/skin/images/placeholder_image.svg"; - -//// Telemetry -const TELEMETRY_TAP_TO_LOAD_ENABLED = "TAP_TO_LOAD_ENABLED"; -const TELEMETRY_SHOW_IMAGE_SIZE = "TAP_TO_LOAD_IMAGE_SIZE"; -const TOPIC_GATHER_TELEMETRY = "gather-telemetry"; - -//// Gecko preference -const PREF_IMAGEBLOCKING = "browser.image_blocking"; - -//// Enabled options -const OPTION_NEVER = 0; -const OPTION_ALWAYS = 1; -const OPTION_WIFI_ONLY = 2; - - -/** - * Content policy for blocking images - */ -function ImageBlockingPolicy() { - Services.obs.addObserver(this, TOPIC_GATHER_TELEMETRY, false); -} - -ImageBlockingPolicy.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy, Ci.nsIObserver]), - classDescription: "Click-To-Play Image", - classID: Components.ID("{f55f77f9-d33d-4759-82fc-60db3ee0bb91}"), - contractID: "@mozilla.org/browser/blockimages-policy;1", - xpcom_categories: [{category: "content-policy", service: true}], - - // nsIContentPolicy interface implementation - shouldLoad: function(contentType, contentLocation, requestOrigin, node, mimeTypeGuess, extra) { - // When enabled or when on cellular, and option for cellular-only is selected - if (this._enabled() == OPTION_NEVER || (this._enabled() == OPTION_WIFI_ONLY && this._usingCellular())) { - if (contentType === Ci.nsIContentPolicy.TYPE_IMAGE || contentType === Ci.nsIContentPolicy.TYPE_IMAGESET) { - // Accept any non-http(s) image URLs - if (!contentLocation.schemeIs("http") && !contentLocation.schemeIs("https")) { - return Ci.nsIContentPolicy.ACCEPT; - } - - if (node instanceof Ci.nsIDOMHTMLImageElement) { - // Accept if the user has asked to view the image - if (node.getAttribute("data-ctv-show") == "true") { - sendImageSizeTelemetry(node.getAttribute("data-ctv-src")); - return Ci.nsIContentPolicy.ACCEPT; - } - - setTimeout(() => { - // Cache the original image URL and swap in our placeholder - node.setAttribute("data-ctv-src", contentLocation.spec); - node.setAttribute("src", PLACEHOLDER_IMG); - - // For imageset (img + srcset) the "srcset" is used even after we reset the "src" causing a loop. - // We are given the final image URL anyway, so it's OK to just remove the "srcset" value. - node.removeAttribute("srcset"); - }, 0); - } - - // Reject any image that is not associated with a DOM element - return Ci.nsIContentPolicy.REJECT; - } - } - - // Accept all other content types - return Ci.nsIContentPolicy.ACCEPT; - }, - - shouldProcess: function(contentType, contentLocation, requestOrigin, node, mimeTypeGuess, extra) { - return Ci.nsIContentPolicy.ACCEPT; - }, - - _usingCellular: function() { - let network = Cc["@mozilla.org/network/network-link-service;1"].getService(Ci.nsINetworkLinkService); - return !(network.linkType == Ci.nsINetworkLinkService.LINK_TYPE_UNKNOWN || - network.linkType == Ci.nsINetworkLinkService.LINK_TYPE_ETHERNET || - network.linkType == Ci.nsINetworkLinkService.LINK_TYPE_USB || - network.linkType == Ci.nsINetworkLinkService.LINK_TYPE_WIFI); - }, - - _enabled: function() { - return Services.prefs.getIntPref(PREF_IMAGEBLOCKING); - }, - - observe : function (subject, topic, data) { - if (topic == TOPIC_GATHER_TELEMETRY) { - Services.telemetry.getHistogramById(TELEMETRY_TAP_TO_LOAD_ENABLED).add(this._enabled()); - } - }, -}; - -function sendImageSizeTelemetry(imageURL) { - let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest); - xhr.open("HEAD", imageURL, true); - xhr.onreadystatechange = function (e) { - if (xhr.readyState != 4) { - return; - } - if (xhr.status != 200) { - return; - } - let contentLength = xhr.getResponseHeader("Content-Length"); - if (!contentLength) { - return; - } - let imageSize = contentLength / 1024; - Services.telemetry.getHistogramById(TELEMETRY_SHOW_IMAGE_SIZE).add(imageSize); - }; - xhr.send(null); -} - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ImageBlockingPolicy]); diff --git a/mobile/android/components/LoginManagerPrompter.js b/mobile/android/components/LoginManagerPrompter.js deleted file mode 100644 index e70afbe14..000000000 --- a/mobile/android/components/LoginManagerPrompter.js +++ /dev/null @@ -1,413 +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 Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -/* Constants for password prompt telemetry. -* Mirrored in nsLoginManagerPrompter.js */ -const PROMPT_DISPLAYED = 0; - -const PROMPT_ADD = 1; -const PROMPT_NOTNOW = 2; -const PROMPT_NEVER = 3; - -const PROMPT_UPDATE = 1; - -/* ==================== LoginManagerPrompter ==================== */ -/* - * LoginManagerPrompter - * - * Implements interfaces for prompting the user to enter/save/change auth info. - * - * nsILoginManagerPrompter: Used by Login Manager for saving/changing logins - * found in HTML forms. - */ -function LoginManagerPrompter() { -} - -LoginManagerPrompter.prototype = { - classID : Components.ID("97d12931-abe2-11df-94e2-0800200c9a66"), - QueryInterface : XPCOMUtils.generateQI([Ci.nsILoginManagerPrompter]), - - _factory : null, - _window : null, - _debug : false, // mirrors signon.debug - - __pwmgr : null, // Password Manager service - get _pwmgr() { - if (!this.__pwmgr) - this.__pwmgr = Cc["@mozilla.org/login-manager;1"]. - getService(Ci.nsILoginManager); - return this.__pwmgr; - }, - - __promptService : null, // Prompt service for user interaction - get _promptService() { - if (!this.__promptService) - this.__promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"]. - getService(Ci.nsIPromptService2); - return this.__promptService; - }, - - __strBundle : null, // String bundle for L10N - get _strBundle() { - if (!this.__strBundle) { - let bunService = Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService); - this.__strBundle = { - pwmgr : bunService.createBundle("chrome://passwordmgr/locale/passwordmgr.properties"), - brand : bunService.createBundle("chrome://branding/locale/brand.properties") - }; - - if (!this.__strBundle) - throw "String bundle for Login Manager not present!"; - } - - return this.__strBundle; - }, - - __ellipsis : null, - get _ellipsis() { - if (!this.__ellipsis) { - this.__ellipsis = "\u2026"; - try { - this.__ellipsis = Services.prefs.getComplexValue( - "intl.ellipsis", Ci.nsIPrefLocalizedString).data; - } catch (e) { } - } - return this.__ellipsis; - }, - - /* - * log - * - * Internal function for logging debug messages to the Error Console window. - */ - log : function (message) { - if (!this._debug) - return; - - dump("Pwmgr Prompter: " + message + "\n"); - Services.console.logStringMessage("Pwmgr Prompter: " + message); - }, - - /* ---------- nsILoginManagerPrompter prompts ---------- */ - - /* - * init - * - */ - init : function (aWindow, aFactory) { - this._chromeWindow = this._getChromeWindow(aWindow).wrappedJSObject; - this._factory = aFactory || null; - this._browser = null; - - var prefBranch = Services.prefs.getBranch("signon."); - this._debug = prefBranch.getBoolPref("debug"); - this.log("===== initialized ====="); - }, - - set browser(aBrowser) { - this._browser = aBrowser; - }, - - // setting this attribute is ignored because Android does not consider - // opener windows when displaying login notifications - set opener(aOpener) { }, - - /* - * promptToSavePassword - * - */ - promptToSavePassword : function (aLogin) { - this._showSaveLoginNotification(aLogin); - Services.telemetry.getHistogramById("PWMGR_PROMPT_REMEMBER_ACTION").add(PROMPT_DISPLAYED); - Services.obs.notifyObservers(aLogin, "passwordmgr-prompt-save", null); - }, - - /* - * _showLoginNotification - * - * Displays a notification doorhanger. - * @param aBody - * String message to be displayed in the doorhanger - * @param aButtons - * Buttons to display with the doorhanger - * @param aUsername - * Username string used in creating a doorhanger action - * @param aPassword - * Password string used in creating a doorhanger action - */ - _showLoginNotification : function (aBody, aButtons, aUsername, aPassword) { - let tabID = this._chromeWindow.BrowserApp.getTabForBrowser(this._browser).id; - - let actionText = { - text: aUsername, - type: "EDIT", - bundle: { username: aUsername, - password: aPassword } - }; - - // The page we're going to hasn't loaded yet, so we want to persist - // across the first location change. - - // Sites like Gmail perform a funky redirect dance before you end up - // at the post-authentication page. I don't see a good way to - // heuristically determine when to ignore such location changes, so - // we'll try ignoring location changes based on a time interval. - let options = { - persistWhileVisible: true, - timeout: Date.now() + 10000, - actionText: actionText - } - - var nativeWindow = this._getNativeWindow(); - if (nativeWindow) - nativeWindow.doorhanger.show(aBody, "password", aButtons, tabID, options, "LOGIN"); - }, - - /* - * _showSaveLoginNotification - * - * Displays a notification doorhanger (rather than a popup), to allow the user to - * save the specified login. This allows the user to see the results of - * their login, and only save a login which they know worked. - * - */ - _showSaveLoginNotification : function (aLogin) { - let brandShortName = this._strBundle.brand.GetStringFromName("brandShortName"); - let notificationText = this._getLocalizedString("saveLogin", [brandShortName]); - - let username = aLogin.username ? this._sanitizeUsername(aLogin.username) : ""; - - // The callbacks in |buttons| have a closure to access the variables - // in scope here; set one to |this._pwmgr| so we can get back to pwmgr - // without a getService() call. - var pwmgr = this._pwmgr; - let promptHistogram = Services.telemetry.getHistogramById("PWMGR_PROMPT_REMEMBER_ACTION"); - - var buttons = [ - { - label: this._getLocalizedString("neverButton"), - callback: function() { - promptHistogram.add(PROMPT_NEVER); - pwmgr.setLoginSavingEnabled(aLogin.hostname, false); - } - }, - { - label: this._getLocalizedString("rememberButton"), - callback: function(checked, response) { - if (response) { - aLogin.username = response["username"] || aLogin.username; - aLogin.password = response["password"] || aLogin.password; - } - pwmgr.addLogin(aLogin); - promptHistogram.add(PROMPT_ADD); - }, - positive: true - } - ]; - - this._showLoginNotification(notificationText, buttons, aLogin.username, aLogin.password); - }, - - /* - * promptToChangePassword - * - * Called when we think we detect a password change for an existing - * login, when the form being submitted contains multiple password - * fields. - * - */ - promptToChangePassword : function (aOldLogin, aNewLogin) { - this._showChangeLoginNotification(aOldLogin, aNewLogin.password); - Services.telemetry.getHistogramById("PWMGR_PROMPT_UPDATE_ACTION").add(PROMPT_DISPLAYED); - let oldGUID = aOldLogin.QueryInterface(Ci.nsILoginMetaInfo).guid; - Services.obs.notifyObservers(aNewLogin, "passwordmgr-prompt-change", oldGUID); - }, - - /* - * _showChangeLoginNotification - * - * Shows the Change Password notification doorhanger. - * - */ - _showChangeLoginNotification : function (aOldLogin, aNewPassword) { - var notificationText; - if (aOldLogin.username) { - let displayUser = this._sanitizeUsername(aOldLogin.username); - notificationText = this._getLocalizedString("updatePassword", [displayUser]); - } else { - notificationText = this._getLocalizedString("updatePasswordNoUser"); - } - - // The callbacks in |buttons| have a closure to access the variables - // in scope here; set one to |this._pwmgr| so we can get back to pwmgr - // without a getService() call. - var self = this; - let promptHistogram = Services.telemetry.getHistogramById("PWMGR_PROMPT_UPDATE_ACTION"); - - var buttons = [ - { - label: this._getLocalizedString("dontUpdateButton"), - callback: function() { - promptHistogram.add(PROMPT_NOTNOW); - // do nothing - } - }, - { - label: this._getLocalizedString("updateButton"), - callback: function(checked, response) { - let password = response ? response["password"] : aNewPassword; - self._updateLogin(aOldLogin, password); - - promptHistogram.add(PROMPT_UPDATE); - }, - positive: true - } - ]; - - this._showLoginNotification(notificationText, buttons, aOldLogin.username, aNewPassword); - }, - - /* - * promptToChangePasswordWithUsernames - * - * Called when we detect a password change in a form submission, but we - * don't know which existing login (username) it's for. Asks the user - * to select a username and confirm the password change. - * - * Note: The caller doesn't know the username for aNewLogin, so this - * function fills in .username and .usernameField with the values - * from the login selected by the user. - * - * Note; XPCOM stupidity: |count| is just |logins.length|. - */ - promptToChangePasswordWithUsernames : function (logins, count, aNewLogin) { - const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS; - - var usernames = logins.map(l => l.username); - var dialogText = this._getLocalizedString("userSelectText"); - var dialogTitle = this._getLocalizedString("passwordChangeTitle"); - var selectedIndex = { value: null }; - - // If user selects ok, outparam.value is set to the index - // of the selected username. - var ok = this._promptService.select(null, - dialogTitle, dialogText, - usernames.length, usernames, - selectedIndex); - if (ok) { - // Now that we know which login to use, modify its password. - let selectedLogin = logins[selectedIndex.value]; - this.log("Updating password for user " + selectedLogin.username); - this._updateLogin(selectedLogin, aNewLogin.password); - } - }, - - /* ---------- Internal Methods ---------- */ - - /* - * _updateLogin - */ - _updateLogin : function (login, newPassword) { - var now = Date.now(); - var propBag = Cc["@mozilla.org/hash-property-bag;1"]. - createInstance(Ci.nsIWritablePropertyBag); - if (newPassword) { - propBag.setProperty("password", newPassword); - // Explicitly set the password change time here (even though it would - // be changed automatically), to ensure that it's exactly the same - // value as timeLastUsed. - propBag.setProperty("timePasswordChanged", now); - } - propBag.setProperty("timeLastUsed", now); - propBag.setProperty("timesUsedIncrement", 1); - this._pwmgr.modifyLogin(login, propBag); - }, - - /* - * _getChromeWindow - * - * Given a content DOM window, returns the chrome window it's in. - */ - _getChromeWindow: function (aWindow) { - if (aWindow instanceof Ci.nsIDOMChromeWindow) - return aWindow; - var chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .chromeEventHandler.ownerDocument.defaultView; - return chromeWin; - }, - - /* - * _getNativeWindow - * - * Returns the NativeWindow to this prompter, or null if there isn't - * a NativeWindow available (w/ error sent to logcat). - */ - _getNativeWindow : function () { - let nativeWindow = null; - try { - let chromeWin = this._chromeWindow; - if (chromeWin.NativeWindow) { - nativeWindow = chromeWin.NativeWindow; - } else { - Cu.reportError("NativeWindow not available on window"); - } - - } catch (e) { - // If any errors happen, just assume no native window helper. - Cu.reportError("No NativeWindow available: " + e); - } - return nativeWindow; - }, - - /* - * _getLocalizedString - * - * Can be called as: - * _getLocalizedString("key1"); - * _getLocalizedString("key2", ["arg1"]); - * _getLocalizedString("key3", ["arg1", "arg2"]); - * (etc) - * - * Returns the localized string for the specified key, - * formatted if required. - * - */ - _getLocalizedString : function (key, formatArgs) { - if (formatArgs) - return this._strBundle.pwmgr.formatStringFromName( - key, formatArgs, formatArgs.length); - else - return this._strBundle.pwmgr.GetStringFromName(key); - }, - - /* - * _sanitizeUsername - * - * Sanitizes the specified username, by stripping quotes and truncating if - * it's too long. This helps prevent an evil site from messing with the - * "save password?" prompt too much. - */ - _sanitizeUsername : function (username) { - if (username.length > 30) { - username = username.substring(0, 30); - username += this._ellipsis; - } - return username.replace(/['"]/g, ""); - }, -}; // end of LoginManagerPrompter implementation - - -var component = [LoginManagerPrompter]; -this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component); diff --git a/mobile/android/components/MobileComponents.manifest b/mobile/android/components/MobileComponents.manifest deleted file mode 100644 index fe5deb95f..000000000 --- a/mobile/android/components/MobileComponents.manifest +++ /dev/null @@ -1,123 +0,0 @@ -# AboutRedirector.js -component {322ba47e-7047-4f71-aebf-cb7d69325cd9} AboutRedirector.js -contract @mozilla.org/network/protocol/about;1?what= {322ba47e-7047-4f71-aebf-cb7d69325cd9} -contract @mozilla.org/network/protocol/about;1?what=fennec {322ba47e-7047-4f71-aebf-cb7d69325cd9} -contract @mozilla.org/network/protocol/about;1?what=firefox {322ba47e-7047-4f71-aebf-cb7d69325cd9} -contract @mozilla.org/network/protocol/about;1?what=empty {322ba47e-7047-4f71-aebf-cb7d69325cd9} -contract @mozilla.org/network/protocol/about;1?what=rights {322ba47e-7047-4f71-aebf-cb7d69325cd9} -contract @mozilla.org/network/protocol/about;1?what=certerror {322ba47e-7047-4f71-aebf-cb7d69325cd9} -contract @mozilla.org/network/protocol/about;1?what=home {322ba47e-7047-4f71-aebf-cb7d69325cd9} -contract @mozilla.org/network/protocol/about;1?what=downloads {322ba47e-7047-4f71-aebf-cb7d69325cd9} -contract @mozilla.org/network/protocol/about;1?what=reader {322ba47e-7047-4f71-aebf-cb7d69325cd9} -contract @mozilla.org/network/protocol/about;1?what=feedback {322ba47e-7047-4f71-aebf-cb7d69325cd9} -contract @mozilla.org/network/protocol/about;1?what=privatebrowsing {322ba47e-7047-4f71-aebf-cb7d69325cd9} -#ifdef MOZ_SERVICES_HEALTHREPORT -contract @mozilla.org/network/protocol/about;1?what=healthreport {322ba47e-7047-4f71-aebf-cb7d69325cd9} -#endif -#ifdef MOZ_SAFE_BROWSING -contract @mozilla.org/network/protocol/about;1?what=blocked {322ba47e-7047-4f71-aebf-cb7d69325cd9} -#endif -contract @mozilla.org/network/protocol/about;1?what=accounts {322ba47e-7047-4f71-aebf-cb7d69325cd9} -contract @mozilla.org/network/protocol/about;1?what=logins {322ba47e-7047-4f71-aebf-cb7d69325cd9} - -# DirectoryProvider.js -component {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} DirectoryProvider.js -contract @mozilla.org/browser/directory-provider;1 {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} -category xpcom-directory-providers browser-directory-provider @mozilla.org/browser/directory-provider;1 - -# stylesheets -category agent-style-sheets browser-content-stylesheet chrome://browser/skin/content.css - -# SessionStore.js -component {8c1f07d6-cba3-4226-a315-8bd43d67d032} SessionStore.js -contract @mozilla.org/browser/sessionstore;1 {8c1f07d6-cba3-4226-a315-8bd43d67d032} -category app-startup SessionStore service,@mozilla.org/browser/sessionstore;1 - -# ContentPermissionPrompt.js -component {C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5} ContentPermissionPrompt.js -contract @mozilla.org/content-permission/prompt;1 {C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5} - -# PromptService.js -component {9a61149b-2276-4a0a-b79c-be994ad106cf} PromptService.js -contract @mozilla.org/prompter;1 {9a61149b-2276-4a0a-b79c-be994ad106cf} -contract @mozilla.org/embedcomp/prompt-service;1 {9a61149b-2276-4a0a-b79c-be994ad106cf} -component {80dae1e9-e0d2-4974-915f-f97050fa8068} PromptService.js -contract @mozilla.org/network/authprompt-adapter-factory;1 {80dae1e9-e0d2-4974-915f-f97050fa8068} - -# PresentationDevicePrompt.js -component {388bd149-c919-4a43-b646-d7ec57877689} PresentationDevicePrompt.js -contract @mozilla.org/presentation-device/prompt;1 {388bd149-c919-4a43-b646-d7ec57877689} - -# PresentationRequestUIGlue.js -component {9c550ef7-3ff6-4bd1-9ad1-5a3735b90d21} PresentationRequestUIGlue.js -contract @mozilla.org/presentation/requestuiglue;1 {9c550ef7-3ff6-4bd1-9ad1-5a3735b90d21} - -# ImageBlockingPolicy.js -component {f55f77f9-d33d-4759-82fc-60db3ee0bb91} ImageBlockingPolicy.js -contract @mozilla.org/browser/blockimages-policy;1 {f55f77f9-d33d-4759-82fc-60db3ee0bb91} -category content-policy ImageBlockingPolicy @mozilla.org/browser/blockimages-policy;1 - -# XPIDialogService.js -component {c1242012-27d8-477e-a0f1-0b098ffc329b} XPIDialogService.js -contract @mozilla.org/addons/web-install-prompt;1 {c1242012-27d8-477e-a0f1-0b098ffc329b} - -# HelperAppDialog.js -component {e9d277a0-268a-4ec2-bb8c-10fdf3e44611} HelperAppDialog.js -contract @mozilla.org/helperapplauncherdialog;1 {e9d277a0-268a-4ec2-bb8c-10fdf3e44611} - -# BrowserCLH.js -component {be623d20-d305-11de-8a39-0800200c9a66} BrowserCLH.js application={aa3c5121-dab2-40e2-81ca-7ea25febc110} -contract @mozilla.org/browser/browser-clh;1 {be623d20-d305-11de-8a39-0800200c9a66} -category app-startup BrowserCLH @mozilla.org/browser/browser-clh;1 - -# ContentDispatchChooser.js -component {5a072a22-1e66-4100-afc1-07aed8b62fc5} ContentDispatchChooser.js -contract @mozilla.org/content-dispatch-chooser;1 {5a072a22-1e66-4100-afc1-07aed8b62fc5} - -# AddonUpdateService.js -component {93c8824c-9b87-45ae-bc90-5b82a1e4d877} AddonUpdateService.js -contract @mozilla.org/browser/addon-update-service;1 {93c8824c-9b87-45ae-bc90-5b82a1e4d877} -category update-timer AddonUpdateService @mozilla.org/browser/addon-update-service;1,getService,auto-addon-background-update-timer,extensions.autoupdate.interval,86400 - -# LoginManagerPrompter.js -component {97d12931-abe2-11df-94e2-0800200c9a66} LoginManagerPrompter.js -contract @mozilla.org/login-manager/prompter;1 {97d12931-abe2-11df-94e2-0800200c9a66} - -# BlocklistPrompt.js -component {4e6ea350-b09a-11df-94e2-0800200c9a66} BlocklistPrompt.js -contract @mozilla.org/addons/blocklist-prompt;1 {4e6ea350-b09a-11df-94e2-0800200c9a66} - -# NSSDialogService.js -component {cbc08081-49b6-4561-9c18-a7707a50bda1} NSSDialogService.js -contract @mozilla.org/nsCertificateDialogs;1 {cbc08081-49b6-4561-9c18-a7707a50bda1} -contract @mozilla.org/nsClientAuthDialogs;1 {cbc08081-49b6-4561-9c18-a7707a50bda1} - -# SiteSpecificUserAgent.js -component {d5234c9d-0ee2-4b3c-9da3-18be9e5cf7e6} SiteSpecificUserAgent.js -contract @mozilla.org/dom/site-specific-user-agent;1 {d5234c9d-0ee2-4b3c-9da3-18be9e5cf7e6} - -# FilePicker.js -component {18a4e042-7c7c-424b-a583-354e68553a7f} FilePicker.js -contract @mozilla.org/filepicker;1 {18a4e042-7c7c-424b-a583-354e68553a7f} - -#ifndef RELEASE_OR_BETA -# TabSource.js -component {5850c76e-b916-4218-b99a-31f004e0a7e7} TabSource.js -contract @mozilla.org/tab-source-service;1 {5850c76e-b916-4218-b99a-31f004e0a7e7} -#endif - -# Snippets.js -component {a78d7e59-b558-4321-a3d6-dffe2f1e76dd} Snippets.js -contract @mozilla.org/snippets;1 {a78d7e59-b558-4321-a3d6-dffe2f1e76dd} -category browser-delayed-startup-finished Snippets @mozilla.org/snippets;1 -category update-timer Snippets @mozilla.org/snippets;1,getService,snippets-update-timer,browser.snippets.updateInterval,86400 - -# ColorPicker.js -component {430b987f-bb9f-46a3-99a5-241749220b29} ColorPicker.js -contract @mozilla.org/colorpicker;1 {430b987f-bb9f-46a3-99a5-241749220b29} - -# PersistentNotificationHandler.js -component {75390fe7-f8a3-423a-b3b1-258d7eabed40} PersistentNotificationHandler.js -contract @mozilla.org/persistent-notification-handler;1 {75390fe7-f8a3-423a-b3b1-258d7eabed40} -category persistent-notification-click PersistentNotificationHandler @mozilla.org/persistent-notification-handler;1 -category persistent-notification-close PersistentNotificationHandler @mozilla.org/persistent-notification-handler;1 diff --git a/mobile/android/components/NSSDialogService.js b/mobile/android/components/NSSDialogService.js deleted file mode 100644 index 671cc8c35..000000000 --- a/mobile/android/components/NSSDialogService.js +++ /dev/null @@ -1,276 +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 Ci = Components.interfaces; -const Cu = Components.utils; -const Cc = Components.classes; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Prompt", - "resource://gre/modules/Prompt.jsm"); - -// ----------------------------------------------------------------------- -// NSS Dialog Service -// ----------------------------------------------------------------------- - -function NSSDialogs() { } - -NSSDialogs.prototype = { - classID: Components.ID("{cbc08081-49b6-4561-9c18-a7707a50bda1}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsICertificateDialogs, Ci.nsIClientAuthDialogs]), - - /** - * Escapes the given input via HTML entity encoding. Used to prevent HTML - * injection when the input is to be placed inside an HTML body, but not in - * any other context. - * - * @param {String} input The input to interpret as a plain string. - * @returns {String} The escaped input. - */ - escapeHTML: function(input) { - return input.replace(/&/g, "&") - .replace(/</g, "<") - .replace(/>/g, ">") - .replace(/"/g, """) - .replace(/'/g, "'") - .replace(/\//g, "/"); - }, - - getString: function(aName) { - if (!this.bundle) { - this.bundle = Services.strings.createBundle("chrome://browser/locale/pippki.properties"); - } - return this.bundle.GetStringFromName(aName); - }, - - formatString: function(aName, argList) { - if (!this.bundle) { - this.bundle = - Services.strings.createBundle("chrome://browser/locale/pippki.properties"); - } - let escapedArgList = Array.from(argList, x => this.escapeHTML(x)); - return this.bundle.formatStringFromName(aName, escapedArgList, - escapedArgList.length); - }, - - getPrompt: function(aTitle, aText, aButtons) { - return new Prompt({ - title: aTitle, - text: aText, - buttons: aButtons, - }); - }, - - showPrompt: function(aPrompt) { - let response = null; - aPrompt.show(function(data) { - response = data; - }); - - // Spin this thread while we wait for a result - let thread = Services.tm.currentThread; - while (response === null) - thread.processNextEvent(true); - - return response; - }, - - confirmDownloadCACert: function(aCtx, aCert, aTrust) { - while (true) { - let prompt = this.getPrompt(this.getString("downloadCert.title"), - this.getString("downloadCert.message1"), - [ this.getString("nssdialogs.ok.label"), - this.getString("downloadCert.viewCert.label"), - this.getString("nssdialogs.cancel.label") - ]); - - prompt.addCheckbox({ id: "trustSSL", label: this.getString("downloadCert.trustSSL"), checked: false }) - .addCheckbox({ id: "trustEmail", label: this.getString("downloadCert.trustEmail"), checked: false }) - .addCheckbox({ id: "trustSign", label: this.getString("downloadCert.trustObjSign"), checked: false }); - let response = this.showPrompt(prompt); - - // they hit the "view cert" button, so show the cert and try again - if (response.button == 1) { - this.viewCert(aCtx, aCert); - continue; - } else if (response.button != 0) { - return false; - } - - aTrust.value = Ci.nsIX509CertDB.UNTRUSTED; - if (response.trustSSL) aTrust.value |= Ci.nsIX509CertDB.TRUSTED_SSL; - if (response.trustEmail) aTrust.value |= Ci.nsIX509CertDB.TRUSTED_EMAIL; - if (response.trustSign) aTrust.value |= Ci.nsIX509CertDB.TRUSTED_OBJSIGN; - return true; - } - }, - - setPKCS12FilePassword: function(aCtx, aPassword) { - // this dialog is never shown in Fennec; in Desktop it is shown while backing up a personal - // certificate to a file via Preferences->Advanced->Encryption->View Certificates->Your Certificates - throw "Unimplemented"; - }, - - getPKCS12FilePassword: function(aCtx, aPassword) { - let prompt = this.getPrompt(this.getString("pkcs12.getpassword.title"), - this.getString("pkcs12.getpassword.message"), - [ this.getString("nssdialogs.ok.label"), - this.getString("nssdialogs.cancel.label") - ]).addPassword({id: "pw"}); - let response = this.showPrompt(prompt); - if (response.button != 0) { - return false; - } - - aPassword.value = response.pw; - return true; - }, - - certInfoSection: function(aHeading, aDataPairs, aTrailingNewline = true) { - let certInfoStrings = [ - "<big>" + this.getString(aHeading) + "</big>", - ]; - - for (let i = 0; i < aDataPairs.length; i += 2) { - let key = aDataPairs[i]; - let value = aDataPairs[i + 1]; - certInfoStrings.push(this.formatString(key, [value])); - } - - if (aTrailingNewline) { - certInfoStrings.push("<br/>"); - } - - return certInfoStrings.join("<br/>"); - }, - - viewCert: function(aCtx, aCert) { - let p = this.getPrompt(this.getString("certmgr.title"), "", [ - this.getString("nssdialogs.ok.label"), - ]); - p.addLabel({ label: this.certInfoSection("certmgr.subjectinfo.label", - ["certdetail.cn", aCert.commonName, - "certdetail.o", aCert.organization, - "certdetail.ou", aCert.organizationalUnit, - "certdetail.serialnumber", aCert.serialNumber])}) - .addLabel({ label: this.certInfoSection("certmgr.issuerinfo.label", - ["certdetail.cn", aCert.issuerCommonName, - "certdetail.o", aCert.issuerOrganization, - "certdetail.ou", aCert.issuerOrganizationUnit])}) - .addLabel({ label: this.certInfoSection("certmgr.periodofvalidity.label", - ["certdetail.notBefore", aCert.validity.notBeforeLocalDay, - "certdetail.notAfter", aCert.validity.notAfterLocalDay])}) - .addLabel({ label: this.certInfoSection("certmgr.fingerprints.label", - ["certdetail.sha256fingerprint", aCert.sha256Fingerprint, - "certdetail.sha1fingerprint", aCert.sha1Fingerprint], - false) }); - this.showPrompt(p); - }, - - /** - * Returns a list of details of the given cert relevant for TLS client - * authentication. - * - * @param {nsIX509Cert} cert Cert to get the details of. - * @returns {String} <br/> delimited list of details. - */ - getCertDetails: function(cert) { - let detailLines = [ - this.formatString("clientAuthAsk.issuedTo", [cert.subjectName]), - this.formatString("clientAuthAsk.serial", [cert.serialNumber]), - this.formatString("clientAuthAsk.validityPeriod", - [cert.validity.notBeforeLocalTime, - cert.validity.notAfterLocalTime]), - ]; - let keyUsages = cert.keyUsages; - if (keyUsages) { - detailLines.push(this.formatString("clientAuthAsk.keyUsages", - [keyUsages])); - } - let emailAddresses = cert.getEmailAddresses({}); - if (emailAddresses.length > 0) { - let joinedAddresses = emailAddresses.join(", "); - detailLines.push(this.formatString("clientAuthAsk.emailAddresses", - [joinedAddresses])); - } - detailLines.push(this.formatString("clientAuthAsk.issuedBy", - [cert.issuerName])); - detailLines.push(this.formatString("clientAuthAsk.storedOn", - [cert.tokenName])); - - return detailLines.join("<br/>"); - }, - - viewCertDetails: function(details) { - let p = this.getPrompt(this.getString("clientAuthAsk.message3"), - '', - [ this.getString("nssdialogs.ok.label") ]); - p.addLabel({ label: details }); - this.showPrompt(p); - }, - - chooseCertificate: function(ctx, hostname, port, organization, issuerOrg, - certList, selectedIndex) { - let rememberSetting = - Services.prefs.getBoolPref("security.remember_cert_checkbox_default_setting"); - - let serverRequestedDetails = [ - this.formatString("clientAuthAsk.hostnameAndPort", - [hostname, port.toString()]), - this.formatString("clientAuthAsk.organization", [organization]), - this.formatString("clientAuthAsk.issuer", [issuerOrg]), - ].join("<br/>"); - - let certNickList = []; - let certDetailsList = []; - for (let i = 0; i < certList.length; i++) { - let cert = certList.queryElementAt(i, Ci.nsIX509Cert); - certNickList.push(this.formatString("clientAuthAsk.nickAndSerial", - [cert.nickname, cert.serialNumber])); - certDetailsList.push(this.getCertDetails(cert)); - } - - selectedIndex.value = 0; - while (true) { - let buttons = [ - this.getString("nssdialogs.ok.label"), - this.getString("clientAuthAsk.viewCert.label"), - this.getString("nssdialogs.cancel.label"), - ]; - let prompt = this.getPrompt(this.getString("clientAuthAsk.title"), - this.getString("clientAuthAsk.message1"), - buttons) - .addLabel({ id: "requestedDetails", label: serverRequestedDetails } ) - .addMenulist({ - id: "nicknames", - label: this.getString("clientAuthAsk.message2"), - values: certNickList, - selected: selectedIndex.value, - }).addCheckbox({ - id: "rememberBox", - label: this.getString("clientAuthAsk.remember.label"), - checked: rememberSetting - }); - let response = this.showPrompt(prompt); - selectedIndex.value = response.nicknames; - if (response.button == 1 /* buttons[1] */) { - this.viewCertDetails(certDetailsList[selectedIndex.value]); - continue; - } else if (response.button == 0 /* buttons[0] */) { - if (response.rememberBox == true) { - let caud = ctx.QueryInterface(Ci.nsIClientAuthUserDecision); - if (caud) { - caud.rememberClientAuthCertificate = true; - } - } - return true; - } - return false; - } - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NSSDialogs]); diff --git a/mobile/android/components/PersistentNotificationHandler.js b/mobile/android/components/PersistentNotificationHandler.js deleted file mode 100644 index 2a3529f5f..000000000 --- a/mobile/android/components/PersistentNotificationHandler.js +++ /dev/null @@ -1,78 +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"; - -const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, 'Services', // jshint ignore:line - 'resource://gre/modules/Services.jsm'); -XPCOMUtils.defineLazyServiceGetter(this, "notificationStorage", - "@mozilla.org/notificationStorage;1", - "nsINotificationStorage"); -XPCOMUtils.defineLazyServiceGetter(this, "serviceWorkerManager", - "@mozilla.org/serviceworkers/manager;1", - "nsIServiceWorkerManager"); - -function PersistentNotificationHandler() { -} - -PersistentNotificationHandler.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), - classID: Components.ID("{75390fe7-f8a3-423a-b3b1-258d7eabed40}"), - - observe(subject, topic, data) { - if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) { - Cu.import("resource://gre/modules/NotificationDB.jsm"); - } - const persistentInfo = JSON.parse(data); - - if (topic === 'persistent-notification-click') { - notificationStorage.getByID(persistentInfo.origin, persistentInfo.id, { - handle(id, title, dir, lang, body, tag, icon, data, behavior, serviceWorkerRegistrationScope) { - serviceWorkerManager.sendNotificationClickEvent( - persistentInfo.originSuffix, - serviceWorkerRegistrationScope, - id, - title, - dir, - lang, - body, - tag, - icon, - data, - behavior - ); - notificationStorage.delete(persistentInfo.origin, persistentInfo.id); - } - }); - } else if (topic === 'persistent-notification-close') { - notificationStorage.getByID(persistentInfo.origin, persistentInfo.id, { - handle(id, title, dir, lang, body, tag, icon, data, behavior, serviceWorkerRegistrationScope) { - serviceWorkerManager.sendNotificationCloseEvent( - persistentInfo.originSuffix, - serviceWorkerRegistrationScope, - id, - title, - dir, - lang, - body, - tag, - icon, - data, - behavior - ); - notificationStorage.delete(persistentInfo.origin, persistentInfo.id); - } - }); - } - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ - PersistentNotificationHandler -]); diff --git a/mobile/android/components/PresentationDevicePrompt.js b/mobile/android/components/PresentationDevicePrompt.js deleted file mode 100644 index e3e063373..000000000 --- a/mobile/android/components/PresentationDevicePrompt.js +++ /dev/null @@ -1,134 +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"; - -const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; - -Cu.import('resource://gre/modules/XPCOMUtils.jsm'); -Cu.import('resource://gre/modules/Services.jsm'); - -XPCOMUtils.defineLazyModuleGetter(this, "Prompt", - "resource://gre/modules/Prompt.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry", - "resource://gre/modules/UITelemetry.jsm"); - -const kPRESENTATIONDEVICEPROMPT_CONTRACTID = "@mozilla.org/presentation-device/prompt;1"; -const kPRESENTATIONDEVICEPROMPT_CID = Components.ID("{388bd149-c919-4a43-b646-d7ec57877689}"); - -function debug(aMsg) { - // dump("-*- PresentationDevicePrompt: " + aMsg + "\n"); -} - -// nsIPresentationDevicePrompt -function PresentationDevicePrompt() { - debug("PresentationDevicePrompt init"); -} - -PresentationDevicePrompt.prototype = { - classID: kPRESENTATIONDEVICEPROMPT_CID, - contractID: kPRESENTATIONDEVICEPROMPT_CONTRACTID, - classDescription: "Fennec Presentation Device Prompt", - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevicePrompt]), - - _devices: [], // Store all available presentation devices - _request: null, // Store the request from presentation api - - _getString: function(aName) { - debug("_getString"); - - if (!this.bundle) { - this.bundle = Services.strings.createBundle("chrome://browser/locale/devicePrompt.properties"); - } - return this.bundle.GetStringFromName(aName); - }, - - _loadDevices: function(requestURLs) { - debug("_loadDevices"); - - let deviceManager = Cc["@mozilla.org/presentation-device/manager;1"] - .getService(Ci.nsIPresentationDeviceManager); - let devices = deviceManager.getAvailableDevices(requestURLs).QueryInterface(Ci.nsIArray); - - // Re-load the available devices - this._devices = []; - for (let i = 0; i < devices.length; i++) { - let device = devices.queryElementAt(i, Ci.nsIPresentationDevice); - this._devices.push(device); - } - }, - - _getPromptMenu: function(aDevices) { - debug("_getPromptMenu"); - - return aDevices.map(function(device) { - return { label: device.name }; - }); - }, - - _getPrompt: function(aTitle, aMenu) { - debug("_getPrompt"); - - let p = new Prompt({ - title: aTitle, - }); - - p.setSingleChoiceItems(aMenu); - - return p; - }, - - _showPrompt: function(aPrompt, aCallback) { - debug("_showPrompt"); - - aPrompt.show(function(data) { - let buttonIndex = data.button; - aCallback(buttonIndex); - }); - }, - - _selectDevice: function(aIndex) { - debug("_selectDevice"); - - if (!this._request) { - return; - } - - if (aIndex < 0) { // Cancel request if no selected device, - this._request.cancel(Cr.NS_ERROR_DOM_NOT_ALLOWED_ERR); - return; - } else if (!this._devices.length) { // or there is no available devices - this._request.cancel(Cr.NS_ERROR_DOM_NOT_FOUND_ERR); - return; - } - - this._request.select(this._devices[aIndex]); - }, - - // This will be fired when window.PresentationRequest(URL).start() is called - promptDeviceSelection: function(aRequest) { - debug("promptDeviceSelection"); - - // Load available presentation devices into this._devices - this._loadDevices(aRequest.requestURLs); - - if (!this._devices.length) { // Cancel request if no available device - aRequest.cancel(Cr.NS_ERROR_DOM_NOT_FOUND_ERR); - return; - } - - this._request = aRequest; - - let prompt = this._getPrompt(this._getString("deviceMenu.title"), - this._getPromptMenu(this._devices)); - - this._showPrompt(prompt, this._selectDevice.bind(this)); - - UITelemetry.addEvent("show.1", "dialog", null, "prompt_device_selection"); - }, -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationDevicePrompt]); diff --git a/mobile/android/components/PresentationRequestUIGlue.js b/mobile/android/components/PresentationRequestUIGlue.js deleted file mode 100644 index af252c875..000000000 --- a/mobile/android/components/PresentationRequestUIGlue.js +++ /dev/null @@ -1,86 +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 { interfaces: Ci, utils: Cu, classes: Cc } = Components; - -const TOPIC_PRESENTATION_RECEIVER_LAUNCH = "presentation-receiver:launch"; -const TOPIC_PRESENTATION_RECEIVER_LAUNCH_RESPONSE = "presentation-receiver:launch:response"; - -// globals XPCOMUtils -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -// globals Services -Cu.import("resource://gre/modules/Services.jsm"); - -function log(str) { - // dump("-*- PresentationRequestUIGlue.js -*-: " + str + "\n"); -} - -function PresentationRequestUIGlue() { } - -PresentationRequestUIGlue.prototype = { - sendRequest: function sendRequest(aURL, aSessionId, aDevice) { - log("PresentationRequestUIGlue - sendRequest aURL=" + aURL + - " aSessionId=" + aSessionId); - - let localDevice; - try { - localDevice = aDevice.QueryInterface(Ci.nsIPresentationLocalDevice); - } catch (e) { - /* XXX: Currently, Fennec only support 1-UA devices. Remove this - * Promise.reject() when it starts to support 2-UA devices. - */ - log("Not an 1-UA device.") - return new Promise.reject(); - } - - return new Promise((aResolve, aReject) => { - - let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator); - let requestId = uuidGenerator.generateUUID().toString(); - - let handleObserve = (aSubject, aTopic, aData) => { - log("Got observe: aTopic=" + aTopic); - - let data = JSON.parse(aData); - if (data.requestId != requestId) { - return; - } - - Services.obs.removeObserver(handleObserve, - TOPIC_PRESENTATION_RECEIVER_LAUNCH_RESPONSE); - switch(data.result) { - case "success": - aResolve(aSubject); - break; - case "error": - aReject(); - break; - }; - }; - - Services.obs.addObserver(handleObserve, - TOPIC_PRESENTATION_RECEIVER_LAUNCH_RESPONSE, - false); - - let data = { - url: aURL, - windowId: localDevice.windowId, - requestId: requestId - }; - Services.obs.notifyObservers(null, - TOPIC_PRESENTATION_RECEIVER_LAUNCH, - JSON.stringify(data)); - }) - }, - - classID: Components.ID("9c550ef7-3ff6-4bd1-9ad1-5a3735b90d21"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationRequestUIGlue]) -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationRequestUIGlue]); diff --git a/mobile/android/components/PromptService.js b/mobile/android/components/PromptService.js deleted file mode 100644 index 93aff67ee..000000000 --- a/mobile/android/components/PromptService.js +++ /dev/null @@ -1,878 +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 Ci = Components.interfaces; -const Cc = Components.classes; -const Cr = Components.results; -const Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Prompt", - "resource://gre/modules/Prompt.jsm"); - -var gPromptService = null; - -function PromptService() { - gPromptService = this; -} - -PromptService.prototype = { - classID: Components.ID("{9a61149b-2276-4a0a-b79c-be994ad106cf}"), - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]), - - /* ---------- nsIPromptFactory ---------- */ - // XXX Copied from nsPrompter.js. - getPrompt: function getPrompt(domWin, iid) { - // This is still kind of dumb; the C++ code delegated to login manager - // here, which in turn calls back into us via nsIPromptService2. - if (iid.equals(Ci.nsIAuthPrompt2) || iid.equals(Ci.nsIAuthPrompt)) { - try { - let pwmgr = Cc["@mozilla.org/passwordmanager/authpromptfactory;1"].getService(Ci.nsIPromptFactory); - return pwmgr.getPrompt(domWin, iid); - } catch (e) { - Cu.reportError("nsPrompter: Delegation to password manager failed: " + e); - } - } - - let p = new InternalPrompt(domWin); - p.QueryInterface(iid); - return p; - }, - - /* ---------- private memebers ---------- */ - - // nsIPromptService and nsIPromptService2 methods proxy to our Prompt class - callProxy: function(aMethod, aArguments) { - let prompt; - let domWin = aArguments[0]; - prompt = new InternalPrompt(domWin); - return prompt[aMethod].apply(prompt, Array.prototype.slice.call(aArguments, 1)); - }, - - /* ---------- nsIPromptService ---------- */ - - alert: function() { - return this.callProxy("alert", arguments); - }, - alertCheck: function() { - return this.callProxy("alertCheck", arguments); - }, - confirm: function() { - return this.callProxy("confirm", arguments); - }, - confirmCheck: function() { - return this.callProxy("confirmCheck", arguments); - }, - confirmEx: function() { - return this.callProxy("confirmEx", arguments); - }, - prompt: function() { - return this.callProxy("prompt", arguments); - }, - promptUsernameAndPassword: function() { - return this.callProxy("promptUsernameAndPassword", arguments); - }, - promptPassword: function() { - return this.callProxy("promptPassword", arguments); - }, - select: function() { - return this.callProxy("select", arguments); - }, - - /* ---------- nsIPromptService2 ---------- */ - promptAuth: function() { - return this.callProxy("promptAuth", arguments); - }, - asyncPromptAuth: function() { - return this.callProxy("asyncPromptAuth", arguments); - } -}; - -function InternalPrompt(aDomWin) { - this._domWin = aDomWin; -} - -InternalPrompt.prototype = { - _domWin: null, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt, Ci.nsIAuthPrompt, Ci.nsIAuthPrompt2]), - - /* ---------- internal methods ---------- */ - _getPrompt: function _getPrompt(aTitle, aText, aButtons, aCheckMsg, aCheckState) { - let p = new Prompt({ - window: this._domWin, - title: aTitle, - message: aText, - buttons: aButtons || [ - PromptUtils.getLocaleString("OK"), - PromptUtils.getLocaleString("Cancel") - ] - }); - return p; - }, - - addCheckbox: function addCheckbox(aPrompt, aCheckMsg, aCheckState) { - // Don't bother to check for aCheckSate. For nsIPomptService interfaces, aCheckState is an - // out param and is required to be defined. If we've gotten here without it, something - // has probably gone wrong and we should fail - if (aCheckMsg) { - aPrompt.addCheckbox({ - label: PromptUtils.cleanUpLabel(aCheckMsg), - checked: aCheckState.value - }); - } - - return aPrompt; - }, - - addTextbox: function(prompt, value, autofocus, hint) { - prompt.addTextbox({ - value: (value !== null) ? value : "", - autofocus: autofocus, - hint: hint - }); - }, - - addPassword: function(prompt, value, autofocus, hint) { - prompt.addPassword({ - value: (value !== null) ? value : "", - autofocus: autofocus, - hint: hint - }); - }, - - /* Shows a native prompt, and then spins the event loop for this thread while we wait - * for a response - */ - showPrompt: function showPrompt(aPrompt) { - if (this._domWin) { - PromptUtils.fireDialogEvent(this._domWin, "DOMWillOpenModalDialog"); - let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - winUtils.enterModalState(); - } - - let retval = null; - aPrompt.show(function(data) { - retval = data; - }); - - // Spin this thread while we wait for a result - let thread = Services.tm.currentThread; - while (retval == null) - thread.processNextEvent(true); - - if (this._domWin) { - let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - winUtils.leaveModalState(); - PromptUtils.fireDialogEvent(this._domWin, "DOMModalDialogClosed"); - } - - return retval; - }, - - /* - * ---------- interface disambiguation ---------- - * - * XXX Copied from nsPrompter.js. - * - * nsIPrompt and nsIAuthPrompt share 3 method names with slightly - * different arguments. All but prompt() have the same number of - * arguments, so look at the arg types to figure out how we're being - * called. :-( - */ - prompt: function prompt() { - if (gPromptService.inContentProcess) - return gPromptService.callProxy("prompt", [null].concat(Array.prototype.slice.call(arguments))); - - // also, the nsIPrompt flavor has 5 args instead of 6. - if (typeof arguments[2] == "object") - return this.nsIPrompt_prompt.apply(this, arguments); - else - return this.nsIAuthPrompt_prompt.apply(this, arguments); - }, - - promptUsernameAndPassword: function promptUsernameAndPassword() { - // Both have 6 args, so use types. - if (typeof arguments[2] == "object") - return this.nsIPrompt_promptUsernameAndPassword.apply(this, arguments); - else - return this.nsIAuthPrompt_promptUsernameAndPassword.apply(this, arguments); - }, - - promptPassword: function promptPassword() { - // Both have 5 args, so use types. - if (typeof arguments[2] == "object") - return this.nsIPrompt_promptPassword.apply(this, arguments); - else - return this.nsIAuthPrompt_promptPassword.apply(this, arguments); - }, - - /* ---------- nsIPrompt ---------- */ - - alert: function alert(aTitle, aText) { - let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]); - p.setHint("alert"); - this.showPrompt(p); - }, - - alertCheck: function alertCheck(aTitle, aText, aCheckMsg, aCheckState) { - let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]); - this.addCheckbox(p, aCheckMsg, aCheckState); - let data = this.showPrompt(p); - if (aCheckState && data.button > -1) - aCheckState.value = data.checkbox0; - }, - - confirm: function confirm(aTitle, aText) { - let p = this._getPrompt(aTitle, aText); - p.setHint("confirm"); - let data = this.showPrompt(p); - return (data.button == 0); - }, - - confirmCheck: function confirmCheck(aTitle, aText, aCheckMsg, aCheckState) { - let p = this._getPrompt(aTitle, aText, null); - this.addCheckbox(p, aCheckMsg, aCheckState); - let data = this.showPrompt(p); - let ok = data.button == 0; - if (aCheckState && data.button > -1) - aCheckState.value = data.checkbox0; - return ok; - }, - - confirmEx: function confirmEx(aTitle, aText, aButtonFlags, aButton0, - aButton1, aButton2, aCheckMsg, aCheckState) { - let buttons = []; - let titles = [aButton0, aButton1, aButton2]; - for (let i = 0; i < 3; i++) { - let bTitle = null; - switch (aButtonFlags & 0xff) { - case Ci.nsIPromptService.BUTTON_TITLE_OK : - bTitle = PromptUtils.getLocaleString("OK"); - break; - case Ci.nsIPromptService.BUTTON_TITLE_CANCEL : - bTitle = PromptUtils.getLocaleString("Cancel"); - break; - case Ci.nsIPromptService.BUTTON_TITLE_YES : - bTitle = PromptUtils.getLocaleString("Yes"); - break; - case Ci.nsIPromptService.BUTTON_TITLE_NO : - bTitle = PromptUtils.getLocaleString("No"); - break; - case Ci.nsIPromptService.BUTTON_TITLE_SAVE : - bTitle = PromptUtils.getLocaleString("Save"); - break; - case Ci.nsIPromptService.BUTTON_TITLE_DONT_SAVE : - bTitle = PromptUtils.getLocaleString("DontSave"); - break; - case Ci.nsIPromptService.BUTTON_TITLE_REVERT : - bTitle = PromptUtils.getLocaleString("Revert"); - break; - case Ci.nsIPromptService.BUTTON_TITLE_IS_STRING : - bTitle = PromptUtils.cleanUpLabel(titles[i]); - break; - } - - if (bTitle) - buttons.push(bTitle); - - aButtonFlags >>= 8; - } - - let p = this._getPrompt(aTitle, aText, buttons); - this.addCheckbox(p, aCheckMsg, aCheckState); - let data = this.showPrompt(p); - if (aCheckState && data.button > -1) - aCheckState.value = data.checkbox0; - return data.button; - }, - - nsIPrompt_prompt: function nsIPrompt_prompt(aTitle, aText, aValue, aCheckMsg, aCheckState) { - let p = this._getPrompt(aTitle, aText, null, aCheckMsg, aCheckState); - p.setHint("prompt"); - this.addTextbox(p, aValue.value, true); - this.addCheckbox(p, aCheckMsg, aCheckState); - let data = this.showPrompt(p); - - let ok = data.button == 0; - if (aCheckState && data.button > -1) - aCheckState.value = data.checkbox0; - if (ok) - aValue.value = data.textbox0; - return ok; - }, - - nsIPrompt_promptPassword: function nsIPrompt_promptPassword( - aTitle, aText, aPassword, aCheckMsg, aCheckState) { - let p = this._getPrompt(aTitle, aText, null); - this.addPassword(p, aPassword.value, true, PromptUtils.getLocaleString("password", "passwdmgr")); - this.addCheckbox(p, aCheckMsg, aCheckState); - let data = this.showPrompt(p); - - let ok = data.button == 0; - if (aCheckState && data.button > -1) - aCheckState.value = data.checkbox0; - if (ok) - aPassword.value = data.password0; - return ok; - }, - - nsIPrompt_promptUsernameAndPassword: function nsIPrompt_promptUsernameAndPassword( - aTitle, aText, aUsername, aPassword, aCheckMsg, aCheckState) { - let p = this._getPrompt(aTitle, aText, null); - this.addTextbox(p, aUsername.value, true, PromptUtils.getLocaleString("username", "passwdmgr")); - this.addPassword(p, aPassword.value, false, PromptUtils.getLocaleString("password", "passwdmgr")); - this.addCheckbox(p, aCheckMsg, aCheckState); - let data = this.showPrompt(p); - - let ok = data.button == 0; - if (aCheckState && data.button > -1) - aCheckState.value = data.checkbox0; - - if (ok) { - aUsername.value = data.textbox0; - aPassword.value = data.password0; - } - return ok; - }, - - select: function select(aTitle, aText, aCount, aSelectList, aOutSelection) { - let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]); - p.addMenulist({ values: aSelectList }); - let data = this.showPrompt(p); - - let ok = data.button == 0; - if (ok) - aOutSelection.value = data.menulist0; - - return ok; - }, - - /* ---------- nsIAuthPrompt ---------- */ - - nsIAuthPrompt_prompt : function (title, text, passwordRealm, savePassword, defaultText, result) { - // TODO: Port functions from nsLoginManagerPrompter.js to here - if (defaultText) - result.value = defaultText; - return this.nsIPrompt_prompt(title, text, result, null, {}); - }, - - nsIAuthPrompt_promptUsernameAndPassword : function(aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass) { - return this.nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, aUser, aPass); - }, - - nsIAuthPrompt_promptPassword : function(aTitle, aText, aPasswordRealm, aSavePassword, aPass) { - return this.nsIAuthPrompt_loginPrompt(aTitle, aText, aPasswordRealm, aSavePassword, null, aPass); - }, - - nsIAuthPrompt_loginPrompt: function(aTitle, aPasswordRealm, aSavePassword, aUser, aPass) { - let checkMsg = null; - let check = { value: false }; - let hostname, realm; - [hostname, realm, aUser] = PromptUtils.getHostnameAndRealm(aPasswordRealm); - - let canSave = PromptUtils.canSaveLogin(hostname, aSavePassword); - if (canSave) { - // Look for existing logins. - let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, realm); - [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, aUser, aPass); - } - - // (eslint-disable: see bug 1177904) - let ok = false; - if (aUser) - ok = this.nsIPrompt_promptUsernameAndPassword(aTitle, aText, aUser, aPass, checkMsg, check); // eslint-disable-line no-undef - else - ok = this.nsIPrompt_promptPassword(aTitle, aText, aPass, checkMsg, check); // eslint-disable-line no-undef - - if (ok && canSave && check.value) - PromptUtils.savePassword(hostname, realm, aUser, aPass); - - return ok; - }, - - /* ---------- nsIAuthPrompt2 ---------- */ - - promptAuth: function promptAuth(aChannel, aLevel, aAuthInfo) { - let checkMsg = null; - let check = { value: false }; - let message = PromptUtils.makeDialogText(aChannel, aAuthInfo); - let [username, password] = PromptUtils.getAuthInfo(aAuthInfo); - let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo); - let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm); - - let canSave = PromptUtils.canSaveLogin(hostname, null); - if (canSave) - [checkMsg, check] = PromptUtils.getUsernameAndPassword(foundLogins, username, password); - - if (username.value && password.value) { - PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value); - } - - let canAutologin = false; - if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY && - !(aAuthInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED) && - Services.prefs.getBoolPref("signon.autologin.proxy")) - canAutologin = true; - - let ok = canAutologin; - if (!ok && aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) - ok = this.nsIPrompt_promptPassword(null, message, password, checkMsg, check); - else if (!ok) - ok = this.nsIPrompt_promptUsernameAndPassword(null, message, username, password, checkMsg, check); - - PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value); - - if (ok && canSave && check.value) - PromptUtils.savePassword(foundLogins, username, password, hostname, httpRealm); - - return ok; - }, - - _asyncPrompts: {}, - _asyncPromptInProgress: false, - - _doAsyncPrompt : function() { - if (this._asyncPromptInProgress) - return; - - // Find the first prompt key we have in the queue - let hashKey = null; - for (hashKey in this._asyncPrompts) - break; - - if (!hashKey) - return; - - // If login manger has logins for this host, defer prompting if we're - // already waiting on a master password entry. - let prompt = this._asyncPrompts[hashKey]; - let prompter = prompt.prompter; - let [hostname, httpRealm] = PromptUtils.getAuthTarget(prompt.channel, prompt.authInfo); - let foundLogins = PromptUtils.pwmgr.findLogins({}, hostname, null, httpRealm); - if (foundLogins.length > 0 && PromptUtils.pwmgr.uiBusy) - return; - - this._asyncPromptInProgress = true; - prompt.inProgress = true; - - let self = this; - - let runnable = { - run: function() { - let ok = false; - try { - ok = prompter.promptAuth(prompt.channel, prompt.level, prompt.authInfo); - } catch (e) { - Cu.reportError("_doAsyncPrompt:run: " + e + "\n"); - } - - delete self._asyncPrompts[hashKey]; - prompt.inProgress = false; - self._asyncPromptInProgress = false; - - for (let consumer of prompt.consumers) { - if (!consumer.callback) - // Not having a callback means that consumer didn't provide it - // or canceled the notification - continue; - - try { - if (ok) - consumer.callback.onAuthAvailable(consumer.context, prompt.authInfo); - else - consumer.callback.onAuthCancelled(consumer.context, true); - } catch (e) { /* Throw away exceptions caused by callback */ } - } - self._doAsyncPrompt(); - } - } - - Services.tm.mainThread.dispatch(runnable, Ci.nsIThread.DISPATCH_NORMAL); - }, - - asyncPromptAuth: function asyncPromptAuth(aChannel, aCallback, aContext, aLevel, aAuthInfo) { - let cancelable = null; - try { - // If the user submits a login but it fails, we need to remove the - // notification bar that was displayed. Conveniently, the user will - // be prompted for authentication again, which brings us here. - //this._removeLoginNotifications(); - - cancelable = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - callback: aCallback, - context: aContext, - cancel: function() { - this.callback.onAuthCancelled(this.context, false); - this.callback = null; - this.context = null; - } - }; - let [hostname, httpRealm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo); - let hashKey = aLevel + "|" + hostname + "|" + httpRealm; - let asyncPrompt = this._asyncPrompts[hashKey]; - if (asyncPrompt) { - asyncPrompt.consumers.push(cancelable); - return cancelable; - } - - asyncPrompt = { - consumers: [cancelable], - channel: aChannel, - authInfo: aAuthInfo, - level: aLevel, - inProgress : false, - prompter: this - } - - this._asyncPrompts[hashKey] = asyncPrompt; - this._doAsyncPrompt(); - } catch (e) { - Cu.reportError("PromptService: " + e + "\n"); - throw e; - } - return cancelable; - } -}; - -var PromptUtils = { - getLocaleString: function pu_getLocaleString(aKey, aService) { - if (aService == "passwdmgr") - return this.cleanUpLabel(this.passwdBundle.GetStringFromName(aKey)); - - return this.cleanUpLabel(this.bundle.GetStringFromName(aKey)); - }, - - // - // Copied from chrome://global/content/commonDialog.js - // - cleanUpLabel: function cleanUpLabel(aLabel) { - // This is for labels which may contain embedded access keys. - // If we end in (&X) where X represents the access key, optionally preceded - // by spaces and/or followed by the ':' character, - // remove the access key placeholder + leading spaces from the label. - // Otherwise a character preceded by one but not two &s is the access key. - - // Note that if you change the following code, see the comment of - // nsTextBoxFrame::UpdateAccessTitle. - if (!aLabel) - return ""; - - if (/ *\(\&([^&])\)(:?)$/.test(aLabel)) { - aLabel = RegExp.leftContext + RegExp.$2; - } else if (/^([^&]*)\&(([^&]).*$)/.test(aLabel)) { - aLabel = RegExp.$1 + RegExp.$2; - } - - // Special code for using that & symbol - aLabel = aLabel.replace(/\&\&/g, "&"); - - return aLabel; - }, - - get pwmgr() { - delete this.pwmgr; - return this.pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); - }, - - getHostnameAndRealm: function pu_getHostnameAndRealm(aRealmString) { - let httpRealm = /^.+ \(.+\)$/; - if (httpRealm.test(aRealmString)) - return [null, null, null]; - - let uri = Services.io.newURI(aRealmString, null, null); - let pathname = ""; - - if (uri.path != "/") - pathname = uri.path; - - let formattedHostname = this._getFormattedHostname(uri); - return [formattedHostname, formattedHostname + pathname, uri.username]; - }, - - canSaveLogin: function pu_canSaveLogin(aHostname, aSavePassword) { - let canSave = !this._inPrivateBrowsing && this.pwmgr.getLoginSavingEnabled(aHostname) - if (aSavePassword) - canSave = canSave && (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) - return canSave; - }, - - getUsernameAndPassword: function pu_getUsernameAndPassword(aFoundLogins, aUser, aPass) { - let checkLabel = null; - let check = { value: false }; - let selectedLogin; - - checkLabel = this.getLocaleString("rememberButton", "passwdmgr"); - - // XXX Like the original code, we can't deal with multiple - // account selection. (bug 227632) - if (aFoundLogins.length > 0) { - selectedLogin = aFoundLogins[0]; - - // If the caller provided a username, try to use it. If they - // provided only a password, this will try to find a password-only - // login (or return null if none exists). - if (aUser.value) - selectedLogin = this.findLogin(aFoundLogins, "username", aUser.value); - - if (selectedLogin) { - check.value = true; - aUser.value = selectedLogin.username; - // If the caller provided a password, prefer it. - if (!aPass.value) - aPass.value = selectedLogin.password; - } - } - - return [checkLabel, check]; - }, - - findLogin: function pu_findLogin(aLogins, aName, aValue) { - for (let i = 0; i < aLogins.length; i++) - if (aLogins[i][aName] == aValue) - return aLogins[i]; - return null; - }, - - savePassword: function pu_savePassword(aLogins, aUser, aPass, aHostname, aRealm) { - let selectedLogin = this.findLogin(aLogins, "username", aUser.value); - - // If we didn't find an existing login, or if the username - // changed, save as a new login. - if (!selectedLogin) { - // add as new - var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo); - newLogin.init(aHostname, null, aRealm, aUser.value, aPass.value, "", ""); - this.pwmgr.addLogin(newLogin); - } else if (aPass.value != selectedLogin.password) { - // update password - this.updateLogin(selectedLogin, aPass.value); - } else { - this.updateLogin(selectedLogin); - } - }, - - updateLogin: function pu_updateLogin(aLogin, aPassword) { - let now = Date.now(); - let propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag); - if (aPassword) { - propBag.setProperty("password", aPassword); - // Explicitly set the password change time here (even though it would - // be changed automatically), to ensure that it's exactly the same - // value as timeLastUsed. - propBag.setProperty("timePasswordChanged", now); - } - propBag.setProperty("timeLastUsed", now); - propBag.setProperty("timesUsedIncrement", 1); - - this.pwmgr.modifyLogin(aLogin, propBag); - }, - - // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/nsPrompt.cpp#388 - makeDialogText: function pu_makeDialogText(aChannel, aAuthInfo) { - let isProxy = (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY); - let isPassOnly = (aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD); - let isCrossOrig = (aAuthInfo.flags & - Ci.nsIAuthInformation.CROSS_ORIGIN_SUB_RESOURCE); - - let username = aAuthInfo.username; - let [displayHost, realm] = this.getAuthTarget(aChannel, aAuthInfo); - - // Suppress "the site says: $realm" when we synthesized a missing realm. - if (!aAuthInfo.realm && !isProxy) - realm = ""; - - // Trim obnoxiously long realms. - if (realm.length > 150) { - realm = realm.substring(0, 150); - // Append "..." (or localized equivalent). - realm += this.ellipsis; - } - - let text; - if (isProxy) { - text = this.bundle.formatStringFromName("EnterLoginForProxy3", [realm, displayHost], 2); - } else if (isPassOnly) { - text = this.bundle.formatStringFromName("EnterPasswordFor", [username, displayHost], 2); - } else if (isCrossOrig) { - text = this.bundle.formatStringFromName("EnterUserPasswordForCrossOrigin2", [displayHost], 1); - } else if (!realm) { - text = this.bundle.formatStringFromName("EnterUserPasswordFor2", [displayHost], 1); - } else { - text = this.bundle.formatStringFromName("EnterLoginForRealm3", [realm, displayHost], 2); - } - - return text; - }, - - // JS port of http://mxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/nsPromptUtils.h#89 - getAuthHostPort: function pu_getAuthHostPort(aChannel, aAuthInfo) { - let uri = aChannel.URI; - let res = { host: null, port: -1 }; - if (aAuthInfo.flags & aAuthInfo.AUTH_PROXY) { - let proxy = aChannel.QueryInterface(Ci.nsIProxiedChannel); - res.host = proxy.proxyInfo.host; - res.port = proxy.proxyInfo.port; - } else { - res.host = uri.host; - res.port = uri.port; - } - return res; - }, - - getAuthTarget : function pu_getAuthTarget(aChannel, aAuthInfo) { - let hostname, realm; - // If our proxy is demanding authentication, don't use the - // channel's actual destination. - if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) { - if (!(aChannel instanceof Ci.nsIProxiedChannel)) - throw "proxy auth needs nsIProxiedChannel"; - - let info = aChannel.proxyInfo; - if (!info) - throw "proxy auth needs nsIProxyInfo"; - - // Proxies don't have a scheme, but we'll use "moz-proxy://" - // so that it's more obvious what the login is for. - let idnService = Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService); - hostname = "moz-proxy://" + idnService.convertUTF8toACE(info.host) + ":" + info.port; - realm = aAuthInfo.realm; - if (!realm) - realm = hostname; - - return [hostname, realm]; - } - hostname = this.getFormattedHostname(aChannel.URI); - - // If a HTTP WWW-Authenticate header specified a realm, that value - // will be available here. If it wasn't set or wasn't HTTP, we'll use - // the formatted hostname instead. - realm = aAuthInfo.realm; - if (!realm) - realm = hostname; - - return [hostname, realm]; - }, - - getAuthInfo : function pu_getAuthInfo(aAuthInfo) { - let flags = aAuthInfo.flags; - let username = {value: ""}; - let password = {value: ""}; - - if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && aAuthInfo.domain) - username.value = aAuthInfo.domain + "\\" + aAuthInfo.username; - else - username.value = aAuthInfo.username; - - password.value = aAuthInfo.password - - return [username, password]; - }, - - setAuthInfo : function (aAuthInfo, username, password) { - var flags = aAuthInfo.flags; - if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) { - // Domain is separated from username by a backslash - var idx = username.indexOf("\\"); - if (idx == -1) { - aAuthInfo.username = username; - } else { - aAuthInfo.domain = username.substring(0, idx); - aAuthInfo.username = username.substring(idx+1); - } - } else { - aAuthInfo.username = username; - } - aAuthInfo.password = password; - }, - - /** - * Strip out things like userPass and path for display. - */ - getFormattedHostname : function pu_getFormattedHostname(uri) { - return uri.scheme + "://" + uri.hostPort; - }, - - fireDialogEvent: function(aDomWin, aEventName) { - // accessing the document object can throw if this window no longer exists. See bug 789888. - try { - if (!aDomWin.document) - return; - let event = aDomWin.document.createEvent("Events"); - event.initEvent(aEventName, true, true); - let winUtils = aDomWin.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - winUtils.dispatchEventToChromeOnly(aDomWin, event); - } catch(ex) { - } - } -}; - -XPCOMUtils.defineLazyGetter(PromptUtils, "passwdBundle", function () { - return Services.strings.createBundle("chrome://passwordmgr/locale/passwordmgr.properties"); -}); - -XPCOMUtils.defineLazyGetter(PromptUtils, "bundle", function () { - return Services.strings.createBundle("chrome://global/locale/commonDialogs.properties"); -}); - - -// Factory for wrapping nsIAuthPrompt interfaces to make them usable via an nsIAuthPrompt2 interface. -// XXX Copied from nsPrompter.js. -function AuthPromptAdapterFactory() { -} - -AuthPromptAdapterFactory.prototype = { - classID: Components.ID("{80dae1e9-e0d2-4974-915f-f97050fa8068}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIAuthPromptAdapterFactory]), - - /* ---------- nsIAuthPromptAdapterFactory ---------- */ - - createAdapter: function(aPrompt) { - return new AuthPromptAdapter(aPrompt); - } -}; - - -// Takes an nsIAuthPrompt implementation, wraps it with a nsIAuthPrompt2 shell. -// XXX Copied from nsPrompter.js. -function AuthPromptAdapter(aPrompt) { - this.prompt = aPrompt; -} - -AuthPromptAdapter.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIAuthPrompt2]), - prompt: null, - - /* ---------- nsIAuthPrompt2 ---------- */ - - promptAuth: function(aChannel, aLevel, aAuthInfo, aCheckLabel, aCheckValue) { - let message = PromptUtils.makeDialogText(aChannel, aAuthInfo); - - let [username, password] = PromptUtils.getAuthInfo(aAuthInfo); - let [host, realm] = PromptUtils.getAuthTarget(aChannel, aAuthInfo); - let authTarget = host + " (" + realm + ")"; - - let ok; - if (aAuthInfo.flags & Ci.nsIAuthInformation.ONLY_PASSWORD) { - ok = this.prompt.promptPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, password); - } else { - ok = this.prompt.promptUsernameAndPassword(null, message, authTarget, Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, username, password); - } - - if (ok) { - PromptUtils.setAuthInfo(aAuthInfo, username.value, password.value); - } - return ok; - }, - - asyncPromptAuth: function(aChannel, aCallback, aContext, aLevel, aAuthInfo, aCheckLabel, aCheckValue) { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PromptService, AuthPromptAdapterFactory]); diff --git a/mobile/android/components/SessionStore.idl b/mobile/android/components/SessionStore.idl deleted file mode 100644 index 14ddd5834..000000000 --- a/mobile/android/components/SessionStore.idl +++ /dev/null @@ -1,86 +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/. */ - -#include "nsISupports.idl" - -interface nsIDOMWindow; -interface nsIDOMNode; - -/** - * nsISessionStore keeps track of the current browsing state. - * - * The nsISessionStore API operates mostly on browser windows and the browser - * tabs contained in them. - */ - -[scriptable, uuid(da9ffc70-d444-47d4-b4ab-df3fb0fd24d0)] -interface nsISessionStore : nsISupports -{ - /** - * Get the current browsing state. - * @returns a JSON string representing the session state. - */ - AString getBrowserState(); - - /** - * Get the number of restore-able tabs for a browser window - */ - unsigned long getClosedTabCount(in nsIDOMWindow aWindow); - - /** - * Get closed tab data - * - * @param aWindow is the browser window for which to get closed tab data - * @returns a JS array of closed tabs. - */ - jsval getClosedTabs(in nsIDOMWindow aWindow); - - /** - * @param aWindow is the browser window to reopen a closed tab in. - * @param aCloseTabData is the data of the tab to be restored. - * @returns a reference to the reopened tab. - */ - nsIDOMNode undoCloseTab(in nsIDOMWindow aWindow, in jsval aCloseTabData); - - /** - * @param aWindow is the browser window associated with the closed tab. - * @param aIndex is the index of the closed tab to be removed (FIFO ordered). - */ - nsIDOMNode forgetClosedTab(in nsIDOMWindow aWindow, in unsigned long aIndex); - - /** - * @param aTab is the browser tab to get the value for. - * @param aKey is the value's name. - * - * @returns A string value or an empty string if none is set. - */ - AString getTabValue(in jsval aTab, in AString aKey); - - /** - * @param aTab is the browser tab to set the value for. - * @param aKey is the value's name. - * @param aStringValue is the value itself (use JSON.stringify/parse before setting JS objects). - */ - void setTabValue(in jsval aTab, in AString aKey, in AString aStringValue); - - /** - * @param aTab is the browser tab to get the value for. - * @param aKey is the value's name. - */ - void deleteTabValue(in jsval aTab, in AString aKey); - - /** - * Restores the previous browser session using a fast, lightweight strategy - * @param aSessionString The session string to restore from. If null, the - * backup session file is read from. - */ - void restoreLastSession(in AString aSessionString); - - /** - * Removes a window from the current session history. Data from this window - * won't be saved when its closed. - * @param aWindow The window to remove - */ - void removeWindow(in nsIDOMWindow aWindow); -}; diff --git a/mobile/android/components/SessionStore.js b/mobile/android/components/SessionStore.js deleted file mode 100644 index a23c52fe3..000000000 --- a/mobile/android/components/SessionStore.js +++ /dev/null @@ -1,1791 +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 Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FormData", "resource://gre/modules/FormData.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ScrollPosition", "resource://gre/modules/ScrollPosition.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Log", "resource://gre/modules/AndroidLog.jsm", "AndroidLog"); -XPCOMUtils.defineLazyModuleGetter(this, "SharedPreferences", "resource://gre/modules/SharedPreferences.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Utils", "resource://gre/modules/sessionstore/Utils.jsm"); -XPCOMUtils.defineLazyServiceGetter(this, "serializationHelper", - "@mozilla.org/network/serialization-helper;1", - "nsISerializationHelper"); - -function dump(a) { - Services.console.logStringMessage(a); -} - -let loggingEnabled = false; - -function log(a) { - if (!loggingEnabled) { - return; - } - Log.d("SessionStore", a); -} - -// ----------------------------------------------------------------------- -// Session Store -// ----------------------------------------------------------------------- - -const STATE_STOPPED = 0; -const STATE_RUNNING = 1; -const STATE_QUITTING = -1; -const STATE_QUITTING_FLUSHED = -2; - -const PRIVACY_NONE = 0; -const PRIVACY_ENCRYPTED = 1; -const PRIVACY_FULL = 2; - -const PREFS_RESTORE_FROM_CRASH = "browser.sessionstore.resume_from_crash"; -const PREFS_MAX_CRASH_RESUMES = "browser.sessionstore.max_resumed_crashes"; - -const MINIMUM_SAVE_DELAY = 2000; -// We reduce the delay in background because we could be killed at any moment, -// however we don't set it to 0 in order to allow for multiple events arriving -// one after the other to be batched together in one write operation. -const MINIMUM_SAVE_DELAY_BACKGROUND = 200; - -function SessionStore() { } - -SessionStore.prototype = { - classID: Components.ID("{8c1f07d6-cba3-4226-a315-8bd43d67d032}"), - - QueryInterface: XPCOMUtils.generateQI([Ci.nsISessionStore, - Ci.nsIDOMEventListener, - Ci.nsIObserver, - Ci.nsISupportsWeakReference]), - - _windows: {}, - _lastSaveTime: 0, - _lastBackupTime: 0, - _interval: 10000, - _backupInterval: 120000, // 2 minutes - _minSaveDelay: MINIMUM_SAVE_DELAY, - _maxTabsUndo: 5, - _pendingWrite: 0, - _scrollSavePending: null, - _writeInProgress: false, - - // We only want to start doing backups if we've successfully - // written the session data at least once. - _sessionDataIsGood: false, - - // The index where the most recently closed tab was in the tabs array - // when it was closed. - _lastClosedTabIndex: -1, - - // Whether or not to send notifications for changes to the closed tabs. - _notifyClosedTabs: false, - - // If we're simultaneously closing both a tab and Firefox, we don't want - // to bother reloading the newly selected tab if it is zombified. - // The Java UI will tell us which tab to watch out for. - _keepAsZombieTabId: -1, - - init: function ss_init() { - loggingEnabled = Services.prefs.getBoolPref("browser.sessionstore.debug_logging"); - - // Get file references - this._sessionFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile); - this._sessionFileBackup = this._sessionFile.clone(); - this._sessionFilePrevious = this._sessionFile.clone(); - this._sessionFileTemp = this._sessionFile.clone(); - this._sessionFile.append("sessionstore.js"); // The main session store save file. - this._sessionFileBackup.append("sessionstore.bak"); // A backup copy to guard against interrupted writes. - this._sessionFilePrevious.append("sessionstore.old"); // The previous session's file, used for what used to be the "Tabs from last time". - this._sessionFileTemp.append(this._sessionFile.leafName + ".tmp"); // Temporary file for writing changes to disk. - - this._loadState = STATE_STOPPED; - this._startupRestoreFinished = false; - - this._interval = Services.prefs.getIntPref("browser.sessionstore.interval"); - this._backupInterval = Services.prefs.getIntPref("browser.sessionstore.backupInterval"); - this._maxTabsUndo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo"); - - // Copy changes in Gecko settings to their Java counterparts, - // so the startup code can access them - Services.prefs.addObserver(PREFS_RESTORE_FROM_CRASH, function() { - SharedPreferences.forApp().setBoolPref(PREFS_RESTORE_FROM_CRASH, - Services.prefs.getBoolPref(PREFS_RESTORE_FROM_CRASH)); - }, false); - Services.prefs.addObserver(PREFS_MAX_CRASH_RESUMES, function() { - SharedPreferences.forApp().setIntPref(PREFS_MAX_CRASH_RESUMES, - Services.prefs.getIntPref(PREFS_MAX_CRASH_RESUMES)); - }, false); - }, - - _clearDisk: function ss_clearDisk() { - this._sessionDataIsGood = false; - - if (this._loadState > STATE_QUITTING) { - OS.File.remove(this._sessionFile.path); - OS.File.remove(this._sessionFileBackup.path); - OS.File.remove(this._sessionFilePrevious.path); - OS.File.remove(this._sessionFileTemp.path); - } else { // We're shutting down and must delete synchronously - if (this._sessionFile.exists()) { this._sessionFile.remove(false); } - if (this._sessionFileBackup.exists()) { this._sessionFileBackup.remove(false); } - if (this._sessionFileBackup.exists()) { this._sessionFilePrevious.remove(false); } - if (this._sessionFileBackup.exists()) { this._sessionFileTemp.remove(false); } - } - }, - - observe: function ss_observe(aSubject, aTopic, aData) { - let self = this; - let observerService = Services.obs; - switch (aTopic) { - case "app-startup": - observerService.addObserver(this, "final-ui-startup", true); - observerService.addObserver(this, "domwindowopened", true); - observerService.addObserver(this, "domwindowclosed", true); - observerService.addObserver(this, "browser:purge-session-history", true); - observerService.addObserver(this, "quit-application-requested", true); - observerService.addObserver(this, "quit-application-proceeding", true); - observerService.addObserver(this, "quit-application", true); - observerService.addObserver(this, "Session:Restore", true); - observerService.addObserver(this, "Session:NotifyLocationChange", true); - observerService.addObserver(this, "Tab:KeepZombified", true); - observerService.addObserver(this, "application-background", true); - observerService.addObserver(this, "application-foreground", true); - observerService.addObserver(this, "ClosedTabs:StartNotifications", true); - observerService.addObserver(this, "ClosedTabs:StopNotifications", true); - observerService.addObserver(this, "last-pb-context-exited", true); - observerService.addObserver(this, "Session:RestoreRecentTabs", true); - observerService.addObserver(this, "Tabs:OpenMultiple", true); - break; - case "final-ui-startup": - observerService.removeObserver(this, "final-ui-startup"); - this.init(); - break; - case "domwindowopened": { - let window = aSubject; - window.addEventListener("load", function() { - self.onWindowOpen(window); - window.removeEventListener("load", arguments.callee, false); - }, false); - break; - } - case "domwindowclosed": // catch closed windows - this.onWindowClose(aSubject); - break; - case "quit-application-requested": - log("quit-application-requested"); - // Get a current snapshot of all windows - if (this._pendingWrite) { - this._forEachBrowserWindow(function(aWindow) { - self._collectWindowData(aWindow); - }); - } - break; - case "quit-application-proceeding": - log("quit-application-proceeding"); - // Freeze the data at what we've got (ignoring closing windows) - this._loadState = STATE_QUITTING; - break; - case "quit-application": - log("quit-application"); - observerService.removeObserver(this, "domwindowopened"); - observerService.removeObserver(this, "domwindowclosed"); - observerService.removeObserver(this, "quit-application-requested"); - observerService.removeObserver(this, "quit-application-proceeding"); - observerService.removeObserver(this, "quit-application"); - - // Flush all pending writes to disk now - this.flushPendingState(); - this._loadState = STATE_QUITTING_FLUSHED; - - break; - case "browser:purge-session-history": // catch sanitization - log("browser:purge-session-history"); - this._clearDisk(); - - // Clear all data about closed tabs - for (let [ssid, win] of Object.entries(this._windows)) - win.closedTabs = []; - - this._lastClosedTabIndex = -1; - - if (this._loadState == STATE_RUNNING) { - // Save the purged state immediately - this.saveState(); - } else if (this._loadState <= STATE_QUITTING) { - this.saveStateDelayed(); - if (this._loadState == STATE_QUITTING_FLUSHED) { - this.flushPendingState(); - } - } - - Services.obs.notifyObservers(null, "sessionstore-state-purge-complete", ""); - if (this._notifyClosedTabs) { - this._sendClosedTabsToJava(Services.wm.getMostRecentWindow("navigator:browser")); - } - break; - case "timer-callback": - if (this._loadState == STATE_RUNNING) { - // Timer call back for delayed saving - this._saveTimer = null; - log("timer-callback, pendingWrite = " + this._pendingWrite); - if (this._pendingWrite) { - this.saveState(); - } - } - break; - case "Session:Restore": { - Services.obs.removeObserver(this, "Session:Restore"); - if (aData) { - // Be ready to handle any restore failures by making sure we have a valid tab opened - let window = Services.wm.getMostRecentWindow("navigator:browser"); - let restoreCleanup = { - observe: function (aSubject, aTopic, aData) { - Services.obs.removeObserver(restoreCleanup, "sessionstore-windows-restored"); - - if (window.BrowserApp.tabs.length == 0) { - window.BrowserApp.addTab("about:home", { - selected: true - }); - } - // Normally, _restoreWindow() will have set this to true already, - // but we want to make sure it's set even in case of a restore failure. - this._startupRestoreFinished = true; - log("startupRestoreFinished = true (through notification)"); - }.bind(this) - }; - Services.obs.addObserver(restoreCleanup, "sessionstore-windows-restored", false); - - // Do a restore, triggered by Java - let data = JSON.parse(aData); - this.restoreLastSession(data.sessionString); - } else { - // Not doing a restore; just send restore message - this._startupRestoreFinished = true; - log("startupRestoreFinished = true"); - Services.obs.notifyObservers(null, "sessionstore-windows-restored", ""); - } - break; - } - case "Session:NotifyLocationChange": { - let browser = aSubject; - - if (browser.__SS_restoreReloadPending && this._startupRestoreFinished) { - delete browser.__SS_restoreReloadPending; - log("remove restoreReloadPending"); - } - - if (browser.__SS_restoreDataOnLocationChange) { - delete browser.__SS_restoreDataOnLocationChange; - this._restoreZoom(browser.__SS_data.scrolldata, browser); - } - break; - } - case "Tabs:OpenMultiple": { - let data = JSON.parse(aData); - - this._openTabs(data); - - if (data.shouldNotifyTabsOpenedToJava) { - Messaging.sendRequest({ - type: "Tabs:TabsOpened" - }); - } - break; - } - case "Tab:KeepZombified": { - if (aData >= 0) { - this._keepAsZombieTabId = aData; - log("Tab:KeepZombified " + aData); - } - break; - } - case "application-background": - // We receive this notification when Android's onPause callback is - // executed. After onPause, the application may be terminated at any - // point without notice; therefore, we must synchronously write out any - // pending save state to ensure that this data does not get lost. - log("application-background"); - // Tab events dispatched immediately before the application was backgrounded - // might actually arrive after this point, therefore save them without delay. - if (this._loadState == STATE_RUNNING) { - this._interval = 0; - this._minSaveDelay = MINIMUM_SAVE_DELAY_BACKGROUND; // A small delay allows successive tab events to be batched together. - this.flushPendingState(); - } - break; - case "application-foreground": - // Reset minimum interval between session store writes back to default. - log("application-foreground"); - this._interval = Services.prefs.getIntPref("browser.sessionstore.interval"); - this._minSaveDelay = MINIMUM_SAVE_DELAY; - - // If we skipped restoring a zombified tab before backgrounding, - // we might have to do it now instead. - let window = Services.wm.getMostRecentWindow("navigator:browser"); - if (window) { // Might not yet be ready during a cold startup. - let tab = window.BrowserApp.selectedTab; - if (tab.browser.__SS_restore) { - this._restoreZombieTab(tab.browser, tab.id); - } - } - break; - case "ClosedTabs:StartNotifications": - this._notifyClosedTabs = true; - log("ClosedTabs:StartNotifications"); - this._sendClosedTabsToJava(Services.wm.getMostRecentWindow("navigator:browser")); - break; - case "ClosedTabs:StopNotifications": - this._notifyClosedTabs = false; - log("ClosedTabs:StopNotifications"); - break; - case "last-pb-context-exited": - // Clear private closed tab data when we leave private browsing. - for (let window of Object.values(this._windows)) { - window.closedTabs = window.closedTabs.filter(tab => !tab.isPrivate); - } - this._lastClosedTabIndex = -1; - break; - case "Session:RestoreRecentTabs": { - let data = JSON.parse(aData); - this._restoreTabs(data); - break; - } - } - }, - - handleEvent: function ss_handleEvent(aEvent) { - let window = aEvent.currentTarget.ownerDocument.defaultView; - switch (aEvent.type) { - case "TabOpen": { - let browser = aEvent.target; - log("TabOpen for tab " + window.BrowserApp.getTabForBrowser(browser).id); - this.onTabAdd(window, browser); - break; - } - case "TabClose": { - let browser = aEvent.target; - log("TabClose for tab " + window.BrowserApp.getTabForBrowser(browser).id); - this.onTabClose(window, browser, aEvent.detail); - this.onTabRemove(window, browser); - break; - } - case "TabPreZombify": { - let browser = aEvent.target; - log("TabPreZombify for tab " + window.BrowserApp.getTabForBrowser(browser).id); - this.onTabRemove(window, browser, true); - break; - } - case "TabPostZombify": { - let browser = aEvent.target; - log("TabPostZombify for tab " + window.BrowserApp.getTabForBrowser(browser).id); - this.onTabAdd(window, browser, true); - break; - } - case "TabSelect": { - let browser = aEvent.target; - log("TabSelect for tab " + window.BrowserApp.getTabForBrowser(browser).id); - this.onTabSelect(window, browser); - break; - } - case "DOMTitleChanged": { - // Use DOMTitleChanged to detect page loads over alternatives. - // onLocationChange happens too early, so we don't have the page title - // yet; pageshow happens too late, so we could lose session data if the - // browser were killed. - let browser = aEvent.currentTarget; - log("DOMTitleChanged for tab " + window.BrowserApp.getTabForBrowser(browser).id); - this.onTabLoad(window, browser); - break; - } - case "load": { - let browser = aEvent.currentTarget; - - // Skip subframe loads. - if (browser.contentDocument !== aEvent.originalTarget) { - return; - } - - // Handle restoring the text data into the content and frames. - // We wait until the main content and all frames are loaded - // before trying to restore this data. - log("load for tab " + window.BrowserApp.getTabForBrowser(browser).id); - if (browser.__SS_restoreDataOnLoad) { - delete browser.__SS_restoreDataOnLoad; - this._restoreTextData(browser.__SS_data.formdata, browser); - } - break; - } - case "pageshow": - case "AboutReaderContentReady": { - let browser = aEvent.currentTarget; - - // Skip subframe pageshows. - if (browser.contentDocument !== aEvent.originalTarget) { - return; - } - - if (browser.currentURI.spec.startsWith("about:reader") && - !browser.contentDocument.body.classList.contains("loaded")) { - // Don't restore the scroll position of an about:reader page at this point; - // wait for the custom event dispatched from AboutReader.jsm instead. - return; - } - - // Restoring the scroll position needs to happen after the zoom level has been - // restored, which is done by the MobileViewportManager either on first paint - // or on load, whichever comes first. - // In the latter case, our load handler runs before the MVM's one, which is the - // wrong way around, so we have to use a later event instead. - log(aEvent.type + " for tab " + window.BrowserApp.getTabForBrowser(browser).id); - if (browser.__SS_restoreDataOnPageshow) { - delete browser.__SS_restoreDataOnPageshow; - this._restoreScrollPosition(browser.__SS_data.scrolldata, browser); - } else { - // We're not restoring, capture the initial scroll position on pageshow. - this.onTabScroll(window, browser); - } - break; - } - case "change": - case "input": - case "DOMAutoComplete": { - let browser = aEvent.currentTarget; - log("TabInput for tab " + window.BrowserApp.getTabForBrowser(browser).id); - this.onTabInput(window, browser); - break; - } - case "resize": - case "scroll": { - let browser = aEvent.currentTarget; - // Duplicated logging check to avoid calling getTabForBrowser on each scroll event. - if (loggingEnabled) { - log(aEvent.type + " for tab " + window.BrowserApp.getTabForBrowser(browser).id); - } - if (!this._scrollSavePending) { - this._scrollSavePending = - window.setTimeout(() => { - this._scrollSavePending = null; - this.onTabScroll(window, browser); - }, 500); - } - break; - } - } - }, - - onWindowOpen: function ss_onWindowOpen(aWindow) { - // Return if window has already been initialized - if (aWindow && aWindow.__SSID && this._windows[aWindow.__SSID]) { - return; - } - - // Ignore non-browser windows and windows opened while shutting down - if (aWindow.document.documentElement.getAttribute("windowtype") != "navigator:browser" || this._loadState <= STATE_QUITTING) { - return; - } - - // Assign it a unique identifier (timestamp) and create its data object - aWindow.__SSID = "window" + Date.now(); - this._windows[aWindow.__SSID] = { tabs: [], selected: 0, closedTabs: [] }; - - // Perform additional initialization when the first window is loading - if (this._loadState == STATE_STOPPED) { - this._loadState = STATE_RUNNING; - this._lastSaveTime = Date.now(); - } - - // Add tab change listeners to all already existing tabs - let tabs = aWindow.BrowserApp.tabs; - for (let i = 0; i < tabs.length; i++) - this.onTabAdd(aWindow, tabs[i].browser, true); - - // Notification of tab add/remove/selection/zombification - let browsers = aWindow.document.getElementById("browsers"); - browsers.addEventListener("TabOpen", this, true); - browsers.addEventListener("TabClose", this, true); - browsers.addEventListener("TabSelect", this, true); - browsers.addEventListener("TabPreZombify", this, true); - browsers.addEventListener("TabPostZombify", this, true); - }, - - onWindowClose: function ss_onWindowClose(aWindow) { - // Ignore windows not tracked by SessionStore - if (!aWindow.__SSID || !this._windows[aWindow.__SSID]) { - return; - } - - let browsers = aWindow.document.getElementById("browsers"); - browsers.removeEventListener("TabOpen", this, true); - browsers.removeEventListener("TabClose", this, true); - browsers.removeEventListener("TabSelect", this, true); - browsers.removeEventListener("TabPreZombify", this, true); - browsers.removeEventListener("TabPostZombify", this, true); - - if (this._loadState == STATE_RUNNING) { - // Update all window data for a last time - this._collectWindowData(aWindow); - - // Clear this window from the list - delete this._windows[aWindow.__SSID]; - - // Save the state without this window to disk - this.saveStateDelayed(); - } - - let tabs = aWindow.BrowserApp.tabs; - for (let i = 0; i < tabs.length; i++) - this.onTabRemove(aWindow, tabs[i].browser, true); - - delete aWindow.__SSID; - }, - - onTabAdd: function ss_onTabAdd(aWindow, aBrowser, aNoNotification) { - // Use DOMTitleChange to catch the initial load and restore history - aBrowser.addEventListener("DOMTitleChanged", this, true); - - // Use load to restore text data - aBrowser.addEventListener("load", this, true); - - // Gecko might set the initial zoom level after the JS "load" event, - // so we have to restore zoom and scroll position after that. - aBrowser.addEventListener("pageshow", this, true); - aBrowser.addEventListener("AboutReaderContentReady", this, true); - - // Use a combination of events to watch for text data changes - aBrowser.addEventListener("change", this, true); - aBrowser.addEventListener("input", this, true); - aBrowser.addEventListener("DOMAutoComplete", this, true); - - // Record the current scroll position and zoom level. - aBrowser.addEventListener("scroll", this, true); - aBrowser.addEventListener("resize", this, true); - - log("onTabAdd() ran for tab " + aWindow.BrowserApp.getTabForBrowser(aBrowser).id + - ", aNoNotification = " + aNoNotification); - if (!aNoNotification) { - this.saveStateDelayed(); - } - this._updateCrashReportURL(aWindow); - }, - - onTabRemove: function ss_onTabRemove(aWindow, aBrowser, aNoNotification) { - // Cleanup event listeners - aBrowser.removeEventListener("DOMTitleChanged", this, true); - aBrowser.removeEventListener("load", this, true); - aBrowser.removeEventListener("pageshow", this, true); - aBrowser.removeEventListener("AboutReaderContentReady", this, true); - aBrowser.removeEventListener("change", this, true); - aBrowser.removeEventListener("input", this, true); - aBrowser.removeEventListener("DOMAutoComplete", this, true); - aBrowser.removeEventListener("scroll", this, true); - aBrowser.removeEventListener("resize", this, true); - - delete aBrowser.__SS_data; - - log("onTabRemove() ran for tab " + aWindow.BrowserApp.getTabForBrowser(aBrowser).id + - ", aNoNotification = " + aNoNotification); - if (!aNoNotification) { - this.saveStateDelayed(); - } - }, - - onTabClose: function ss_onTabClose(aWindow, aBrowser, aTabIndex) { - if (this._maxTabsUndo == 0) { - return; - } - - if (aWindow.BrowserApp.tabs.length > 0) { - // Bundle this browser's data and extra data and save in the closedTabs - // window property - let data = aBrowser.__SS_data || {}; - data.extData = aBrowser.__SS_extdata || {}; - - this._windows[aWindow.__SSID].closedTabs.unshift(data); - let length = this._windows[aWindow.__SSID].closedTabs.length; - if (length > this._maxTabsUndo) { - this._windows[aWindow.__SSID].closedTabs.splice(this._maxTabsUndo, length - this._maxTabsUndo); - } - - this._lastClosedTabIndex = aTabIndex; - - if (this._notifyClosedTabs) { - this._sendClosedTabsToJava(aWindow); - } - - log("onTabClose() ran for tab " + aWindow.BrowserApp.getTabForBrowser(aBrowser).id); - let evt = new Event("SSTabCloseProcessed", {"bubbles":true, "cancelable":false}); - aBrowser.dispatchEvent(evt); - } - }, - - onTabLoad: function ss_onTabLoad(aWindow, aBrowser) { - // If this browser belongs to a zombie tab or the initial restore hasn't yet finished, - // skip any session save activity. - if (aBrowser.__SS_restore || !this._startupRestoreFinished || aBrowser.__SS_restoreReloadPending) { - return; - } - - // Ignore a transient "about:blank" - if (!aBrowser.canGoBack && aBrowser.currentURI.spec == "about:blank") { - return; - } - - let history = aBrowser.sessionHistory; - - // Serialize the tab data - let entries = []; - let index = history.index + 1; - for (let i = 0; i < history.count; i++) { - let historyEntry = history.getEntryAtIndex(i, false); - // Don't try to restore wyciwyg URLs - if (historyEntry.URI.schemeIs("wyciwyg")) { - // Adjust the index to account for skipped history entries - if (i <= history.index) { - index--; - } - continue; - } - let entry = this._serializeHistoryEntry(historyEntry); - entries.push(entry); - } - let data = { entries: entries, index: index }; - - let formdata; - let scrolldata; - if (aBrowser.__SS_data) { - formdata = aBrowser.__SS_data.formdata; - scrolldata = aBrowser.__SS_data.scrolldata; - } - delete aBrowser.__SS_data; - - this._collectTabData(aWindow, aBrowser, data); - if (aBrowser.__SS_restoreDataOnLoad || aBrowser.__SS_restoreDataOnPageshow) { - // If the tab has been freshly restored and the "load" or "pageshow" - // events haven't yet fired, we need to preserve any form data and - // scroll positions that might have been present. - aBrowser.__SS_data.formdata = formdata; - aBrowser.__SS_data.scrolldata = scrolldata; - } else { - // When navigating via the forward/back buttons, Gecko restores - // the form data all by itself and doesn't invoke any input events. - // As _collectTabData() doesn't save any form data, we need to manually - // capture it to bridge the time until the next input event arrives. - this.onTabInput(aWindow, aBrowser); - } - - log("onTabLoad() ran for tab " + aWindow.BrowserApp.getTabForBrowser(aBrowser).id); - let evt = new Event("SSTabDataUpdated", {"bubbles":true, "cancelable":false}); - aBrowser.dispatchEvent(evt); - this.saveStateDelayed(); - - this._updateCrashReportURL(aWindow); - }, - - onTabSelect: function ss_onTabSelect(aWindow, aBrowser) { - if (this._loadState != STATE_RUNNING) { - return; - } - - let browsers = aWindow.document.getElementById("browsers"); - let index = browsers.selectedIndex; - this._windows[aWindow.__SSID].selected = parseInt(index) + 1; // 1-based - - let tabId = aWindow.BrowserApp.getTabForBrowser(aBrowser).id; - - // Restore the resurrected browser - if (aBrowser.__SS_restore) { - if (tabId != this._keepAsZombieTabId) { - this._restoreZombieTab(aBrowser, tabId); - } else { - log("keeping as zombie tab " + tabId); - } - } - // The tab id passed through Tab:KeepZombified is valid for one TabSelect only. - this._keepAsZombieTabId = -1; - - log("onTabSelect() ran for tab " + tabId); - this.saveStateDelayed(); - this._updateCrashReportURL(aWindow); - - // If the selected tab has changed while listening for closed tab - // notifications, we may have switched between different private browsing - // modes. - if (this._notifyClosedTabs) { - this._sendClosedTabsToJava(aWindow); - } - }, - - _restoreZombieTab: function ss_restoreZombieTab(aBrowser, aTabId) { - let data = aBrowser.__SS_data; - this._restoreTab(data, aBrowser); - - delete aBrowser.__SS_restore; - aBrowser.removeAttribute("pending"); - log("restoring zombie tab " + aTabId); - }, - - onTabInput: function ss_onTabInput(aWindow, aBrowser) { - // If this browser belongs to a zombie tab or the initial restore hasn't yet finished, - // skip any session save activity. - if (aBrowser.__SS_restore || !this._startupRestoreFinished || aBrowser.__SS_restoreReloadPending) { - return; - } - - // Don't bother trying to save text data if we don't have history yet - let data = aBrowser.__SS_data; - if (!data || data.entries.length == 0) { - return; - } - - // Start with storing the main content - let content = aBrowser.contentWindow; - - // If the main content document has an associated URL that we are not - // allowed to store data for, bail out. We explicitly discard data for any - // children as well even if storing data for those frames would be allowed. - if (!this.checkPrivacyLevel(content.document.documentURI)) { - return; - } - - // Store the main content - let formdata = FormData.collect(content) || {}; - - // Loop over direct child frames, and store the text data - let children = []; - for (let i = 0; i < content.frames.length; i++) { - let frame = content.frames[i]; - if (!this.checkPrivacyLevel(frame.document.documentURI)) { - continue; - } - - let result = FormData.collect(frame); - if (result && Object.keys(result).length) { - children[i] = result; - } - } - - // If any frame had text data, add it to the main form data - if (children.length) { - formdata.children = children; - } - - // If we found any form data, main content or frames, let's save it - if (Object.keys(formdata).length) { - data.formdata = formdata; - log("onTabInput() ran for tab " + aWindow.BrowserApp.getTabForBrowser(aBrowser).id); - this.saveStateDelayed(); - } - }, - - onTabScroll: function ss_onTabScroll(aWindow, aBrowser) { - // If we've been called directly, cancel any pending timeouts. - if (this._scrollSavePending) { - aWindow.clearTimeout(this._scrollSavePending); - this._scrollSavePending = null; - log("onTabScroll() clearing pending timeout"); - } - - // If this browser belongs to a zombie tab or the initial restore hasn't yet finished, - // skip any session save activity. - if (aBrowser.__SS_restore || !this._startupRestoreFinished || aBrowser.__SS_restoreReloadPending) { - return; - } - - // Don't bother trying to save scroll positions if we don't have history yet. - let data = aBrowser.__SS_data; - if (!data || data.entries.length == 0) { - return; - } - - // Neither bother if we're yet to restore the previous scroll position. - if (aBrowser.__SS_restoreDataOnLoad || aBrowser.__SS_restoreDataOnPageshow) { - return; - } - - // Start with storing the main content. - let content = aBrowser.contentWindow; - - // Store the main content. - let scrolldata = ScrollPosition.collect(content) || {}; - - // Loop over direct child frames, and store the scroll positions. - let children = []; - for (let i = 0; i < content.frames.length; i++) { - let frame = content.frames[i]; - - let result = ScrollPosition.collect(frame); - if (result && Object.keys(result).length) { - children[i] = result; - } - } - - // If any frame had scroll positions, add them to the main scroll data. - if (children.length) { - scrolldata.children = children; - } - - // Save the current document resolution. - let zoom = { value: 1 }; - content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface( - Ci.nsIDOMWindowUtils).getResolution(zoom); - scrolldata.zoom = {}; - scrolldata.zoom.resolution = zoom.value; - log("onTabScroll() zoom level: " + zoom.value); - - // Save some data that'll help in adjusting the zoom level - // when restoring in a different screen orientation. - scrolldata.zoom.displaySize = this._getContentViewerSize(content); - log("onTabScroll() displayWidth: " + scrolldata.zoom.displaySize.width); - - // Save zoom and scroll data. - data.scrolldata = scrolldata; - log("onTabScroll() ran for tab " + aWindow.BrowserApp.getTabForBrowser(aBrowser).id); - let evt = new Event("SSTabScrollCaptured", {"bubbles":true, "cancelable":false}); - aBrowser.dispatchEvent(evt); - this.saveStateDelayed(); - }, - - _getContentViewerSize: function ss_getContentViewerSize(aWindow) { - let displaySize = {}; - let width = {}, height = {}; - aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface( - Ci.nsIDOMWindowUtils).getContentViewerSize(width, height); - - displaySize.width = width.value; - displaySize.height = height.value; - - return displaySize; - }, - - saveStateDelayed: function ss_saveStateDelayed() { - if (!this._saveTimer) { - // Interval until the next disk operation is allowed - let currentDelay = this._lastSaveTime + this._interval - Date.now(); - - // If we have to wait, set a timer, otherwise saveState directly - let delay = Math.max(currentDelay, this._minSaveDelay); - if (delay > 0) { - this._pendingWrite++; - this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this._saveTimer.init(this, delay, Ci.nsITimer.TYPE_ONE_SHOT); - log("saveStateDelayed() timer delay = " + delay + - ", incrementing _pendingWrite to " + this._pendingWrite); - } else { - log("saveStateDelayed() no delay"); - this.saveState(); - } - } else { - log("saveStateDelayed() timer already running, taking no action"); - } - }, - - saveState: function ss_saveState() { - this._pendingWrite++; - log("saveState(), incrementing _pendingWrite to " + this._pendingWrite); - this._saveState(true); - }, - - // Immediately and synchronously writes any pending state to disk. - flushPendingState: function ss_flushPendingState() { - log("flushPendingState(), _pendingWrite = " + this._pendingWrite); - if (this._pendingWrite) { - this._saveState(false); - } - }, - - _saveState: function ss_saveState(aAsync) { - log("_saveState(aAsync = " + aAsync + ")"); - // Kill any queued timer and save immediately - if (this._saveTimer) { - this._saveTimer.cancel(); - this._saveTimer = null; - log("_saveState() killed queued timer"); - } - - // Periodically save a "known good" copy of the session store data. - if (!this._writeInProgress && Date.now() - this._lastBackupTime > this._backupInterval && - this._sessionDataIsGood && this._sessionFile.exists()) { - if (this._sessionFileBackup.exists()) { - this._sessionFileBackup.remove(false); - } - - log("_saveState() backing up session data"); - this._sessionFile.copyTo(null, this._sessionFileBackup.leafName); - this._lastBackupTime = Date.now(); - } - - let data = this._getCurrentState(); - let normalData = { windows: [] }; - let privateData = { windows: [] }; - log("_saveState() current state collected"); - - for (let winIndex = 0; winIndex < data.windows.length; ++winIndex) { - let win = data.windows[winIndex]; - let normalWin = {}; - for (let prop in win) { - normalWin[prop] = data[prop]; - } - normalWin.tabs = []; - - // Save normal closed tabs. Forget about private closed tabs. - normalWin.closedTabs = win.closedTabs.filter(tab => !tab.isPrivate); - - normalData.windows.push(normalWin); - privateData.windows.push({ tabs: [] }); - - // Split the session data into private and non-private data objects. - // Non-private session data will be saved to disk, and private session - // data will be sent to Java for Android to hold it in memory. - for (let i = 0; i < win.tabs.length; ++i) { - let tab = win.tabs[i]; - let savedWin = tab.isPrivate ? privateData.windows[winIndex] : normalData.windows[winIndex]; - savedWin.tabs.push(tab); - if (win.selected == i + 1) { - savedWin.selected = savedWin.tabs.length; - } - } - } - - // Write only non-private data to disk - if (normalData.windows[0] && normalData.windows[0].tabs) { - log("_saveState() writing normal data, " + - normalData.windows[0].tabs.length + " tabs in window[0]"); - } else { - log("_saveState() writing empty normal data"); - } - this._writeFile(this._sessionFile, this._sessionFileTemp, normalData, aAsync); - - // If we have private data, send it to Java; otherwise, send null to - // indicate that there is no private data - Messaging.sendRequest({ - type: "PrivateBrowsing:Data", - session: (privateData.windows.length > 0 && privateData.windows[0].tabs.length > 0) ? JSON.stringify(privateData) : null - }); - - this._lastSaveTime = Date.now(); - }, - - _getCurrentState: function ss_getCurrentState() { - let self = this; - this._forEachBrowserWindow(function(aWindow) { - self._collectWindowData(aWindow); - }); - - let data = { windows: [] }; - for (let index in this._windows) { - data.windows.push(this._windows[index]); - } - - return data; - }, - - _collectTabData: function ss__collectTabData(aWindow, aBrowser, aHistory) { - // If this browser is being restored, skip any session save activity - if (aBrowser.__SS_restore) { - return; - } - - aHistory = aHistory || { entries: [{ url: aBrowser.currentURI.spec, title: aBrowser.contentTitle }], index: 1 }; - - let tabData = {}; - tabData.entries = aHistory.entries; - tabData.index = aHistory.index; - tabData.attributes = { image: aBrowser.mIconURL }; - tabData.desktopMode = aWindow.BrowserApp.getTabForBrowser(aBrowser).desktopMode; - tabData.isPrivate = aBrowser.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing; - - aBrowser.__SS_data = tabData; - }, - - _collectWindowData: function ss__collectWindowData(aWindow) { - // Ignore windows not tracked by SessionStore - if (!aWindow.__SSID || !this._windows[aWindow.__SSID]) { - return; - } - - let winData = this._windows[aWindow.__SSID]; - winData.tabs = []; - - let browsers = aWindow.document.getElementById("browsers"); - let index = browsers.selectedIndex; - winData.selected = parseInt(index) + 1; // 1-based - - let tabs = aWindow.BrowserApp.tabs; - for (let i = 0; i < tabs.length; i++) { - let browser = tabs[i].browser; - if (browser.__SS_data) { - let tabData = browser.__SS_data; - if (browser.__SS_extdata) { - tabData.extData = browser.__SS_extdata; - } - winData.tabs.push(tabData); - } - } - }, - - _forEachBrowserWindow: function ss_forEachBrowserWindow(aFunc) { - let windowsEnum = Services.wm.getEnumerator("navigator:browser"); - while (windowsEnum.hasMoreElements()) { - let window = windowsEnum.getNext(); - if (window.__SSID && !window.closed) { - aFunc.call(this, window); - } - } - }, - - /** - * Writes the session state to a disk file, while doing some telemetry and notification - * bookkeeping. - * @param aFile nsIFile used for saving the session - * @param aFileTemp nsIFile used as a temporary file in writing the data - * @param aData JSON session state - * @param aAsync boolelan used to determine the method of saving the state - */ - _writeFile: function ss_writeFile(aFile, aFileTemp, aData, aAsync) { - let state = JSON.stringify(aData); - - // Convert data string to a utf-8 encoded array buffer - let buffer = new TextEncoder().encode(state); - Services.telemetry.getHistogramById("FX_SESSION_RESTORE_FILE_SIZE_BYTES").add(buffer.byteLength); - - Services.obs.notifyObservers(null, "sessionstore-state-write", ""); - let startWriteMs = Cu.now(); - - log("_writeFile(aAsync = " + aAsync + "), _pendingWrite = " + this._pendingWrite); - this._writeInProgress = true; - let pendingWrite = this._pendingWrite; - this._write(aFile, aFileTemp, buffer, aAsync).then(() => { - let stopWriteMs = Cu.now(); - - // Make sure this._pendingWrite is the same value it was before we - // fired off the async write. If the count is different, another write - // is pending, so we shouldn't reset this._pendingWrite yet. - if (pendingWrite === this._pendingWrite) { - this._pendingWrite = 0; - this._writeInProgress = false; - } - - log("_writeFile() _write() returned, _pendingWrite = " + this._pendingWrite); - - // We don't use a stopwatch here since the calls are async and stopwatches can only manage - // a single timer per histogram. - Services.telemetry.getHistogramById("FX_SESSION_RESTORE_WRITE_FILE_MS").add(Math.round(stopWriteMs - startWriteMs)); - Services.obs.notifyObservers(null, "sessionstore-state-write-complete", ""); - this._sessionDataIsGood = true; - }); - }, - - /** - * Writes the session state to a disk file, using async or sync methods - * @param aFile nsIFile used for saving the session - * @param aFileTemp nsIFile used as a temporary file in writing the data - * @param aBuffer UTF-8 encoded ArrayBuffer of the session state - * @param aAsync boolelan used to determine the method of saving the state - * @return Promise that resolves when the file has been written - */ - _write: function ss_write(aFile, aFileTemp, aBuffer, aAsync) { - // Use async file writer and just return it's promise - if (aAsync) { - log("_write() writing asynchronously"); - return OS.File.writeAtomic(aFile.path, aBuffer, { tmpPath: aFileTemp.path }); - } - - // Convert buffer to an encoded string and sync write to disk - let bytes = String.fromCharCode.apply(null, new Uint16Array(aBuffer)); - let stream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream); - stream.init(aFileTemp, 0x02 | 0x08 | 0x20, 0o666, 0); - stream.write(bytes, bytes.length); - stream.close(); - // Mimic writeAtomic behaviour when tmpPath is set and write - // to a temp file which is then renamed at the end. - aFileTemp.renameTo(null, aFile.leafName); - log("_write() writing synchronously"); - - // Return a resolved promise to make the caller happy - return Promise.resolve(); - }, - - _updateCrashReportURL: function ss_updateCrashReportURL(aWindow) { - let crashReporterBuilt = "nsICrashReporter" in Ci && Services.appinfo instanceof Ci.nsICrashReporter; - if (!crashReporterBuilt) { - return; - } - - if (!aWindow.BrowserApp.selectedBrowser) { - return; - } - - try { - let currentURI = aWindow.BrowserApp.selectedBrowser.currentURI.clone(); - // if the current URI contains a username/password, remove it - try { - currentURI.userPass = ""; - } catch (ex) { } // ignore failures on about: URIs - - Services.appinfo.annotateCrashReport("URL", currentURI.spec); - } catch (ex) { - // don't make noise when crashreporter is built but not enabled - if (ex.result != Cr.NS_ERROR_NOT_INITIALIZED) { - Cu.reportError("SessionStore:" + ex); - } - } - }, - - /** - * Determines whether a given session history entry has been added dynamically. - */ - isDynamic: function(aEntry) { - // aEntry.isDynamicallyAdded() is true for dynamically added - // <iframe> and <frameset>, but also for <html> (the root of the - // document) so we use aEntry.parent to ensure that we're not looking - // at the root of the document - return aEntry.parent && aEntry.isDynamicallyAdded(); - }, - - /** - * Get an object that is a serialized representation of a History entry. - */ - _serializeHistoryEntry: function _serializeHistoryEntry(aEntry) { - let entry = { url: aEntry.URI.spec }; - - if (aEntry.title && aEntry.title != entry.url) { - entry.title = aEntry.title; - } - - if (!(aEntry instanceof Ci.nsISHEntry)) { - return entry; - } - - let cacheKey = aEntry.cacheKey; - if (cacheKey && cacheKey instanceof Ci.nsISupportsPRUint32 && cacheKey.data != 0) { - entry.cacheKey = cacheKey.data; - } - - entry.ID = aEntry.ID; - entry.docshellID = aEntry.docshellID; - - if (aEntry.referrerURI) { - entry.referrer = aEntry.referrerURI.spec; - } - - if (aEntry.originalURI) { - entry.originalURI = aEntry.originalURI.spec; - } - - if (aEntry.loadReplace) { - entry.loadReplace = aEntry.loadReplace; - } - - if (aEntry.contentType) { - entry.contentType = aEntry.contentType; - } - - if (aEntry.scrollRestorationIsManual) { - entry.scrollRestorationIsManual = true; - } else { - let x = {}, y = {}; - aEntry.getScrollPosition(x, y); - if (x.value != 0 || y.value != 0) { - entry.scroll = x.value + "," + y.value; - } - } - - // Collect triggeringPrincipal data for the current history entry. - // Please note that before Bug 1297338 there was no concept of a - // principalToInherit. To remain backward/forward compatible we - // serialize the principalToInherit as triggeringPrincipal_b64. - // Once principalToInherit is well established (within FF55) - // we can update this code, remove triggeringPrincipal_b64 and - // just keep triggeringPrincipal_base64 as well as - // principalToInherit_base64; see Bug 1301666. - if (aEntry.principalToInherit) { - try { - let principalToInherit = Utils.serializePrincipal(aEntry.principalToInherit); - if (principalToInherit) { - entry.triggeringPrincipal_b64 = principalToInherit; - entry.principalToInherit_base64 = principalToInherit; - } - } catch (e) { - dump(e); - } - } - - if (aEntry.triggeringPrincipal) { - try { - let triggeringPrincipal = Utils.serializePrincipal(aEntry.triggeringPrincipal); - if (triggeringPrincipal) { - entry.triggeringPrincipal_base64 = triggeringPrincipal; - } - } catch (e) { - dump(e); - } - } - - entry.docIdentifier = aEntry.BFCacheEntry.ID; - - if (aEntry.stateData != null) { - entry.structuredCloneState = aEntry.stateData.getDataAsBase64(); - entry.structuredCloneVersion = aEntry.stateData.formatVersion; - } - - if (!(aEntry instanceof Ci.nsISHContainer)) { - return entry; - } - - if (aEntry.childCount > 0) { - let children = []; - for (let i = 0; i < aEntry.childCount; i++) { - let child = aEntry.GetChildAt(i); - - if (child && !this.isDynamic(child)) { - // don't try to restore framesets containing wyciwyg URLs (cf. bug 424689 and bug 450595) - if (child.URI.schemeIs("wyciwyg")) { - children = []; - break; - } - children.push(this._serializeHistoryEntry(child)); - } - } - - if (children.length) { - entry.children = children; - } - } - - return entry; - }, - - _deserializeHistoryEntry: function _deserializeHistoryEntry(aEntry, aIdMap, aDocIdentMap) { - let shEntry = Cc["@mozilla.org/browser/session-history-entry;1"].createInstance(Ci.nsISHEntry); - - shEntry.setURI(Services.io.newURI(aEntry.url, null, null)); - shEntry.setTitle(aEntry.title || aEntry.url); - if (aEntry.subframe) { - shEntry.setIsSubFrame(aEntry.subframe || false); - } - shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory; - if (aEntry.contentType) { - shEntry.contentType = aEntry.contentType; - } - if (aEntry.referrer) { - shEntry.referrerURI = Services.io.newURI(aEntry.referrer, null, null); - } - - if (aEntry.originalURI) { - shEntry.originalURI = Services.io.newURI(aEntry.originalURI, null, null); - } - - if (aEntry.loadReplace) { - shEntry.loadReplace = aEntry.loadReplace; - } - - if (aEntry.cacheKey) { - let cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].createInstance(Ci.nsISupportsPRUint32); - cacheKey.data = aEntry.cacheKey; - shEntry.cacheKey = cacheKey; - } - - if (aEntry.ID) { - // get a new unique ID for this frame (since the one from the last - // start might already be in use) - let id = aIdMap[aEntry.ID] || 0; - if (!id) { - for (id = Date.now(); id in aIdMap.used; id++); - aIdMap[aEntry.ID] = id; - aIdMap.used[id] = true; - } - shEntry.ID = id; - } - - if (aEntry.docshellID) { - shEntry.docshellID = aEntry.docshellID; - } - - if (aEntry.structuredCloneState && aEntry.structuredCloneVersion) { - shEntry.stateData = - Cc["@mozilla.org/docshell/structured-clone-container;1"]. - createInstance(Ci.nsIStructuredCloneContainer); - - shEntry.stateData.initFromBase64(aEntry.structuredCloneState, aEntry.structuredCloneVersion); - } - - if (aEntry.scrollRestorationIsManual) { - shEntry.scrollRestorationIsManual = true; - } else if (aEntry.scroll) { - let scrollPos = aEntry.scroll.split(","); - scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0]; - shEntry.setScrollPosition(scrollPos[0], scrollPos[1]); - } - - let childDocIdents = {}; - if (aEntry.docIdentifier) { - // If we have a serialized document identifier, try to find an SHEntry - // which matches that doc identifier and adopt that SHEntry's - // BFCacheEntry. If we don't find a match, insert shEntry as the match - // for the document identifier. - let matchingEntry = aDocIdentMap[aEntry.docIdentifier]; - if (!matchingEntry) { - matchingEntry = {shEntry: shEntry, childDocIdents: childDocIdents}; - aDocIdentMap[aEntry.docIdentifier] = matchingEntry; - } else { - shEntry.adoptBFCacheEntry(matchingEntry.shEntry); - childDocIdents = matchingEntry.childDocIdents; - } - } - - // The field aEntry.owner_b64 got renamed to aEntry.triggeringPricipal_b64 in - // Bug 1286472. To remain backward compatible we still have to support that - // field for a few cycles before we can remove it within Bug 1289785. - if (aEntry.owner_b64) { - aEntry.triggeringPricipal_b64 = aEntry.owner_b64; - delete aEntry.owner_b64; - } - - // Before introducing the concept of principalToInherit we only had - // a triggeringPrincipal within every entry which basically is the - // equivalent of the new principalToInherit. To avoid compatibility - // issues, we first check if the entry has entries for - // triggeringPrincipal_base64 and principalToInherit_base64. If not - // we fall back to using the principalToInherit (which is stored - // as triggeringPrincipal_b64) as the triggeringPrincipal and - // the principalToInherit. - // FF55 will remove the triggeringPrincipal_b64, see Bug 1301666. - if (aEntry.triggeringPrincipal_base64 || aEntry.principalToInherit_base64) { - if (aEntry.triggeringPrincipal_base64) { - shEntry.triggeringPrincipal = - Utils.deserializePrincipal(aEntry.triggeringPrincipal_base64); - } - if (aEntry.principalToInherit_base64) { - shEntry.principalToInherit = - Utils.deserializePrincipal(aEntry.principalToInherit_base64); - } - } else if (aEntry.triggeringPrincipal_b64) { - shEntry.triggeringPrincipal = Utils.deserializePrincipal(aEntry.triggeringPrincipal_b64); - shEntry.principalToInherit = shEntry.triggeringPrincipal; - } - - if (aEntry.children && shEntry instanceof Ci.nsISHContainer) { - for (let i = 0; i < aEntry.children.length; i++) { - if (!aEntry.children[i].url) { - continue; - } - - // We're getting sessionrestore.js files with a cycle in the - // doc-identifier graph, likely due to bug 698656. (That is, we have - // an entry where doc identifier A is an ancestor of doc identifier B, - // and another entry where doc identifier B is an ancestor of A.) - // - // If we were to respect these doc identifiers, we'd create a cycle in - // the SHEntries themselves, which causes the docshell to loop forever - // when it looks for the root SHEntry. - // - // So as a hack to fix this, we restrict the scope of a doc identifier - // to be a node's siblings and cousins, and pass childDocIdents, not - // aDocIdents, to _deserializeHistoryEntry. That is, we say that two - // SHEntries with the same doc identifier have the same document iff - // they have the same parent or their parents have the same document. - - shEntry.AddChild(this._deserializeHistoryEntry(aEntry.children[i], aIdMap, childDocIdents), i); - } - } - - return shEntry; - }, - - // This function iterates through a list of urls opening a new tab for each. - _openTabs: function ss_openTabs(aData) { - let window = Services.wm.getMostRecentWindow("navigator:browser"); - for (let i = 0; i < aData.urls.length; i++) { - let url = aData.urls[i]; - let params = { - selected: (i == aData.urls.length - 1), - isPrivate: false, - desktopMode: false, - }; - - let tab = window.BrowserApp.addTab(url, params); - } - }, - - // This function iterates through a list of tab data restoring session for each of them. - _restoreTabs: function ss_restoreTabs(aData) { - let window = Services.wm.getMostRecentWindow("navigator:browser"); - for (let i = 0; i < aData.tabs.length; i++) { - let tabData = JSON.parse(aData.tabs[i]); - let isSelectedTab = (i == aData.tabs.length - 1); - let params = { - selected: isSelectedTab, - isPrivate: tabData.isPrivate, - desktopMode: tabData.desktopMode, - cancelEditMode: isSelectedTab - }; - - let tab = window.BrowserApp.addTab(tabData.entries[tabData.index - 1].url, params); - tab.browser.__SS_data = tabData; - tab.browser.__SS_extdata = tabData.extData; - this._restoreTab(tabData, tab.browser); - } - }, - - /** - * Don't save sensitive data if the user doesn't want to - * (distinguishes between encrypted and non-encrypted sites) - */ - checkPrivacyLevel: function ss_checkPrivacyLevel(aURL) { - let isHTTPS = aURL.startsWith("https:"); - let pref = "browser.sessionstore.privacy_level"; - return Services.prefs.getIntPref(pref) < (isHTTPS ? PRIVACY_ENCRYPTED : PRIVACY_FULL); - }, - - /** - * Starts the restoration process for a browser. History is restored at this - * point, but text data must be delayed until the content loads. - */ - _restoreTab: function ss_restoreTab(aTabData, aBrowser) { - // aTabData shouldn't be empty here, but if it is, - // _restoreHistory() will crash otherwise. - if (!aTabData || aTabData.entries.length == 0) { - Cu.reportError("SessionStore.js: Error trying to restore tab with empty tabdata"); - return; - } - this._restoreHistory(aTabData, aBrowser.sessionHistory); - - // Various bits of state can only be restored if page loading has progressed far enough: - // The MobileViewportManager needs to be told as early as possible about - // our desired zoom level so it can take it into account during the - // initial document resolution calculation. - aBrowser.__SS_restoreDataOnLocationChange = true; - // Restoring saved form data requires the input fields to be available, - // so we have to wait for the content to load. - aBrowser.__SS_restoreDataOnLoad = true; - // Restoring the scroll position depends on the document resolution having been set, - // which is only guaranteed to have happened *after* we receive the load event. - aBrowser.__SS_restoreDataOnPageshow = true; - }, - - /** - * Takes serialized history data and create news entries into the given - * nsISessionHistory object. - */ - _restoreHistory: function ss_restoreHistory(aTabData, aHistory) { - if (aHistory.count > 0) { - aHistory.PurgeHistory(aHistory.count); - } - aHistory.QueryInterface(Ci.nsISHistoryInternal); - - // Helper hashes for ensuring unique frame IDs and unique document - // identifiers. - let idMap = { used: {} }; - let docIdentMap = {}; - - for (let i = 0; i < aTabData.entries.length; i++) { - if (!aTabData.entries[i].url) { - continue; - } - aHistory.addEntry(this._deserializeHistoryEntry(aTabData.entries[i], idMap, docIdentMap), true); - } - - // We need to force set the active history item and cause it to reload since - // we stop the load above - let activeIndex = (aTabData.index || aTabData.entries.length) - 1; - aHistory.getEntryAtIndex(activeIndex, true); - - try { - aHistory.QueryInterface(Ci.nsISHistory).reloadCurrentEntry(); - } catch (e) { - // This will throw if the current entry is an error page. - } - }, - - /** - * Takes serialized form text data and restores it into the given browser. - */ - _restoreTextData: function ss_restoreTextData(aFormData, aBrowser) { - if (aFormData) { - log("_restoreTextData()"); - FormData.restoreTree(aBrowser.contentWindow, aFormData); - } - }, - - /** - * Restores the zoom level of the window. This needs to be called before - * first paint/load (whichever comes first) to take any effect. - */ - _restoreZoom: function ss_restoreZoom(aScrollData, aBrowser) { - if (aScrollData && aScrollData.zoom && aScrollData.zoom.displaySize) { - log("_restoreZoom(), resolution: " + aScrollData.zoom.resolution + - ", old displayWidth: " + aScrollData.zoom.displaySize.width); - - let utils = aBrowser.contentWindow.QueryInterface( - Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - // Restore zoom level. - utils.setRestoreResolution(aScrollData.zoom.resolution, - aScrollData.zoom.displaySize.width, - aScrollData.zoom.displaySize.height); - } - }, - - /** - * Takes serialized scroll positions and restores them into the given browser. - */ - _restoreScrollPosition: function ss_restoreScrollPosition(aScrollData, aBrowser) { - if (aScrollData) { - log("_restoreScrollPosition()"); - ScrollPosition.restoreTree(aBrowser.contentWindow, aScrollData); - } - }, - - getBrowserState: function ss_getBrowserState() { - return this._getCurrentState(); - }, - - _restoreWindow: function ss_restoreWindow(aData) { - let state; - try { - state = JSON.parse(aData); - } catch (e) { - throw "Invalid session JSON: " + aData; - } - - // To do a restore, we must have at least one window with one tab - if (!state || state.windows.length == 0 || !state.windows[0].tabs || state.windows[0].tabs.length == 0) { - throw "Invalid session JSON: " + aData; - } - - let window = Services.wm.getMostRecentWindow("navigator:browser"); - - let tabs = state.windows[0].tabs; - let selected = state.windows[0].selected; - log("_restoreWindow() selected tab in aData is " + selected + " of " + tabs.length) - if (selected == null || selected > tabs.length) { // Clamp the selected index if it's bogus - log("_restoreWindow() resetting selected tab"); - selected = 1; - } - log("restoreWindow() window.BrowserApp.selectedTab is " + window.BrowserApp.selectedTab.id); - - for (let i = 0; i < tabs.length; i++) { - let tabData = tabs[i]; - let entry = tabData.entries[tabData.index - 1]; - - // Use stubbed tab if we've already created it; otherwise, make a new tab - let tab; - if (tabData.tabId == null) { - let params = { - selected: (selected == i+1), - delayLoad: true, - title: entry.title, - desktopMode: (tabData.desktopMode == true), - isPrivate: (tabData.isPrivate == true) - }; - tab = window.BrowserApp.addTab(entry.url, params); - } else { - tab = window.BrowserApp.getTabForId(tabData.tabId); - delete tabData.tabId; - - // Don't restore tab if user has closed it - if (tab == null) { - continue; - } - } - - tab.browser.__SS_data = tabData; - tab.browser.__SS_extdata = tabData.extData; - - if (window.BrowserApp.selectedTab == tab) { - this._restoreTab(tabData, tab.browser); - - // We can now lift the general ban on tab data capturing, - // but we still need to protect the foreground tab until we're - // sure it's actually reloading after history restoring has finished. - tab.browser.__SS_restoreReloadPending = true; - this._startupRestoreFinished = true; - log("startupRestoreFinished = true"); - - delete tab.browser.__SS_restore; - tab.browser.removeAttribute("pending"); - } else { - // Mark the browser for delay loading - tab.browser.__SS_restore = true; - tab.browser.setAttribute("pending", "true"); - } - } - - // Restore the closed tabs array on the current window. - if (state.windows[0].closedTabs) { - this._windows[window.__SSID].closedTabs = state.windows[0].closedTabs; - log("_restoreWindow() loaded " + state.windows[0].closedTabs.length + " closed tabs"); - } - }, - - getClosedTabCount: function ss_getClosedTabCount(aWindow) { - if (!aWindow || !aWindow.__SSID || !this._windows[aWindow.__SSID]) { - return 0; // not a browser window, or not otherwise tracked by SS. - } - - return this._windows[aWindow.__SSID].closedTabs.length; - }, - - getClosedTabs: function ss_getClosedTabs(aWindow) { - if (!aWindow.__SSID) { - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - } - - return this._windows[aWindow.__SSID].closedTabs; - }, - - undoCloseTab: function ss_undoCloseTab(aWindow, aCloseTabData) { - if (!aWindow.__SSID) { - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - } - - let closedTabs = this._windows[aWindow.__SSID].closedTabs; - if (!closedTabs) { - return null; - } - - // If the tab data is in the closedTabs array, remove it. - closedTabs.find(function (tabData, i) { - if (tabData == aCloseTabData) { - closedTabs.splice(i, 1); - return true; - } - }); - - // create a new tab and bring to front - let params = { - selected: true, - isPrivate: aCloseTabData.isPrivate, - desktopMode: aCloseTabData.desktopMode, - tabIndex: this._lastClosedTabIndex - }; - let tab = aWindow.BrowserApp.addTab(aCloseTabData.entries[aCloseTabData.index - 1].url, params); - tab.browser.__SS_data = aCloseTabData; - tab.browser.__SS_extdata = aCloseTabData.extData; - this._restoreTab(aCloseTabData, tab.browser); - - this._lastClosedTabIndex = -1; - - if (this._notifyClosedTabs) { - this._sendClosedTabsToJava(aWindow); - } - - return tab.browser; - }, - - forgetClosedTab: function ss_forgetClosedTab(aWindow, aIndex) { - if (!aWindow.__SSID) { - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - } - - let closedTabs = this._windows[aWindow.__SSID].closedTabs; - - // default to the most-recently closed tab - aIndex = aIndex || 0; - if (!(aIndex in closedTabs)) { - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - } - - // remove closed tab from the array - closedTabs.splice(aIndex, 1); - - // Forget the last closed tab index if we're forgetting the last closed tab. - if (aIndex == 0) { - this._lastClosedTabIndex = -1; - } - if (this._notifyClosedTabs) { - this._sendClosedTabsToJava(aWindow); - } - }, - - _sendClosedTabsToJava: function ss_sendClosedTabsToJava(aWindow) { - if (!aWindow.__SSID) { - throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); - } - - let closedTabs = this._windows[aWindow.__SSID].closedTabs; - let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(aWindow.BrowserApp.selectedBrowser); - - let tabs = closedTabs - .filter(tab => tab.isPrivate == isPrivate) - .map(function (tab) { - // Get the url and title for the last entry in the session history. - let lastEntry = tab.entries[tab.entries.length - 1]; - return { - url: lastEntry.url, - title: lastEntry.title || "", - data: tab - }; - }); - - log("sending " + tabs.length + " closed tabs to Java"); - Messaging.sendRequest({ - type: "ClosedTabs:Data", - tabs: tabs - }); - }, - - getTabValue: function ss_getTabValue(aTab, aKey) { - let browser = aTab.browser; - let data = browser.__SS_extdata || {}; - return data[aKey] || ""; - }, - - setTabValue: function ss_setTabValue(aTab, aKey, aStringValue) { - let browser = aTab.browser; - if (!browser.__SS_extdata) { - browser.__SS_extdata = {}; - } - browser.__SS_extdata[aKey] = aStringValue; - this.saveStateDelayed(); - }, - - deleteTabValue: function ss_deleteTabValue(aTab, aKey) { - let browser = aTab.browser; - if (browser.__SS_extdata && aKey in browser.__SS_extdata) { - delete browser.__SS_extdata[aKey]; - this.saveStateDelayed(); - } - }, - - restoreLastSession: Task.async(function* (aSessionString) { - let notifyMessage = ""; - - try { - this._restoreWindow(aSessionString); - } catch (e) { - Cu.reportError("SessionStore: " + e); - notifyMessage = "fail"; - } - - Services.obs.notifyObservers(null, "sessionstore-windows-restored", notifyMessage); - }), - - removeWindow: function ss_removeWindow(aWindow) { - if (!aWindow || !aWindow.__SSID || !this._windows[aWindow.__SSID]) { - return; - } - - delete this._windows[aWindow.__SSID]; - delete aWindow.__SSID; - - if (this._loadState == STATE_RUNNING) { - // Save the purged state immediately - this.saveState(); - } else if (this._loadState <= STATE_QUITTING) { - this.saveStateDelayed(); - } - } - -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStore]); diff --git a/mobile/android/components/SiteSpecificUserAgent.js b/mobile/android/components/SiteSpecificUserAgent.js deleted file mode 100644 index f95d7ab16..000000000 --- a/mobile/android/components/SiteSpecificUserAgent.js +++ /dev/null @@ -1,33 +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 Cu = Components.utils; -const Cc = Components.classes; -const Ci = Components.interfaces; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/UserAgentOverrides.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -const DEFAULT_UA = Cc["@mozilla.org/network/protocol;1?name=http"] - .getService(Ci.nsIHttpProtocolHandler) - .userAgent; - -function SiteSpecificUserAgent() {} - -SiteSpecificUserAgent.prototype = { - getUserAgentForURIAndWindow: function ssua_getUserAgentForURIAndWindow(aURI, aWindow) { - let UA; - let win = Services.wm.getMostRecentWindow("navigator:browser"); - if (win && win.DesktopUserAgent) { - UA = win.DesktopUserAgent.getUserAgentForWindow(aWindow); - } - return UA || UserAgentOverrides.getOverrideForURI(aURI) || DEFAULT_UA; - }, - - classID: Components.ID("{d5234c9d-0ee2-4b3c-9da3-18be9e5cf7e6}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsISiteSpecificUserAgent]) -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SiteSpecificUserAgent]); diff --git a/mobile/android/components/Snippets.js b/mobile/android/components/Snippets.js deleted file mode 100644 index 92639236f..000000000 --- a/mobile/android/components/Snippets.js +++ /dev/null @@ -1,446 +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 { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -Cu.import("resource://gre/modules/Accounts.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Home", "resource://gre/modules/Home.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry", "resource://gre/modules/UITelemetry.jsm"); - - -XPCOMUtils.defineLazyGetter(this, "gEncoder", function() { return new gChromeWin.TextEncoder(); }); -XPCOMUtils.defineLazyGetter(this, "gDecoder", function() { return new gChromeWin.TextDecoder(); }); - -// URL to fetch snippets, in the urlFormatter service format. -const SNIPPETS_UPDATE_URL_PREF = "browser.snippets.updateUrl"; - -// URL to send stats data to metrics. -const SNIPPETS_STATS_URL_PREF = "browser.snippets.statsUrl"; - -// URL to fetch country code, a value that's cached and refreshed once per month. -const SNIPPETS_GEO_URL_PREF = "browser.snippets.geoUrl"; - -// Timestamp when we last updated the user's country code. -const SNIPPETS_GEO_LAST_UPDATE_PREF = "browser.snippets.geoLastUpdate"; - -// Pref where we'll cache the user's country. -const SNIPPETS_COUNTRY_CODE_PREF = "browser.snippets.countryCode"; - -// Pref where we store an array IDs of snippets that should not be shown again -const SNIPPETS_REMOVED_IDS_PREF = "browser.snippets.removedIds"; - -// How frequently we update the user's country code from the server (30 days). -const SNIPPETS_GEO_UPDATE_INTERVAL_MS = 86400000*30; - -// Should be bumped up if the snippets content format changes. -const SNIPPETS_VERSION = 1; - -XPCOMUtils.defineLazyGetter(this, "gSnippetsURL", function() { - let updateURL = Services.prefs.getCharPref(SNIPPETS_UPDATE_URL_PREF).replace("%SNIPPETS_VERSION%", SNIPPETS_VERSION); - return Services.urlFormatter.formatURL(updateURL); -}); - -// Where we cache snippets data -XPCOMUtils.defineLazyGetter(this, "gSnippetsPath", function() { - return OS.Path.join(OS.Constants.Path.profileDir, "snippets.json"); -}); - -XPCOMUtils.defineLazyGetter(this, "gStatsURL", function() { - return Services.prefs.getCharPref(SNIPPETS_STATS_URL_PREF); -}); - -// Where we store stats about which snippets have been shown -XPCOMUtils.defineLazyGetter(this, "gStatsPath", function() { - return OS.Path.join(OS.Constants.Path.profileDir, "snippets-stats.txt"); -}); - -XPCOMUtils.defineLazyGetter(this, "gGeoURL", function() { - return Services.prefs.getCharPref(SNIPPETS_GEO_URL_PREF); -}); - -XPCOMUtils.defineLazyGetter(this, "gCountryCode", function() { - try { - return Services.prefs.getCharPref(SNIPPETS_COUNTRY_CODE_PREF); - } catch (e) { - // Return an empty string if the country code pref isn't set yet. - return ""; - } -}); - -XPCOMUtils.defineLazyGetter(this, "gChromeWin", function() { - return Services.wm.getMostRecentWindow("navigator:browser"); -}); - -/** - * Updates snippet data and country code (if necessary). - */ -function update() { - // Check to see if we should update the user's country code from the geo server. - let lastUpdate = 0; - try { - lastUpdate = parseFloat(Services.prefs.getCharPref(SNIPPETS_GEO_LAST_UPDATE_PREF)); - } catch (e) {} - - if (Date.now() - lastUpdate > SNIPPETS_GEO_UPDATE_INTERVAL_MS) { - // We should update the snippets after updating the country code, - // so that we can filter snippets to add to the banner. - updateCountryCode(updateSnippets); - } else { - updateSnippets(); - } -} - -/** - * Fetches the user's country code from the geo server and stores the value in a pref. - * - * @param callback function called once country code is updated - */ -function updateCountryCode(callback) { - _httpGetRequest(gGeoURL, function(responseText) { - // Store the country code in a pref. - let data = JSON.parse(responseText); - Services.prefs.setCharPref(SNIPPETS_COUNTRY_CODE_PREF, data.country_code); - - // Set last update time. - Services.prefs.setCharPref(SNIPPETS_GEO_LAST_UPDATE_PREF, Date.now()); - - callback(); - }); -} - -/** - * Loads snippets from snippets server, caches the response, and - * updates the home banner with the new set of snippets. - */ -function updateSnippets() { - _httpGetRequest(gSnippetsURL, function(responseText) { - try { - let messages = JSON.parse(responseText); - updateBanner(messages); - - // Only cache the response if it is valid JSON. - cacheSnippets(responseText); - } catch (e) { - Cu.reportError("Error parsing snippets responseText: " + e); - } - }); -} - -/** - * Caches snippets server response text to `snippets.json` in profile directory. - * - * @param response responseText returned from snippets server - */ -function cacheSnippets(response) { - let data = gEncoder.encode(response); - let promise = OS.File.writeAtomic(gSnippetsPath, data, { tmpPath: gSnippetsPath + ".tmp" }); - promise.then(null, e => Cu.reportError("Error caching snippets: " + e)); -} - -/** - * Loads snippets from cached `snippets.json`. - */ -function loadSnippetsFromCache() { - let promise = OS.File.read(gSnippetsPath); - promise.then(array => { - let messages = JSON.parse(gDecoder.decode(array)); - updateBanner(messages); - }, e => { - if (e instanceof OS.File.Error && e.becauseNoSuchFile) { - Services.console.logStringMessage("Couldn't show snippets because cache does not exist yet."); - } else { - Cu.reportError("Error loading snippets from cache: " + e); - } - }); -} - -// Array of the message ids added to the home banner, used to remove -// older set of snippets when new ones are available. -var gMessageIds = []; - -/** - * Updates set of snippets in the home banner message rotation. - * - * @param messages JSON array of message data JSON objects. - * Each message object should have the following properties: - * - id (?): Unique identifier for this snippets message - * - text (string): Text to show as banner message - * - url (string): URL to open when banner is clicked - * - icon (data URI): Icon to appear in banner - * - countries (list of strings): Country codes for where this message should be shown (e.g. ["US", "GR"]) - */ -function updateBanner(messages) { - // Remove the current messages, if there are any. - gMessageIds.forEach(function(id) { - Home.banner.remove(id); - }) - gMessageIds = []; - - try { - let removedSnippetIds = JSON.parse(Services.prefs.getCharPref(SNIPPETS_REMOVED_IDS_PREF)); - messages = messages.filter(function(message) { - // Only include the snippet if it has not been previously removed. - return removedSnippetIds.indexOf(message.id) === -1; - }); - } catch (e) { - // If the pref doesn't exist, there aren't any snippets to filter out. - } - - messages.forEach(function(message) { - // Don't add this message to the banner if it's not supposed to be shown in this country. - if ("countries" in message && message.countries.indexOf(gCountryCode) === -1) { - return; - } - - let id = Home.banner.add({ - text: message.text, - icon: message.icon, - weight: message.weight, - onclick: function() { - gChromeWin.BrowserApp.loadURI(message.url); - removeSnippet(id, message.id); - UITelemetry.addEvent("action.1", "banner", null, message.id); - }, - ondismiss: function() { - removeSnippet(id, message.id); - UITelemetry.addEvent("cancel.1", "banner", null, message.id); - }, - onshown: function() { - // 10% of the time, record the snippet id and a timestamp - if (Math.random() < .1) { - writeStat(message.id, new Date().toISOString()); - } - } - }); - // Keep track of the message we added so that we can remove it later. - gMessageIds.push(id); - }); -} - -/** - * Removes a snippet message from the home banner rotation, and stores its - * snippet id in a pref so we'll never show it again. - * - * @param messageId unique id for home banner message, returned from Home.banner API - * @param snippetId unique id for snippet, sent from snippets server - */ -function removeSnippet(messageId, snippetId) { - // Remove the message from the home banner rotation. - Home.banner.remove(messageId); - - // Remove the message from the stored message ids. - gMessageIds.splice(gMessageIds.indexOf(messageId), 1); - - let removedSnippetIds; - try { - removedSnippetIds = JSON.parse(Services.prefs.getCharPref(SNIPPETS_REMOVED_IDS_PREF)); - } catch (e) { - removedSnippetIds = []; - } - - removedSnippetIds.push(snippetId); - Services.prefs.setCharPref(SNIPPETS_REMOVED_IDS_PREF, JSON.stringify(removedSnippetIds)); -} - -/** - * Appends snippet id and timestamp to the end of `snippets-stats.txt`. - * - * @param snippetId unique id for snippet, sent from snippets server - * @param timestamp in ISO8601 - */ -function writeStat(snippetId, timestamp) { - let data = gEncoder.encode(snippetId + "," + timestamp + ";"); - - Task.spawn(function() { - try { - let file = yield OS.File.open(gStatsPath, { append: true, write: true }); - try { - yield file.write(data); - } finally { - yield file.close(); - } - } catch (ex if ex instanceof OS.File.Error && ex.becauseNoSuchFile) { - // If the file doesn't exist yet, create it. - yield OS.File.writeAtomic(gStatsPath, data, { tmpPath: gStatsPath + ".tmp" }); - } - }).then(null, e => Cu.reportError("Error writing snippets stats: " + e)); -} - -/** - * Reads snippets stats data from `snippets-stats.txt` and sends the data to metrics. - */ -function sendStats() { - let promise = OS.File.read(gStatsPath); - promise.then(array => sendStatsRequest(gDecoder.decode(array)), e => { - if (e instanceof OS.File.Error && e.becauseNoSuchFile) { - // If the file doesn't exist, there aren't any stats to send. - } else { - Cu.reportError("Error eading snippets stats: " + e); - } - }); -} - -/** - * Sends stats to metrics about which snippets have been shown. - * Appends snippet ids and timestamps as parameters to a GET request. - * e.g. https://snippets-stats.mozilla.org/mobile?s1=3825&t1=2013-11-17T18:27Z&s2=6326&t2=2013-11-18T18:27Z - * - * @param data contents of stats data file - */ -function sendStatsRequest(data) { - let params = []; - let stats = data.split(";"); - - // The last item in the array will be an empty string, so stop before then. - for (let i = 0; i < stats.length - 1; i++) { - let stat = stats[i].split(","); - params.push("s" + i + "=" + encodeURIComponent(stat[0])); - params.push("t" + i + "=" + encodeURIComponent(stat[1])); - } - - let url = gStatsURL + "?" + params.join("&"); - - // Remove the file after succesfully sending the data. - _httpGetRequest(url, removeStats); -} - -/** - * Removes text file where we store snippets stats. - */ -function removeStats() { - let promise = OS.File.remove(gStatsPath); - promise.then(null, e => Cu.reportError("Error removing snippets stats: " + e)); -} - -/** - * Helper function to make HTTP GET requests. - * - * @param url where we send the request - * @param callback function that is called with the xhr responseText - */ -function _httpGetRequest(url, callback) { - let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest); - try { - xhr.open("GET", url, true); - } catch (e) { - Cu.reportError("Error opening request to " + url + ": " + e); - return; - } - xhr.onerror = function onerror(e) { - Cu.reportError("Error making request to " + url + ": " + e.error); - } - xhr.onload = function onload(event) { - if (xhr.status !== 200) { - Cu.reportError("Request to " + url + " returned status " + xhr.status); - return; - } - if (callback) { - callback(xhr.responseText); - } - } - xhr.send(null); -} - -function loadSyncPromoBanner() { - Accounts.anySyncAccountsExist().then( - (exist) => { - // Don't show the banner if sync accounts exist. - if (exist) { - return; - } - - let stringBundle = Services.strings.createBundle("chrome://browser/locale/sync.properties"); - let text = stringBundle.GetStringFromName("promoBanner.message.text"); - let link = stringBundle.GetStringFromName("promoBanner.message.link"); - - let id = Home.banner.add({ - text: text + "<a href=\"#\">" + link + "</a>", - icon: "drawable://sync_promo", - onclick: function() { - // Remove the message, so that it won't show again for the rest of the app lifetime. - Home.banner.remove(id); - Accounts.launchSetup(); - - UITelemetry.addEvent("action.1", "banner", null, "syncpromo"); - }, - ondismiss: function() { - // Remove the sync promo message from the banner and never try to show it again. - Home.banner.remove(id); - Services.prefs.setBoolPref("browser.snippets.syncPromo.enabled", false); - - UITelemetry.addEvent("cancel.1", "banner", null, "syncpromo"); - } - }); - }, - (err) => { - Cu.reportError("Error checking whether sync account exists: " + err); - } - ); -} - -function loadHomePanelsBanner() { - let stringBundle = Services.strings.createBundle("chrome://browser/locale/aboutHome.properties"); - let text = stringBundle.GetStringFromName("banner.firstrunHomepage.text"); - - let id = Home.banner.add({ - text: text, - icon: "drawable://homepage_banner_firstrun", - onclick: function() { - // Remove the message, so that it won't show again for the rest of the app lifetime. - Home.banner.remove(id); - // User has interacted with this snippet so don't show it again. - Services.prefs.setBoolPref("browser.snippets.firstrunHomepage.enabled", false); - - UITelemetry.addEvent("action.1", "banner", null, "firstrun-homepage"); - }, - ondismiss: function() { - Home.banner.remove(id); - Services.prefs.setBoolPref("browser.snippets.firstrunHomepage.enabled", false); - - UITelemetry.addEvent("cancel.1", "banner", null, "firstrun-homepage"); - } - }); -} - -function Snippets() {} - -Snippets.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsITimerCallback]), - classID: Components.ID("{a78d7e59-b558-4321-a3d6-dffe2f1e76dd}"), - - observe: function(subject, topic, data) { - switch(topic) { - case "browser-delayed-startup-finished": - // Add snippets to be cycled through. - if (Services.prefs.getBoolPref("browser.snippets.firstrunHomepage.enabled")) { - loadHomePanelsBanner(); - } - - if (Services.prefs.getBoolPref("browser.snippets.syncPromo.enabled")) { - loadSyncPromoBanner(); - } - - if (Services.prefs.getBoolPref("browser.snippets.enabled")) { - loadSnippetsFromCache(); - } - break; - } - }, - - // By default, this timer fires once every 24 hours. See the "browser.snippets.updateInterval" pref. - notify: function(timer) { - if (!Services.prefs.getBoolPref("browser.snippets.enabled")) { - return; - } - update(); - sendStats(); - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Snippets]); diff --git a/mobile/android/components/TabSource.js b/mobile/android/components/TabSource.js deleted file mode 100644 index c35a54438..000000000 --- a/mobile/android/components/TabSource.js +++ /dev/null @@ -1,91 +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" - -const { classes: Cc, interfaces: Ci, manager: Cm, utils: Cu, results: Cr } = Components; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Prompt", - "resource://gre/modules/Prompt.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", - "resource://gre/modules/Messaging.jsm"); - -function TabSource() { -} - -TabSource.prototype = { - classID: Components.ID("{5850c76e-b916-4218-b99a-31f004e0a7e7}"), - classDescription: "Fennec Tab Source", - contractID: "@mozilla.org/tab-source-service;1", - QueryInterface: XPCOMUtils.generateQI([Ci.nsITabSource]), - - getTabToStream: function() { - let app = Services.wm.getMostRecentWindow("navigator:browser").BrowserApp; - let tabs = app.tabs; - if (tabs == null || tabs.length == 0) { - Services.console.logStringMessage("ERROR: No tabs"); - return null; - } - - let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); - let title = bundle.GetStringFromName("tabshare.title") - - let prompt = new Prompt({ - title: title, - window: null - }).setSingleChoiceItems(tabs.map(function(tab) { - let label; - if (tab.browser.contentTitle) - label = tab.browser.contentTitle; - else if (tab.browser.contentURI) - label = tab.browser.contentURI.spec; - else - label = tab.originalURI.spec; - return { label: label, - icon: "thumbnail:" + tab.id } - })); - - let result = null; - prompt.show(function(data) { - result = data.button; - }); - - // Spin this thread while we wait for a result. - let thread = Services.tm.currentThread; - while (result == null) { - thread.processNextEvent(true); - } - - if (result == -1) { - return null; - } - return tabs[result].browser.contentWindow; - }, - - notifyStreamStart: function(window) { - let app = Services.wm.getMostRecentWindow("navigator:browser").BrowserApp; - let tabs = app.tabs; - for (var i in tabs) { - if (tabs[i].browser.contentWindow == window) { - Messaging.sendRequest({ type: "Tab:StreamStart", tabID: tabs[i].id }); - } - } - }, - - notifyStreamStop: function(window) { - let app = Services.wm.getMostRecentWindow("navigator:browser").BrowserApp; - let tabs = app.tabs; - for (let i in tabs) { - if (tabs[i].browser.contentWindow == window) { - Messaging.sendRequest({ type: "Tab:StreamStop", tabID: tabs[i].id }); - } - } - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TabSource]); diff --git a/mobile/android/components/XPIDialogService.js b/mobile/android/components/XPIDialogService.js deleted file mode 100644 index 2a33d4ddf..000000000 --- a/mobile/android/components/XPIDialogService.js +++ /dev/null @@ -1,49 +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 Ci = Components.interfaces; -const Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm"); - -// ----------------------------------------------------------------------- -// Web Install Prompt service -// ----------------------------------------------------------------------- - -function WebInstallPrompt() { } - -WebInstallPrompt.prototype = { - classID: Components.ID("{c1242012-27d8-477e-a0f1-0b098ffc329b}"), - QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallPrompt]), - - confirm: function(aBrowser, aURL, aInstalls) { - let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); - - let prompt = Services.prompt; - let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_IS_STRING + prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_CANCEL; - let title = bundle.GetStringFromName("addonsConfirmInstall.title"); - let button = bundle.GetStringFromName("addonsConfirmInstall.install"); - - aInstalls.forEach(function(install) { - let message; - if (install.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) { - title = bundle.GetStringFromName("addonsConfirmInstallUnsigned.title") - message = bundle.GetStringFromName("addonsConfirmInstallUnsigned.message") + "\n\n" + install.name; - } else { - message = install.name; - } - - let result = (prompt.confirmEx(aBrowser.contentWindow, title, message, flags, button, null, null, null, {value: false}) == 0); - if (result) - install.install(); - else - install.cancel(); - }); - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebInstallPrompt]); diff --git a/mobile/android/components/build/moz.build b/mobile/android/components/build/moz.build deleted file mode 100644 index 7a5c439e7..000000000 --- a/mobile/android/components/build/moz.build +++ /dev/null @@ -1,31 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -XPIDL_SOURCES += [ - 'nsIShellService.idl', -] - -XPIDL_MODULE = 'browsercomps' - -EXPORTS += [ - 'nsBrowserComponents.h', -] - -SOURCES += [ - 'nsBrowserModule.cpp', - 'nsShellService.cpp', -] - -if CONFIG['MOZ_ANDROID_HISTORY']: - SOURCES += [ - 'nsAndroidHistory.cpp', - ] - LOCAL_INCLUDES += [ - '/docshell/base', - '/dom/base', - ] - -FINAL_LIBRARY = 'xul' diff --git a/mobile/android/components/build/nsAndroidHistory.cpp b/mobile/android/components/build/nsAndroidHistory.cpp deleted file mode 100644 index 2610781c0..000000000 --- a/mobile/android/components/build/nsAndroidHistory.cpp +++ /dev/null @@ -1,395 +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/. */ - -#include "nsThreadUtils.h" -#include "nsAndroidHistory.h" -#include "nsComponentManagerUtils.h" -#include "nsIURI.h" -#include "nsIObserverService.h" -#include "GeneratedJNIWrappers.h" -#include "Link.h" - -#include "mozilla/Services.h" -#include "mozilla/Preferences.h" - -#define NS_LINK_VISITED_EVENT_TOPIC "link-visited" - -// We copy Places here. -// Note that we don't yet observe this pref at runtime. -#define PREF_HISTORY_ENABLED "places.history.enabled" - -// Time we wait to see if a pending visit is really a redirect -#define PENDING_REDIRECT_TIMEOUT 3000 - -using namespace mozilla; -using mozilla::dom::Link; - -NS_IMPL_ISUPPORTS(nsAndroidHistory, IHistory, nsIRunnable, nsITimerCallback) - -nsAndroidHistory* nsAndroidHistory::sHistory = nullptr; - -/*static*/ -nsAndroidHistory* -nsAndroidHistory::GetSingleton() -{ - if (!sHistory) { - sHistory = new nsAndroidHistory(); - NS_ENSURE_TRUE(sHistory, nullptr); - } - - NS_ADDREF(sHistory); - return sHistory; -} - -nsAndroidHistory::nsAndroidHistory() - : mHistoryEnabled(true) -{ - LoadPrefs(); - - mTimer = do_CreateInstance(NS_TIMER_CONTRACTID); -} - -NS_IMETHODIMP -nsAndroidHistory::RegisterVisitedCallback(nsIURI *aURI, Link *aContent) -{ - if (!aContent || !aURI) - return NS_OK; - - // Silently return if URI is something we would never add to DB. - bool canAdd; - nsresult rv = CanAddURI(aURI, &canAdd); - NS_ENSURE_SUCCESS(rv, rv); - if (!canAdd) { - return NS_OK; - } - - nsAutoCString uri; - rv = aURI->GetSpec(uri); - if (NS_FAILED(rv)) return rv; - NS_ConvertUTF8toUTF16 uriString(uri); - - nsTArray<Link*>* list = mListeners.Get(uriString); - if (! list) { - list = new nsTArray<Link*>(); - mListeners.Put(uriString, list); - } - list->AppendElement(aContent); - - if (jni::IsAvailable()) { - java::GeckoAppShell::CheckURIVisited(uriString); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsAndroidHistory::UnregisterVisitedCallback(nsIURI *aURI, Link *aContent) -{ - if (!aContent || !aURI) - return NS_OK; - - nsAutoCString uri; - nsresult rv = aURI->GetSpec(uri); - if (NS_FAILED(rv)) return rv; - NS_ConvertUTF8toUTF16 uriString(uri); - - nsTArray<Link*>* list = mListeners.Get(uriString); - if (! list) - return NS_OK; - - list->RemoveElement(aContent); - if (list->IsEmpty()) { - mListeners.Remove(uriString); - delete list; - } - return NS_OK; -} - -void -nsAndroidHistory::AppendToRecentlyVisitedURIs(nsIURI* aURI) { - if (mRecentlyVisitedURIs.Length() < RECENTLY_VISITED_URI_SIZE) { - // Append a new element while the array is not full. - mRecentlyVisitedURIs.AppendElement(aURI); - } else { - // Otherwise, replace the oldest member. - mRecentlyVisitedURIsNextIndex %= RECENTLY_VISITED_URI_SIZE; - mRecentlyVisitedURIs.ElementAt(mRecentlyVisitedURIsNextIndex) = aURI; - mRecentlyVisitedURIsNextIndex++; - } -} - -bool -nsAndroidHistory::ShouldRecordHistory() { - return mHistoryEnabled; -} - -void -nsAndroidHistory::LoadPrefs() { - mHistoryEnabled = Preferences::GetBool(PREF_HISTORY_ENABLED, true); -} - -inline bool -nsAndroidHistory::IsRecentlyVisitedURI(nsIURI* aURI) { - bool equals = false; - RecentlyVisitedArray::index_type i; - RecentlyVisitedArray::size_type length = mRecentlyVisitedURIs.Length(); - for (i = 0; i < length && !equals; ++i) { - aURI->Equals(mRecentlyVisitedURIs.ElementAt(i), &equals); - } - return equals; -} - -void -nsAndroidHistory::AppendToEmbedURIs(nsIURI* aURI) { - if (mEmbedURIs.Length() < EMBED_URI_SIZE) { - // Append a new element while the array is not full. - mEmbedURIs.AppendElement(aURI); - } else { - // Otherwise, replace the oldest member. - mEmbedURIsNextIndex %= EMBED_URI_SIZE; - mEmbedURIs.ElementAt(mEmbedURIsNextIndex) = aURI; - mEmbedURIsNextIndex++; - } -} - -inline bool -nsAndroidHistory::IsEmbedURI(nsIURI* aURI) { - bool equals = false; - EmbedArray::index_type i; - EmbedArray::size_type length = mEmbedURIs.Length(); - for (i = 0; i < length && !equals; ++i) { - aURI->Equals(mEmbedURIs.ElementAt(i), &equals); - } - return equals; -} - -inline bool -nsAndroidHistory::RemovePendingVisitURI(nsIURI* aURI) { - // Remove the first pending URI that matches. Return a boolean to - // let the caller know if we removed a URI or not. - bool equals = false; - PendingVisitArray::index_type i; - for (i = 0; i < mPendingVisitURIs.Length(); ++i) { - aURI->Equals(mPendingVisitURIs.ElementAt(i), &equals); - if (equals) { - mPendingVisitURIs.RemoveElementAt(i); - return true; - } - } - return false; -} - -NS_IMETHODIMP -nsAndroidHistory::Notify(nsITimer *timer) -{ - // Any pending visits left in the queue have exceeded our threshold for - // redirects, so save them - PendingVisitArray::index_type i; - for (i = 0; i < mPendingVisitURIs.Length(); ++i) { - SaveVisitURI(mPendingVisitURIs.ElementAt(i)); - } - mPendingVisitURIs.Clear(); - - return NS_OK; -} - -void -nsAndroidHistory::SaveVisitURI(nsIURI* aURI) { - // Add the URI to our cache so we can take a fast path later - AppendToRecentlyVisitedURIs(aURI); - - if (jni::IsAvailable()) { - // Save this URI in our history - nsAutoCString spec; - (void)aURI->GetSpec(spec); - java::GeckoAppShell::MarkURIVisited(NS_ConvertUTF8toUTF16(spec)); - } - - // Finally, notify that we've been visited. - nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService(); - if (obsService) { - obsService->NotifyObservers(aURI, NS_LINK_VISITED_EVENT_TOPIC, nullptr); - } -} - -NS_IMETHODIMP -nsAndroidHistory::VisitURI(nsIURI *aURI, nsIURI *aLastVisitedURI, uint32_t aFlags) -{ - if (!aURI) { - return NS_OK; - } - - if (!(aFlags & VisitFlags::TOP_LEVEL)) { - return NS_OK; - } - - if (aFlags & VisitFlags::UNRECOVERABLE_ERROR) { - return NS_OK; - } - - // Silently return if URI is something we shouldn't add to DB. - bool canAdd; - nsresult rv = CanAddURI(aURI, &canAdd); - NS_ENSURE_SUCCESS(rv, rv); - if (!canAdd) { - return NS_OK; - } - - if (aLastVisitedURI) { - if (aFlags & VisitFlags::REDIRECT_SOURCE || - aFlags & VisitFlags::REDIRECT_PERMANENT || - aFlags & VisitFlags::REDIRECT_TEMPORARY) { - // aLastVisitedURI redirected to aURI. We want to ignore aLastVisitedURI, - // so remove the pending visit. We want to give aURI a chance to be saved, - // so don't return early. - RemovePendingVisitURI(aLastVisitedURI); - } - - bool same; - rv = aURI->Equals(aLastVisitedURI, &same); - NS_ENSURE_SUCCESS(rv, rv); - if (same && IsRecentlyVisitedURI(aURI)) { - // Do not save refresh visits if we have visited this URI recently. - return NS_OK; - } - - // Since we have a last visited URI and we were not redirected, it is - // safe to save the visit if it's still pending. - if (RemovePendingVisitURI(aLastVisitedURI)) { - SaveVisitURI(aLastVisitedURI); - } - } - - // Let's wait and see if this visit is not a redirect. - mPendingVisitURIs.AppendElement(aURI); - mTimer->InitWithCallback(this, PENDING_REDIRECT_TIMEOUT, nsITimer::TYPE_ONE_SHOT); - - return NS_OK; -} - -NS_IMETHODIMP -nsAndroidHistory::SetURITitle(nsIURI *aURI, const nsAString& aTitle) -{ - // Silently return if URI is something we shouldn't add to DB. - bool canAdd; - nsresult rv = CanAddURI(aURI, &canAdd); - NS_ENSURE_SUCCESS(rv, rv); - if (!canAdd) { - return NS_OK; - } - - if (IsEmbedURI(aURI)) { - return NS_OK; - } - - if (jni::IsAvailable()) { - nsAutoCString uri; - nsresult rv = aURI->GetSpec(uri); - if (NS_FAILED(rv)) return rv; - if (RemovePendingVisitURI(aURI)) { - // We have a title, so aURI isn't a redirect, so save the visit now before setting the title. - SaveVisitURI(aURI); - } - NS_ConvertUTF8toUTF16 uriString(uri); - java::GeckoAppShell::SetURITitle(uriString, aTitle); - } - return NS_OK; -} - -NS_IMETHODIMP -nsAndroidHistory::NotifyVisited(nsIURI *aURI) -{ - if (aURI && sHistory) { - nsAutoCString spec; - (void)aURI->GetSpec(spec); - sHistory->mPendingLinkURIs.Push(NS_ConvertUTF8toUTF16(spec)); - NS_DispatchToMainThread(sHistory); - } - return NS_OK; -} - -NS_IMETHODIMP -nsAndroidHistory::Run() -{ - while (! mPendingLinkURIs.IsEmpty()) { - nsString uriString = mPendingLinkURIs.Pop(); - nsTArray<Link*>* list = sHistory->mListeners.Get(uriString); - if (list) { - for (unsigned int i = 0; i < list->Length(); i++) { - list->ElementAt(i)->SetLinkState(eLinkState_Visited); - } - // as per the IHistory interface contract, remove the - // Link pointers once they have been notified - mListeners.Remove(uriString); - delete list; - } - } - return NS_OK; -} - -// Filter out unwanted URIs such as "chrome:", "mailbox:", etc. -// -// The model is if we don't know differently then add which basically means -// we are suppose to try all the things we know not to allow in and then if -// we don't bail go on and allow it in. -// -// Logic ported from nsNavHistory::CanAddURI. - -NS_IMETHODIMP -nsAndroidHistory::CanAddURI(nsIURI* aURI, bool* canAdd) -{ - NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread"); - NS_ENSURE_ARG(aURI); - NS_ENSURE_ARG_POINTER(canAdd); - - // See if we're disabled. - if (!ShouldRecordHistory()) { - *canAdd = false; - return NS_OK; - } - - nsAutoCString scheme; - nsresult rv = aURI->GetScheme(scheme); - NS_ENSURE_SUCCESS(rv, rv); - - // first check the most common cases (HTTP, HTTPS) to allow in to avoid most - // of the work - if (scheme.EqualsLiteral("http")) { - *canAdd = true; - return NS_OK; - } - if (scheme.EqualsLiteral("https")) { - *canAdd = true; - return NS_OK; - } - if (scheme.EqualsLiteral("about")) { - nsAutoCString path; - rv = aURI->GetPath(path); - NS_ENSURE_SUCCESS(rv, rv); - - if (StringBeginsWith(path, NS_LITERAL_CSTRING("reader"))) { - *canAdd = true; - return NS_OK; - } - } - - // now check for all bad things - if (scheme.EqualsLiteral("about") || - scheme.EqualsLiteral("imap") || - scheme.EqualsLiteral("news") || - scheme.EqualsLiteral("mailbox") || - scheme.EqualsLiteral("moz-anno") || - scheme.EqualsLiteral("view-source") || - scheme.EqualsLiteral("chrome") || - scheme.EqualsLiteral("resource") || - scheme.EqualsLiteral("data") || - scheme.EqualsLiteral("wyciwyg") || - scheme.EqualsLiteral("javascript") || - scheme.EqualsLiteral("blob")) { - *canAdd = false; - return NS_OK; - } - *canAdd = true; - return NS_OK; -} diff --git a/mobile/android/components/build/nsAndroidHistory.h b/mobile/android/components/build/nsAndroidHistory.h deleted file mode 100644 index 382fbcd2e..000000000 --- a/mobile/android/components/build/nsAndroidHistory.h +++ /dev/null @@ -1,97 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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/. */ - -#ifndef NS_ANDROIDHISTORY_H -#define NS_ANDROIDHISTORY_H - -#include "IHistory.h" -#include "nsDataHashtable.h" -#include "nsTPriorityQueue.h" -#include "nsIRunnable.h" -#include "nsIURI.h" -#include "nsITimer.h" - - -#define NS_ANDROIDHISTORY_CID \ - {0xCCAA4880, 0x44DD, 0x40A7, {0xA1, 0x3F, 0x61, 0x56, 0xFC, 0x88, 0x2C, 0x0B}} - -// Max size of History::mRecentlyVisitedURIs -#define RECENTLY_VISITED_URI_SIZE 8 - -// Max size of History::mEmbedURIs -#define EMBED_URI_SIZE 128 - -class nsAndroidHistory final : public mozilla::IHistory, - public nsIRunnable, - public nsITimerCallback -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_IHISTORY - NS_DECL_NSIRUNNABLE - NS_DECL_NSITIMERCALLBACK - - /** - * Obtains a pointer that has had AddRef called on it. Used by the service - * manager only. - */ - static nsAndroidHistory* GetSingleton(); - - nsAndroidHistory(); - -private: - ~nsAndroidHistory() {} - - static nsAndroidHistory* sHistory; - - // Will mimic the value of the places.history.enabled preference. - bool mHistoryEnabled; - - void LoadPrefs(); - bool ShouldRecordHistory(); - nsresult CanAddURI(nsIURI* aURI, bool* canAdd); - - /** - * We need to manage data used to determine a:visited status. - */ - nsDataHashtable<nsStringHashKey, nsTArray<mozilla::dom::Link *> *> mListeners; - nsTPriorityQueue<nsString> mPendingLinkURIs; - - /** - * Redirection (temporary and permanent) flags are sent with the redirected - * URI, not the original URI. Since we want to ignore the original URI, we - * need to cache the pending visit and make sure it doesn't redirect. - */ - RefPtr<nsITimer> mTimer; - typedef AutoTArray<nsCOMPtr<nsIURI>, RECENTLY_VISITED_URI_SIZE> PendingVisitArray; - PendingVisitArray mPendingVisitURIs; - - bool RemovePendingVisitURI(nsIURI* aURI); - void SaveVisitURI(nsIURI* aURI); - - /** - * mRecentlyVisitedURIs remembers URIs which are recently added to the DB, - * to avoid saving these locations repeatedly in a short period. - */ - typedef AutoTArray<nsCOMPtr<nsIURI>, RECENTLY_VISITED_URI_SIZE> RecentlyVisitedArray; - RecentlyVisitedArray mRecentlyVisitedURIs; - RecentlyVisitedArray::index_type mRecentlyVisitedURIsNextIndex; - - void AppendToRecentlyVisitedURIs(nsIURI* aURI); - bool IsRecentlyVisitedURI(nsIURI* aURI); - - /** - * mEmbedURIs remembers URIs which are explicitly not added to the DB, - * to avoid wasting time on these locations. - */ - typedef AutoTArray<nsCOMPtr<nsIURI>, EMBED_URI_SIZE> EmbedArray; - EmbedArray::index_type mEmbedURIsNextIndex; - EmbedArray mEmbedURIs; - - void AppendToEmbedURIs(nsIURI* aURI); - bool IsEmbedURI(nsIURI* aURI); -}; - -#endif diff --git a/mobile/android/components/build/nsBrowserComponents.h b/mobile/android/components/build/nsBrowserComponents.h deleted file mode 100644 index c9830d9c5..000000000 --- a/mobile/android/components/build/nsBrowserComponents.h +++ /dev/null @@ -1,7 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// Needed for building our components as part of libxul -#define APP_COMPONENT_MODULES MODULE(nsBrowserCompsModule) diff --git a/mobile/android/components/build/nsBrowserModule.cpp b/mobile/android/components/build/nsBrowserModule.cpp deleted file mode 100644 index 6f9fe67bf..000000000 --- a/mobile/android/components/build/nsBrowserModule.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/ModuleUtils.h" - -#include "nsShellService.h" - -#ifdef MOZ_ANDROID_HISTORY -#include "nsDocShellCID.h" -#include "nsAndroidHistory.h" -#define NS_ANDROIDHISTORY_CID \ - {0xCCAA4880, 0x44DD, 0x40A7, {0xA1, 0x3F, 0x61, 0x56, 0xFC, 0x88, 0x2C, 0x0B}} -#endif - -NS_GENERIC_FACTORY_CONSTRUCTOR(nsShellService) -NS_DEFINE_NAMED_CID(nsShellService_CID); - -#ifdef MOZ_ANDROID_HISTORY -NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsAndroidHistory, nsAndroidHistory::GetSingleton) -NS_DEFINE_NAMED_CID(NS_ANDROIDHISTORY_CID); -#endif - -static const mozilla::Module::CIDEntry kBrowserCIDs[] = { - { &knsShellService_CID, false, nullptr, nsShellServiceConstructor }, -#ifdef MOZ_ANDROID_HISTORY - { &kNS_ANDROIDHISTORY_CID, false, nullptr, nsAndroidHistoryConstructor }, -#endif - { nullptr } -}; - -static const mozilla::Module::ContractIDEntry kBrowserContracts[] = { - { nsShellService_ContractID, &knsShellService_CID }, -#ifdef MOZ_ANDROID_HISTORY - { NS_IHISTORY_CONTRACTID, &kNS_ANDROIDHISTORY_CID }, -#endif - { nullptr } -}; - -static const mozilla::Module kBrowserModule = { - mozilla::Module::kVersion, - kBrowserCIDs, - kBrowserContracts -}; - -NSMODULE_DEFN(nsBrowserCompsModule) = &kBrowserModule; diff --git a/mobile/android/components/build/nsIShellService.idl b/mobile/android/components/build/nsIShellService.idl deleted file mode 100644 index e7f8d9277..000000000 --- a/mobile/android/components/build/nsIShellService.idl +++ /dev/null @@ -1,26 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -[scriptable, uuid(fd2450a3-966b-44a9-a8eb-316256bb80b4)] -interface nsIShellService : nsISupports -{ - /** - * This method displays a UI to switch to (or launch) a different task - */ - void switchTask(); - - /** - * This method creates a shortcut on a desktop or homescreen that opens in - * the our application. - * - * @param aTitle the user-friendly name of the shortcut. - * @param aURI the URI to open. - * @param aIconData obsolete and ignored, but remains for backward compatibility; pass an empty string - * @param aIntent obsolete and ignored, but remains for backward compatibility; pass an empty string - */ - void createShortcut(in AString aTitle, in AString aURI, in AString aIconData, in AString aIntent); -}; diff --git a/mobile/android/components/build/nsShellService.cpp b/mobile/android/components/build/nsShellService.cpp deleted file mode 100644 index 86cac86b4..000000000 --- a/mobile/android/components/build/nsShellService.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsShellService.h" -#include "nsString.h" - -#include "GeneratedJNIWrappers.h" - -using namespace mozilla; - -NS_IMPL_ISUPPORTS(nsShellService, nsIShellService) - -NS_IMETHODIMP -nsShellService::SwitchTask() -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -nsShellService::CreateShortcut(const nsAString& aTitle, const nsAString& aURI, - const nsAString& aIcondata, const nsAString& aIntent) -{ - if (!aTitle.Length() || !aURI.Length()) - return NS_ERROR_FAILURE; - - java::GeckoAppShell::CreateShortcut(aTitle, aURI); - return NS_OK; -} diff --git a/mobile/android/components/build/nsShellService.h b/mobile/android/components/build/nsShellService.h deleted file mode 100644 index ba56cbcae..000000000 --- a/mobile/android/components/build/nsShellService.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef __NS_SHELLSERVICE_H__ -#define __NS_SHELLSERVICE_H__ - -#include "nsIShellService.h" - -class nsShellService final : public nsIShellService -{ -public: - - NS_DECL_ISUPPORTS - NS_DECL_NSISHELLSERVICE - - nsShellService() {} - -private: - ~nsShellService() {} -}; - -#define nsShellService_CID \ -{0xae9ebe1c, 0x61e9, 0x45fa, {0x8f, 0x34, 0xc1, 0x07, 0x80, 0x3a, 0x5b, 0x44}} - -#define nsShellService_ContractID "@mozilla.org/browser/shell-service;1" - -#endif diff --git a/mobile/android/components/extensions/.eslintrc.js b/mobile/android/components/extensions/.eslintrc.js deleted file mode 100644 index 4b67e27b8..000000000 --- a/mobile/android/components/extensions/.eslintrc.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; - -module.exports = { - "extends": "../../../../toolkit/components/extensions/.eslintrc.js", -}; diff --git a/mobile/android/components/extensions/ext-pageAction.js b/mobile/android/components/extensions/ext-pageAction.js deleted file mode 100644 index fb1c3a3f3..000000000 --- a/mobile/android/components/extensions/ext-pageAction.js +++ /dev/null @@ -1,169 +0,0 @@ -/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set sts=2 sw=2 et tw=80: */ -"use strict"; - -XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter", - "resource://devtools/shared/event-emitter.js"); - -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); - -// Import the android PageActions module. -XPCOMUtils.defineLazyModuleGetter(this, "PageActions", - "resource://gre/modules/PageActions.jsm"); - -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); - -var { - IconDetails, - SingletonEventManager, -} = ExtensionUtils; - -// WeakMap[Extension -> PageAction] -var pageActionMap = new WeakMap(); - -function PageAction(options, extension) { - this.id = null; - - this.extension = extension; - this.icons = IconDetails.normalize({path: options.default_icon}, extension); - - this.popupUrl = options.default_popup; - - this.options = { - title: options.default_title || extension.name, - id: `{${extension.uuid}}`, - clickCallback: () => { - if (this.popupUrl) { - let win = Services.wm.getMostRecentWindow("navigator:browser"); - win.BrowserApp.addTab(this.popupUrl, { - selected: true, - parentId: win.BrowserApp.selectedTab.id, - }); - } else { - this.emit("click"); - } - }, - }; - - this.shouldShow = false; - - EventEmitter.decorate(this); -} - -PageAction.prototype = { - show(tabId, context) { - if (this.id) { - return Promise.resolve(); - } - - if (this.options.icon) { - this.id = PageActions.add(this.options); - return Promise.resolve(); - } - - this.shouldShow = true; - - // TODO(robwu): Remove dependency on contentWindow from this file. It should - // be put in a separate file called ext-c-pageAction.js. - // Note: Fennec is not going to be multi-process for the foreseaable future, - // so this layering violation has no immediate impact. However, it is should - // be done at some point. - let {contentWindow} = context.xulBrowser; - - // TODO(robwu): Why is this contentWindow.devicePixelRatio, while - // convertImageURLToDataURL uses browserWindow.devicePixelRatio? - let {icon} = IconDetails.getPreferredIcon(this.icons, this.extension, - 18 * contentWindow.devicePixelRatio); - - let browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); - return IconDetails.convertImageURLToDataURL(icon, contentWindow, browserWindow).then(dataURI => { - if (this.shouldShow) { - this.options.icon = dataURI; - this.id = PageActions.add(this.options); - } - }).catch(() => { - return Promise.reject({ - message: "Failed to load PageAction icon", - }); - }); - }, - - hide(tabId) { - this.shouldShow = false; - if (this.id) { - PageActions.remove(this.id); - this.id = null; - } - }, - - setPopup(tab, url) { - // TODO: Only set the popup for the specified tab once we have Tabs API support. - this.popupUrl = url; - }, - - getPopup(tab) { - // TODO: Only return the popup for the specified tab once we have Tabs API support. - return this.popupUrl; - }, - - shutdown() { - this.hide(); - }, -}; - -/* eslint-disable mozilla/balanced-listeners */ -extensions.on("manifest_page_action", (type, directive, extension, manifest) => { - let pageAction = new PageAction(manifest.page_action, extension); - pageActionMap.set(extension, pageAction); -}); - -extensions.on("shutdown", (type, extension) => { - if (pageActionMap.has(extension)) { - pageActionMap.get(extension).shutdown(); - pageActionMap.delete(extension); - } -}); -/* eslint-enable mozilla/balanced-listeners */ - -extensions.registerSchemaAPI("pageAction", "addon_parent", context => { - let {extension} = context; - return { - pageAction: { - onClicked: new SingletonEventManager(context, "pageAction.onClicked", fire => { - let listener = (event) => { - fire(); - }; - pageActionMap.get(extension).on("click", listener); - return () => { - pageActionMap.get(extension).off("click", listener); - }; - }).api(), - - show(tabId) { - return pageActionMap.get(extension) - .show(tabId, context) - .then(() => {}); - }, - - hide(tabId) { - pageActionMap.get(extension).hide(tabId); - return Promise.resolve(); - }, - - setPopup(details) { - // TODO: Use the Tabs API to get the tab from details.tabId. - let tab = null; - let url = details.popup && context.uri.resolve(details.popup); - pageActionMap.get(extension).setPopup(tab, url); - }, - - getPopup(details) { - // TODO: Use the Tabs API to get the tab from details.tabId. - let tab = null; - let popup = pageActionMap.get(extension).getPopup(tab); - return Promise.resolve(popup); - }, - }, - }; -}); diff --git a/mobile/android/components/extensions/extensions-mobile.manifest b/mobile/android/components/extensions/extensions-mobile.manifest deleted file mode 100644 index f15540d62..000000000 --- a/mobile/android/components/extensions/extensions-mobile.manifest +++ /dev/null @@ -1,5 +0,0 @@ -# scripts -category webextension-scripts pageAction chrome://browser/content/ext-pageAction.js - -# schemas -category webextension-schemas page_action chrome://browser/content/schemas/page_action.json
\ No newline at end of file diff --git a/mobile/android/components/extensions/jar.mn b/mobile/android/components/extensions/jar.mn deleted file mode 100644 index a3d2b8de8..000000000 --- a/mobile/android/components/extensions/jar.mn +++ /dev/null @@ -1,6 +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/. - -chrome.jar: - content/ext-pageAction.js
\ No newline at end of file diff --git a/mobile/android/components/extensions/moz.build b/mobile/android/components/extensions/moz.build deleted file mode 100644 index 0953fcefc..000000000 --- a/mobile/android/components/extensions/moz.build +++ /dev/null @@ -1,16 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn'] - -EXTRA_COMPONENTS += [ - 'extensions-mobile.manifest', -] - -DIRS += ['schemas'] - -MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini'] -MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini'] diff --git a/mobile/android/components/extensions/schemas/jar.mn b/mobile/android/components/extensions/schemas/jar.mn deleted file mode 100644 index 1a587ce20..000000000 --- a/mobile/android/components/extensions/schemas/jar.mn +++ /dev/null @@ -1,6 +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/. - -chrome.jar: - content/schemas/page_action.json
\ No newline at end of file diff --git a/mobile/android/components/extensions/schemas/moz.build b/mobile/android/components/extensions/schemas/moz.build deleted file mode 100644 index eb4454d28..000000000 --- a/mobile/android/components/extensions/schemas/moz.build +++ /dev/null @@ -1,7 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file diff --git a/mobile/android/components/extensions/schemas/page_action.json b/mobile/android/components/extensions/schemas/page_action.json deleted file mode 100644 index 5e9280922..000000000 --- a/mobile/android/components/extensions/schemas/page_action.json +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -[ - { - "namespace": "manifest", - "types": [ - { - "$extend": "WebExtensionManifest", - "properties": { - "page_action": { - "type": "object", - "additionalProperties": { "$ref": "UnrecognizedProperty" }, - "properties": { - "default_title": { - "type": "string", - "optional": true, - "preprocess": "localize" - }, - "default_icon": { - "$ref": "IconPath", - "optional": true - }, - "default_popup": { - "type": "string", - "format": "relativeUrl", - "optional": true, - "preprocess": "localize" - }, - "browser_style": { - "type": "boolean", - "optional": true - } - }, - "optional": true - } - } - } - ] - }, - { - "namespace": "pageAction", - "description": "Use the <code>browser.pageAction</code> API to put icons inside the address bar. Page actions represent actions that can be taken on the current page, but that aren't applicable to all pages.", - "permissions": ["manifest:page_action"], - "types": [ - { - "id": "ImageDataType", - "type": "object", - "isInstanceOf": "ImageData", - "additionalProperties": { "type": "any" }, - "description": "Pixel data for an image. Must be an ImageData object (for example, from a <code>canvas</code> element)." - } - ], - "functions": [ - { - "name": "show", - "type": "function", - "description": "Shows the page action. The page action is shown whenever the tab is selected.", - "async": "callback", - "parameters": [ - {"type": "integer", "name": "tabId", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."}, - { - "type": "function", - "name": "callback", - "optional": true, - "parameters": [] - } - ] - }, - { - "name": "hide", - "type": "function", - "description": "Hides the page action.", - "async": "callback", - "parameters": [ - {"type": "integer", "name": "tabId", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."}, - { - "type": "function", - "name": "callback", - "optional": true, - "parameters": [] - } - ] - }, - { - "name": "setTitle", - "unsupported": true, - "type": "function", - "description": "Sets the title of the page action. This is displayed in a tooltip over the page action.", - "parameters": [ - { - "name": "details", - "type": "object", - "properties": { - "tabId": {"type": "integer", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."}, - "title": {"type": "string", "description": "The tooltip string."} - } - } - ] - }, - { - "name": "getTitle", - "unsupported": true, - "type": "function", - "description": "Gets the title of the page action.", - "async": "callback", - "parameters": [ - { - "name": "details", - "type": "object", - "properties": { - "tabId": { - "type": "integer", - "description": "Specify the tab to get the title from." - } - } - }, - { - "type": "function", - "name": "callback", - "parameters": [ - { - "name": "result", - "type": "string" - } - ] - } - ] - }, - { - "name": "setIcon", - "unsupported": true, - "type": "function", - "description": "Sets the icon for the page action. The icon can be specified either as the path to an image file or as the pixel data from a canvas element, or as dictionary of either one of those. Either the <b>path</b> or the <b>imageData</b> property must be specified.", - "async": "callback", - "parameters": [ - { - "name": "details", - "type": "object", - "properties": { - "tabId": {"type": "integer", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."}, - "imageData": { - "choices": [ - { "$ref": "ImageDataType" }, - { - "type": "object", - "additionalProperties": {"$ref": "ImageDataType"} - } - ], - "optional": true, - "description": "Either an ImageData object or a dictionary {size -> ImageData} representing icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * 19 will be selected. Initially only scales 1 and 2 will be supported. At least one image must be specified. Note that 'details.imageData = foo' is equivalent to 'details.imageData = {'19': foo}'" - }, - "path": { - "choices": [ - { "type": "string" }, - { - "type": "object", - "additionalProperties": {"type": "string"} - } - ], - "optional": true, - "description": "Either a relative image path or a dictionary {size -> relative image path} pointing to icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * 19 will be selected. Initially only scales 1 and 2 will be supported. At least one image must be specified. Note that 'details.path = foo' is equivalent to 'details.imageData = {'19': foo}'" - } - } - }, - { - "type": "function", - "name": "callback", - "optional": true, - "parameters": [] - } - ] - }, - { - "name": "setPopup", - "type": "function", - "async": "callback", - "description": "Sets the html document to be opened as a popup when the user clicks on the page action's icon.", - "parameters": [ - { - "name": "details", - "type": "object", - "properties": { - "tabId": {"type": "integer", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."}, - "popup": { - "type": "string", - "description": "The html file to show in a popup. If set to the empty string (''), no popup is shown." - } - } - }, - { - "type": "function", - "name": "callback", - "optional": true, - "parameters": [] - } - ] - }, - { - "name": "getPopup", - "type": "function", - "description": "Gets the html document set as the popup for this page action.", - "async": "callback", - "parameters": [ - { - "name": "details", - "type": "object", - "properties": { - "tabId": { - "type": "integer", - "description": "Specify the tab to get the popup from." - } - } - }, - { - "type": "function", - "name": "callback", - "optional": true, - "parameters": [] - } - ] - } - ], - "events": [ - { - "name": "onClicked", - "type": "function", - "description": "Fired when a page action icon is clicked. This event will not fire if the page action has a popup.", - "parameters": [ - { - "name": "tab", - "$ref": "tabs.Tab" - } - ] - } - ] - } -] diff --git a/mobile/android/components/extensions/test/mochitest/.eslintrc.js b/mobile/android/components/extensions/test/mochitest/.eslintrc.js deleted file mode 100644 index 5f9059e18..000000000 --- a/mobile/android/components/extensions/test/mochitest/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -"use strict"; - -module.exports = { - "extends": "../../../../../../toolkit/components/extensions/test/mochitest/.eslintrc.js", - - "globals": { - "isPageActionShown": true, - "clickPageAction": true, - }, -}; diff --git a/mobile/android/components/extensions/test/mochitest/chrome.ini b/mobile/android/components/extensions/test/mochitest/chrome.ini deleted file mode 100644 index e19ddf393..000000000 --- a/mobile/android/components/extensions/test/mochitest/chrome.ini +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] -support-files = - head.js -tags = webextensions - -[test_ext_pageAction.html] -[test_ext_pageAction_popup.html] diff --git a/mobile/android/components/extensions/test/mochitest/head.js b/mobile/android/components/extensions/test/mochitest/head.js deleted file mode 100644 index be9683682..000000000 --- a/mobile/android/components/extensions/test/mochitest/head.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -/* exported isPageActionShown clickPageAction */ - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/PageActions.jsm"); - -function isPageActionShown(uuid) { - return PageActions.isShown(uuid); -} - -function clickPageAction(uuid) { - PageActions.synthesizeClick(uuid); -} diff --git a/mobile/android/components/extensions/test/mochitest/mochitest.ini b/mobile/android/components/extensions/test/mochitest/mochitest.ini deleted file mode 100644 index 59ef4bd20..000000000 --- a/mobile/android/components/extensions/test/mochitest/mochitest.ini +++ /dev/null @@ -1,6 +0,0 @@ -[DEFAULT] -support-files = - ../../../../../../toolkit/components/extensions/test/mochitest/test_ext_all_apis.js -tags = webextensions - -[test_ext_all_apis.html] diff --git a/mobile/android/components/extensions/test/mochitest/test_ext_all_apis.html b/mobile/android/components/extensions/test/mochitest/test_ext_all_apis.html deleted file mode 100644 index aec3eb7c1..000000000 --- a/mobile/android/components/extensions/test/mochitest/test_ext_all_apis.html +++ /dev/null @@ -1,23 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <title>WebExtension test</title> - <meta charset="utf-8"> - <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script> - <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> -</head> -<body> -<script> -"use strict"; -/* exported expectedContentApisTargetSpecific, expectedBackgroundApisTargetSpecific */ -let expectedContentApisTargetSpecific = [ -]; - -let expectedBackgroundApisTargetSpecific = [ -]; -</script> -<script src="test_ext_all_apis.js"></script> -</body> -</html> diff --git a/mobile/android/components/extensions/test/mochitest/test_ext_pageAction.html b/mobile/android/components/extensions/test/mochitest/test_ext_pageAction.html deleted file mode 100644 index b13c551bd..000000000 --- a/mobile/android/components/extensions/test/mochitest/test_ext_pageAction.html +++ /dev/null @@ -1,99 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <title>PageAction Test</title> - <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> - <script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> - <script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script> - <script type="text/javascript" src="head.js"></script> - <link rel="stylesheet" href="chrome://mochikit/contents/tests/SimpleTest/test.css"/> -</head> -<body> - -<script type="text/javascript"> -"use strict"; - -let dataURI = "iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAC4klEQVRYhdWXLWzbQBSADQtDAwsHC1tUhUxqfL67lk2tdn+OJg0ODU0rLByqgqINBY6tmlbn7LMTJ5FaFVVBk1G0oUGjG2jT2Y7jxmmcbU/6iJ+f36fz+e5sGP9riCGm9hB37RG+scd4Yo/wsDXCZyIE2xuXsce4bY+wXkAsQtzYmExrfFgvkJkRbkzo1ehoxx5iXcgI/9iYUGt8WH9MqDXEcmNChmEYrRCf2SHWeYgQx3x0tLNRIeKQLTtEFyJEep4NTuhk8BC+yMrwEE3+iozo42d8gK7FAOkMsRiiN8QhW2ttSK5QTfRRV4QoymVeJMvPvDp7gCZigD613MN6yRFA3SWarow9QB9LCfG+NeF9qCtjAKOSQjCqVKhfVsiHEQ+grgx/lRGqUihAc1uL8EFD+KCRO+GrF4J61phcoRoPoEzkYhZYpykh5sMb7kOdIeY+jHKur4QI4Feh4AFX1nVeLxrAvQchGsBz5ls6wa2QdwcvIcE2863bTH79KOvsz/uUYJsp+J0pSzNlDckVqqVGUAF+n6uS7txcOl6wot4JVy70ufDLy4pWLUQVPE81pRI0mGe9oxLMHSeohHvMs/STUNaUK6vDPCvOyxMFDx4achehRDJmHnydnkPww5OFfLxrGIZBFDyYl4LpMzlTQFIP6AQx86w2UeYBccFpJrcKv5L9eGDtUAU6RIELqsB74uynjy/UBRF1gS5BTFxwQT1wTiXoUg9MH7m/3NZRRoi5IJytUbMgzv4Wc832+oQkiKgEehmyMkkpKsFkQV11QsRJL5rJYBLItQgRaUZEmnoZXsomz3vGiWw+I9KMF9SVFOqZEemZekli1jN3U/UOqhHHvC6oWWGElhfSpGdOk6+O9prdwvtLj5BjRsQxdRnot+Zeifpy/2/0stktKTRNLmbk0mwXyl8253fyojj+8rxOHNAhjjm5n0/5OOCGOKBzkrMO0Z75lvSAzKlrF32Z/3z8BqLAn+yMV7VhAAAAAElFTkSuQmCC"; - -let image = atob(dataURI); -const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0)).buffer; - -function background() { - browser.test.assertTrue("pageAction" in browser, "Namespace 'pageAction' exists in browser"); - browser.test.assertTrue("show" in browser.pageAction, "API method 'show' exists in browser.pageAction"); - - // TODO: Use the Tabs API to obtain the tab ids for showing pageActions. - let tabId = 1; - browser.test.onMessage.addListener(msg => { - if (msg === "pageAction-show") { - browser.pageAction.show(tabId).then(() => { - browser.test.sendMessage("page-action-shown"); - }); - } else if (msg === "pageAction-hide") { - browser.pageAction.hide(tabId).then(() => { - browser.test.sendMessage("page-action-hidden"); - }); - } - }); - - browser.pageAction.onClicked.addListener(tab => { - // TODO: Make sure we get the correct tab once basic tabs support is added. - browser.test.sendMessage("page-action-clicked"); - }); - - let extensionInfo = { - // Extract the assigned uuid from the background page url. - uuid: `{${window.location.hostname}}`, - }; - - browser.test.sendMessage("ready", extensionInfo); -} - -add_task(function* test_pageAction() { - let extension = ExtensionTestUtils.loadExtension({ - background, - manifest: { - "name": "PageAction Extension", - "page_action": { - "default_title": "Page Action", - "default_icon": { - "18": "extension.png", - }, - }, - "applications": { - "gecko": { - "id": "foo@bar.com", - }, - }, - }, - files: { - "extension.png": IMAGE_ARRAYBUFFER, - }, - }); - - yield extension.startup(); - let {uuid} = yield extension.awaitMessage("ready"); - - extension.sendMessage("pageAction-show"); - yield extension.awaitMessage("page-action-shown"); - ok(isPageActionShown(uuid), "The PageAction should be shown"); - - extension.sendMessage("pageAction-hide"); - yield extension.awaitMessage("page-action-hidden"); - ok(!isPageActionShown(uuid), "The PageAction should be hidden"); - - extension.sendMessage("pageAction-show"); - yield extension.awaitMessage("page-action-shown"); - ok(isPageActionShown(uuid), "The PageAction should be shown"); - - clickPageAction(uuid); - yield extension.awaitMessage("page-action-clicked"); - ok(isPageActionShown(uuid), "The PageAction should still be shown after being clicked"); - - yield extension.unload(); - ok(!isPageActionShown(uuid), "The PageAction should be removed after unload"); -}); -</script> - -</body> -</html> diff --git a/mobile/android/components/extensions/test/mochitest/test_ext_pageAction_popup.html b/mobile/android/components/extensions/test/mochitest/test_ext_pageAction_popup.html deleted file mode 100644 index 89edc7c29..000000000 --- a/mobile/android/components/extensions/test/mochitest/test_ext_pageAction_popup.html +++ /dev/null @@ -1,169 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <title>PageAction Test</title> - <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> - <script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> - <script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script> - <script type="text/javascript" src="head.js"></script> - <link rel="stylesheet" href="chrome://mochikit/contents/tests/SimpleTest/test.css"/> -</head> -<body> - -<script type="text/javascript"> -"use strict"; - -Cu.import("resource://gre/modules/Services.jsm"); - -let dataURI = "iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAC4klEQVRYhdWXLWzbQBSADQtDAwsHC1tUhUxqfL67lk2tdn+OJg0ODU0rLByqgqINBY6tmlbn7LMTJ5FaFVVBk1G0oUGjG2jT2Y7jxmmcbU/6iJ+f36fz+e5sGP9riCGm9hB37RG+scd4Yo/wsDXCZyIE2xuXsce4bY+wXkAsQtzYmExrfFgvkJkRbkzo1ehoxx5iXcgI/9iYUGt8WH9MqDXEcmNChmEYrRCf2SHWeYgQx3x0tLNRIeKQLTtEFyJEep4NTuhk8BC+yMrwEE3+iozo42d8gK7FAOkMsRiiN8QhW2ttSK5QTfRRV4QoymVeJMvPvDp7gCZigD613MN6yRFA3SWarow9QB9LCfG+NeF9qCtjAKOSQjCqVKhfVsiHEQ+grgx/lRGqUihAc1uL8EFD+KCRO+GrF4J61phcoRoPoEzkYhZYpykh5sMb7kOdIeY+jHKur4QI4Feh4AFX1nVeLxrAvQchGsBz5ls6wa2QdwcvIcE2863bTH79KOvsz/uUYJsp+J0pSzNlDckVqqVGUAF+n6uS7txcOl6wot4JVy70ufDLy4pWLUQVPE81pRI0mGe9oxLMHSeohHvMs/STUNaUK6vDPCvOyxMFDx4achehRDJmHnydnkPww5OFfLxrGIZBFDyYl4LpMzlTQFIP6AQx86w2UeYBccFpJrcKv5L9eGDtUAU6RIELqsB74uynjy/UBRF1gS5BTFxwQT1wTiXoUg9MH7m/3NZRRoi5IJytUbMgzv4Wc832+oQkiKgEehmyMkkpKsFkQV11QsRJL5rJYBLItQgRaUZEmnoZXsomz3vGiWw+I9KMF9SVFOqZEemZekli1jN3U/UOqhHHvC6oWWGElhfSpGdOk6+O9prdwvtLj5BjRsQxdRnot+Zeifpy/2/0stktKTRNLmbk0mwXyl8253fyojj+8rxOHNAhjjm5n0/5OOCGOKBzkrMO0Z75lvSAzKlrF32Z/3z8BqLAn+yMV7VhAAAAAElFTkSuQmCC"; - -let image = atob(dataURI); -const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0)).buffer; - -add_task(function* test_contentscript() { - function background() { - // TODO: Use the Tabs API to obtain the tab ids for showing pageActions. - let tabId = 1; - let onClickedListenerEnabled = false; - - browser.test.onMessage.addListener((msg, details) => { - if (msg === "page-action-show") { - // TODO: switch to using .show(tabId).then(...) once bug 1270742 lands. - browser.pageAction.show(tabId).then(() => { - browser.test.sendMessage("page-action-shown"); - }); - } else if (msg == "page-action-set-popup") { - browser.pageAction.setPopup({popup: details.name, tabId: tabId}).then(() => { - browser.test.sendMessage("page-action-popup-set"); - }); - } else if (msg == "page-action-get-popup") { - browser.pageAction.getPopup({tabId: tabId}).then(url => { - browser.test.sendMessage("page-action-got-popup", url); - }); - } else if (msg == "page-action-enable-onClicked-listener") { - onClickedListenerEnabled = true; - browser.test.sendMessage("page-action-onClicked-listener-enabled"); - } else if (msg == "page-action-disable-onClicked-listener") { - onClickedListenerEnabled = false; - browser.test.sendMessage("page-action-onClicked-listener-disabled"); - } - }); - - browser.pageAction.onClicked.addListener(tab => { - browser.test.assertTrue(onClickedListenerEnabled, "The onClicked listener should only fire when it is enabled."); - browser.test.sendMessage("page-action-onClicked-fired"); - }); - - let extensionInfo = { - // Extract the assigned uuid from the background page url. - uuid: `{${window.location.hostname}}`, - }; - - browser.test.sendMessage("ready", extensionInfo); - } - - function popupScript() { - window.onload = () => { - browser.test.sendMessage("page-action-from-popup", location.href); - }; - browser.test.onMessage.addListener((msg, details) => { - if (msg == "page-action-close-popup") { - if (details.location == location.href) { - window.close(); - } - } - }); - } - - let extension = ExtensionTestUtils.loadExtension({ - background, - manifest: { - "name": "PageAction Extension", - "page_action": { - "default_title": "Page Action", - "default_popup": "default.html", - "default_icon": { - "18": "extension.png", - }, - }, - }, - files: { - "default.html": `<html><head><meta charset="utf-8"><script src="popup.js"><\/script></head></html>`, - "extension.png": IMAGE_ARRAYBUFFER, - "a.html": `<html><head><meta charset="utf-8"><script src="popup.js"><\/script></head></html>`, - "b.html": `<html><head><meta charset="utf-8"><script src="popup.js"><\/script></head></html>`, - "popup.js": popupScript, - }, - }); - - let tabClosedPromise = () => { - return new Promise(resolve => { - let chromeWin = Services.wm.getMostRecentWindow("navigator:browser"); - let BrowserApp = chromeWin.BrowserApp; - - let tabCloseListener = (event) => { - BrowserApp.deck.removeEventListener("TabClose", tabCloseListener, false); - let browser = event.target; - let url = browser.currentURI.spec; - resolve(url); - }; - - BrowserApp.deck.addEventListener("TabClose", tabCloseListener, false); - }); - }; - - function* testPopup(name, uuid) { - // We don't need to set the popup when testing default_popup. - if (name != "default.html") { - extension.sendMessage("page-action-set-popup", {name}); - yield extension.awaitMessage("page-action-popup-set"); - } - - extension.sendMessage("page-action-get-popup"); - let url = yield extension.awaitMessage("page-action-got-popup"); - - if (name == "") { - ok(url == name, "Calling pageAction.getPopup should return an empty string when the popup is not set."); - - // The onClicked listener should get called when the popup is set to an empty string. - extension.sendMessage("page-action-enable-onClicked-listener"); - yield extension.awaitMessage("page-action-onClicked-listener-enabled"); - - clickPageAction(uuid); - yield extension.awaitMessage("page-action-onClicked-fired"); - - extension.sendMessage("page-action-disable-onClicked-listener"); - yield extension.awaitMessage("page-action-onClicked-listener-disabled"); - } else { - ok(url.includes(name), "Calling pageAction.getPopup should return the correct popup URL when the popup is set."); - - clickPageAction(uuid); - let location = yield extension.awaitMessage("page-action-from-popup"); - ok(location.includes(name), "The popup with the correct URL should be shown."); - - extension.sendMessage("page-action-close-popup", {location}); - - url = yield tabClosedPromise(); - ok(url.includes(name), "The tab for the popup should be closed."); - } - } - - yield extension.startup(); - let {uuid} = yield extension.awaitMessage("ready"); - - extension.sendMessage("page-action-show"); - yield extension.awaitMessage("page-action-shown"); - ok(isPageActionShown(uuid), "The PageAction should be shown."); - - yield testPopup("default.html", uuid); - yield testPopup("a.html", uuid); - yield testPopup("", uuid); - yield testPopup("b.html", uuid); - - yield extension.unload(); - ok(!isPageActionShown(uuid), "The PageAction should be removed after unload."); -}); -</script> - -</body> -</html> diff --git a/mobile/android/components/moz.build b/mobile/android/components/moz.build deleted file mode 100644 index 9e6683662..000000000 --- a/mobile/android/components/moz.build +++ /dev/null @@ -1,47 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -XPIDL_SOURCES += [ - 'SessionStore.idl', -] - -XPIDL_MODULE = 'MobileComponents' - -EXTRA_COMPONENTS += [ - 'AboutRedirector.js', - 'AddonUpdateService.js', - 'BlocklistPrompt.js', - 'BrowserCLH.js', - 'ColorPicker.js', - 'ContentDispatchChooser.js', - 'ContentPermissionPrompt.js', - 'DirectoryProvider.js', - 'FilePicker.js', - 'HelperAppDialog.js', - 'ImageBlockingPolicy.js', - 'LoginManagerPrompter.js', - 'NSSDialogService.js', - 'PersistentNotificationHandler.js', - 'PresentationDevicePrompt.js', - 'PresentationRequestUIGlue.js', - 'PromptService.js', - 'SessionStore.js', - 'SiteSpecificUserAgent.js', - 'Snippets.js', - 'TabSource.js', - 'XPIDialogService.js', -] - -# Keep it this way if at all possible. If you need preprocessing, -# consider adding fields to AppConstants.jsm. -EXTRA_PP_COMPONENTS += [ - 'MobileComponents.manifest', -] - -DIRS += [ - 'extensions', - 'build', -] |