mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 19:35:51 +00:00
Merge from mozilla-central
This commit is contained in:
commit
7ec790e011
@ -157,7 +157,7 @@ __defineSetter__("PluralForm", function (val) {
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
|
||||
"resource:///modules/TelemetryStopwatch.jsm");
|
||||
"resource://gre/modules/TelemetryStopwatch.jsm");
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
XPCOMUtils.defineLazyGetter(this, "Weave", function() {
|
||||
|
@ -105,5 +105,7 @@ function getIconForApp(aShell, callback) {
|
||||
function onIconDownloaded(aShell, aMimeType, aStatusCode, aIcon, aCallback) {
|
||||
if (Components.isSuccessCode(aStatusCode)) {
|
||||
aShell.processIcon(aMimeType, aIcon, aCallback);
|
||||
} else {
|
||||
aCallback.call(aShell);
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource:///modules/Services.jsm");
|
||||
Cu.import("resource:///modules/FileUtils.jsm");
|
||||
Cu.import("resource:///modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
let WebappsInstaller = {
|
||||
/**
|
||||
@ -495,7 +495,7 @@ MacNativeApp.prototype = {
|
||||
throw(ex);
|
||||
}
|
||||
|
||||
getIconForApp(this);
|
||||
getIconForApp(this, this._createPListFile);
|
||||
},
|
||||
|
||||
_removeInstallation: function(keepProfile) {
|
||||
@ -561,7 +561,10 @@ MacNativeApp.prototype = {
|
||||
writer.setString("Webapp", "Name", this.appName);
|
||||
writer.setString("Webapp", "Profile", this.appProfileDir.leafName);
|
||||
writer.writeFile();
|
||||
},
|
||||
|
||||
_createPListFile: function() {
|
||||
// ${InstallDir}/Contents/Info.plist
|
||||
let infoPListContent = '<?xml version="1.0" encoding="UTF-8"?>\n\
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n\
|
||||
<plist version="1.0">\n\
|
||||
@ -616,7 +619,7 @@ MacNativeApp.prototype = {
|
||||
* @param aCallback a callback function to be called
|
||||
* after the process finishes
|
||||
*/
|
||||
processIcon: function(aMimeType, aIcon) {
|
||||
processIcon: function(aMimeType, aIcon, aCallback) {
|
||||
try {
|
||||
let process = Cc["@mozilla.org/process/util;1"]
|
||||
.createInstance(Ci.nsIProcess);
|
||||
@ -633,6 +636,8 @@ MacNativeApp.prototype = {
|
||||
9);
|
||||
} catch(e) {
|
||||
throw(e);
|
||||
} finally {
|
||||
aCallback.call(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ let Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Webapps.jsm");
|
||||
Cu.import("resource://gre/modules/WebappsInstaller.jsm");
|
||||
Cu.import("resource:///modules/WebappsInstaller.jsm");
|
||||
|
||||
let webappsUI = {
|
||||
init: function webappsUI_init() {
|
||||
|
@ -950,6 +950,10 @@ toolbar[iconsize="small"] #feed-button {
|
||||
-moz-box-align: stretch;
|
||||
}
|
||||
|
||||
.urlbar-input-box {
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
||||
.urlbar-history-dropmarker {
|
||||
-moz-appearance: toolbarbutton-dropdown;
|
||||
}
|
||||
@ -1023,6 +1027,7 @@ toolbar[iconsize="small"] #feed-button {
|
||||
#urlbar-display {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
-moz-margin-start: 0;
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
@ -1033,7 +1038,7 @@ toolbar[iconsize="small"] #feed-button {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
-moz-margin-start: 4px;
|
||||
-moz-margin-end: 0;
|
||||
-moz-margin-end: 3px;
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-generic.png);
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
@ -1050,7 +1055,7 @@ toolbar[iconsize="small"] #feed-button {
|
||||
-moz-image-region: rect(0, 32px, 16px, 16px);
|
||||
}
|
||||
|
||||
#identity-box:hover:active > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon,
|
||||
#identity-box:hover:active > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon,
|
||||
#identity-box[open=true] > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
|
||||
-moz-image-region: rect(0, 48px, 16px, 32px);
|
||||
}
|
||||
@ -1082,7 +1087,6 @@ toolbar[iconsize="small"] #feed-button {
|
||||
}
|
||||
|
||||
#identity-icon-labels {
|
||||
-moz-margin-start: 4px;
|
||||
-moz-padding-start: 2px;
|
||||
-moz-padding-end: 5px;
|
||||
}
|
||||
@ -1091,6 +1095,7 @@ toolbar[iconsize="small"] #feed-button {
|
||||
background-color: #fff;
|
||||
color: hsl(92,81%,16%);
|
||||
-moz-border-end: 1px solid hsla(92,81%,16%,.2);
|
||||
-moz-margin-end: 4px;
|
||||
}
|
||||
|
||||
/* Identity popup icons */
|
||||
|
@ -1025,7 +1025,6 @@ toolbar[mode="icons"] #zoom-in-button {
|
||||
}
|
||||
|
||||
#identity-box.verifiedIdentity {
|
||||
background-color: #fff;
|
||||
color: hsl(92,100%,20%);
|
||||
-moz-border-end: 1px solid hsla(92,81%,16%,.2);
|
||||
-moz-padding-end: 4px;
|
||||
@ -1098,7 +1097,7 @@ toolbar[mode="icons"] #zoom-in-button {
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-https-ev.png);
|
||||
}
|
||||
|
||||
#identity-box:hover:active > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon,
|
||||
#identity-box:hover:active > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon,
|
||||
#identity-box[open=true] > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
|
||||
-moz-image-region: rect(0, 32px, 16px, 16px);
|
||||
}
|
||||
|
@ -1299,6 +1299,10 @@ html|*.urlbar-input:-moz-lwtheme:-moz-placeholder,
|
||||
.urlbar-textbox-container {
|
||||
-moz-box-align: stretch;
|
||||
}
|
||||
|
||||
.urlbar-input-box {
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
||||
#urlbar-icons {
|
||||
-moz-box-align: center;
|
||||
@ -1347,6 +1351,7 @@ html|*.urlbar-input:-moz-lwtheme:-moz-placeholder,
|
||||
#urlbar-display {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
-moz-margin-start: 0;
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
@ -1401,9 +1406,13 @@ html|*.urlbar-input:-moz-lwtheme:-moz-placeholder,
|
||||
}
|
||||
|
||||
#identity-box.verifiedIdentity {
|
||||
background-color: #fff;
|
||||
color: hsl(92,100%,20%);
|
||||
-moz-border-end: 1px solid hsla(92,81%,16%,.2);
|
||||
-moz-margin-end: 4px;
|
||||
}
|
||||
|
||||
#identity-box.verifiedIdentity:not(:-moz-lwtheme) {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#identity-box:-moz-focusring {
|
||||
@ -1412,8 +1421,8 @@ html|*.urlbar-input:-moz-lwtheme:-moz-placeholder,
|
||||
}
|
||||
|
||||
#identity-icon-labels {
|
||||
-moz-margin-start: 5px;
|
||||
-moz-margin-end: 3px;
|
||||
-moz-padding-start: 2px;
|
||||
-moz-padding-end: 5px;
|
||||
}
|
||||
|
||||
/* Location bar dropmarker */
|
||||
@ -1448,10 +1457,7 @@ html|*.urlbar-input:-moz-lwtheme:-moz-placeholder,
|
||||
#page-proxy-favicon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-top: 1px;
|
||||
margin-bottom: 1px;
|
||||
-moz-margin-start: 4px;
|
||||
-moz-margin-end: 0;
|
||||
margin: 1px 3px;
|
||||
list-style-image: url(chrome://browser/skin/identity-icons-generic.png);
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
load 257752-1-recursion.xul
|
||||
load 329335-1.xul
|
||||
load 329884-1.xul
|
||||
HTTP load 330010-1.xul
|
||||
load 330012-1.xul
|
||||
load 397148-1.xul
|
||||
skip-if(winWidget) HTTP load 330010-1.xul # bug 742455
|
||||
skip-if(winWidget) load 330012-1.xul # bug 742455
|
||||
skip-if(winWidget) load 397148-1.xul # bug 742455
|
||||
load 404346-1.xul
|
||||
load 415019-1.xul
|
||||
load 417840-1.xul
|
||||
|
@ -426,6 +426,24 @@ nsScreen::FullScreenEventListener::HandleEvent(nsIDOMEvent* aEvent)
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
aEvent->GetCurrentTarget(getter_AddRefs(target));
|
||||
|
||||
// We have to make sure that the event we got is the event sent when
|
||||
// fullscreen is disabled because we could get one when fullscreen
|
||||
// got enabled if the lock call is done at the same moment.
|
||||
nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(target);
|
||||
MOZ_ASSERT(window);
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> doc;
|
||||
window->GetDocument(getter_AddRefs(doc));
|
||||
// If we have no doc, we will just continue, remove the event and unlock.
|
||||
// This is an edge case were orientation lock and fullscreen is meaningless.
|
||||
if (doc) {
|
||||
bool fullscreen;
|
||||
doc->GetMozFullScreen(&fullscreen);
|
||||
if (fullscreen) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
target->RemoveSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"),
|
||||
this, true);
|
||||
|
||||
|
@ -35,14 +35,66 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#include "mozilla/Hal.h"
|
||||
#include "AudioManager.h"
|
||||
#include "gonk/AudioSystem.h"
|
||||
|
||||
using namespace mozilla::dom::gonk;
|
||||
using namespace android;
|
||||
using namespace mozilla::hal;
|
||||
using namespace mozilla;
|
||||
|
||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AudioManager" , ## args)
|
||||
|
||||
NS_IMPL_ISUPPORTS1(AudioManager, nsIAudioManager)
|
||||
|
||||
static AudioSystem::audio_devices
|
||||
GetRoutingMode(int aType) {
|
||||
if (aType == nsIAudioManager::FORCE_SPEAKER) {
|
||||
return AudioSystem::DEVICE_OUT_SPEAKER;
|
||||
} else if (aType == nsIAudioManager::FORCE_HEADPHONES) {
|
||||
return AudioSystem::DEVICE_OUT_WIRED_HEADSET;
|
||||
} else if (aType == nsIAudioManager::FORCE_BT_SCO) {
|
||||
return AudioSystem::DEVICE_OUT_BLUETOOTH_SCO;
|
||||
} else if (aType == nsIAudioManager::FORCE_BT_A2DP) {
|
||||
return AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
|
||||
} else {
|
||||
return AudioSystem::DEVICE_IN_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
InternalSetAudioRoutes(SwitchState aState)
|
||||
{
|
||||
if (aState == SWITCH_STATE_ON) {
|
||||
AudioManager::SetAudioRoute(nsIAudioManager::FORCE_HEADPHONES);
|
||||
} else if (aState == SWITCH_STATE_OFF) {
|
||||
AudioManager::SetAudioRoute(nsIAudioManager::FORCE_SPEAKER);
|
||||
}
|
||||
}
|
||||
|
||||
class HeadphoneSwitchObserver : public SwitchObserver
|
||||
{
|
||||
public:
|
||||
void Notify(const SwitchEvent& aEvent) {
|
||||
InternalSetAudioRoutes(aEvent.status());
|
||||
}
|
||||
};
|
||||
|
||||
AudioManager::AudioManager() : mPhoneState(PHONE_STATE_CURRENT),
|
||||
mObserver(new HeadphoneSwitchObserver())
|
||||
{
|
||||
RegisterSwitchObserver(SWITCH_HEADPHONES, mObserver);
|
||||
|
||||
InternalSetAudioRoutes(GetCurrentSwitchState(SWITCH_HEADPHONES));
|
||||
}
|
||||
|
||||
AudioManager::~AudioManager() {
|
||||
UnregisterSwitchObserver(SWITCH_HEADPHONES, mObserver);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AudioManager::GetMicrophoneMuted(bool* aMicrophoneMuted)
|
||||
{
|
||||
@ -161,3 +213,12 @@ AudioManager::GetForceForUse(PRInt32 aUsage, PRInt32* aForce) {
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
AudioManager::SetAudioRoute(int aRoutes) {
|
||||
audio_io_handle_t handle = AudioSystem::getOutput(AudioSystem::SYSTEM);
|
||||
|
||||
String8 cmd;
|
||||
cmd.appendFormat("%s=%d", AudioParameter::keyRouting, GetRoutingMode(aRoutes));
|
||||
AudioSystem::setParameters(handle, cmd);
|
||||
}
|
||||
|
@ -38,15 +38,22 @@
|
||||
#ifndef mozilla_dom_system_b2g_audiomanager_h__
|
||||
#define mozilla_dom_system_b2g_audiomanager_h__
|
||||
|
||||
#include "mozilla/Observer.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIAudioManager.h"
|
||||
|
||||
|
||||
// {b2b51423-502d-4d77-89b3-7786b562b084}
|
||||
#define NS_AUDIOMANAGER_CID {0x94f6fd70, 0x7615, 0x4af9, \
|
||||
{0x89, 0x10, 0xf9, 0x3c, 0x55, 0xe6, 0x62, 0xec}}
|
||||
#define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace hal {
|
||||
class SwitchEvent;
|
||||
typedef Observer<SwitchEvent> SwitchObserver;
|
||||
} // namespace hal
|
||||
|
||||
namespace dom {
|
||||
namespace gonk {
|
||||
|
||||
@ -56,16 +63,18 @@ public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIAUDIOMANAGER
|
||||
|
||||
AudioManager() : mPhoneState(PHONE_STATE_CURRENT)
|
||||
{
|
||||
}
|
||||
AudioManager();
|
||||
~AudioManager();
|
||||
|
||||
static void SetAudioRoute(int aRoutes);
|
||||
protected:
|
||||
PRInt32 mPhoneState;
|
||||
|
||||
private:
|
||||
nsAutoPtr<mozilla::hal::SwitchObserver> mObserver;
|
||||
};
|
||||
|
||||
|
||||
} /* namespace telephony */
|
||||
} /* namespace gonk */
|
||||
} /* namespace dom */
|
||||
} /* namespace mozilla */
|
||||
|
||||
|
@ -67,3 +67,4 @@ XPCSHELL_TESTS = tests
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
include $(topsrcdir)/ipc/chromium/chromium-config.mk
|
||||
|
78
hal/Hal.cpp
78
hal/Hal.cpp
@ -596,5 +596,83 @@ UnlockScreenOrientation()
|
||||
PROXY_IF_SANDBOXED(UnlockScreenOrientation());
|
||||
}
|
||||
|
||||
void
|
||||
EnableSwitchNotifications(hal::SwitchDevice aDevice) {
|
||||
AssertMainThread();
|
||||
PROXY_IF_SANDBOXED(EnableSwitchNotifications(aDevice));
|
||||
}
|
||||
|
||||
void
|
||||
DisableSwitchNotifications(hal::SwitchDevice aDevice) {
|
||||
AssertMainThread();
|
||||
PROXY_IF_SANDBOXED(DisableSwitchNotifications(aDevice));
|
||||
}
|
||||
|
||||
hal::SwitchState GetCurrentSwitchState(hal::SwitchDevice aDevice)
|
||||
{
|
||||
AssertMainThread();
|
||||
RETURN_PROXY_IF_SANDBOXED(GetCurrentSwitchState(aDevice));
|
||||
}
|
||||
|
||||
typedef mozilla::ObserverList<SwitchEvent> SwitchObserverList;
|
||||
|
||||
static SwitchObserverList *sSwitchObserverLists = NULL;
|
||||
|
||||
static SwitchObserverList&
|
||||
GetSwitchObserverList(hal::SwitchDevice aDevice) {
|
||||
MOZ_ASSERT(0 <= aDevice && aDevice < NUM_SWITCH_DEVICE);
|
||||
if (sSwitchObserverLists == NULL) {
|
||||
sSwitchObserverLists = new SwitchObserverList[NUM_SWITCH_DEVICE];
|
||||
}
|
||||
return sSwitchObserverLists[aDevice];
|
||||
}
|
||||
|
||||
static void
|
||||
ReleaseObserversIfNeeded() {
|
||||
for (int i = 0; i < NUM_SWITCH_DEVICE; i++) {
|
||||
if (sSwitchObserverLists[i].Length() != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
//The length of every list is 0, no observer in the list.
|
||||
delete [] sSwitchObserverLists;
|
||||
sSwitchObserverLists = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
RegisterSwitchObserver(hal::SwitchDevice aDevice, hal::SwitchObserver *aObserver)
|
||||
{
|
||||
AssertMainThread();
|
||||
SwitchObserverList& observer = GetSwitchObserverList(aDevice);
|
||||
observer.AddObserver(aObserver);
|
||||
if (observer.Length() == 1) {
|
||||
EnableSwitchNotifications(aDevice);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
UnregisterSwitchObserver(hal::SwitchDevice aDevice, hal::SwitchObserver *aObserver)
|
||||
{
|
||||
AssertMainThread();
|
||||
SwitchObserverList& observer = GetSwitchObserverList(aDevice);
|
||||
observer.RemoveObserver(aObserver);
|
||||
if (observer.Length() == 0) {
|
||||
DisableSwitchNotifications(aDevice);
|
||||
ReleaseObserversIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotifySwitchChange(const hal::SwitchEvent& aEvent)
|
||||
{
|
||||
// When callback this notification, main thread may call unregister function
|
||||
// first. We should check if this pointer is valid.
|
||||
if (!sSwitchObserverLists)
|
||||
return;
|
||||
|
||||
SwitchObserverList& observer = GetSwitchObserverList(aEvent.device());
|
||||
observer.Broadcast(aEvent);
|
||||
}
|
||||
|
||||
} // namespace hal
|
||||
} // namespace mozilla
|
||||
|
25
hal/Hal.h
25
hal/Hal.h
@ -345,6 +345,31 @@ bool LockScreenOrientation(const dom::ScreenOrientation& aOrientation);
|
||||
*/
|
||||
void UnlockScreenOrientation();
|
||||
|
||||
/**
|
||||
* Register an observer for the switch of given SwitchDevice.
|
||||
*
|
||||
* The observer will receive data whenever the data generated by the
|
||||
* given switch.
|
||||
*/
|
||||
void RegisterSwitchObserver(hal::SwitchDevice aDevice, hal::SwitchObserver *aSwitchObserver);
|
||||
|
||||
/**
|
||||
* Unregister an observer for the switch of given SwitchDevice.
|
||||
*/
|
||||
void UnregisterSwitchObserver(hal::SwitchDevice aDevice, hal::SwitchObserver *aSwitchObserver);
|
||||
|
||||
/**
|
||||
* Notify the state of the switch.
|
||||
*
|
||||
* This API is internal to hal; clients shouldn't call it directly.
|
||||
*/
|
||||
void NotifySwitchChange(const hal::SwitchEvent& aEvent);
|
||||
|
||||
/**
|
||||
* Get current switch information.
|
||||
*/
|
||||
hal::SwitchState GetCurrentSwitchState(hal::SwitchDevice aDevice);
|
||||
|
||||
} // namespace MOZ_HAL_NAMESPACE
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -87,6 +87,16 @@ void EnableScreenOrientationNotifications();
|
||||
*/
|
||||
void DisableScreenOrientationNotifications();
|
||||
|
||||
/**
|
||||
* Enable switch notifications from the backend
|
||||
*/
|
||||
void EnableSwitchNotifications(hal::SwitchDevice aDevice);
|
||||
|
||||
/**
|
||||
* Disable switch notifications from the backend
|
||||
*/
|
||||
void DisableSwitchNotifications(hal::SwitchDevice aDevice);
|
||||
|
||||
} // namespace MOZ_HAL_NAMESPACE
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -37,6 +37,22 @@ enum FlashMode {
|
||||
eHalLightFlash_Hardware = 2 // hardware assisted flashing
|
||||
};
|
||||
|
||||
class SwitchEvent;
|
||||
|
||||
enum SwitchDevice {
|
||||
SWITCH_DEVICE_UNKNOWN = -1,
|
||||
SWITCH_HEADPHONES,
|
||||
NUM_SWITCH_DEVICE
|
||||
};
|
||||
|
||||
enum SwitchState {
|
||||
SWITCH_STATE_UNKNOWN = -1,
|
||||
SWITCH_STATE_ON,
|
||||
SWITCH_STATE_OFF,
|
||||
NUM_SWITCH_STATE
|
||||
};
|
||||
|
||||
typedef Observer<SwitchEvent> SwitchObserver;
|
||||
} // namespace hal
|
||||
} // namespace mozilla
|
||||
|
||||
@ -97,6 +113,27 @@ struct ParamTraits<mozilla::hal::WakeLockControl>
|
||||
mozilla::hal::WAKE_LOCK_ADD_ONE>
|
||||
{};
|
||||
|
||||
/**
|
||||
* Serializer for SwitchState
|
||||
*/
|
||||
template <>
|
||||
struct ParamTraits<mozilla::hal::SwitchState>:
|
||||
public EnumSerializer<mozilla::hal::SwitchState,
|
||||
mozilla::hal::SWITCH_STATE_UNKNOWN,
|
||||
mozilla::hal::NUM_SWITCH_STATE> {
|
||||
};
|
||||
|
||||
/**
|
||||
* Serializer for SwitchDevice
|
||||
*/
|
||||
template <>
|
||||
struct ParamTraits<mozilla::hal::SwitchDevice>:
|
||||
public EnumSerializer<mozilla::hal::SwitchDevice,
|
||||
mozilla::hal::SWITCH_DEVICE_UNKNOWN,
|
||||
mozilla::hal::NUM_SWITCH_DEVICE> {
|
||||
};
|
||||
|
||||
|
||||
} // namespace IPC
|
||||
|
||||
#endif // mozilla_hal_Types_h
|
||||
|
@ -85,6 +85,7 @@ CPPSRCS += \
|
||||
Power.cpp \
|
||||
GonkSensor.cpp \
|
||||
UeventPoller.cpp \
|
||||
GonkSwitch.cpp \
|
||||
$(NULL)
|
||||
else ifeq (Linux,$(OS_TARGET))
|
||||
CPPSRCS += \
|
||||
@ -121,6 +122,7 @@ CPPSRCS += \
|
||||
FallbackLights.cpp \
|
||||
FallbackTime.cpp \
|
||||
FallbackWakeLocks.cpp \
|
||||
FallbackSwitch.cpp \
|
||||
$(NULL)
|
||||
endif #}
|
||||
|
||||
|
29
hal/fallback/FallbackSwitch.cpp
Normal file
29
hal/fallback/FallbackSwitch.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/Hal.h"
|
||||
|
||||
using namespace mozilla::hal;
|
||||
|
||||
namespace mozilla {
|
||||
namespace hal_impl {
|
||||
|
||||
void
|
||||
EnableSwitchNotifications(SwitchDevice aDevice)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
DisableSwitchNotifications(SwitchDevice aDevice)
|
||||
{
|
||||
}
|
||||
|
||||
SwitchState
|
||||
GetCurrentSwitchState(SwitchDevice aDevice) {
|
||||
return SWITCH_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
} // namespace hal_impl
|
||||
} // namespace mozilla
|
195
hal/gonk/GonkSwitch.cpp
Normal file
195
hal/gonk/GonkSwitch.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <android/log.h>
|
||||
#include <sysutils/NetlinkEvent.h>
|
||||
|
||||
#include "base/message_loop.h"
|
||||
|
||||
#include "Hal.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "UeventPoller.h"
|
||||
|
||||
using namespace mozilla::hal;
|
||||
|
||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkSwitch" , ## args)
|
||||
|
||||
namespace mozilla {
|
||||
namespace hal_impl {
|
||||
|
||||
struct {const char* name; SwitchDevice device; } kSwitchNameMap[] = {
|
||||
{ "h2w", SWITCH_HEADPHONES },
|
||||
{ NULL, SWITCH_DEVICE_UNKNOWN },
|
||||
};
|
||||
|
||||
static SwitchDevice
|
||||
NameToDevice(const char* name) {
|
||||
for (int i = 0; kSwitchNameMap[i].device != SWITCH_DEVICE_UNKNOWN; i++) {
|
||||
if (strcmp(name, kSwitchNameMap[i].name) == 0) {
|
||||
return kSwitchNameMap[i].device;
|
||||
}
|
||||
}
|
||||
return SWITCH_DEVICE_UNKNOWN;
|
||||
}
|
||||
|
||||
class SwitchEventRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
SwitchEventRunnable(SwitchEvent& event) : mEvent(event) {}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
NotifySwitchChange(mEvent);
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
SwitchEvent mEvent;
|
||||
};
|
||||
|
||||
class SwitchEventObserver : public IUeventObserver
|
||||
{
|
||||
public:
|
||||
SwitchEventObserver() : mEnableNum(0) {
|
||||
InternalInit();
|
||||
}
|
||||
~SwitchEventObserver() {}
|
||||
|
||||
int GetEnableCount() {
|
||||
return mEnableNum;
|
||||
}
|
||||
|
||||
void EnableSwitch(SwitchDevice aDevice) {
|
||||
mEventInfo[aDevice].mEnable = true;
|
||||
mEnableNum++;
|
||||
}
|
||||
|
||||
void DisableSwitch(SwitchDevice aDevice) {
|
||||
mEventInfo[aDevice].mEnable = false;
|
||||
mEnableNum--;
|
||||
}
|
||||
|
||||
void Notify(const NetlinkEvent& event) {
|
||||
const char* name;
|
||||
const char* state;
|
||||
|
||||
SwitchDevice device = ProcessEvent(event, &name, &state);
|
||||
if (device == SWITCH_DEVICE_UNKNOWN) {
|
||||
return;
|
||||
}
|
||||
|
||||
EventInfo& info = mEventInfo[device];
|
||||
info.mEvent.status() = atoi(state) == 0 ? SWITCH_STATE_OFF : SWITCH_STATE_ON;
|
||||
if (info.mEnable) {
|
||||
NS_DispatchToMainThread(new SwitchEventRunnable(info.mEvent));
|
||||
}
|
||||
}
|
||||
|
||||
SwitchState GetCurrentInformation(SwitchDevice aDevice) {
|
||||
return mEventInfo[aDevice].mEvent.status();
|
||||
}
|
||||
|
||||
private:
|
||||
class EventInfo {
|
||||
public:
|
||||
EventInfo() : mEnable(false) {}
|
||||
SwitchEvent mEvent;
|
||||
bool mEnable;
|
||||
};
|
||||
|
||||
EventInfo mEventInfo[NUM_SWITCH_DEVICE];
|
||||
size_t mEnableNum;
|
||||
|
||||
void InternalInit() {
|
||||
for (int i = 0; i < NUM_SWITCH_DEVICE; i++) {
|
||||
mEventInfo[i].mEvent.device() = kSwitchNameMap[i].device;
|
||||
mEventInfo[i].mEvent.status() = SWITCH_STATE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetEventInfo(const NetlinkEvent& event, const char** name, const char** state) {
|
||||
//working around the android code not being const-correct
|
||||
NetlinkEvent *e = const_cast<NetlinkEvent*>(&event);
|
||||
const char* subsystem = e->getSubsystem();
|
||||
|
||||
if (!subsystem || strcmp(subsystem, "switch")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*name = e->findParam("SWITCH_NAME");
|
||||
*state = e->findParam("SWITCH_STATE");
|
||||
|
||||
if (!*name || !*state) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SwitchDevice ProcessEvent(const NetlinkEvent& event, const char** name, const char** state) {
|
||||
bool rv = GetEventInfo(event, name, state);
|
||||
NS_ENSURE_TRUE(rv, SWITCH_DEVICE_UNKNOWN);
|
||||
return NameToDevice(*name);
|
||||
}
|
||||
};
|
||||
|
||||
SwitchEventObserver* sSwitchObserver;
|
||||
|
||||
static void
|
||||
InitializeResourceIfNeed()
|
||||
{
|
||||
if (!sSwitchObserver) {
|
||||
sSwitchObserver = new SwitchEventObserver();
|
||||
RegisterUeventListener(sSwitchObserver);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ReleaseResourceIfNeed()
|
||||
{
|
||||
if (sSwitchObserver->GetEnableCount() == 0) {
|
||||
UnregisterUeventListener(sSwitchObserver);
|
||||
delete sSwitchObserver;
|
||||
sSwitchObserver = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
EnableSwitchNotificationsIOThread(SwitchDevice aDevice)
|
||||
{
|
||||
InitializeResourceIfNeed();
|
||||
sSwitchObserver->EnableSwitch(aDevice);
|
||||
}
|
||||
|
||||
void
|
||||
EnableSwitchNotifications(SwitchDevice aDevice)
|
||||
{
|
||||
XRE_GetIOMessageLoop()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(EnableSwitchNotificationsIOThread, aDevice));
|
||||
}
|
||||
|
||||
static void
|
||||
DisableSwitchNotificationsIOThread(SwitchDevice aDevice)
|
||||
{
|
||||
MOZ_ASSERT(sSwitchObserver->GetEnableCount());
|
||||
sSwitchObserver->DisableSwitch(aDevice);
|
||||
ReleaseResourceIfNeed();
|
||||
}
|
||||
|
||||
void
|
||||
DisableSwitchNotifications(SwitchDevice aDevice)
|
||||
{
|
||||
XRE_GetIOMessageLoop()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(DisableSwitchNotificationsIOThread, aDevice));
|
||||
}
|
||||
|
||||
SwitchState
|
||||
GetCurrentSwitchState(SwitchDevice aDevice)
|
||||
{
|
||||
MOZ_ASSERT(sSwitchObserver && sSwitchObserver->GetEnableCount());
|
||||
return sSwitchObserver->GetCurrentInformation(aDevice);
|
||||
}
|
||||
|
||||
} // hal_impl
|
||||
} //mozilla
|
@ -52,6 +52,8 @@ using mozilla::hal::SensorType;
|
||||
using mozilla::hal::SensorAccuracyType;
|
||||
using mozilla::hal::WakeLockControl;
|
||||
using mozilla::dom::ScreenOrientation;
|
||||
using mozilla::hal::SwitchState;
|
||||
using mozilla::hal::SwitchDevice;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -82,6 +84,11 @@ namespace hal {
|
||||
double bandwidth;
|
||||
bool canBeMetered;
|
||||
};
|
||||
|
||||
struct SwitchEvent {
|
||||
SwitchDevice device;
|
||||
SwitchState status;
|
||||
};
|
||||
}
|
||||
|
||||
namespace hal {
|
||||
@ -102,6 +109,7 @@ child:
|
||||
NotifyNetworkChange(NetworkInformation aNetworkInfo);
|
||||
NotifyWakeLockChange(WakeLockInformation aWakeLockInfo);
|
||||
NotifyScreenOrientationChange(ScreenOrientation aScreenOrientation);
|
||||
NotifySwitchChange(SwitchEvent aEvent);
|
||||
|
||||
parent:
|
||||
Vibrate(uint32[] pattern, uint64[] id, PBrowser browser);
|
||||
@ -150,6 +158,11 @@ parent:
|
||||
sync LockScreenOrientation(ScreenOrientation aOrientation)
|
||||
returns (bool allowed);
|
||||
UnlockScreenOrientation();
|
||||
|
||||
EnableSwitchNotifications(SwitchDevice aDevice);
|
||||
DisableSwitchNotifications(SwitchDevice aDevice);
|
||||
sync GetCurrentSwitchState(SwitchDevice aDevice)
|
||||
returns (SwitchState aState);
|
||||
|
||||
child:
|
||||
NotifySensorChange(SensorData aSensorData);
|
||||
|
@ -240,12 +240,33 @@ GetWakeLockInfo(const nsAString &aTopic, WakeLockInformation *aWakeLockInfo)
|
||||
Hal()->SendGetWakeLockInfo(nsString(aTopic), aWakeLockInfo);
|
||||
}
|
||||
|
||||
void
|
||||
EnableSwitchNotifications(SwitchDevice aDevice)
|
||||
{
|
||||
Hal()->SendEnableSwitchNotifications(aDevice);
|
||||
}
|
||||
|
||||
void
|
||||
DisableSwitchNotifications(SwitchDevice aDevice)
|
||||
{
|
||||
Hal()->SendDisableSwitchNotifications(aDevice);
|
||||
}
|
||||
|
||||
SwitchState
|
||||
GetCurrentSwitchState(SwitchDevice aDevice)
|
||||
{
|
||||
SwitchState state;
|
||||
Hal()->SendGetCurrentSwitchState(aDevice, &state);
|
||||
return state;
|
||||
}
|
||||
|
||||
class HalParent : public PHalParent
|
||||
, public BatteryObserver
|
||||
, public NetworkObserver
|
||||
, public ISensorObserver
|
||||
, public WakeLockObserver
|
||||
, public ScreenOrientationObserver
|
||||
, public SwitchObserver
|
||||
{
|
||||
public:
|
||||
NS_OVERRIDE virtual bool
|
||||
@ -501,6 +522,32 @@ public:
|
||||
{
|
||||
unused << SendNotifyWakeLockChange(aWakeLockInfo);
|
||||
}
|
||||
|
||||
NS_OVERRIDE virtual bool
|
||||
RecvEnableSwitchNotifications(const SwitchDevice& aDevice)
|
||||
{
|
||||
hal::RegisterSwitchObserver(aDevice, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_OVERRIDE virtual bool
|
||||
RecvDisableSwitchNotifications(const SwitchDevice& aDevice)
|
||||
{
|
||||
hal::UnregisterSwitchObserver(aDevice, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Notify(const SwitchEvent& aSwitchEvent)
|
||||
{
|
||||
unused << SendNotifySwitchChange(aSwitchEvent);
|
||||
}
|
||||
|
||||
NS_OVERRIDE virtual bool
|
||||
RecvGetCurrentSwitchState(const SwitchDevice& aDevice, hal::SwitchState *aState)
|
||||
{
|
||||
*aState = hal::GetCurrentSwitchState(aDevice);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class HalChild : public PHalChild {
|
||||
@ -531,6 +578,12 @@ public:
|
||||
hal::NotifyScreenOrientationChange(aScreenOrientation);
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_OVERRIDE virtual bool
|
||||
RecvNotifySwitchChange(const mozilla::hal::SwitchEvent& aEvent) {
|
||||
hal::NotifySwitchChange(aEvent);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
|
@ -60,8 +60,10 @@ nsresult xpcJSWeakReference::Init(JSContext* cx, const JS::Value& object)
|
||||
// See if the object is a wrapped native that supports weak references.
|
||||
nsISupports* supports =
|
||||
nsXPConnect::GetXPConnect()->GetNativeOfWrapper(cx, &obj);
|
||||
if (supports) {
|
||||
mReferent = do_GetWeakReference(supports);
|
||||
nsCOMPtr<nsISupportsWeakReference> supportsWeakRef =
|
||||
do_QueryInterface(supports);
|
||||
if (supportsWeakRef) {
|
||||
supportsWeakRef->GetWeakReference(getter_AddRefs(mReferent));
|
||||
if (mReferent) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -511,11 +511,16 @@ function OnDocumentLoad(event)
|
||||
}
|
||||
|
||||
var contentRootElement = currentDoc ? currentDoc.documentElement : null;
|
||||
currentDoc = null;
|
||||
setupZoom(contentRootElement);
|
||||
setupDisplayport(contentRootElement);
|
||||
var inPrintMode = false;
|
||||
|
||||
function AfterOnLoadScripts() {
|
||||
// Regrab the root element, because the document may have changed.
|
||||
var contentRootElement =
|
||||
content.document ? content.document.documentElement : null;
|
||||
|
||||
// Take a snapshot now. We need to do this before we check whether
|
||||
// we should wait, since this might trigger dispatching of
|
||||
// MozPaintWait events and make shouldWaitForExplicitPaintWaiters() true
|
||||
@ -671,7 +676,6 @@ function LogInfo(str)
|
||||
|
||||
const SYNC_DEFAULT = 0x0;
|
||||
const SYNC_ALLOW_DISABLE = 0x1;
|
||||
var gDummyCanvas = null;
|
||||
function SynchronizeForSnapshot(flags)
|
||||
{
|
||||
if (flags & SYNC_ALLOW_DISABLE) {
|
||||
@ -682,13 +686,11 @@ function SynchronizeForSnapshot(flags)
|
||||
}
|
||||
}
|
||||
|
||||
if (gDummyCanvas == null) {
|
||||
gDummyCanvas = content.document.createElementNS(XHTML_NS, "canvas");
|
||||
gDummyCanvas.setAttribute("width", 1);
|
||||
gDummyCanvas.setAttribute("height", 1);
|
||||
}
|
||||
var dummyCanvas = content.document.createElementNS(XHTML_NS, "canvas");
|
||||
dummyCanvas.setAttribute("width", 1);
|
||||
dummyCanvas.setAttribute("height", 1);
|
||||
|
||||
var ctx = gDummyCanvas.getContext("2d");
|
||||
var ctx = dummyCanvas.getContext("2d");
|
||||
var flags = ctx.DRAWWINDOW_DRAW_CARET | ctx.DRAWWINDOW_DRAW_VIEW | ctx.DRAWWINDOW_USE_WIDGET_LAYERS;
|
||||
ctx.drawWindow(content, 0, 0, 1, 1, "rgb(255,255,255)", flags);
|
||||
}
|
||||
|
@ -405,9 +405,9 @@ void *_mmap(void *addr, size_t length, int prot, int flags,
|
||||
struct {
|
||||
void *addr;
|
||||
size_t length;
|
||||
int prot;
|
||||
int flags;
|
||||
int fd;
|
||||
long prot;
|
||||
long flags;
|
||||
long fd;
|
||||
off_t offset;
|
||||
} args = { addr, length, prot, flags, fd, offset };
|
||||
return (void *) syscall(SYS_mmap, &args);
|
||||
|
@ -124,6 +124,9 @@ public class ProfileMigrator {
|
||||
private static final String ROOT_NAME = "root_name";
|
||||
private static final String ROOT_FOLDER_ID = "folder_id";
|
||||
|
||||
// We use this to ignore the tags folder during migration.
|
||||
private static final String ROOT_TAGS_FOLDER_NAME = "tags";
|
||||
|
||||
private static final String BOOKMARK_QUERY_SELECT =
|
||||
"SELECT places.url AS p_url," +
|
||||
" bookmark.guid AS b_guid," +
|
||||
@ -557,6 +560,7 @@ public class ProfileMigrator {
|
||||
|
||||
private class PlacesRunnable implements Runnable {
|
||||
private Map<Long, Long> mRerootMap;
|
||||
private Long mTagsPlacesFolderId;
|
||||
private ArrayList<ContentProviderOperation> mOperations;
|
||||
private int mMaxEntries;
|
||||
// We support 2 classes of schemas: Firefox Places 12-13
|
||||
@ -644,6 +648,12 @@ public class ProfileMigrator {
|
||||
mRerootMap.put(placesFolderId, getFolderId(name));
|
||||
Log.v(LOGTAG, "Name: " + name + ", pid=" + placesFolderId
|
||||
+ ", nid=" + mRerootMap.get(placesFolderId));
|
||||
|
||||
// Keep track of the tags folder id so we can avoid
|
||||
// migrating tags later.
|
||||
if (ROOT_TAGS_FOLDER_NAME.equals(name))
|
||||
mTagsPlacesFolderId = placesFolderId;
|
||||
|
||||
cursor.moveToNext();
|
||||
}
|
||||
cursor.close();
|
||||
@ -1051,9 +1061,10 @@ public class ProfileMigrator {
|
||||
int type = cursor.getInt(typeCol);
|
||||
long parent = cursor.getLong(parentCol);
|
||||
|
||||
// Places has an explicit root folder, id=1 parent=0.
|
||||
// Skip that.
|
||||
if (id == 1 && parent == 0 && type == PLACES_TYPE_FOLDER) {
|
||||
// Places has an explicit root folder, id=1 parent=0. Skip that.
|
||||
// Also, skip tags, since we don't use those in native fennec.
|
||||
if ((id == 1 && parent == 0 && type == PLACES_TYPE_FOLDER) ||
|
||||
parent == mTagsPlacesFolderId) {
|
||||
cursor.moveToNext();
|
||||
continue;
|
||||
}
|
||||
|
15
mobile/android/base/sync/EngineSettings.java
Normal file
15
mobile/android/base/sync/EngineSettings.java
Normal file
@ -0,0 +1,15 @@
|
||||
/* 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;
|
||||
|
||||
public class EngineSettings {
|
||||
public final String syncID;
|
||||
public final int version;
|
||||
|
||||
public EngineSettings(final String syncID, final int version) {
|
||||
this.syncID = syncID;
|
||||
this.version = version;
|
||||
}
|
||||
}
|
@ -131,6 +131,30 @@ public class ExtendedJSONObject {
|
||||
return (String) this.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an Integer if the value for this key is an Integer, Long, or String
|
||||
* that can be parsed as a base 10 Integer.
|
||||
* Passes through null.
|
||||
*
|
||||
* @throws NumberFormatException
|
||||
*/
|
||||
public Integer getIntegerSafely(String key) throws NumberFormatException {
|
||||
Object val = this.object.get(key);
|
||||
if (val == null) {
|
||||
return null;
|
||||
}
|
||||
if (val instanceof Integer) {
|
||||
return (Integer) val;
|
||||
}
|
||||
if (val instanceof Long) {
|
||||
return new Integer(((Long) val).intValue());
|
||||
}
|
||||
if (val instanceof String) {
|
||||
return Integer.parseInt((String) val, 10);
|
||||
}
|
||||
throw new NumberFormatException("Expecting Integer, got " + val.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a server timestamp value as milliseconds since epoch.
|
||||
* @param key
|
||||
|
@ -8,7 +8,11 @@ import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@ -31,9 +35,7 @@ import org.mozilla.gecko.sync.net.SyncStorageRequest;
|
||||
import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
|
||||
import org.mozilla.gecko.sync.net.SyncStorageResponse;
|
||||
import org.mozilla.gecko.sync.stage.AndroidBrowserBookmarksServerSyncStage;
|
||||
import org.mozilla.gecko.sync.stage.FormHistoryServerSyncStage;
|
||||
import org.mozilla.gecko.sync.stage.AndroidBrowserHistoryServerSyncStage;
|
||||
import org.mozilla.gecko.sync.stage.PasswordsServerSyncStage;
|
||||
import org.mozilla.gecko.sync.stage.CheckPreconditionsStage;
|
||||
import org.mozilla.gecko.sync.stage.CompletedStage;
|
||||
import org.mozilla.gecko.sync.stage.EnsureClusterURLStage;
|
||||
@ -41,9 +43,11 @@ import org.mozilla.gecko.sync.stage.EnsureCrypto5KeysStage;
|
||||
import org.mozilla.gecko.sync.stage.FennecTabsServerSyncStage;
|
||||
import org.mozilla.gecko.sync.stage.FetchInfoCollectionsStage;
|
||||
import org.mozilla.gecko.sync.stage.FetchMetaGlobalStage;
|
||||
import org.mozilla.gecko.sync.stage.FormHistoryServerSyncStage;
|
||||
import org.mozilla.gecko.sync.stage.GlobalSyncStage;
|
||||
import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
|
||||
import org.mozilla.gecko.sync.stage.NoSuchStageException;
|
||||
import org.mozilla.gecko.sync.stage.PasswordsServerSyncStage;
|
||||
import org.mozilla.gecko.sync.stage.SyncClientsEngineStage;
|
||||
|
||||
import android.content.Context;
|
||||
@ -152,6 +156,8 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
|
||||
|
||||
registerCommands();
|
||||
prepareStages();
|
||||
|
||||
// TODO: data-driven plan for the sync, referring to prepareStages.
|
||||
}
|
||||
|
||||
protected void registerCommands() {
|
||||
@ -160,14 +166,32 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
|
||||
processor.registerCommand("resetEngine", new CommandRunner() {
|
||||
@Override
|
||||
public void executeCommand(List<String> args) {
|
||||
resetClient(new String[] { args.get(0) });
|
||||
HashSet<String> names = new HashSet<String>();
|
||||
names.add(args.get(0));
|
||||
resetStagesByName(names);
|
||||
}
|
||||
});
|
||||
|
||||
processor.registerCommand("resetAll", new CommandRunner() {
|
||||
@Override
|
||||
public void executeCommand(List<String> args) {
|
||||
resetClient(null);
|
||||
resetAllStages();
|
||||
}
|
||||
});
|
||||
|
||||
processor.registerCommand("wipeEngine", new CommandRunner() {
|
||||
@Override
|
||||
public void executeCommand(List<String> args) {
|
||||
HashSet<String> names = new HashSet<String>();
|
||||
names.add(args.get(0));
|
||||
wipeStagesByName(names);
|
||||
}
|
||||
});
|
||||
|
||||
processor.registerCommand("wipeAll", new CommandRunner() {
|
||||
@Override
|
||||
public void executeCommand(List<String> args) {
|
||||
wipeAllStages();
|
||||
}
|
||||
});
|
||||
|
||||
@ -180,24 +204,31 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
|
||||
}
|
||||
|
||||
protected void prepareStages() {
|
||||
stages = new HashMap<Stage, GlobalSyncStage>();
|
||||
stages.put(Stage.checkPreconditions, new CheckPreconditionsStage());
|
||||
stages.put(Stage.ensureClusterURL, new EnsureClusterURLStage());
|
||||
stages.put(Stage.fetchInfoCollections, new FetchInfoCollectionsStage());
|
||||
stages.put(Stage.fetchMetaGlobal, new FetchMetaGlobalStage());
|
||||
stages.put(Stage.ensureKeysStage, new EnsureCrypto5KeysStage());
|
||||
stages.put(Stage.syncClientsEngine, new SyncClientsEngineStage());
|
||||
HashMap<Stage, GlobalSyncStage> stages = new HashMap<Stage, GlobalSyncStage>();
|
||||
|
||||
// TODO: more stages.
|
||||
stages.put(Stage.syncTabs, new FennecTabsServerSyncStage());
|
||||
stages.put(Stage.syncPasswords, new PasswordsServerSyncStage());
|
||||
stages.put(Stage.syncBookmarks, new AndroidBrowserBookmarksServerSyncStage());
|
||||
stages.put(Stage.syncHistory, new AndroidBrowserHistoryServerSyncStage());
|
||||
stages.put(Stage.syncFormHistory, new FormHistoryServerSyncStage());
|
||||
stages.put(Stage.completed, new CompletedStage());
|
||||
stages.put(Stage.checkPreconditions, new CheckPreconditionsStage(this));
|
||||
stages.put(Stage.ensureClusterURL, new EnsureClusterURLStage(this));
|
||||
stages.put(Stage.fetchInfoCollections, new FetchInfoCollectionsStage(this));
|
||||
stages.put(Stage.fetchMetaGlobal, new FetchMetaGlobalStage(this));
|
||||
stages.put(Stage.ensureKeysStage, new EnsureCrypto5KeysStage(this));
|
||||
stages.put(Stage.syncClientsEngine, new SyncClientsEngineStage(this));
|
||||
|
||||
stages.put(Stage.syncTabs, new FennecTabsServerSyncStage(this));
|
||||
stages.put(Stage.syncPasswords, new PasswordsServerSyncStage(this));
|
||||
stages.put(Stage.syncBookmarks, new AndroidBrowserBookmarksServerSyncStage(this));
|
||||
stages.put(Stage.syncHistory, new AndroidBrowserHistoryServerSyncStage(this));
|
||||
stages.put(Stage.syncFormHistory, new FormHistoryServerSyncStage(this));
|
||||
|
||||
stages.put(Stage.completed, new CompletedStage(this));
|
||||
|
||||
this.stages = Collections.unmodifiableMap(stages);
|
||||
}
|
||||
|
||||
protected GlobalSyncStage getStageByName(Stage next) throws NoSuchStageException {
|
||||
public GlobalSyncStage getSyncStageByName(String name) throws NoSuchStageException {
|
||||
return getSyncStageByName(Stage.byName(name));
|
||||
}
|
||||
|
||||
public GlobalSyncStage getSyncStageByName(Stage next) throws NoSuchStageException {
|
||||
GlobalSyncStage stage = stages.get(next);
|
||||
if (stage == null) {
|
||||
throw new NoSuchStageException(next);
|
||||
@ -205,6 +236,32 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
|
||||
return stage;
|
||||
}
|
||||
|
||||
public Collection<GlobalSyncStage> getSyncStagesByEnum(Collection<Stage> enums) {
|
||||
ArrayList<GlobalSyncStage> out = new ArrayList<GlobalSyncStage>();
|
||||
for (Stage name : enums) {
|
||||
try {
|
||||
GlobalSyncStage stage = this.getSyncStageByName(name);
|
||||
out.add(stage);
|
||||
} catch (NoSuchStageException e) {
|
||||
Logger.warn(LOG_TAG, "Unable to find stage with name " + name);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public Collection<GlobalSyncStage> getSyncStagesByName(Collection<String> names) {
|
||||
ArrayList<GlobalSyncStage> out = new ArrayList<GlobalSyncStage>();
|
||||
for (String name : names) {
|
||||
try {
|
||||
GlobalSyncStage stage = this.getSyncStageByName(name);
|
||||
out.add(stage);
|
||||
} catch (NoSuchStageException e) {
|
||||
Logger.warn(LOG_TAG, "Unable to find stage with name " + name);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance and loop around the stages of a sync.
|
||||
* @param current
|
||||
@ -232,7 +289,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
|
||||
Stage next = nextStage(this.currentState);
|
||||
GlobalSyncStage nextStage;
|
||||
try {
|
||||
nextStage = this.getStageByName(next);
|
||||
nextStage = this.getSyncStageByName(next);
|
||||
} catch (NoSuchStageException e) {
|
||||
this.abort(e, "No such stage " + next);
|
||||
return;
|
||||
@ -240,7 +297,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
|
||||
this.currentState = next;
|
||||
Logger.info(LOG_TAG, "Running next stage " + next + " (" + nextStage + ")...");
|
||||
try {
|
||||
nextStage.execute(this);
|
||||
nextStage.execute();
|
||||
} catch (Exception ex) {
|
||||
Logger.warn(LOG_TAG, "Caught exception " + ex + " running stage " + next);
|
||||
this.abort(ex, "Uncaught exception in stage.");
|
||||
@ -435,10 +492,9 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
|
||||
String localSyncID = this.getSyncID();
|
||||
if (!remoteSyncID.equals(localSyncID)) {
|
||||
// Sync ID has changed. Reset timestamps and fetch new keys.
|
||||
resetClient(null);
|
||||
resetAllStages();
|
||||
config.purgeCryptoKeys();
|
||||
config.syncID = remoteSyncID;
|
||||
// TODO TODO TODO
|
||||
}
|
||||
config.persistToPrefs();
|
||||
advance();
|
||||
@ -486,7 +542,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
|
||||
|
||||
@Override
|
||||
public void onWiped(long timestamp) {
|
||||
session.resetClient(null);
|
||||
session.resetAllStages();
|
||||
session.config.purgeCryptoKeys();
|
||||
session.config.persistToPrefs();
|
||||
|
||||
@ -558,9 +614,26 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
|
||||
freshStartDelegate.onFreshStartFailed(e);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Note that we do not yet implement wipeRemote: it's only necessary for
|
||||
// first sync options.
|
||||
// -- reset local stages, wipe server for each stage *except* clients
|
||||
// (stages only, not whole server!), send wipeEngine commands to each client.
|
||||
//
|
||||
// Similarly for startOver (because we don't receive that notification).
|
||||
// -- remove client data from server, reset local stages, clear keys, reset
|
||||
// backoff, clear all prefs, discard credentials.
|
||||
//
|
||||
// Change passphrase: wipe entire server, reset client to force upload, sync.
|
||||
//
|
||||
// When an engine is disabled: wipe its collections on the server, reupload
|
||||
// meta/global.
|
||||
//
|
||||
// On syncing each stage: if server has engine version 0 or old, wipe server,
|
||||
// reset client to prompt reupload.
|
||||
// If sync ID mismatch: take that syncID and reset client.
|
||||
|
||||
private void wipeServer(final CredentialsSource credentials, final WipeServerDelegate wipeDelegate) {
|
||||
SyncStorageRequest request;
|
||||
final GlobalSession self = this;
|
||||
@ -609,16 +682,62 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
|
||||
request.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset our state. Clear our sync ID, reset each engine, drop any
|
||||
* cached records.
|
||||
*/
|
||||
private void resetClient(String[] engines) {
|
||||
if (engines == null) {
|
||||
// Set `engines` to be *all* the engines.
|
||||
public void wipeAllStages() {
|
||||
Logger.info(LOG_TAG, "Wiping all stages.");
|
||||
// Includes "clients".
|
||||
this.wipeStagesByEnum(Stage.getNamedStages());
|
||||
}
|
||||
|
||||
public static void wipeStages(Collection<GlobalSyncStage> stages) {
|
||||
for (GlobalSyncStage stage : stages) {
|
||||
try {
|
||||
Logger.info(LOG_TAG, "Wiping " + stage);
|
||||
stage.wipeLocal();
|
||||
} catch (Exception e) {
|
||||
Logger.error(LOG_TAG, "Ignoring wipe failure for stage " + stage, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void wipeStagesByEnum(Collection<Stage> stages) {
|
||||
GlobalSession.wipeStages(this.getSyncStagesByEnum(stages));
|
||||
}
|
||||
|
||||
public void wipeStagesByName(Collection<String> names) {
|
||||
GlobalSession.wipeStages(this.getSyncStagesByName(names));
|
||||
}
|
||||
|
||||
public void resetAllStages() {
|
||||
Logger.info(LOG_TAG, "Resetting all stages.");
|
||||
// Includes "clients".
|
||||
this.resetStagesByEnum(Stage.getNamedStages());
|
||||
}
|
||||
|
||||
public static void resetStages(Collection<GlobalSyncStage> stages) {
|
||||
for (GlobalSyncStage stage : stages) {
|
||||
try {
|
||||
Logger.info(LOG_TAG, "Resetting " + stage);
|
||||
stage.resetLocal();
|
||||
} catch (Exception e) {
|
||||
Logger.error(LOG_TAG, "Ignoring reset failure for stage " + stage, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void resetStagesByEnum(Collection<Stage> stages) {
|
||||
GlobalSession.resetStages(this.getSyncStagesByEnum(stages));
|
||||
}
|
||||
|
||||
public void resetStagesByName(Collection<String> names) {
|
||||
for (String name : names) {
|
||||
try {
|
||||
GlobalSyncStage stage = this.getSyncStageByName(name);
|
||||
Logger.info(LOG_TAG, "Resetting " + name + "(" + stage + ")");
|
||||
stage.resetLocal();
|
||||
} catch (NoSuchStageException e) {
|
||||
Logger.warn(LOG_TAG, "Cannot reset stage " + name + ": no such stage.");
|
||||
}
|
||||
}
|
||||
// TODO: futz with config?!
|
||||
// TODO: engines?!
|
||||
}
|
||||
|
||||
/**
|
||||
@ -635,21 +754,40 @@ public class GlobalSession implements CredentialsSource, PrefsSource, HttpRespon
|
||||
* Otherwise, returns true if there is an entry for this engine in the
|
||||
* meta/global "engines" object.
|
||||
*
|
||||
* @param engineName
|
||||
* @param engineName the name to check (e.g., "bookmarks").
|
||||
* @param engineSettings
|
||||
* if non-null, verify that the server engine settings are congruent
|
||||
* with this, throwing the appropriate MetaGlobalException if not.
|
||||
* @return
|
||||
* true if the engine with the provided name is present in the
|
||||
* meta/global "engines" object.
|
||||
* meta/global "engines" object, and verification passed.
|
||||
*
|
||||
* @throws MetaGlobalException
|
||||
*/
|
||||
public boolean engineIsEnabled(String engineName) throws MetaGlobalException {
|
||||
public boolean engineIsEnabled(String engineName, EngineSettings engineSettings) throws MetaGlobalException {
|
||||
if (this.config.metaGlobal == null) {
|
||||
throw new MetaGlobalNotSetException();
|
||||
}
|
||||
if (this.config.metaGlobal.engines == null) {
|
||||
throw new MetaGlobalMissingEnginesException();
|
||||
}
|
||||
return this.config.metaGlobal.engines.get(engineName) != null;
|
||||
ExtendedJSONObject engineEntry;
|
||||
try {
|
||||
engineEntry = this.config.metaGlobal.engines.getObject(engineName);
|
||||
} catch (NonObjectJSONException e) {
|
||||
Logger.error(LOG_TAG, "Engine field for " + engineName + " in meta/global is not an object.");
|
||||
throw new MetaGlobalMissingEnginesException();
|
||||
}
|
||||
|
||||
if (engineEntry == null) {
|
||||
Logger.debug(LOG_TAG, "Engine " + engineName + " not enabled: no meta/global entry.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (engineSettings != null) {
|
||||
MetaGlobal.verifyEngineSettings(engineEntry, engineSettings);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ClientsDataDelegate getClientsDelegate() {
|
||||
|
@ -8,6 +8,10 @@ import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.mozilla.gecko.sync.MetaGlobalException.MetaGlobalMalformedSyncIDException;
|
||||
import org.mozilla.gecko.sync.MetaGlobalException.MetaGlobalMalformedVersionException;
|
||||
import org.mozilla.gecko.sync.MetaGlobalException.MetaGlobalStaleClientSyncIDException;
|
||||
import org.mozilla.gecko.sync.MetaGlobalException.MetaGlobalStaleClientVersionException;
|
||||
import org.mozilla.gecko.sync.delegates.MetaGlobalDelegate;
|
||||
import org.mozilla.gecko.sync.net.SyncStorageRecordRequest;
|
||||
import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
|
||||
@ -105,6 +109,55 @@ public class MetaGlobal implements SyncStorageRequestDelegate {
|
||||
this.engines = engines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the server settings and local settings match.
|
||||
* Throws a specific exception if that's not the case.
|
||||
*/
|
||||
public static void verifyEngineSettings(ExtendedJSONObject engineEntry,
|
||||
EngineSettings engineSettings)
|
||||
throws MetaGlobalMalformedVersionException, MetaGlobalMalformedSyncIDException, MetaGlobalStaleClientVersionException, MetaGlobalStaleClientSyncIDException {
|
||||
|
||||
if (engineEntry == null) {
|
||||
throw new IllegalArgumentException("engineEntry cannot be null.");
|
||||
}
|
||||
if (engineSettings == null) {
|
||||
throw new IllegalArgumentException("engineSettings cannot be null.");
|
||||
}
|
||||
try {
|
||||
Integer version = engineEntry.getIntegerSafely("version");
|
||||
if (version == null ||
|
||||
version.intValue() == 0) {
|
||||
// Invalid version. Wipe the server.
|
||||
throw new MetaGlobalException.MetaGlobalMalformedVersionException();
|
||||
}
|
||||
if (version > engineSettings.version) {
|
||||
// We're out of date.
|
||||
throw new MetaGlobalException.MetaGlobalStaleClientVersionException(version);
|
||||
}
|
||||
try {
|
||||
String syncID = engineEntry.getString("syncID");
|
||||
if (syncID == null) {
|
||||
// No syncID. This should never happen. Wipe the server.
|
||||
throw new MetaGlobalException.MetaGlobalMalformedSyncIDException();
|
||||
}
|
||||
if (!syncID.equals(engineSettings.syncID)) {
|
||||
// Our syncID is wrong. Reset client and take the server syncID.
|
||||
throw new MetaGlobalException.MetaGlobalStaleClientSyncIDException(syncID);
|
||||
}
|
||||
// Great!
|
||||
return;
|
||||
|
||||
} catch (ClassCastException e) {
|
||||
// Malformed syncID on the server. Wipe the server.
|
||||
throw new MetaGlobalException.MetaGlobalMalformedSyncIDException();
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// Invalid version. Wipe the server.
|
||||
throw new MetaGlobalException.MetaGlobalMalformedVersionException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getSyncID() {
|
||||
return syncID;
|
||||
}
|
||||
|
@ -1,42 +1,37 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Android Sync Client.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Richard Newman <rnewman@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
/* 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;
|
||||
|
||||
public class MetaGlobalException extends SyncException {
|
||||
private static final long serialVersionUID = -6182315615113508925L;
|
||||
|
||||
public static class MetaGlobalMalformedSyncIDException extends MetaGlobalException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
public static class MetaGlobalMalformedVersionException extends MetaGlobalException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
public static class MetaGlobalOutdatedVersionException extends MetaGlobalException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
public static class MetaGlobalStaleClientVersionException extends MetaGlobalException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public final int serverVersion;
|
||||
public MetaGlobalStaleClientVersionException(final int version) {
|
||||
this.serverVersion = version;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MetaGlobalStaleClientSyncIDException extends MetaGlobalException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public final String serverSyncID;
|
||||
public MetaGlobalStaleClientSyncIDException(final String syncID) {
|
||||
this.serverSyncID = syncID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -431,6 +431,12 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo
|
||||
String parentName = getParentName(androidParentGUID);
|
||||
BookmarkRecord bookmark = AndroidBrowserBookmarksRepositorySession.bookmarkFromMirrorCursor(cur, androidParentGUID, parentName, childArray);
|
||||
|
||||
if (bookmark == null) {
|
||||
Logger.warn(LOG_TAG, "Unable to extract bookmark from cursor. Record GUID " + recordGUID +
|
||||
", parent " + androidParentGUID + "/" + androidParentID);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (needsReparenting) {
|
||||
Logger.warn(LOG_TAG, "Bookmark record " + recordGUID + " has a bad parent pointer. Reparenting now.");
|
||||
|
||||
@ -786,13 +792,13 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Clear our queued deletions.
|
||||
deletionManager.clear();
|
||||
super.run();
|
||||
} catch (Exception ex) {
|
||||
delegate.onWipeFailed(ex);
|
||||
return;
|
||||
}
|
||||
// Clear our queued deletions.
|
||||
deletionManager.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -908,6 +914,8 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo
|
||||
if (typeString == null) {
|
||||
Logger.warn(LOG_TAG, "Unsupported type code " + rowType);
|
||||
return null;
|
||||
} else {
|
||||
Logger.trace(LOG_TAG, "Record " + guid + " has type " + typeString);
|
||||
}
|
||||
|
||||
rec.type = typeString;
|
||||
|
@ -0,0 +1,28 @@
|
||||
/* 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.stage;
|
||||
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
|
||||
/**
|
||||
* This is simply a stage that is not responsible for synchronizing repositories.
|
||||
*/
|
||||
public abstract class AbstractNonRepositorySyncStage implements GlobalSyncStage {
|
||||
protected final GlobalSession session;
|
||||
|
||||
public AbstractNonRepositorySyncStage(GlobalSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetLocal() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wipeLocal() {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
@ -1,44 +1,12 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Android Sync Client.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Richard Newman <rnewman@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
/* 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.stage;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
import org.mozilla.gecko.sync.Logger;
|
||||
import org.mozilla.gecko.sync.MetaGlobalException;
|
||||
import org.mozilla.gecko.sync.repositories.ConstrainedServer11Repository;
|
||||
@ -56,6 +24,10 @@ public class AndroidBrowserBookmarksServerSyncStage extends ServerSyncStage {
|
||||
private static final String BOOKMARKS_SORT = "index";
|
||||
private static final long BOOKMARKS_REQUEST_LIMIT = 5000; // Sanity limit.
|
||||
|
||||
public AndroidBrowserBookmarksServerSyncStage(GlobalSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCollection() {
|
||||
return "bookmarks";
|
||||
@ -87,7 +59,7 @@ public class AndroidBrowserBookmarksServerSyncStage extends ServerSyncStage {
|
||||
|
||||
@Override
|
||||
protected boolean isEnabled() throws MetaGlobalException {
|
||||
if (session == null || session.getContext() == null) {
|
||||
if (session.getContext() == null) {
|
||||
return false;
|
||||
}
|
||||
boolean migrated = FennecControlHelper.areBookmarksMigrated(session.getContext());
|
||||
|
@ -39,6 +39,7 @@ package org.mozilla.gecko.sync.stage;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
import org.mozilla.gecko.sync.Logger;
|
||||
import org.mozilla.gecko.sync.MetaGlobalException;
|
||||
import org.mozilla.gecko.sync.repositories.ConstrainedServer11Repository;
|
||||
@ -56,6 +57,10 @@ public class AndroidBrowserHistoryServerSyncStage extends ServerSyncStage {
|
||||
private static final String HISTORY_SORT = "index";
|
||||
private static final long HISTORY_REQUEST_LIMIT = 250;
|
||||
|
||||
public AndroidBrowserHistoryServerSyncStage(GlobalSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCollection() {
|
||||
return "history";
|
||||
|
@ -1,46 +1,18 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Android Sync Client.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Richard Newman <rnewman@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
/* 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.stage;
|
||||
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
|
||||
public class CheckPreconditionsStage implements GlobalSyncStage {
|
||||
public void execute(GlobalSession session) throws NoSuchStageException {
|
||||
public class CheckPreconditionsStage extends AbstractNonRepositorySyncStage {
|
||||
public CheckPreconditionsStage(GlobalSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws NoSuchStageException {
|
||||
session.advance();
|
||||
}
|
||||
}
|
||||
|
@ -1,51 +1,22 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Android Sync Client.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Richard Newman <rnewman@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
/* 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.stage;
|
||||
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
|
||||
public class CompletedStage implements GlobalSyncStage {
|
||||
|
||||
public class CompletedStage extends AbstractNonRepositorySyncStage {
|
||||
|
||||
public CompletedStage(GlobalSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(GlobalSession session) throws NoSuchStageException {
|
||||
public void execute() throws NoSuchStageException {
|
||||
// TODO: Update tracking timestamps, close connections, etc.
|
||||
// TODO: call clean() on each Repository in the sync constellation.
|
||||
session.completeSync();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,11 @@ import ch.boye.httpclientandroidlib.HttpEntity;
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
|
||||
|
||||
public class EnsureClusterURLStage implements GlobalSyncStage {
|
||||
public class EnsureClusterURLStage extends AbstractNonRepositorySyncStage {
|
||||
public EnsureClusterURLStage(GlobalSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
public interface ClusterURLFetchDelegate {
|
||||
/**
|
||||
* 200 - Success.
|
||||
@ -172,7 +176,7 @@ public class EnsureClusterURLStage implements GlobalSyncStage {
|
||||
resource.get();
|
||||
}
|
||||
|
||||
public void execute(final GlobalSession session) throws NoSuchStageException {
|
||||
public void execute() throws NoSuchStageException {
|
||||
final URI oldClusterURL = session.config.getClusterURL();
|
||||
final boolean wantNodeAssignment = session.callback.wantNodeAssignment();
|
||||
|
||||
|
@ -23,16 +23,20 @@ import org.mozilla.gecko.sync.net.SyncStorageRecordRequest;
|
||||
import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
|
||||
import org.mozilla.gecko.sync.net.SyncStorageResponse;
|
||||
|
||||
public class EnsureCrypto5KeysStage implements GlobalSyncStage, SyncStorageRequestDelegate, KeyUploadDelegate {
|
||||
public class EnsureCrypto5KeysStage
|
||||
extends AbstractNonRepositorySyncStage
|
||||
implements SyncStorageRequestDelegate, KeyUploadDelegate {
|
||||
|
||||
public EnsureCrypto5KeysStage(GlobalSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
private static final String LOG_TAG = "EnsureC5KeysStage";
|
||||
private static final String CRYPTO_COLLECTION = "crypto";
|
||||
protected GlobalSession session;
|
||||
protected boolean retrying = false;
|
||||
|
||||
@Override
|
||||
public void execute(GlobalSession session) throws NoSuchStageException {
|
||||
this.session = session;
|
||||
|
||||
public void execute() throws NoSuchStageException {
|
||||
InfoCollections infoCollections = session.config.infoCollections;
|
||||
if (infoCollections == null) {
|
||||
session.abort(null, "No info/collections set in EnsureCrypto5KeysStage.");
|
||||
@ -161,7 +165,7 @@ public class EnsureCrypto5KeysStage implements GlobalSyncStage, SyncStorageReque
|
||||
Logger.debug(LOG_TAG, "New keys uploaded. Starting stage again to fetch them.");
|
||||
try {
|
||||
retrying = true;
|
||||
this.execute(this.session);
|
||||
this.execute();
|
||||
} catch (NoSuchStageException e) {
|
||||
session.abort(e, "No such stage.");
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package org.mozilla.gecko.sync.stage;
|
||||
|
||||
import org.mozilla.gecko.sync.repositories.domain.TabsRecord;
|
||||
import org.mozilla.gecko.sync.CryptoRecord;
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
import org.mozilla.gecko.sync.repositories.RecordFactory;
|
||||
import org.mozilla.gecko.sync.repositories.Repository;
|
||||
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
|
||||
@ -14,6 +15,10 @@ import org.mozilla.gecko.sync.repositories.domain.Record;
|
||||
public class FennecTabsServerSyncStage extends ServerSyncStage {
|
||||
private static final String COLLECTION = "tabs";
|
||||
|
||||
public FennecTabsServerSyncStage(GlobalSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
public class FennecTabsRecordFactory extends RecordFactory {
|
||||
@Override
|
||||
public Record createRecord(Record record) {
|
||||
|
@ -1,39 +1,6 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Android Sync Client.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Richard Newman <rnewman@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
/* 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.stage;
|
||||
|
||||
@ -46,15 +13,14 @@ import org.mozilla.gecko.sync.net.SyncStorageResponse;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class FetchInfoCollectionsStage implements GlobalSyncStage {
|
||||
public class FetchInfoCollectionsStage extends AbstractNonRepositorySyncStage {
|
||||
private static final String LOG_TAG = "FetchInfoCollStage";
|
||||
|
||||
public class StageInfoCollectionsDelegate implements InfoCollectionsDelegate {
|
||||
public FetchInfoCollectionsStage(GlobalSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
private GlobalSession session;
|
||||
public StageInfoCollectionsDelegate(GlobalSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
public class StageInfoCollectionsDelegate implements InfoCollectionsDelegate {
|
||||
|
||||
@Override
|
||||
public void handleSuccess(InfoCollections global) {
|
||||
@ -77,9 +43,9 @@ public class FetchInfoCollectionsStage implements GlobalSyncStage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(GlobalSession session) throws NoSuchStageException {
|
||||
public void execute() throws NoSuchStageException {
|
||||
try {
|
||||
session.fetchInfoCollections(new StageInfoCollectionsDelegate(session));
|
||||
session.fetchInfoCollections(new StageInfoCollectionsDelegate());
|
||||
} catch (URISyntaxException e) {
|
||||
session.abort(e, "Invalid URI.");
|
||||
}
|
||||
|
@ -12,7 +12,11 @@ import org.mozilla.gecko.sync.PersistedMetaGlobal;
|
||||
import org.mozilla.gecko.sync.delegates.MetaGlobalDelegate;
|
||||
import org.mozilla.gecko.sync.net.SyncStorageResponse;
|
||||
|
||||
public class FetchMetaGlobalStage implements GlobalSyncStage {
|
||||
public class FetchMetaGlobalStage extends AbstractNonRepositorySyncStage {
|
||||
public FetchMetaGlobalStage(GlobalSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
private static final String LOG_TAG = "FetchMetaGlobalStage";
|
||||
private static final String META_COLLECTION = "meta";
|
||||
|
||||
@ -51,7 +55,7 @@ public class FetchMetaGlobalStage implements GlobalSyncStage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(GlobalSession session) throws NoSuchStageException {
|
||||
public void execute() throws NoSuchStageException {
|
||||
InfoCollections infoCollections = session.config.infoCollections;
|
||||
if (infoCollections == null) {
|
||||
session.abort(null, "No info/collections set in FetchMetaGlobalStage.");
|
||||
|
@ -7,6 +7,7 @@ package org.mozilla.gecko.sync.stage;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.mozilla.gecko.sync.CryptoRecord;
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
import org.mozilla.gecko.sync.repositories.ConstrainedServer11Repository;
|
||||
import org.mozilla.gecko.sync.repositories.RecordFactory;
|
||||
import org.mozilla.gecko.sync.repositories.Repository;
|
||||
@ -21,9 +22,13 @@ public class FormHistoryServerSyncStage extends ServerSyncStage {
|
||||
private static final String FORM_HISTORY_SORT = "index";
|
||||
private static final long FORM_HISTORY_REQUEST_LIMIT = 5000; // Sanity limit.
|
||||
|
||||
public FormHistoryServerSyncStage(GlobalSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(org.mozilla.gecko.sync.GlobalSession session) throws NoSuchStageException {
|
||||
super.execute(session);
|
||||
public void execute() throws NoSuchStageException {
|
||||
super.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -4,7 +4,12 @@
|
||||
|
||||
package org.mozilla.gecko.sync.stage;
|
||||
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public interface GlobalSyncStage {
|
||||
public static enum Stage {
|
||||
@ -18,19 +23,59 @@ public interface GlobalSyncStage {
|
||||
ensureSpecialRecords,
|
||||
updateEngineTimestamps,
|
||||
*/
|
||||
syncClientsEngine,
|
||||
syncClientsEngine("clients"),
|
||||
/*
|
||||
processFirstSyncPref,
|
||||
processClientCommands,
|
||||
updateEnabledEngines,
|
||||
*/
|
||||
syncTabs,
|
||||
syncPasswords,
|
||||
syncBookmarks,
|
||||
syncHistory,
|
||||
syncFormHistory,
|
||||
completed,
|
||||
syncTabs("tabs"),
|
||||
syncPasswords("passwords"),
|
||||
syncBookmarks("bookmarks"),
|
||||
syncHistory("history"),
|
||||
syncFormHistory("forms"),
|
||||
completed;
|
||||
|
||||
// Maintain a mapping from names ("bookmarks") to Stage enumerations (syncBookmarks).
|
||||
private static final Map<String, Stage> named = new HashMap<String, Stage>();
|
||||
static {
|
||||
for (Stage s : EnumSet.allOf(Stage.class)) {
|
||||
if (s.getRepositoryName() != null) {
|
||||
named.put(s.getRepositoryName(), s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Stage byName(final String name) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
return named.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an immutable collection of Stages.
|
||||
*/
|
||||
public static Collection<Stage> getNamedStages() {
|
||||
return Collections.unmodifiableCollection(named.values());
|
||||
}
|
||||
|
||||
// Each Stage tracks its repositoryName.
|
||||
private final String repositoryName;
|
||||
public String getRepositoryName() {
|
||||
return repositoryName;
|
||||
}
|
||||
|
||||
private Stage() {
|
||||
this.repositoryName = null;
|
||||
}
|
||||
|
||||
private Stage(final String name) {
|
||||
this.repositoryName = name;
|
||||
}
|
||||
}
|
||||
|
||||
public void execute(GlobalSession session) throws NoSuchStageException;
|
||||
public void execute() throws NoSuchStageException;
|
||||
public void resetLocal();
|
||||
public void wipeLocal() throws Exception;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package org.mozilla.gecko.sync.stage;
|
||||
|
||||
import org.mozilla.gecko.sync.CryptoRecord;
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
import org.mozilla.gecko.sync.repositories.RecordFactory;
|
||||
import org.mozilla.gecko.sync.repositories.Repository;
|
||||
import org.mozilla.gecko.sync.repositories.android.PasswordsRepositorySession;
|
||||
@ -12,11 +13,15 @@ import org.mozilla.gecko.sync.repositories.domain.PasswordRecord;
|
||||
import org.mozilla.gecko.sync.repositories.domain.Record;
|
||||
|
||||
public class PasswordsServerSyncStage extends ServerSyncStage {
|
||||
public PasswordsServerSyncStage(GlobalSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCollection() {
|
||||
return "passwords";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getEngineName() {
|
||||
return "passwords";
|
||||
@ -41,5 +46,4 @@ public class PasswordsServerSyncStage extends ServerSyncStage {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package org.mozilla.gecko.sync.stage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.mozilla.gecko.sync.GlobalSession;
|
||||
@ -17,12 +18,22 @@ import org.mozilla.gecko.sync.NonObjectJSONException;
|
||||
import org.mozilla.gecko.sync.SynchronizerConfiguration;
|
||||
import org.mozilla.gecko.sync.crypto.KeyBundle;
|
||||
import org.mozilla.gecko.sync.middleware.Crypto5MiddlewareRepository;
|
||||
import org.mozilla.gecko.sync.repositories.InactiveSessionException;
|
||||
import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
|
||||
import org.mozilla.gecko.sync.repositories.RecordFactory;
|
||||
import org.mozilla.gecko.sync.repositories.Repository;
|
||||
import org.mozilla.gecko.sync.repositories.RepositorySession;
|
||||
import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
|
||||
import org.mozilla.gecko.sync.repositories.Server11Repository;
|
||||
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate;
|
||||
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate;
|
||||
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate;
|
||||
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate;
|
||||
import org.mozilla.gecko.sync.synchronizer.Synchronizer;
|
||||
import org.mozilla.gecko.sync.synchronizer.SynchronizerDelegate;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Fetch from a server collection into a local repository, encrypting
|
||||
* and decrypting along the way.
|
||||
@ -34,8 +45,16 @@ public abstract class ServerSyncStage implements
|
||||
GlobalSyncStage,
|
||||
SynchronizerDelegate {
|
||||
|
||||
protected GlobalSession session;
|
||||
protected String LOG_TAG = "ServerSyncStage";
|
||||
protected static final String LOG_TAG = "ServerSyncStage";
|
||||
|
||||
protected final GlobalSession session;
|
||||
|
||||
public ServerSyncStage(GlobalSession session) {
|
||||
if (session == null) {
|
||||
throw new IllegalArgumentException("session must not be null.");
|
||||
}
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override these in your subclasses.
|
||||
@ -44,8 +63,11 @@ public abstract class ServerSyncStage implements
|
||||
* @throws MetaGlobalException
|
||||
*/
|
||||
protected boolean isEnabled() throws MetaGlobalException {
|
||||
return session.engineIsEnabled(this.getEngineName());
|
||||
// TODO: pass EngineSettings here to check syncID and storage version.
|
||||
// Catch the subclasses of MetaGlobalException to trigger various resets and wipes.
|
||||
return session.engineIsEnabled(this.getEngineName(), null);
|
||||
}
|
||||
|
||||
protected abstract String getCollection();
|
||||
protected abstract String getEngineName();
|
||||
protected abstract Repository getLocalRepository();
|
||||
@ -77,6 +99,14 @@ public abstract class ServerSyncStage implements
|
||||
return this.getCollection() + ".";
|
||||
}
|
||||
|
||||
protected SynchronizerConfiguration getConfig() throws NonObjectJSONException, IOException, ParseException {
|
||||
return new SynchronizerConfiguration(session.config.getBranch(bundlePrefix()));
|
||||
}
|
||||
|
||||
protected void persistConfig(SynchronizerConfiguration synchronizerConfiguration) {
|
||||
synchronizerConfiguration.persist(session.config.getBranch(bundlePrefix()));
|
||||
}
|
||||
|
||||
public Synchronizer getConfiguredSynchronizer(GlobalSession session) throws NoCollectionKeysSetException, URISyntaxException, NonObjectJSONException, IOException, ParseException {
|
||||
Repository remote = wrappedServerRepo();
|
||||
|
||||
@ -84,7 +114,7 @@ public abstract class ServerSyncStage implements
|
||||
synchronizer.repositoryA = remote;
|
||||
synchronizer.repositoryB = this.getLocalRepository();
|
||||
|
||||
SynchronizerConfiguration config = new SynchronizerConfiguration(session.config.getBranch(bundlePrefix()));
|
||||
SynchronizerConfiguration config = this.getConfig();
|
||||
synchronizer.load(config);
|
||||
|
||||
// TODO: should wipe in either direction?
|
||||
@ -93,11 +123,178 @@ public abstract class ServerSyncStage implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(GlobalSession session) throws NoSuchStageException {
|
||||
public void resetLocal() {
|
||||
// Clear both timestamps.
|
||||
SynchronizerConfiguration config;
|
||||
try {
|
||||
config = this.getConfig();
|
||||
} catch (Exception e) {
|
||||
Logger.warn(LOG_TAG, "Unable to reset " + this + ": fetching config failed.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
config.localBundle.setTimestamp(0L);
|
||||
config.remoteBundle.setTimestamp(0L);
|
||||
Logger.info(LOG_TAG, "Reset timestamps for " + this);
|
||||
persistConfig(config);
|
||||
}
|
||||
|
||||
// Not thread-safe. Use with caution.
|
||||
private class WipeWaiter {
|
||||
public boolean sessionSucceeded = true;
|
||||
public boolean wipeSucceeded = true;
|
||||
public Exception error;
|
||||
|
||||
public void notify(Exception e, boolean sessionSucceeded) {
|
||||
this.sessionSucceeded = sessionSucceeded;
|
||||
this.wipeSucceeded = false;
|
||||
this.error = e;
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously wipe this stage by instantiating a local repository session
|
||||
* and wiping that.
|
||||
*
|
||||
* Logs and rethrows an exception on failure.
|
||||
*/
|
||||
@Override
|
||||
public void wipeLocal() throws Exception {
|
||||
// Reset, then clear data.
|
||||
this.resetLocal();
|
||||
|
||||
final WipeWaiter monitor = new WipeWaiter();
|
||||
final Context context = session.getContext();
|
||||
final Repository r = this.getLocalRepository();
|
||||
|
||||
final Runnable doWipe = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
r.createSession(new RepositorySessionCreationDelegate() {
|
||||
|
||||
@Override
|
||||
public void onSessionCreated(final RepositorySession session) {
|
||||
try {
|
||||
session.begin(new RepositorySessionBeginDelegate() {
|
||||
|
||||
@Override
|
||||
public void onBeginSucceeded(final RepositorySession session) {
|
||||
session.wipe(new RepositorySessionWipeDelegate() {
|
||||
@Override
|
||||
public void onWipeSucceeded() {
|
||||
try {
|
||||
session.finish(new RepositorySessionFinishDelegate() {
|
||||
|
||||
@Override
|
||||
public void onFinishSucceeded(RepositorySession session,
|
||||
RepositorySessionBundle bundle) {
|
||||
// Hurrah.
|
||||
synchronized (monitor) {
|
||||
monitor.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishFailed(Exception ex) {
|
||||
// Assume that no finish => no wipe.
|
||||
synchronized (monitor) {
|
||||
monitor.notify(ex, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositorySessionFinishDelegate deferredFinishDelegate(ExecutorService executor) {
|
||||
return this;
|
||||
}
|
||||
});
|
||||
} catch (InactiveSessionException e) {
|
||||
// Cannot happen. Call for safety.
|
||||
synchronized (monitor) {
|
||||
monitor.notify(e, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWipeFailed(Exception ex) {
|
||||
session.abort();
|
||||
synchronized (monitor) {
|
||||
monitor.notify(ex, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositorySessionWipeDelegate deferredWipeDelegate(ExecutorService executor) {
|
||||
return this;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeginFailed(Exception ex) {
|
||||
session.abort();
|
||||
synchronized (monitor) {
|
||||
monitor.notify(ex, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositorySessionBeginDelegate deferredBeginDelegate(ExecutorService executor) {
|
||||
return this;
|
||||
}
|
||||
});
|
||||
} catch (InvalidSessionTransitionException e) {
|
||||
session.abort();
|
||||
synchronized (monitor) {
|
||||
monitor.notify(e, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionCreateFailed(Exception ex) {
|
||||
synchronized (monitor) {
|
||||
monitor.notify(ex, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositorySessionCreationDelegate deferredCreationDelegate() {
|
||||
return this;
|
||||
}
|
||||
}, context);
|
||||
}
|
||||
};
|
||||
|
||||
final Thread wiping = new Thread(doWipe);
|
||||
synchronized (monitor) {
|
||||
wiping.start();
|
||||
try {
|
||||
monitor.wait();
|
||||
} catch (InterruptedException e) {
|
||||
Logger.error(LOG_TAG, "Wipe interrupted.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!monitor.sessionSucceeded) {
|
||||
Logger.error(LOG_TAG, "Failed to create session for wipe.");
|
||||
throw monitor.error;
|
||||
}
|
||||
|
||||
if (!monitor.wipeSucceeded) {
|
||||
Logger.error(LOG_TAG, "Failed to wipe session.");
|
||||
throw monitor.error;
|
||||
}
|
||||
|
||||
Logger.info(LOG_TAG, "Wiping stage complete.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws NoSuchStageException {
|
||||
final String name = getEngineName();
|
||||
Logger.debug(LOG_TAG, "Starting execute for " + name);
|
||||
|
||||
this.session = session;
|
||||
try {
|
||||
if (!this.isEnabled()) {
|
||||
Logger.info(LOG_TAG, "Stage " + name + " disabled; skipping.");
|
||||
@ -141,7 +338,7 @@ public abstract class ServerSyncStage implements
|
||||
|
||||
SynchronizerConfiguration synchronizerConfiguration = synchronizer.save();
|
||||
if (synchronizerConfiguration != null) {
|
||||
synchronizerConfiguration.persist(session.config.getBranch(bundlePrefix()));
|
||||
persistConfig(synchronizerConfiguration);
|
||||
} else {
|
||||
Logger.warn(LOG_TAG, "Didn't get configuration from synchronizer after success");
|
||||
}
|
||||
|
@ -41,16 +41,44 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
||||
public static final int CLIENTS_TTL_REFRESH = 604800000; // 7 days
|
||||
public static final int MAX_UPLOAD_FAILURE_COUNT = 5;
|
||||
|
||||
protected GlobalSession session;
|
||||
protected final GlobalSession session;
|
||||
protected final ClientRecordFactory factory = new ClientRecordFactory();
|
||||
protected ClientUploadDelegate clientUploadDelegate;
|
||||
protected ClientDownloadDelegate clientDownloadDelegate;
|
||||
|
||||
// Be sure to use this safely via getClientsDatabaseAccessor/closeDataAccessor.
|
||||
protected ClientsDatabaseAccessor db;
|
||||
|
||||
protected volatile boolean shouldWipe;
|
||||
protected volatile boolean commandsProcessedShouldUpload;
|
||||
protected final AtomicInteger uploadAttemptsCount = new AtomicInteger();
|
||||
|
||||
public SyncClientsEngineStage(GlobalSession session) {
|
||||
if (session == null) {
|
||||
throw new IllegalArgumentException("session must not be null.");
|
||||
}
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
protected int getClientsCount() {
|
||||
return getClientsDatabaseAccessor().clientsCount();
|
||||
}
|
||||
|
||||
protected synchronized ClientsDatabaseAccessor getClientsDatabaseAccessor() {
|
||||
if (db == null) {
|
||||
db = new ClientsDatabaseAccessor(session.getContext());
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
protected synchronized void closeDataAccessor() {
|
||||
if (db == null) {
|
||||
return;
|
||||
}
|
||||
db.close();
|
||||
db = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The following two delegates, ClientDownloadDelegate and ClientUploadDelegate
|
||||
* are both triggered in a chain, starting when execute() calls
|
||||
@ -93,11 +121,11 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
||||
|
||||
final int clientsCount;
|
||||
try {
|
||||
clientsCount = db.clientsCount();
|
||||
clientsCount = getClientsCount();
|
||||
} finally {
|
||||
// Close the database to clear cached readableDatabase/writableDatabase
|
||||
// after we've completed our last transaction (db.store()).
|
||||
db.close();
|
||||
closeDataAccessor();
|
||||
}
|
||||
|
||||
Logger.debug(LOG_TAG, "Database contains " + clientsCount + " clients.");
|
||||
@ -119,7 +147,7 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
||||
session.abort(new HTTPFailureException(response), "Client download failed.");
|
||||
} finally {
|
||||
// Close the database upon failure.
|
||||
db.close();
|
||||
closeDataAccessor();
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +159,7 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
||||
session.abort(ex, "Failure fetching client record.");
|
||||
} finally {
|
||||
// Close the database upon error.
|
||||
db.close();
|
||||
closeDataAccessor();
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,10 +278,7 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(GlobalSession session) throws NoSuchStageException {
|
||||
this.session = session;
|
||||
init();
|
||||
|
||||
public void execute() throws NoSuchStageException {
|
||||
if (shouldDownload()) {
|
||||
downloadClientRecords(); // Will kick off upload, too…
|
||||
} else {
|
||||
@ -261,6 +286,24 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetLocal() {
|
||||
// Clear timestamps and local data.
|
||||
session.config.persistServerClientRecordTimestamp(0L);
|
||||
session.getClientsDelegate().setClientsCount(0);
|
||||
try {
|
||||
getClientsDatabaseAccessor().wipe();
|
||||
} finally {
|
||||
closeDataAccessor();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wipeLocal() throws Exception {
|
||||
// Nothing more to do.
|
||||
this.resetLocal();
|
||||
}
|
||||
|
||||
protected ClientRecord newLocalClientRecord(ClientsDataDelegate delegate) {
|
||||
final String ourGUID = delegate.getAccountGUID();
|
||||
final String ourName = delegate.getClientName();
|
||||
@ -270,10 +313,6 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
||||
return r;
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
db = new ClientsDatabaseAccessor(session.getContext());
|
||||
}
|
||||
|
||||
// TODO: Bug 726055 - More considered handling of when to sync.
|
||||
protected boolean shouldDownload() {
|
||||
// Ask info/collections whether a download is needed.
|
||||
@ -309,7 +348,7 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
||||
|
||||
// TODO: Bug 715792 - Process commands here.
|
||||
for (int i = 0; i < commands.size(); i++) {
|
||||
processor.processCommand(new ExtendedJSONObject((JSONObject)commands.get(i)));
|
||||
processor.processCommand(new ExtendedJSONObject((JSONObject) commands.get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,6 +409,7 @@ public class SyncClientsEngineStage implements GlobalSyncStage {
|
||||
}
|
||||
|
||||
protected void wipeAndStore(ClientRecord record) {
|
||||
ClientsDatabaseAccessor db = getClientsDatabaseAccessor();
|
||||
if (shouldWipe) {
|
||||
db.wipe();
|
||||
shouldWipe = false;
|
||||
|
@ -3068,7 +3068,7 @@ var FormAssistant = {
|
||||
|
||||
// We only want to show autocomplete suggestions for certain elements
|
||||
_isAutoComplete: function _isAutoComplete(aElement) {
|
||||
if (!(aElement instanceof HTMLInputElement) ||
|
||||
if (!(aElement instanceof HTMLInputElement) || aElement.readOnly ||
|
||||
(aElement.getAttribute("type") == "password") ||
|
||||
(aElement.hasAttribute("autocomplete") &&
|
||||
aElement.getAttribute("autocomplete").toLowerCase() == "off"))
|
||||
|
File diff suppressed because one or more lines are too long
@ -95,6 +95,9 @@ MOZ_INTERNAL_SIGNING_FORMAT =
|
||||
endif
|
||||
SDK_SUFFIX = $(PKG_SUFFIX)
|
||||
SDK = $(SDK_PATH)$(PKG_BASENAME).sdk$(SDK_SUFFIX)
|
||||
ifdef UNIVERSAL_BINARY
|
||||
SDK = $(SDK_PATH)$(PKG_BASENAME)-$(TARGET_CPU).sdk$(SDK_SUFFIX)
|
||||
endif
|
||||
|
||||
# JavaScript Shell packaging
|
||||
ifndef LIBXUL_SDK
|
||||
|
@ -139,6 +139,12 @@
|
||||
onset="this.setAttribute('timeout', val); return val;"
|
||||
onget="return parseInt(this.getAttribute('timeout')) || 0;"/>
|
||||
|
||||
<method name="onSearchBegin">
|
||||
<body><![CDATA[
|
||||
this._fireEvent("searchbegin");
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!-- =================== nsIDOMXULMenuListElement =================== -->
|
||||
|
||||
<property name="editable" readonly="true"
|
||||
@ -660,6 +666,9 @@
|
||||
this.mFailureItems = 0;
|
||||
this.mDefaultMatchFilled = false; // clear out our prefill state.
|
||||
|
||||
// Notify the input that the search is beginning.
|
||||
this.onSearchBegin();
|
||||
|
||||
// tell each session to start searching...
|
||||
for (var name in this.mSessions)
|
||||
try {
|
||||
|
Loading…
Reference in New Issue
Block a user