mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-28 19:38:13 +00:00
Merge m-c to inbound.
This commit is contained in:
commit
5813178548
@ -223,6 +223,7 @@ let AdbController = {
|
||||
lockEnabled: undefined,
|
||||
disableAdbTimer: null,
|
||||
disableAdbTimeoutHours: 12,
|
||||
umsActive: false,
|
||||
|
||||
debug: function(str) {
|
||||
dump("AdbController: " + str + "\n");
|
||||
@ -304,6 +305,51 @@ let AdbController = {
|
||||
},
|
||||
|
||||
updateState: function() {
|
||||
this.umsActive = false;
|
||||
this.storages = navigator.getDeviceStorages('sdcard');
|
||||
this.updateStorageState(0);
|
||||
},
|
||||
|
||||
updateStorageState: function(storageIndex) {
|
||||
if (storageIndex >= this.storages.length) {
|
||||
// We've iterated through all of the storage objects, now we can
|
||||
// really do updateStateInternal.
|
||||
this.updateStateInternal();
|
||||
return;
|
||||
}
|
||||
let storage = this.storages[storageIndex];
|
||||
if (this.DEBUG) {
|
||||
this.debug("Checking availability of storage: '" +
|
||||
storage.storageName);
|
||||
}
|
||||
|
||||
let req = storage.available();
|
||||
req.onsuccess = function(e) {
|
||||
if (this.DEBUG) {
|
||||
this.debug("Storage: '" + storage.storageName + "' is '" +
|
||||
e.target.result);
|
||||
}
|
||||
if (e.target.result == 'shared') {
|
||||
// We've found a storage area that's being shared with the PC.
|
||||
// We can stop looking now.
|
||||
this.umsActive = true;
|
||||
this.updateStateInternal();
|
||||
return;
|
||||
}
|
||||
this.updateStorageState(storageIndex + 1);
|
||||
}.bind(this);
|
||||
req.onerror = function(e) {
|
||||
dump("AdbController: error querying storage availability for '" +
|
||||
this.storages[storageIndex].storageName + "' (ignoring)\n");
|
||||
this.updateStorageState(storageIndex + 1);
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
updateStateInternal: function() {
|
||||
if (this.DEBUG) {
|
||||
this.debug("updateStateInternal: called");
|
||||
}
|
||||
|
||||
if (this.remoteDebuggerEnabled === undefined ||
|
||||
this.lockEnabled === undefined ||
|
||||
this.locked === undefined) {
|
||||
@ -338,8 +384,15 @@ let AdbController = {
|
||||
this.debug("isDebugging=" + isDebugging);
|
||||
}
|
||||
|
||||
// If USB Mass Storage, USB tethering, or a debug session is active,
|
||||
// then we don't want to disable adb in an automatic fashion (i.e.
|
||||
// when the screen locks or due to timeout).
|
||||
let sysUsbConfig = libcutils.property_get("sys.usb.config");
|
||||
let rndisActive = (sysUsbConfig.split(",").indexOf("rndis") >= 0);
|
||||
let usbFuncActive = rndisActive || this.umsActive || isDebugging;
|
||||
|
||||
let enableAdb = this.remoteDebuggerEnabled &&
|
||||
(!(this.lockEnabled && this.locked) || isDebugging);
|
||||
(!(this.lockEnabled && this.locked) || usbFuncActive);
|
||||
|
||||
let useDisableAdbTimer = true;
|
||||
try {
|
||||
@ -359,7 +412,8 @@ let AdbController = {
|
||||
this.debug("updateState: enableAdb = " + enableAdb +
|
||||
" remoteDebuggerEnabled = " + this.remoteDebuggerEnabled +
|
||||
" lockEnabled = " + this.lockEnabled +
|
||||
" locked = " + this.locked);
|
||||
" locked = " + this.locked +
|
||||
" usbFuncActive = " + usbFuncActive);
|
||||
}
|
||||
|
||||
// Configure adb.
|
||||
@ -391,7 +445,7 @@ let AdbController = {
|
||||
}
|
||||
}
|
||||
if (useDisableAdbTimer) {
|
||||
if (enableAdb && !isDebugging) {
|
||||
if (enableAdb && !usbFuncActive) {
|
||||
this.startDisableAdbTimer();
|
||||
} else {
|
||||
this.stopDisableAdbTimer();
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "62c0ad5b88f15d5da1cc2496b9534087dbc5d015",
|
||||
"revision": "f63fa4e31cea664886f43504529d96bc046506fc",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -2958,8 +2958,15 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL,
|
||||
|
||||
if (Components.isSuccessCode(result)) {
|
||||
isSigned = true;
|
||||
} else if (result == Cr.NS_ERROR_FILE_CORRUPTED) {
|
||||
} else if (result == Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY ||
|
||||
result == Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY ||
|
||||
result == Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING) {
|
||||
throw "APP_PACKAGE_CORRUPTED";
|
||||
} else if (result == Cr.NS_ERROR_FILE_CORRUPTED ||
|
||||
result == Cr.NS_ERROR_SIGNED_JAR_ENTRY_TOO_LARGE ||
|
||||
result == Cr.NS_ERROR_SIGNED_JAR_ENTRY_INVALID ||
|
||||
result == Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID) {
|
||||
throw "APP_PACKAGE_INVALID";
|
||||
} else if ((!aIsLocalFileInstall || isLaterThanBuildTime) &&
|
||||
(result != Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED)) {
|
||||
throw "INVALID_SIGNATURE";
|
||||
|
@ -206,9 +206,9 @@ function DOMDownloadImpl() {
|
||||
this.path = null;
|
||||
this.state = "stopped";
|
||||
this.contentType = null;
|
||||
this.error = null;
|
||||
|
||||
/* fields that require getters/setters */
|
||||
this._error = null;
|
||||
this._startTime = new Date();
|
||||
|
||||
/* private fields */
|
||||
@ -247,6 +247,14 @@ DOMDownloadImpl.prototype = {
|
||||
return this.__DOM_IMPL__.getEventHandler("onstatechange");
|
||||
},
|
||||
|
||||
get error() {
|
||||
return this._error;
|
||||
},
|
||||
|
||||
set error(aError) {
|
||||
this._error = aError;
|
||||
},
|
||||
|
||||
get startTime() {
|
||||
return this._startTime;
|
||||
},
|
||||
|
@ -51,14 +51,10 @@ function checkConsistentDownloadAttributes(download) {
|
||||
todo(download.path === expectedDownloadPath,
|
||||
"Download path = " + expectedDownloadPath);
|
||||
|
||||
// bug 948287: Accessing startTime attribute at download start fires
|
||||
// NS_ERROR_UNEXPECTED in emulator
|
||||
//ok(download.startTime >= todayDate,
|
||||
// "Download start time should be greater than or equal to today");
|
||||
ok(download.startTime >= todayDate,
|
||||
"Download start time should be greater than or equal to today");
|
||||
|
||||
// bug 945366: Accessing error attribute at download start fires
|
||||
// NS_ERROR_UNEXPECTED in emulator
|
||||
//is(download.error, null, "Download does not have an error");
|
||||
is(download.error, null, "Download does not have an error");
|
||||
|
||||
is(download.url, expectedServeURL,
|
||||
"Download URL = " + expectedServeURL);
|
||||
|
@ -415,7 +415,7 @@ let FormAssistant = {
|
||||
this.scrollIntoViewTimeout = content.setTimeout(function () {
|
||||
this.scrollIntoViewTimeout = null;
|
||||
if (this.focusedElement && !FormVisibility.isVisible(this.focusedElement)) {
|
||||
this.focusedElement.scrollIntoView(false);
|
||||
scrollSelectionOrElementIntoView(this.focusedElement);
|
||||
}
|
||||
}.bind(this), RESIZE_SCROLL_DELAY);
|
||||
}
|
||||
@ -1017,6 +1017,23 @@ function setSelectionRange(element, start, end) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll the given element into view.
|
||||
*
|
||||
* Calls scrollSelectionIntoView for contentEditable elements.
|
||||
*/
|
||||
function scrollSelectionOrElementIntoView(element) {
|
||||
let editor = getPlaintextEditor(element);
|
||||
if (editor) {
|
||||
editor.selectionController.scrollSelectionIntoView(
|
||||
Ci.nsISelectionController.SELECTION_NORMAL,
|
||||
Ci.nsISelectionController.SELECTION_FOCUS_REGION,
|
||||
Ci.nsISelectionController.SCROLL_SYNCHRONOUS);
|
||||
} else {
|
||||
element.scrollIntoView(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Get nsIPlaintextEditor object from an input field
|
||||
function getPlaintextEditor(element) {
|
||||
let editor = null;
|
||||
|
@ -138,8 +138,12 @@ MozNFCPeer.prototype = {
|
||||
*/
|
||||
function mozNfc() {
|
||||
debug("In mozNfc Constructor");
|
||||
this._nfcContentHelper = Cc["@mozilla.org/nfc/content-helper;1"]
|
||||
.getService(Ci.nsINfcContentHelper);
|
||||
try {
|
||||
this._nfcContentHelper = Cc["@mozilla.org/nfc/content-helper;1"]
|
||||
.getService(Ci.nsINfcContentHelper);
|
||||
} catch(e) {
|
||||
debug("No NFC support.")
|
||||
}
|
||||
}
|
||||
mozNfc.prototype = {
|
||||
_nfcContentHelper: null,
|
||||
|
@ -25,6 +25,9 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
let NFC = {};
|
||||
Cu.import("resource://gre/modules/nfc_consts.js", NFC);
|
||||
|
||||
Cu.import("resource://gre/modules/systemlibs.js");
|
||||
const NFC_ENABLED = libcutils.property_get("ro.moz.nfc.enabled", "false") === "true";
|
||||
|
||||
// set to true in nfc_consts.js to see debug messages
|
||||
let DEBUG = NFC.DEBUG_NFC;
|
||||
|
||||
@ -627,4 +630,6 @@ Nfc.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Nfc]);
|
||||
if (NFC_ENABLED) {
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Nfc]);
|
||||
}
|
||||
|
@ -26,6 +26,9 @@ Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||
let NFC = {};
|
||||
Cu.import("resource://gre/modules/nfc_consts.js", NFC);
|
||||
|
||||
Cu.import("resource://gre/modules/systemlibs.js");
|
||||
const NFC_ENABLED = libcutils.property_get("ro.moz.nfc.enabled", "false") === "true";
|
||||
|
||||
// set to true to in nfc_consts.js to see debug messages
|
||||
let DEBUG = NFC.DEBUG_CONTENT_HELPER;
|
||||
|
||||
@ -384,4 +387,6 @@ NfcContentHelper.prototype = {
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NfcContentHelper]);
|
||||
if (NFC_ENABLED) {
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NfcContentHelper]);
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ interface DOMDownload : EventTarget {
|
||||
|
||||
// A DOM error object, that will be not null when a download is stopped
|
||||
// because something failed.
|
||||
readonly attribute DOMError error;
|
||||
readonly attribute DOMError? error;
|
||||
|
||||
// Pauses the download.
|
||||
Promise pause();
|
||||
|
@ -539,12 +539,14 @@ sync_java_files = [
|
||||
'sync/crypto/PersistedCrypto5Keys.java',
|
||||
'sync/CryptoRecord.java',
|
||||
'sync/DelayedWorkTracker.java',
|
||||
'sync/delegates/BaseGlobalSessionCallback.java',
|
||||
'sync/delegates/ClientsDataDelegate.java',
|
||||
'sync/delegates/FreshStartDelegate.java',
|
||||
'sync/delegates/GlobalSessionCallback.java',
|
||||
'sync/delegates/JSONRecordFetchDelegate.java',
|
||||
'sync/delegates/KeyUploadDelegate.java',
|
||||
'sync/delegates/MetaGlobalDelegate.java',
|
||||
'sync/delegates/NodeAssignmentCallback.java',
|
||||
'sync/delegates/WipeServerDelegate.java',
|
||||
'sync/EngineSettings.java',
|
||||
'sync/ExtendedJSONObject.java',
|
||||
@ -662,9 +664,11 @@ sync_java_files = [
|
||||
'sync/repositories/domain/HistoryRecord.java',
|
||||
'sync/repositories/domain/HistoryRecordFactory.java',
|
||||
'sync/repositories/domain/PasswordRecord.java',
|
||||
'sync/repositories/domain/PasswordRecordFactory.java',
|
||||
'sync/repositories/domain/Record.java',
|
||||
'sync/repositories/domain/RecordParseException.java',
|
||||
'sync/repositories/domain/TabsRecord.java',
|
||||
'sync/repositories/domain/TabsRecordFactory.java',
|
||||
'sync/repositories/domain/VersionConstants.java',
|
||||
'sync/repositories/FetchFailedException.java',
|
||||
'sync/repositories/HashSetStoreTracker.java',
|
||||
@ -715,6 +719,8 @@ sync_java_files = [
|
||||
'sync/setup/InvalidSyncKeyException.java',
|
||||
'sync/setup/SyncAccounts.java',
|
||||
'sync/setup/SyncAuthenticatorService.java',
|
||||
'sync/SharedPreferencesClientsDataDelegate.java',
|
||||
'sync/SharedPreferencesNodeAssignmentCallback.java',
|
||||
'sync/stage/AbstractNonRepositorySyncStage.java',
|
||||
'sync/stage/AbstractSessionManagingSyncStage.java',
|
||||
'sync/stage/AndroidBrowserBookmarksServerSyncStage.java',
|
||||
|
@ -22,12 +22,13 @@ import org.json.simple.parser.ParseException;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.sync.crypto.CryptoException;
|
||||
import org.mozilla.gecko.sync.crypto.KeyBundle;
|
||||
import org.mozilla.gecko.sync.delegates.BaseGlobalSessionCallback;
|
||||
import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
|
||||
import org.mozilla.gecko.sync.delegates.FreshStartDelegate;
|
||||
import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
|
||||
import org.mozilla.gecko.sync.delegates.JSONRecordFetchDelegate;
|
||||
import org.mozilla.gecko.sync.delegates.KeyUploadDelegate;
|
||||
import org.mozilla.gecko.sync.delegates.MetaGlobalDelegate;
|
||||
import org.mozilla.gecko.sync.delegates.NodeAssignmentCallback;
|
||||
import org.mozilla.gecko.sync.delegates.WipeServerDelegate;
|
||||
import org.mozilla.gecko.sync.net.AuthHeaderProvider;
|
||||
import org.mozilla.gecko.sync.net.BaseResource;
|
||||
@ -70,9 +71,10 @@ public class GlobalSession implements PrefsSource, HttpResponseObserver {
|
||||
protected Map<Stage, GlobalSyncStage> stages;
|
||||
public Stage currentState = Stage.idle;
|
||||
|
||||
public final GlobalSessionCallback callback;
|
||||
private Context context;
|
||||
private ClientsDataDelegate clientsDelegate;
|
||||
public final BaseGlobalSessionCallback callback;
|
||||
protected final Context context;
|
||||
protected final ClientsDataDelegate clientsDelegate;
|
||||
protected final NodeAssignmentCallback nodeAssignmentCallback;
|
||||
|
||||
/**
|
||||
* Map from engine name to new settings for an updated meta/global record.
|
||||
@ -98,15 +100,15 @@ public class GlobalSession implements PrefsSource, HttpResponseObserver {
|
||||
return config.wboURI(collection, id);
|
||||
}
|
||||
|
||||
public GlobalSession(String serverURL,
|
||||
String username,
|
||||
public GlobalSession(String username,
|
||||
AuthHeaderProvider authHeaderProvider,
|
||||
String prefsPath,
|
||||
KeyBundle syncKeyBundle,
|
||||
GlobalSessionCallback callback,
|
||||
BaseGlobalSessionCallback callback,
|
||||
Context context,
|
||||
Bundle extras,
|
||||
ClientsDataDelegate clientsDelegate)
|
||||
ClientsDataDelegate clientsDelegate,
|
||||
NodeAssignmentCallback nodeAssignmentCallback)
|
||||
throws SyncConfigurationException, IllegalArgumentException, IOException, ParseException, NonObjectJSONException {
|
||||
if (username == null) {
|
||||
throw new IllegalArgumentException("username must not be null.");
|
||||
@ -116,12 +118,6 @@ public class GlobalSession implements PrefsSource, HttpResponseObserver {
|
||||
}
|
||||
|
||||
Logger.debug(LOG_TAG, "GlobalSession initialized with bundle " + extras);
|
||||
URI serverURI;
|
||||
try {
|
||||
serverURI = (serverURL == null) ? null : new URI(serverURL);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new SyncConfigurationException();
|
||||
}
|
||||
|
||||
if (syncKeyBundle == null ||
|
||||
syncKeyBundle.getEncryptionKey() == null ||
|
||||
@ -132,10 +128,9 @@ public class GlobalSession implements PrefsSource, HttpResponseObserver {
|
||||
this.callback = callback;
|
||||
this.context = context;
|
||||
this.clientsDelegate = clientsDelegate;
|
||||
this.nodeAssignmentCallback = nodeAssignmentCallback;
|
||||
|
||||
config = new SyncConfiguration(username, authHeaderProvider, prefsPath, this);
|
||||
|
||||
config.serverURL = serverURI;
|
||||
config.syncKeyBundle = syncKeyBundle;
|
||||
|
||||
registerCommands();
|
||||
@ -199,7 +194,7 @@ public class GlobalSession implements PrefsSource, HttpResponseObserver {
|
||||
HashMap<Stage, GlobalSyncStage> stages = new HashMap<Stage, GlobalSyncStage>();
|
||||
|
||||
stages.put(Stage.checkPreconditions, new CheckPreconditionsStage());
|
||||
stages.put(Stage.ensureClusterURL, new EnsureClusterURLStage());
|
||||
stages.put(Stage.ensureClusterURL, new EnsureClusterURLStage(nodeAssignmentCallback));
|
||||
stages.put(Stage.fetchInfoCollections, new FetchInfoCollectionsStage());
|
||||
stages.put(Stage.fetchMetaGlobal, new FetchMetaGlobalStage());
|
||||
stages.put(Stage.ensureKeysStage, new EnsureCrypto5KeysStage());
|
||||
|
@ -27,6 +27,9 @@ public class JSONRecordFetcher {
|
||||
protected JSONRecordFetchDelegate delegate;
|
||||
|
||||
public JSONRecordFetcher(final String uri, final AuthHeaderProvider authHeaderProvider) {
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("uri must not be null");
|
||||
}
|
||||
this.uri = uri;
|
||||
this.authHeaderProvider = authHeaderProvider;
|
||||
}
|
||||
|
@ -0,0 +1,57 @@
|
||||
/* 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/. */
|
||||
|
||||
package org.mozilla.gecko.sync;
|
||||
|
||||
import org.mozilla.gecko.background.common.GlobalConstants;
|
||||
import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
/**
|
||||
* A <code>ClientsDataDelegate</code> implementation that persists to a
|
||||
* <code>SharedPreferences</code> instance.
|
||||
*/
|
||||
public class SharedPreferencesClientsDataDelegate implements ClientsDataDelegate {
|
||||
protected final SharedPreferences sharedPreferences;
|
||||
|
||||
public SharedPreferencesClientsDataDelegate(SharedPreferences sharedPreferences) {
|
||||
this.sharedPreferences = sharedPreferences;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getAccountGUID() {
|
||||
String accountGUID = sharedPreferences.getString(SyncConfiguration.PREF_ACCOUNT_GUID, null);
|
||||
if (accountGUID == null) {
|
||||
accountGUID = Utils.generateGuid();
|
||||
sharedPreferences.edit().putString(SyncConfiguration.PREF_ACCOUNT_GUID, accountGUID).commit();
|
||||
}
|
||||
return accountGUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getClientName() {
|
||||
String clientName = sharedPreferences.getString(SyncConfiguration.PREF_CLIENT_NAME, null);
|
||||
if (clientName == null) {
|
||||
clientName = GlobalConstants.MOZ_APP_DISPLAYNAME + " on " + android.os.Build.MODEL;
|
||||
sharedPreferences.edit().putString(SyncConfiguration.PREF_CLIENT_NAME, clientName).commit();
|
||||
}
|
||||
return clientName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setClientsCount(int clientsCount) {
|
||||
sharedPreferences.edit().putLong(SyncConfiguration.PREF_NUM_CLIENTS, (long) clientsCount).commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocalGUID(String guid) {
|
||||
return getAccountGUID().equals(guid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getClientsCount() {
|
||||
return (int) sharedPreferences.getLong(SyncConfiguration.PREF_NUM_CLIENTS, 0);
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/* 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/. */
|
||||
|
||||
package org.mozilla.gecko.sync;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.mozilla.gecko.sync.delegates.NodeAssignmentCallback;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
|
||||
public class SharedPreferencesNodeAssignmentCallback implements NodeAssignmentCallback {
|
||||
protected final SharedPreferences sharedPreferences;
|
||||
protected final String nodeWeaveURL;
|
||||
|
||||
public SharedPreferencesNodeAssignmentCallback(SharedPreferences sharedPreferences, String nodeWeaveURL)
|
||||
throws SyncConfigurationException {
|
||||
this.sharedPreferences = sharedPreferences;
|
||||
if (nodeWeaveURL == null) {
|
||||
throw new IllegalArgumentException("nodeWeaveURL must not be null");
|
||||
}
|
||||
try {
|
||||
new URI(nodeWeaveURL);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new SyncConfigurationException();
|
||||
}
|
||||
this.nodeWeaveURL = nodeWeaveURL;
|
||||
}
|
||||
|
||||
public synchronized boolean getClusterURLIsStale() {
|
||||
return sharedPreferences.getBoolean(SyncConfiguration.PREF_CLUSTER_URL_IS_STALE, false);
|
||||
}
|
||||
|
||||
public synchronized void setClusterURLIsStale(boolean clusterURLIsStale) {
|
||||
Editor edit = sharedPreferences.edit();
|
||||
edit.putBoolean(SyncConfiguration.PREF_CLUSTER_URL_IS_STALE, clusterURLIsStale);
|
||||
edit.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantNodeAssignment() {
|
||||
return getClusterURLIsStale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informNodeAuthenticationFailed(GlobalSession session, URI failedClusterURL) {
|
||||
// TODO: communicate to the user interface that we need a new user password!
|
||||
// TODO: only freshen the cluster URL (better yet, forget the cluster URL) after the user has provided new credentials.
|
||||
setClusterURLIsStale(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informNodeAssigned(GlobalSession session, URI oldClusterURL, URI newClusterURL) {
|
||||
setClusterURLIsStale(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nodeWeaveURL() {
|
||||
return this.nodeWeaveURL;
|
||||
}
|
||||
}
|
@ -175,12 +175,9 @@ public class SyncConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
public static final String DEFAULT_USER_API = "https://auth.services.mozilla.com/user/1.0/";
|
||||
|
||||
private static final String LOG_TAG = "SyncConfiguration";
|
||||
|
||||
// These must be set in GlobalSession's constructor.
|
||||
public URI serverURL;
|
||||
public URI clusterURL;
|
||||
public KeyBundle syncKeyBundle;
|
||||
|
||||
@ -453,21 +450,6 @@ public class SyncConfiguration {
|
||||
collectionKeys = k;
|
||||
}
|
||||
|
||||
public String nodeWeaveURL() {
|
||||
return this.nodeWeaveURL((this.serverURL == null) ? null : this.serverURL.toASCIIString());
|
||||
}
|
||||
|
||||
public String nodeWeaveURL(String serverURL) {
|
||||
String userPart = username + "/node/weave";
|
||||
if (serverURL == null) {
|
||||
return DEFAULT_USER_API + userPart;
|
||||
}
|
||||
if (!serverURL.endsWith("/")) {
|
||||
serverURL = serverURL + "/";
|
||||
}
|
||||
return serverURL + "user/1.0/" + userPart;
|
||||
}
|
||||
|
||||
protected String infoBaseURL() {
|
||||
return clusterURL + GlobalSession.API_VERSION + "/" + username + "/info/";
|
||||
}
|
||||
|
@ -53,4 +53,6 @@ public class SyncConstants {
|
||||
* Account type.
|
||||
*/
|
||||
public static final String PER_ACCOUNT_TYPE_PERMISSION = "@MOZ_ANDROID_SHARED_ACCOUNT_TYPE@.permission.PER_ACCOUNT_TYPE";
|
||||
|
||||
public static final String DEFAULT_AUTH_SERVER = "https://auth.services.mozilla.com/";
|
||||
}
|
||||
|
@ -530,4 +530,15 @@ public class Utils {
|
||||
public static String obfuscateEmail(final String in) {
|
||||
return in.replaceAll("[^@\\.]", "X");
|
||||
}
|
||||
|
||||
public static String nodeWeaveURL(String serverURL, String username) {
|
||||
String userPart = username + "/node/weave";
|
||||
if (serverURL == null) {
|
||||
return SyncConstants.DEFAULT_AUTH_SERVER + "user/1.0/" + userPart;
|
||||
}
|
||||
if (!serverURL.endsWith("/")) {
|
||||
serverURL = serverURL + "/";
|
||||
}
|
||||
return serverURL + "user/1.0/" + userPart;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
/* 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/. */
|
||||
|
||||
package org.mozilla.gecko.sync.delegates;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
|
||||
|
||||
public interface BaseGlobalSessionCallback {
|
||||
/**
|
||||
* Request that no further syncs occur within the next `backoff` milliseconds.
|
||||
* @param backoff a duration in milliseconds.
|
||||
*/
|
||||
void requestBackoff(long backoff);
|
||||
|
||||
/**
|
||||
* Called on a 401 HTTP response.
|
||||
*/
|
||||
void informUnauthorizedResponse(GlobalSession globalSession, URI oldClusterURL);
|
||||
|
||||
|
||||
/**
|
||||
* Called when an HTTP failure indicates that a software upgrade is required.
|
||||
*/
|
||||
void informUpgradeRequiredResponse(GlobalSession session);
|
||||
|
||||
void handleAborted(GlobalSession globalSession, String reason);
|
||||
void handleError(GlobalSession globalSession, Exception ex);
|
||||
void handleSuccess(GlobalSession globalSession);
|
||||
void handleStageCompleted(Stage currentState, GlobalSession globalSession);
|
||||
|
||||
boolean shouldBackOff();
|
||||
}
|
@ -4,63 +4,5 @@
|
||||
|
||||
package org.mozilla.gecko.sync.delegates;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
|
||||
|
||||
public interface GlobalSessionCallback {
|
||||
|
||||
/**
|
||||
* Request that no further syncs occur within the next `backoff` milliseconds.
|
||||
* @param backoff a duration in milliseconds.
|
||||
*/
|
||||
void requestBackoff(long backoff);
|
||||
|
||||
/**
|
||||
* If true, request node assignment from the server, i.e., fetch node/weave cluster URL.
|
||||
*/
|
||||
boolean wantNodeAssignment();
|
||||
|
||||
/**
|
||||
* Called on a 401 HTTP response.
|
||||
*/
|
||||
void informUnauthorizedResponse(GlobalSession globalSession, URI oldClusterURL);
|
||||
|
||||
/**
|
||||
* Called when a new node is assigned. If there already was an old node, the
|
||||
* new node is different from the old node assignment, indicating node
|
||||
* reassignment. If there wasn't an old node, we've been freshly assigned.
|
||||
*
|
||||
* @param globalSession
|
||||
* @param oldClusterURL
|
||||
* The old node/weave cluster URL (possibly null).
|
||||
* @param newClusterURL
|
||||
* The new node/weave cluster URL (not null).
|
||||
*/
|
||||
void informNodeAssigned(GlobalSession globalSession, URI oldClusterURL, URI newClusterURL);
|
||||
|
||||
/**
|
||||
* Called when wantNodeAssignment() is true, and the new node assignment is
|
||||
* the same as the old node assignment, indicating a user authentication
|
||||
* error.
|
||||
*
|
||||
* @param globalSession
|
||||
* @param failedClusterURL
|
||||
* The new node/weave cluster URL.
|
||||
*/
|
||||
void informNodeAuthenticationFailed(GlobalSession globalSession, URI failedClusterURL);
|
||||
|
||||
/**
|
||||
* Called when an HTTP failure indicates that a software upgrade is required.
|
||||
*/
|
||||
void informUpgradeRequiredResponse(GlobalSession session);
|
||||
|
||||
void handleAborted(GlobalSession globalSession, String reason);
|
||||
void handleError(GlobalSession globalSession, Exception ex);
|
||||
void handleSuccess(GlobalSession globalSession);
|
||||
void handleStageCompleted(Stage currentState, GlobalSession globalSession);
|
||||
|
||||
boolean shouldBackOff();
|
||||
|
||||
public interface GlobalSessionCallback extends BaseGlobalSessionCallback, NodeAssignmentCallback {
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
/* 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/. */
|
||||
|
||||
package org.mozilla.gecko.sync.delegates;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
|
||||
public interface NodeAssignmentCallback {
|
||||
/**
|
||||
* If true, request node assignment from the server, i.e., fetch node/weave cluster URL.
|
||||
*/
|
||||
public boolean wantNodeAssignment();
|
||||
|
||||
/**
|
||||
* Called when a new node is assigned. If there already was an old node, the
|
||||
* new node is different from the old node assignment, indicating node
|
||||
* reassignment. If there wasn't an old node, we've been freshly assigned.
|
||||
*
|
||||
* @param globalSession
|
||||
* @param oldClusterURL
|
||||
* The old node/weave cluster URL (possibly null).
|
||||
* @param newClusterURL
|
||||
* The new node/weave cluster URL (not null).
|
||||
*/
|
||||
public void informNodeAssigned(GlobalSession globalSession, URI oldClusterURL, URI newClusterURL);
|
||||
|
||||
/**
|
||||
* Called when wantNodeAssignment() is true, and the new node assignment is
|
||||
* the same as the old node assignment, indicating a user authentication
|
||||
* error.
|
||||
*
|
||||
* @param globalSession
|
||||
* @param failedClusterURL
|
||||
* The new node/weave cluster URL.
|
||||
*/
|
||||
public void informNodeAuthenticationFailed(GlobalSession globalSession, URI failedClusterURL);
|
||||
|
||||
public String nodeWeaveURL();
|
||||
}
|
@ -91,7 +91,10 @@ public class BaseResource implements Resource {
|
||||
}
|
||||
|
||||
public BaseResource(URI uri, boolean rewrite) {
|
||||
if (rewrite && uri.getHost().equals("localhost")) {
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("uri must not be null");
|
||||
}
|
||||
if (rewrite && "localhost".equals(uri.getHost())) {
|
||||
// Rewrite localhost URIs to refer to the special Android emulator loopback passthrough interface.
|
||||
Logger.debug(LOG_TAG, "Rewriting " + uri + " to point to " + ANDROID_LOOPBACK_IP + ".");
|
||||
try {
|
||||
|
@ -0,0 +1,19 @@
|
||||
/* 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/. */
|
||||
|
||||
package org.mozilla.gecko.sync.repositories.domain;
|
||||
|
||||
import org.mozilla.gecko.sync.CryptoRecord;
|
||||
import org.mozilla.gecko.sync.repositories.RecordFactory;
|
||||
import org.mozilla.gecko.sync.repositories.domain.PasswordRecord;
|
||||
import org.mozilla.gecko.sync.repositories.domain.Record;
|
||||
|
||||
public class PasswordRecordFactory extends RecordFactory {
|
||||
@Override
|
||||
public Record createRecord(Record record) {
|
||||
PasswordRecord r = new PasswordRecord();
|
||||
r.initFromEnvelope((CryptoRecord) record);
|
||||
return r;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
/* 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/. */
|
||||
|
||||
package org.mozilla.gecko.sync.repositories.domain;
|
||||
|
||||
import org.mozilla.gecko.sync.CryptoRecord;
|
||||
import org.mozilla.gecko.sync.repositories.RecordFactory;
|
||||
|
||||
public class TabsRecordFactory extends RecordFactory {
|
||||
@Override
|
||||
public Record createRecord(Record record) {
|
||||
TabsRecord r = new TabsRecord();
|
||||
r.initFromEnvelope((CryptoRecord) record);
|
||||
return r;
|
||||
}
|
||||
}
|
@ -51,10 +51,6 @@ public class Constants {
|
||||
Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
||||
|
||||
// Constants for Account Authentication.
|
||||
public static final String AUTH_NODE_DEFAULT = "https://auth.services.mozilla.com/";
|
||||
public static final String AUTH_NODE_PATHNAME = "user/";
|
||||
public static final String AUTH_NODE_VERSION = "1.0/";
|
||||
public static final String AUTH_NODE_SUFFIX = "node/weave";
|
||||
public static final String AUTH_SERVER_VERSION = "1.1/";
|
||||
public static final String AUTH_SERVER_SUFFIX = "info/collections/";
|
||||
|
||||
|
@ -44,8 +44,6 @@ public class SyncAccounts {
|
||||
private static final String MOTO_BLUR_SETTINGS_ACTIVITY = "com.motorola.blur.settings.AccountsAndServicesPreferenceActivity";
|
||||
private static final String MOTO_BLUR_PACKAGE = "com.motorola.blur.setup";
|
||||
|
||||
public final static String DEFAULT_SERVER = "https://auth.services.mozilla.com/";
|
||||
|
||||
/**
|
||||
* Return Sync accounts.
|
||||
*
|
||||
@ -249,10 +247,10 @@ public class SyncAccounts {
|
||||
final String syncKey = syncAccount.syncKey;
|
||||
final String password = syncAccount.password;
|
||||
final String serverURL = (syncAccount.serverURL == null) ?
|
||||
DEFAULT_SERVER : syncAccount.serverURL;
|
||||
SyncConstants.DEFAULT_AUTH_SERVER : syncAccount.serverURL;
|
||||
|
||||
Logger.debug(LOG_TAG, "Using account manager " + accountManager);
|
||||
if (!RepoUtils.stringsEqual(syncAccount.serverURL, DEFAULT_SERVER)) {
|
||||
if (!RepoUtils.stringsEqual(syncAccount.serverURL, SyncConstants.DEFAULT_AUTH_SERVER)) {
|
||||
Logger.info(LOG_TAG, "Setting explicit server URL: " + serverURL);
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ public class AccountActivity extends AccountAuthenticatorActivity {
|
||||
private String username;
|
||||
private String password;
|
||||
private String key;
|
||||
private String server = Constants.AUTH_NODE_DEFAULT;
|
||||
private String server = SyncConstants.DEFAULT_AUTH_SERVER;
|
||||
|
||||
// UI elements.
|
||||
private EditText serverInput;
|
||||
@ -162,7 +162,7 @@ public class AccountActivity extends AccountAuthenticatorActivity {
|
||||
username = usernameInput.getText().toString().toLowerCase(Locale.US);
|
||||
password = passwordInput.getText().toString();
|
||||
key = synckeyInput.getText().toString();
|
||||
server = Constants.AUTH_NODE_DEFAULT;
|
||||
server = SyncConstants.DEFAULT_AUTH_SERVER;
|
||||
|
||||
if (serverCheckbox.isChecked()) {
|
||||
String userServer = serverInput.getText().toString();
|
||||
|
@ -15,7 +15,6 @@ import java.security.GeneralSecurityException;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.sync.net.BaseResource;
|
||||
import org.mozilla.gecko.sync.net.BaseResourceDelegate;
|
||||
import org.mozilla.gecko.sync.setup.Constants;
|
||||
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
|
||||
@ -53,7 +52,8 @@ public class EnsureUserExistenceStage implements AuthenticatorStage {
|
||||
|
||||
};
|
||||
|
||||
String userRequestUrl = aa.nodeServer + Constants.AUTH_NODE_PATHNAME + Constants.AUTH_NODE_VERSION + aa.username;
|
||||
// This is not the same as Utils.nodeWeaveURL: it's missing the trailing node/weave.
|
||||
String userRequestUrl = aa.nodeServer + "user/1.0/" + aa.username;
|
||||
final BaseResource httpResource = new BaseResource(userRequestUrl);
|
||||
httpResource.delegate = new BaseResourceDelegate(httpResource) {
|
||||
|
||||
|
@ -12,9 +12,9 @@ import java.net.URISyntaxException;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.sync.Utils;
|
||||
import org.mozilla.gecko.sync.net.BaseResource;
|
||||
import org.mozilla.gecko.sync.net.BaseResourceDelegate;
|
||||
import org.mozilla.gecko.sync.setup.Constants;
|
||||
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
|
||||
@ -61,7 +61,7 @@ public class FetchUserNodeStage implements AuthenticatorStage {
|
||||
aa.abort(AuthenticationResult.FAILURE_OTHER, e);
|
||||
}
|
||||
};
|
||||
String nodeRequestUrl = aa.nodeServer + Constants.AUTH_NODE_PATHNAME + Constants.AUTH_NODE_VERSION + aa.username + "/" + Constants.AUTH_NODE_SUFFIX;
|
||||
String nodeRequestUrl = Utils.nodeWeaveURL(aa.nodeServer, aa.username);
|
||||
// Might contain a plaintext username in the case of old Sync accounts.
|
||||
Logger.pii(LOG_TAG, "NodeUrl: " + nodeRequestUrl);
|
||||
final BaseResource httpResource = makeFetchNodeRequest(callbackDelegate, nodeRequestUrl);
|
||||
|
@ -16,6 +16,7 @@ import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.sync.NodeAuthenticationException;
|
||||
import org.mozilla.gecko.sync.NullClusterURLException;
|
||||
import org.mozilla.gecko.sync.ThreadPool;
|
||||
import org.mozilla.gecko.sync.delegates.NodeAssignmentCallback;
|
||||
import org.mozilla.gecko.sync.net.BaseResource;
|
||||
import org.mozilla.gecko.sync.net.BaseResourceDelegate;
|
||||
|
||||
@ -24,6 +25,8 @@ import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
|
||||
|
||||
public class EnsureClusterURLStage extends AbstractNonRepositorySyncStage {
|
||||
private static final String LOG_TAG = EnsureClusterURLStage.class.getSimpleName();
|
||||
|
||||
public interface ClusterURLFetchDelegate {
|
||||
/**
|
||||
* 200 - Success.
|
||||
@ -50,7 +53,12 @@ public class EnsureClusterURLStage extends AbstractNonRepositorySyncStage {
|
||||
public void handleError(Exception e);
|
||||
}
|
||||
|
||||
protected static final String LOG_TAG = "EnsureClusterURLStage";
|
||||
protected final NodeAssignmentCallback callback;
|
||||
|
||||
public EnsureClusterURLStage(NodeAssignmentCallback callback) {
|
||||
super();
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
// TODO: if cluster URL has changed since last time, we need to ensure that we do
|
||||
// a fresh start. This takes place at the GlobalSession level. Verify!
|
||||
@ -173,7 +181,7 @@ public class EnsureClusterURLStage extends AbstractNonRepositorySyncStage {
|
||||
|
||||
public void execute() throws NoSuchStageException {
|
||||
final URI oldClusterURL = session.config.getClusterURL();
|
||||
final boolean wantNodeAssignment = session.callback.wantNodeAssignment();
|
||||
final boolean wantNodeAssignment = callback.wantNodeAssignment();
|
||||
|
||||
if (!wantNodeAssignment && oldClusterURL != null) {
|
||||
Logger.info(LOG_TAG, "Cluster URL is already set and not stale. Continuing with sync.");
|
||||
@ -190,12 +198,12 @@ public class EnsureClusterURLStage extends AbstractNonRepositorySyncStage {
|
||||
|
||||
if (oldClusterURL != null && oldClusterURL.equals(url)) {
|
||||
// Our cluster URL is marked as stale and the fresh cluster URL is the same -- this is the user's problem.
|
||||
session.callback.informNodeAuthenticationFailed(session, url);
|
||||
callback.informNodeAuthenticationFailed(session, url);
|
||||
session.abort(new NodeAuthenticationException(), "User password has changed.");
|
||||
return;
|
||||
}
|
||||
|
||||
session.callback.informNodeAssigned(session, oldClusterURL, url); // No matter what, we're getting a new node/weave clusterURL.
|
||||
callback.informNodeAssigned(session, oldClusterURL, url); // No matter what, we're getting a new node/weave clusterURL.
|
||||
session.config.setClusterURL(url);
|
||||
|
||||
ThreadPool.run(new Runnable() {
|
||||
@ -216,7 +224,12 @@ public class EnsureClusterURLStage extends AbstractNonRepositorySyncStage {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
Logger.warn(LOG_TAG, "Got HTTP failure fetching node assignment: " + statusCode);
|
||||
if (statusCode == 404) {
|
||||
URI serverURL = session.config.serverURL;
|
||||
URI serverURL = null;
|
||||
try {
|
||||
serverURL = new URI(callback.nodeWeaveURL());
|
||||
} catch (URISyntaxException e) {
|
||||
// Fall through to abort.
|
||||
}
|
||||
if (serverURL != null) {
|
||||
Logger.info(LOG_TAG, "Using serverURL <" + serverURL.toASCIIString() + "> as clusterURL.");
|
||||
session.config.setClusterURL(serverURL);
|
||||
@ -224,7 +237,7 @@ public class EnsureClusterURLStage extends AbstractNonRepositorySyncStage {
|
||||
return;
|
||||
}
|
||||
Logger.warn(LOG_TAG, "No serverURL set to use as fallback cluster URL. Aborting sync.");
|
||||
// Fallthrough to abort.
|
||||
// Fall through to abort.
|
||||
} else {
|
||||
session.interpretHTTPFailure(response);
|
||||
}
|
||||
@ -241,7 +254,7 @@ public class EnsureClusterURLStage extends AbstractNonRepositorySyncStage {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
fetchClusterURL(session.config.nodeWeaveURL(), delegate);
|
||||
fetchClusterURL(callback.nodeWeaveURL(), delegate);
|
||||
} catch (URISyntaxException e) {
|
||||
session.abort(e, "Invalid URL for node/weave.");
|
||||
}
|
||||
|
@ -4,27 +4,16 @@
|
||||
|
||||
package org.mozilla.gecko.sync.stage;
|
||||
|
||||
import org.mozilla.gecko.sync.CryptoRecord;
|
||||
import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
|
||||
import org.mozilla.gecko.sync.repositories.RecordFactory;
|
||||
import org.mozilla.gecko.sync.repositories.Repository;
|
||||
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
|
||||
import org.mozilla.gecko.sync.repositories.domain.Record;
|
||||
import org.mozilla.gecko.sync.repositories.domain.TabsRecord;
|
||||
import org.mozilla.gecko.sync.repositories.domain.TabsRecordFactory;
|
||||
import org.mozilla.gecko.sync.repositories.domain.VersionConstants;
|
||||
|
||||
public class FennecTabsServerSyncStage extends ServerSyncStage {
|
||||
private static final String COLLECTION = "tabs";
|
||||
|
||||
public class FennecTabsRecordFactory extends RecordFactory {
|
||||
@Override
|
||||
public Record createRecord(Record record) {
|
||||
TabsRecord r = new TabsRecord();
|
||||
r.initFromEnvelope((CryptoRecord) record);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCollection() {
|
||||
return COLLECTION;
|
||||
@ -48,6 +37,6 @@ public class FennecTabsServerSyncStage extends ServerSyncStage {
|
||||
|
||||
@Override
|
||||
protected RecordFactory getRecordFactory() {
|
||||
return new FennecTabsRecordFactory();
|
||||
return new TabsRecordFactory();
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,10 @@
|
||||
|
||||
package org.mozilla.gecko.sync.stage;
|
||||
|
||||
import org.mozilla.gecko.sync.CryptoRecord;
|
||||
import org.mozilla.gecko.sync.repositories.RecordFactory;
|
||||
import org.mozilla.gecko.sync.repositories.Repository;
|
||||
import org.mozilla.gecko.sync.repositories.android.PasswordsRepositorySession;
|
||||
import org.mozilla.gecko.sync.repositories.domain.PasswordRecord;
|
||||
import org.mozilla.gecko.sync.repositories.domain.Record;
|
||||
import org.mozilla.gecko.sync.repositories.domain.PasswordRecordFactory;
|
||||
import org.mozilla.gecko.sync.repositories.domain.VersionConstants;
|
||||
|
||||
public class PasswordsServerSyncStage extends ServerSyncStage {
|
||||
@ -37,14 +35,4 @@ public class PasswordsServerSyncStage extends ServerSyncStage {
|
||||
protected RecordFactory getRecordFactory() {
|
||||
return new PasswordRecordFactory();
|
||||
}
|
||||
|
||||
public class PasswordRecordFactory extends RecordFactory {
|
||||
|
||||
@Override
|
||||
public Record createRecord(Record record) {
|
||||
PasswordRecord r = new PasswordRecord();
|
||||
r.initFromEnvelope((CryptoRecord) record);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ import org.mozilla.gecko.sync.AlreadySyncingException;
|
||||
import org.mozilla.gecko.sync.CredentialException;
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
import org.mozilla.gecko.sync.NonObjectJSONException;
|
||||
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
|
||||
import org.mozilla.gecko.sync.SharedPreferencesNodeAssignmentCallback;
|
||||
import org.mozilla.gecko.sync.SyncConfiguration;
|
||||
import org.mozilla.gecko.sync.SyncConfigurationException;
|
||||
import org.mozilla.gecko.sync.SyncConstants;
|
||||
@ -26,8 +28,8 @@ import org.mozilla.gecko.sync.Utils;
|
||||
import org.mozilla.gecko.sync.config.AccountPickler;
|
||||
import org.mozilla.gecko.sync.crypto.CryptoException;
|
||||
import org.mozilla.gecko.sync.crypto.KeyBundle;
|
||||
import org.mozilla.gecko.sync.delegates.BaseGlobalSessionCallback;
|
||||
import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
|
||||
import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
|
||||
import org.mozilla.gecko.sync.net.AuthHeaderProvider;
|
||||
import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
|
||||
import org.mozilla.gecko.sync.net.ConnectionMonitorThread;
|
||||
@ -51,7 +53,7 @@ import android.database.sqlite.SQLiteConstraintException;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSessionCallback, ClientsDataDelegate {
|
||||
public class SyncAdapter extends AbstractThreadedSyncAdapter implements BaseGlobalSessionCallback {
|
||||
private static final String LOG_TAG = "SyncAdapter";
|
||||
|
||||
private static final int BACKOFF_PAD_SECONDS = 5;
|
||||
@ -195,6 +197,8 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe
|
||||
protected Account localAccount;
|
||||
protected boolean thisSyncIsForced = false;
|
||||
protected SharedPreferences accountSharedPreferences;
|
||||
protected SharedPreferencesClientsDataDelegate clientsDataDelegate;
|
||||
protected SharedPreferencesNodeAssignmentCallback nodeAssignmentDelegate;
|
||||
|
||||
/**
|
||||
* Return the number of milliseconds until we're allowed to sync again,
|
||||
@ -220,7 +224,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wantNodeAssignment()) {
|
||||
if (nodeAssignmentDelegate.wantNodeAssignment()) {
|
||||
/*
|
||||
* We recently had a 401 and we aborted the last sync. We should kick off
|
||||
* another sync to fetch a new node/weave cluster URL, since ours is
|
||||
@ -359,11 +363,14 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe
|
||||
final String profile = Constants.DEFAULT_PROFILE;
|
||||
final long version = SyncConfiguration.CURRENT_PREFS_VERSION;
|
||||
self.accountSharedPreferences = Utils.getSharedPreferences(mContext, product, username, serverURL, profile, version);
|
||||
self.clientsDataDelegate = new SharedPreferencesClientsDataDelegate(accountSharedPreferences);
|
||||
final String nodeWeaveURL = Utils.nodeWeaveURL(serverURL, username);
|
||||
self.nodeAssignmentDelegate = new SharedPreferencesNodeAssignmentCallback(accountSharedPreferences, nodeWeaveURL);
|
||||
|
||||
Logger.info(LOG_TAG,
|
||||
"Client is named '" + getClientName() + "'" +
|
||||
", has client guid " + getAccountGUID() +
|
||||
", and has " + getClientsCount() + " clients.");
|
||||
"Client is named '" + clientsDataDelegate.getClientName() + "'" +
|
||||
", has client guid " + clientsDataDelegate.getAccountGUID() +
|
||||
", and has " + clientsDataDelegate.getClientsCount() + " clients.");
|
||||
|
||||
thisSyncIsForced = (extras != null) && (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false));
|
||||
long delay = delayMilliseconds();
|
||||
@ -405,7 +412,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe
|
||||
syncMonitor.wait();
|
||||
|
||||
if (setNextSync.get()) {
|
||||
long interval = getSyncInterval();
|
||||
long interval = getSyncInterval(clientsDataDelegate);
|
||||
long next = System.currentTimeMillis() + interval;
|
||||
|
||||
if (thisSyncIsForced) {
|
||||
@ -426,13 +433,13 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe
|
||||
}
|
||||
}
|
||||
|
||||
public int getSyncInterval() {
|
||||
public int getSyncInterval(ClientsDataDelegate clientsDataDelegate) {
|
||||
// Must have been a problem that means we can't access the Account.
|
||||
if (this.localAccount == null) {
|
||||
return SINGLE_DEVICE_INTERVAL_MILLISECONDS;
|
||||
}
|
||||
|
||||
int clientsCount = this.getClientsCount();
|
||||
int clientsCount = clientsDataDelegate.getClientsCount();
|
||||
if (clientsCount <= 1) {
|
||||
return SINGLE_DEVICE_INTERVAL_MILLISECONDS;
|
||||
}
|
||||
@ -481,8 +488,8 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe
|
||||
password,
|
||||
serverURL,
|
||||
null, // We'll re-fetch cluster URL; not great, but not harmful.
|
||||
getClientName(),
|
||||
getAccountGUID());
|
||||
clientsDataDelegate.getClientName(),
|
||||
clientsDataDelegate.getAccountGUID());
|
||||
|
||||
// Bug 772971: pickle Sync account parameters on background thread to
|
||||
// avoid strict mode warnings.
|
||||
@ -502,11 +509,10 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
// TODO: default serverURL.
|
||||
final KeyBundle keyBundle = new KeyBundle(username, syncKey);
|
||||
final AuthHeaderProvider authHeaderProvider = new BasicAuthHeaderProvider(username, password);
|
||||
GlobalSession globalSession = new GlobalSession(serverURL, username, authHeaderProvider, prefsPath,
|
||||
keyBundle, this, this.mContext, extras, this);
|
||||
GlobalSession globalSession = new GlobalSession(username, authHeaderProvider, prefsPath,
|
||||
keyBundle, this, this.mContext, extras, clientsDataDelegate, nodeAssignmentDelegate);
|
||||
|
||||
globalSession.start();
|
||||
}
|
||||
@ -545,72 +551,9 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe
|
||||
Logger.trace(LOG_TAG, "Stage completed: " + currentState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getAccountGUID() {
|
||||
String accountGUID = accountSharedPreferences.getString(SyncConfiguration.PREF_ACCOUNT_GUID, null);
|
||||
if (accountGUID == null) {
|
||||
Logger.debug(LOG_TAG, "Account GUID was null. Creating a new one.");
|
||||
accountGUID = Utils.generateGuid();
|
||||
accountSharedPreferences.edit().putString(SyncConfiguration.PREF_ACCOUNT_GUID, accountGUID).commit();
|
||||
}
|
||||
return accountGUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getClientName() {
|
||||
String clientName = accountSharedPreferences.getString(SyncConfiguration.PREF_CLIENT_NAME, null);
|
||||
if (clientName == null) {
|
||||
clientName = GlobalConstants.MOZ_APP_DISPLAYNAME + " on " + android.os.Build.MODEL;
|
||||
accountSharedPreferences.edit().putString(SyncConfiguration.PREF_CLIENT_NAME, clientName).commit();
|
||||
}
|
||||
return clientName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setClientsCount(int clientsCount) {
|
||||
accountSharedPreferences.edit().putLong(SyncConfiguration.PREF_NUM_CLIENTS, (long) clientsCount).commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocalGUID(String guid) {
|
||||
return getAccountGUID().equals(guid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getClientsCount() {
|
||||
return (int) accountSharedPreferences.getLong(SyncConfiguration.PREF_NUM_CLIENTS, 0);
|
||||
}
|
||||
|
||||
public synchronized boolean getClusterURLIsStale() {
|
||||
return accountSharedPreferences.getBoolean(SyncConfiguration.PREF_CLUSTER_URL_IS_STALE, false);
|
||||
}
|
||||
|
||||
public synchronized void setClusterURLIsStale(boolean clusterURLIsStale) {
|
||||
Editor edit = accountSharedPreferences.edit();
|
||||
edit.putBoolean(SyncConfiguration.PREF_CLUSTER_URL_IS_STALE, clusterURLIsStale);
|
||||
edit.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantNodeAssignment() {
|
||||
return getClusterURLIsStale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informNodeAuthenticationFailed(GlobalSession session, URI failedClusterURL) {
|
||||
// TODO: communicate to the user interface that we need a new user password!
|
||||
// TODO: only freshen the cluster URL (better yet, forget the cluster URL) after the user has provided new credentials.
|
||||
setClusterURLIsStale(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informNodeAssigned(GlobalSession session, URI oldClusterURL, URI newClusterURL) {
|
||||
setClusterURLIsStale(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informUnauthorizedResponse(GlobalSession session, URI oldClusterURL) {
|
||||
setClusterURLIsStale(true);
|
||||
nodeAssignmentDelegate.setClusterURLIsStale(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -41,10 +41,9 @@ public class TestClientsStage extends AndroidSyncTestCase {
|
||||
final ClientsDataDelegate delegate = new MockClientsDataDelegate();
|
||||
|
||||
final GlobalSession session = new GlobalSession(
|
||||
null,
|
||||
TEST_USERNAME, new BasicAuthHeaderProvider(TEST_USERNAME, TEST_PASSWORD), null,
|
||||
new KeyBundle(TEST_USERNAME, TEST_SYNC_KEY),
|
||||
callback, context, null, delegate);
|
||||
callback, context, null, delegate, callback);
|
||||
|
||||
SyncClientsEngineStage stage = new SyncClientsEngineStage() {
|
||||
|
||||
|
@ -153,10 +153,9 @@ public class TestResetting extends AndroidSyncTestCase {
|
||||
|
||||
private GlobalSession createDefaultGlobalSession(final GlobalSessionCallback callback) throws SyncConfigurationException, IllegalArgumentException, NonObjectJSONException, IOException, ParseException, CryptoException {
|
||||
return new GlobalSession(
|
||||
null,
|
||||
TEST_USERNAME, new BasicAuthHeaderProvider(TEST_USERNAME, TEST_PASSWORD), null,
|
||||
new KeyBundle(TEST_USERNAME, TEST_SYNC_KEY),
|
||||
callback, getApplicationContext(), null, null) {
|
||||
callback, getApplicationContext(), null, null, callback) {
|
||||
|
||||
@Override
|
||||
public boolean engineIsEnabled(String engineName,
|
||||
|
@ -169,7 +169,7 @@ public class TestSyncAccounts extends AndroidSyncTestCase {
|
||||
assertNotNull(account);
|
||||
|
||||
SharedPreferences prefs = Utils.getSharedPreferences(context, TEST_PRODUCT, TEST_USERNAME,
|
||||
SyncAccounts.DEFAULT_SERVER, TEST_PROFILE, TEST_VERSION);
|
||||
SyncConstants.DEFAULT_AUTH_SERVER, TEST_PROFILE, TEST_VERSION);
|
||||
|
||||
// Verify that client record is set.
|
||||
assertEquals(TEST_GUID, prefs.getString(SyncConfiguration.PREF_ACCOUNT_GUID, null));
|
||||
|
@ -5,10 +5,10 @@ package org.mozilla.gecko.background.sync;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
|
||||
import org.mozilla.gecko.background.testhelpers.DefaultGlobalSessionCallback;
|
||||
import org.mozilla.gecko.background.testhelpers.MockGlobalSession;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
@ -21,7 +21,6 @@ import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
|
||||
import org.mozilla.gecko.sync.setup.Constants;
|
||||
import org.mozilla.gecko.sync.setup.SyncAccounts;
|
||||
import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters;
|
||||
import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
|
||||
import org.mozilla.gecko.sync.syncadapter.SyncAdapter;
|
||||
|
||||
import android.accounts.Account;
|
||||
@ -163,7 +162,7 @@ public class TestUpgradeRequired extends AndroidSyncTestCase {
|
||||
*/
|
||||
public void testUpgradeResponse() throws SyncConfigurationException, IllegalArgumentException, NonObjectJSONException, IOException, ParseException, CryptoException {
|
||||
final Result calledUpgradeRequired = new Result();
|
||||
final GlobalSessionCallback callback = new BlankGlobalSessionCallback() {
|
||||
final GlobalSessionCallback callback = new DefaultGlobalSessionCallback() {
|
||||
@Override
|
||||
public void informUpgradeRequiredResponse(final GlobalSession session) {
|
||||
calledUpgradeRequired.called = true;
|
||||
@ -171,7 +170,7 @@ public class TestUpgradeRequired extends AndroidSyncTestCase {
|
||||
};
|
||||
|
||||
final GlobalSession session = new MockGlobalSession(
|
||||
TEST_SERVER, TEST_USERNAME, TEST_PASSWORD,
|
||||
TEST_USERNAME, TEST_PASSWORD,
|
||||
new KeyBundle(TEST_USERNAME, TEST_SYNC_KEY), callback);
|
||||
|
||||
session.interpretHTTPFailure(simulate400());
|
||||
@ -182,55 +181,4 @@ public class TestUpgradeRequired extends AndroidSyncTestCase {
|
||||
public void tearDown() {
|
||||
deleteTestAccount();
|
||||
}
|
||||
|
||||
public abstract class BlankGlobalSessionCallback implements GlobalSessionCallback {
|
||||
public BlankGlobalSessionCallback() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestBackoff(long backoff) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantNodeAssignment() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informUnauthorizedResponse(GlobalSession globalSession,
|
||||
URI oldClusterURL) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informNodeAssigned(GlobalSession globalSession,
|
||||
URI oldClusterURL, URI newClusterURL) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void informNodeAuthenticationFailed(GlobalSession globalSession,
|
||||
URI failedClusterURL) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAborted(GlobalSession globalSession, String reason) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(GlobalSession globalSession, Exception ex) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSuccess(GlobalSession globalSession) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleStageCompleted(Stage currentState,
|
||||
GlobalSession globalSession) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBackOff() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,4 +60,9 @@ public class DefaultGlobalSessionCallback implements GlobalSessionCallback {
|
||||
public boolean shouldBackOff() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nodeWeaveURL() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,10 @@ import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
|
||||
|
||||
public class MockGlobalSession extends MockPrefsGlobalSession {
|
||||
|
||||
public MockGlobalSession(String clusterURL, String username, String password,
|
||||
public MockGlobalSession(String username, String password,
|
||||
KeyBundle syncKeyBundle, GlobalSessionCallback callback)
|
||||
throws SyncConfigurationException, IllegalArgumentException, IOException, ParseException, NonObjectJSONException {
|
||||
super(clusterURL, username, password, null, syncKeyBundle, callback, /* context */ null, null, null);
|
||||
super(username, password, null, syncKeyBundle, callback, /* context */ null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -26,23 +26,23 @@ public class MockPrefsGlobalSession extends GlobalSession {
|
||||
|
||||
public MockSharedPreferences prefs;
|
||||
|
||||
public MockPrefsGlobalSession(String serverURL,
|
||||
public MockPrefsGlobalSession(
|
||||
String username, String password, String prefsPath,
|
||||
KeyBundle syncKeyBundle, GlobalSessionCallback callback, Context context,
|
||||
Bundle extras, ClientsDataDelegate clientsDelegate)
|
||||
throws SyncConfigurationException, IllegalArgumentException, IOException,
|
||||
ParseException, NonObjectJSONException {
|
||||
this(serverURL, username, new BasicAuthHeaderProvider(username, password), prefsPath, syncKeyBundle, callback, context, extras, clientsDelegate);
|
||||
this(username, new BasicAuthHeaderProvider(username, password), prefsPath, syncKeyBundle, callback, context, extras, clientsDelegate);
|
||||
}
|
||||
|
||||
public MockPrefsGlobalSession(String serverURL,
|
||||
public MockPrefsGlobalSession(
|
||||
String username, AuthHeaderProvider authHeaderProvider, String prefsPath,
|
||||
KeyBundle syncKeyBundle, GlobalSessionCallback callback, Context context,
|
||||
Bundle extras, ClientsDataDelegate clientsDelegate)
|
||||
throws SyncConfigurationException, IllegalArgumentException, IOException,
|
||||
ParseException, NonObjectJSONException {
|
||||
super(serverURL, username, authHeaderProvider, prefsPath, syncKeyBundle,
|
||||
callback, context, extras, clientsDelegate);
|
||||
super(username, authHeaderProvider, prefsPath, syncKeyBundle,
|
||||
callback, context, extras, clientsDelegate, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -294,33 +294,6 @@
|
||||
|
||||
"dom/imptests/editing/conformancetest/test_runtest.html":"takes too long",
|
||||
|
||||
"dom/media/tests/mochitest/test_dataChannel_basicAudio.html":"bug 908473",
|
||||
"dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html":"",
|
||||
"dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html":"",
|
||||
"dom/media/tests/mochitest/test_dataChannel_basicDataOnly.html":"",
|
||||
"dom/media/tests/mochitest/test_dataChannel_basicVideo.html":"",
|
||||
"dom/media/tests/mochitest/test_dataChannel_noOffer.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_addCandidateInHaveLocalOffer.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_basicAudio.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_basicVideo.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_bug822674.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_bug825703.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_bug827843.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_bug834153.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_bug835370.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideoAudio.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInHaveLocalOffer.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInStable.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_setLocalOfferInHaveRemoteOffer.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInStable.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_setRemoteOfferInHaveLocalOffer.html":"",
|
||||
"dom/media/tests/mochitest/test_peerConnection_throwInCallbacks.html":"",
|
||||
"dom/media/tests/ipc/test_ipc.html":"nested ipc not working",
|
||||
|
||||
"dom/network/tests/test_networkstats_basics.html":"Will be fixed in bug 858005",
|
||||
|
Loading…
Reference in New Issue
Block a user