diff options
author | Matt A. Tobin <email@mattatobin.com> | 2019-04-23 15:32:23 -0400 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2019-04-23 15:32:23 -0400 |
commit | abe80cc31d5a40ebed743085011fbcda0c1a9a10 (patch) | |
tree | fb3762f06b84745b182af281abb107b95a9fcf01 /mobile/android/modules | |
parent | 63295d0087eb58a6eb34cad324c4c53d1b220491 (diff) | |
download | UXP-abe80cc31d5a40ebed743085011fbcda0c1a9a10.tar UXP-abe80cc31d5a40ebed743085011fbcda0c1a9a10.tar.gz UXP-abe80cc31d5a40ebed743085011fbcda0c1a9a10.tar.lz UXP-abe80cc31d5a40ebed743085011fbcda0c1a9a10.tar.xz UXP-abe80cc31d5a40ebed743085011fbcda0c1a9a10.zip |
Issue #1053 - Drop support Android and remove Fennec - Part 1a: Remove mobile/android
Diffstat (limited to 'mobile/android/modules')
25 files changed, 0 insertions, 5743 deletions
diff --git a/mobile/android/modules/Accounts.jsm b/mobile/android/modules/Accounts.jsm deleted file mode 100644 index ee0fc0c5b..000000000 --- a/mobile/android/modules/Accounts.jsm +++ /dev/null @@ -1,79 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["Accounts"]; - -const { utils: Cu } = Components; - -Cu.import("resource://gre/modules/Deprecated.jsm"); /*global Deprecated */ -Cu.import("resource://gre/modules/Messaging.jsm"); /*global Messaging */ -Cu.import("resource://gre/modules/Promise.jsm"); /*global Promise */ -Cu.import("resource://gre/modules/Services.jsm"); /*global Services */ - -/** - * A promise-based API for querying the existence of Sync accounts, - * and accessing the Sync setup wizard. - * - * Usage: - * - * Cu.import("resource://gre/modules/Accounts.jsm"); - * Accounts.anySyncAccountsExist().then( - * (exist) => { - * console.log("Accounts exist? " + exist); - * if (!exist) { - * Accounts.launchSetup(); - * } - * }, - * (err) => { - * console.log("We failed so hard."); - * } - * ); - */ -var Accounts = Object.freeze({ - _accountsExist: function (kind) { - return Messaging.sendRequestForResult({ - type: "Accounts:Exist", - kind: kind - }).then(data => data.exists); - }, - - syncAccountsExist: function () { - Deprecated.warning("The legacy Sync account type has been removed from Firefox for Android."); - return Promise.resolve(false); - }, - - anySyncAccountsExist: function () { - return this._accountsExist("any"); - }, - - /** - * Fire-and-forget: open the Firefox accounts activity, which - * will be the Getting Started screen if FxA isn't yet set up. - * - * Optional extras are passed, as a JSON string, to the Firefox - * Account Getting Started activity in the extras bundle of the - * activity launch intent, under the key "extras". - * - * There is no return value from this method. - */ - launchSetup: function (extras) { - Messaging.sendRequest({ - type: "Accounts:Create", - extras: extras - }); - }, - - _addDefaultEndpoints: function (json) { - // Empty without FxA - let newData = Cu.cloneInto(json, {}, { cloneFunctions: false }); - return newData; - }, - - showSyncPreferences: function () { - // Only show Sync preferences of an existing Android Account. - throw new Error("Can't show Sync preferences without accounts!"); - } -}); diff --git a/mobile/android/modules/AndroidLog.jsm b/mobile/android/modules/AndroidLog.jsm deleted file mode 100644 index be6cca4e8..000000000 --- a/mobile/android/modules/AndroidLog.jsm +++ /dev/null @@ -1,92 +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"; - -/** - * Native Android logging for JavaScript. Lets you specify a priority and tag - * in addition to the message being logged. Resembles the android.util.Log API - * <http://developer.android.com/reference/android/util/Log.html>. - * - * // Import it as a JSM: - * let Log = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog; - * - * // Or require it in a chrome worker: - * importScripts("resource://gre/modules/workers/require.js"); - * let Log = require("resource://gre/modules/AndroidLog.jsm"); - * - * // Use Log.i, Log.v, Log.d, Log.w, and Log.e to log verbose, debug, info, - * // warning, and error messages, respectively. - * Log.v("MyModule", "This is a verbose message."); - * Log.d("MyModule", "This is a debug message."); - * Log.i("MyModule", "This is an info message."); - * Log.w("MyModule", "This is a warning message."); - * Log.e("MyModule", "This is an error message."); - * - * // Bind a function with a tag to replace a bespoke dump/log/debug function: - * let debug = Log.d.bind(null, "MyModule"); - * debug("This is a debug message."); - * // Outputs "D/GeckoMyModule(#####): This is a debug message." - * - * // Or "bind" the module object to a tag to automatically tag messages: - * Log = Log.bind("MyModule"); - * Log.d("This is a debug message."); - * // Outputs "D/GeckoMyModule(#####): This is a debug message." - * - * Note: the module automatically prepends "Gecko" to the tag you specify, - * since all tags used by Fennec code should start with that string; and it - * truncates tags longer than MAX_TAG_LENGTH characters (not including "Gecko"). - */ - -if (typeof Components != "undefined") { - // Specify exported symbols for JSM module loader. - this.EXPORTED_SYMBOLS = ["AndroidLog"]; - Components.utils.import("resource://gre/modules/ctypes.jsm"); -} - -// From <https://android.googlesource.com/platform/system/core/+/master/include/android/log.h>. -const ANDROID_LOG_VERBOSE = 2; -const ANDROID_LOG_DEBUG = 3; -const ANDROID_LOG_INFO = 4; -const ANDROID_LOG_WARN = 5; -const ANDROID_LOG_ERROR = 6; - -// android.util.Log.isLoggable throws IllegalArgumentException if a tag length -// exceeds 23 characters, and we prepend five characters ("Gecko") to every tag, -// so we truncate tags exceeding 18 characters (although __android_log_write -// itself and other android.util.Log methods don't seem to mind longer tags). -const MAX_TAG_LENGTH = 18; - -var liblog = ctypes.open("liblog.so"); // /system/lib/liblog.so -var __android_log_write = liblog.declare("__android_log_write", - ctypes.default_abi, - ctypes.int, // return value: num bytes logged - ctypes.int, // priority (ANDROID_LOG_* constant) - ctypes.char.ptr, // tag - ctypes.char.ptr); // message - -var AndroidLog = { - MAX_TAG_LENGTH: MAX_TAG_LENGTH, - v: (tag, msg) => __android_log_write(ANDROID_LOG_VERBOSE, "Gecko" + tag.substring(0, MAX_TAG_LENGTH), msg), - d: (tag, msg) => __android_log_write(ANDROID_LOG_DEBUG, "Gecko" + tag.substring(0, MAX_TAG_LENGTH), msg), - i: (tag, msg) => __android_log_write(ANDROID_LOG_INFO, "Gecko" + tag.substring(0, MAX_TAG_LENGTH), msg), - w: (tag, msg) => __android_log_write(ANDROID_LOG_WARN, "Gecko" + tag.substring(0, MAX_TAG_LENGTH), msg), - e: (tag, msg) => __android_log_write(ANDROID_LOG_ERROR, "Gecko" + tag.substring(0, MAX_TAG_LENGTH), msg), - - bind: function(tag) { - return { - MAX_TAG_LENGTH: MAX_TAG_LENGTH, - v: AndroidLog.v.bind(null, tag), - d: AndroidLog.d.bind(null, tag), - i: AndroidLog.i.bind(null, tag), - w: AndroidLog.w.bind(null, tag), - e: AndroidLog.e.bind(null, tag), - }; - }, -}; - -if (typeof Components == "undefined") { - // Specify exported symbols for require.js module loader. - module.exports = AndroidLog; -} diff --git a/mobile/android/modules/DelayedInit.jsm b/mobile/android/modules/DelayedInit.jsm deleted file mode 100644 index 7c33a3ede..000000000 --- a/mobile/android/modules/DelayedInit.jsm +++ /dev/null @@ -1,177 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict" - -/*globals MessageLoop */ - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -this.EXPORTED_SYMBOLS = ["DelayedInit"]; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyServiceGetter(this, "MessageLoop", - "@mozilla.org/message-loop;1", - "nsIMessageLoop"); - -/** - * Use DelayedInit to schedule initializers to run some time after startup. - * Initializers are added to a list of pending inits. Whenever the main thread - * message loop is idle, DelayedInit will start running initializers from the - * pending list. To prevent monopolizing the message loop, every idling period - * has a maximum duration. When that's reached, we give up the message loop and - * wait for the next idle. - * - * DelayedInit is compatible with lazy getters like those from XPCOMUtils. When - * the lazy getter is first accessed, its corresponding initializer is run - * automatically if it hasn't been run already. Each initializer also has a - * maximum wait parameter that specifies a mandatory timeout; when the timeout - * is reached, the initializer is forced to run. - * - * DelayedInit.schedule(() => Foo.init(), null, null, 5000); - * - * In the example above, Foo.init will run automatically when the message loop - * becomes idle, or when 5000ms has elapsed, whichever comes first. - * - * DelayedInit.schedule(() => Foo.init(), this, "Foo", 5000); - * - * In the example above, Foo.init will run automatically when the message loop - * becomes idle, when |this.Foo| is accessed, or when 5000ms has elapsed, - * whichever comes first. - * - * It may be simpler to have a wrapper for DelayedInit.schedule. For example, - * - * function InitLater(fn, obj, name) { - * return DelayedInit.schedule(fn, obj, name, 5000); // constant max wait - * } - * InitLater(() => Foo.init()); - * InitLater(() => Bar.init(), this, "Bar"); - */ -var DelayedInit = { - schedule: function (fn, object, name, maxWait) { - return Impl.scheduleInit(fn, object, name, maxWait); - }, -}; - -// Maximum duration for each idling period. Pending inits are run until this -// duration is exceeded; then we wait for next idling period. -const MAX_IDLE_RUN_MS = 50; - -var Impl = { - pendingInits: [], - - onIdle: function () { - let startTime = Cu.now(); - let time = startTime; - let nextDue; - - // Go through all the pending inits. Even if we don't run them, - // we still need to find out when the next timeout should be. - for (let init of this.pendingInits) { - if (init.complete) { - continue; - } - - if (time - startTime < MAX_IDLE_RUN_MS) { - init.maybeInit(); - time = Cu.now(); - } else { - // We ran out of time; find when the next closest due time is. - nextDue = nextDue ? Math.min(nextDue, init.due) : init.due; - } - } - - // Get rid of completed ones. - this.pendingInits = this.pendingInits.filter((init) => !init.complete); - - if (nextDue !== undefined) { - // Schedule the next idle, if we still have pending inits. - MessageLoop.postIdleTask(() => this.onIdle(), - Math.max(0, nextDue - time)); - } - }, - - addPendingInit: function (fn, wait) { - let init = { - fn: fn, - due: Cu.now() + wait, - complete: false, - maybeInit: function () { - if (this.complete) { - return false; - } - this.complete = true; - this.fn.call(); - this.fn = null; - return true; - }, - }; - - if (!this.pendingInits.length) { - // Schedule for the first idle. - MessageLoop.postIdleTask(() => this.onIdle(), wait); - } - this.pendingInits.push(init); - return init; - }, - - scheduleInit: function (fn, object, name, wait) { - let init = this.addPendingInit(fn, wait); - - if (!object || !name) { - // No lazy getter needed. - return; - } - - // Get any existing information about the property. - let prop = Object.getOwnPropertyDescriptor(object, name) || - { configurable: true, enumerable: true, writable: true }; - - if (!prop.configurable) { - // Object.defineProperty won't work, so just perform init here. - init.maybeInit(); - return; - } - - // Define proxy getter/setter that will call first initializer first, - // before delegating the get/set to the original target. - Object.defineProperty(object, name, { - get: function proxy_getter() { - init.maybeInit(); - - // If the initializer actually ran, it may have replaced our proxy - // property with a real one, so we need to reload he property. - let newProp = Object.getOwnPropertyDescriptor(object, name); - if (newProp.get !== proxy_getter) { - // Set prop if newProp doesn't refer to our proxy property. - prop = newProp; - } else { - // Otherwise, reset to the original property. - Object.defineProperty(object, name, prop); - } - - if (prop.get) { - return prop.get.call(object); - } - return prop.value; - }, - set: function (newVal) { - init.maybeInit(); - - // Since our initializer already ran, - // we can get rid of our proxy property. - if (prop.get || prop.set) { - Object.defineProperty(object, name, prop); - return prop.set.call(object); - } - - prop.value = newVal; - Object.defineProperty(object, name, prop); - return newVal; - }, - configurable: true, - enumerable: true, - }); - } -}; diff --git a/mobile/android/modules/DownloadNotifications.jsm b/mobile/android/modules/DownloadNotifications.jsm deleted file mode 100644 index 39b520979..000000000 --- a/mobile/android/modules/DownloadNotifications.jsm +++ /dev/null @@ -1,291 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["DownloadNotifications"]; - -const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Downloads", "resource://gre/modules/Downloads.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Notifications", "resource://gre/modules/Notifications.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry", "resource://gre/modules/UITelemetry.jsm"); - -XPCOMUtils.defineLazyServiceGetter(this, "ParentalControls", - "@mozilla.org/parental-controls-service;1", "nsIParentalControlsService"); - -var Log = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.i.bind(null, "DownloadNotifications"); - -XPCOMUtils.defineLazyGetter(this, "strings", - () => Services.strings.createBundle("chrome://browser/locale/browser.properties")); - -Object.defineProperty(this, "window", - { get: () => Services.wm.getMostRecentWindow("navigator:browser") }); - -const kButtons = { - PAUSE: new DownloadNotificationButton("pause", - "drawable://pause", - "alertDownloadsPause"), - RESUME: new DownloadNotificationButton("resume", - "drawable://play", - "alertDownloadsResume"), - CANCEL: new DownloadNotificationButton("cancel", - "drawable://close", - "alertDownloadsCancel") -}; - -var notifications = new Map(); - -var DownloadNotifications = { - _notificationKey: "downloads", - - init: function () { - Downloads.getList(Downloads.ALL) - .then(list => list.addView(this)) - .then(() => this._viewAdded = true, Cu.reportError); - - // All click, cancel, and button presses will be handled by this handler as part of the Notifications callback API. - Notifications.registerHandler(this._notificationKey, this); - }, - - onDownloadAdded: function (download) { - // Don't create notifications for pre-existing succeeded downloads. - // We still add notifications for canceled downloads in case the - // user decides to retry the download. - if (download.succeeded && !this._viewAdded) { - return; - } - - if (!ParentalControls.isAllowed(ParentalControls.DOWNLOAD)) { - download.cancel().catch(Cu.reportError); - download.removePartialData().catch(Cu.reportError); - Snackbars.show(strings.GetStringFromName("downloads.disabledInGuest"), Snackbars.LENGTH_LONG); - return; - } - - let notification = new DownloadNotification(download); - notifications.set(download, notification); - notification.showOrUpdate(); - - // If this is a new download, show a snackbar as well. - if (this._viewAdded) { - Snackbars.show(strings.GetStringFromName("alertDownloadsToast"), Snackbars.LENGTH_LONG); - } - }, - - onDownloadChanged: function (download) { - let notification = notifications.get(download); - - if (download.succeeded) { - let file = new FileUtils.File(download.target.path); - - Snackbars.show(strings.formatStringFromName("alertDownloadSucceeded", [file.leafName], 1), Snackbars.LENGTH_LONG, { - action: { - label: strings.GetStringFromName("helperapps.open"), - callback: () => { - UITelemetry.addEvent("launch.1", "toast", null, "downloads"); - try { - file.launch(); - } catch (ex) { - this.showInAboutDownloads(download); - } - if (notification) { - notification.hide(); - } - } - }}); - } - - if (notification) { - notification.showOrUpdate(); - } - }, - - onDownloadRemoved: function (download) { - let notification = notifications.get(download); - if (!notification) { - Cu.reportError("Download doesn't have a notification."); - return; - } - - notification.hide(); - notifications.delete(download); - }, - - _findDownloadForCookie: function(cookie) { - return Downloads.getList(Downloads.ALL) - .then(list => list.getAll()) - .then((downloads) => { - for (let download of downloads) { - let cookie2 = getCookieFromDownload(download); - if (cookie2 === cookie) { - return download; - } - } - - throw "Couldn't find download for " + cookie; - }); - }, - - onCancel: function(cookie) { - // TODO: I'm not sure what we do here... - }, - - showInAboutDownloads: function (download) { - let hash = "#" + window.encodeURIComponent(download.target.path); - - // Force using string equality to find a tab - window.BrowserApp.selectOrAddTab("about:downloads" + hash, null, { startsWith: true }); - }, - - onClick: function(cookie) { - this._findDownloadForCookie(cookie).then((download) => { - if (download.succeeded) { - // We don't call Download.launch(), because there's (currently) no way to - // tell if the file was actually launched or not, and we want to show - // about:downloads if the launch failed. - let file = new FileUtils.File(download.target.path); - try { - file.launch(); - } catch (ex) { - this.showInAboutDownloads(download); - } - } else { - ConfirmCancelPrompt.show(download); - } - }).catch(Cu.reportError); - }, - - onButtonClick: function(button, cookie) { - this._findDownloadForCookie(cookie).then((download) => { - if (button === kButtons.PAUSE.buttonId) { - download.cancel().catch(Cu.reportError); - } else if (button === kButtons.RESUME.buttonId) { - download.start().catch(Cu.reportError); - } else if (button === kButtons.CANCEL.buttonId) { - download.cancel().catch(Cu.reportError); - download.removePartialData().catch(Cu.reportError); - } - }).catch(Cu.reportError); - }, -}; - -function getCookieFromDownload(download) { - return download.target.path + - download.source.url + - download.startTime; -} - -function DownloadNotification(download) { - this.download = download; - this._fileName = OS.Path.basename(download.target.path); - - this.id = null; -} - -DownloadNotification.prototype = { - _updateFromDownload: function () { - this._downloading = !this.download.stopped; - this._paused = this.download.canceled && this.download.hasPartialData; - this._succeeded = this.download.succeeded; - - this._show = this._downloading || this._paused || this._succeeded; - }, - - get options() { - if (!this._show) { - return null; - } - - let options = { - icon: "drawable://alert_download", - cookie: getCookieFromDownload(this.download), - handlerKey: DownloadNotifications._notificationKey - }; - - if (this._downloading) { - options.icon = "drawable://alert_download_animation"; - if (this.download.currentBytes == 0) { - this._updateOptionsForStatic(options, "alertDownloadsStart2"); - } else { - let buttons = this.download.hasPartialData ? [kButtons.PAUSE, kButtons.CANCEL] : - [kButtons.CANCEL] - this._updateOptionsForOngoing(options, buttons); - } - } else if (this._paused) { - this._updateOptionsForOngoing(options, [kButtons.RESUME, kButtons.CANCEL]); - } else if (this._succeeded) { - options.persistent = false; - this._updateOptionsForStatic(options, "alertDownloadsDone2"); - } - - return options; - }, - - _updateOptionsForStatic : function (options, titleName) { - options.title = strings.GetStringFromName(titleName); - options.message = this._fileName; - }, - - _updateOptionsForOngoing: function (options, buttons) { - options.title = this._fileName; - options.message = this.download.progress + "%"; - options.buttons = buttons; - options.ongoing = true; - options.progress = this.download.progress; - options.persistent = true; - }, - - showOrUpdate: function () { - this._updateFromDownload(); - - if (this._show) { - if (!this.id) { - this.id = Notifications.create(this.options); - } else if (!this.options.ongoing) { - // We need to explictly cancel ongoing notifications, - // since updating them to be non-ongoing doesn't seem - // to work. See bug 1130834. - Notifications.cancel(this.id); - this.id = Notifications.create(this.options); - } else { - Notifications.update(this.id, this.options); - } - } else { - this.hide(); - } - }, - - hide: function () { - if (this.id) { - Notifications.cancel(this.id); - this.id = null; - } - }, -}; - -var ConfirmCancelPrompt = { - show: function (download) { - // Open a prompt that offers a choice to cancel the download - let title = strings.GetStringFromName("downloadCancelPromptTitle1"); - let message = strings.GetStringFromName("downloadCancelPromptMessage1"); - - if (Services.prompt.confirm(null, title, message)) { - download.cancel().catch(Cu.reportError); - download.removePartialData().catch(Cu.reportError); - } - } -}; - -function DownloadNotificationButton(buttonId, iconUrl, titleStringName, onClicked) { - this.buttonId = buttonId; - this.title = strings.GetStringFromName(titleStringName); - this.icon = iconUrl; -} diff --git a/mobile/android/modules/HelperApps.jsm b/mobile/android/modules/HelperApps.jsm deleted file mode 100644 index 0ac478da0..000000000 --- a/mobile/android/modules/HelperApps.jsm +++ /dev/null @@ -1,229 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -/* globals ContentAreaUtils */ - -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, "Messaging", - "resource://gre/modules/Messaging.jsm"); - -XPCOMUtils.defineLazyGetter(this, "ContentAreaUtils", function() { - let ContentAreaUtils = {}; - Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", ContentAreaUtils); - return ContentAreaUtils; -}); - -this.EXPORTED_SYMBOLS = ["App","HelperApps"]; - -function App(data) { - this.name = data.name; - this.isDefault = data.isDefault; - this.packageName = data.packageName; - this.activityName = data.activityName; - this.iconUri = "-moz-icon://" + data.packageName; -} - -App.prototype = { - // callback will be null if a result is not requested - launch: function(uri, callback) { - HelperApps._launchApp(this, uri, callback); - return false; - } -} - -var HelperApps = { - get defaultBrowsers() { - delete this.defaultBrowsers; - this.defaultBrowsers = this._getHandlers("http://www.example.com", { - filterBrowsers: false, - filterHtml: false - }); - return this.defaultBrowsers; - }, - - // Finds handlers that have registered for text/html pages or urls ending in html. Some apps, like - // the Samsung Video player will only appear for these urls, while some Browsers (like Link Bubble) - // won't register here because of the text/html mime type. - get defaultHtmlHandlers() { - delete this.defaultHtmlHandlers; - return this.defaultHtmlHandlers = this._getHandlers("http://www.example.com/index.html", { - filterBrowsers: false, - filterHtml: false - }); - }, - - _getHandlers: function(url, options) { - let values = {}; - - let handlers = this.getAppsForUri(Services.io.newURI(url, null, null), options); - handlers.forEach(function(app) { - values[app.name] = app; - }, this); - - return values; - }, - - get protoSvc() { - delete this.protoSvc; - return this.protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].getService(Ci.nsIExternalProtocolService); - }, - - get urlHandlerService() { - delete this.urlHandlerService; - return this.urlHandlerService = Cc["@mozilla.org/uriloader/external-url-handler-service;1"].getService(Ci.nsIExternalURLHandlerService); - }, - - prompt: function showPicker(apps, promptOptions, callback) { - let p = new Prompt(promptOptions).addIconGrid({ items: apps }); - p.show(callback); - }, - - getAppsForProtocol: function getAppsForProtocol(scheme) { - let protoHandlers = this.protoSvc.getProtocolHandlerInfoFromOS(scheme, {}).possibleApplicationHandlers; - - let results = {}; - for (let i = 0; i < protoHandlers.length; i++) { - try { - let protoApp = protoHandlers.queryElementAt(i, Ci.nsIHandlerApp); - results[protoApp.name] = new App({ - name: protoApp.name, - description: protoApp.detailedDescription, - }); - } catch(e) {} - } - - return results; - }, - - getAppsForUri: function getAppsForUri(uri, flags = { }, callback) { - // Return early for well-known internal schemes - if (!uri || uri.schemeIs("about") || uri.schemeIs("chrome")) { - if (callback) { - callback([]); - } - return []; - } - - flags.filterBrowsers = "filterBrowsers" in flags ? flags.filterBrowsers : true; - flags.filterHtml = "filterHtml" in flags ? flags.filterHtml : true; - - // Query for apps that can/can't handle the mimetype - let msg = this._getMessage("Intent:GetHandlers", uri, flags); - let parseData = (d) => { - let apps = [] - if (!d) { - return apps; - } - - apps = this._parseApps(d.apps); - - if (flags.filterBrowsers) { - apps = apps.filter((app) => { - return app.name && !this.defaultBrowsers[app.name]; - }); - } - - // Some apps will register for html files (the Samsung Video player) but should be shown - // for non-HTML files (like videos). This filters them only if the page has an htm of html - // file extension. - if (flags.filterHtml) { - // Matches from the first '.' to the end of the string, '?', or '#' - let ext = /\.([^\?#]*)/.exec(uri.path); - if (ext && (ext[1] === "html" || ext[1] === "htm")) { - apps = apps.filter(function(app) { - return app.name && !this.defaultHtmlHandlers[app.name]; - }, this); - } - } - - return apps; - }; - - if (!callback) { - let data = this._sendMessageSync(msg); - return parseData(data); - } else { - Messaging.sendRequestForResult(msg).then(function(data) { - callback(parseData(data)); - }); - } - }, - - launchUri: function launchUri(uri) { - let msg = this._getMessage("Intent:Open", uri); - Messaging.sendRequest(msg); - }, - - _parseApps: function _parseApps(appInfo) { - // appInfo -> {apps: [app1Label, app1Default, app1PackageName, app1ActivityName, app2Label, app2Defaut, ...]} - // see GeckoAppShell.java getHandlersForIntent function for details - const numAttr = 4; // 4 elements per ResolveInfo: label, default, package name, activity name. - - let apps = []; - for (let i = 0; i < appInfo.length; i += numAttr) { - apps.push(new App({"name" : appInfo[i], - "isDefault" : appInfo[i+1], - "packageName" : appInfo[i+2], - "activityName" : appInfo[i+3]})); - } - - return apps; - }, - - _getMessage: function(type, uri, options = {}) { - let mimeType = options.mimeType; - if (uri && mimeType == undefined) { - mimeType = ContentAreaUtils.getMIMETypeForURI(uri) || ""; - } - - return { - type: type, - mime: mimeType, - action: options.action || "", // empty action string defaults to android.intent.action.VIEW - url: uri ? uri.spec : "", - packageName: options.packageName || "", - className: options.className || "" - }; - }, - - _launchApp: function launchApp(app, uri, callback) { - if (callback) { - let msg = this._getMessage("Intent:OpenForResult", uri, { - packageName: app.packageName, - className: app.activityName - }); - - Messaging.sendRequestForResult(msg).then(callback); - } else { - let msg = this._getMessage("Intent:Open", uri, { - packageName: app.packageName, - className: app.activityName - }); - - Messaging.sendRequest(msg); - } - }, - - _sendMessageSync: function(msg) { - let res = null; - Messaging.sendRequestForResult(msg).then(function(data) { - res = data; - }); - - let thread = Services.tm.currentThread; - while (res == null) { - thread.processNextEvent(true); - } - - return res; - }, -}; diff --git a/mobile/android/modules/Home.jsm b/mobile/android/modules/Home.jsm deleted file mode 100644 index e77d35dbd..000000000 --- a/mobile/android/modules/Home.jsm +++ /dev/null @@ -1,487 +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"; - -this.EXPORTED_SYMBOLS = ["Home"]; - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/SharedPreferences.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); - -// Keep this in sync with the constant defined in PanelAuthCache.java -const PREFS_PANEL_AUTH_PREFIX = "home_panels_auth_"; - -// Default weight for a banner message. -const DEFAULT_WEIGHT = 100; - -// See bug 915424 -function resolveGeckoURI(aURI) { - if (!aURI) - throw "Can't resolve an empty uri"; - - if (aURI.startsWith("chrome://")) { - let registry = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci["nsIChromeRegistry"]); - return registry.convertChromeURL(Services.io.newURI(aURI, null, null)).spec; - } else if (aURI.startsWith("resource://")) { - let handler = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler); - return handler.resolveURI(Services.io.newURI(aURI, null, null)); - } - return aURI; -} - -function BannerMessage(options) { - let uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); - this.id = uuidgen.generateUUID().toString(); - - if ("text" in options && options.text != null) - this.text = options.text; - - if ("icon" in options && options.icon != null) - this.iconURI = resolveGeckoURI(options.icon); - - if ("onshown" in options && typeof options.onshown === "function") - this.onshown = options.onshown; - - if ("onclick" in options && typeof options.onclick === "function") - this.onclick = options.onclick; - - if ("ondismiss" in options && typeof options.ondismiss === "function") - this.ondismiss = options.ondismiss; - - let weight = parseInt(options.weight, 10); - this.weight = weight > 0 ? weight : DEFAULT_WEIGHT; -} - -// We need this object to have access to the HomeBanner -// private members without leaking it outside Home.jsm. -var HomeBannerMessageHandlers; - -var HomeBanner = (function () { - // Whether there is a "HomeBanner:Get" request we couldn't fulfill. - let _pendingRequest = false; - - // Functions used to handle messages sent from Java. - HomeBannerMessageHandlers = { - "HomeBanner:Get": function handleBannerGet(data) { - if (Object.keys(_messages).length > 0) { - _sendBannerData(); - } else { - _pendingRequest = true; - } - } - }; - - // Holds the messages that will rotate through the banner. - let _messages = {}; - - // Choose a random message from the set of messages, biasing towards those with higher weight. - // Weight logic copied from desktop snippets: - // https://github.com/mozilla/snippets-service/blob/7d80edb8b1cddaed075275c2fc7cdf69a10f4003/snippets/base/templates/base/includes/snippet_js.html#L119 - let _sendBannerData = function() { - let totalWeight = 0; - for (let key in _messages) { - let message = _messages[key]; - totalWeight += message.weight; - message.totalWeight = totalWeight; - } - - let threshold = Math.random() * totalWeight; - for (let key in _messages) { - let message = _messages[key]; - if (threshold < message.totalWeight) { - Messaging.sendRequest({ - type: "HomeBanner:Data", - id: message.id, - text: message.text, - iconURI: message.iconURI - }); - return; - } - } - }; - - let _handleShown = function(id) { - let message = _messages[id]; - if (message.onshown) - message.onshown(); - }; - - let _handleClick = function(id) { - let message = _messages[id]; - if (message.onclick) - message.onclick(); - }; - - let _handleDismiss = function(id) { - let message = _messages[id]; - if (message.ondismiss) - message.ondismiss(); - }; - - return Object.freeze({ - observe: function(subject, topic, data) { - switch(topic) { - case "HomeBanner:Shown": - _handleShown(data); - break; - - case "HomeBanner:Click": - _handleClick(data); - break; - - case "HomeBanner:Dismiss": - _handleDismiss(data); - break; - } - }, - - /** - * Adds a new banner message to the rotation. - * - * @return id Unique identifer for the message. - */ - add: function(options) { - let message = new BannerMessage(options); - _messages[message.id] = message; - - // If this is the first message we're adding, add - // observers to listen for requests from the Java UI. - if (Object.keys(_messages).length == 1) { - Services.obs.addObserver(this, "HomeBanner:Shown", false); - Services.obs.addObserver(this, "HomeBanner:Click", false); - Services.obs.addObserver(this, "HomeBanner:Dismiss", false); - - // Send a message to Java if there's a pending "HomeBanner:Get" request. - if (_pendingRequest) { - _pendingRequest = false; - _sendBannerData(); - } - } - - return message.id; - }, - - /** - * Removes a banner message from the rotation. - * - * @param id The id of the message to remove. - */ - remove: function(id) { - if (!(id in _messages)) { - throw "Home.banner: Can't remove message that doesn't exist: id = " + id; - } - - delete _messages[id]; - - // If there are no more messages, remove the observers. - if (Object.keys(_messages).length == 0) { - Services.obs.removeObserver(this, "HomeBanner:Shown"); - Services.obs.removeObserver(this, "HomeBanner:Click"); - Services.obs.removeObserver(this, "HomeBanner:Dismiss"); - } - } - }); -})(); - -// We need this object to have access to the HomePanels -// private members without leaking it outside Home.jsm. -var HomePanelsMessageHandlers; - -var HomePanels = (function () { - // Functions used to handle messages sent from Java. - HomePanelsMessageHandlers = { - - "HomePanels:Get": function handlePanelsGet(data) { - data = JSON.parse(data); - - let requestId = data.requestId; - let ids = data.ids || null; - - let panels = []; - for (let id in _registeredPanels) { - // Null ids means we want to fetch all available panels - if (ids == null || ids.indexOf(id) >= 0) { - try { - panels.push(_generatePanel(id)); - } catch(e) { - Cu.reportError("Home.panels: Invalid options, panel.id = " + id + ": " + e); - } - } - } - - Messaging.sendRequest({ - type: "HomePanels:Data", - panels: panels, - requestId: requestId - }); - }, - - "HomePanels:Authenticate": function handlePanelsAuthenticate(id) { - // Generate panel options to get auth handler. - let options = _registeredPanels[id](); - if (!options.auth) { - throw "Home.panels: Invalid auth for panel.id = " + id; - } - if (!options.auth.authenticate || typeof options.auth.authenticate !== "function") { - throw "Home.panels: Invalid auth authenticate function: panel.id = " + this.id; - } - options.auth.authenticate(); - }, - - "HomePanels:RefreshView": function handlePanelsRefreshView(data) { - data = JSON.parse(data); - - let options = _registeredPanels[data.panelId](); - let view = options.views[data.viewIndex]; - - if (!view) { - throw "Home.panels: Invalid view for panel.id = " + data.panelId - + ", view.index = " + data.viewIndex; - } - - if (!view.onrefresh || typeof view.onrefresh !== "function") { - throw "Home.panels: Invalid onrefresh for panel.id = " + data.panelId - + ", view.index = " + data.viewIndex; - } - - view.onrefresh(); - }, - - "HomePanels:Installed": function handlePanelsInstalled(id) { - _assertPanelExists(id); - - let options = _registeredPanels[id](); - if (!options.oninstall) { - return; - } - if (typeof options.oninstall !== "function") { - throw "Home.panels: Invalid oninstall function: panel.id = " + this.id; - } - options.oninstall(); - }, - - "HomePanels:Uninstalled": function handlePanelsUninstalled(id) { - _assertPanelExists(id); - - let options = _registeredPanels[id](); - if (!options.onuninstall) { - return; - } - if (typeof options.onuninstall !== "function") { - throw "Home.panels: Invalid onuninstall function: panel.id = " + this.id; - } - options.onuninstall(); - } - }; - - // Holds the current set of registered panels that can be - // installed, updated, uninstalled, or unregistered. It maps - // panel ids with the functions that dynamically generate - // their respective panel options. This is used to retrieve - // the current list of available panels in the system. - // See HomePanels:Get handler. - let _registeredPanels = {}; - - // Valid layouts for a panel. - let Layout = Object.freeze({ - FRAME: "frame" - }); - - // Valid types of views for a dataset. - let View = Object.freeze({ - LIST: "list", - GRID: "grid" - }); - - // Valid item types for a panel view. - let Item = Object.freeze({ - ARTICLE: "article", - IMAGE: "image", - ICON: "icon" - }); - - // Valid item handlers for a panel view. - let ItemHandler = Object.freeze({ - BROWSER: "browser", - INTENT: "intent" - }); - - function Panel(id, options) { - this.id = id; - this.title = options.title; - this.layout = options.layout; - this.views = options.views; - this.default = !!options.default; - - if (!this.id || !this.title) { - throw "Home.panels: Can't create a home panel without an id and title!"; - } - - if (!this.layout) { - // Use FRAME layout by default - this.layout = Layout.FRAME; - } else if (!_valueExists(Layout, this.layout)) { - throw "Home.panels: Invalid layout for panel: panel.id = " + this.id + ", panel.layout =" + this.layout; - } - - for (let view of this.views) { - if (!_valueExists(View, view.type)) { - throw "Home.panels: Invalid view type: panel.id = " + this.id + ", view.type = " + view.type; - } - - if (!view.itemType) { - if (view.type == View.LIST) { - // Use ARTICLE item type by default in LIST views - view.itemType = Item.ARTICLE; - } else if (view.type == View.GRID) { - // Use IMAGE item type by default in GRID views - view.itemType = Item.IMAGE; - } - } else if (!_valueExists(Item, view.itemType)) { - throw "Home.panels: Invalid item type: panel.id = " + this.id + ", view.itemType = " + view.itemType; - } - - if (!view.itemHandler) { - // Use BROWSER item handler by default - view.itemHandler = ItemHandler.BROWSER; - } else if (!_valueExists(ItemHandler, view.itemHandler)) { - throw "Home.panels: Invalid item handler: panel.id = " + this.id + ", view.itemHandler = " + view.itemHandler; - } - - if (!view.dataset) { - throw "Home.panels: No dataset provided for view: panel.id = " + this.id + ", view.type = " + view.type; - } - - if (view.onrefresh) { - view.refreshEnabled = true; - } - } - - if (options.auth) { - if (!options.auth.messageText) { - throw "Home.panels: Invalid auth messageText: panel.id = " + this.id; - } - if (!options.auth.buttonText) { - throw "Home.panels: Invalid auth buttonText: panel.id = " + this.id; - } - - this.authConfig = { - messageText: options.auth.messageText, - buttonText: options.auth.buttonText - }; - - // Include optional image URL if it is specified. - if (options.auth.imageUrl) { - this.authConfig.imageUrl = options.auth.imageUrl; - } - } - - if (options.position >= 0) { - this.position = options.position; - } - } - - let _generatePanel = function(id) { - let options = _registeredPanels[id](); - return new Panel(id, options); - }; - - // Helper function used to see if a value is in an object. - let _valueExists = function(obj, value) { - for (let key in obj) { - if (obj[key] == value) { - return true; - } - } - return false; - }; - - let _assertPanelExists = function(id) { - if (!(id in _registeredPanels)) { - throw "Home.panels: Panel doesn't exist: id = " + id; - } - }; - - return Object.freeze({ - Layout: Layout, - View: View, - Item: Item, - ItemHandler: ItemHandler, - - register: function(id, optionsCallback) { - // Bail if the panel already exists - if (id in _registeredPanels) { - throw "Home.panels: Panel already exists: id = " + id; - } - - if (!optionsCallback || typeof optionsCallback !== "function") { - throw "Home.panels: Panel callback must be a function: id = " + id; - } - - _registeredPanels[id] = optionsCallback; - }, - - unregister: function(id) { - _assertPanelExists(id); - - delete _registeredPanels[id]; - }, - - install: function(id) { - _assertPanelExists(id); - - Messaging.sendRequest({ - type: "HomePanels:Install", - panel: _generatePanel(id) - }); - }, - - uninstall: function(id) { - _assertPanelExists(id); - - Messaging.sendRequest({ - type: "HomePanels:Uninstall", - id: id - }); - }, - - update: function(id) { - _assertPanelExists(id); - - Messaging.sendRequest({ - type: "HomePanels:Update", - panel: _generatePanel(id) - }); - }, - - setAuthenticated: function(id, isAuthenticated) { - _assertPanelExists(id); - - let authKey = PREFS_PANEL_AUTH_PREFIX + id; - let sharedPrefs = SharedPreferences.forProfile(); - sharedPrefs.setBoolPref(authKey, isAuthenticated); - } - }); -})(); - -// Public API -this.Home = Object.freeze({ - banner: HomeBanner, - panels: HomePanels, - - // Lazy notification observer registered in browser.js - observe: function(subject, topic, data) { - if (topic in HomeBannerMessageHandlers) { - HomeBannerMessageHandlers[topic](data); - } else if (topic in HomePanelsMessageHandlers) { - HomePanelsMessageHandlers[topic](data); - } else { - Cu.reportError("Home.observe: message handler not found for topic: " + topic); - } - } -}); diff --git a/mobile/android/modules/HomeProvider.jsm b/mobile/android/modules/HomeProvider.jsm deleted file mode 100644 index bca8fa526..000000000 --- a/mobile/android/modules/HomeProvider.jsm +++ /dev/null @@ -1,407 +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"; - -this.EXPORTED_SYMBOLS = [ "HomeProvider" ]; - -const { utils: Cu, classes: Cc, interfaces: Ci } = Components; - -Cu.import("resource://gre/modules/Messaging.jsm"); -Cu.import("resource://gre/modules/osfile.jsm"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Sqlite.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -/* - * SCHEMA_VERSION history: - * 1: Create HomeProvider (bug 942288) - * 2: Add filter column to items table (bug 942295/975841) - * 3: Add background_color and background_url columns (bug 1157539) - */ -const SCHEMA_VERSION = 3; - -// The maximum number of items you can attempt to save at once. -const MAX_SAVE_COUNT = 100; - -XPCOMUtils.defineLazyGetter(this, "DB_PATH", function() { - return OS.Path.join(OS.Constants.Path.profileDir, "home.sqlite"); -}); - -const PREF_STORAGE_LAST_SYNC_TIME_PREFIX = "home.storage.lastSyncTime."; -const PREF_SYNC_UPDATE_MODE = "home.sync.updateMode"; -const PREF_SYNC_CHECK_INTERVAL_SECS = "home.sync.checkIntervalSecs"; - -XPCOMUtils.defineLazyGetter(this, "gSyncCheckIntervalSecs", function() { - return Services.prefs.getIntPref(PREF_SYNC_CHECK_INTERVAL_SECS); -}); - -XPCOMUtils.defineLazyServiceGetter(this, - "gUpdateTimerManager", "@mozilla.org/updates/timer-manager;1", "nsIUpdateTimerManager"); - -/** - * All SQL statements should be defined here. - */ -const SQL = { - createItemsTable: - "CREATE TABLE items (" + - "_id INTEGER PRIMARY KEY AUTOINCREMENT, " + - "dataset_id TEXT NOT NULL, " + - "url TEXT," + - "title TEXT," + - "description TEXT," + - "image_url TEXT," + - "background_color TEXT," + - "background_url TEXT," + - "filter TEXT," + - "created INTEGER" + - ")", - - dropItemsTable: - "DROP TABLE items", - - insertItem: - "INSERT INTO items (dataset_id, url, title, description, image_url, background_color, background_url, filter, created) " + - "VALUES (:dataset_id, :url, :title, :description, :image_url, :background_color, :background_url, :filter, :created)", - - deleteFromDataset: - "DELETE FROM items WHERE dataset_id = :dataset_id", - - addColumnBackgroundColor: - "ALTER TABLE items ADD COLUMN background_color TEXT", - - addColumnBackgroundUrl: - "ALTER TABLE items ADD COLUMN background_url TEXT", -} - -/** - * Technically this function checks to see if the user is on a local network, - * but we express this as "wifi" to the user. - */ -function isUsingWifi() { - let network = Cc["@mozilla.org/network/network-link-service;1"].getService(Ci.nsINetworkLinkService); - return (network.linkType === Ci.nsINetworkLinkService.LINK_TYPE_WIFI || network.linkType === Ci.nsINetworkLinkService.LINK_TYPE_ETHERNET); -} - -function getNowInSeconds() { - return Math.round(Date.now() / 1000); -} - -function getLastSyncPrefName(datasetId) { - return PREF_STORAGE_LAST_SYNC_TIME_PREFIX + datasetId; -} - -// Whether or not we've registered an update timer. -var gTimerRegistered = false; - -// Map of datasetId -> { interval: <integer>, callback: <function> } -var gSyncCallbacks = {}; - -/** - * nsITimerCallback implementation. Checks to see if it's time to sync any registered datasets. - * - * @param timer The timer which has expired. - */ -function syncTimerCallback(timer) { - for (let datasetId in gSyncCallbacks) { - let lastSyncTime = 0; - try { - lastSyncTime = Services.prefs.getIntPref(getLastSyncPrefName(datasetId)); - } catch(e) { } - - let now = getNowInSeconds(); - let { interval: interval, callback: callback } = gSyncCallbacks[datasetId]; - - if (lastSyncTime < now - interval) { - let success = HomeProvider.requestSync(datasetId, callback); - if (success) { - Services.prefs.setIntPref(getLastSyncPrefName(datasetId), now); - } - } - } -} - -this.HomeStorage = function(datasetId) { - this.datasetId = datasetId; -}; - -this.ValidationError = function(message) { - this.name = "ValidationError"; - this.message = message; -}; -ValidationError.prototype = new Error(); -ValidationError.prototype.constructor = ValidationError; - -this.HomeProvider = Object.freeze({ - ValidationError: ValidationError, - - /** - * Returns a storage associated with a given dataset identifer. - * - * @param datasetId - * (string) Unique identifier for the dataset. - * - * @return HomeStorage - */ - getStorage: function(datasetId) { - return new HomeStorage(datasetId); - }, - - /** - * Checks to see if it's an appropriate time to sync. - * - * @param datasetId Unique identifier for the dataset to sync. - * @param callback Function to call when it's time to sync, called with datasetId as a parameter. - * - * @return boolean Whether or not we were able to sync. - */ - requestSync: function(datasetId, callback) { - // Make sure it's a good time to sync. - if ((Services.prefs.getIntPref(PREF_SYNC_UPDATE_MODE) === 1) && !isUsingWifi()) { - Cu.reportError("HomeProvider: Failed to sync because device is not on a local network"); - return false; - } - - callback(datasetId); - return true; - }, - - /** - * Specifies that a sync should be requested for the given dataset and update interval. - * - * @param datasetId Unique identifier for the dataset to sync. - * @param interval Update interval in seconds. By default, this is throttled to 3600 seconds (1 hour). - * @param callback Function to call when it's time to sync, called with datasetId as a parameter. - */ - addPeriodicSync: function(datasetId, interval, callback) { - // Warn developers if they're expecting more frequent notifications that we allow. - if (interval < gSyncCheckIntervalSecs) { - Cu.reportError("HomeProvider: Warning for dataset " + datasetId + - " : Sync notifications are throttled to " + gSyncCheckIntervalSecs + " seconds"); - } - - gSyncCallbacks[datasetId] = { - interval: interval, - callback: callback - }; - - if (!gTimerRegistered) { - gUpdateTimerManager.registerTimer("home-provider-sync-timer", syncTimerCallback, gSyncCheckIntervalSecs); - gTimerRegistered = true; - } - }, - - /** - * Removes a periodic sync timer. - * - * @param datasetId Dataset to sync. - */ - removePeriodicSync: function(datasetId) { - delete gSyncCallbacks[datasetId]; - Services.prefs.clearUserPref(getLastSyncPrefName(datasetId)); - // You can't unregister a update timer, so we don't try to do that. - } -}); - -var gDatabaseEnsured = false; - -/** - * Creates the database schema. - */ -function createDatabase(db) { - return Task.spawn(function create_database_task() { - yield db.execute(SQL.createItemsTable); - }); -} - -/** - * Migrates the database schema to a new version. - */ -function upgradeDatabase(db, oldVersion, newVersion) { - return Task.spawn(function upgrade_database_task() { - switch (oldVersion) { - case 1: - // Migration from v1 to latest: - // Recreate the items table discarding any - // existing data. - yield db.execute(SQL.dropItemsTable); - yield db.execute(SQL.createItemsTable); - break; - - case 2: - // Migration from v2 to latest: - // Add new columns: background_color, background_url - yield db.execute(SQL.addColumnBackgroundColor); - yield db.execute(SQL.addColumnBackgroundUrl); - break; - } - }); -} - -/** - * Opens a database connection and makes sure that the database schema version - * is correct, performing migrations if necessary. Consumers should be sure - * to close any database connections they open. - * - * @return Promise - * @resolves Handle on an opened SQLite database. - */ -function getDatabaseConnection() { - return Task.spawn(function get_database_connection_task() { - let db = yield Sqlite.openConnection({ path: DB_PATH }); - if (gDatabaseEnsured) { - throw new Task.Result(db); - } - - try { - // Check to see if we need to perform any migrations. - let dbVersion = parseInt(yield db.getSchemaVersion()); - - // getSchemaVersion() returns a 0 int if the schema - // version is undefined. - if (dbVersion === 0) { - yield createDatabase(db); - } else if (dbVersion < SCHEMA_VERSION) { - yield upgradeDatabase(db, dbVersion, SCHEMA_VERSION); - } - - yield db.setSchemaVersion(SCHEMA_VERSION); - } catch(e) { - // Close the DB connection before passing the exception to the consumer. - yield db.close(); - throw e; - } - - gDatabaseEnsured = true; - throw new Task.Result(db); - }); -} - -/** - * Validates an item to be saved to the DB. - * - * @param item - * (object) item object to be validated. - */ -function validateItem(datasetId, item) { - if (!item.url) { - throw new ValidationError('HomeStorage: All rows must have an URL: datasetId = ' + - datasetId); - } - - if (!item.image_url && !item.title && !item.description) { - throw new ValidationError('HomeStorage: All rows must have at least an image URL, ' + - 'or a title or a description: datasetId = ' + datasetId); - } -} - -var gRefreshTimers = {}; - -/** - * Sends a message to Java to refresh the given dataset. Delays sending - * messages to avoid successive refreshes, which can result in flashing views. - */ -function refreshDataset(datasetId) { - // Bail if there's already a refresh timer waiting to fire - if (gRefreshTimers[datasetId]) { - return; - } - - let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.initWithCallback(function(timer) { - delete gRefreshTimers[datasetId]; - - Messaging.sendRequest({ - type: "HomePanels:RefreshDataset", - datasetId: datasetId - }); - }, 100, Ci.nsITimer.TYPE_ONE_SHOT); - - gRefreshTimers[datasetId] = timer; -} - -HomeStorage.prototype = { - /** - * Saves data rows to the DB. - * - * @param data - * An array of JS objects represnting row items to save. - * Each object may have the following properties: - * - url (string) - * - title (string) - * - description (string) - * - image_url (string) - * - filter (string) - * @param options - * A JS object holding additional cofiguration properties. - * The following properties are currently supported: - * - replace (boolean): Whether or not to replace existing items. - * - * @return Promise - * @resolves When the operation has completed. - */ - save: function(data, options) { - if (data && data.length > MAX_SAVE_COUNT) { - throw "save failed for dataset = " + this.datasetId + - ": you cannot save more than " + MAX_SAVE_COUNT + " items at once"; - } - - return Task.spawn(function save_task() { - let db = yield getDatabaseConnection(); - try { - yield db.executeTransaction(function save_transaction() { - if (options && options.replace) { - yield db.executeCached(SQL.deleteFromDataset, { dataset_id: this.datasetId }); - } - - // Insert data into DB. - for (let item of data) { - validateItem(this.datasetId, item); - - // XXX: Directly pass item as params? More validation for item? - let params = { - dataset_id: this.datasetId, - url: item.url, - title: item.title, - description: item.description, - image_url: item.image_url, - background_color: item.background_color, - background_url: item.background_url, - filter: item.filter, - created: Date.now() - }; - yield db.executeCached(SQL.insertItem, params); - } - }.bind(this)); - } finally { - yield db.close(); - } - - refreshDataset(this.datasetId); - }.bind(this)); - }, - - /** - * Deletes all rows associated with this storage. - * - * @return Promise - * @resolves When the operation has completed. - */ - deleteAll: function() { - return Task.spawn(function delete_all_task() { - let db = yield getDatabaseConnection(); - try { - let params = { dataset_id: this.datasetId }; - yield db.executeCached(SQL.deleteFromDataset, params); - } finally { - yield db.close(); - } - - refreshDataset(this.datasetId); - }.bind(this)); - } -}; diff --git a/mobile/android/modules/JNI.jsm b/mobile/android/modules/JNI.jsm deleted file mode 100644 index 1e10b9cfb..000000000 --- a/mobile/android/modules/JNI.jsm +++ /dev/null @@ -1,1167 +0,0 @@ -// JavaScript to Java bridge via the Java Native Interface -// Allows calling into Android SDK from JavaScript in Firefox Add-On. -// Released into the public domain. -// C. Scott Ananian <cscott@laptop.org> (http://cscott.net) - -// NOTE: All changes to this file should first be pushed to the repo at: -// https://github.com/cscott/skeleton-addon-fxandroid/tree/jni - -var EXPORTED_SYMBOLS = ["JNI","android_log"]; - -Components.utils.import("resource://gre/modules/ctypes.jsm") - -var liblog = ctypes.open('liblog.so'); -var android_log = liblog.declare("__android_log_write", - ctypes.default_abi, - ctypes.int32_t, - ctypes.int32_t, - ctypes.char.ptr, - ctypes.char.ptr); - -var libxul = ctypes.open('libxul.so'); - -var jenvptr = ctypes.voidptr_t; -var jclass = ctypes.voidptr_t; -var jobject = ctypes.voidptr_t; -var jvalue = ctypes.voidptr_t; -var jmethodid = ctypes.voidptr_t; -var jfieldid = ctypes.voidptr_t; - -var jboolean = ctypes.uint8_t; -var jbyte = ctypes.int8_t; -var jchar = ctypes.uint16_t; -var jshort = ctypes.int16_t; -var jint = ctypes.int32_t; -var jlong = ctypes.int64_t; -var jfloat = ctypes.float32_t; -var jdouble = ctypes.float64_t; - -var jsize = jint; -var jstring = jobject; -var jarray = jobject; -var jthrowable = jobject; - -var JNINativeInterface = new ctypes.StructType( - "JNINativeInterface", - [{reserved0: ctypes.voidptr_t}, - {reserved1: ctypes.voidptr_t}, - {reserved2: ctypes.voidptr_t}, - {reserved3: ctypes.voidptr_t}, - {GetVersion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.int32_t, - [ctypes.voidptr_t]).ptr}, - {DefineClass: new ctypes.FunctionType(ctypes.default_abi, - jclass, - [jenvptr, ctypes.char.ptr, jobject, - jbyte.array(), jsize]).ptr}, - {FindClass: new ctypes.FunctionType(ctypes.default_abi, - jclass, - [jenvptr, - ctypes.char.ptr]).ptr}, - {FromReflectedMethod: new ctypes.FunctionType(ctypes.default_abi, - jmethodid, - [jenvptr, jobject]).ptr}, - {FromReflectedField: new ctypes.FunctionType(ctypes.default_abi, - jfieldid, - [jenvptr, jobject]).ptr}, - {ToReflectedMethod: new ctypes.FunctionType(ctypes.default_abi, - jobject, - [jenvptr, jclass, - jmethodid]).ptr}, - {GetSuperclass: new ctypes.FunctionType(ctypes.default_abi, - jclass, [jenvptr, jclass]).ptr}, - {IsAssignableFrom: new ctypes.FunctionType(ctypes.default_abi, - jboolean, - [jenvptr, jclass, jclass]).ptr}, - {ToReflectedField: new ctypes.FunctionType(ctypes.default_abi, - jobject, - [jenvptr, jclass, - jfieldid]).ptr}, - {Throw: new ctypes.FunctionType(ctypes.default_abi, - jint, [jenvptr, jthrowable]).ptr}, - {ThrowNew: new ctypes.FunctionType(ctypes.default_abi, - jint, [jenvptr, jclass, - ctypes.char.ptr]).ptr}, - {ExceptionOccurred: new ctypes.FunctionType(ctypes.default_abi, - jthrowable, [jenvptr]).ptr}, - {ExceptionDescribe: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, [jenvptr]).ptr}, - {ExceptionClear: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, [jenvptr]).ptr}, - {FatalError: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, - ctypes.char.ptr]).ptr}, - {PushLocalFrame: new ctypes.FunctionType(ctypes.default_abi, - jint, - [jenvptr, jint]).ptr}, - {PopLocalFrame: new ctypes.FunctionType(ctypes.default_abi, - jobject, - [jenvptr, jobject]).ptr}, - {NewGlobalRef: new ctypes.FunctionType(ctypes.default_abi, - jobject, [jenvptr, jobject]).ptr}, - {DeleteGlobalRef: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, - jobject]).ptr}, - {DeleteLocalRef: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, - jobject]).ptr}, - {IsSameObject: new ctypes.FunctionType(ctypes.default_abi, - jboolean, - [jenvptr, jobject, jobject]).ptr}, - {NewLocalRef: new ctypes.FunctionType(ctypes.default_abi, - jobject, [jenvptr, jobject]).ptr}, - {EnsureLocalCapacity: new ctypes.FunctionType(ctypes.default_abi, - jint, [jenvptr, jint]).ptr}, - {AllocObject: new ctypes.FunctionType(ctypes.default_abi, - jobject, [jenvptr, jclass]).ptr}, - {NewObject: new ctypes.FunctionType(ctypes.default_abi, - jobject, - [jenvptr, - jclass, - jmethodid, - "..."]).ptr}, - {NewObjectV: ctypes.voidptr_t}, - {NewObjectA: ctypes.voidptr_t}, - {GetObjectClass: new ctypes.FunctionType(ctypes.default_abi, - jclass, - [jenvptr, jobject]).ptr}, - {IsInstanceOf: new ctypes.FunctionType(ctypes.default_abi, - jboolean, - [jenvptr, jobject, jclass]).ptr}, - {GetMethodID: new ctypes.FunctionType(ctypes.default_abi, - jmethodid, - [jenvptr, - jclass, - ctypes.char.ptr, - ctypes.char.ptr]).ptr}, - {CallObjectMethod: new ctypes.FunctionType(ctypes.default_abi, - jobject, - [jenvptr, jobject, jmethodid, - "..."]).ptr}, - {CallObjectMethodV: ctypes.voidptr_t}, - {CallObjectMethodA: ctypes.voidptr_t}, - {CallBooleanMethod: new ctypes.FunctionType(ctypes.default_abi, - jboolean, - [jenvptr, - jobject, - jmethodid, - "..."]).ptr}, - {CallBooleanMethodV: ctypes.voidptr_t}, - {CallBooleanMethodA: ctypes.voidptr_t}, - {CallByteMethod: new ctypes.FunctionType(ctypes.default_abi, - jbyte, - [jenvptr, - jobject, - jmethodid, - "..."]).ptr}, - {CallByteMethodV: ctypes.voidptr_t}, - {CallByteMethodA: ctypes.voidptr_t}, - {CallCharMethod: new ctypes.FunctionType(ctypes.default_abi, - jchar, - [jenvptr, - jobject, - jmethodid, - "..."]).ptr}, - {CallCharMethodV: ctypes.voidptr_t}, - {CallCharMethodA: ctypes.voidptr_t}, - {CallShortMethod: new ctypes.FunctionType(ctypes.default_abi, - jshort, - [jenvptr, - jobject, - jmethodid, - "..."]).ptr}, - {CallShortMethodV: ctypes.voidptr_t}, - {CallShortMethodA: ctypes.voidptr_t}, - {CallIntMethod: new ctypes.FunctionType(ctypes.default_abi, - jint, - [jenvptr, - jobject, - jmethodid, - "..."]).ptr}, - {CallIntMethodV: ctypes.voidptr_t}, - {CallIntMethodA: ctypes.voidptr_t}, - {CallLongMethod: new ctypes.FunctionType(ctypes.default_abi, - jlong, - [jenvptr, - jobject, - jmethodid, - "..."]).ptr}, - {CallLongMethodV: ctypes.voidptr_t}, - {CallLongMethodA: ctypes.voidptr_t}, - {CallFloatMethod: new ctypes.FunctionType(ctypes.default_abi, - jfloat, - [jenvptr, - jobject, - jmethodid, - "..."]).ptr}, - {CallFloatMethodV: ctypes.voidptr_t}, - {CallFloatMethodA: ctypes.voidptr_t}, - {CallDoubleMethod: new ctypes.FunctionType(ctypes.default_abi, - jdouble, - [jenvptr, - jobject, - jmethodid, - "..."]).ptr}, - {CallDoubleMethodV: ctypes.voidptr_t}, - {CallDoubleMethodA: ctypes.voidptr_t}, - {CallVoidMethod: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, - jobject, - jmethodid, - "..."]).ptr}, - {CallVoidMethodV: ctypes.voidptr_t}, - {CallVoidMethodA: ctypes.voidptr_t}, - {CallNonvirtualObjectMethod: new ctypes.FunctionType(ctypes.default_abi, - jobject, - [jenvptr, jobject, - jclass, jmethodid, - "..."]).ptr}, - {CallNonvirtualObjectMethodV: ctypes.voidptr_t}, - {CallNonvirtualObjectMethodA: ctypes.voidptr_t}, - {CallNonvirtualBooleanMethod: new ctypes.FunctionType(ctypes.default_abi, - jboolean, - [jenvptr, jobject, - jclass, jmethodid, - "..."]).ptr}, - {CallNonvirtualBooleanMethodV: ctypes.voidptr_t}, - {CallNonvirtualBooleanMethodA: ctypes.voidptr_t}, - {CallNonvirtualByteMethod: new ctypes.FunctionType(ctypes.default_abi, - jbyte, - [jenvptr, jobject, - jclass, jmethodid, - "..."]).ptr}, - {CallNonvirtualByteMethodV: ctypes.voidptr_t}, - {CallNonvirtualByteMethodA: ctypes.voidptr_t}, - {CallNonvirtualCharMethod: new ctypes.FunctionType(ctypes.default_abi, - jchar, - [jenvptr, jobject, - jclass, jmethodid, - "..."]).ptr}, - {CallNonvirtualCharMethodV: ctypes.voidptr_t}, - {CallNonvirtualCharMethodA: ctypes.voidptr_t}, - {CallNonvirtualShortMethod: new ctypes.FunctionType(ctypes.default_abi, - jshort, - [jenvptr, jobject, - jclass, jmethodid, - "..."]).ptr}, - {CallNonvirtualShortMethodV: ctypes.voidptr_t}, - {CallNonvirtualShortMethodA: ctypes.voidptr_t}, - {CallNonvirtualIntMethod: new ctypes.FunctionType(ctypes.default_abi, - jint, - [jenvptr, jobject, - jclass, jmethodid, - "..."]).ptr}, - {CallNonvirtualIntMethodV: ctypes.voidptr_t}, - {CallNonvirtualIntMethodA: ctypes.voidptr_t}, - {CallNonvirtualLongMethod: new ctypes.FunctionType(ctypes.default_abi, - jlong, - [jenvptr, jobject, - jclass, jmethodid, - "..."]).ptr}, - {CallNonvirtualLongMethodV: ctypes.voidptr_t}, - {CallNonvirtualLongMethodA: ctypes.voidptr_t}, - {CallNonvirtualFloatMethod: new ctypes.FunctionType(ctypes.default_abi, - jfloat, - [jenvptr, jobject, - jclass, jmethodid, - "..."]).ptr}, - {CallNonvirtualFloatMethodV: ctypes.voidptr_t}, - {CallNonvirtualFloatMethodA: ctypes.voidptr_t}, - {CallNonvirtualDoubleMethod: new ctypes.FunctionType(ctypes.default_abi, - jdouble, - [jenvptr, jobject, - jclass, jmethodid, - "..."]).ptr}, - {CallNonvirtualDoubleMethodV: ctypes.voidptr_t}, - {CallNonvirtualDoubleMethodA: ctypes.voidptr_t}, - {CallNonvirtualVoidMethod: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jobject, - jclass, jmethodid, - "..."]).ptr}, - {CallNonvirtualVoidMethodV: ctypes.voidptr_t}, - {CallNonvirtualVoidMethodA: ctypes.voidptr_t}, - {GetFieldID: new ctypes.FunctionType(ctypes.default_abi, - jfieldid, - [jenvptr, jclass, - ctypes.char.ptr, - ctypes.char.ptr]).ptr}, - {GetObjectField: new ctypes.FunctionType(ctypes.default_abi, - jobject, - [jenvptr, jobject, - jfieldid]).ptr}, - {GetBooleanField: new ctypes.FunctionType(ctypes.default_abi, - jboolean, - [jenvptr, jobject, - jfieldid]).ptr}, - {GetByteField: new ctypes.FunctionType(ctypes.default_abi, - jbyte, - [jenvptr, jobject, - jfieldid]).ptr}, - {GetCharField: new ctypes.FunctionType(ctypes.default_abi, - jchar, - [jenvptr, jobject, - jfieldid]).ptr}, - {GetShortField: new ctypes.FunctionType(ctypes.default_abi, - jshort, - [jenvptr, jobject, - jfieldid]).ptr}, - {GetIntField: new ctypes.FunctionType(ctypes.default_abi, - jint, - [jenvptr, jobject, - jfieldid]).ptr}, - {GetLongField: new ctypes.FunctionType(ctypes.default_abi, - jlong, - [jenvptr, jobject, - jfieldid]).ptr}, - {GetFloatField: new ctypes.FunctionType(ctypes.default_abi, - jfloat, - [jenvptr, jobject, - jfieldid]).ptr}, - {GetDoubleField: new ctypes.FunctionType(ctypes.default_abi, - jdouble, - [jenvptr, jobject, - jfieldid]).ptr}, - {SetObjectField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jobject, - jfieldid, jobject]).ptr}, - {SetBooleanField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jobject, - jfieldid, jboolean]).ptr}, - {SetByteField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jobject, - jfieldid, jbyte]).ptr}, - {SetCharField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jobject, - jfieldid, jchar]).ptr}, - {SetShortField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jobject, - jfieldid, jshort]).ptr}, - {SetIntField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jobject, - jfieldid, jint]).ptr}, - {SetLongField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jobject, - jfieldid, jlong]).ptr}, - {SetFloatField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jobject, - jfieldid, jfloat]).ptr}, - {SetDoubleField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jobject, - jfieldid, jdouble]).ptr}, - {GetStaticMethodID: new ctypes.FunctionType(ctypes.default_abi, - jmethodid, - [jenvptr, - jclass, - ctypes.char.ptr, - ctypes.char.ptr]).ptr}, - {CallStaticObjectMethod: new ctypes.FunctionType(ctypes.default_abi, - jobject, - [jenvptr, jclass, - jmethodid, - "..."]).ptr}, - {CallStaticObjectMethodV: ctypes.voidptr_t}, - {CallStaticObjectMethodA: ctypes.voidptr_t}, - {CallStaticBooleanMethod: new ctypes.FunctionType(ctypes.default_abi, - jboolean, - [jenvptr, jclass, - jmethodid, - "..."]).ptr}, - {CallStaticBooleanMethodV: ctypes.voidptr_t}, - {CallStaticBooleanMethodA: ctypes.voidptr_t}, - {CallStaticByteMethod: new ctypes.FunctionType(ctypes.default_abi, - jbyte, - [jenvptr, jclass, - jmethodid, - "..."]).ptr}, - {CallStaticByteMethodV: ctypes.voidptr_t}, - {CallStaticByteMethodA: ctypes.voidptr_t}, - {CallStaticCharMethod: new ctypes.FunctionType(ctypes.default_abi, - jchar, - [jenvptr, jclass, - jmethodid, - "..."]).ptr}, - {CallStaticCharMethodV: ctypes.voidptr_t}, - {CallStaticCharMethodA: ctypes.voidptr_t}, - {CallStaticShortMethod: new ctypes.FunctionType(ctypes.default_abi, - jshort, - [jenvptr, jclass, - jmethodid, - "..."]).ptr}, - {CallStaticShortMethodV: ctypes.voidptr_t}, - {CallStaticShortMethodA: ctypes.voidptr_t}, - {CallStaticIntMethod: new ctypes.FunctionType(ctypes.default_abi, - jint, - [jenvptr, jclass, - jmethodid, - "..."]).ptr}, - {CallStaticIntMethodV: ctypes.voidptr_t}, - {CallStaticIntMethodA: ctypes.voidptr_t}, - {CallStaticLongMethod: new ctypes.FunctionType(ctypes.default_abi, - jlong, - [jenvptr, jclass, - jmethodid, - "..."]).ptr}, - {CallStaticLongMethodV: ctypes.voidptr_t}, - {CallStaticLongMethodA: ctypes.voidptr_t}, - {CallStaticFloatMethod: new ctypes.FunctionType(ctypes.default_abi, - jfloat, - [jenvptr, jclass, - jmethodid, - "..."]).ptr}, - {CallStaticFloatMethodV: ctypes.voidptr_t}, - {CallStaticFloatMethodA: ctypes.voidptr_t}, - {CallStaticDoubleMethod: new ctypes.FunctionType(ctypes.default_abi, - jdouble, - [jenvptr, jclass, - jmethodid, - "..."]).ptr}, - {CallStaticDoubleMethodV: ctypes.voidptr_t}, - {CallStaticDoubleMethodA: ctypes.voidptr_t}, - {CallStaticVoidMethod: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jclass, - jmethodid, - "..."]).ptr}, - {CallStaticVoidMethodV: ctypes.voidptr_t}, - {CallStaticVoidMethodA: ctypes.voidptr_t}, - {GetStaticFieldID: new ctypes.FunctionType(ctypes.default_abi, - jfieldid, - [jenvptr, jclass, - ctypes.char.ptr, - ctypes.char.ptr]).ptr}, - {GetStaticObjectField: new ctypes.FunctionType(ctypes.default_abi, - jobject, - [jenvptr, jclass, - jfieldid]).ptr}, - {GetStaticBooleanField: new ctypes.FunctionType(ctypes.default_abi, - jboolean, - [jenvptr, jclass, - jfieldid]).ptr}, - {GetStaticByteField: new ctypes.FunctionType(ctypes.default_abi, - jbyte, - [jenvptr, jclass, - jfieldid]).ptr}, - {GetStaticCharField: new ctypes.FunctionType(ctypes.default_abi, - jchar, - [jenvptr, jclass, - jfieldid]).ptr}, - {GetStaticShortField: new ctypes.FunctionType(ctypes.default_abi, - jshort, - [jenvptr, jclass, - jfieldid]).ptr}, - {GetStaticIntField: new ctypes.FunctionType(ctypes.default_abi, - jint, - [jenvptr, jclass, - jfieldid]).ptr}, - {GetStaticLongField: new ctypes.FunctionType(ctypes.default_abi, - jlong, - [jenvptr, jclass, - jfieldid]).ptr}, - {GetStaticFloatField: new ctypes.FunctionType(ctypes.default_abi, - jfloat, - [jenvptr, jclass, - jfieldid]).ptr}, - {GetStaticDoubleField: new ctypes.FunctionType(ctypes.default_abi, - jdouble, - [jenvptr, jclass, - jfieldid]).ptr}, - {SetStaticObjectField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jclass, - jfieldid, jobject]).ptr}, - {SetStaticBooleanField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jclass, - jfieldid, jboolean]).ptr}, - {SetStaticByteField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jclass, - jfieldid, jbyte]).ptr}, - {SetStaticCharField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jclass, - jfieldid, jchar]).ptr}, - {SetStaticShortField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jclass, - jfieldid, jshort]).ptr}, - {SetStaticIntField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jclass, - jfieldid, jint]).ptr}, - {SetStaticLongField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jclass, - jfieldid, jlong]).ptr}, - {SetStaticFloatField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jclass, - jfieldid, jfloat]).ptr}, - {SetStaticDoubleField: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jclass, - jfieldid, jdouble]).ptr}, - - {NewString: new ctypes.FunctionType(ctypes.default_abi, - jstring, - [jenvptr, jchar.ptr, jsize]).ptr}, - {GetStringLength: new ctypes.FunctionType(ctypes.default_abi, - jsize, - [jenvptr, jstring]).ptr}, - {GetStringChars: new ctypes.FunctionType(ctypes.default_abi, - jchar.ptr, - [jenvptr, jstring, - jboolean.ptr]).ptr}, - {ReleaseStringChars: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jstring, - jchar.ptr]).ptr}, - - {NewStringUTF: new ctypes.FunctionType(ctypes.default_abi, - jstring, - [jenvptr, - ctypes.char.ptr]).ptr}, - {GetStringUTFLength: new ctypes.FunctionType(ctypes.default_abi, - jsize, - [jenvptr, jstring]).ptr}, - {GetStringUTFChars: new ctypes.FunctionType(ctypes.default_abi, - ctypes.char.ptr, - [jenvptr, jstring, - jboolean.ptr]).ptr}, - {ReleaseStringUTFChars: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jstring, - ctypes.char.ptr]).ptr}, - {GetArrayLength: new ctypes.FunctionType(ctypes.default_abi, - jsize, - [jenvptr, jarray]).ptr}, - {NewObjectArray: new ctypes.FunctionType(ctypes.default_abi, - jarray, - [jenvptr, jsize, - jclass, jobject]).ptr}, - {GetObjectArrayElement: new ctypes.FunctionType(ctypes.default_abi, - jobject, - [jenvptr, jarray, - jsize]).ptr}, - {SetObjectArrayElement: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jobject]).ptr}, - {NewBooleanArray: new ctypes.FunctionType(ctypes.default_abi, - jarray, - [jenvptr, jsize]).ptr}, - {NewByteArray: new ctypes.FunctionType(ctypes.default_abi, - jarray, - [jenvptr, jsize]).ptr}, - {NewCharArray: new ctypes.FunctionType(ctypes.default_abi, - jarray, - [jenvptr, jsize]).ptr}, - {NewShortArray: new ctypes.FunctionType(ctypes.default_abi, - jarray, - [jenvptr, jsize]).ptr}, - {NewIntArray: new ctypes.FunctionType(ctypes.default_abi, - jarray, - [jenvptr, jsize]).ptr}, - {NewLongArray: new ctypes.FunctionType(ctypes.default_abi, - jarray, - [jenvptr, jsize]).ptr}, - {NewFloatArray: new ctypes.FunctionType(ctypes.default_abi, - jarray, - [jenvptr, jsize]).ptr}, - {NewDoubleArray: new ctypes.FunctionType(ctypes.default_abi, - jarray, - [jenvptr, jsize]).ptr}, - {GetBooleanArrayElements: new ctypes.FunctionType(ctypes.default_abi, - jboolean.ptr, - [jenvptr, jarray, - jboolean.ptr]).ptr}, - {GetByteArrayElements: new ctypes.FunctionType(ctypes.default_abi, - jbyte.ptr, - [jenvptr, jarray, - jboolean.ptr]).ptr}, - {GetCharArrayElements: new ctypes.FunctionType(ctypes.default_abi, - jchar.ptr, - [jenvptr, jarray, - jboolean.ptr]).ptr}, - {GetShortArrayElements: new ctypes.FunctionType(ctypes.default_abi, - jshort.ptr, - [jenvptr, jarray, - jboolean.ptr]).ptr}, - {GetIntArrayElements: new ctypes.FunctionType(ctypes.default_abi, - jint.ptr, - [jenvptr, jarray, - jboolean.ptr]).ptr}, - {GetLongArrayElements: new ctypes.FunctionType(ctypes.default_abi, - jlong.ptr, - [jenvptr, jarray, - jboolean.ptr]).ptr}, - {GetFloatArrayElements: new ctypes.FunctionType(ctypes.default_abi, - jfloat.ptr, - [jenvptr, jarray, - jboolean.ptr]).ptr}, - {GetDoubleArrayElements: new ctypes.FunctionType(ctypes.default_abi, - jdouble.ptr, - [jenvptr, jarray, - jboolean.ptr]).ptr}, - {ReleaseBooleanArrayElements: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jboolean.ptr, - jint]).ptr}, - {ReleaseByteArrayElements: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jbyte.ptr, - jint]).ptr}, - {ReleaseCharArrayElements: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jchar.ptr, - jint]).ptr}, - {ReleaseShortArrayElements: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jshort.ptr, - jint]).ptr}, - {ReleaseIntArrayElements: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jint.ptr, - jint]).ptr}, - {ReleaseLongArrayElements: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jlong.ptr, - jint]).ptr}, - {ReleaseFloatArrayElements: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jfloat.ptr, - jint]).ptr}, - {ReleaseDoubleArrayElements: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jdouble.ptr, - jint]).ptr}, - {GetBooleanArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jboolean.array()]).ptr}, - {GetByteArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jbyte.array()]).ptr}, - {GetCharArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jchar.array()]).ptr}, - {GetShortArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jshort.array()]).ptr}, - {GetIntArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jint.array()]).ptr}, - {GetLongArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jlong.array()]).ptr}, - {GetFloatArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jfloat.array()]).ptr}, - {GetDoubleArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jdouble.array()]).ptr}, - {SetBooleanArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jboolean.array()]).ptr}, - {SetByteArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jbyte.array()]).ptr}, - {SetCharArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jchar.array()]).ptr}, - {SetShortArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jshort.array()]).ptr}, - {SetIntArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jint.array()]).ptr}, - {SetLongArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jlong.array()]).ptr}, - {SetFloatArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jfloat.array()]).ptr}, - {SetDoubleArrayRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jarray, - jsize, jsize, - jdouble.array()]).ptr}, - {RegisterNatives: ctypes.voidptr_t}, - {UnregisterNatives: ctypes.voidptr_t}, - {MonitorEnter: new ctypes.FunctionType(ctypes.default_abi, - jint, [jenvptr, jobject]).ptr}, - {MonitorExit: new ctypes.FunctionType(ctypes.default_abi, - jint, [jenvptr, jobject]).ptr}, - {GetJavaVM: ctypes.voidptr_t}, - {GetStringRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jstring, - jsize, jsize, - jchar.array()]).ptr}, - {GetStringUTFRegion: new ctypes.FunctionType(ctypes.default_abi, - ctypes.void_t, - [jenvptr, jstring, - jsize, jsize, - ctypes.char.array()]).ptr}, - {GetPrimitiveArrayCritical: ctypes.voidptr_t}, - {ReleasePrimitiveArrayCritical: ctypes.voidptr_t}, - {GetStringCritical: ctypes.voidptr_t}, - {ReleaseStringCritical: ctypes.voidptr_t}, - {NewWeakGlobalRef: ctypes.voidptr_t}, - {DeleteWeakGlobalRef: ctypes.voidptr_t}, - {ExceptionCheck: new ctypes.FunctionType(ctypes.default_abi, - jboolean, [jenvptr]).ptr}, - {NewDirectByteBuffer: ctypes.voidptr_t}, - {GetDirectBufferAddress: ctypes.voidptr_t}, - {GetDirectBufferCapacity: ctypes.voidptr_t}, - {GetObjectRefType: ctypes.voidptr_t}] -); - -var GetJNIForThread = libxul.declare("GetJNIForThread", - ctypes.default_abi, - JNINativeInterface.ptr.ptr); - -var registry = Object.create(null); -var classes = Object.create(null); - -function JNIUnloadClasses(jenv) { - Object.getOwnPropertyNames(registry).forEach(function(classname) { - var jcls = unwrap(registry[classname]); - jenv.contents.contents.DeleteGlobalRef(jenv, jcls); - - // Purge the registry, so we don't try to reuse stale global references - // in JNI calls and we garbage-collect the JS global reference objects. - delete registry[classname]; - }); - - // The refs also get added to the 'classes' object, so we should purge it too. - // That object is a hierarchical data structure organized by class path parts, - // but deleting its own properties should be sufficient to break its refs. - Object.getOwnPropertyNames(classes).forEach(function(topLevelPart) { - delete classes[topLevelPart]; - }); -} - -var PREFIX = 'js#'; -// this regex matches one component of a type signature: -// any number of array modifiers, followed by either a -// primitive type character or L<classname>; -var sigRegex = () => /\[*([VZBCSIJFD]|L([^.\/;]+(\/[^.\/;]+)*);)/g; -var ensureSig = function(classname_or_signature) { - // convert a classname into a signature, - // leaving unchanged signatures. We assume that - // anything not a valid signature is a classname. - var m = sigRegex().exec(classname_or_signature); - return (m && m[0] === classname_or_signature) ? classname_or_signature : - 'L' + classname_or_signature.replace(/\./g, '/') + ';'; -}; -var wrap = function(obj, classSig) { - if (!classSig) { return obj; } - // don't wrap primitive types. - if (classSig.charAt(0)!=='L' && - classSig.charAt(0)!=='[') { return obj; } - var proto = registry[classSig][PREFIX+'proto']; - return new proto(obj); -}; -var unwrap = function(obj, opt_jenv, opt_ctype) { - if (obj && typeof(obj)==='object' && (PREFIX+'obj') in obj) { - return obj[PREFIX+'obj']; - } else if (opt_jenv && opt_ctype) { - if (opt_ctype !== jobject) - return opt_ctype(obj); // cast to given primitive ctype - if (typeof(obj)==='string') - return unwrap(JNINewString(opt_jenv, obj)); // create Java String - } - return obj; -}; -var ensureLoaded = function(jenv, classSig) { - if (!Object.hasOwnProperty.call(registry, classSig)) { - JNILoadClass(jenv, classSig); - } - return registry[classSig]; -}; - -function JNINewString(jenv, value) { - var s = jenv.contents.contents.NewStringUTF(jenv, ctypes.char.array()(value)); - ensureLoaded(jenv, "Ljava/lang/String;"); - return wrap(s, "Ljava/lang/String;"); -} - -function JNIReadString(jenv, jstring_value) { - var val = unwrap(jstring_value); - if ((!val) || val.isNull()) { return null; } - var chars = jenv.contents.contents.GetStringUTFChars(jenv, val, null); - var result = chars.readString(); - jenv.contents.contents.ReleaseStringUTFChars(jenv, val, chars); - return result; -} - -var sigInfo = { - 'V': { name: 'Void', longName: 'Void', ctype: ctypes.void_t }, - 'Z': { name: 'Boolean', longName: 'Boolean', ctype: jboolean }, - 'B': { name: 'Byte', longName: 'Byte', ctype: jbyte }, - 'C': { name: 'Char', longName: 'Char', ctype: jchar }, - 'S': { name: 'Short', longName: 'Short', ctype: jshort }, - 'I': { name: 'Int', longName: 'Integer', ctype: jint }, - 'J': { name: 'Long', longName: 'Long', ctype: jlong }, - 'F': { name: 'Float', longName: 'Float', ctype: jfloat }, - 'D': { name: 'Double', longName: 'Double', ctype: jdouble }, - 'L': { name: 'Object', longName: 'Object', ctype: jobject }, - '[': { name: 'Object', longName: 'Object', ctype: jarray } -}; - -var sig2type = function(sig) { return sigInfo[sig.charAt(0)].name; }; -var sig2ctype = function(sig) { return sigInfo[sig.charAt(0)].ctype; }; -var sig2prim = function(sig) { return sigInfo[sig.charAt(0)].longName; }; - -// return the class object for a signature string. -// allocates 1 or 2 local refs -function JNIClassObj(jenv, classSig) { - var jenvpp = function() { return jenv.contents.contents; }; - // Deal with funny calling convention of JNI FindClass method. - // Classes get the leading & trailing chars stripped; primitives - // have to be looked up via their wrapper type. - var prim = function(ty) { - var jcls = jenvpp().FindClass(jenv, "java/lang/"+ty); - var jfld = jenvpp().GetStaticFieldID(jenv, jcls, "TYPE", - "Ljava/lang/Class;"); - return jenvpp().GetStaticObjectField(jenv, jcls, jfld); - }; - switch (classSig.charAt(0)) { - case '[': - return jenvpp().FindClass(jenv, classSig); - case 'L': - classSig = classSig.substring(1, classSig.indexOf(';')); - return jenvpp().FindClass(jenv, classSig); - default: - return prim(sig2prim(classSig)); - } -} - -// return the signature string for a Class object. -// allocates 2 local refs -function JNIClassSig(jenv, jcls) { - var jenvpp = function() { return jenv.contents.contents; }; - var jclscls = jenvpp().FindClass(jenv, "java/lang/Class"); - var jmtd = jenvpp().GetMethodID(jenv, jclscls, - "getName", "()Ljava/lang/String;"); - var name = jenvpp().CallObjectMethod(jenv, jcls, jmtd); - name = JNIReadString(jenv, name); - // API is weird. Make sure we're using slashes not dots - name = name.replace(/\./g, '/'); - // special case primitives, arrays - if (name.charAt(0)==='[') return name; - switch(name) { - case 'void': return 'V'; - case 'boolean': return 'Z'; - case 'byte': return 'B'; - case 'char': return 'C'; - case 'short': return 'S'; - case 'int': return 'I'; - case 'long': return 'J'; - case 'float': return 'F'; - case 'double': return 'D'; - default: - return 'L' + name + ';'; - } -} - -// create dispatch method -// we resolve overloaded methods only by # of arguments. If you need -// further resolution, use the 'long form' of the method name, ie: -// obj['toString()Ljava/lang/String'].call(obj); -var overloadFunc = function(basename) { - return function() { - return this[basename+'('+arguments.length+')'].apply(this, arguments); - }; -}; - -// Create appropriate wrapper fields/methods for a Java class. -function JNILoadClass(jenv, classSig, opt_props) { - var jenvpp = function() { return jenv.contents.contents; }; - var props = opt_props || {}; - - // allocate a local reference frame with enough space - // this class (1 or 2 local refs) plus superclass (3 refs) - // plus array element class (1 or 2 local refs) - var numLocals = 7; - jenvpp().PushLocalFrame(jenv, numLocals); - - var jcls; - if (Object.hasOwnProperty.call(registry, classSig)) { - jcls = unwrap(registry[classSig]); - } else { - jcls = jenvpp().NewGlobalRef(jenv, JNIClassObj(jenv, classSig)); - - // get name of superclass - var jsuper = jenvpp().GetSuperclass(jenv, jcls); - if (jsuper.isNull()) { - jsuper = null; - } else { - jsuper = JNIClassSig(jenv, jsuper); - } - - registry[classSig] = Object.create(jsuper?ensureLoaded(jenv, jsuper):null); - registry[classSig][PREFIX+'obj'] = jcls; // global ref, persistent. - registry[classSig][PREFIX+'proto'] = - function(o) { this[PREFIX+'obj'] = o; }; - registry[classSig][PREFIX+'proto'].prototype = - Object.create(jsuper ? - ensureLoaded(jenv, jsuper)[PREFIX+'proto'].prototype : - null); - // Add a __cast__ method to the wrapper corresponding to the class - registry[classSig].__cast__ = function(obj) { - return wrap(unwrap(obj), classSig); - }; - - // make wrapper accessible via the classes object. - var path = sig2type(classSig).toLowerCase(); - if (classSig.charAt(0)==='L') { - path = classSig.substring(1, classSig.length-1); - } - if (classSig.charAt(0)!=='[') { - var root = classes, i; - var parts = path.split('/'); - for (i = 0; i < parts.length-1; i++) { - if (!Object.hasOwnProperty.call(root, parts[i])) { - root[parts[i]] = Object.create(null); - } - root = root[parts[i]]; - } - root[parts[parts.length-1]] = registry[classSig]; - } - } - - var r = registry[classSig]; - var rpp = r[PREFIX+'proto'].prototype; - - if (classSig.charAt(0)==='[') { - // add 'length' field for arrays - Object.defineProperty(rpp, 'length', { - get: function() { - return jenvpp().GetArrayLength(jenv, unwrap(this)); - } - }); - // add 'get' and 'set' methods, 'new' constructor - var elemSig = classSig.substring(1); - ensureLoaded(jenv, elemSig); - - registry[elemSig].__array__ = r; - if (!Object.hasOwnProperty.call(registry[elemSig], 'array')) - registry[elemSig].array = r; - - if (elemSig.charAt(0)==='L' || elemSig.charAt(0)==='[') { - var elemClass = unwrap(registry[elemSig]); - - rpp.get = function(idx) { - return wrap(jenvpp().GetObjectArrayElement(jenv, unwrap(this), idx), - elemSig); - }; - rpp.set = function(idx, value) { - jenvpp().SetObjectArrayElement(jenv, unwrap(this), idx, - unwrap(value, jenv, jobject)); - }; - rpp.getElements = function(start, len) { - var i, r=[]; - for (i=0; i<len; i++) { r.push(this.get(start+i)); } - return r; - }; - rpp.setElements = function(start, vals) { - vals.forEach(function(v, i) { this.set(start+i, v); }.bind(this)); - }; - r['new'] = function(length) { - return wrap(jenvpp().NewObjectArray(jenv, length, elemClass, null), - classSig); - }; - } else { - var ty = sig2type(elemSig), ctype = sig2ctype(elemSig); - var constructor = "New"+ty+"Array"; - var getter = "Get"+ty+"ArrayRegion"; - var setter = "Set"+ty+"ArrayRegion"; - rpp.get = function(idx) { return this.getElements(idx, 1)[0]; }; - rpp.set = function(idx, val) { this.setElements(idx, [val]); }; - rpp.getElements = function(start, len) { - var j = jenvpp(); - var buf = new (ctype.array())(len); - j[getter].call(j, jenv, unwrap(this), start, len, buf); - return buf; - }; - rpp.setElements = function(start, vals) { - var j = jenvpp(); - j[setter].call(j, jenv, unwrap(this), start, vals.length, - ctype.array()(vals)); - }; - r['new'] = function(length) { - var j = jenvpp(); - return wrap(j[constructor].call(j, jenv, length), classSig); - }; - } - } - - (props.static_fields || []).forEach(function(fld) { - var jfld = jenvpp().GetStaticFieldID(jenv, jcls, fld.name, fld.sig); - var ty = sig2type(fld.sig), nm = fld.sig; - var getter = "GetStatic"+ty+"Field", setter = "SetStatic"+ty+"Field"; - ensureLoaded(jenv, nm); - var props = { - get: function() { - var j = jenvpp(); - return wrap(j[getter].call(j, jenv, jcls, jfld), nm); - }, - set: function(newValue) { - var j = jenvpp(); - j[setter].call(j, jenv, jcls, jfld, unwrap(newValue)); - } - }; - Object.defineProperty(r, fld.name, props); - // add static fields to object instances, too. - Object.defineProperty(rpp, fld.name, props); - }); - (props.static_methods || []).forEach(function(mtd) { - var jmtd = jenvpp().GetStaticMethodID(jenv, jcls, mtd.name, mtd.sig); - var argctypes = mtd.sig.match(sigRegex()).map(s => sig2ctype(s)); - var returnSig = mtd.sig.substring(mtd.sig.indexOf(')')+1); - var ty = sig2type(returnSig), nm = returnSig; - var call = "CallStatic"+ty+"Method"; - ensureLoaded(jenv, nm); - r[mtd.name] = rpp[mtd.name] = overloadFunc(mtd.name); - r[mtd.name + mtd.sig] = r[mtd.name+'('+(argctypes.length-1)+')'] = - // add static methods to object instances, too. - rpp[mtd.name + mtd.sig] = rpp[mtd.name+'('+(argctypes.length-1)+')'] = function() { - var i, j = jenvpp(); - var args = [jenv, jcls, jmtd]; - for (i=0; i<arguments.length; i++) { - args.push(unwrap(arguments[i], jenv, argctypes[i])); - } - return wrap(j[call].apply(j, args), nm); - }; - }); - (props.constructors || []).forEach(function(mtd) { - mtd.name = "<init>"; - var jmtd = jenvpp().GetMethodID(jenv, jcls, mtd.name, mtd.sig); - var argctypes = mtd.sig.match(sigRegex()).map(s => sig2ctype(s)); - var returnSig = mtd.sig.substring(mtd.sig.indexOf(')')+1); - - r['new'] = overloadFunc('new'); - r['new'+mtd.sig] = r['new('+(argctypes.length-1)+')'] = function() { - var i, j = jenvpp(); - var args = [jenv, jcls, jmtd]; - for (i=0; i<arguments.length; i++) { - args.push(unwrap(arguments[i], jenv, argctypes[i])); - } - return wrap(j.NewObject.apply(j, args), classSig); - }; - }); - (props.fields || []).forEach(function(fld) { - var jfld = jenvpp().GetFieldID(jenv, jcls, fld.name, fld.sig); - var ty = sig2type(fld.sig), nm = fld.sig; - var getter = "Get"+ty+"Field", setter = "Set"+ty+"Field"; - ensureLoaded(jenv, nm); - Object.defineProperty(rpp, fld.name, { - get: function() { - var j = jenvpp(); - return wrap(j[getter].call(j, jenv, unwrap(this), jfld), nm); - }, - set: function(newValue) { - var j = jenvpp(); - j[setter].call(j, jenv, unwrap(this), jfld, unwrap(newValue)); - } - }); - }); - (props.methods || []).forEach(function(mtd) { - var jmtd = jenvpp().GetMethodID(jenv, jcls, mtd.name, mtd.sig); - var argctypes = mtd.sig.match(sigRegex()).map(s => sig2ctype(s)); - var returnSig = mtd.sig.substring(mtd.sig.indexOf(')')+1); - var ty = sig2type(returnSig), nm = returnSig; - var call = "Call"+ty+"Method"; - ensureLoaded(jenv, nm); - rpp[mtd.name] = overloadFunc(mtd.name); - rpp[mtd.name + mtd.sig] = rpp[mtd.name+'('+(argctypes.length-1)+')'] = function() { - var i, j = jenvpp(); - var args = [jenv, unwrap(this), jmtd]; - for (i=0; i<arguments.length; i++) { - args.push(unwrap(arguments[i], jenv, argctypes[i])); - } - return wrap(j[call].apply(j, args), nm); - }; - }); - jenvpp().PopLocalFrame(jenv, null); - return r; -} - -// exported object -var JNI = { - // primitive types - jboolean: jboolean, - jbyte: jbyte, - jchar: jchar, - jshort: jshort, - jint: jint, - jlong: jlong, - jfloat: jfloat, - jdouble: jdouble, - jsize: jsize, - - // class registry - classes: classes, - - // methods - GetForThread: GetJNIForThread, - NewString: JNINewString, - ReadString: JNIReadString, - LoadClass: function(jenv, classname_or_signature, props) { - return JNILoadClass(jenv, ensureSig(classname_or_signature), props); - }, - UnloadClasses: JNIUnloadClasses -}; diff --git a/mobile/android/modules/JavaAddonManager.jsm b/mobile/android/modules/JavaAddonManager.jsm deleted file mode 100644 index a24535ede..000000000 --- a/mobile/android/modules/JavaAddonManager.jsm +++ /dev/null @@ -1,115 +0,0 @@ -// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["JavaAddonManager"]; - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; /*global Components */ - -Cu.import("resource://gre/modules/Messaging.jsm"); /*global Messaging */ -Cu.import("resource://gre/modules/Services.jsm"); /*global Services */ - -function resolveGeckoURI(uri) { - if (!uri) { - throw new Error("Can't resolve an empty uri"); - } - if (uri.startsWith("chrome://")) { - let registry = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci["nsIChromeRegistry"]); - return registry.convertChromeURL(Services.io.newURI(uri, null, null)).spec; - } else if (uri.startsWith("resource://")) { - let handler = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler); - return handler.resolveURI(Services.io.newURI(uri, null, null)); - } - return uri; -} - -/** - * A promise-based API - */ -var JavaAddonManager = Object.freeze({ - classInstanceFromFile: function(classname, filename) { - if (!classname) { - throw new Error("classname cannot be null"); - } - if (!filename) { - throw new Error("filename cannot be null"); - } - return Messaging.sendRequestForResult({ - type: "JavaAddonManagerV1:Load", - classname: classname, - filename: resolveGeckoURI(filename) - }) - .then((guid) => { - if (!guid) { - throw new Error("Internal error: guid should not be null"); - } - return new JavaAddonV1({classname: classname, guid: guid}); - }); - } -}); - -function JavaAddonV1(options = {}) { - if (!(this instanceof JavaAddonV1)) { - return new JavaAddonV1(options); - } - if (!options.classname) { - throw new Error("options.classname cannot be null"); - } - if (!options.guid) { - throw new Error("options.guid cannot be null"); - } - this._classname = options.classname; - this._guid = options.guid; - this._loaded = true; - this._listeners = {}; -} - -JavaAddonV1.prototype = Object.freeze({ - unload: function() { - if (!this._loaded) { - return; - } - - Messaging.sendRequestForResult({ - type: "JavaAddonManagerV1:Unload", - guid: this._guid - }) - .then(() => { - this._loaded = false; - for (let listener of this._listeners) { - // If we use this.removeListener, we prefix twice. - Messaging.removeListener(listener); - } - this._listeners = {}; - }); - }, - - _prefix: function(message) { - let newMessage = Cu.cloneInto(message, {}, { cloneFunctions: false }); - newMessage.type = this._guid + ":" + message.type; - return newMessage; - }, - - sendRequest: function(message) { - return Messaging.sendRequest(this._prefix(message)); - }, - - sendRequestForResult: function(message) { - return Messaging.sendRequestForResult(this._prefix(message)); - }, - - addListener: function(listener, message) { - let prefixedMessage = this._guid + ":" + message; - this._listeners[prefixedMessage] = listener; - return Messaging.addListener(listener, prefixedMessage); - }, - - removeListener: function(message) { - let prefixedMessage = this._guid + ":" + message; - delete this._listeners[prefixedMessage]; - return Messaging.removeListener(prefixedMessage); - } -}); diff --git a/mobile/android/modules/LightweightThemeConsumer.jsm b/mobile/android/modules/LightweightThemeConsumer.jsm deleted file mode 100644 index 3d3ca4c0b..000000000 --- a/mobile/android/modules/LightweightThemeConsumer.jsm +++ /dev/null @@ -1,44 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var EXPORTED_SYMBOLS = ["LightweightThemeConsumer"]; -var Cc = Components.classes; -var Ci = Components.interfaces; - -Components.utils.import("resource://gre/modules/Services.jsm"); -Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm"); - -function LightweightThemeConsumer(aDocument) { - this._doc = aDocument; - Services.obs.addObserver(this, "lightweight-theme-styling-update", false); - Services.obs.addObserver(this, "lightweight-theme-apply", false); - - this._update(LightweightThemeManager.currentThemeForDisplay); -} - -LightweightThemeConsumer.prototype = { - observe: function (aSubject, aTopic, aData) { - if (aTopic == "lightweight-theme-styling-update") - this._update(JSON.parse(aData)); - else if (aTopic == "lightweight-theme-apply") - this._update(LightweightThemeManager.currentThemeForDisplay); - }, - - destroy: function () { - Services.obs.removeObserver(this, "lightweight-theme-styling-update"); - Services.obs.removeObserver(this, "lightweight-theme-apply"); - this._doc = null; - }, - - _update: function (aData) { - if (!aData) - aData = { headerURL: "", footerURL: "", textcolor: "", accentcolor: "" }; - - let active = !!aData.headerURL; - - let msg = active ? { type: "LightweightTheme:Update", data: aData } : - { type: "LightweightTheme:Disable" }; - Services.androidBridge.handleGeckoMessage(msg); - } -} diff --git a/mobile/android/modules/MediaPlayerApp.jsm b/mobile/android/modules/MediaPlayerApp.jsm deleted file mode 100644 index 949863d1f..000000000 --- a/mobile/android/modules/MediaPlayerApp.jsm +++ /dev/null @@ -1,166 +0,0 @@ -// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["MediaPlayerApp"]; - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); -var log = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.d.bind(null, "MediaPlayerApp"); - -// Helper function for sending commands to Java. -function send(type, data, callback) { - let msg = { - type: type - }; - - for (let i in data) { - msg[i] = data[i]; - } - - Messaging.sendRequestForResult(msg) - .then(result => callback(result, null), - error => callback(null, error)); -} - -/* These apps represent players supported natively by the platform. This class will proxy commands - * to native controls */ -function MediaPlayerApp(service) { - this.service = service; - this.location = service.location; - this.id = service.uuid; -} - -MediaPlayerApp.prototype = { - start: function start(callback) { - send("MediaPlayer:Start", { id: this.id }, (result, err) => { - if (callback) { - callback(err == null); - } - }); - }, - - stop: function stop(callback) { - send("MediaPlayer:Stop", { id: this.id }, (result, err) => { - if (callback) { - callback(err == null); - } - }); - }, - - remoteMedia: function remoteMedia(callback, listener) { - if (callback) { - callback(new RemoteMedia(this.id, listener)); - } - }, - - mirror: function mirror(callback) { - send("MediaPlayer:Mirror", { id: this.id }, (result, err) => { - if (callback) { - callback(err == null); - } - }); - } -} - -/* RemoteMedia provides a proxy to a native media player session. - */ -function RemoteMedia(id, listener) { - this._id = id; - this._listener = listener; - - if ("onRemoteMediaStart" in this._listener) { - Services.tm.mainThread.dispatch((function() { - this._listener.onRemoteMediaStart(this); - }).bind(this), Ci.nsIThread.DISPATCH_NORMAL); - } -} - -RemoteMedia.prototype = { - shutdown: function shutdown() { - Services.obs.removeObserver(this, "MediaPlayer:Playing"); - Services.obs.removeObserver(this, "MediaPlayer:Paused"); - - this._send("MediaPlayer:End", {}, (result, err) => { - this._status = "shutdown"; - if ("onRemoteMediaStop" in this._listener) { - this._listener.onRemoteMediaStop(this); - } - }); - }, - - play: function play() { - this._send("MediaPlayer:Play", {}, (result, err) => { - if (err) { - Cu.reportError("Can't play " + err); - this.shutdown(); - return; - } - - this._status = "started"; - }); - }, - - pause: function pause() { - this._send("MediaPlayer:Pause", {}, (result, err) => { - if (err) { - Cu.reportError("Can't pause " + err); - this.shutdown(); - return; - } - - this._status = "paused"; - }); - }, - - load: function load(aData) { - this._send("MediaPlayer:Load", aData, (result, err) => { - if (err) { - Cu.reportError("Can't load " + err); - this.shutdown(); - return; - } - - Services.obs.addObserver(this, "MediaPlayer:Playing", false); - Services.obs.addObserver(this, "MediaPlayer:Paused", false); - this._status = "started"; - }) - }, - - get status() { - return this._status; - }, - - observe: function (aSubject, aTopic, aData) { - switch (aTopic) { - case "MediaPlayer:Playing": - if (this._status !== "started") { - this._status = "started"; - if ("onRemoteMediaStatus" in this._listener) { - this._listener.onRemoteMediaStatus(this); - } - } - break; - case "MediaPlayer:Paused": - if (this._status !== "paused") { - this._status = "paused"; - if ("onRemoteMediaStatus" in this._listener) { - this._listener.onRemoteMediaStatus(this); - } - } - break; - default: - break; - } - }, - - _send: function(msg, data, callback) { - data.id = this._id; - send(msg, data, callback); - } -} diff --git a/mobile/android/modules/Messaging.jsm b/mobile/android/modules/Messaging.jsm deleted file mode 100644 index 30b7f5a96..000000000 --- a/mobile/android/modules/Messaging.jsm +++ /dev/null @@ -1,183 +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 } = Components; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); - -this.EXPORTED_SYMBOLS = ["sendMessageToJava", "Messaging"]; - -XPCOMUtils.defineLazyServiceGetter(this, "uuidgen", - "@mozilla.org/uuid-generator;1", - "nsIUUIDGenerator"); - -function sendMessageToJava(aMessage, aCallback) { - Cu.reportError("sendMessageToJava is deprecated. Use Messaging API instead."); - - if (aCallback) { - Messaging.sendRequestForResult(aMessage) - .then(result => aCallback(result, null), - error => aCallback(null, error)); - } else { - Messaging.sendRequest(aMessage); - } -} - -var Messaging = { - /** - * Add a listener for the given message. - * - * Only one request listener can be registered for a given message. - * - * Example usage: - * // aData is data sent from Java with the request. The return value is - * // used to respond to the request. The return type *must* be an instance - * // of Object. - * let listener = function (aData) { - * if (aData == "foo") { - * return { response: "bar" }; - * } - * return {}; - * }; - * Messaging.addListener(listener, "Demo:Request"); - * - * The listener may also be a generator function, useful for performing a - * task asynchronously. For example: - * let listener = function* (aData) { - * // Respond with "bar" after 2 seconds. - * yield new Promise(resolve => setTimeout(resolve, 2000)); - * return { response: "bar" }; - * }; - * Messaging.addListener(listener, "Demo:Request"); - * - * @param aListener Listener callback taking a single data parameter (see - * example usage above). - * @param aMessage Event name that this listener should observe. - */ - addListener: function (aListener, aMessage) { - requestHandler.addListener(aListener, aMessage); - }, - - /** - * Removes a listener for a given message. - * - * @param aMessage The event to stop listening for. - */ - removeListener: function (aMessage) { - requestHandler.removeListener(aMessage); - }, - - /** - * Sends a request to Java. - * - * @param aMessage Message to send; must be an object with a "type" property - */ - sendRequest: function (aMessage) { - Services.androidBridge.handleGeckoMessage(aMessage); - }, - - /** - * Sends a request to Java, returning a Promise that resolves to the response. - * - * @param aMessage Message to send; must be an object with a "type" property - * @returns A Promise resolving to the response - */ - sendRequestForResult: function (aMessage) { - return new Promise((resolve, reject) => { - let id = uuidgen.generateUUID().toString(); - let obs = { - observe: function (aSubject, aTopic, aData) { - let data = JSON.parse(aData); - if (data.__guid__ != id) { - return; - } - - Services.obs.removeObserver(obs, aMessage.type + ":Response"); - - if (data.status === "success") { - resolve(data.response); - } else { - reject(data.response); - } - } - }; - - aMessage.__guid__ = id; - Services.obs.addObserver(obs, aMessage.type + ":Response", false); - - this.sendRequest(aMessage); - }); - }, - - /** - * Handles a request from Java, using the given listener method. - * This is mainly an internal method used by the RequestHandler object, but can be - * used in nsIObserver.observe implmentations that fall outside the normal usage - * patterns. - * - * @param aTopic The string name of the message - * @param aData The data sent to the observe method from Java - * @param aListener A function that takes a JSON data argument and returns a - * response which is sent to Java. - */ - handleRequest: Task.async(function* (aTopic, aData, aListener) { - let wrapper = JSON.parse(aData); - - try { - let response = yield aListener(wrapper.data); - if (typeof response !== "object" || response === null) { - throw new Error("Gecko request listener did not return an object"); - } - - Messaging.sendRequest({ - type: "Gecko:Request" + wrapper.id, - response: response - }); - } catch (e) { - Cu.reportError("Error in Messaging handler for " + aTopic + ": " + e); - - Messaging.sendRequest({ - type: "Gecko:Request" + wrapper.id, - error: { - message: e.message || (e && e.toString()), - stack: e.stack || Components.stack.formattedStack, - } - }); - } - }) -}; - -var requestHandler = { - _listeners: {}, - - addListener: function (aListener, aMessage) { - if (aMessage in this._listeners) { - throw new Error("Error in addListener: A listener already exists for message " + aMessage); - } - - if (typeof aListener !== "function") { - throw new Error("Error in addListener: Listener must be a function for message " + aMessage); - } - - this._listeners[aMessage] = aListener; - Services.obs.addObserver(this, aMessage, false); - }, - - removeListener: function (aMessage) { - if (!(aMessage in this._listeners)) { - throw new Error("Error in removeListener: There is no listener for message " + aMessage); - } - - delete this._listeners[aMessage]; - Services.obs.removeObserver(this, aMessage); - }, - - observe: function(aSubject, aTopic, aData) { - let listener = this._listeners[aTopic]; - Messaging.handleRequest(aTopic, aData, listener); - } -}; diff --git a/mobile/android/modules/NetErrorHelper.jsm b/mobile/android/modules/NetErrorHelper.jsm deleted file mode 100644 index 9c74df8fe..000000000 --- a/mobile/android/modules/NetErrorHelper.jsm +++ /dev/null @@ -1,175 +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 } = Components; -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); -Cu.import("resource://gre/modules/UITelemetry.jsm"); - -this.EXPORTED_SYMBOLS = ["NetErrorHelper"]; - -const KEY_CODE_ENTER = 13; - -/* Handlers is a list of objects that will be notified when an error page is shown - * or when an event occurs on the page that they are registered to handle. Registration - * is done by just adding yourself to the dictionary. - * - * handlers.myKey = { - * onPageShown: function(browser) { }, - * handleEvent: function(event) { }, - * } - * - * The key that you register yourself with should match the ID of the element you want to - * watch for click events on. - */ - -var handlers = {}; - -function NetErrorHelper(browser) { - browser.addEventListener("click", this.handleClick, true); - - let listener = () => { - browser.removeEventListener("click", this.handleClick, true); - browser.removeEventListener("pagehide", listener, true); - }; - browser.addEventListener("pagehide", listener, true); - - // Handlers may want to customize the page - for (let id in handlers) { - if (handlers[id].onPageShown) { - handlers[id].onPageShown(browser); - } - } -} - -NetErrorHelper.attachToBrowser = function(browser) { - return new NetErrorHelper(browser); -} - -NetErrorHelper.prototype = { - handleClick: function(event) { - let node = event.target; - - while(node) { - if (node.id in handlers && handlers[node.id].handleClick) { - handlers[node.id].handleClick(event); - return; - } - - node = node.parentNode; - } - }, -} - -handlers.searchbutton = { - onPageShown: function(browser) { - let search = browser.contentDocument.querySelector("#searchbox"); - if (!search) { - return; - } - - let browserWin = Services.wm.getMostRecentWindow("navigator:browser"); - let tab = browserWin.BrowserApp.getTabForBrowser(browser); - - // If there is no stored userRequested, just hide the searchbox - if (!tab.userRequested) { - search.style.display = "none"; - } else { - let text = browser.contentDocument.querySelector("#searchtext"); - text.value = tab.userRequested; - text.addEventListener("keypress", (event) => { - if (event.keyCode === KEY_CODE_ENTER) { - this.doSearch(event.target.value); - } - }); - } - }, - - handleClick: function(event) { - let value = event.target.previousElementSibling.value; - this.doSearch(value); - }, - - doSearch: function(value) { - UITelemetry.addEvent("neterror.1", "button", null, "search"); - let engine = Services.search.defaultEngine; - let uri = engine.getSubmission(value).uri; - - let browserWin = Services.wm.getMostRecentWindow("navigator:browser"); - // Reset the user search to whatever the new search term was - browserWin.BrowserApp.loadURI(uri.spec, undefined, { isSearch: true, userRequested: value }); - } -}; - -handlers.wifi = { - // This registers itself with the nsIObserverService as a weak ref, - // so we have to implement GetWeakReference as well. - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, - Ci.nsISupportsWeakReference]), - - GetWeakReference: function() { - return Cu.getWeakReference(this); - }, - - onPageShown: function(browser) { - // If we have a connection, don't bother showing the wifi toggle. - let network = Cc["@mozilla.org/network/network-link-service;1"].getService(Ci.nsINetworkLinkService); - if (network.isLinkUp && network.linkStatusKnown) { - let nodes = browser.contentDocument.querySelectorAll("#wifi"); - for (let i = 0; i < nodes.length; i++) { - nodes[i].style.display = "none"; - } - } - }, - - handleClick: function(event) { - let node = event.target; - while(node && node.id !== "wifi") { - node = node.parentNode; - } - - if (!node) { - return; - } - - UITelemetry.addEvent("neterror.1", "button", null, "wifitoggle"); - // Show indeterminate progress while we wait for the network. - node.disabled = true; - node.classList.add("inProgress"); - - this.node = Cu.getWeakReference(node); - Services.obs.addObserver(this, "network:link-status-changed", true); - - Messaging.sendRequest({ - type: "Wifi:Enable" - }); - }, - - observe: function(subject, topic, data) { - let node = this.node.get(); - if (!node) { - return; - } - - // Remove the progress bar - node.disabled = false; - node.classList.remove("inProgress"); - - let network = Cc["@mozilla.org/network/network-link-service;1"].getService(Ci.nsINetworkLinkService); - if (network.isLinkUp && network.linkStatusKnown) { - // If everything worked, reload the page - UITelemetry.addEvent("neterror.1", "button", null, "wifitoggle.reload"); - Services.obs.removeObserver(this, "network:link-status-changed"); - - // Even at this point, Android sometimes lies about the real state of the network and this reload request fails. - // Add a 500ms delay before refreshing the page. - node.ownerDocument.defaultView.setTimeout(function() { - node.ownerDocument.location.reload(false); - }, 500); - } - } -} - diff --git a/mobile/android/modules/Notifications.jsm b/mobile/android/modules/Notifications.jsm deleted file mode 100644 index a035bb2e3..000000000 --- a/mobile/android/modules/Notifications.jsm +++ /dev/null @@ -1,259 +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 } = Components; - -Cu.import("resource://gre/modules/Services.jsm"); - -this.EXPORTED_SYMBOLS = ["Notifications"]; - -function log(msg) { - // Services.console.logStringMessage(msg); -} - -var _notificationsMap = {}; -var _handlersMap = {}; - -function Notification(aId, aOptions) { - this._id = aId; - this._when = (new Date()).getTime(); - this.fillWithOptions(aOptions); -} - -Notification.prototype = { - fillWithOptions: function(aOptions) { - if ("icon" in aOptions && aOptions.icon != null) - this._icon = aOptions.icon; - else - throw "Notification icon is mandatory"; - - if ("title" in aOptions && aOptions.title != null) - this._title = aOptions.title; - else - throw "Notification title is mandatory"; - - if ("message" in aOptions && aOptions.message != null) - this._message = aOptions.message; - else - this._message = null; - - if ("priority" in aOptions && aOptions.priority != null) - this._priority = aOptions.priority; - - if ("buttons" in aOptions && aOptions.buttons != null) { - if (aOptions.buttons.length > 3) - throw "Too many buttons provided. The max number is 3"; - - this._buttons = {}; - for (let i = 0; i < aOptions.buttons.length; i++) { - let button_id = aOptions.buttons[i].buttonId; - this._buttons[button_id] = aOptions.buttons[i]; - } - } else { - this._buttons = null; - } - - if ("ongoing" in aOptions && aOptions.ongoing != null) - this._ongoing = aOptions.ongoing; - else - this._ongoing = false; - - if ("progress" in aOptions && aOptions.progress != null) - this._progress = aOptions.progress; - else - this._progress = null; - - if ("onCancel" in aOptions && aOptions.onCancel != null) - this._onCancel = aOptions.onCancel; - else - this._onCancel = null; - - if ("onClick" in aOptions && aOptions.onClick != null) - this._onClick = aOptions.onClick; - else - this._onClick = null; - - if ("cookie" in aOptions && aOptions.cookie != null) - this._cookie = aOptions.cookie; - else - this._cookie = null; - - if ("handlerKey" in aOptions && aOptions.handlerKey != null) - this._handlerKey = aOptions.handlerKey; - - if ("persistent" in aOptions && aOptions.persistent != null) - this._persistent = aOptions.persistent; - else - this._persistent = false; - }, - - show: function() { - let msg = { - type: "Notification:Show", - id: this._id, - title: this._title, - smallIcon: this._icon, - ongoing: this._ongoing, - when: this._when, - persistent: this._persistent, - }; - - if (this._message) - msg.text = this._message; - - if (this._progress) { - msg.progress_value = this._progress; - msg.progress_max = 100; - msg.progress_indeterminate = false; - } else if (Number.isNaN(this._progress)) { - msg.progress_value = 0; - msg.progress_max = 0; - msg.progress_indeterminate = true; - } - - if (this._cookie) - msg.cookie = JSON.stringify(this._cookie); - - if (this._priority) - msg.priority = this._priority; - - if (this._buttons) { - msg.actions = []; - let buttonName; - for (buttonName in this._buttons) { - let button = this._buttons[buttonName]; - let obj = { - buttonId: button.buttonId, - title : button.title, - icon : button.icon - }; - msg.actions.push(obj); - } - } - - if (this._light) - msg.light = this._light; - - if (this._handlerKey) - msg.handlerKey = this._handlerKey; - - Services.androidBridge.handleGeckoMessage(msg); - return this; - }, - - cancel: function() { - let msg = { - type: "Notification:Hide", - id: this._id, - handlerKey: this._handlerKey, - cookie: JSON.stringify(this._cookie), - }; - Services.androidBridge.handleGeckoMessage(msg); - } -} - -var Notifications = { - get idService() { - delete this.idService; - return this.idService = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); - }, - - registerHandler: function(key, handler) { - if (!_handlersMap[key]) { - _handlersMap[key] = []; - } - _handlersMap[key].push(handler); - }, - - unregisterHandler: function(key, handler) { - let h = _handlersMap[key]; - if (!h) { - return; - } - let i = h.indexOf(handler); - if (i > -1) { - h.splice(i, 1); - } - }, - - create: function notif_notify(aOptions) { - let id = this.idService.generateUUID().toString(); - - let notification = new Notification(id, aOptions); - _notificationsMap[id] = notification; - notification.show(); - - return id; - }, - - update: function notif_update(aId, aOptions) { - let notification = _notificationsMap[aId]; - if (!notification) - throw "Unknown notification id"; - notification.fillWithOptions(aOptions); - notification.show(); - }, - - cancel: function notif_cancel(aId) { - let notification = _notificationsMap[aId]; - if (notification) - notification.cancel(); - }, - - observe: function notif_observe(aSubject, aTopic, aData) { - Services.console.logStringMessage(aTopic + " " + aData); - - let data = JSON.parse(aData); - let id = data.id; - let handlerKey = data.handlerKey; - let cookie = data.cookie ? JSON.parse(data.cookie) : undefined; - let notification = _notificationsMap[id]; - - switch (data.eventType) { - case "notification-clicked": - if (notification && notification._onClick) - notification._onClick(id, notification._cookie); - - if (handlerKey) { - _handlersMap[handlerKey].forEach(function(handler) { - handler.onClick(cookie); - }); - } - - break; - case "notification-button-clicked": - if (handlerKey) { - _handlersMap[handlerKey].forEach(function(handler) { - handler.onButtonClick(data.buttonId, cookie); - }); - } - - break; - case "notification-cleared": - case "notification-closed": - if (handlerKey) { - _handlersMap[handlerKey].forEach(function(handler) { - handler.onCancel(cookie); - }); - } - - if (notification && notification._onCancel) - notification._onCancel(id, notification._cookie); - delete _notificationsMap[id]; // since the notification was dismissed, we no longer need to hold a reference. - break; - } - }, - - QueryInterface: function (aIID) { - if (!aIID.equals(Ci.nsISupports) && - !aIID.equals(Ci.nsIObserver) && - !aIID.equals(Ci.nsISupportsWeakReference)) - throw Components.results.NS_ERROR_NO_INTERFACE; - return this; - } -}; - -Services.obs.addObserver(Notifications, "Notification:Event", false); diff --git a/mobile/android/modules/PageActions.jsm b/mobile/android/modules/PageActions.jsm deleted file mode 100644 index a66268f82..000000000 --- a/mobile/android/modules/PageActions.jsm +++ /dev/null @@ -1,113 +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 } = Components; -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); - -XPCOMUtils.defineLazyServiceGetter(this, "uuidgen", - "@mozilla.org/uuid-generator;1", - "nsIUUIDGenerator"); - -this.EXPORTED_SYMBOLS = ["PageActions"]; - -// Copied from browser.js -// TODO: We should move this method to a common importable location -function resolveGeckoURI(aURI) { - if (!aURI) - throw "Can't resolve an empty uri"; - - if (aURI.startsWith("chrome://")) { - let registry = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci["nsIChromeRegistry"]); - return registry.convertChromeURL(Services.io.newURI(aURI, null, null)).spec; - } else if (aURI.startsWith("resource://")) { - let handler = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler); - return handler.resolveURI(Services.io.newURI(aURI, null, null)); - } - return aURI; -} - -var PageActions = { - _items: { }, - - _inited: false, - - _maybeInit: function() { - if (!this._inited && Object.keys(this._items).length > 0) { - this._inited = true; - Services.obs.addObserver(this, "PageActions:Clicked", false); - Services.obs.addObserver(this, "PageActions:LongClicked", false); - } - }, - - _maybeUninit: function() { - if (this._inited && Object.keys(this._items).length == 0) { - this._inited = false; - Services.obs.removeObserver(this, "PageActions:Clicked"); - Services.obs.removeObserver(this, "PageActions:LongClicked"); - } - }, - - observe: function(aSubject, aTopic, aData) { - let item = this._items[aData]; - if (aTopic == "PageActions:Clicked") { - if (item.clickCallback) { - item.clickCallback(); - } - } else if (aTopic == "PageActions:LongClicked") { - if (item.longClickCallback) { - item.longClickCallback(); - } - } - }, - - isShown: function(id) { - return !!this._items[id]; - }, - - synthesizeClick: function(id) { - let item = this._items[id]; - if (item && item.clickCallback) { - item.clickCallback(); - } - }, - - add: function(aOptions) { - let id = aOptions.id || uuidgen.generateUUID().toString() - - Messaging.sendRequest({ - type: "PageActions:Add", - id: id, - title: aOptions.title, - icon: resolveGeckoURI(aOptions.icon), - important: "important" in aOptions ? aOptions.important : false - }); - - this._items[id] = {}; - - if (aOptions.clickCallback) { - this._items[id].clickCallback = aOptions.clickCallback; - } - - if (aOptions.longClickCallback) { - this._items[id].longClickCallback = aOptions.longClickCallback; - } - - this._maybeInit(); - return id; - }, - - remove: function(id) { - Messaging.sendRequest({ - type: "PageActions:Remove", - id: id - }); - - delete this._items[id]; - this._maybeUninit(); - } -} diff --git a/mobile/android/modules/Prompt.jsm b/mobile/android/modules/Prompt.jsm deleted file mode 100644 index 5bed87650..000000000 --- a/mobile/android/modules/Prompt.jsm +++ /dev/null @@ -1,234 +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" - -var Cc = Components.classes; -var Ci = Components.interfaces; - -Components.utils.import("resource://gre/modules/Services.jsm"); -Components.utils.import("resource://gre/modules/Messaging.jsm"); - -this.EXPORTED_SYMBOLS = ["Prompt"]; - -function log(msg) { - Services.console.logStringMessage(msg); -} - -function Prompt(aOptions) { - this.window = "window" in aOptions ? aOptions.window : null; - - this.msg = { async: true }; - - if (this.window) { - let window = Services.wm.getMostRecentWindow("navigator:browser"); - var tab = window.BrowserApp.getTabForWindow(this.window); - if (tab) { - this.msg.tabId = tab.id; - } - } - - if (aOptions.priority === 1) - this.msg.type = "Prompt:ShowTop" - else - this.msg.type = "Prompt:Show" - - if ("title" in aOptions && aOptions.title != null) - this.msg.title = aOptions.title; - - if ("message" in aOptions && aOptions.message != null) - this.msg.text = aOptions.message; - - if ("buttons" in aOptions && aOptions.buttons != null) - this.msg.buttons = aOptions.buttons; - - if ("doubleTapButton" in aOptions && aOptions.doubleTapButton != null) - this.msg.doubleTapButton = aOptions.doubleTapButton; - - if ("hint" in aOptions && aOptions.hint != null) - this.msg.hint = aOptions.hint; -} - -Prompt.prototype = { - setHint: function(aHint) { - if (!aHint) - delete this.msg.hint; - else - this.msg.hint = aHint; - return this; - }, - - addButton: function(aOptions) { - if (!this.msg.buttons) - this.msg.buttons = []; - this.msg.buttons.push(aOptions.label); - return this; - }, - - _addInput: function(aOptions) { - let obj = aOptions; - if (this[aOptions.type + "_count"] === undefined) - this[aOptions.type + "_count"] = 0; - - obj.id = aOptions.id || (aOptions.type + this[aOptions.type + "_count"]); - this[aOptions.type + "_count"]++; - - if (!this.msg.inputs) - this.msg.inputs = []; - this.msg.inputs.push(obj); - return this; - }, - - addCheckbox: function(aOptions) { - return this._addInput({ - type: "checkbox", - label: aOptions.label, - checked: aOptions.checked, - id: aOptions.id - }); - }, - - addTextbox: function(aOptions) { - return this._addInput({ - type: "textbox", - value: aOptions.value, - hint: aOptions.hint, - autofocus: aOptions.autofocus, - id: aOptions.id - }); - }, - - addNumber: function(aOptions) { - return this._addInput({ - type: "number", - value: aOptions.value, - hint: aOptions.hint, - autofocus: aOptions.autofocus, - id: aOptions.id - }); - }, - - addPassword: function(aOptions) { - return this._addInput({ - type: "password", - value: aOptions.value, - hint: aOptions.hint, - autofocus: aOptions.autofocus, - id : aOptions.id - }); - }, - - addDatePicker: function(aOptions) { - return this._addInput({ - type: aOptions.type || "date", - value: aOptions.value, - id: aOptions.id, - max: aOptions.max, - min: aOptions.min - }); - }, - - addColorPicker: function(aOptions) { - return this._addInput({ - type: "color", - value: aOptions.value, - id: aOptions.id - }); - }, - - addLabel: function(aOptions) { - return this._addInput({ - type: "label", - label: aOptions.label, - id: aOptions.id - }); - }, - - addMenulist: function(aOptions) { - return this._addInput({ - type: "menulist", - values: aOptions.values, - id: aOptions.id - }); - }, - - addIconGrid: function(aOptions) { - return this._addInput({ - type: "icongrid", - items: aOptions.items, - id: aOptions.id - }); - }, - - addTabs: function(aOptions) { - return this._addInput({ - type: "tabs", - items: aOptions.items, - id: aOptions.id - }); - }, - - show: function(callback) { - this.callback = callback; - log("Sending message"); - this._innerShow(); - }, - - _innerShow: function() { - Messaging.sendRequestForResult(this.msg).then((data) => { - if (this.callback) - this.callback(data); - }); - }, - - _setListItems: function(aItems) { - let hasSelected = false; - this.msg.listitems = []; - - aItems.forEach(function(item) { - let obj = { id: item.id }; - - obj.label = item.label; - - if (item.disabled) - obj.disabled = true; - - if (item.selected) { - if (!this.msg.choiceMode) { - this.msg.choiceMode = "single"; - } - obj.selected = item.selected; - } - - if (item.header) - obj.isGroup = true; - - if (item.menu) - obj.isParent = true; - - if (item.child) - obj.inGroup = true; - - if (item.showAsActions) - obj.showAsActions = item.showAsActions; - - if (item.icon) - obj.icon = item.icon; - - this.msg.listitems.push(obj); - - }, this); - return this; - }, - - setSingleChoiceItems: function(aItems) { - return this._setListItems(aItems); - }, - - setMultiChoiceItems: function(aItems) { - this.msg.choiceMode = "multiple"; - return this._setListItems(aItems); - }, - -} diff --git a/mobile/android/modules/RuntimePermissions.jsm b/mobile/android/modules/RuntimePermissions.jsm deleted file mode 100644 index 42d8024b1..000000000 --- a/mobile/android/modules/RuntimePermissions.jsm +++ /dev/null @@ -1,41 +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 } = Components; - -this.EXPORTED_SYMBOLS = ["RuntimePermissions"]; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm"); - -// See: http://developer.android.com/reference/android/Manifest.permission.html -const CAMERA = "android.permission.CAMERA"; -const WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE"; -const RECORD_AUDIO = "android.permission.RECORD_AUDIO"; - -var RuntimePermissions = { - CAMERA: CAMERA, - RECORD_AUDIO: RECORD_AUDIO, - WRITE_EXTERNAL_STORAGE: WRITE_EXTERNAL_STORAGE, - - /** - * Check whether the permissions have been granted or not. If needed prompt the user to accept the permissions. - * - * @returns A promise resolving to true if all the permissions have been granted or false if any of the - * permissions have been denied. - */ - waitForPermissions: function(permission) { - let permissions = [].concat(permission); - - let msg = { - type: 'RuntimePermissions:Prompt', - permissions: permissions - }; - - return Messaging.sendRequestForResult(msg); - } -};
\ No newline at end of file diff --git a/mobile/android/modules/SSLExceptions.jsm b/mobile/android/modules/SSLExceptions.jsm deleted file mode 100644 index 48dfe8d92..000000000 --- a/mobile/android/modules/SSLExceptions.jsm +++ /dev/null @@ -1,118 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict" - -var Cc = Components.classes; -var Ci = Components.interfaces; -var Cu = Components.utils; - -Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); - -this.EXPORTED_SYMBOLS = ["SSLExceptions"]; - -/** - A class to add exceptions to override SSL certificate problems. The functionality - itself is borrowed from exceptionDialog.js. -*/ -function SSLExceptions() { - this._overrideService = Cc["@mozilla.org/security/certoverride;1"] - .getService(Ci.nsICertOverrideService); -} - - -SSLExceptions.prototype = { - _overrideService: null, - _sslStatus: null, - - getInterface: function SSLE_getInterface(aIID) { - return this.QueryInterface(aIID); - }, - QueryInterface: function SSLE_QueryInterface(aIID) { - if (aIID.equals(Ci.nsIBadCertListener2) || - aIID.equals(Ci.nsISupports)) - return this; - - throw Components.results.NS_ERROR_NO_INTERFACE; - }, - - /** - To collect the SSL status we intercept the certificate error here - and store the status for later use. - */ - notifyCertProblem: function SSLE_notifyCertProblem(socketInfo, sslStatus, targetHost) { - this._sslStatus = sslStatus.QueryInterface(Ci.nsISSLStatus); - return true; // suppress error UI - }, - - /** - Attempt to download the certificate for the location specified to get the SSLState - for the certificate and the errors. - */ - _checkCert: function SSLE_checkCert(aURI) { - this._sslStatus = null; - - let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest); - try { - if (aURI) { - req.open("GET", aURI.prePath, false); - req.channel.notificationCallbacks = this; - req.send(null); - } - } catch (e) { - // We *expect* exceptions if there are problems with the certificate - // presented by the site. Log it, just in case, but we can proceed here, - // with appropriate sanity checks - Components.utils.reportError("Attempted to connect to a site with a bad certificate in the add exception dialog. " + - "This results in a (mostly harmless) exception being thrown. " + - "Logged for information purposes only: " + e); - } - - return this._sslStatus; - }, - - /** - Internal method to create an override. - */ - _addOverride: function SSLE_addOverride(aURI, aWindow, aTemporary) { - let SSLStatus = this._checkCert(aURI); - let certificate = SSLStatus.serverCert; - - let flags = 0; - - // in private browsing do not store exceptions permanently ever - if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) { - aTemporary = true; - } - - if (SSLStatus.isUntrusted) - flags |= this._overrideService.ERROR_UNTRUSTED; - if (SSLStatus.isDomainMismatch) - flags |= this._overrideService.ERROR_MISMATCH; - if (SSLStatus.isNotValidAtThisTime) - flags |= this._overrideService.ERROR_TIME; - - this._overrideService.rememberValidityOverride( - aURI.asciiHost, - aURI.port, - certificate, - flags, - aTemporary); - }, - - /** - Creates a permanent exception to override all overridable errors for - the given URL. - */ - addPermanentException: function SSLE_addPermanentException(aURI, aWindow) { - this._addOverride(aURI, aWindow, false); - }, - - /** - Creates a temporary exception to override all overridable errors for - the given URL. - */ - addTemporaryException: function SSLE_addTemporaryException(aURI, aWindow) { - this._addOverride(aURI, aWindow, true); - } -}; diff --git a/mobile/android/modules/Sanitizer.jsm b/mobile/android/modules/Sanitizer.jsm deleted file mode 100644 index 014a89688..000000000 --- a/mobile/android/modules/Sanitizer.jsm +++ /dev/null @@ -1,303 +0,0 @@ -// -*- indent-tabs-mode: nil; js-indent-level: 4 -*- -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/*globals LoadContextInfo, FormHistory, Accounts */ - -var Cc = Components.classes; -var Ci = Components.interfaces; -var Cu = Components.utils; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/LoadContextInfo.jsm"); -Cu.import("resource://gre/modules/FormHistory.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://gre/modules/Downloads.jsm"); -Cu.import("resource://gre/modules/osfile.jsm"); -Cu.import("resource://gre/modules/Accounts.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "DownloadIntegration", - "resource://gre/modules/DownloadIntegration.jsm"); - -function dump(a) { - Services.console.logStringMessage(a); -} - -this.EXPORTED_SYMBOLS = ["Sanitizer"]; - -function Sanitizer() {} -Sanitizer.prototype = { - clearItem: function (aItemName) - { - let item = this.items[aItemName]; - let canClear = item.canClear; - if (typeof canClear == "function") { - canClear(function clearCallback(aCanClear) { - if (aCanClear) - item.clear(); - }); - } else if (canClear) { - item.clear(); - } - }, - - items: { - cache: { - clear: function () - { - return new Promise(function(resolve, reject) { - var cache = Cc["@mozilla.org/netwerk/cache-storage-service;1"].getService(Ci.nsICacheStorageService); - try { - cache.clear(); - } catch(er) {} - - let imageCache = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools) - .getImgCacheForDocument(null); - try { - imageCache.clearCache(false); // true=chrome, false=content - } catch(er) {} - - resolve(); - }); - }, - - get canClear() - { - return true; - } - }, - - cookies: { - clear: function () - { - return new Promise(function(resolve, reject) { - Services.cookies.removeAll(); - resolve(); - }); - }, - - get canClear() - { - return true; - } - }, - - siteSettings: { - clear: Task.async(function* () { - // Clear site-specific permissions like "Allow this site to open popups" - Services.perms.removeAll(); - - // Clear site-specific settings like page-zoom level - Cc["@mozilla.org/content-pref/service;1"] - .getService(Ci.nsIContentPrefService2) - .removeAllDomains(null); - - // Clear site security settings - var sss = Cc["@mozilla.org/ssservice;1"] - .getService(Ci.nsISiteSecurityService); - sss.clearAll(); - - // Clear push subscriptions - yield new Promise((resolve, reject) => { - let push = Cc["@mozilla.org/push/Service;1"] - .getService(Ci.nsIPushService); - push.clearForDomain("*", status => { - if (Components.isSuccessCode(status)) { - resolve(); - } else { - reject(new Error("Error clearing push subscriptions: " + - status)); - } - }); - }); - }), - - get canClear() - { - return true; - } - }, - - offlineApps: { - clear: function () - { - return new Promise(function(resolve, reject) { - var cacheService = Cc["@mozilla.org/netwerk/cache-storage-service;1"].getService(Ci.nsICacheStorageService); - var appCacheStorage = cacheService.appCacheStorage(LoadContextInfo.default, null); - try { - appCacheStorage.asyncEvictStorage(null); - } catch(er) {} - - resolve(); - }); - }, - - get canClear() - { - return true; - } - }, - - history: { - clear: function () - { - return Messaging.sendRequestForResult({ type: "Sanitize:ClearHistory" }) - .catch(e => Cu.reportError("Java-side history clearing failed: " + e)) - .then(function() { - try { - Services.obs.notifyObservers(null, "browser:purge-session-history", ""); - } - catch (e) { } - - try { - var predictor = Cc["@mozilla.org/network/predictor;1"].getService(Ci.nsINetworkPredictor); - predictor.reset(); - } catch (e) { } - }); - }, - - get canClear() - { - // bug 347231: Always allow clearing history due to dependencies on - // the browser:purge-session-history notification. (like error console) - return true; - } - }, - - searchHistory: { - clear: function () - { - return Messaging.sendRequestForResult({ type: "Sanitize:ClearHistory", clearSearchHistory: true }) - .catch(e => Cu.reportError("Java-side search history clearing failed: " + e)) - }, - - get canClear() - { - return true; - } - }, - - formdata: { - clear: function () - { - return new Promise(function(resolve, reject) { - FormHistory.update({ op: "remove" }); - resolve(); - }); - }, - - canClear: function (aCallback) - { - let count = 0; - let countDone = { - handleResult: function(aResult) { count = aResult; }, - handleError: function(aError) { Cu.reportError(aError); }, - handleCompletion: function(aReason) { aCallback(aReason == 0 && count > 0); } - }; - FormHistory.count({}, countDone); - } - }, - - downloadFiles: { - clear: Task.async(function* () { - let list = yield Downloads.getList(Downloads.ALL); - let downloads = yield list.getAll(); - var finalizePromises = []; - - // Logic copied from DownloadList.removeFinished. Ideally, we would - // just use that method directly, but we want to be able to remove the - // downloaded files as well. - for (let download of downloads) { - // Remove downloads that have been canceled, even if the cancellation - // operation hasn't completed yet so we don't check "stopped" here. - // Failed downloads with partial data are also removed. - if (download.stopped && (!download.hasPartialData || download.error)) { - // Remove the download first, so that the views don't get the change - // notifications that may occur during finalization. - yield list.remove(download); - // Ensure that the download is stopped and no partial data is kept. - // This works even if the download state has changed meanwhile. We - // don't need to wait for the procedure to be complete before - // processing the other downloads in the list. - finalizePromises.push(download.finalize(true).then(() => null, Cu.reportError)); - - // Delete the downloaded files themselves. - OS.File.remove(download.target.path).then(() => null, ex => { - if (!(ex instanceof OS.File.Error && ex.becauseNoSuchFile)) { - Cu.reportError(ex); - } - }); - } - } - - yield Promise.all(finalizePromises); - yield DownloadIntegration.forceSave(); - }), - - get canClear() - { - return true; - } - }, - - passwords: { - clear: function () - { - return new Promise(function(resolve, reject) { - Services.logins.removeAllLogins(); - resolve(); - }); - }, - - get canClear() - { - let count = Services.logins.countLogins("", "", ""); // count all logins - return (count > 0); - } - }, - - sessions: { - clear: function () - { - return new Promise(function(resolve, reject) { - // clear all auth tokens - var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing); - sdr.logoutAndTeardown(); - - // clear FTP and plain HTTP auth sessions - Services.obs.notifyObservers(null, "net:clear-active-logins", null); - - resolve(); - }); - }, - - get canClear() - { - return true; - } - }, - - syncedTabs: { - clear: function () - { - return Messaging.sendRequestForResult({ type: "Sanitize:ClearSyncedTabs" }) - .catch(e => Cu.reportError("Java-side synced tabs clearing failed: " + e)); - }, - - canClear: function(aCallback) - { - Accounts.anySyncAccountsExist().then(aCallback) - .catch(function(err) { - Cu.reportError("Java-side synced tabs clearing failed: " + err) - aCallback(false); - }); - } - } - - } -}; - -this.Sanitizer = new Sanitizer(); diff --git a/mobile/android/modules/SharedPreferences.jsm b/mobile/android/modules/SharedPreferences.jsm deleted file mode 100644 index 3f32df6ea..000000000 --- a/mobile/android/modules/SharedPreferences.jsm +++ /dev/null @@ -1,254 +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"; - -this.EXPORTED_SYMBOLS = ["SharedPreferences"]; - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -// For adding observers. -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); - -var Scope = Object.freeze({ - APP: "app", - PROFILE: "profile", - GLOBAL: "global" -}); - -/** - * Public API to getting a SharedPreferencesImpl instance. These scopes mirror GeckoSharedPrefs. - */ -var SharedPreferences = { - forApp: function() { - return new SharedPreferencesImpl({ scope: Scope.APP }); - }, - - forProfile: function() { - return new SharedPreferencesImpl({ scope: Scope.PROFILE }); - }, - - /** - * Get SharedPreferences for the named profile; if the profile name is null, - * returns the preferences for the current profile (just like |forProfile|). - */ - forProfileName: function(profileName) { - return new SharedPreferencesImpl({ scope: Scope.PROFILE, profileName: profileName }); - }, - - /** - * Get SharedPreferences for the given Android branch; if the branch is null, - * returns the default preferences branch for the application, which is the - * output of |PreferenceManager.getDefaultSharedPreferences|. - */ - forAndroid: function(branch) { - return new SharedPreferencesImpl({ scope: Scope.GLOBAL, branch: branch }); - } -}; - -/** - * Create an interface to an Android SharedPreferences branch. - * - * options {Object} with the following valid keys: - * - scope {String} (required) specifies the scope of preferences that should be accessed. - * - branch {String} (only when using Scope.GLOBAL) should be a string describing a preferences branch, - * like "UpdateService" or "background.data", or null to access the - * default preferences branch for the application. - * - profileName {String} (optional, only valid when using Scope.PROFILE) - */ -function SharedPreferencesImpl(options = {}) { - if (!(this instanceof SharedPreferencesImpl)) { - return new SharedPreferencesImpl(options); - } - - if (options.scope == null || options.scope == undefined) { - throw "Shared Preferences must specifiy a scope."; - } - - this._scope = options.scope; - this._profileName = options.profileName; - this._branch = options.branch; - this._observers = {}; -} - -SharedPreferencesImpl.prototype = Object.freeze({ - _set: function _set(prefs) { - Messaging.sendRequest({ - type: "SharedPreferences:Set", - preferences: prefs, - scope: this._scope, - profileName: this._profileName, - branch: this._branch, - }); - }, - - _setOne: function _setOne(prefName, value, type) { - let prefs = []; - prefs.push({ - name: prefName, - value: value, - type: type, - }); - this._set(prefs); - }, - - setBoolPref: function setBoolPref(prefName, value) { - this._setOne(prefName, value, "bool"); - }, - - setCharPref: function setCharPref(prefName, value) { - this._setOne(prefName, value, "string"); - }, - - setIntPref: function setIntPref(prefName, value) { - this._setOne(prefName, value, "int"); - }, - - _get: function _get(prefs, callback) { - let result = null; - Messaging.sendRequestForResult({ - type: "SharedPreferences:Get", - preferences: prefs, - scope: this._scope, - profileName: this._profileName, - branch: this._branch, - }).then((data) => { - result = data.values; - }); - - let thread = Services.tm.currentThread; - while (result == null) - thread.processNextEvent(true); - - return result; - }, - - _getOne: function _getOne(prefName, type) { - let prefs = []; - prefs.push({ - name: prefName, - type: type, - }); - let values = this._get(prefs); - if (values.length != 1) { - throw new Error("Got too many values: " + values.length); - } - return values[0].value; - }, - - getBoolPref: function getBoolPref(prefName) { - return this._getOne(prefName, "bool"); - }, - - getCharPref: function getCharPref(prefName) { - return this._getOne(prefName, "string"); - }, - - getIntPref: function getIntPref(prefName) { - return this._getOne(prefName, "int"); - }, - - /** - * Invoke `observer` after a change to the preference `domain` in - * the current branch. - * - * `observer` should implement the nsIObserver.observe interface. - */ - addObserver: function addObserver(domain, observer, holdWeak) { - if (!domain) - throw new Error("domain must not be null"); - if (!observer) - throw new Error("observer must not be null"); - if (holdWeak) - throw new Error("Weak references not yet implemented."); - - if (!this._observers.hasOwnProperty(domain)) - this._observers[domain] = []; - if (this._observers[domain].indexOf(observer) > -1) - return; - - this._observers[domain].push(observer); - - this._updateAndroidListener(); - }, - - /** - * Do not invoke `observer` after a change to the preference - * `domain` in the current branch. - */ - removeObserver: function removeObserver(domain, observer) { - if (!this._observers.hasOwnProperty(domain)) - return; - let index = this._observers[domain].indexOf(observer); - if (index < 0) - return; - - this._observers[domain].splice(index, 1); - if (this._observers[domain].length < 1) - delete this._observers[domain]; - - this._updateAndroidListener(); - }, - - _updateAndroidListener: function _updateAndroidListener() { - if (this._listening && Object.keys(this._observers).length < 1) - this._uninstallAndroidListener(); - if (!this._listening && Object.keys(this._observers).length > 0) - this._installAndroidListener(); - }, - - _installAndroidListener: function _installAndroidListener() { - if (this._listening) - return; - this._listening = true; - - Services.obs.addObserver(this, "SharedPreferences:Changed", false); - Messaging.sendRequest({ - type: "SharedPreferences:Observe", - enable: true, - scope: this._scope, - profileName: this._profileName, - branch: this._branch, - }); - }, - - observe: function observe(subject, topic, data) { - if (topic != "SharedPreferences:Changed") { - return; - } - - let msg = JSON.parse(data); - if (msg.scope !== this._scope || - ((this._scope === Scope.PROFILE) && (msg.profileName !== this._profileName)) || - ((this._scope === Scope.GLOBAL) && (msg.branch !== this._branch))) { - return; - } - - if (!this._observers.hasOwnProperty(msg.key)) { - return; - } - - let observers = this._observers[msg.key]; - for (let obs of observers) { - obs.observe(obs, msg.key, msg.value); - } - }, - - _uninstallAndroidListener: function _uninstallAndroidListener() { - if (!this._listening) - return; - this._listening = false; - - Services.obs.removeObserver(this, "SharedPreferences:Changed"); - Messaging.sendRequest({ - type: "SharedPreferences:Observe", - enable: false, - scope: this._scope, - profileName: this._profileName, - branch: this._branch, - }); - }, -}); diff --git a/mobile/android/modules/Snackbars.jsm b/mobile/android/modules/Snackbars.jsm deleted file mode 100644 index 066a28c56..000000000 --- a/mobile/android/modules/Snackbars.jsm +++ /dev/null @@ -1,72 +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 } = Components; - -this.EXPORTED_SYMBOLS = ["Snackbars"]; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm"); - -const LENGTH_INDEFINITE = -2; -const LENGTH_LONG = 0; -const LENGTH_SHORT = -1; - -var Snackbars = { - LENGTH_INDEFINITE: LENGTH_INDEFINITE, - LENGTH_LONG: LENGTH_LONG, - LENGTH_SHORT: LENGTH_SHORT, - - show: function(aMessage, aDuration, aOptions) { - - // Takes care of the deprecated toast calls - if (typeof aDuration === "string") { - [aDuration, aOptions] = migrateToastIfNeeded(aDuration, aOptions); - } - - let msg = { - type: 'Snackbar:Show', - message: aMessage, - duration: aDuration, - }; - - if (aOptions && aOptions.backgroundColor) { - msg.backgroundColor = aOptions.backgroundColor; - } - - if (aOptions && aOptions.action) { - msg.action = {}; - - if (aOptions.action.label) { - msg.action.label = aOptions.action.label; - } - - Messaging.sendRequestForResult(msg).then(result => aOptions.action.callback()); - } else { - Messaging.sendRequest(msg); - } - } -}; - -function migrateToastIfNeeded(aDuration, aOptions) { - let duration; - if (aDuration === "long") { - duration = LENGTH_LONG; - } - else { - duration = LENGTH_SHORT; - } - - let options = {}; - if (aOptions && aOptions.button) { - options.action = { - label: aOptions.button.label, - callback: () => aOptions.button.callback(), - }; - } - return [duration, options]; -}
\ No newline at end of file diff --git a/mobile/android/modules/TabMirror.jsm b/mobile/android/modules/TabMirror.jsm deleted file mode 100644 index 72a640ec8..000000000 --- a/mobile/android/modules/TabMirror.jsm +++ /dev/null @@ -1,153 +0,0 @@ -/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 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 } = Components; -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); - -const CONFIG = { iceServers: [{ "urls": ["stun:stun.services.mozilla.com"] }] }; - -var log = Cu.import("resource://gre/modules/AndroidLog.jsm", - {}).AndroidLog.d.bind(null, "TabMirror"); - -var failure = function(x) { - log("ERROR: " + JSON.stringify(x)); -}; - -var TabMirror = function(deviceId, window) { - - this.deviceId = deviceId; - // Save RTCSessionDescription and RTCIceCandidate for later when the window object is not available. - this.RTCSessionDescription = window.RTCSessionDescription; - this.RTCIceCandidate = window.RTCIceCandidate; - - Services.obs.addObserver((aSubject, aTopic, aData) => this._processMessage(aData), "MediaPlayer:Response", false); - this._sendMessage({ start: true }); - this._window = window; - this._pc = new window.RTCPeerConnection(CONFIG, {}); - if (!this._pc) { - throw "Failure creating Webrtc object"; - } - -}; - -TabMirror.prototype = { - _window: null, - _screenSize: { width: 1280, height: 720 }, - _pc: null, - _start: function() { - this._pc.onicecandidate = this._onIceCandidate.bind(this); - - let windowId = this._window.BrowserApp.selectedBrowser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID; - let constraints = { - video: { - mediaSource: "browser", - browserWindow: windowId, - scrollWithPage: true, - advanced: [ - { width: { min: 0, max: this._screenSize.width }, - height: { min: 0, max: this._screenSize.height } - }, - { aspectRatio: this._screenSize.width / this._screenSize.height } - ] - } - }; - - this._window.navigator.mozGetUserMedia(constraints, this._onGumSuccess.bind(this), this._onGumFailure.bind(this)); - }, - - _processMessage: function(data) { - if (!data) { - return; - } - - let msg = JSON.parse(data); - - if (!msg) { - return; - } - - if (msg.sdp && msg.type === "answer") { - this._processAnswer(msg); - } else if (msg.type == "size") { - if (msg.height) { - this._screenSize.height = msg.height; - } - if (msg.width) { - this._screenSize.width = msg.width; - } - this._start(); - } else if (msg.candidate) { - this._processIceCandidate(msg); - } else { - log("dropping unrecognized message: " + JSON.stringify(msg)); - } - }, - - // Signaling methods - _processAnswer: function(msg) { - this._pc.setRemoteDescription(new this.RTCSessionDescription(msg), - this._setRemoteAnswerSuccess.bind(this), failure); - }, - - _processIceCandidate: function(msg) { - // WebRTC generates a warning if the success and fail callbacks are not passed in. - this._pc.addIceCandidate(new this.RTCIceCandidate(msg), () => log("Ice Candiated added successfuly"), () => log("Failed to add Ice Candidate")); - }, - - _setRemoteAnswerSuccess: function() { - }, - - _setLocalSuccessOffer: function(sdp) { - this._sendMessage(sdp); - }, - - _createOfferSuccess: function(sdp) { - this._pc.setLocalDescription(sdp, () => this._setLocalSuccessOffer(sdp), failure); - }, - - _onIceCandidate: function (msg) { - log("NEW Ice Candidate: " + JSON.stringify(msg.candidate)); - this._sendMessage(msg.candidate); - }, - - _ready: function() { - this._pc.createOffer(this._createOfferSuccess.bind(this), failure); - }, - - _onGumSuccess: function(stream){ - this._pc.addStream(stream); - this._ready(); - }, - - _onGumFailure: function() { - log("Could not get video stream"); - this._pc.close(); - }, - - _sendMessage: function(msg) { - if (this.deviceId) { - let obj = { - type: "MediaPlayer:Message", - id: this.deviceId, - data: JSON.stringify(msg) - }; - Messaging.sendRequest(obj); - } - }, - - stop: function() { - if (this.deviceId) { - let obj = { - type: "MediaPlayer:End", - id: this.deviceId - }; - Services.androidBridge.handleGeckoMessage(obj); - } - }, -}; - - -this.EXPORTED_SYMBOLS = ["TabMirror"]; diff --git a/mobile/android/modules/WebsiteMetadata.jsm b/mobile/android/modules/WebsiteMetadata.jsm deleted file mode 100644 index 39af9ddeb..000000000 --- a/mobile/android/modules/WebsiteMetadata.jsm +++ /dev/null @@ -1,475 +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 } = Components; - -this.EXPORTED_SYMBOLS = ["WebsiteMetadata"]; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); - -var WebsiteMetadata = { - /** - * Asynchronously parse the document extract metadata. A 'Website:Metadata' event with the metadata - * will be sent. - */ - parseAsynchronously: function(doc) { - Task.spawn(function() { - let metadata = getMetadata(doc, doc.location.href, { - image_url: metadataRules['image_url'] - }); - - // No metadata was extracted, so don't bother sending it. - if (Object.keys(metadata).length === 0) { - return; - } - - let msg = { - type: 'Website:Metadata', - location: doc.location.href, - metadata: metadata, - }; - - Messaging.sendRequest(msg); - }); - } -}; - -// ################################################################################################# -// # Modified version of makeUrlAbsolute() to not import url parser library (and dependencies) -// ################################################################################################# - -function makeUrlAbsolute(context, relative) { - var a = context.doc.createElement('a'); - a.href = relative; - return a.href; -} - -// ################################################################################################# -// # page-metadata-parser -// # https://github.com/mozilla/page-metadata-parser/ -// # 61c58cbd0f0bf2153df832a388a79c66b288b98c -// ################################################################################################# - -function buildRuleset(name, rules, processors) { - const reversedRules = Array.from(rules).reverse(); - const builtRuleset = ruleset(...reversedRules.map(([query, handler], order) => rule( - dom(query), - node => [{ - score: order, - flavor: name, - notes: handler(node), - }] - ))); - - return (doc, context) => { - const kb = builtRuleset.score(doc); - const maxNode = kb.max(name); - - if (maxNode) { - let value = maxNode.flavors.get(name); - - if (processors) { - processors.forEach(processor => { - value = processor(value, context); - }); - } - - if (value) { - if (value.trim) { - return value.trim(); - } - return value; - } - } - }; -} - -const metadataRules = { - description: { - rules: [ - ['meta[property="og:description"]', node => node.element.getAttribute('content')], - ['meta[name="description"]', node => node.element.getAttribute('content')], - ], - }, - - icon_url: { - rules: [ - ['link[rel="apple-touch-icon"]', node => node.element.getAttribute('href')], - ['link[rel="apple-touch-icon-precomposed"]', node => node.element.getAttribute('href')], - ['link[rel="icon"]', node => node.element.getAttribute('href')], - ['link[rel="fluid-icon"]', node => node.element.getAttribute('href')], - ['link[rel="shortcut icon"]', node => node.element.getAttribute('href')], - ['link[rel="Shortcut Icon"]', node => node.element.getAttribute('href')], - ['link[rel="mask-icon"]', node => node.element.getAttribute('href')], - ], - processors: [ - (icon_url, context) => makeUrlAbsolute(context, icon_url) - ] - }, - - image_url: { - rules: [ - ['meta[property="og:image:secure_url"]', node => node.element.getAttribute('content')], - ['meta[property="og:image:url"]', node => node.element.getAttribute('content')], - ['meta[property="og:image"]', node => node.element.getAttribute('content')], - ['meta[property="twitter:image"]', node => node.element.getAttribute('content')], - ['meta[name="thumbnail"]', node => node.element.getAttribute('content')], - ], - processors: [ - (image_url, context) => makeUrlAbsolute(context, image_url) - ], - }, - - keywords: { - rules: [ - ['meta[name="keywords"]', node => node.element.getAttribute('content')], - ], - processors: [ - (keywords) => keywords.split(',').map((keyword) => keyword.trim()), - ] - }, - - title: { - rules: [ - ['meta[property="og:title"]', node => node.element.getAttribute('content')], - ['meta[property="twitter:title"]', node => node.element.getAttribute('content')], - ['meta[name="hdl"]', node => node.element.getAttribute('content')], - ['title', node => node.element.text], - ], - }, - - type: { - rules: [ - ['meta[property="og:type"]', node => node.element.getAttribute('content')], - ], - }, - - url: { - rules: [ - ['meta[property="og:url"]', node => node.element.getAttribute('content')], - ['link[rel="canonical"]', node => node.element.getAttribute('href')], - ], - }, -}; - -function getMetadata(doc, url, rules) { - const metadata = {}; - const context = {url,doc}; - const ruleSet = rules || metadataRules; - - Object.keys(ruleSet).map(metadataKey => { - const metadataRule = ruleSet[metadataKey]; - - if(Array.isArray(metadataRule.rules)) { - const builtRule = buildRuleset(metadataKey, metadataRule.rules, metadataRule.processors); - metadata[metadataKey] = builtRule(doc, context); - } else { - metadata[metadataKey] = getMetadata(doc, url, metadataRule); - } - }); - - return metadata; -} - -// ################################################################################################# -// # Fathom dependencies resolved -// ################################################################################################# - -// const {forEach} = require('wu'); -function forEach(fn, obj) { - for (let x of obj) { - fn(x); - } -} - -function best(iterable, by, isBetter) { - let bestSoFar, bestKeySoFar; - let isFirst = true; - forEach( - function (item) { - const key = by(item); - if (isBetter(key, bestKeySoFar) || isFirst) { - bestSoFar = item; - bestKeySoFar = key; - isFirst = false; - } - }, - iterable); - if (isFirst) { - throw new Error('Tried to call best() on empty iterable'); - } - return bestSoFar; -} - -// const {max} = require('./utils'); -function max(iterable, by = identity) { - return best(iterable, by, (a, b) => a > b); -} - -// ################################################################################################# -// # Fathom -// # https://github.com/mozilla/fathom -// # cac59e470816f17fc1efd4a34437b585e3e451cd -// ################################################################################################# - -// Get a key of a map, first setting it to a default value if it's missing. -function getDefault(map, key, defaultMaker) { - if (map.has(key)) { - return map.get(key); - } - const defaultValue = defaultMaker(); - map.set(key, defaultValue); - return defaultValue; -} - - -// Construct a filtration network of rules. -function ruleset(...rules) { - const rulesByInputFlavor = new Map(); // [someInputFlavor: [rule, ...]] - - // File each rule under its input flavor: - forEach(rule => getDefault(rulesByInputFlavor, rule.source.inputFlavor, () => []).push(rule), - rules); - - return { - // Iterate over a DOM tree or subtree, building up a knowledgebase, a - // data structure holding scores and annotations for interesting - // elements. Return the knowledgebase. - // - // This is the "rank" portion of the rank-and-yank algorithm. - score: function (tree) { - const kb = knowledgebase(); - - // Introduce the whole DOM into the KB as flavor 'dom' to get - // things started: - const nonterminals = [[{tree}, 'dom']]; // [[node, flavor], [node, flavor], ...] - - // While there are new facts, run the applicable rules over them to - // generate even newer facts. Repeat until everything's fully - // digested. Rules run in no particular guaranteed order. - while (nonterminals.length) { - const [inNode, inFlavor] = nonterminals.pop(); - for (let rule of getDefault(rulesByInputFlavor, inFlavor, () => [])) { - const outFacts = resultsOf(rule, inNode, inFlavor, kb); - for (let fact of outFacts) { - const outNode = kb.nodeForElement(fact.element); - - // No matter whether or not this flavor has been - // emitted before for this node, we multiply the score. - // We want to be able to add rules that refine the - // scoring of a node, without having to rewire the path - // of flavors that winds through the ruleset. - // - // 1 score per Node is plenty. That simplifies our - // data, our rankers, our flavor system (since we don't - // need to represent score axes), and our engine. If - // somebody wants more score axes, they can fake it - // themselves with notes, thus paying only for what - // they eat. (We can even provide functions that help - // with that.) Most rulesets will probably be concerned - // with scoring only 1 thing at a time anyway. So, - // rankers return a score multiplier + 0 or more new - // flavors with optional notes. Facts can never be - // deleted from the KB by rankers (or order would start - // to matter); after all, they're *facts*. - outNode.score *= fact.score; - - // Add a new annotation to a node--but only if there - // wasn't already one of the given flavor already - // there; otherwise there's no point. - // - // You might argue that we might want to modify an - // existing note here, but that would be a bad - // idea. Notes of a given flavor should be - // considered immutable once laid down. Otherwise, the - // order of execution of same-flavored rules could - // matter, hurting pluggability. Emit a new flavor and - // a new note if you want to do that. - // - // Also, choosing not to add a new fact to nonterminals - // when we're not adding a new flavor saves the work of - // running the rules against it, which would be - // entirely redundant and perform no new work (unless - // the rankers were nondeterministic, but don't do - // that). - if (!outNode.flavors.has(fact.flavor)) { - outNode.flavors.set(fact.flavor, fact.notes); - kb.indexNodeByFlavor(outNode, fact.flavor); // TODO: better encapsulation rather than indexing explicitly - nonterminals.push([outNode, fact.flavor]); - } - } - } - } - return kb; - } - }; -} - - -// Construct a container for storing and querying facts, where a fact has a -// flavor (used to dispatch further rules upon), a corresponding DOM element, a -// score, and some other arbitrary notes opaque to fathom. -function knowledgebase() { - const nodesByFlavor = new Map(); // Map{'texty' -> [NodeA], - // 'spiffy' -> [NodeA, NodeB]} - // NodeA = {element: <someElement>, - // - // // Global nodewide score. Add - // // custom ones with notes if - // // you want. - // score: 8, - // - // // Flavors is a map of flavor names to notes: - // flavors: Map{'texty' -> {ownText: 'blah', - // someOtherNote: 'foo', - // someCustomScore: 10}, - // // This is an empty note: - // 'fluffy' -> undefined}} - const nodesByElement = new Map(); - - return { - // Return the "node" (our own data structure that we control) that - // corresponds to a given DOM element, creating one if necessary. - nodeForElement: function (element) { - return getDefault(nodesByElement, - element, - () => ({element, - score: 1, - flavors: new Map()})); - }, - - // Return the highest-scored node of the given flavor, undefined if - // there is none. - max: function (flavor) { - const nodes = nodesByFlavor.get(flavor); - return nodes === undefined ? undefined : max(nodes, node => node.score); - }, - - // Let the KB know that a new flavor has been added to an element. - indexNodeByFlavor: function (node, flavor) { - getDefault(nodesByFlavor, flavor, () => []).push(node); - }, - - nodesOfFlavor: function (flavor) { - return getDefault(nodesByFlavor, flavor, () => []); - } - }; -} - - -// Apply a rule (as returned by a call to rule()) to a fact, and return the -// new facts that result. -function resultsOf(rule, node, flavor, kb) { - // If more types of rule pop up someday, do fancier dispatching here. - return rule.source.flavor === 'flavor' ? resultsOfFlavorRule(rule, node, flavor) : resultsOfDomRule(rule, node, kb); -} - - -// Pull the DOM tree off the special property of the root "dom" fact, and query -// against it. -function *resultsOfDomRule(rule, specialDomNode, kb) { - // Use the special "tree" property of the special starting node: - const matches = specialDomNode.tree.querySelectorAll(rule.source.selector); - - for (let i = 0; i < matches.length; i++) { // matches is a NodeList, which doesn't conform to iterator protocol - const element = matches[i]; - const newFacts = explicitFacts(rule.ranker(kb.nodeForElement(element))); - for (let fact of newFacts) { - if (fact.element === undefined) { - fact.element = element; - } - if (fact.flavor === undefined) { - throw new Error('Rankers of dom() rules must return a flavor in each fact. Otherwise, there is no way for that fact to be used later.'); - } - yield fact; - } - } -} - - -function *resultsOfFlavorRule(rule, node, flavor) { - const newFacts = explicitFacts(rule.ranker(node)); - - for (let fact of newFacts) { - // If the ranker didn't specify a different element, assume it's - // talking about the one we passed in: - if (fact.element === undefined) { - fact.element = node.element; - } - if (fact.flavor === undefined) { - fact.flavor = flavor; - } - yield fact; - } -} - - -// Take the possibly abbreviated output of a ranker function, and make it -// explicitly an iterable with a defined score. -// -// Rankers can return undefined, which means "no facts", a single fact, or an -// array of facts. -function *explicitFacts(rankerResult) { - const array = (rankerResult === undefined) ? [] : (Array.isArray(rankerResult) ? rankerResult : [rankerResult]); - for (let fact of array) { - if (fact.score === undefined) { - fact.score = 1; - } - yield fact; - } -} - - -// TODO: For the moment, a lot of responsibility is on the rankers to return a -// pretty big data structure of up to 4 properties. This is a bit verbose for -// an arrow function (as I hope we can use most of the time) and the usual case -// will probably be returning just a score multiplier. Make that case more -// concise. - -// TODO: It is likely that rankers should receive the notes of their input type -// as a 2nd arg, for brevity. - - -// Return a condition that uses a DOM selector to find its matches from the -// original DOM tree. -// -// For consistency, Nodes will still be delivered to the transformers, but -// they'll have empty flavors and score = 1. -// -// Condition constructors like dom() and flavor() build stupid, introspectable -// objects that the query engine can read. They don't actually do the query -// themselves. That way, the query planner can be smarter than them, figuring -// out which indices to use based on all of them. (We'll probably keep a heap -// by each dimension's score and a hash by flavor name, for starters.) Someday, -// fancy things like this may be possible: rule(and(tag('p'), klass('snork')), -// ...) -function dom(selector) { - return { - flavor: 'dom', - inputFlavor: 'dom', - selector - }; -} - - -// Return a condition that discriminates on nodes of the knowledgebase by flavor. -function flavor(inputFlavor) { - return { - flavor: 'flavor', - inputFlavor - }; -} - - -function rule(source, ranker) { - return { - source, - ranker - }; -} diff --git a/mobile/android/modules/dbg-browser-actors.js b/mobile/android/modules/dbg-browser-actors.js deleted file mode 100644 index f96a39151..000000000 --- a/mobile/android/modules/dbg-browser-actors.js +++ /dev/null @@ -1,77 +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"; -/** - * Fennec-specific actors. - */ - -const { RootActor } = require("devtools/server/actors/root"); -const { DebuggerServer } = require("devtools/server/main"); -const { BrowserTabList, BrowserAddonList, sendShutdownEvent } = - require("devtools/server/actors/webbrowser"); - -/** - * Construct a root actor appropriate for use in a server running in a - * browser on Android. The returned root actor: - * - respects the factories registered with DebuggerServer.addGlobalActor, - * - uses a MobileTabList to supply tab actors, - * - sends all navigator:browser window documents a Debugger:Shutdown event - * when it exits. - * - * * @param aConnection DebuggerServerConnection - * The conection to the client. - */ -function createRootActor(aConnection) -{ - let parameters = { - tabList: new MobileTabList(aConnection), - addonList: new BrowserAddonList(aConnection), - globalActorFactories: DebuggerServer.globalActorFactories, - onShutdown: sendShutdownEvent - }; - return new RootActor(aConnection, parameters); -} - -/** - * A live list of BrowserTabActors representing the current browser tabs, - * to be provided to the root actor to answer 'listTabs' requests. - * - * This object also takes care of listening for TabClose events and - * onCloseWindow notifications, and exiting the BrowserTabActors concerned. - * - * (See the documentation for RootActor for the definition of the "live - * list" interface.) - * - * @param aConnection DebuggerServerConnection - * The connection in which this list's tab actors may participate. - * - * @see BrowserTabList for more a extensive description of how tab list objects - * work. - */ -function MobileTabList(aConnection) -{ - BrowserTabList.call(this, aConnection); -} - -MobileTabList.prototype = Object.create(BrowserTabList.prototype); - -MobileTabList.prototype.constructor = MobileTabList; - -MobileTabList.prototype._getSelectedBrowser = function(aWindow) { - return aWindow.BrowserApp.selectedBrowser; -}; - -MobileTabList.prototype._getChildren = function(aWindow) { - return aWindow.BrowserApp.tabs.map(tab => tab.browser); -}; - -exports.register = function(handle) { - handle.setRootActor(createRootActor); -}; - -exports.unregister = function(handle) { - handle.setRootActor(null); -}; diff --git a/mobile/android/modules/moz.build b/mobile/android/modules/moz.build deleted file mode 100644 index dd62e484a..000000000 --- a/mobile/android/modules/moz.build +++ /dev/null @@ -1,32 +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/. - -EXTRA_JS_MODULES += [ - 'Accounts.jsm', - 'AndroidLog.jsm', - 'dbg-browser-actors.js', - 'DelayedInit.jsm', - 'DownloadNotifications.jsm', - 'HelperApps.jsm', - 'Home.jsm', - 'HomeProvider.jsm', - 'JavaAddonManager.jsm', - 'JNI.jsm', - 'LightweightThemeConsumer.jsm', - 'MediaPlayerApp.jsm', - 'Messaging.jsm', - 'NetErrorHelper.jsm', - 'Notifications.jsm', - 'PageActions.jsm', - 'Prompt.jsm', - 'RuntimePermissions.jsm', - 'Sanitizer.jsm', - 'SharedPreferences.jsm', - 'Snackbars.jsm', - 'SSLExceptions.jsm', - 'TabMirror.jsm', - 'WebsiteMetadata.jsm' -] |