Last active
August 29, 2015 13:57
-
-
Save modeswitch/9537777 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/services/cloudsync/CloudSync.js b/services/cloudsync/CloudSync.js | |
new file mode 100644 | |
index 0000000..8bdff6b | |
--- /dev/null | |
+++ b/services/cloudsync/CloudSync.js | |
@@ -0,0 +1,71 @@ | |
+/* This Source Code Form is subject to the terms of the Mozilla Public | |
+ * License, v. 2.0. If a copy of the MPL was not distributed with this | |
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
+ | |
+const Cc = Components.classes; | |
+const Ci = Components.interfaces; | |
+const Cu = Components.utils; | |
+ | |
+Cu.import("resource://gre/modules/XPCOMUtils.jsm"); | |
+Cu.import("resource://gre/modules/Services.jsm"); | |
+Cu.import("resource://gre/modules/FileUtils.jsm"); | |
+Cu.import("resource://gre/modules/Promise.jsm"); | |
+Cu.import("resource://services-sync/util.js"); | |
+ | |
+const SYNC_PREFS_BRANCH = "services.cloudsync."; | |
+ | |
+ | |
+function CloudSyncService() { | |
+ this.wrappedJSObject = this; | |
+ this.ready = false; | |
+} | |
+CloudSyncService.prototype = { | |
+ classID: Components.ID("{21e3b97b-4e8b-42e7-b494-bb4a788e4168}"), | |
+ | |
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, | |
+ Ci.nsISupportsWeakReference]), | |
+ | |
+ ensureLoaded: function () { | |
+ Components.utils.import("resource://services-cloudsync/main.js"); | |
+ | |
+ // Side-effect of accessing the service is that it is instantiated. | |
+ CloudSync.Service; | |
+ }, | |
+ | |
+ whenLoaded: function() { | |
+ if (this.ready) { | |
+ return Promise.resolve(); | |
+ } | |
+ let deferred = Promise.defer(); | |
+ | |
+ Services.obs.addObserver(function onReady() { | |
+ Services.obs.removeObserver(onReady, "cloudsync:service:ready"); | |
+ deferred.resolve(); | |
+ }, "cloudsync:service:ready", false); | |
+ this.ensureLoaded(); | |
+ return deferred.promise; | |
+ }, | |
+ | |
+ observe: function (subject, topic, data) { | |
+ switch (topic) { | |
+ case "app-startup": | |
+ let os = Cc["@mozilla.org/observer-service;1"]. | |
+ getService(Ci.nsIObserverService); | |
+ os.addObserver(this, "final-ui-startup", true); | |
+ break; | |
+ | |
+ case "final-ui-startup": | |
+ // Force Weave service to load if it hasn't triggered from overlays | |
+ this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); | |
+ this.timer.initWithCallback({ | |
+ notify: function() { | |
+ this.ensureLoaded(); | |
+ }.bind(this) | |
+ }, 10000, Ci.nsITimer.TYPE_ONE_SHOT); | |
+ break; | |
+ } | |
+ } | |
+}; | |
+ | |
+const components = [CloudSyncService]; | |
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); | |
diff --git a/services/cloudsync/CloudSyncComponents.manifest b/services/cloudsync/CloudSyncComponents.manifest | |
new file mode 100644 | |
index 0000000..76ede57 | |
--- /dev/null | |
+++ b/services/cloudsync/CloudSyncComponents.manifest | |
@@ -0,0 +1,5 @@ | |
+component {21e3b97b-4e8b-42e7-b494-bb4a788e4168} CloudSync.js | |
+contract @mozilla.org/cloudsync/service;1 {21e3b97b-4e8b-42e7-b494-bb4a788e4168} | |
+category app-startup CloudSyncService service,@mozilla.org/cloudsync/service;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} application={92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} application={99bceaaa-e3c6-48c1-b981-ef9b46b67d60} | |
+ | |
+resource services-cloudsync resource://gre/modules/services-cloudsync/ | |
\ No newline at end of file | |
diff --git a/services/cloudsync/Makefile.in b/services/cloudsync/Makefile.in | |
new file mode 100644 | |
index 0000000..2841eb0 | |
--- /dev/null | |
+++ b/services/cloudsync/Makefile.in | |
@@ -0,0 +1,30 @@ | |
+# 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/. | |
+ | |
+# Definitions used by constants.js. | |
+cloudsync_version := 1.0.0 | |
+cloudsync_id := {a133808b-ec8d-4cab-8767-4ef9cf8f66c3} | |
+ | |
+# Preprocess files. | |
+CLOUDSYNC_PP := modules/constants.js | |
+CLOUDSYNC_PP_FLAGS := \ | |
+ -Dcloudsync_version=$(cloudsync_version) \ | |
+ -Dcloudsync_id='$(cloudsync_id)' | |
+CLOUDSYNC_PP_PATH = $(FINAL_TARGET)/modules/services-cloudsync | |
+PP_TARGETS += CLOUDSYNC_PP | |
+ | |
+# The set of core JavaScript modules for Sync. These are copied as-is. | |
+cloudsync_modules := \ | |
+ local.js \ | |
+ main.js \ | |
+ service.js \ | |
+ tabs.js \ | |
+ $(NULL) | |
+ | |
+PREF_JS_EXPORTS := $(srcdir)/services-cloudsync.js | |
+ | |
+# Install JS module files. | |
+CLOUDSYNC_MAIN_FILES := $(addprefix modules/,$(cloudsync_modules)) | |
+CLOUDSYNC_MAIN_DEST = $(FINAL_TARGET)/modules/services-cloudsync | |
+INSTALL_TARGETS += CLOUDSYNC_MAIN | |
\ No newline at end of file | |
diff --git a/services/cloudsync/modules/constants.js b/services/cloudsync/modules/constants.js | |
new file mode 100644 | |
index 0000000..d543d21 | |
--- /dev/null | |
+++ b/services/cloudsync/modules/constants.js | |
@@ -0,0 +1,189 @@ | |
+#filter substitution | |
+/* This Source Code Form is subject to the terms of the Mozilla Public | |
+ * License, v. 2.0. If a copy of the MPL was not distributed with this | |
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
+ | |
+// Process each item in the "constants hash" to add to "global" and give a name | |
+this.EXPORTED_SYMBOLS = [((this[key] = val), key) for ([key, val] in Iterator({ | |
+ | |
+CLOUDSYNC_VERSION: "@cloudsync_version@", | |
+ | |
+// Sync Server API version that the client supports. | |
+CLOUDSYNC_API_VERSION: "1.0", | |
+USER_API_VERSION: "1.0", | |
+MISC_API_VERSION: "1.0", | |
+ | |
+// Version of the data format this client supports. The data format describes | |
+// how records are packaged; this is separate from the Server API version and | |
+// the per-engine cleartext formats. | |
+STORAGE_VERSION: 5, | |
+PREFS_BRANCH: "services.cloudsync.", | |
+ | |
+/* | |
+// Host "key" to access Weave Identity in the password manager | |
+PWDMGR_HOST: "chrome://cloudsync", | |
+PWDMGR_PASSWORD_REALM: "Mozilla Services Password", | |
+PWDMGR_PASSPHRASE_REALM: "Mozilla Services Encryption Passphrase", | |
+PWDMGR_KEYBUNDLE_REALM: "Mozilla Services Key Bundles", | |
+ | |
+// Put in [] because those aren't allowed in a collection name. | |
+DEFAULT_KEYBUNDLE_NAME: "[default]", | |
+ | |
+// Our extra input to SHA256-HMAC in generateEntry. | |
+// This includes the full crypto spec; change this when our algo changes. | |
+HMAC_INPUT: "Sync-AES_256_CBC-HMAC256", | |
+ | |
+// Key dimensions. | |
+SYNC_KEY_ENCODED_LENGTH: 26, | |
+SYNC_KEY_DECODED_LENGTH: 16, | |
+SYNC_KEY_HYPHENATED_LENGTH: 31, // 26 chars, 5 hyphens. | |
+ | |
+NO_SYNC_NODE_INTERVAL: 10 * 60 * 1000, // 10 minutes | |
+ | |
+MAX_ERROR_COUNT_BEFORE_BACKOFF: 3, | |
+MAX_IGNORE_ERROR_COUNT: 5, | |
+ | |
+// Backoff intervals | |
+MINIMUM_BACKOFF_INTERVAL: 15 * 60 * 1000, // 15 minutes | |
+MAXIMUM_BACKOFF_INTERVAL: 8 * 60 * 60 * 1000, // 8 hours | |
+ | |
+// HMAC event handling timeout. | |
+// 10 minutes: a compromise between the multi-desktop sync interval | |
+// and the mobile sync interval. | |
+HMAC_EVENT_INTERVAL: 600000, | |
+ | |
+// How long to wait between sync attempts if the Master Password is locked. | |
+MASTER_PASSWORD_LOCKED_RETRY_INTERVAL: 15 * 60 * 1000, // 15 minutes | |
+ | |
+// How long to initially wait between sync attempts if the identity manager is | |
+// not ready. As we expect this to become ready relatively quickly, we retry | |
+// in (IDENTITY_NOT_READY_RETRY_INTERVAL * num_failures) seconds. | |
+IDENTITY_NOT_READY_RETRY_INTERVAL: 5 * 1000, // 5 seconds | |
+ | |
+// Separate from the ID fetch batch size to allow tuning for mobile. | |
+MOBILE_BATCH_SIZE: 50, | |
+ | |
+// 50 is hardcoded here because of URL length restrictions. | |
+// (GUIDs can be up to 64 chars long.) | |
+// Individual engines can set different values for their limit if their | |
+// identifiers are shorter. | |
+DEFAULT_GUID_FETCH_BATCH_SIZE: 50, | |
+DEFAULT_MOBILE_GUID_FETCH_BATCH_SIZE: 50, | |
+ | |
+// Default batch size for applying incoming records. | |
+DEFAULT_STORE_BATCH_SIZE: 1, | |
+HISTORY_STORE_BATCH_SIZE: 50, // same as MOBILE_BATCH_SIZE | |
+FORMS_STORE_BATCH_SIZE: 50, // same as MOBILE_BATCH_SIZE | |
+PASSWORDS_STORE_BATCH_SIZE: 50, // same as MOBILE_BATCH_SIZE | |
+ADDONS_STORE_BATCH_SIZE: 1000000, // process all addons at once | |
+APPS_STORE_BATCH_SIZE: 50, // same as MOBILE_BATCH_SIZE | |
+ | |
+// score thresholds for early syncs | |
+SINGLE_USER_THRESHOLD: 1000, | |
+MULTI_DEVICE_THRESHOLD: 300, | |
+ | |
+// Other score increment constants | |
+SCORE_INCREMENT_SMALL: 1, | |
+SCORE_INCREMENT_MEDIUM: 10, | |
+ | |
+// Instant sync score increment | |
+SCORE_INCREMENT_XLARGE: 300 + 1, //MULTI_DEVICE_THRESHOLD + 1 | |
+ | |
+// Delay before incrementing global score | |
+SCORE_UPDATE_DELAY: 100, | |
+ | |
+// Delay for the back observer debouncer. This is chosen to be longer than any | |
+// observed spurious idle/back events and short enough to pre-empt user activity. | |
+IDLE_OBSERVER_BACK_DELAY: 100, | |
+ | |
+// Number of records to upload in a single POST (multiple POSTS if exceeded) | |
+// FIXME: Record size limit is 256k (new cluster), so this can be quite large! | |
+// (Bug 569295) | |
+MAX_UPLOAD_RECORDS: 100, | |
+MAX_HISTORY_UPLOAD: 5000, | |
+MAX_HISTORY_DOWNLOAD: 5000, | |
+ | |
+// Top-level statuses: | |
+STATUS_OK: "success.status_ok", | |
+SYNC_FAILED: "error.sync.failed", | |
+LOGIN_FAILED: "error.login.failed", | |
+SYNC_FAILED_PARTIAL: "error.sync.failed_partial", | |
+CLIENT_NOT_CONFIGURED: "service.client_not_configured", | |
+STATUS_DISABLED: "service.disabled", | |
+MASTER_PASSWORD_LOCKED: "service.master_password_locked", | |
+ | |
+// success states | |
+LOGIN_SUCCEEDED: "success.login", | |
+SYNC_SUCCEEDED: "success.sync", | |
+ENGINE_SUCCEEDED: "success.engine", | |
+ | |
+// login failure status codes: | |
+LOGIN_FAILED_NO_USERNAME: "error.login.reason.no_username", | |
+LOGIN_FAILED_NO_PASSWORD: "error.login.reason.no_password2", | |
+LOGIN_FAILED_NO_PASSPHRASE: "error.login.reason.no_recoverykey", | |
+LOGIN_FAILED_NETWORK_ERROR: "error.login.reason.network", | |
+LOGIN_FAILED_SERVER_ERROR: "error.login.reason.server", | |
+LOGIN_FAILED_INVALID_PASSPHRASE: "error.login.reason.recoverykey", | |
+LOGIN_FAILED_LOGIN_REJECTED: "error.login.reason.account", | |
+LOGIN_FAILED_NOT_READY: "error.login.reason.initializing", | |
+ | |
+// sync failure status codes | |
+METARECORD_DOWNLOAD_FAIL: "error.sync.reason.metarecord_download_fail", | |
+VERSION_OUT_OF_DATE: "error.sync.reason.version_out_of_date", | |
+DESKTOP_VERSION_OUT_OF_DATE: "error.sync.reason.desktop_version_out_of_date", | |
+SETUP_FAILED_NO_PASSPHRASE: "error.sync.reason.setup_failed_no_passphrase", | |
+CREDENTIALS_CHANGED: "error.sync.reason.credentials_changed", | |
+ABORT_SYNC_COMMAND: "aborting sync, process commands said so", | |
+NO_SYNC_NODE_FOUND: "error.sync.reason.no_node_found", | |
+OVER_QUOTA: "error.sync.reason.over_quota", | |
+PROLONGED_SYNC_FAILURE: "error.sync.prolonged_failure", | |
+SERVER_MAINTENANCE: "error.sync.reason.serverMaintenance", | |
+ | |
+RESPONSE_OVER_QUOTA: "14", | |
+ | |
+// engine failure status codes | |
+ENGINE_UPLOAD_FAIL: "error.engine.reason.record_upload_fail", | |
+ENGINE_DOWNLOAD_FAIL: "error.engine.reason.record_download_fail", | |
+ENGINE_UNKNOWN_FAIL: "error.engine.reason.unknown_fail", | |
+ENGINE_APPLY_FAIL: "error.engine.reason.apply_fail", | |
+ENGINE_METARECORD_DOWNLOAD_FAIL: "error.engine.reason.metarecord_download_fail", | |
+ENGINE_METARECORD_UPLOAD_FAIL: "error.engine.reason.metarecord_upload_fail", | |
+ | |
+JPAKE_ERROR_CHANNEL: "jpake.error.channel", | |
+JPAKE_ERROR_NETWORK: "jpake.error.network", | |
+JPAKE_ERROR_SERVER: "jpake.error.server", | |
+JPAKE_ERROR_TIMEOUT: "jpake.error.timeout", | |
+JPAKE_ERROR_INTERNAL: "jpake.error.internal", | |
+JPAKE_ERROR_INVALID: "jpake.error.invalid", | |
+JPAKE_ERROR_NODATA: "jpake.error.nodata", | |
+JPAKE_ERROR_KEYMISMATCH: "jpake.error.keymismatch", | |
+JPAKE_ERROR_WRONGMESSAGE: "jpake.error.wrongmessage", | |
+JPAKE_ERROR_USERABORT: "jpake.error.userabort", | |
+JPAKE_ERROR_DELAYUNSUPPORTED: "jpake.error.delayunsupported", | |
+ | |
+// info types for Service.getStorageInfo | |
+INFO_COLLECTIONS: "collections", | |
+INFO_COLLECTION_USAGE: "collection_usage", | |
+INFO_COLLECTION_COUNTS: "collection_counts", | |
+INFO_QUOTA: "quota", | |
+ | |
+// Ways that a sync can be disabled (messages only to be printed in debug log) | |
+kSyncMasterPasswordLocked: "User elected to leave Master Password locked", | |
+kSyncWeaveDisabled: "Weave is disabled", | |
+kSyncNetworkOffline: "Network is offline", | |
+kSyncBackoffNotMet: "Trying to sync before the server said it's okay", | |
+kFirstSyncChoiceNotMade: "User has not selected an action for first sync", | |
+ | |
+// Application IDs | |
+FIREFOX_ID: "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}", | |
+FENNEC_ID: "{a23983c0-fd0e-11dc-95ff-0800200c9a66}", | |
+SEAMONKEY_ID: "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}", | |
+TEST_HARNESS_ID: "[email protected]", | |
+ | |
+MIN_PP_LENGTH: 12, | |
+MIN_PASS_LENGTH: 8, | |
+ | |
+LOG_DATE_FORMAT: "%Y-%m-%d %H:%M:%S", | |
+*/ | |
+ | |
+}))]; | |
diff --git a/services/cloudsync/modules/local.js b/services/cloudsync/modules/local.js | |
new file mode 100644 | |
index 0000000..6310e97 | |
--- /dev/null | |
+++ b/services/cloudsync/modules/local.js | |
@@ -0,0 +1,58 @@ | |
+/* This Source Code Form is subject to the terms of the Mozilla Public | |
+ * License, v. 2.0. If a copy of the MPL was not distributed with this | |
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
+ | |
+this.EXPORTED_SYMBOLS = ["Local"]; | |
+ | |
+const Cc = Components.classes; | |
+const Ci = Components.interfaces; | |
+const Cr = Components.results; | |
+const Cu = Components.utils; | |
+ | |
+Cu.import("resource://services-common/stringbundle.js"); | |
+Cu.import("resource://services-sync/util.js"); | |
+ | |
+function _Local() { | |
+ | |
+} | |
+ | |
+_Local.prototype = { | |
+ get id() { | |
+ // Generate a random GUID id we don't have one | |
+ let localID = Svc.Prefs.get("client.GUID", ""); | |
+ return localID == "" ? this.localID = Utils.makeGUID() : localID; | |
+ }, | |
+ | |
+ get name() { | |
+ let localName = Svc.Prefs.get("client.name", ""); | |
+ if (localName != "") | |
+ return localName; | |
+ | |
+ // Generate a client name if we don't have a useful one yet | |
+ let env = Cc["@mozilla.org/process/environment;1"] | |
+ .getService(Ci.nsIEnvironment); | |
+ let user = env.get("USER") || env.get("USERNAME") || | |
+ Svc.Prefs.get("account") || Svc.Prefs.get("username"); | |
+ | |
+ let appName; | |
+ let brand = new StringBundle("chrome://branding/locale/brand.properties"); | |
+ let brandName = brand.get("brandShortName"); | |
+ try { | |
+ let syncStrings = new StringBundle("chrome://browser/locale/sync.properties"); | |
+ appName = syncStrings.getFormattedString("sync.defaultAccountApplication", [brandName]); | |
+ } catch (ex) {} | |
+ appName = appName || brandName; | |
+ | |
+ let system = | |
+ // 'device' is defined on unix systems | |
+ Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2).get("device") || | |
+ // hostname of the system, usually assigned by the user or admin | |
+ Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2).get("host") || | |
+ // fall back on ua info string | |
+ Cc["@mozilla.org/network/protocol;1?name=http"].getService(Ci.nsIHttpProtocolHandler).oscpu; | |
+ | |
+ return this.localName = Str.sync.get("client.name2", [user, appName, system]); | |
+ } | |
+}; | |
+ | |
+this.Local = new _Local(); | |
\ No newline at end of file | |
diff --git a/services/cloudsync/modules/main.js b/services/cloudsync/modules/main.js | |
new file mode 100644 | |
index 0000000..9430d0a | |
--- /dev/null | |
+++ b/services/cloudsync/modules/main.js | |
@@ -0,0 +1,26 @@ | |
+/* This Source Code Form is subject to the terms of the Mozilla Public | |
+ * License, v. 2.0. If a copy of the MPL was not distributed with this | |
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
+ | |
+this.EXPORTED_SYMBOLS = ['CloudSync']; | |
+ | |
+this.CloudSync = {}; | |
+let lazies = { | |
+ "service.js": ["Service"], | |
+ "local.js": ["Local"], | |
+ "tabs.js": ["Tabs"], | |
+}; | |
+ | |
+function lazyImport(module, dest, props) { | |
+ function getter(prop) function() { | |
+ let ns = {}; | |
+ Components.utils.import(module, ns); | |
+ delete dest[prop]; | |
+ return dest[prop] = ns[prop]; | |
+ }; | |
+ props.forEach(function(prop) dest.__defineGetter__(prop, getter(prop))); | |
+} | |
+ | |
+for (let mod in lazies) { | |
+ lazyImport("resource://services-cloudsync/" + mod, CloudSync, lazies[mod]); | |
+} | |
\ No newline at end of file | |
diff --git a/services/cloudsync/modules/service.js b/services/cloudsync/modules/service.js | |
new file mode 100644 | |
index 0000000..b723176 | |
--- /dev/null | |
+++ b/services/cloudsync/modules/service.js | |
@@ -0,0 +1,40 @@ | |
+/* This Source Code Form is subject to the terms of the Mozilla Public | |
+ * License, v. 2.0. If a copy of the MPL was not distributed with this | |
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
+ | |
+this.EXPORTED_SYMBOLS = ["Service"]; | |
+ | |
+const Cc = Components.classes; | |
+const Ci = Components.interfaces; | |
+const Cr = Components.results; | |
+const Cu = Components.utils; | |
+ | |
+Cu.import("resource://services-sync/util.js"); | |
+ | |
+function CloudSyncllService() { | |
+ this._notify = Utils.notify("cloudsync:service:"); | |
+} | |
+ | |
+CloudSyncllService.prototype = { | |
+ onStartup: function onStartup() { | |
+ // Send an event now that Weave service is ready. We don't do this | |
+ // synchronously so that observers can import this module before | |
+ // registering an observer. | |
+ Utils.nextTick(function onNextTick() { | |
+ // UI code uses the flag on the XPCOM service so it doesn't have | |
+ // to load a bunch of modules. | |
+ let xps = Cc["@mozilla.org/cloudsync/service;1"] | |
+ .getService(Ci.nsISupports) | |
+ .wrappedJSObject; | |
+ xps.ready = true; | |
+ | |
+ Svc.Obs.notify("cloudsync:service:ready"); | |
+ }.bind(this)); | |
+ }, | |
+ | |
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, | |
+ Ci.nsISupportsWeakReference]), | |
+}; | |
+ | |
+this.Service = new CloudSyncllService(); | |
+Service.onStartup(); | |
\ No newline at end of file | |
diff --git a/services/cloudsync/modules/tabs.js b/services/cloudsync/modules/tabs.js | |
new file mode 100644 | |
index 0000000..81c22d3 | |
--- /dev/null | |
+++ b/services/cloudsync/modules/tabs.js | |
@@ -0,0 +1,214 @@ | |
+/* This Source Code Form is subject to the terms of the Mozilla Public | |
+ * License, v. 2.0. If a copy of the MPL was not distributed with this | |
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
+ | |
+this.EXPORTED_SYMBOLS = ["Tabs"]; | |
+ | |
+const Cc = Components.classes; | |
+const Ci = Components.interfaces; | |
+const Cr = Components.results; | |
+const Cu = Components.utils; | |
+ | |
+Cu.import("resource://services-common/observers.js"); | |
+Cu.import("resource://services-sync/util.js"); | |
+ | |
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", | |
+ "resource://gre/modules/PrivateBrowsingUtils.jsm"); | |
+ | |
+function _Tabs() { | |
+ this.cachedRemotesInfo = {}; | |
+ this.listeners = []; | |
+ | |
+ this.onTab = Utils.bind2(this, this.onTab); | |
+ this._unregisterListeners = Utils.bind2(this, this._unregisterListeners); | |
+ | |
+ Observers.notify("cloudsync:tabs:start", null); | |
+} | |
+ | |
+_Tabs.prototype = { | |
+ addEventListener: function addEventListener(type, listener) { | |
+ if("change" !== type || | |
+ this.listeners.indexOf(listener) >= 0) { | |
+ return; | |
+ } | |
+ | |
+ this.listeners.push(listener); | |
+ if(1 == this.listeners.length) { | |
+ this.resume(); | |
+ } | |
+ }, | |
+ | |
+ removeEventListener: function removeEventListener(type, listener) { | |
+ var i = this.listeners.indexOf(listener); | |
+ if("change" !== type || | |
+ i < 0) { | |
+ return; | |
+ } | |
+ | |
+ this.listeners.splice(i, 1); | |
+ if(0 == this.listeners.length) { | |
+ this.suspend(); | |
+ } | |
+ }, | |
+ | |
+ // [ | |
+ // client: { | |
+ // id: client id, | |
+ // name: client name, | |
+ // isMobile: true if client is a mobile device, false otherwise | |
+ // }, | |
+ // timestamp: the time that this info was generated | |
+ // tabs: [ array of tabs, with the following format | |
+ // { | |
+ // title: the page title of the tab | |
+ // lastUsed: integer in epoch time | |
+ // icon: url to an icon | |
+ // urlHistory: array of urls | |
+ // } | |
+ // ], | |
+ // ] | |
+ setRemoteTabs: function setRemoteTabs(tabsInfo) { | |
+ tabsInfo.forEach(function(info) { | |
+ let client = info.client; | |
+ if(this.cachedRemotesInfo[client.id]) { | |
+ let current = this.cachedRemotesInfo[client.id]; | |
+ if(current.timestamp > info.timestamp) { | |
+ return; | |
+ } | |
+ } | |
+ | |
+ this.cachedRemotesInfo[client.id] = info; | |
+ }.bind(this)); | |
+ | |
+ Observers.notify("cloudsync:tabs:set-remote-tabs", null); | |
+ }, | |
+ | |
+ getLocalTabs: function getLocalTabs(filter) { | |
+ filter = (undefined === filter) ? true : filter; | |
+ let filteredUrls = new RegExp(Svc.Prefs.get("engine.tabs.filteredUrls"), "i"); | |
+ | |
+ let allTabs = []; | |
+ | |
+ let currentState = JSON.parse(Svc.Session.getBrowserState()); | |
+ currentState.windows.forEach(function (window) { | |
+ if (window.isPrivate) { | |
+ return; | |
+ } | |
+ window.tabs.forEach(function (tab) { | |
+ // Make sure there are history entries to look at. | |
+ if (!tab.entries.length) | |
+ return; | |
+ // Until we store full or partial history, just grab the current entry. | |
+ // index is 1 based, so make sure we adjust. | |
+ let entry = tab.entries[tab.index - 1]; | |
+ | |
+ // Filter out some urls if necessary. SessionStore can return empty | |
+ // tabs in some cases - easiest thing is to just ignore them for now. | |
+ if (!entry.url || filter && filteredUrls.test(entry.url)) | |
+ return; | |
+ | |
+ // I think it's also possible that attributes[.image] might not be set | |
+ // so handle that as well. | |
+ allTabs.push({ | |
+ title: entry.title || "", | |
+ urlHistory: [entry.url], | |
+ icon: tab.attributes && tab.attributes.image || "", | |
+ lastUsed: Math.floor((tab.lastAccessed || 0) / 1000) | |
+ }); | |
+ }); | |
+ }); | |
+ | |
+ return allTabs; | |
+ }, | |
+ | |
+ isOpenLocally: function isLocal(url) { | |
+ return this.getLocalTabs().some(function(tab) { | |
+ return tab.urlHistory[0] == url; | |
+ }); | |
+ }, | |
+ | |
+ getAllRemotes: function getAllRemotes() { | |
+ return this.cachedRemotesInfo; | |
+ }, | |
+ | |
+ getRemoteById: function getRemoteById(id) { | |
+ return this.cachedRemotesInfo[id]; | |
+ }, | |
+ | |
+ clearAllRemotes: function clearAllRemotes() { | |
+ this.cachedRemotesInfo = {}; | |
+ }, | |
+ | |
+ clearRemoteById: function clearRemoteById(id) { | |
+ delete this.cachedRemotesInfo[id]; | |
+ }, | |
+ | |
+ resume: function resume() { | |
+ Svc.Obs.add("domwindowopened", this); | |
+ let wins = Services.wm.getEnumerator("navigator:browser"); | |
+ while (wins.hasMoreElements()) { | |
+ this._registerListenersForWindow(wins.getNext()); | |
+ } | |
+ }, | |
+ | |
+ suspend: function suspend() { | |
+ Svc.Obs.remove("domwindowopened", this); | |
+ let wins = Services.wm.getEnumerator("navigator:browser"); | |
+ while (wins.hasMoreElements()) { | |
+ this._unregisterListenersForWindow(wins.getNext()); | |
+ } | |
+ }, | |
+ | |
+ observe: function(subject, topic, data) { | |
+ switch (topic) { | |
+ case "domwindowopened": | |
+ let onLoad = () => { | |
+ subject.removeEventListener("load", onLoad, false); | |
+ // Only register after the window is done loading to avoid unloads. | |
+ this._registerListenersForWindow(subject); | |
+ }; | |
+ | |
+ // Add tab listeners now that a window has opened. | |
+ subject.addEventListener("load", onLoad, false); | |
+ break; | |
+ } | |
+ }, | |
+ | |
+ _topics: ["pageshow", "TabOpen", "TabClose", "TabSelect"], | |
+ _registerListenersForWindow: function registerListenersFW(window) { | |
+ // this._log.trace("Registering tab listeners in window"); | |
+ for each (let topic in this._topics) { | |
+ window.addEventListener(topic, this.onTab, false); | |
+ } | |
+ window.addEventListener("unload", this._unregisterListeners, false); | |
+ }, | |
+ | |
+ _unregisterListeners: function unregisterListeners(event) { | |
+ this._unregisterListenersForWindow(event.target); | |
+ }, | |
+ | |
+ _unregisterListenersForWindow: function unregisterListenersFW(window) { | |
+ // this._log.trace("Removing tab listeners in window"); | |
+ window.removeEventListener("unload", this._unregisterListeners, false); | |
+ for each (let topic in this._topics) { | |
+ window.removeEventListener(topic, this.onTab, false); | |
+ } | |
+ }, | |
+ | |
+ onTab: function onTab(event) { | |
+ if (event.originalTarget.linkedBrowser) { | |
+ let win = event.originalTarget.linkedBrowser.contentWindow; | |
+ if (PrivateBrowsingUtils.isWindowPrivate(win) && | |
+ !PrivateBrowsingUtils.permanentPrivateBrowsing) { | |
+ this._log.trace("Ignoring tab event from private browsing."); | |
+ return; | |
+ } | |
+ } | |
+ | |
+ this.listeners.forEach(function(listener) { | |
+ listener.call(undefined); | |
+ }); | |
+ } | |
+}; | |
+ | |
+this.Tabs = new _Tabs(); | |
\ No newline at end of file | |
diff --git a/services/cloudsync/moz.build b/services/cloudsync/moz.build | |
new file mode 100644 | |
index 0000000..a3a9ccf | |
--- /dev/null | |
+++ b/services/cloudsync/moz.build | |
@@ -0,0 +1,13 @@ | |
+# -*- Mode: python; c-basic-offset: 4; 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_COMPONENTS += [ | |
+ 'CloudSync.js', | |
+] | |
+ | |
+EXTRA_PP_COMPONENTS += [ | |
+ 'CloudSyncComponents.manifest', | |
+] | |
diff --git a/services/cloudsync/services-cloudsync.js b/services/cloudsync/services-cloudsync.js | |
new file mode 100644 | |
index 0000000..7a121aa | |
--- /dev/null | |
+++ b/services/cloudsync/services-cloudsync.js | |
@@ -0,0 +1,80 @@ | |
+/* This Source Code Form is subject to the terms of the Mozilla Public | |
+ * License, v. 2.0. If a copy of the MPL was not distributed with this | |
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
+ | |
+/* | |
+pref("services.sync.serverURL", "https://auth.services.mozilla.com/"); | |
+pref("services.sync.userURL", "user/"); | |
+pref("services.sync.miscURL", "misc/"); | |
+pref("services.sync.termsURL", "https://services.mozilla.com/tos/"); | |
+pref("services.sync.privacyURL", "https://services.mozilla.com/privacy-policy/"); | |
+pref("services.sync.statusURL", "https://services.mozilla.com/status/"); | |
+pref("services.sync.syncKeyHelpURL", "https://services.mozilla.com/help/synckey"); | |
+ | |
+pref("services.sync.lastversion", "firstrun"); | |
+pref("services.sync.sendVersionInfo", true); | |
+ | |
+pref("services.sync.scheduler.eolInterval", 604800); // 1 week | |
+pref("services.sync.scheduler.singleDeviceInterval", 86400); // 1 day | |
+pref("services.sync.scheduler.idleInterval", 3600); // 1 hour | |
+pref("services.sync.scheduler.activeInterval", 600); // 10 minutes | |
+pref("services.sync.scheduler.immediateInterval", 90); // 1.5 minutes | |
+pref("services.sync.scheduler.idleTime", 300); // 5 minutes | |
+ | |
+pref("services.sync.errorhandler.networkFailureReportTimeout", 1209600); // 2 weeks | |
+ | |
+pref("services.sync.engine.addons", true); | |
+pref("services.sync.engine.bookmarks", true); | |
+pref("services.sync.engine.history", true); | |
+pref("services.sync.engine.passwords", true); | |
+pref("services.sync.engine.prefs", true); | |
+pref("services.sync.engine.tabs", true); | |
+pref("services.sync.engine.tabs.filteredUrls", "^(about:.*|chrome://weave/.*|wyciwyg:.*|file:.*)$"); | |
+ | |
+pref("services.sync.jpake.serverURL", "https://setup.services.mozilla.com/"); | |
+pref("services.sync.jpake.pollInterval", 1000); | |
+pref("services.sync.jpake.firstMsgMaxTries", 300); // 5 minutes | |
+pref("services.sync.jpake.lastMsgMaxTries", 300); // 5 minutes | |
+pref("services.sync.jpake.maxTries", 10); | |
+ | |
+// Allow add-ons to be synced from non-trusted sources. | |
+pref("services.sync.addons.ignoreRepositoryChecking", false); | |
+ | |
+// If true, add-on sync ignores changes to the user-enabled flag. This | |
+// allows people to have the same set of add-ons installed across all | |
+// profiles while maintaining different enabled states. | |
+pref("services.sync.addons.ignoreUserEnabledChanges", false); | |
+ | |
+// Comma-delimited list of hostnames to trust for add-on install. | |
+pref("services.sync.addons.trustedSourceHostnames", "addons.mozilla.org"); | |
+ | |
+pref("services.sync.log.appender.console", "Warn"); | |
+pref("services.sync.log.appender.dump", "Error"); | |
+pref("services.sync.log.appender.file.level", "Trace"); | |
+pref("services.sync.log.appender.file.logOnError", true); | |
+pref("services.sync.log.appender.file.logOnSuccess", false); | |
+pref("services.sync.log.appender.file.maxErrorAge", 864000); // 10 days | |
+pref("services.sync.log.rootLogger", "Debug"); | |
+pref("services.sync.log.logger.addonutils", "Debug"); | |
+pref("services.sync.log.logger.service.main", "Debug"); | |
+pref("services.sync.log.logger.status", "Debug"); | |
+pref("services.sync.log.logger.authenticator", "Debug"); | |
+pref("services.sync.log.logger.network.resources", "Debug"); | |
+pref("services.sync.log.logger.service.jpakeclient", "Debug"); | |
+pref("services.sync.log.logger.engine.bookmarks", "Debug"); | |
+pref("services.sync.log.logger.engine.clients", "Debug"); | |
+pref("services.sync.log.logger.engine.forms", "Debug"); | |
+pref("services.sync.log.logger.engine.history", "Debug"); | |
+pref("services.sync.log.logger.engine.passwords", "Debug"); | |
+pref("services.sync.log.logger.engine.prefs", "Debug"); | |
+pref("services.sync.log.logger.engine.tabs", "Debug"); | |
+pref("services.sync.log.logger.engine.addons", "Debug"); | |
+pref("services.sync.log.logger.engine.apps", "Debug"); | |
+pref("services.sync.log.logger.userapi", "Debug"); | |
+pref("services.sync.log.cryptoDebug", false); | |
+ | |
+pref("services.sync.tokenServerURI", "https://token.services.mozilla.com/1.0/sync/1.5"); | |
+ | |
+pref("services.sync.fxa.termsURL", "https://accounts.firefox.com/legal/terms"); | |
+pref("services.sync.fxa.privacyURL", "https://accounts.firefox.com/legal/privacy"); | |
+*/ | |
\ No newline at end of file |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment