зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland
This commit is contained in:
Коммит
bb29fe249d
|
@ -18,6 +18,7 @@
|
|||
#include "nsTextEquivUtils.h"
|
||||
#include "DocAccessibleChild.h"
|
||||
#include "EventTree.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "Relation.h"
|
||||
#include "Role.h"
|
||||
#include "RootAccessible.h"
|
||||
|
@ -844,6 +845,13 @@ Accessible::HandleAccEvent(AccEvent* aEvent)
|
|||
{
|
||||
NS_ENSURE_ARG_POINTER(aEvent);
|
||||
|
||||
if (profiler_is_active()) {
|
||||
nsAutoCString strEventType;
|
||||
GetAccService()->GetStringEventType(aEvent->GetEventType(), strEventType);
|
||||
|
||||
profiler_tracing("A11y Event", strEventType.get());
|
||||
}
|
||||
|
||||
if (IPCAccessibilityActive() && Document()) {
|
||||
DocAccessibleChild* ipcDoc = mDoc->IPCDoc();
|
||||
MOZ_ASSERT(ipcDoc);
|
||||
|
|
|
@ -50,6 +50,10 @@ this.ControlCenter = {
|
|||
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
await openIdentityPopup();
|
||||
},
|
||||
|
||||
async verifyConfig() {
|
||||
return Promise.reject("Bug 1373563: intermittent controlCenter_localFile on Taskcluster");
|
||||
},
|
||||
},
|
||||
|
||||
http: {
|
||||
|
@ -82,7 +86,7 @@ this.ControlCenter = {
|
|||
|
||||
singlePermission: {
|
||||
async applyConfig() {
|
||||
let uri = Services.io.newURI(PERMISSIONS_PAGE)
|
||||
let uri = Services.io.newURI(PERMISSIONS_PAGE);
|
||||
SitePermissions.set(uri, "camera", SitePermissions.ALLOW);
|
||||
|
||||
await loadPage(PERMISSIONS_PAGE);
|
||||
|
@ -95,7 +99,7 @@ this.ControlCenter = {
|
|||
// TODO: (Bug 1330601) Rewrite this to consider temporary (TAB) permission states.
|
||||
// There are 2 possible non-default permission states, so we alternate between them.
|
||||
let states = [SitePermissions.ALLOW, SitePermissions.BLOCK];
|
||||
let uri = Services.io.newURI(PERMISSIONS_PAGE)
|
||||
let uri = Services.io.newURI(PERMISSIONS_PAGE);
|
||||
SitePermissions.listPermissions().forEach(function(permission, index) {
|
||||
SitePermissions.set(uri, permission, states[index % 2]);
|
||||
});
|
||||
|
|
|
@ -24,7 +24,7 @@ function assertEditorBreakpoint(dbg, line) {
|
|||
}
|
||||
|
||||
add_task(function*() {
|
||||
requestLongerTimeout(2);
|
||||
requestLongerTimeout(3);
|
||||
|
||||
const dbg = yield initDebugger("doc-scripts.html");
|
||||
const { selectors: { getBreakpoints, getBreakpoint }, getState } = dbg;
|
||||
|
|
|
@ -927,11 +927,8 @@ GK_ATOM(onmozkeydownonplugin, "onmozkeydownonplugin")
|
|||
GK_ATOM(onmozkeyuponplugin, "onmozkeyuponplugin")
|
||||
GK_ATOM(onmozpointerlockchange, "onmozpointerlockchange")
|
||||
GK_ATOM(onmozpointerlockerror, "onmozpointerlockerror")
|
||||
GK_ATOM(onmoztimechange, "onmoztimechange")
|
||||
GK_ATOM(onMozMousePixelScroll, "onMozMousePixelScroll")
|
||||
GK_ATOM(onMozScrolledAreaChanged, "onMozScrolledAreaChanged")
|
||||
GK_ATOM(onmoznetworkupload, "onmoznetworkupload")
|
||||
GK_ATOM(onmoznetworkdownload, "onmoznetworkdownload")
|
||||
GK_ATOM(onmapfolderlistingreq, "onmapfolderlistingreq")
|
||||
GK_ATOM(onmapmessageslistingreq, "onmapmessageslistingreq")
|
||||
GK_ATOM(onmapgetmessagereq, "onmapgetmessagereq")
|
||||
|
|
|
@ -257,10 +257,6 @@
|
|||
#include "mozilla/dom/SpeechSynthesis.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
#include "nsPISocketTransportService.h"
|
||||
#endif
|
||||
|
||||
// Apple system headers seem to have a check() macro. <sigh>
|
||||
#ifdef check
|
||||
class nsIScriptTimeoutHandler;
|
||||
|
@ -1622,10 +1618,6 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
|
|||
mIdleRequestExecutor(nullptr),
|
||||
#ifdef DEBUG
|
||||
mSetOpenerWindowCalled(false),
|
||||
#endif
|
||||
#ifdef MOZ_B2G
|
||||
mNetworkUploadObserverEnabled(false),
|
||||
mNetworkDownloadObserverEnabled(false),
|
||||
#endif
|
||||
mCleanedUp(false),
|
||||
mDialogAbuseCount(0),
|
||||
|
@ -1971,11 +1963,6 @@ nsGlobalWindow::CleanUp()
|
|||
sns->Unregister(mObserver);
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
DisableNetworkEvent(eNetworkUpload);
|
||||
DisableNetworkEvent(eNetworkDownload);
|
||||
#endif // MOZ_B2G
|
||||
|
||||
if (mIdleService) {
|
||||
mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
|
||||
}
|
||||
|
@ -2047,9 +2034,6 @@ nsGlobalWindow::CleanUp()
|
|||
DisableVRUpdates();
|
||||
mHasVREvents = false;
|
||||
mHasVRDisplayActivateEvents = false;
|
||||
#ifdef MOZ_B2G
|
||||
DisableTimeChangeNotifications();
|
||||
#endif
|
||||
DisableIdleCallbackRequests();
|
||||
} else {
|
||||
MOZ_ASSERT(!mHasGamepad);
|
||||
|
@ -12168,27 +12152,6 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
if (!nsCRT::strcmp(aTopic, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC) ||
|
||||
!nsCRT::strcmp(aTopic, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC)) {
|
||||
MOZ_ASSERT(IsInnerWindow());
|
||||
if (!AsInner()->IsCurrentInnerWindow()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
|
||||
event->InitEvent(
|
||||
!nsCRT::strcmp(aTopic, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC)
|
||||
? NETWORK_UPLOAD_EVENT_NAME
|
||||
: NETWORK_DOWNLOAD_EVENT_NAME,
|
||||
false, false);
|
||||
event->SetTrusted(true);
|
||||
|
||||
bool dummy;
|
||||
return DispatchEvent(event, &dummy);
|
||||
}
|
||||
#endif // MOZ_B2G
|
||||
|
||||
if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
||||
MOZ_ASSERT(!NS_strcmp(aData, u"intl.accept_languages"));
|
||||
MOZ_ASSERT(IsInnerWindow());
|
||||
|
@ -14667,80 +14630,6 @@ nsGlobalWindow::ReportLargeAllocStatus()
|
|||
message);
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
void
|
||||
nsGlobalWindow::EnableNetworkEvent(EventMessage aEventMessage)
|
||||
{
|
||||
MOZ_ASSERT(IsInnerWindow());
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permMgr =
|
||||
services::GetPermissionManager();
|
||||
if (!permMgr) {
|
||||
NS_ERROR("No PermissionManager available!");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t permission = nsIPermissionManager::DENY_ACTION;
|
||||
permMgr->TestExactPermissionFromPrincipal(GetPrincipal(), "network-events",
|
||||
&permission);
|
||||
|
||||
if (permission != nsIPermissionManager::ALLOW_ACTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (!os) {
|
||||
NS_ERROR("ObserverService should be available!");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aEventMessage) {
|
||||
case eNetworkUpload:
|
||||
if (!mNetworkUploadObserverEnabled) {
|
||||
mNetworkUploadObserverEnabled = true;
|
||||
os->AddObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC, false);
|
||||
}
|
||||
break;
|
||||
case eNetworkDownload:
|
||||
if (!mNetworkDownloadObserverEnabled) {
|
||||
mNetworkDownloadObserverEnabled = true;
|
||||
os->AddObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC, false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::DisableNetworkEvent(EventMessage aEventMessage)
|
||||
{
|
||||
MOZ_ASSERT(IsInnerWindow());
|
||||
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (!os) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aEventMessage) {
|
||||
case eNetworkUpload:
|
||||
if (mNetworkUploadObserverEnabled) {
|
||||
mNetworkUploadObserverEnabled = false;
|
||||
os->RemoveObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC);
|
||||
}
|
||||
break;
|
||||
case eNetworkDownload:
|
||||
if (mNetworkDownloadObserverEnabled) {
|
||||
mNetworkDownloadObserverEnabled = false;
|
||||
os->RemoveObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // MOZ_B2G
|
||||
|
||||
void
|
||||
nsGlobalWindow::RedefineProperty(JSContext* aCx, const char* aPropName,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
|
|
|
@ -653,13 +653,6 @@ public:
|
|||
virtual void EnableTimeChangeNotifications() override;
|
||||
virtual void DisableTimeChangeNotifications() override;
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
// Inner windows only.
|
||||
virtual void EnableNetworkEvent(mozilla::EventMessage aEventMessage) override;
|
||||
virtual void DisableNetworkEvent(
|
||||
mozilla::EventMessage aEventMessage) override;
|
||||
#endif // MOZ_B2G
|
||||
|
||||
virtual nsresult SetArguments(nsIArray* aArguments) override;
|
||||
|
||||
void MaybeForgiveSpamCount();
|
||||
|
@ -1977,11 +1970,6 @@ protected:
|
|||
nsCOMPtr<nsIURI> mLastOpenedURI;
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
bool mNetworkUploadObserverEnabled;
|
||||
bool mNetworkDownloadObserverEnabled;
|
||||
#endif // MOZ_B2G
|
||||
|
||||
bool mCleanedUp;
|
||||
|
||||
nsCOMPtr<nsIDOMOfflineResourceList> mApplicationCache;
|
||||
|
|
|
@ -1687,21 +1687,21 @@ nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
|
|||
u"CC(T+%.1f)[%s-%i] max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n"
|
||||
u"ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu";
|
||||
nsString msg;
|
||||
msg.Adopt(nsTextFormatter::smprintf(kFmt, double(delta) / PR_USEC_PER_SEC,
|
||||
ProcessNameForCollectorLog(), getpid(),
|
||||
gCCStats.mMaxSliceTime, gCCStats.mTotalSliceTime,
|
||||
aResults.mNumSlices, gCCStats.mSuspected,
|
||||
aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
|
||||
aResults.mFreedRefCounted, aResults.mFreedGCed,
|
||||
sCCollectedWaitingForGC, sCCollectedZonesWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
|
||||
gcMsg.get(),
|
||||
sForgetSkippableBeforeCC,
|
||||
minForgetSkippableTime / PR_USEC_PER_MSEC,
|
||||
sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
|
||||
(sTotalForgetSkippableTime / cleanups) /
|
||||
PR_USEC_PER_MSEC,
|
||||
sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
|
||||
gCCStats.mMaxSkippableDuration, sRemovedPurples));
|
||||
nsTextFormatter::ssprintf(msg, kFmt, double(delta) / PR_USEC_PER_SEC,
|
||||
ProcessNameForCollectorLog(), getpid(),
|
||||
gCCStats.mMaxSliceTime, gCCStats.mTotalSliceTime,
|
||||
aResults.mNumSlices, gCCStats.mSuspected,
|
||||
aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
|
||||
aResults.mFreedRefCounted, aResults.mFreedGCed,
|
||||
sCCollectedWaitingForGC, sCCollectedZonesWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
|
||||
gcMsg.get(),
|
||||
sForgetSkippableBeforeCC,
|
||||
minForgetSkippableTime / PR_USEC_PER_MSEC,
|
||||
sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
|
||||
(sTotalForgetSkippableTime / cleanups) /
|
||||
PR_USEC_PER_MSEC,
|
||||
sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
|
||||
gCCStats.mMaxSkippableDuration, sRemovedPurples);
|
||||
if (sPostGCEventsToConsole) {
|
||||
nsCOMPtr<nsIConsoleService> cs =
|
||||
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
||||
|
@ -1741,27 +1741,27 @@ nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
|
|||
u"\"total\": %lu, "
|
||||
u"\"removed\": %lu } "
|
||||
u"}";
|
||||
nsString json;
|
||||
|
||||
json.Adopt(nsTextFormatter::smprintf(kJSONFmt, PR_Now(), ccNowDuration,
|
||||
gCCStats.mMaxSliceTime,
|
||||
gCCStats.mTotalSliceTime,
|
||||
gCCStats.mMaxGCDuration,
|
||||
gCCStats.mMaxSkippableDuration,
|
||||
gCCStats.mSuspected,
|
||||
aResults.mVisitedRefCounted, aResults.mVisitedGCed,
|
||||
aResults.mFreedRefCounted, aResults.mFreedGCed,
|
||||
sCCollectedWaitingForGC,
|
||||
sCCollectedZonesWaitingForGC,
|
||||
sLikelyShortLivingObjectsNeedingGC,
|
||||
aResults.mForcedGC,
|
||||
sForgetSkippableBeforeCC,
|
||||
minForgetSkippableTime / PR_USEC_PER_MSEC,
|
||||
sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
|
||||
(sTotalForgetSkippableTime / cleanups) /
|
||||
PR_USEC_PER_MSEC,
|
||||
sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
|
||||
sRemovedPurples));
|
||||
nsString json;
|
||||
nsTextFormatter::ssprintf(json, kJSONFmt, PR_Now(), ccNowDuration,
|
||||
gCCStats.mMaxSliceTime,
|
||||
gCCStats.mTotalSliceTime,
|
||||
gCCStats.mMaxGCDuration,
|
||||
gCCStats.mMaxSkippableDuration,
|
||||
gCCStats.mSuspected,
|
||||
aResults.mVisitedRefCounted, aResults.mVisitedGCed,
|
||||
aResults.mFreedRefCounted, aResults.mFreedGCed,
|
||||
sCCollectedWaitingForGC,
|
||||
sCCollectedZonesWaitingForGC,
|
||||
sLikelyShortLivingObjectsNeedingGC,
|
||||
aResults.mForcedGC,
|
||||
sForgetSkippableBeforeCC,
|
||||
minForgetSkippableTime / PR_USEC_PER_MSEC,
|
||||
sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
|
||||
(sTotalForgetSkippableTime / cleanups) /
|
||||
PR_USEC_PER_MSEC,
|
||||
sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
|
||||
sRemovedPurples);
|
||||
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
|
||||
if (observerService) {
|
||||
observerService->NotifyObservers(nullptr, "cycle-collection-statistics", json.get());
|
||||
|
@ -2253,12 +2253,12 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
|
|||
PRTime delta = GetCollectionTimeDelta();
|
||||
|
||||
if (sPostGCEventsToConsole) {
|
||||
nsString prefix, gcstats;
|
||||
nsString gcstats;
|
||||
gcstats.Adopt(aDesc.formatSummaryMessage(aCx));
|
||||
prefix.Adopt(nsTextFormatter::smprintf(u"GC(T+%.1f)[%s-%i] ",
|
||||
double(delta) / PR_USEC_PER_SEC,
|
||||
ProcessNameForCollectorLog(),
|
||||
getpid()));
|
||||
nsAutoString prefix;
|
||||
nsTextFormatter::ssprintf(prefix, u"GC(T+%.1f)[%s-%i] ",
|
||||
double(delta) / PR_USEC_PER_SEC,
|
||||
ProcessNameForCollectorLog(), getpid());
|
||||
nsString msg = prefix + gcstats;
|
||||
nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
||||
if (cs) {
|
||||
|
@ -2339,11 +2339,11 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
|
|||
}
|
||||
|
||||
if (sPostGCEventsToConsole) {
|
||||
nsString prefix, gcstats;
|
||||
nsString gcstats;
|
||||
gcstats.Adopt(aDesc.formatSliceMessage(aCx));
|
||||
prefix.Adopt(nsTextFormatter::smprintf(u"[%s-%i] ",
|
||||
ProcessNameForCollectorLog(),
|
||||
getpid()));
|
||||
nsAutoString prefix;
|
||||
nsTextFormatter::ssprintf(prefix, u"[%s-%i] ",
|
||||
ProcessNameForCollectorLog(), getpid());
|
||||
nsString msg = prefix + gcstats;
|
||||
nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
||||
if (cs) {
|
||||
|
|
|
@ -493,24 +493,6 @@ public:
|
|||
virtual void EnableTimeChangeNotifications() = 0;
|
||||
virtual void DisableTimeChangeNotifications() = 0;
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
/**
|
||||
* Tell the window that it should start to listen to the network event of the
|
||||
* given aType.
|
||||
*
|
||||
* Inner windows only.
|
||||
*/
|
||||
virtual void EnableNetworkEvent(mozilla::EventMessage aEventMessage) = 0;
|
||||
|
||||
/**
|
||||
* Tell the window that it should stop to listen to the network event of the
|
||||
* given aType.
|
||||
*
|
||||
* Inner windows only.
|
||||
*/
|
||||
virtual void DisableNetworkEvent(mozilla::EventMessage aEventMessage) = 0;
|
||||
#endif // MOZ_B2G
|
||||
|
||||
/**
|
||||
* Tell this window that there is an observer for gamepad input
|
||||
*
|
||||
|
|
|
@ -13,9 +13,6 @@
|
|||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
#ifdef MOZ_B2G
|
||||
#include "mozilla/Hal.h"
|
||||
#endif // #ifdef MOZ_B2G
|
||||
#include "mozilla/HalSensor.h"
|
||||
#include "mozilla/InternalMutationEvent.h"
|
||||
#include "mozilla/JSEventHandler.h"
|
||||
|
@ -350,14 +347,6 @@ EventListenerManager::AddEventListenerInternal(
|
|||
} else if (aTypeAtom == nsGkAtoms::onorientationchange) {
|
||||
EnableDevice(eOrientationChange);
|
||||
#endif
|
||||
#ifdef MOZ_B2G
|
||||
} else if (aTypeAtom == nsGkAtoms::onmoztimechange) {
|
||||
EnableDevice(eTimeChange);
|
||||
} else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) {
|
||||
EnableDevice(eNetworkUpload);
|
||||
} else if (aTypeAtom == nsGkAtoms::onmoznetworkdownload) {
|
||||
EnableDevice(eNetworkDownload);
|
||||
#endif // MOZ_B2G
|
||||
} else if (aTypeAtom == nsGkAtoms::ontouchstart ||
|
||||
aTypeAtom == nsGkAtoms::ontouchend ||
|
||||
aTypeAtom == nsGkAtoms::ontouchmove ||
|
||||
|
@ -492,11 +481,6 @@ EventListenerManager::IsDeviceType(EventMessage aEventMessage)
|
|||
case eUserProximity:
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
case eOrientationChange:
|
||||
#endif
|
||||
#ifdef MOZ_B2G
|
||||
case eTimeChange:
|
||||
case eNetworkUpload:
|
||||
case eNetworkDownload:
|
||||
#endif
|
||||
return true;
|
||||
default:
|
||||
|
@ -548,15 +532,6 @@ EventListenerManager::EnableDevice(EventMessage aEventMessage)
|
|||
case eOrientationChange:
|
||||
window->EnableOrientationChangeListener();
|
||||
break;
|
||||
#endif
|
||||
#ifdef MOZ_B2G
|
||||
case eTimeChange:
|
||||
window->EnableTimeChangeNotifications();
|
||||
break;
|
||||
case eNetworkUpload:
|
||||
case eNetworkDownload:
|
||||
window->EnableNetworkEvent(aEventMessage);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
NS_WARNING("Enabling an unknown device sensor.");
|
||||
|
@ -604,15 +579,6 @@ EventListenerManager::DisableDevice(EventMessage aEventMessage)
|
|||
window->DisableOrientationChangeListener();
|
||||
break;
|
||||
#endif
|
||||
#ifdef MOZ_B2G
|
||||
case eTimeChange:
|
||||
window->DisableTimeChangeNotifications();
|
||||
break;
|
||||
case eNetworkUpload:
|
||||
case eNetworkDownload:
|
||||
window->DisableNetworkEvent(aEventMessage);
|
||||
break;
|
||||
#endif // MOZ_B2G
|
||||
default:
|
||||
NS_WARNING("Disabling an unknown device sensor.");
|
||||
break;
|
||||
|
|
|
@ -642,21 +642,6 @@ WINDOW_ONLY_EVENT(appinstalled,
|
|||
eBasicEventClass)
|
||||
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
WINDOW_ONLY_EVENT(moztimechange,
|
||||
eTimeChange,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
WINDOW_ONLY_EVENT(moznetworkupload,
|
||||
eNetworkUpload,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
WINDOW_ONLY_EVENT(moznetworkdownload,
|
||||
eNetworkDownload,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
#endif // MOZ_B2G
|
||||
|
||||
TOUCH_EVENT(touchstart,
|
||||
eTouchStart,
|
||||
EventNameType_All,
|
||||
|
|
|
@ -32,10 +32,6 @@ DEFINE_KEYNAME_INTERNAL(PrintableKey, "MozPrintableKey")
|
|||
DEFINE_KEYNAME_INTERNAL(SoftLeft, "MozSoftLeft")
|
||||
DEFINE_KEYNAME_INTERNAL(SoftRight, "MozSoftRight")
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
DEFINE_KEYNAME_INTERNAL(HomeScreen, "MozHomeScreen")
|
||||
#endif // #ifdef MOZ_B2G
|
||||
|
||||
/******************************************************************************
|
||||
* Modifier Keys
|
||||
*****************************************************************************/
|
||||
|
|
|
@ -203,7 +203,7 @@ TouchEvent::PrefEnabled(nsIDocShell* aDocShell)
|
|||
enabled = false;
|
||||
} else {
|
||||
if (sPrefCacheValue == 2) {
|
||||
#if defined(MOZ_B2G) || defined(MOZ_WIDGET_ANDROID)
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
// Touch support is always enabled on B2G and android.
|
||||
enabled = true;
|
||||
#elif defined(XP_WIN) || MOZ_WIDGET_GTK == 3
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
var isWindows = (getPlatform() == "windows");
|
||||
|
||||
var apz_touch_action_prefs = [
|
||||
// Obviously we need touch-action support enabled for testing touch-action.
|
||||
["layout.css.touch_action.enabled", true],
|
||||
|
@ -30,6 +32,7 @@ var apz_touch_action_prefs = [
|
|||
// position is synced back to the main thread. So we disable displayport
|
||||
// expiry for these tests.
|
||||
["apz.displayport_expiry_ms", 0],
|
||||
["apz.test.fails_with_native_injection", isWindows],
|
||||
];
|
||||
|
||||
function apzScriptInjector(name) {
|
||||
|
|
|
@ -1025,13 +1025,6 @@ UploadLastDir::FetchDirectoryAndDisplayPicker(nsIDocument* aDoc,
|
|||
nsCOMPtr<nsIContentPrefCallback2> prefCallback =
|
||||
new UploadLastDir::ContentPrefCallback(aFilePicker, aFpCallback);
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
if (XRE_IsContentProcess()) {
|
||||
prefCallback->HandleCompletion(nsIContentPrefCallback2::COMPLETE_ERROR);
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Attempt to get the CPS, if it's not present we'll fallback to use the Desktop folder
|
||||
nsCOMPtr<nsIContentPrefService2> contentPrefService =
|
||||
do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
|
||||
|
@ -1056,12 +1049,6 @@ UploadLastDir::StoreLastUsedDirectory(nsIDocument* aDoc, nsIFile* aDir)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
if (XRE_IsContentProcess()) {
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
|
||||
NS_PRECONDITION(docURI, "docURI is null");
|
||||
|
||||
|
@ -5921,7 +5908,7 @@ HTMLInputElement::ChooseDirectory(ErrorResult& aRv)
|
|||
// "Pick Folder..." button on platforms that don't have a directory picker
|
||||
// we have to redirect to the file picker here.
|
||||
InitFilePicker(
|
||||
#if defined(ANDROID) || defined(MOZ_B2G)
|
||||
#if defined(ANDROID)
|
||||
// No native directory picker - redirect to plain file picker
|
||||
FILE_PICKER_FILE
|
||||
#else
|
||||
|
|
|
@ -802,10 +802,10 @@ nsTextInputSelectionImpl::CheckVisibilityContent(nsIContent* aNode,
|
|||
return shell->CheckVisibilityContent(aNode, aStartOffset, aEndOffset, aRetval);
|
||||
}
|
||||
|
||||
class nsTextInputListener : public nsISelectionListener,
|
||||
public nsIDOMEventListener,
|
||||
public nsIEditorObserver,
|
||||
public nsSupportsWeakReference
|
||||
class nsTextInputListener final : public nsISelectionListener,
|
||||
public nsIDOMEventListener,
|
||||
public nsIEditorObserver,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
/** the default constructor
|
||||
|
@ -820,6 +820,10 @@ public:
|
|||
void SettingValue(bool aValue) { mSettingValue = aValue; }
|
||||
void SetValueChanged(bool aSetValueChanged) { mSetValueChanged = aSetValueChanged; }
|
||||
|
||||
// aFrame is an optional pointer to our frame, if not passed the method will
|
||||
// use mFrame to compute it lazily.
|
||||
void HandleValueChanged(nsTextControlFrame* aFrame = nullptr);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_DECL_NSISELECTIONLISTENER
|
||||
|
@ -1082,18 +1086,29 @@ nsTextInputListener::EditAction()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
HandleValueChanged(frame);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsTextInputListener::HandleValueChanged(nsTextControlFrame* aFrame)
|
||||
{
|
||||
// Make sure we know we were changed (do NOT set this to false if there are
|
||||
// no undo items; JS could change the value and we'd still need to save it)
|
||||
if (mSetValueChanged) {
|
||||
frame->SetValueChanged(true);
|
||||
if (!aFrame) {
|
||||
nsITextControlFrame* frameBase = do_QueryFrame(mFrame);
|
||||
aFrame = static_cast<nsTextControlFrame*> (frameBase);
|
||||
NS_ASSERTION(aFrame, "Where is our frame?");
|
||||
}
|
||||
aFrame->SetValueChanged(true);
|
||||
}
|
||||
|
||||
if (!mSettingValue) {
|
||||
mTxtCtrlElement->OnValueChanged(/* aNotify = */ true,
|
||||
/* aWasInteractiveUserChange = */ true);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -2684,6 +2699,11 @@ nsTextEditorState::SetValue(const nsAString& aValue, const nsAString* aOldValue,
|
|||
}
|
||||
|
||||
textEditor->SetText(newValue);
|
||||
|
||||
// Call the listener's HandleValueChanged() callback manually, since
|
||||
// we don't use the transaction manager in this path and it could be
|
||||
// that the editor would bypass calling the listener for that reason.
|
||||
mTextListener->HandleValueChanged();
|
||||
}
|
||||
|
||||
mTextListener->SetValueChanged(true);
|
||||
|
|
|
@ -35,7 +35,9 @@ addLoadEvent(function () {
|
|||
SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
["full-screen-api.unprefix.enabled", true],
|
||||
["full-screen-api.allow-trusted-requests-only", false]
|
||||
["full-screen-api.allow-trusted-requests-only", false],
|
||||
// Use legacy data: URI behavior to run test.
|
||||
["security.data_uri.unique_opaque_origin", false]
|
||||
]
|
||||
}, next);
|
||||
});
|
||||
|
|
|
@ -3647,8 +3647,6 @@ UpgradeSchemaFrom18_0To19_0(mozIStorageConnection* aConnection)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
#if !defined(MOZ_B2G)
|
||||
|
||||
class NormalJSContext;
|
||||
|
||||
class UpgradeFileIdsFunction final
|
||||
|
@ -3684,8 +3682,6 @@ private:
|
|||
nsIVariant** aResult) override;
|
||||
};
|
||||
|
||||
#endif // MOZ_B2G
|
||||
|
||||
nsresult
|
||||
UpgradeSchemaFrom19_0To20_0(nsIFile* aFMDirectory,
|
||||
mozIStorageConnection* aConnection)
|
||||
|
@ -3695,20 +3691,6 @@ UpgradeSchemaFrom19_0To20_0(nsIFile* aFMDirectory,
|
|||
|
||||
AUTO_PROFILER_LABEL("UpgradeSchemaFrom19_0To20_0", STORAGE);
|
||||
|
||||
#if defined(MOZ_B2G)
|
||||
|
||||
// We don't have to do the upgrade of file ids on B2G. The old format was
|
||||
// only used by the previous single process implementation and B2G was
|
||||
// always multi process. This is a nice optimization since the upgrade needs
|
||||
// to deserialize all structured clones which reference a stored file or
|
||||
// a mutable file.
|
||||
nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(20, 0));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
#else // MOZ_B2G
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT count(*) "
|
||||
|
@ -3807,8 +3789,6 @@ UpgradeSchemaFrom19_0To20_0(nsIFile* aFMDirectory,
|
|||
return rv;
|
||||
}
|
||||
|
||||
#endif // MOZ_B2G
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -19546,8 +19526,6 @@ NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction)
|
|||
NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction)
|
||||
NS_IMPL_ISUPPORTS(StripObsoleteOriginAttributesFunction, mozIStorageFunction);
|
||||
|
||||
#if !defined(MOZ_B2G)
|
||||
|
||||
nsresult
|
||||
UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory,
|
||||
mozIStorageConnection* aConnection)
|
||||
|
@ -19639,8 +19617,6 @@ UpgradeFileIdsFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
#endif // MOZ_B2G
|
||||
|
||||
// static
|
||||
void
|
||||
DatabaseOperationBase::GetBindingClauseForKeyRange(
|
||||
|
|
|
@ -804,8 +804,6 @@ public:
|
|||
|
||||
// We don't need to upgrade database on B2G. See the comment in ActorsParent.cpp,
|
||||
// UpgradeSchemaFrom18_0To19_0()
|
||||
#if !defined(MOZ_B2G)
|
||||
|
||||
class UpgradeDeserializationHelper
|
||||
{
|
||||
public:
|
||||
|
@ -872,8 +870,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
#endif // MOZ_B2G
|
||||
|
||||
template <class Traits>
|
||||
JSObject*
|
||||
CommonStructuredCloneReadCallback(JSContext* aCx,
|
||||
|
@ -1214,8 +1210,6 @@ IDBObjectStore::DeserializeIndexValue(JSContext* aCx,
|
|||
return true;
|
||||
}
|
||||
|
||||
#if !defined(MOZ_B2G)
|
||||
|
||||
// static
|
||||
bool
|
||||
IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx,
|
||||
|
@ -1252,8 +1246,6 @@ IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx,
|
|||
return true;
|
||||
}
|
||||
|
||||
#endif // MOZ_B2G
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
void
|
||||
|
|
|
@ -101,12 +101,10 @@ public:
|
|||
StructuredCloneReadInfo& aCloneReadInfo,
|
||||
JS::MutableHandle<JS::Value> aValue);
|
||||
|
||||
#if !defined(MOZ_B2G)
|
||||
static bool
|
||||
DeserializeUpgradeValue(JSContext* aCx,
|
||||
StructuredCloneReadInfo& aCloneReadInfo,
|
||||
JS::MutableHandle<JS::Value> aValue);
|
||||
#endif
|
||||
|
||||
static const JSClass*
|
||||
DummyPropClass()
|
||||
|
|
|
@ -35,8 +35,6 @@
|
|||
#include "mozilla/dom/MemoryReportRequest.h"
|
||||
#include "mozilla/dom/ProcessGlobal.h"
|
||||
#include "mozilla/dom/PushNotifier.h"
|
||||
#include "mozilla/dom/LocalStorage.h"
|
||||
#include "mozilla/dom/StorageIPC.h"
|
||||
#include "mozilla/dom/TabGroup.h"
|
||||
#include "mozilla/dom/workers/ServiceWorkerManager.h"
|
||||
#include "mozilla/dom/nsIContentChild.h"
|
||||
|
@ -2105,21 +2103,6 @@ ContentChild::DeallocPMediaChild(media::PMediaChild *aActor)
|
|||
return media::DeallocPMediaChild(aActor);
|
||||
}
|
||||
|
||||
PStorageChild*
|
||||
ContentChild::AllocPStorageChild()
|
||||
{
|
||||
MOZ_CRASH("We should never be manually allocating PStorageChild actors");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::DeallocPStorageChild(PStorageChild* aActor)
|
||||
{
|
||||
StorageDBChild* child = static_cast<StorageDBChild*>(aActor);
|
||||
child->ReleaseIPDLReference();
|
||||
return true;
|
||||
}
|
||||
|
||||
PSpeechSynthesisChild*
|
||||
ContentChild::AllocPSpeechSynthesisChild()
|
||||
{
|
||||
|
@ -2317,7 +2300,7 @@ ContentChild::ProcessingError(Result aCode, const char* aReason)
|
|||
MOZ_CRASH("not reached");
|
||||
}
|
||||
|
||||
#if defined(MOZ_CRASHREPORTER) && !defined(MOZ_B2G)
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
nsDependentCString reason(aReason);
|
||||
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ipc_channel_error"), reason);
|
||||
#endif
|
||||
|
@ -3247,19 +3230,6 @@ ContentChild::RecvBlobURLUnregistration(const nsCString& aURI)
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentChild::RecvDispatchLocalStorageChange(const nsString& aDocumentURI,
|
||||
const nsString& aKey,
|
||||
const nsString& aOldValue,
|
||||
const nsString& aNewValue,
|
||||
const IPC::Principal& aPrincipal,
|
||||
const bool& aIsPrivate)
|
||||
{
|
||||
LocalStorage::DispatchStorageEvent(aDocumentURI, aKey, aOldValue, aNewValue,
|
||||
aPrincipal, aIsPrivate, nullptr, true);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
#if defined(XP_WIN) && defined(ACCESSIBILITY)
|
||||
bool
|
||||
ContentChild::SendGetA11yContentId()
|
||||
|
|
|
@ -49,7 +49,6 @@ namespace dom {
|
|||
|
||||
class AlertObserver;
|
||||
class ConsoleListener;
|
||||
class PStorageChild;
|
||||
class ClonedMessageData;
|
||||
class TabChild;
|
||||
class GetFilesHelperChild;
|
||||
|
@ -300,10 +299,6 @@ public:
|
|||
|
||||
virtual bool DeallocPMediaChild(PMediaChild* aActor) override;
|
||||
|
||||
virtual PStorageChild* AllocPStorageChild() override;
|
||||
|
||||
virtual bool DeallocPStorageChild(PStorageChild* aActor) override;
|
||||
|
||||
virtual PPresentationChild* AllocPPresentationChild() override;
|
||||
|
||||
virtual bool DeallocPPresentationChild(PPresentationChild* aActor) override;
|
||||
|
@ -414,14 +409,6 @@ public:
|
|||
virtual mozilla::ipc::IPCResult
|
||||
RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistations) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvDispatchLocalStorageChange(const nsString& aDocumentURI,
|
||||
const nsString& aKey,
|
||||
const nsString& aOldValue,
|
||||
const nsString& aNewValue,
|
||||
const IPC::Principal& aPrincipal,
|
||||
const bool& aIsPrivate) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvLastPrivateDocShellDestroyed() override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
|
|
|
@ -48,8 +48,6 @@
|
|||
#include "mozilla/dom/PContentPermissionRequestParent.h"
|
||||
#include "mozilla/dom/PCycleCollectWithLogsParent.h"
|
||||
#include "mozilla/dom/ServiceWorkerRegistrar.h"
|
||||
#include "mozilla/dom/Storage.h"
|
||||
#include "mozilla/dom/StorageIPC.h"
|
||||
#include "mozilla/dom/power/PowerManagerService.h"
|
||||
#include "mozilla/dom/Permissions.h"
|
||||
#include "mozilla/dom/PresentationParent.h"
|
||||
|
@ -2100,12 +2098,10 @@ ContentParent::ContentParent(ContentParent* aOpener,
|
|||
if (XRE_IsParentProcess()) {
|
||||
audio::AudioNotificationSender::Init();
|
||||
}
|
||||
#if !defined(MOZ_B2G)
|
||||
// Request Windows message deferral behavior on our side of the PContent
|
||||
// channel. Generally only applies to the situation where we get caught in
|
||||
// a deadlock with the plugin process when sending CPOWs.
|
||||
GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
@ -2934,7 +2930,7 @@ ContentParent::KillHard(const char* aReason)
|
|||
mCalledKillHard = true;
|
||||
mForceKillTimer = nullptr;
|
||||
|
||||
#if defined(MOZ_CRASHREPORTER) && !defined(MOZ_B2G)
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
// We're about to kill the child process associated with this content.
|
||||
// Something has gone wrong to get us here, so we generate a minidump
|
||||
// of the parent and child for submission to the crash server.
|
||||
|
@ -2974,7 +2970,7 @@ ContentParent::KillHard(const char* aReason)
|
|||
void
|
||||
ContentParent::OnGenerateMinidumpComplete(bool aDumpResult)
|
||||
{
|
||||
#if defined(MOZ_CRASHREPORTER) && !defined(MOZ_B2G)
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
if (mCrashReporter && aDumpResult) {
|
||||
// CrashReporterHost::GenerateMinidumpAndPair() is successful.
|
||||
mCreatedPairedMinidumps = mCrashReporter->FinalizeCrashReport();
|
||||
|
@ -3304,20 +3300,6 @@ ContentParent::DeallocPMediaParent(media::PMediaParent *aActor)
|
|||
return media::DeallocPMediaParent(aActor);
|
||||
}
|
||||
|
||||
PStorageParent*
|
||||
ContentParent::AllocPStorageParent()
|
||||
{
|
||||
return new StorageDBParent();
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::DeallocPStorageParent(PStorageParent* aActor)
|
||||
{
|
||||
StorageDBParent* child = static_cast<StorageDBParent*>(aActor);
|
||||
child->ReleaseIPDLReference();
|
||||
return true;
|
||||
}
|
||||
|
||||
PPresentationParent*
|
||||
ContentParent::AllocPPresentationParent()
|
||||
{
|
||||
|
@ -4962,25 +4944,6 @@ ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aUR
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentParent::RecvBroadcastLocalStorageChange(const nsString& aDocumentURI,
|
||||
const nsString& aKey,
|
||||
const nsString& aOldValue,
|
||||
const nsString& aNewValue,
|
||||
const Principal& aPrincipal,
|
||||
const bool& aIsPrivate)
|
||||
{
|
||||
for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
|
||||
if (cp != this) {
|
||||
Unused << cp->SendDispatchLocalStorageChange(
|
||||
nsString(aDocumentURI), nsString(aKey), nsString(aOldValue),
|
||||
nsString(aNewValue), IPC::Principal(aPrincipal), aIsPrivate);
|
||||
}
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentParent::RecvGetA11yContentId(uint32_t* aContentId)
|
||||
{
|
||||
|
|
|
@ -97,7 +97,6 @@ namespace dom {
|
|||
|
||||
class Element;
|
||||
class TabParent;
|
||||
class PStorageParent;
|
||||
class ClonedMessageData;
|
||||
class MemoryReport;
|
||||
class TabContext;
|
||||
|
@ -449,13 +448,6 @@ public:
|
|||
virtual PHeapSnapshotTempFileHelperParent*
|
||||
AllocPHeapSnapshotTempFileHelperParent() override;
|
||||
|
||||
virtual PStorageParent* AllocPStorageParent() override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvPStorageConstructor(PStorageParent* aActor) override
|
||||
{
|
||||
return PContentParent::RecvPStorageConstructor(aActor);
|
||||
}
|
||||
|
||||
virtual PJavaScriptParent*
|
||||
AllocPJavaScriptParent() override;
|
||||
|
||||
|
@ -566,14 +558,6 @@ public:
|
|||
virtual mozilla::ipc::IPCResult
|
||||
RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvBroadcastLocalStorageChange(const nsString& aDocumentURI,
|
||||
const nsString& aKey,
|
||||
const nsString& aOldValue,
|
||||
const nsString& aNewValue,
|
||||
const IPC::Principal& aPrincipal,
|
||||
const bool& aIsPrivate) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvGetA11yContentId(uint32_t* aContentId) override;
|
||||
|
||||
|
@ -931,8 +915,6 @@ private:
|
|||
|
||||
virtual bool DeallocPMediaParent(PMediaParent* aActor) override;
|
||||
|
||||
virtual bool DeallocPStorageParent(PStorageParent* aActor) override;
|
||||
|
||||
virtual PPresentationParent* AllocPPresentationParent() override;
|
||||
|
||||
virtual bool DeallocPPresentationParent(PPresentationParent* aActor) override;
|
||||
|
|
|
@ -31,7 +31,6 @@ include protocol PParentToChildStream;
|
|||
include protocol POfflineCacheUpdate;
|
||||
include protocol PRenderFrame;
|
||||
include protocol PSpeechSynthesis;
|
||||
include protocol PStorage;
|
||||
include protocol PTestShell;
|
||||
include protocol PJavaScript;
|
||||
include protocol PRemoteSpellcheckEngine;
|
||||
|
@ -291,7 +290,6 @@ nested(upto inside_cpow) sync protocol PContent
|
|||
manages PChildToParentStream;
|
||||
manages PParentToChildStream;
|
||||
manages PSpeechSynthesis;
|
||||
manages PStorage;
|
||||
manages PTestShell;
|
||||
manages PJavaScript;
|
||||
manages PRemoteSpellcheckEngine;
|
||||
|
@ -566,13 +564,6 @@ child:
|
|||
|
||||
async BlobURLUnregistration(nsCString aURI);
|
||||
|
||||
async DispatchLocalStorageChange(nsString documentURI,
|
||||
nsString key,
|
||||
nsString oldValue,
|
||||
nsString newValue,
|
||||
Principal principal,
|
||||
bool isPrivate);
|
||||
|
||||
async GMPsChanged(GMPCapabilityData[] capabilities);
|
||||
|
||||
|
||||
|
@ -713,8 +704,6 @@ parent:
|
|||
|
||||
async PSpeechSynthesis();
|
||||
|
||||
nested(inside_cpow) async PStorage();
|
||||
|
||||
async PMedia();
|
||||
|
||||
async PWebrtcGlobal();
|
||||
|
@ -1048,13 +1037,6 @@ parent:
|
|||
|
||||
async UnstoreAndBroadcastBlobURLUnregistration(nsCString url);
|
||||
|
||||
async BroadcastLocalStorageChange(nsString documentURI,
|
||||
nsString key,
|
||||
nsString oldValue,
|
||||
nsString newValue,
|
||||
Principal principal,
|
||||
bool isPrivate);
|
||||
|
||||
/**
|
||||
* Messages for communicating child Telemetry to the parent process
|
||||
*/
|
||||
|
|
|
@ -246,10 +246,6 @@ MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
|
|||
// It is meant as a temporary solution until service workers can
|
||||
// provide a TabChild equivalent. Don't allow this on b2g since
|
||||
// it might be used to escalate privileges.
|
||||
#ifdef MOZ_B2G
|
||||
mInvalidReason = "ServiceWorkerClients::OpenWindow is not supported.";
|
||||
return;
|
||||
#endif
|
||||
if (!Preferences::GetBool("dom.serviceWorkers.enabled", false)) {
|
||||
mInvalidReason = "ServiceWorkers should be enabled.";
|
||||
return;
|
||||
|
|
|
@ -126,7 +126,6 @@ LOCAL_INCLUDES += [
|
|||
'/dom/geolocation',
|
||||
'/dom/media/webspeech/synth/ipc',
|
||||
'/dom/security',
|
||||
'/dom/storage',
|
||||
'/dom/workers',
|
||||
'/extensions/cookie',
|
||||
'/extensions/spellcheck/src',
|
||||
|
|
|
@ -347,17 +347,9 @@ AudioStream::Init(uint32_t aNumChannels, uint32_t aChannelMap, uint32_t aRate,
|
|||
params.channels = mOutChannels;
|
||||
params.layout = CubebUtils::ConvertChannelMapToCubebLayout(aChannelMap);
|
||||
#if defined(__ANDROID__)
|
||||
#if defined(MOZ_B2G)
|
||||
params.stream_type = CubebUtils::ConvertChannelToCubebType(aAudioChannel);
|
||||
#else
|
||||
params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
|
||||
#endif
|
||||
|
||||
if (params.stream_type == CUBEB_STREAM_TYPE_MAX) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
#endif
|
||||
|
||||
params.format = ToCubebFormat<AUDIO_OUTPUT_FORMAT>::value;
|
||||
mAudioClock.Init(aRate);
|
||||
|
||||
|
|
|
@ -491,33 +491,6 @@ cubeb_channel_layout ConvertChannelMapToCubebLayout(uint32_t aChannelMap)
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__) && defined(MOZ_B2G)
|
||||
cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannel aChannel)
|
||||
{
|
||||
switch(aChannel) {
|
||||
case dom::AudioChannel::Normal:
|
||||
/* FALLTHROUGH */
|
||||
case dom::AudioChannel::Content:
|
||||
return CUBEB_STREAM_TYPE_MUSIC;
|
||||
case dom::AudioChannel::Notification:
|
||||
return CUBEB_STREAM_TYPE_NOTIFICATION;
|
||||
case dom::AudioChannel::Alarm:
|
||||
return CUBEB_STREAM_TYPE_ALARM;
|
||||
case dom::AudioChannel::Telephony:
|
||||
return CUBEB_STREAM_TYPE_VOICE_CALL;
|
||||
case dom::AudioChannel::Ringer:
|
||||
return CUBEB_STREAM_TYPE_RING;
|
||||
case dom::AudioChannel::System:
|
||||
return CUBEB_STREAM_TYPE_SYSTEM;
|
||||
case dom::AudioChannel::Publicnotification:
|
||||
return CUBEB_STREAM_TYPE_SYSTEM_ENFORCED;
|
||||
default:
|
||||
NS_ERROR("The value of AudioChannel is invalid");
|
||||
return CUBEB_STREAM_TYPE_MAX;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void GetCurrentBackend(nsAString& aBackend)
|
||||
{
|
||||
cubeb* cubebContext = GetCubebContext();
|
||||
|
|
|
@ -50,9 +50,6 @@ uint32_t GetCubebPlaybackLatencyInMilliseconds();
|
|||
Maybe<uint32_t> GetCubebMSGLatencyInFrames();
|
||||
bool CubebLatencyPrefSet();
|
||||
cubeb_channel_layout ConvertChannelMapToCubebLayout(uint32_t aChannelMap);
|
||||
#if defined(__ANDROID__) && defined(MOZ_B2G)
|
||||
cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannel aChannel);
|
||||
#endif
|
||||
void GetCurrentBackend(nsAString& aBackend);
|
||||
void GetPreferredChannelLayout(nsAString& aLayout);
|
||||
void GetDeviceCollection(nsTArray<RefPtr<AudioDeviceInfo>>& aDeviceInfos,
|
||||
|
|
|
@ -628,15 +628,7 @@ AudioCallbackDriver::Init()
|
|||
mSampleRate = output.rate = CubebUtils::PreferredSampleRate();
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#if defined(MOZ_B2G)
|
||||
output.stream_type = CubebUtils::ConvertChannelToCubebType(mAudioChannel);
|
||||
#else
|
||||
output.stream_type = CUBEB_STREAM_TYPE_MUSIC;
|
||||
#endif
|
||||
if (output.stream_type == CUBEB_STREAM_TYPE_MAX) {
|
||||
NS_WARNING("Bad stream type");
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
(void)mAudioChannel;
|
||||
#endif
|
||||
|
|
|
@ -69,10 +69,6 @@
|
|||
#include "browser_logging/WebRtcLog.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
#include "MediaPermissionGonk.h"
|
||||
#endif
|
||||
|
||||
#if defined (XP_WIN)
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#include <winsock2.h>
|
||||
|
@ -1937,10 +1933,6 @@ MediaManager::Get() {
|
|||
__LINE__,
|
||||
NS_LITERAL_STRING("Media shutdown"));
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
#ifdef MOZ_B2G
|
||||
// Init MediaPermissionManager before sending out any permission requests.
|
||||
(void) MediaPermissionManager::GetInstance();
|
||||
#endif //MOZ_B2G
|
||||
}
|
||||
return sSingleton;
|
||||
}
|
||||
|
|
|
@ -828,7 +828,7 @@ class RTCPeerConnection {
|
|||
|
||||
async _getPermission() {
|
||||
if (!this._havePermission) {
|
||||
let privileged = this._isChrome || AppConstants.MOZ_B2G ||
|
||||
let privileged = this._isChrome ||
|
||||
Services.prefs.getBoolPref("media.navigator.permission.disabled");
|
||||
|
||||
if (privileged) {
|
||||
|
|
|
@ -55,11 +55,8 @@ public:
|
|||
static const int DEFAULT_169_VIDEO_WIDTH = 1280;
|
||||
static const int DEFAULT_169_VIDEO_HEIGHT = 720;
|
||||
|
||||
#ifndef MOZ_B2G
|
||||
static const int DEFAULT_SAMPLE_RATE = 32000;
|
||||
#else
|
||||
static const int DEFAULT_SAMPLE_RATE = 16000;
|
||||
#endif
|
||||
|
||||
// This allows using whatever rate the graph is using for the
|
||||
// MediaStreamTrack. This is useful for microphone data, we know it's already
|
||||
// at the correct rate for insertion in the MSG.
|
||||
|
|
|
@ -814,7 +814,6 @@ MediaEngineWebRTCMicrophoneSource::AllocChannel()
|
|||
|
||||
// Check for availability.
|
||||
if (!mAudioInput->SetRecordingDevice(mCapIndex)) {
|
||||
#ifndef MOZ_B2G
|
||||
// Because of the permission mechanism of B2G, we need to skip the status
|
||||
// check here.
|
||||
bool avail = false;
|
||||
|
@ -825,7 +824,6 @@ MediaEngineWebRTCMicrophoneSource::AllocChannel()
|
|||
}
|
||||
return false;
|
||||
}
|
||||
#endif // MOZ_B2G
|
||||
|
||||
// Set "codec" to PCM, 32kHz on device's channels
|
||||
ScopedCustomReleasePtr<webrtc::VoECodec> ptrVoECodec(webrtc::VoECodec::GetInterface(mVoiceEngine));
|
||||
|
|
|
@ -16,42 +16,6 @@ typedef mozilla::dom::Sequence<nsString> WebrtcGlobalLog;
|
|||
|
||||
namespace IPC {
|
||||
|
||||
template<typename T>
|
||||
struct ParamTraits<mozilla::dom::Optional<T>>
|
||||
{
|
||||
typedef mozilla::dom::Optional<T> paramType;
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
if (aParam.WasPassed()) {
|
||||
WriteParam(aMsg, true);
|
||||
WriteParam(aMsg, aParam.Value());
|
||||
return;
|
||||
}
|
||||
|
||||
WriteParam(aMsg, false);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
bool was_passed = false;
|
||||
|
||||
if (!ReadParam(aMsg, aIter, &was_passed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aResult->Reset(); //XXX Optional_base seems to reach this point with isSome true.
|
||||
|
||||
if (was_passed) {
|
||||
if (!ReadParam(aMsg, aIter, &(aResult->Construct()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ParamTraits<mozilla::dom::Sequence<T>>
|
||||
{
|
||||
|
|
|
@ -159,10 +159,6 @@ class AlertServiceObserver: public nsIObserver
|
|||
|
||||
// forward to parent
|
||||
if (mNotification) {
|
||||
#ifdef MOZ_B2G
|
||||
if (NS_FAILED(mNotification->CheckInnerWindowCorrectness()))
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
#endif
|
||||
mNotification->HandleAlertServiceNotification(aTopic);
|
||||
}
|
||||
return NS_OK;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "ipc/IPCMessageUtils.h"
|
||||
|
||||
#include "mozilla/dom/quota/PersistenceType.h"
|
||||
#include "mozilla/OriginAttributes.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
|
@ -21,6 +22,30 @@ struct ParamTraits<mozilla::dom::quota::PersistenceType> :
|
|||
mozilla::dom::quota::PERSISTENCE_TYPE_INVALID>
|
||||
{ };
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::OriginAttributesPattern>
|
||||
{
|
||||
typedef mozilla::OriginAttributesPattern paramType;
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
WriteParam(aMsg, aParam.mAppId);
|
||||
WriteParam(aMsg, aParam.mFirstPartyDomain);
|
||||
WriteParam(aMsg, aParam.mInIsolatedMozBrowser);
|
||||
WriteParam(aMsg, aParam.mPrivateBrowsingId);
|
||||
WriteParam(aMsg, aParam.mUserContextId);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
return ReadParam(aMsg, aIter, &aResult->mAppId) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mFirstPartyDomain) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mInIsolatedMozBrowser) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mPrivateBrowsingId) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mUserContextId);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace IPC
|
||||
|
||||
#endif // mozilla_dom_quota_SerializationHelpers_h
|
||||
|
|
|
@ -868,15 +868,6 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
|
|||
{
|
||||
NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1);
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
// load group information (on process-split necko implementations like b2g).
|
||||
// (fix this in bug 1011086)
|
||||
if (!mCallingChannelLoadGroup) {
|
||||
NS_WARNING("Load group required but not present for report sending; cannot send CSP violation reports");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
dom::CSPReport report;
|
||||
nsresult rv;
|
||||
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
var frame = document.getElementById("dataFrame");
|
||||
var principal = SpecialPowers.wrap(frame.contentDocument).nodePrincipal;
|
||||
var cspJSON = principal.cspJSON;
|
||||
var result = principal.cspJSON ? "dataInheritsCSP" : "dataDoesNotInheritCSP";
|
||||
window.parent.postMessage({result}, "*");
|
||||
var cspOBJ = JSON.parse(principal.cspJSON);
|
||||
// make sure we got >>one<< policy
|
||||
var policies = cspOBJ["csp-policies"];
|
||||
window.parent.postMessage({result: policies.length}, "*");
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -22,7 +22,9 @@ SimpleTest.waitForExplicitFinish();
|
|||
window.addEventListener("message", receiveMessage);
|
||||
function receiveMessage(event) {
|
||||
window.removeEventListener("message", receiveMessage);
|
||||
is(event.data.result, "dataInheritsCSP",
|
||||
// toplevel CSP should apply to data: URI iframe hence resulting
|
||||
// in 1 applied policy.
|
||||
is(event.data.result, 1,
|
||||
"data: URI iframe inherits CSP from including context");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "mozilla/dom/StorageBinding.h"
|
||||
#include "mozilla/dom/StorageEvent.h"
|
||||
#include "mozilla/dom/StorageEventBinding.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/EnumSet.h"
|
||||
|
@ -179,13 +181,20 @@ LocalStorage::BroadcastChangeNotification(const nsAString& aKey,
|
|||
const nsAString& aOldValue,
|
||||
const nsAString& aNewValue)
|
||||
{
|
||||
if (!XRE_IsParentProcess() && Principal()) {
|
||||
// If we are in a child process, we want to send a message to the parent in
|
||||
// order to broadcast the StorageEvent correctly to any child process.
|
||||
dom::ContentChild* cc = dom::ContentChild::GetSingleton();
|
||||
Unused << NS_WARN_IF(!cc->SendBroadcastLocalStorageChange(
|
||||
mDocumentURI, nsString(aKey), nsString(aOldValue), nsString(aNewValue),
|
||||
IPC::Principal(Principal()), mIsPrivate));
|
||||
if (Principal()) {
|
||||
// We want to send a message to the parent in order to broadcast the
|
||||
// StorageEvent correctly to any child process.
|
||||
|
||||
PBackgroundChild* actor = BackgroundChild::GetForCurrentThread();
|
||||
MOZ_ASSERT(actor);
|
||||
|
||||
PrincipalInfo principalInfo;
|
||||
nsresult rv = PrincipalToPrincipalInfo(Principal(), &principalInfo);
|
||||
if (!NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Unused << NS_WARN_IF(!actor->SendBroadcastLocalStorageChange(
|
||||
mDocumentURI, nsString(aKey), nsString(aOldValue), nsString(aNewValue),
|
||||
principalInfo, mIsPrivate));
|
||||
}
|
||||
}
|
||||
|
||||
DispatchStorageEvent(mDocumentURI, aKey, aOldValue, aNewValue,
|
||||
|
@ -204,16 +213,6 @@ LocalStorage::DispatchStorageEvent(const nsAString& aDocumentURI,
|
|||
{
|
||||
NotifyChange(aStorage, aPrincipal, aKey, aOldValue, aNewValue,
|
||||
u"localStorage", aDocumentURI, aIsPrivate, aImmediateDispatch);
|
||||
|
||||
// If we are in the parent process and we have the principal, we want to
|
||||
// broadcast this event to every other process.
|
||||
if (XRE_IsParentProcess() && aPrincipal) {
|
||||
for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
|
||||
Unused << cp->SendDispatchLocalStorageChange(
|
||||
nsString(aDocumentURI), nsString(aKey), nsString(aOldValue),
|
||||
nsString(aNewValue), IPC::Principal(aPrincipal), aIsPrivate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -24,10 +24,6 @@ namespace dom {
|
|||
|
||||
#define DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS 20000
|
||||
|
||||
// static
|
||||
StorageDBBridge* LocalStorageCache::sDatabase = nullptr;
|
||||
bool LocalStorageCache::sDatabaseDown = false;
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kDefaultSet = 0;
|
||||
|
@ -243,13 +239,14 @@ LocalStorageCache::Preload()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!StartDatabase()) {
|
||||
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
|
||||
if (!storageChild) {
|
||||
mLoaded = true;
|
||||
mLoadResult = NS_ERROR_FAILURE;
|
||||
return;
|
||||
}
|
||||
|
||||
sDatabase->AsyncPreload(this);
|
||||
storageChild->AsyncPreload(this);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -309,7 +306,7 @@ LocalStorageCache::WaitForPreload(Telemetry::HistogramID aTelemetryID)
|
|||
// No need to check sDatabase for being non-null since preload is either
|
||||
// done before we've shut the DB down or when the DB could not start,
|
||||
// preload has not even be started.
|
||||
sDatabase->SyncPreload(this);
|
||||
StorageDBChild::Get()->SyncPreload(this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -429,17 +426,18 @@ LocalStorageCache::SetItem(const LocalStorage* aStorage, const nsAString& aKey,
|
|||
data.mKeys.Put(aKey, aValue);
|
||||
|
||||
if (aSource == ContentMutation && Persist(aStorage)) {
|
||||
if (!sDatabase) {
|
||||
StorageDBChild* storageChild = StorageDBChild::Get();
|
||||
if (!storageChild) {
|
||||
NS_ERROR("Writing to localStorage after the database has been shut down"
|
||||
", data lose!");
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (DOMStringIsNull(aOld)) {
|
||||
return sDatabase->AsyncAddItem(this, aKey, aValue);
|
||||
return storageChild->AsyncAddItem(this, aKey, aValue);
|
||||
}
|
||||
|
||||
return sDatabase->AsyncUpdateItem(this, aKey, aValue);
|
||||
return storageChild->AsyncUpdateItem(this, aKey, aValue);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -470,13 +468,14 @@ LocalStorageCache::RemoveItem(const LocalStorage* aStorage,
|
|||
data.mKeys.Remove(aKey);
|
||||
|
||||
if (aSource == ContentMutation && Persist(aStorage)) {
|
||||
if (!sDatabase) {
|
||||
StorageDBChild* storageChild = StorageDBChild::Get();
|
||||
if (!storageChild) {
|
||||
NS_ERROR("Writing to localStorage after the database has been shut down"
|
||||
", data lose!");
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return sDatabase->AsyncRemoveItem(this, aKey);
|
||||
return storageChild->AsyncRemoveItem(this, aKey);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -511,13 +510,14 @@ LocalStorageCache::Clear(const LocalStorage* aStorage,
|
|||
}
|
||||
|
||||
if (aSource == ContentMutation && Persist(aStorage) && (refresh || hadData)) {
|
||||
if (!sDatabase) {
|
||||
StorageDBChild* storageChild = StorageDBChild::Get();
|
||||
if (!storageChild) {
|
||||
NS_ERROR("Writing to localStorage after the database has been shut down"
|
||||
", data lose!");
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return sDatabase->AsyncClear(this);
|
||||
return storageChild->AsyncClear(this);
|
||||
}
|
||||
|
||||
return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
|
||||
|
@ -672,73 +672,5 @@ StorageUsage::CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex,
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
StorageDBBridge*
|
||||
LocalStorageCache::StartDatabase()
|
||||
{
|
||||
if (sDatabase || sDatabaseDown) {
|
||||
// When sDatabaseDown is at true, sDatabase is null.
|
||||
// Checking sDatabaseDown flag here prevents reinitialization of
|
||||
// the database after shutdown.
|
||||
return sDatabase;
|
||||
}
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
nsAutoPtr<StorageDBThread> db(new StorageDBThread());
|
||||
|
||||
nsresult rv = db->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sDatabase = db.forget();
|
||||
} else {
|
||||
// Use LocalStorageManager::Ensure in case we're called from
|
||||
// DOMSessionStorageManager's initializer and we haven't yet initialized the
|
||||
// local storage manager.
|
||||
RefPtr<StorageDBChild> db = new StorageDBChild(
|
||||
LocalStorageManager::Ensure());
|
||||
|
||||
nsresult rv = db->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
db.forget(&sDatabase);
|
||||
}
|
||||
|
||||
return sDatabase;
|
||||
}
|
||||
|
||||
// static
|
||||
StorageDBBridge*
|
||||
LocalStorageCache::GetDatabase()
|
||||
{
|
||||
return sDatabase;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
LocalStorageCache::StopDatabase()
|
||||
{
|
||||
if (!sDatabase) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
sDatabaseDown = true;
|
||||
|
||||
nsresult rv = sDatabase->Shutdown();
|
||||
if (XRE_IsParentProcess()) {
|
||||
delete sDatabase;
|
||||
} else {
|
||||
StorageDBChild* child = static_cast<StorageDBChild*>(sDatabase);
|
||||
NS_RELEASE(child);
|
||||
}
|
||||
|
||||
sDatabase = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -131,13 +131,6 @@ public:
|
|||
|
||||
void GetKeys(const LocalStorage* aStorage, nsTArray<nsString>& aKeys);
|
||||
|
||||
// Starts the database engine thread or the IPC bridge
|
||||
static StorageDBBridge* StartDatabase();
|
||||
static StorageDBBridge* GetDatabase();
|
||||
|
||||
// Stops the thread and flushes all uncommited data
|
||||
static nsresult StopDatabase();
|
||||
|
||||
// LocalStorageCacheBridge
|
||||
|
||||
virtual const nsCString Origin() const;
|
||||
|
@ -260,13 +253,6 @@ private:
|
|||
// Whether we have already captured state of the cache preload on our first
|
||||
// access.
|
||||
bool mPreloadTelemetryRecorded : 1;
|
||||
|
||||
// StorageDBThread on the parent or single process,
|
||||
// StorageDBChild on the child process.
|
||||
static StorageDBBridge* sDatabase;
|
||||
|
||||
// False until we shut the database down.
|
||||
static bool sDatabaseDown;
|
||||
};
|
||||
|
||||
// StorageUsage
|
||||
|
@ -274,7 +260,7 @@ private:
|
|||
class StorageUsageBridge
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StorageUsageBridge)
|
||||
NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(StorageUsageBridge)
|
||||
|
||||
virtual const nsCString& OriginScope() = 0;
|
||||
virtual void LoadUsage(const int64_t aUsage) = 0;
|
||||
|
|
|
@ -76,7 +76,7 @@ LocalStorageManager::LocalStorageManager()
|
|||
// Do this only on the child process. The thread IPC bridge
|
||||
// is also used to communicate chrome observer notifications.
|
||||
// Note: must be called after we set sSelf
|
||||
LocalStorageCache::StartDatabase();
|
||||
StorageDBChild::GetOrCreate();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,9 +168,9 @@ LocalStorageManager::GetOriginUsage(const nsACString& aOriginNoSuffix)
|
|||
|
||||
usage = new StorageUsage(aOriginNoSuffix);
|
||||
|
||||
StorageDBBridge* db = LocalStorageCache::StartDatabase();
|
||||
if (db) {
|
||||
db->AsyncGetUsage(usage);
|
||||
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
|
||||
if (storageChild) {
|
||||
storageChild->AsyncGetUsage(usage);
|
||||
}
|
||||
|
||||
mUsages.Put(aOriginNoSuffix, usage);
|
||||
|
@ -234,7 +234,7 @@ LocalStorageManager::GetStorageInternal(CreateMode aCreateMode,
|
|||
if (aCreateMode == CreateMode::CreateIfShouldPreload) {
|
||||
// This is a demand to just preload the cache, if the scope has
|
||||
// no data stored, bypass creation and preload of the cache.
|
||||
StorageDBBridge* db = LocalStorageCache::GetDatabase();
|
||||
StorageDBChild* db = StorageDBChild::Get();
|
||||
if (db) {
|
||||
if (!db->ShouldPreloadOrigin(LocalStorageManager::CreateOrigin(originAttrSuffix, originKey))) {
|
||||
return NS_OK;
|
||||
|
|
|
@ -131,6 +131,7 @@ private:
|
|||
nsDataHashtable<nsCStringHashKey, RefPtr<StorageUsage> > mUsages;
|
||||
|
||||
friend class LocalStorageCache;
|
||||
friend class StorageDBChild;
|
||||
// Releases cache since it is no longer referrered by any Storage object.
|
||||
virtual void DropCache(LocalStorageCache* aCache);
|
||||
|
||||
|
|
|
@ -4,7 +4,12 @@
|
|||
* 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 protocol PContent;
|
||||
include protocol PBackground;
|
||||
|
||||
include "mozilla/dom/quota/SerializationHelpers.h";
|
||||
|
||||
using mozilla::OriginAttributesPattern
|
||||
from "mozilla/OriginAttributes.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -12,16 +17,16 @@ namespace dom {
|
|||
/* This protocol bridges async access to the database thread running on the
|
||||
* parent process and caches running on the child process.
|
||||
*/
|
||||
nested(upto inside_cpow) sync protocol PStorage
|
||||
sync protocol PBackgroundStorage
|
||||
{
|
||||
manager PContent;
|
||||
manager PBackground;
|
||||
|
||||
parent:
|
||||
async __delete__();
|
||||
async DeleteMe();
|
||||
|
||||
nested(inside_cpow) sync Preload(nsCString originSuffix,
|
||||
nsCString originNoSuffix,
|
||||
uint32_t alreadyLoadedCount)
|
||||
sync Preload(nsCString originSuffix,
|
||||
nsCString originNoSuffix,
|
||||
uint32_t alreadyLoadedCount)
|
||||
returns (nsString[] keys, nsString[] values, nsresult rv);
|
||||
|
||||
async AsyncPreload(nsCString originSuffix, nsCString originNoSuffix,
|
||||
|
@ -35,8 +40,20 @@ parent:
|
|||
nsString key);
|
||||
async AsyncClear(nsCString originSuffix, nsCString originNoSuffix);
|
||||
async AsyncFlush();
|
||||
|
||||
|
||||
// These are privileged operations for use only by the observer API for
|
||||
// delayed initialization and clearing origins and will never be used from
|
||||
// content process children. Ideally these would be guarded by checks or
|
||||
// exist on a separate, privileged interface, but PBackgroundStorage is
|
||||
// already insecure.
|
||||
async Startup();
|
||||
async ClearAll();
|
||||
async ClearMatchingOrigin(nsCString originNoSuffix);
|
||||
async ClearMatchingOriginAttributes(OriginAttributesPattern pattern);
|
||||
|
||||
child:
|
||||
async __delete__();
|
||||
|
||||
async Observe(nsCString topic,
|
||||
nsString originAttributesPattern,
|
||||
nsCString originScope);
|
|
@ -23,6 +23,7 @@
|
|||
#include "mozIStorageValueArray.h"
|
||||
#include "mozIStorageFunction.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsVariant.h"
|
||||
#include "mozilla/IOInterposer.h"
|
||||
|
@ -48,6 +49,11 @@ using namespace StorageUtils;
|
|||
|
||||
namespace { // anon
|
||||
|
||||
StorageDBThread* sStorageThread = nullptr;
|
||||
|
||||
// False until we shut the storage thread down.
|
||||
bool sStorageThreadDown = false;
|
||||
|
||||
// This is only a compatibility code for schema version 0. Returns the 'scope'
|
||||
// key in the schema version 0 format for the scope column.
|
||||
nsCString
|
||||
|
@ -106,11 +112,64 @@ Scheme0Scope(LocalStorageCacheBridge* aCache)
|
|||
|
||||
} // anon
|
||||
|
||||
|
||||
// XXX Fix me!
|
||||
#if 0
|
||||
StorageDBBridge::StorageDBBridge()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
class StorageDBThread::InitHelper final
|
||||
: public Runnable
|
||||
{
|
||||
nsCOMPtr<nsIEventTarget> mOwningThread;
|
||||
mozilla::Mutex mMutex;
|
||||
mozilla::CondVar mCondVar;
|
||||
nsString mProfilePath;
|
||||
nsresult mMainThreadResultCode;
|
||||
bool mWaiting;
|
||||
|
||||
public:
|
||||
InitHelper()
|
||||
: Runnable("dom::StorageDBThread::InitHelper")
|
||||
, mOwningThread(GetCurrentThreadEventTarget())
|
||||
, mMutex("InitHelper::mMutex")
|
||||
, mCondVar(mMutex, "InitHelper::mCondVar")
|
||||
, mMainThreadResultCode(NS_OK)
|
||||
, mWaiting(true)
|
||||
{ }
|
||||
|
||||
// Because of the `sync Preload` IPC, we need to be able to synchronously
|
||||
// initialize, which includes consulting and initializing
|
||||
// some main-thread-only APIs. Bug 1386441 discusses improving this situation.
|
||||
nsresult
|
||||
SyncDispatchAndReturnProfilePath(nsAString& aProfilePath);
|
||||
|
||||
private:
|
||||
~InitHelper() override = default;
|
||||
|
||||
nsresult
|
||||
RunOnMainThread();
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
class StorageDBThread::NoteBackgroundThreadRunnable final
|
||||
: public Runnable
|
||||
{
|
||||
nsCOMPtr<nsIEventTarget> mOwningThread;
|
||||
|
||||
public:
|
||||
NoteBackgroundThreadRunnable()
|
||||
: Runnable("dom::StorageDBThread::NoteBackgroundThreadRunnable")
|
||||
, mOwningThread(GetCurrentThreadEventTarget())
|
||||
{ }
|
||||
|
||||
private:
|
||||
~NoteBackgroundThreadRunnable() override = default;
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
StorageDBThread::StorageDBThread()
|
||||
: mThread(nullptr)
|
||||
|
@ -127,26 +186,104 @@ StorageDBThread::StorageDBThread()
|
|||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageDBThread::Init()
|
||||
// static
|
||||
StorageDBThread*
|
||||
StorageDBThread::Get()
|
||||
{
|
||||
nsresult rv;
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
return sStorageThread;
|
||||
}
|
||||
|
||||
// static
|
||||
StorageDBThread*
|
||||
StorageDBThread::GetOrCreate(const nsString& aProfilePath)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (sStorageThread || sStorageThreadDown) {
|
||||
// When sStorageThreadDown is at true, sStorageThread is null.
|
||||
// Checking sStorageThreadDown flag here prevents reinitialization of
|
||||
// the storage thread after shutdown.
|
||||
return sStorageThread;
|
||||
}
|
||||
|
||||
nsAutoPtr<StorageDBThread> storageThread(new StorageDBThread());
|
||||
|
||||
nsresult rv = storageThread->Init(aProfilePath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sStorageThread = storageThread.forget();
|
||||
|
||||
return sStorageThread;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
StorageDBThread::GetProfilePath(nsString& aProfilePath)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Need to determine location on the main thread, since
|
||||
// NS_GetSpecialDirectory access the atom table that can
|
||||
// be accessed only on the main thread.
|
||||
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
|
||||
getter_AddRefs(mDatabaseFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// NS_GetSpecialDirectory accesses the atom table that can
|
||||
// only be accessed on the main thread.
|
||||
nsCOMPtr<nsIFile> profileDir;
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
|
||||
getter_AddRefs(profileDir));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = profileDir->GetPath(aProfilePath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// This service has to be started on the main thread currently.
|
||||
nsCOMPtr<mozIStorageService> ss =
|
||||
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageDBThread::Init(const nsString& aProfilePath)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsString profilePath;
|
||||
if (aProfilePath.IsEmpty()) {
|
||||
RefPtr<InitHelper> helper = new InitHelper();
|
||||
|
||||
rv = helper->SyncDispatchAndReturnProfilePath(profilePath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
profilePath = aProfilePath;
|
||||
}
|
||||
|
||||
mDatabaseFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mDatabaseFile->InitWithPath(profilePath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mDatabaseFile->Append(NS_LITERAL_STRING("webappsstore.sqlite"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Ensure mozIStorageService init on the main thread first.
|
||||
nsCOMPtr<mozIStorageService> service =
|
||||
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Need to keep the lock to avoid setting mThread later then
|
||||
// the thread body executes.
|
||||
MonitorAutoLock monitor(mThreadObserver->GetMonitor());
|
||||
|
@ -158,12 +295,20 @@ StorageDBThread::Init()
|
|||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
RefPtr<NoteBackgroundThreadRunnable> runnable =
|
||||
new NoteBackgroundThreadRunnable();
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageDBThread::Shutdown()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
sStorageThreadDown = true;
|
||||
|
||||
if (!mThread) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
@ -904,7 +1049,7 @@ StorageDBThread::DBOperation::Perform(StorageDBThread* aThread)
|
|||
}
|
||||
|
||||
StatementCache* statements;
|
||||
if (MOZ_UNLIKELY(NS_IsMainThread())) {
|
||||
if (MOZ_UNLIKELY(IsOnBackgroundThread())) {
|
||||
statements = &aThread->mReaderStatements;
|
||||
} else {
|
||||
statements = &aThread->mWorkerStatements;
|
||||
|
@ -1494,5 +1639,84 @@ StorageDBThread::PendingOperations::IsOriginUpdatePending(const nsACString& aOri
|
|||
return false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageDBThread::
|
||||
InitHelper::SyncDispatchAndReturnProfilePath(nsAString& aProfilePath)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
|
||||
|
||||
mozilla::MutexAutoLock autolock(mMutex);
|
||||
while (mWaiting) {
|
||||
mCondVar.Wait();
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(mMainThreadResultCode))) {
|
||||
return mMainThreadResultCode;
|
||||
}
|
||||
|
||||
aProfilePath = mProfilePath;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StorageDBThread::
|
||||
InitHelper::Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult rv = GetProfilePath(mProfilePath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mMainThreadResultCode = rv;
|
||||
}
|
||||
|
||||
mozilla::MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(mWaiting);
|
||||
|
||||
mWaiting = false;
|
||||
mCondVar.Notify();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StorageDBThread::
|
||||
NoteBackgroundThreadRunnable::Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
StorageObserver* observer = StorageObserver::Self();
|
||||
MOZ_ASSERT(observer);
|
||||
|
||||
observer->NoteBackgroundThread(mOwningThread);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StorageDBThread::
|
||||
ShutdownRunnable::Run()
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
mDone = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (sStorageThread) {
|
||||
sStorageThread->Shutdown();
|
||||
|
||||
delete sStorageThread;
|
||||
sStorageThread = nullptr;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -32,6 +32,15 @@ class StorageUsage;
|
|||
|
||||
typedef mozilla::storage::StatementCache<mozIStorageStatement> StatementCache;
|
||||
|
||||
// XXX Fix me!
|
||||
// 1. Move comments to StorageDBThread/StorageDBChild.
|
||||
// 2. Devirtualize relevant methods in StorageDBThread/StorageDBChild.
|
||||
// 3. Remove relevant methods in StorageDBThread/StorageDBChild that are
|
||||
// unused.
|
||||
// 4. Remove this class completely.
|
||||
//
|
||||
// See bug 1387636 for more details.
|
||||
#if 0
|
||||
// Interface used by the cache to post operations to the asynchronous
|
||||
// database thread or process.
|
||||
class StorageDBBridge
|
||||
|
@ -100,17 +109,15 @@ public:
|
|||
// Check whether the scope has any data stored on disk and is thus allowed to
|
||||
// preload
|
||||
virtual bool ShouldPreloadOrigin(const nsACString& aOriginNoSuffix) = 0;
|
||||
|
||||
// Get the complete list of scopes having data
|
||||
virtual void GetOriginsHavingData(InfallibleTArray<nsCString>* aOrigins) = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
// The implementation of the the database engine, this directly works
|
||||
// with the sqlite or any other db API we are based on
|
||||
// This class is resposible for collecting and processing asynchronous
|
||||
// DB operations over caches (LocalStorageCache) communicating though
|
||||
// LocalStorageCacheBridge interface class
|
||||
class StorageDBThread final : public StorageDBBridge
|
||||
class StorageDBThread final
|
||||
{
|
||||
public:
|
||||
class PendingOperations;
|
||||
|
@ -287,11 +294,46 @@ public:
|
|||
Monitor mMonitor;
|
||||
};
|
||||
|
||||
class InitHelper;
|
||||
|
||||
class NoteBackgroundThreadRunnable;
|
||||
|
||||
class ShutdownRunnable : public Runnable
|
||||
{
|
||||
// Only touched on the main thread.
|
||||
bool& mDone;
|
||||
|
||||
public:
|
||||
explicit ShutdownRunnable(bool& aDone)
|
||||
: Runnable("dom::StorageDBThread::ShutdownRunnable")
|
||||
, mDone(aDone)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
private:
|
||||
~ShutdownRunnable()
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
public:
|
||||
StorageDBThread();
|
||||
virtual ~StorageDBThread() {}
|
||||
|
||||
virtual nsresult Init();
|
||||
static StorageDBThread*
|
||||
Get();
|
||||
|
||||
static StorageDBThread*
|
||||
GetOrCreate(const nsString& aProfilePath);
|
||||
|
||||
static nsresult
|
||||
GetProfilePath(nsString& aProfilePath);
|
||||
|
||||
virtual nsresult Init(const nsString& aProfilePath);
|
||||
|
||||
// Flushes all uncommited data and stops the I/O thread.
|
||||
virtual nsresult Shutdown();
|
||||
|
||||
virtual void AsyncPreload(LocalStorageCacheBridge* aCache,
|
||||
|
@ -358,7 +400,9 @@ public:
|
|||
virtual void AsyncFlush();
|
||||
|
||||
virtual bool ShouldPreloadOrigin(const nsACString& aOrigin);
|
||||
virtual void GetOriginsHavingData(InfallibleTArray<nsCString>* aOrigins);
|
||||
|
||||
// Get the complete list of scopes having data.
|
||||
void GetOriginsHavingData(InfallibleTArray<nsCString>* aOrigins);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIFile> mDatabaseFile;
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "mozilla/ipc/PBackgroundParent.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsIDiskSpaceWatcher.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
@ -17,28 +21,37 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
StorageDBChild* sStorageChild = nullptr;
|
||||
|
||||
// False until we shut the storage child down.
|
||||
bool sStorageChildDown = false;
|
||||
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Child
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ADDREF(StorageDBChild)
|
||||
|
||||
NS_IMETHODIMP_(MozExternalRefCountType) StorageDBChild::Release(void)
|
||||
class StorageDBChild::ShutdownObserver final
|
||||
: public nsIObserver
|
||||
{
|
||||
NS_PRECONDITION(0 != mRefCnt, "dup release");
|
||||
nsrefcnt count = --mRefCnt;
|
||||
NS_LOG_RELEASE(this, count, "StorageDBChild");
|
||||
if (count == 1 && mIPCOpen) {
|
||||
Send__delete__(this);
|
||||
return 0;
|
||||
public:
|
||||
ShutdownObserver()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
if (count == 0) {
|
||||
mRefCnt = 1;
|
||||
delete this;
|
||||
return 0;
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
private:
|
||||
~ShutdownObserver()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
StorageDBChild::AddIPDLReference()
|
||||
|
@ -67,6 +80,44 @@ StorageDBChild::~StorageDBChild()
|
|||
{
|
||||
}
|
||||
|
||||
// static
|
||||
StorageDBChild*
|
||||
StorageDBChild::Get()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
return sStorageChild;
|
||||
}
|
||||
|
||||
// static
|
||||
StorageDBChild*
|
||||
StorageDBChild::GetOrCreate()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sStorageChild || sStorageChildDown) {
|
||||
// When sStorageChildDown is at true, sStorageChild is null.
|
||||
// Checking sStorageChildDown flag here prevents reinitialization of
|
||||
// the storage child after shutdown.
|
||||
return sStorageChild;
|
||||
}
|
||||
|
||||
// Use LocalStorageManager::Ensure in case we're called from
|
||||
// DOMSessionStorageManager's initializer and we haven't yet initialized the
|
||||
// local storage manager.
|
||||
RefPtr<StorageDBChild> storageChild =
|
||||
new StorageDBChild(LocalStorageManager::Ensure());
|
||||
|
||||
nsresult rv = storageChild->Init();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
storageChild.forget(&sStorageChild);
|
||||
|
||||
return sStorageChild;
|
||||
}
|
||||
|
||||
nsTHashtable<nsCStringHashKey>&
|
||||
StorageDBChild::OriginsHavingData()
|
||||
{
|
||||
|
@ -80,9 +131,35 @@ StorageDBChild::OriginsHavingData()
|
|||
nsresult
|
||||
StorageDBChild::Init()
|
||||
{
|
||||
ContentChild* child = ContentChild::GetSingleton();
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
PBackgroundChild* actor = BackgroundChild::GetOrCreateForCurrentThread();
|
||||
if (NS_WARN_IF(!actor)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsString profilePath;
|
||||
if (XRE_IsParentProcess()) {
|
||||
nsresult rv = StorageDBThread::GetProfilePath(profilePath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
AddIPDLReference();
|
||||
child->SendPStorageConstructor(this);
|
||||
|
||||
actor->SendPBackgroundStorageConstructor(this, profilePath);
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
|
||||
MOZ_ASSERT(observerService);
|
||||
|
||||
nsCOMPtr<nsIObserver> observer = new ShutdownObserver();
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
observerService->AddObserver(observer,
|
||||
"xpcom-shutdown",
|
||||
false));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -216,6 +293,8 @@ StorageDBChild::RecvObserve(const nsCString& aTopic,
|
|||
const nsString& aOriginAttributesPattern,
|
||||
const nsCString& aOriginScope)
|
||||
{
|
||||
MOZ_ASSERT(!XRE_IsParentProcess());
|
||||
|
||||
StorageObserver::Self()->Notify(
|
||||
aTopic.get(), aOriginAttributesPattern, aOriginScope);
|
||||
return IPC_OK();
|
||||
|
@ -286,10 +365,87 @@ StorageDBChild::RecvError(const nsresult& aRv)
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(StorageDBChild::ShutdownObserver, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
StorageDBChild::
|
||||
ShutdownObserver::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (NS_WARN_IF(!observerService)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
Unused << observerService->RemoveObserver(this, "xpcom-shutdown");
|
||||
|
||||
if (sStorageChild) {
|
||||
sStorageChildDown = true;
|
||||
|
||||
MOZ_ALWAYS_TRUE(sStorageChild->PBackgroundStorageChild::SendDeleteMe());
|
||||
|
||||
NS_RELEASE(sStorageChild);
|
||||
sStorageChild = nullptr;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Parent
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class StorageDBParent::ObserverSink
|
||||
: public StorageObserverSink
|
||||
{
|
||||
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
|
||||
|
||||
// Only touched on the PBackground thread.
|
||||
StorageDBParent* MOZ_NON_OWNING_REF mActor;
|
||||
|
||||
public:
|
||||
explicit ObserverSink(StorageDBParent* aActor)
|
||||
: mOwningEventTarget(GetCurrentThreadEventTarget())
|
||||
, mActor(aActor)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aActor);
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StorageDBParent::ObserverSink);
|
||||
|
||||
void
|
||||
Start();
|
||||
|
||||
void
|
||||
Stop();
|
||||
|
||||
private:
|
||||
~ObserverSink() = default;
|
||||
|
||||
void
|
||||
AddSink();
|
||||
|
||||
void
|
||||
RemoveSink();
|
||||
|
||||
void
|
||||
Notify(const nsCString& aTopic,
|
||||
const nsString& aOriginAttributesPattern,
|
||||
const nsCString& aOriginScope);
|
||||
|
||||
// StorageObserverSink
|
||||
nsresult
|
||||
Observe(const char* aTopic,
|
||||
const nsAString& aOriginAttrPattern,
|
||||
const nsACString& aOriginScope) override;
|
||||
};
|
||||
|
||||
NS_IMPL_ADDREF(StorageDBParent)
|
||||
NS_IMPL_RELEASE(StorageDBParent)
|
||||
|
||||
|
@ -311,78 +467,111 @@ StorageDBParent::ReleaseIPDLReference()
|
|||
|
||||
namespace {
|
||||
|
||||
class SendInitialChildDataRunnable : public Runnable
|
||||
class CheckLowDiskSpaceRunnable
|
||||
: public Runnable
|
||||
{
|
||||
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
|
||||
RefPtr<StorageDBParent> mParent;
|
||||
bool mLowDiskSpace;
|
||||
|
||||
public:
|
||||
explicit SendInitialChildDataRunnable(StorageDBParent* aParent)
|
||||
: Runnable("dom::SendInitialChildDataRunnable")
|
||||
explicit CheckLowDiskSpaceRunnable(StorageDBParent* aParent)
|
||||
: Runnable("dom::CheckLowDiskSpaceRunnable")
|
||||
, mOwningEventTarget(GetCurrentThreadEventTarget())
|
||||
, mParent(aParent)
|
||||
{}
|
||||
, mLowDiskSpace(false)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aParent);
|
||||
}
|
||||
|
||||
private:
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
if (!mParent->IPCOpen()) {
|
||||
if (IsOnBackgroundThread()) {
|
||||
MOZ_ASSERT(mParent);
|
||||
|
||||
if (!mParent->IPCOpen()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mLowDiskSpace) {
|
||||
mozilla::Unused << mParent->SendObserve(
|
||||
nsDependentCString("low-disk-space"), EmptyString(), EmptyCString());
|
||||
}
|
||||
|
||||
mParent = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
StorageDBBridge* db = LocalStorageCache::GetDatabase();
|
||||
if (db) {
|
||||
InfallibleTArray<nsCString> scopes;
|
||||
db->GetOriginsHavingData(&scopes);
|
||||
mozilla::Unused << mParent->SendOriginsHavingData(scopes);
|
||||
}
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// We need to check if the device is in a low disk space situation, so
|
||||
// we can forbid in that case any write in localStorage.
|
||||
nsCOMPtr<nsIDiskSpaceWatcher> diskSpaceWatcher =
|
||||
do_GetService("@mozilla.org/toolkit/disk-space-watcher;1");
|
||||
if (!diskSpaceWatcher) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool lowDiskSpace = false;
|
||||
diskSpaceWatcher->GetIsDiskFull(&lowDiskSpace);
|
||||
diskSpaceWatcher->GetIsDiskFull(&mLowDiskSpace);
|
||||
|
||||
if (lowDiskSpace) {
|
||||
mozilla::Unused << mParent->SendObserve(
|
||||
nsDependentCString("low-disk-space"), EmptyString(), EmptyCString());
|
||||
}
|
||||
MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<StorageDBParent> mParent;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
StorageDBParent::StorageDBParent()
|
||||
: mIPCOpen(false)
|
||||
StorageDBParent::StorageDBParent(const nsString& aProfilePath)
|
||||
: mProfilePath(aProfilePath)
|
||||
, mIPCOpen(false)
|
||||
{
|
||||
StorageObserver* observer = StorageObserver::Self();
|
||||
if (observer) {
|
||||
observer->AddSink(this);
|
||||
}
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
// We are always open by IPC only
|
||||
AddIPDLReference();
|
||||
|
||||
// Cannot send directly from here since the channel
|
||||
// is not completely built at this moment.
|
||||
RefPtr<SendInitialChildDataRunnable> r =
|
||||
new SendInitialChildDataRunnable(this);
|
||||
NS_DispatchToCurrentThread(r);
|
||||
}
|
||||
|
||||
StorageDBParent::~StorageDBParent()
|
||||
{
|
||||
StorageObserver* observer = StorageObserver::Self();
|
||||
if (observer) {
|
||||
observer->RemoveSink(this);
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (mObserverSink) {
|
||||
mObserverSink->Stop();
|
||||
mObserverSink = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StorageDBParent::Init()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
PBackgroundParent* actor = Manager();
|
||||
MOZ_ASSERT(actor);
|
||||
|
||||
if (BackgroundParent::IsOtherProcessActor(actor)) {
|
||||
mObserverSink = new ObserverSink(this);
|
||||
mObserverSink->Start();
|
||||
}
|
||||
|
||||
StorageDBThread* storageThread = StorageDBThread::Get();
|
||||
if (storageThread) {
|
||||
InfallibleTArray<nsCString> scopes;
|
||||
storageThread->GetOriginsHavingData(&scopes);
|
||||
mozilla::Unused << SendOriginsHavingData(scopes);
|
||||
}
|
||||
|
||||
// We need to check if the device is in a low disk space situation, so
|
||||
// we can forbid in that case any write in localStorage.
|
||||
|
||||
RefPtr<CheckLowDiskSpaceRunnable> runnable =
|
||||
new CheckLowDiskSpaceRunnable(this);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
|
||||
}
|
||||
|
||||
StorageDBParent::CacheParentBridge*
|
||||
StorageDBParent::NewCache(const nsACString& aOriginSuffix,
|
||||
const nsACString& aOriginNoSuffix)
|
||||
|
@ -396,32 +585,48 @@ StorageDBParent::ActorDestroy(ActorDestroyReason aWhy)
|
|||
// Implement me! Bug 1005169
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
StorageDBParent::RecvDeleteMe()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
IProtocol* mgr = Manager();
|
||||
if (!PBackgroundStorageParent::Send__delete__(this)) {
|
||||
return IPC_FAIL_NO_REASON(mgr);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
StorageDBParent::RecvAsyncPreload(const nsCString& aOriginSuffix,
|
||||
const nsCString& aOriginNoSuffix,
|
||||
const bool& aPriority)
|
||||
{
|
||||
StorageDBBridge* db = LocalStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
|
||||
if (!storageThread) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
db->AsyncPreload(NewCache(aOriginSuffix, aOriginNoSuffix), aPriority);
|
||||
storageThread->AsyncPreload(NewCache(aOriginSuffix, aOriginNoSuffix),
|
||||
aPriority);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
StorageDBParent::RecvAsyncGetUsage(const nsCString& aOriginNoSuffix)
|
||||
{
|
||||
StorageDBBridge* db = LocalStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
|
||||
if (!storageThread) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
// The object releases it self in LoadUsage method
|
||||
RefPtr<UsageParentBridge> usage =
|
||||
new UsageParentBridge(this, aOriginNoSuffix);
|
||||
db->AsyncGetUsage(usage);
|
||||
|
||||
storageThread->AsyncGetUsage(usage);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -512,8 +717,8 @@ StorageDBParent::RecvPreload(const nsCString& aOriginSuffix,
|
|||
InfallibleTArray<nsString>* aValues,
|
||||
nsresult* aRv)
|
||||
{
|
||||
StorageDBBridge* db = LocalStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
|
||||
if (!storageThread) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
|
@ -521,7 +726,8 @@ StorageDBParent::RecvPreload(const nsCString& aOriginSuffix,
|
|||
new SyncLoadCacheHelper(aOriginSuffix, aOriginNoSuffix, aAlreadyLoadedCount,
|
||||
aKeys, aValues, aRv));
|
||||
|
||||
db->SyncPreload(cache, true);
|
||||
storageThread->SyncPreload(cache, true);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -531,13 +737,15 @@ StorageDBParent::RecvAsyncAddItem(const nsCString& aOriginSuffix,
|
|||
const nsString& aKey,
|
||||
const nsString& aValue)
|
||||
{
|
||||
StorageDBBridge* db = LocalStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
|
||||
if (!storageThread) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
nsresult rv = db->AsyncAddItem(NewCache(aOriginSuffix, aOriginNoSuffix), aKey,
|
||||
aValue);
|
||||
nsresult rv =
|
||||
storageThread->AsyncAddItem(NewCache(aOriginSuffix, aOriginNoSuffix),
|
||||
aKey,
|
||||
aValue);
|
||||
if (NS_FAILED(rv) && mIPCOpen) {
|
||||
mozilla::Unused << SendError(rv);
|
||||
}
|
||||
|
@ -551,13 +759,15 @@ StorageDBParent::RecvAsyncUpdateItem(const nsCString& aOriginSuffix,
|
|||
const nsString& aKey,
|
||||
const nsString& aValue)
|
||||
{
|
||||
StorageDBBridge* db = LocalStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
|
||||
if (!storageThread) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
nsresult rv = db->AsyncUpdateItem(NewCache(aOriginSuffix, aOriginNoSuffix),
|
||||
aKey, aValue);
|
||||
nsresult rv =
|
||||
storageThread->AsyncUpdateItem(NewCache(aOriginSuffix, aOriginNoSuffix),
|
||||
aKey,
|
||||
aValue);
|
||||
if (NS_FAILED(rv) && mIPCOpen) {
|
||||
mozilla::Unused << SendError(rv);
|
||||
}
|
||||
|
@ -570,13 +780,14 @@ StorageDBParent::RecvAsyncRemoveItem(const nsCString& aOriginSuffix,
|
|||
const nsCString& aOriginNoSuffix,
|
||||
const nsString& aKey)
|
||||
{
|
||||
StorageDBBridge* db = LocalStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
|
||||
if (!storageThread) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
nsresult rv = db->AsyncRemoveItem(NewCache(aOriginSuffix, aOriginNoSuffix),
|
||||
aKey);
|
||||
nsresult rv =
|
||||
storageThread->AsyncRemoveItem(NewCache(aOriginSuffix, aOriginNoSuffix),
|
||||
aKey);
|
||||
if (NS_FAILED(rv) && mIPCOpen) {
|
||||
mozilla::Unused << SendError(rv);
|
||||
}
|
||||
|
@ -588,12 +799,13 @@ mozilla::ipc::IPCResult
|
|||
StorageDBParent::RecvAsyncClear(const nsCString& aOriginSuffix,
|
||||
const nsCString& aOriginNoSuffix)
|
||||
{
|
||||
StorageDBBridge* db = LocalStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
|
||||
if (!storageThread) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
nsresult rv = db->AsyncClear(NewCache(aOriginSuffix, aOriginNoSuffix));
|
||||
nsresult rv =
|
||||
storageThread->AsyncClear(NewCache(aOriginSuffix, aOriginNoSuffix));
|
||||
if (NS_FAILED(rv) && mIPCOpen) {
|
||||
mozilla::Unused << SendError(rv);
|
||||
}
|
||||
|
@ -604,29 +816,76 @@ StorageDBParent::RecvAsyncClear(const nsCString& aOriginSuffix,
|
|||
mozilla::ipc::IPCResult
|
||||
StorageDBParent::RecvAsyncFlush()
|
||||
{
|
||||
StorageDBBridge* db = LocalStorageCache::GetDatabase();
|
||||
if (!db) {
|
||||
StorageDBThread* storageThread = StorageDBThread::Get();
|
||||
if (!storageThread) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
db->AsyncFlush();
|
||||
storageThread->AsyncFlush();
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// StorageObserverSink
|
||||
|
||||
nsresult
|
||||
StorageDBParent::Observe(const char* aTopic,
|
||||
const nsAString& aOriginAttributesPattern,
|
||||
const nsACString& aOriginScope)
|
||||
mozilla::ipc::IPCResult
|
||||
StorageDBParent::RecvStartup()
|
||||
{
|
||||
if (mIPCOpen) {
|
||||
mozilla::Unused << SendObserve(nsDependentCString(aTopic),
|
||||
nsString(aOriginAttributesPattern),
|
||||
nsCString(aOriginScope));
|
||||
StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
|
||||
if (!storageThread) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
StorageDBParent::RecvClearAll()
|
||||
{
|
||||
StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
|
||||
if (!storageThread) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
storageThread->AsyncClearAll();
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
StorageDBParent::RecvClearMatchingOrigin(const nsCString& aOriginNoSuffix)
|
||||
{
|
||||
StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
|
||||
if (!storageThread) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
storageThread->AsyncClearMatchingOrigin(aOriginNoSuffix);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
StorageDBParent::RecvClearMatchingOriginAttributes(
|
||||
const OriginAttributesPattern& aPattern)
|
||||
{
|
||||
StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
|
||||
if (!storageThread) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
storageThread->AsyncClearMatchingOriginAttributes(aPattern);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
StorageDBParent::Observe(const nsCString& aTopic,
|
||||
const nsString& aOriginAttributesPattern,
|
||||
const nsCString& aOriginScope)
|
||||
{
|
||||
if (mIPCOpen) {
|
||||
mozilla::Unused <<
|
||||
SendObserve(aTopic, aOriginAttributesPattern, aOriginScope);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -692,6 +951,8 @@ private:
|
|||
break;
|
||||
}
|
||||
|
||||
mParent = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
@ -719,7 +980,10 @@ StorageDBParent::CacheParentBridge::LoadItem(const nsAString& aKey,
|
|||
RefPtr<LoadRunnable> r =
|
||||
new LoadRunnable(mParent, LoadRunnable::loadItem, mOriginSuffix,
|
||||
mOriginNoSuffix, aKey, aValue);
|
||||
NS_DispatchToMainThread(r);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
mOwningEventTarget->Dispatch(r, NS_DISPATCH_NORMAL));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -736,7 +1000,9 @@ StorageDBParent::CacheParentBridge::LoadDone(nsresult aRv)
|
|||
RefPtr<LoadRunnable> r =
|
||||
new LoadRunnable(mParent, LoadRunnable::loadDone, mOriginSuffix,
|
||||
mOriginNoSuffix, aRv);
|
||||
NS_DispatchToMainThread(r);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
mOwningEventTarget->Dispatch(r, NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -746,6 +1012,42 @@ StorageDBParent::CacheParentBridge::LoadWait()
|
|||
MOZ_ASSERT(false);
|
||||
}
|
||||
|
||||
// XXX Fix me!
|
||||
// This should be just:
|
||||
// NS_IMPL_RELEASE_WITH_DESTROY(StorageDBParent::CacheParentBridge, Destroy)
|
||||
// But due to different strings used for refcount logging and different return
|
||||
// types, this is done manually for now.
|
||||
NS_IMETHODIMP_(void)
|
||||
StorageDBParent::CacheParentBridge::Release(void)
|
||||
{
|
||||
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
|
||||
nsrefcnt count = --mRefCnt;
|
||||
NS_LOG_RELEASE(this, count, "LocalStorageCacheBridge");
|
||||
if (0 == count) {
|
||||
mRefCnt = 1; /* stabilize */
|
||||
/* enable this to find non-threadsafe destructors: */
|
||||
/* NS_ASSERT_OWNINGTHREAD(_class); */
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StorageDBParent::CacheParentBridge::Destroy()
|
||||
{
|
||||
if (mOwningEventTarget->IsOnCurrentThread()) {
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Runnable> destroyRunnable =
|
||||
NewNonOwningRunnableMethod("CacheParentBridge::Destroy",
|
||||
this,
|
||||
&CacheParentBridge::Destroy);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(destroyRunnable,
|
||||
NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
// StorageDBParent::UsageParentBridge
|
||||
|
||||
namespace {
|
||||
|
@ -770,6 +1072,9 @@ private:
|
|||
}
|
||||
|
||||
mozilla::Unused << mParent->SendLoadUsage(mOriginScope, mUsage);
|
||||
|
||||
mParent = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -784,7 +1089,168 @@ void
|
|||
StorageDBParent::UsageParentBridge::LoadUsage(const int64_t aUsage)
|
||||
{
|
||||
RefPtr<UsageRunnable> r = new UsageRunnable(mParent, mOriginScope, aUsage);
|
||||
NS_DispatchToMainThread(r);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(r, NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
// XXX Fix me!
|
||||
// This should be just:
|
||||
// NS_IMPL_RELEASE_WITH_DESTROY(StorageDBParent::UsageParentBridge, Destroy)
|
||||
// But due to different strings used for refcount logging, this is done manually
|
||||
// for now.
|
||||
NS_IMETHODIMP_(MozExternalRefCountType)
|
||||
StorageDBParent::UsageParentBridge::Release(void)
|
||||
{
|
||||
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
|
||||
nsrefcnt count = --mRefCnt;
|
||||
NS_LOG_RELEASE(this, count, "StorageUsageBridge");
|
||||
if (count == 0) {
|
||||
Destroy();
|
||||
return 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void
|
||||
StorageDBParent::UsageParentBridge::Destroy()
|
||||
{
|
||||
if (mOwningEventTarget->IsOnCurrentThread()) {
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Runnable> destroyRunnable =
|
||||
NewNonOwningRunnableMethod("UsageParentBridge::Destroy",
|
||||
this,
|
||||
&UsageParentBridge::Destroy);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(destroyRunnable,
|
||||
NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
void
|
||||
StorageDBParent::
|
||||
ObserverSink::Start()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
NewRunnableMethod("StorageDBParent::ObserverSink::AddSink",
|
||||
this,
|
||||
&StorageDBParent::ObserverSink::AddSink);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
|
||||
}
|
||||
|
||||
void
|
||||
StorageDBParent::
|
||||
ObserverSink::Stop()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
mActor = nullptr;
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
NewRunnableMethod("StorageDBParent::ObserverSink::RemoveSink",
|
||||
this,
|
||||
&StorageDBParent::ObserverSink::RemoveSink);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
|
||||
}
|
||||
|
||||
void
|
||||
StorageDBParent::
|
||||
ObserverSink::AddSink()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
StorageObserver* observer = StorageObserver::Self();
|
||||
if (observer) {
|
||||
observer->AddSink(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StorageDBParent::
|
||||
ObserverSink::RemoveSink()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
StorageObserver* observer = StorageObserver::Self();
|
||||
if (observer) {
|
||||
observer->RemoveSink(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StorageDBParent::
|
||||
ObserverSink::Notify(const nsCString& aTopic,
|
||||
const nsString& aOriginAttributesPattern,
|
||||
const nsCString& aOriginScope)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (mActor) {
|
||||
mActor->Observe(aTopic, aOriginAttributesPattern, aOriginScope);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageDBParent::
|
||||
ObserverSink::Observe(const char* aTopic,
|
||||
const nsAString& aOriginAttributesPattern,
|
||||
const nsACString& aOriginScope)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
NewRunnableMethod<nsCString, nsString, nsCString>(
|
||||
"StorageDBParent::ObserverSink::Observe2",
|
||||
this,
|
||||
&StorageDBParent::ObserverSink::Notify,
|
||||
aTopic,
|
||||
aOriginAttributesPattern,
|
||||
aOriginScope);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
mOwningEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Exported functions
|
||||
******************************************************************************/
|
||||
|
||||
PBackgroundStorageParent*
|
||||
AllocPBackgroundStorageParent(const nsString& aProfilePath)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
return new StorageDBParent(aProfilePath);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
RecvPBackgroundStorageConstructor(PBackgroundStorageParent* aActor,
|
||||
const nsString& aProfilePath)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aActor);
|
||||
|
||||
auto* actor = static_cast<StorageDBParent*>(aActor);
|
||||
actor->Init();
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
bool
|
||||
DeallocPBackgroundStorageParent(PBackgroundStorageParent* aActor)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aActor);
|
||||
|
||||
StorageDBParent* actor = static_cast<StorageDBParent*>(aActor);
|
||||
actor->ReleaseIPDLReference();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
#ifndef mozilla_dom_StorageIPC_h
|
||||
#define mozilla_dom_StorageIPC_h
|
||||
|
||||
#include "mozilla/dom/PStorageChild.h"
|
||||
#include "mozilla/dom/PStorageParent.h"
|
||||
#include "mozilla/dom/PBackgroundStorageChild.h"
|
||||
#include "mozilla/dom/PBackgroundStorageParent.h"
|
||||
#include "StorageDBThread.h"
|
||||
#include "LocalStorageCache.h"
|
||||
#include "StorageObserver.h"
|
||||
|
@ -22,21 +22,29 @@ class OriginAttributesPattern;
|
|||
namespace dom {
|
||||
|
||||
class LocalStorageManager;
|
||||
class PBackgroundStorageParent;
|
||||
|
||||
// Child side of the IPC protocol, exposes as DB interface but
|
||||
// is responsible to send all requests to the parent process
|
||||
// and expects asynchronous answers. Those are then transparently
|
||||
// forwarded back to consumers on the child process.
|
||||
class StorageDBChild final : public StorageDBBridge
|
||||
, public PStorageChild
|
||||
class StorageDBChild final
|
||||
: public PBackgroundStorageChild
|
||||
{
|
||||
class ShutdownObserver;
|
||||
|
||||
virtual ~StorageDBChild();
|
||||
|
||||
public:
|
||||
explicit StorageDBChild(LocalStorageManager* aManager);
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release(void);
|
||||
static StorageDBChild*
|
||||
Get();
|
||||
|
||||
static StorageDBChild*
|
||||
GetOrCreate();
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(StorageDBChild);
|
||||
|
||||
void AddIPDLReference();
|
||||
void ReleaseIPDLReference();
|
||||
|
@ -68,17 +76,21 @@ public:
|
|||
}
|
||||
|
||||
virtual void AsyncClearMatchingOrigin(const nsACString& aOriginNoSuffix)
|
||||
{ /* NO-OP on the child process */ }
|
||||
{
|
||||
MOZ_CRASH("Shouldn't be called!");
|
||||
}
|
||||
|
||||
virtual void AsyncClearMatchingOriginAttributes(const OriginAttributesPattern& aPattern)
|
||||
{ /* NO-OP on the child process */ }
|
||||
{
|
||||
MOZ_CRASH("Shouldn't be called!");
|
||||
}
|
||||
|
||||
virtual void AsyncFlush()
|
||||
{ SendAsyncFlush(); }
|
||||
{
|
||||
MOZ_CRASH("Shouldn't be called!");
|
||||
}
|
||||
|
||||
virtual bool ShouldPreloadOrigin(const nsACString& aOriginNoSuffix);
|
||||
virtual void GetOriginsHavingData(InfallibleTArray<nsCString>* aOrigins)
|
||||
{ NS_NOTREACHED("Not implemented for child process"); }
|
||||
|
||||
private:
|
||||
mozilla::ipc::IPCResult RecvObserve(const nsCString& aTopic,
|
||||
|
@ -98,9 +110,6 @@ private:
|
|||
|
||||
nsTHashtable<nsCStringHashKey>& OriginsHavingData();
|
||||
|
||||
ThreadSafeAutoRefCnt mRefCnt;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
|
||||
// Held to get caches to forward answers to.
|
||||
RefPtr<LocalStorageManager> mManager;
|
||||
|
||||
|
@ -123,13 +132,17 @@ private:
|
|||
// LocalStorageCache consumer.
|
||||
// Also responsible for forwardning all chrome operation notifications
|
||||
// such as cookie cleaning etc to the child process.
|
||||
class StorageDBParent final : public PStorageParent
|
||||
, public StorageObserverSink
|
||||
class StorageDBParent final : public PBackgroundStorageParent
|
||||
{
|
||||
class ObserverSink;
|
||||
|
||||
virtual ~StorageDBParent();
|
||||
|
||||
public:
|
||||
StorageDBParent();
|
||||
explicit StorageDBParent(const nsString& aProfilePath);
|
||||
|
||||
void
|
||||
Init();
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release(void);
|
||||
|
@ -147,7 +160,8 @@ public:
|
|||
CacheParentBridge(StorageDBParent* aParentDB,
|
||||
const nsACString& aOriginSuffix,
|
||||
const nsACString& aOriginNoSuffix)
|
||||
: mParent(aParentDB)
|
||||
: mOwningEventTarget(GetCurrentThreadSerialEventTarget())
|
||||
, mParent(aParentDB)
|
||||
, mOriginSuffix(aOriginSuffix), mOriginNoSuffix(aOriginNoSuffix)
|
||||
, mLoaded(false), mLoadedCount(0) {}
|
||||
virtual ~CacheParentBridge() {}
|
||||
|
@ -167,7 +181,14 @@ public:
|
|||
virtual void LoadDone(nsresult aRv);
|
||||
virtual void LoadWait();
|
||||
|
||||
NS_IMETHOD_(void)
|
||||
Release(void);
|
||||
|
||||
private:
|
||||
void
|
||||
Destroy();
|
||||
|
||||
nsCOMPtr<nsISerialEventTarget> mOwningEventTarget;
|
||||
RefPtr<StorageDBParent> mParent;
|
||||
nsCString mOriginSuffix, mOriginNoSuffix;
|
||||
bool mLoaded;
|
||||
|
@ -180,14 +201,23 @@ public:
|
|||
public:
|
||||
UsageParentBridge(StorageDBParent* aParentDB,
|
||||
const nsACString& aOriginScope)
|
||||
: mParent(aParentDB), mOriginScope(aOriginScope) {}
|
||||
: mOwningEventTarget(GetCurrentThreadSerialEventTarget())
|
||||
, mParent(aParentDB)
|
||||
, mOriginScope(aOriginScope) {}
|
||||
virtual ~UsageParentBridge() {}
|
||||
|
||||
// StorageUsageBridge
|
||||
virtual const nsCString& OriginScope() { return mOriginScope; }
|
||||
virtual void LoadUsage(const int64_t usage);
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType)
|
||||
Release(void);
|
||||
|
||||
private:
|
||||
void
|
||||
Destroy();
|
||||
|
||||
nsCOMPtr<nsISerialEventTarget> mOwningEventTarget;
|
||||
RefPtr<StorageDBParent> mParent;
|
||||
nsCString mOriginScope;
|
||||
};
|
||||
|
@ -195,6 +225,8 @@ public:
|
|||
private:
|
||||
// IPC
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
mozilla::ipc::IPCResult RecvDeleteMe() override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvAsyncPreload(const nsCString& aOriginSuffix,
|
||||
const nsCString& aOriginNoSuffix,
|
||||
const bool& aPriority) override;
|
||||
|
@ -220,15 +252,30 @@ private:
|
|||
const nsCString& aOriginNoSuffix) override;
|
||||
mozilla::ipc::IPCResult RecvAsyncFlush() override;
|
||||
|
||||
// StorageObserverSink
|
||||
virtual nsresult Observe(const char* aTopic,
|
||||
const nsAString& aOriginAttrPattern,
|
||||
const nsACString& aOriginScope) override;
|
||||
mozilla::ipc::IPCResult RecvStartup() override;
|
||||
mozilla::ipc::IPCResult RecvClearAll() override;
|
||||
mozilla::ipc::IPCResult RecvClearMatchingOrigin(
|
||||
const nsCString& aOriginNoSuffix) override;
|
||||
mozilla::ipc::IPCResult RecvClearMatchingOriginAttributes(
|
||||
const OriginAttributesPattern& aPattern) override;
|
||||
|
||||
void Observe(const nsCString& aTopic,
|
||||
const nsString& aOriginAttrPattern,
|
||||
const nsCString& aOriginScope);
|
||||
|
||||
private:
|
||||
CacheParentBridge* NewCache(const nsACString& aOriginSuffix,
|
||||
const nsACString& aOriginNoSuffix);
|
||||
|
||||
RefPtr<ObserverSink> mObserverSink;
|
||||
|
||||
// A hack to deal with deadlock between the parent process main thread and
|
||||
// background thread when invoking StorageDBThread::GetOrCreate because it
|
||||
// cannot safely perform a synchronous dispatch back to the main thread
|
||||
// (because we are already synchronously doing things on the stack).
|
||||
// Populated for the same process actors, empty for other process actors.
|
||||
nsString mProfilePath;
|
||||
|
||||
ThreadSafeAutoRefCnt mRefCnt;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
|
||||
|
@ -236,6 +283,16 @@ private:
|
|||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
PBackgroundStorageParent*
|
||||
AllocPBackgroundStorageParent(const nsString& aProfilePath);
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
RecvPBackgroundStorageConstructor(PBackgroundStorageParent* aActor,
|
||||
const nsString& aProfilePath);
|
||||
|
||||
bool
|
||||
DeallocPBackgroundStorageParent(PBackgroundStorageParent* aActor);
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -70,8 +70,9 @@ StorageObserver::Init()
|
|||
|
||||
// Shutdown
|
||||
obs->AddObserver(sSelf, "profile-after-change", true);
|
||||
obs->AddObserver(sSelf, "profile-before-change", true);
|
||||
obs->AddObserver(sSelf, "xpcom-shutdown", true);
|
||||
if (XRE_IsParentProcess()) {
|
||||
obs->AddObserver(sSelf, "profile-before-change", true);
|
||||
}
|
||||
|
||||
// Observe low device storage notifications.
|
||||
obs->AddObserver(sSelf, "disk-space-watcher", true);
|
||||
|
@ -145,6 +146,12 @@ StorageObserver::Notify(const char* aTopic,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
StorageObserver::NoteBackgroundThread(nsIEventTarget* aBackgroundThread)
|
||||
{
|
||||
mBackgroundThread = aBackgroundThread;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StorageObserver::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
|
@ -154,6 +161,8 @@ StorageObserver::Observe(nsISupports* aSubject,
|
|||
|
||||
// Start the thread that opens the database.
|
||||
if (!strcmp(aTopic, kStartupTopic)) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->RemoveObserver(this, kStartupTopic);
|
||||
|
||||
|
@ -169,6 +178,8 @@ StorageObserver::Observe(nsISupports* aSubject,
|
|||
|
||||
// Timer callback used to start the database a short timer after startup
|
||||
if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject);
|
||||
if (!timer) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
@ -177,8 +188,12 @@ StorageObserver::Observe(nsISupports* aSubject,
|
|||
if (timer == mDBThreadStartDelayTimer) {
|
||||
mDBThreadStartDelayTimer = nullptr;
|
||||
|
||||
StorageDBBridge* db = LocalStorageCache::StartDatabase();
|
||||
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
|
||||
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
|
||||
if (NS_WARN_IF(!storageChild)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
storageChild->SendStartup();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -190,10 +205,16 @@ StorageObserver::Observe(nsISupports* aSubject,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
StorageDBBridge* db = LocalStorageCache::StartDatabase();
|
||||
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
|
||||
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
|
||||
if (NS_WARN_IF(!storageChild)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
db->AsyncClearAll();
|
||||
storageChild->AsyncClearAll();
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
storageChild->SendClearAll();
|
||||
}
|
||||
|
||||
Notify("cookie-cleared");
|
||||
|
||||
|
@ -254,10 +275,16 @@ StorageObserver::Observe(nsISupports* aSubject,
|
|||
}
|
||||
|
||||
if (!strcmp(aTopic, "extension:purge-localStorage")) {
|
||||
StorageDBBridge* db = LocalStorageCache::StartDatabase();
|
||||
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
|
||||
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
|
||||
if (NS_WARN_IF(!storageChild)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
db->AsyncClearAll();
|
||||
storageChild->AsyncClearAll();
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
storageChild->SendClearAll();
|
||||
}
|
||||
|
||||
Notify("extension:purge-localStorage-caches");
|
||||
|
||||
|
@ -287,10 +314,14 @@ StorageObserver::Observe(nsISupports* aSubject,
|
|||
rv = CreateReversedDomain(aceDomain, originScope);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
StorageDBBridge* db = LocalStorageCache::StartDatabase();
|
||||
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
|
||||
if (XRE_IsParentProcess()) {
|
||||
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
|
||||
if (NS_WARN_IF(!storageChild)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
db->AsyncClearMatchingOrigin(originScope);
|
||||
storageChild->SendClearMatchingOrigin(originScope);
|
||||
}
|
||||
|
||||
Notify("domain-data-cleared", EmptyString(), originScope);
|
||||
|
||||
|
@ -306,16 +337,20 @@ StorageObserver::Observe(nsISupports* aSubject,
|
|||
|
||||
// Clear data of the origins whose prefixes will match the suffix.
|
||||
if (!strcmp(aTopic, "clear-origin-attributes-data")) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
OriginAttributesPattern pattern;
|
||||
if (!pattern.Init(nsDependentString(aData))) {
|
||||
NS_ERROR("Cannot parse origin attributes pattern");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
StorageDBBridge* db = LocalStorageCache::StartDatabase();
|
||||
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
|
||||
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
|
||||
if (NS_WARN_IF(!storageChild)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
db->AsyncClearMatchingOriginAttributes(pattern);
|
||||
storageChild->SendClearMatchingOriginAttributes(pattern);
|
||||
|
||||
Notify("origin-attr-pattern-cleared", nsDependentString(aData));
|
||||
|
||||
|
@ -328,11 +363,20 @@ StorageObserver::Observe(nsISupports* aSubject,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, "profile-before-change") ||
|
||||
!strcmp(aTopic, "xpcom-shutdown")) {
|
||||
rv = LocalStorageCache::StopDatabase();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Error while stopping Storage DB background thread");
|
||||
if (!strcmp(aTopic, "profile-before-change")) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
if (mBackgroundThread) {
|
||||
bool done = false;
|
||||
|
||||
RefPtr<StorageDBThread::ShutdownRunnable> shutdownRunnable =
|
||||
new StorageDBThread::ShutdownRunnable(done);
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
mBackgroundThread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL));
|
||||
|
||||
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return done; }));
|
||||
|
||||
mBackgroundThread = nullptr;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -350,11 +394,13 @@ StorageObserver::Observe(nsISupports* aSubject,
|
|||
|
||||
#ifdef DOM_STORAGE_TESTS
|
||||
if (!strcmp(aTopic, "domstorage-test-flush-force")) {
|
||||
StorageDBBridge* db = LocalStorageCache::GetDatabase();
|
||||
if (db) {
|
||||
db->AsyncFlush();
|
||||
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
|
||||
if (NS_WARN_IF(!storageChild)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
storageChild->SendAsyncFlush();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,9 @@ public:
|
|||
const nsAString& aOriginAttributesPattern = EmptyString(),
|
||||
const nsACString& aOriginScope = EmptyCString());
|
||||
|
||||
void
|
||||
NoteBackgroundThread(nsIEventTarget* aBackgroundThread);
|
||||
|
||||
private:
|
||||
virtual ~StorageObserver() {}
|
||||
|
||||
|
@ -58,6 +61,8 @@ private:
|
|||
|
||||
static StorageObserver* sSelf;
|
||||
|
||||
nsCOMPtr<nsIEventTarget> mBackgroundThread;
|
||||
|
||||
// Weak references
|
||||
nsTArray<StorageObserverSink*> mSinks;
|
||||
nsCOMPtr<nsITimer> mDBThreadStartDelayTimer;
|
||||
|
|
|
@ -34,7 +34,7 @@ UNIFIED_SOURCES += [
|
|||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'PStorage.ipdl',
|
||||
'PBackgroundStorage.ipdl',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
|
|
@ -155,12 +155,12 @@ async function verifyTabPreload(knownTab, expectStorageExists) {
|
|||
* Instruct the given tab to execute the given series of mutations. For
|
||||
* simplicity, the mutations representation matches the expected events rep.
|
||||
*/
|
||||
async function mutateTabStorage(knownTab, mutations) {
|
||||
async function mutateTabStorage(knownTab, mutations, sentinelValue) {
|
||||
await ContentTask.spawn(
|
||||
knownTab.tab.linkedBrowser,
|
||||
{ mutations },
|
||||
{ mutations, sentinelValue },
|
||||
function(args) {
|
||||
return content.wrappedJSObject.mutateStorage(args.mutations);
|
||||
return content.wrappedJSObject.mutateStorage(args);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -169,25 +169,33 @@ async function mutateTabStorage(knownTab, mutations) {
|
|||
* received events. verifyTabStorageEvents is the corresponding method to
|
||||
* check and assert the recorded events.
|
||||
*/
|
||||
async function recordTabStorageEvents(knownTab) {
|
||||
async function recordTabStorageEvents(knownTab, sentinelValue) {
|
||||
await ContentTask.spawn(
|
||||
knownTab.tab.linkedBrowser,
|
||||
{},
|
||||
function() {
|
||||
return content.wrappedJSObject.listenForStorageEvents();
|
||||
sentinelValue,
|
||||
function(sentinelValue) {
|
||||
return content.wrappedJSObject.listenForStorageEvents(sentinelValue);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current localStorage contents perceived by the tab and assert
|
||||
* that they match the provided expected state.
|
||||
*
|
||||
* If maybeSentinel is non-null, it's assumed to be a string that identifies the
|
||||
* value we should be waiting for the sentinel key to take on. This is
|
||||
* necessary because we cannot make any assumptions about when state will be
|
||||
* propagated to the given process. See the comments in
|
||||
* page_localstorage_e10s.js for more context. In general, a sentinel value is
|
||||
* required for correctness unless the process in question is the one where the
|
||||
* writes were performed or verifyTabStorageEvents was used.
|
||||
*/
|
||||
async function verifyTabStorageState(knownTab, expectedState) {
|
||||
async function verifyTabStorageState(knownTab, expectedState, maybeSentinel) {
|
||||
let actualState = await ContentTask.spawn(
|
||||
knownTab.tab.linkedBrowser,
|
||||
{},
|
||||
function() {
|
||||
return content.wrappedJSObject.getStorageState();
|
||||
maybeSentinel,
|
||||
function(maybeSentinel) {
|
||||
return content.wrappedJSObject.getStorageState(maybeSentinel);
|
||||
});
|
||||
|
||||
for (let [expectedKey, expectedValue] of Object.entries(expectedState)) {
|
||||
|
@ -205,6 +213,9 @@ async function verifyTabStorageState(knownTab, expectedState) {
|
|||
* Retrieve and clear the storage events recorded by the tab and assert that
|
||||
* they match the provided expected events. For simplicity, the expected events
|
||||
* representation is the same as that used by mutateTabStorage.
|
||||
*
|
||||
* Note that by convention for test readability we are passed a 3rd argument of
|
||||
* the sentinel value, but we don't actually care what it is.
|
||||
*/
|
||||
async function verifyTabStorageEvents(knownTab, expectedEvents) {
|
||||
let actualEvents = await ContentTask.spawn(
|
||||
|
@ -318,9 +329,12 @@ add_task(async function() {
|
|||
await verifyTabPreload(readerTab, false);
|
||||
|
||||
// - Configure the tabs.
|
||||
await recordTabStorageEvents(listenerTab);
|
||||
const initialSentinel = 'initial';
|
||||
const noSentinelCheck = null;
|
||||
await recordTabStorageEvents(listenerTab, initialSentinel);
|
||||
|
||||
// - Issue the initial batch of writes and verify.
|
||||
info("initial writes");
|
||||
const initialWriteMutations = [
|
||||
//[key (null=clear), newValue (null=delete), oldValue (verification)]
|
||||
["getsCleared", "1", null],
|
||||
|
@ -341,14 +355,40 @@ add_task(async function() {
|
|||
alsoStays: "6"
|
||||
};
|
||||
|
||||
await mutateTabStorage(writerTab, initialWriteMutations);
|
||||
await mutateTabStorage(writerTab, initialWriteMutations, initialSentinel);
|
||||
|
||||
await verifyTabStorageState(writerTab, initialWriteState);
|
||||
await verifyTabStorageEvents(listenerTab, initialWriteMutations);
|
||||
await verifyTabStorageState(listenerTab, initialWriteState);
|
||||
await verifyTabStorageState(readerTab, initialWriteState);
|
||||
// We expect the writer tab to have the correct state because it just did the
|
||||
// writes. We do not perform a sentinel-check because the writes should be
|
||||
// locally available and consistent.
|
||||
await verifyTabStorageState(writerTab, initialWriteState, noSentinelCheck);
|
||||
// We expect the listener tab to have heard all events despite preload not
|
||||
// having occurred and despite not issuing any reads or writes itself. We
|
||||
// intentionally check the events before the state because we're most
|
||||
// interested in adding the listener having had a side-effect of subscribing
|
||||
// to changes for the process.
|
||||
//
|
||||
// We ensure it had a chance to hear all of the events because we told
|
||||
// recordTabStorageEvents to listen for the given sentinel. The state check
|
||||
// then does not need to do a sentinel check.
|
||||
await verifyTabStorageEvents(
|
||||
listenerTab, initialWriteMutations, initialSentinel);
|
||||
await verifyTabStorageState(
|
||||
listenerTab, initialWriteState, noSentinelCheck);
|
||||
// We expect the reader tab to retrieve the current localStorage state from
|
||||
// the database. Because of the above checks, we are confident that the
|
||||
// writes have hit PBackground and therefore that the (synchronous) state
|
||||
// retrieval contains all the data we need. No sentinel-check is required.
|
||||
await verifyTabStorageState(readerTab, initialWriteState, noSentinelCheck);
|
||||
|
||||
// - Issue second set of writes from lateWriteThenListen
|
||||
// This tests that our new tab that begins by issuing only writes is building
|
||||
// on top of the existing state (although we don't verify that until after the
|
||||
// next set of mutations). We also verify that the initial "writerTab" that
|
||||
// was our first tab and started with only writes sees the writes, even though
|
||||
// it did not add an event listener.
|
||||
|
||||
info("late writes");
|
||||
const lateWriteSentinel = 'lateWrite';
|
||||
const lateWriteMutations = [
|
||||
["lateStays", "10", null],
|
||||
["lateClobbered", "latePre", null],
|
||||
|
@ -361,15 +401,24 @@ add_task(async function() {
|
|||
lateClobbered: "lastPost"
|
||||
});
|
||||
|
||||
await mutateTabStorage(lateWriteThenListenTab, lateWriteMutations);
|
||||
await recordTabStorageEvents(lateWriteThenListenTab);
|
||||
await recordTabStorageEvents(listenerTab, lateWriteSentinel);
|
||||
|
||||
await verifyTabStorageState(writerTab, lateWriteState);
|
||||
await verifyTabStorageEvents(listenerTab, lateWriteMutations);
|
||||
await verifyTabStorageState(listenerTab, lateWriteState);
|
||||
await verifyTabStorageState(readerTab, lateWriteState);
|
||||
await mutateTabStorage(
|
||||
lateWriteThenListenTab, lateWriteMutations, lateWriteSentinel);
|
||||
|
||||
// Verify the writer tab saw the writes. It has to wait for the sentinel to
|
||||
// appear before checking.
|
||||
await verifyTabStorageState(writerTab, lateWriteState, lateWriteSentinel);
|
||||
// Wait for the sentinel event before checking the events and then the state.
|
||||
await verifyTabStorageEvents(
|
||||
listenerTab, lateWriteMutations, lateWriteSentinel);
|
||||
await verifyTabStorageState(listenerTab, lateWriteState, noSentinelCheck);
|
||||
// We need to wait for the sentinel to show up for the reader.
|
||||
await verifyTabStorageState(readerTab, lateWriteState, lateWriteSentinel);
|
||||
|
||||
// - Issue last set of writes from writerTab.
|
||||
info("last set of writes");
|
||||
const lastWriteSentinel = 'lastWrite';
|
||||
const lastWriteMutations = [
|
||||
["lastStays", "20", null],
|
||||
["lastDeleted", "21", null],
|
||||
|
@ -382,14 +431,24 @@ add_task(async function() {
|
|||
lastClobbered: "lastPost"
|
||||
});
|
||||
|
||||
await mutateTabStorage(writerTab, lastWriteMutations);
|
||||
await recordTabStorageEvents(listenerTab, lastWriteSentinel);
|
||||
await recordTabStorageEvents(lateWriteThenListenTab, lastWriteSentinel);
|
||||
|
||||
await verifyTabStorageState(writerTab, lastWriteState);
|
||||
await verifyTabStorageEvents(listenerTab, lastWriteMutations);
|
||||
await verifyTabStorageState(listenerTab, lastWriteState);
|
||||
await verifyTabStorageState(readerTab, lastWriteState);
|
||||
await verifyTabStorageEvents(lateWriteThenListenTab, lastWriteMutations);
|
||||
await verifyTabStorageState(lateWriteThenListenTab, lastWriteState);
|
||||
await mutateTabStorage(writerTab, lastWriteMutations, lastWriteSentinel);
|
||||
|
||||
// The writer performed the writes, no need to wait for the sentinel.
|
||||
await verifyTabStorageState(writerTab, lastWriteState, noSentinelCheck);
|
||||
// Wait for the sentinel event to be received, then check.
|
||||
await verifyTabStorageEvents(
|
||||
listenerTab, lastWriteMutations, lastWriteSentinel);
|
||||
await verifyTabStorageState(listenerTab, lastWriteState, noSentinelCheck);
|
||||
// We need to wait for the sentinel to show up for the reader.
|
||||
await verifyTabStorageState(readerTab, lastWriteState, lastWriteSentinel);
|
||||
// Wait for the sentinel event to be received, then check.
|
||||
await verifyTabStorageEvents(
|
||||
lateWriteThenListenTab, lastWriteMutations, lastWriteSentinel);
|
||||
await verifyTabStorageState(
|
||||
lateWriteThenListenTab, lastWriteState, noSentinelCheck);
|
||||
|
||||
// - Force a LocalStorage DB flush so mOriginsHavingData is updated.
|
||||
// mOriginsHavingData is only updated when the storage thread runs its
|
||||
|
@ -397,9 +456,11 @@ add_task(async function() {
|
|||
// that a flush has occurred before moving on to the next step,
|
||||
// mOriginsHavingData may not include our origin when it's sent down to the
|
||||
// child process.
|
||||
info("flush to make preload check work");
|
||||
await triggerAndWaitForLocalStorageFlush();
|
||||
|
||||
// - Open a fresh tab and make sure it sees the precache/preload
|
||||
info("late open preload check");
|
||||
const lateOpenSeesPreload =
|
||||
await openTestTabInOwnProcess("lateOpenSeesPreload", knownTabs);
|
||||
await verifyTabPreload(lateOpenSeesPreload, true);
|
||||
|
|
|
@ -5,23 +5,78 @@
|
|||
<script>
|
||||
/**
|
||||
* Helper page used by browser_localStorage_e10s.js.
|
||||
*
|
||||
* We expose methods to be invoked by ContentTask.spawn() calls.
|
||||
* ContentTask.spawn() uses the message manager and is PContent-based. When
|
||||
* LocalStorage was PContent-managed, ordering was inherently ensured so we
|
||||
* could assume each page had already received all relevant events. Now some
|
||||
* explicit type of coordination is required.
|
||||
*
|
||||
* This gets complicated because:
|
||||
* - LocalStorage is an ugly API that gives us almost unlimited implementation
|
||||
* flexibility in the face of multiple processes. It's also an API that sites
|
||||
* may misuse which may encourage us to leverage that flexibility in the
|
||||
* future to improve performance at the expense of propagation latency, and
|
||||
* possibly involving content-observable coalescing of events.
|
||||
* - The Quantum DOM effort and its event labeling and separate task queues and
|
||||
* green threading and current LocalStorage implementation mean that using
|
||||
* other PBackground-based APIs such as BroadcastChannel may not provide
|
||||
* reliable ordering guarantees. Specifically, it's hard to guarantee that
|
||||
* a BroadcastChannel postMessage() issued after a series of LocalStorage
|
||||
* writes won't be received by the target window before the writes are
|
||||
* perceived. At least not without constraining the implementations of both
|
||||
* APIs.
|
||||
* - Some of our tests explicitly want to verify LocalStorage behavior without
|
||||
* having a "storage" listener, so we can't add a storage listener if the test
|
||||
* didn't already want one.
|
||||
*
|
||||
* We use 2 approaches for coordination:
|
||||
* 1. If we're already listening for events, we listen for the sentinel value to
|
||||
* be written. This is efficient and appropriate in this case.
|
||||
* 2. If we're not listening for events, we use setTimeout(0) to poll the
|
||||
* localStorage key and value until it changes to our expected value.
|
||||
* setTimeout(0) eventually clamps to setTimeout(4), so in the event we are
|
||||
* experiencing delays, we have reasonable, non-CPU-consuming back-off in
|
||||
* place that leaves the CPU free to time out and fail our test if something
|
||||
* broke. This is ugly but makes us less brittle.
|
||||
*
|
||||
* Both of these involve mutateStorage writing the sentinel value at the end of
|
||||
* the batch. All of our result-returning methods accordingly filter out the
|
||||
* sentinel key/value pair.
|
||||
**/
|
||||
var pageName = document.location.search.substring(1);
|
||||
window.addEventListener(
|
||||
"load",
|
||||
() => { document.getElementById("pageNameH").textContent = pageName; });
|
||||
|
||||
var recordedEvents = null;
|
||||
function storageListener(event) {
|
||||
recordedEvents.push([event.key, event.newValue, event.oldValue]);
|
||||
// Key that conveys the end of a write batch. Filtered out from state and
|
||||
// events.
|
||||
const SENTINEL_KEY = 'WRITE_BATCH_SENTINEL';
|
||||
|
||||
var storageEventsPromise = null;
|
||||
function listenForStorageEvents(sentinelValue) {
|
||||
const recordedEvents = [];
|
||||
storageEventsPromise = new Promise(function(resolve, reject) {
|
||||
window.addEventListener(
|
||||
"storage",
|
||||
function thisHandler(event) {
|
||||
if (event.key === SENTINEL_KEY) {
|
||||
// There should be no way for this to have the wrong value, but reject
|
||||
// if it is wrong.
|
||||
if (event.newValue === sentinelValue) {
|
||||
window.removeEventListener("storage", thisHandler);
|
||||
resolve(recordedEvents);
|
||||
} else {
|
||||
reject(event.newValue);
|
||||
}
|
||||
} else {
|
||||
recordedEvents.push([event.key, event.newValue, event.oldValue]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function listenForStorageEvents() {
|
||||
recordedEvents = [];
|
||||
window.addEventListener("storage", storageListener);
|
||||
}
|
||||
|
||||
function mutateStorage(mutations) {
|
||||
function mutateStorage({ mutations, sentinelValue }) {
|
||||
mutations.forEach(function([key, value]) {
|
||||
if (key !== null) {
|
||||
if (value === null) {
|
||||
|
@ -33,22 +88,48 @@ function mutateStorage(mutations) {
|
|||
localStorage.clear();
|
||||
}
|
||||
});
|
||||
localStorage.setItem(SENTINEL_KEY, sentinelValue);
|
||||
}
|
||||
|
||||
function getStorageState() {
|
||||
// Returns a promise that is resolve when the sentinel key has taken on the
|
||||
// sentinel value. Oddly structured to make sure promises don't let us
|
||||
// accidentally side-step the timeout clamping logic.
|
||||
function waitForSentinelValue(sentinelValue) {
|
||||
return new Promise(function(resolve) {
|
||||
function checkFunc() {
|
||||
if (localStorage.getItem(SENTINEL_KEY) === sentinelValue) {
|
||||
resolve();
|
||||
} else {
|
||||
// I believe linters will only yell at us if we use a non-zero constant.
|
||||
// Other forms of back-off were considered, including attempting to
|
||||
// issue a round-trip through PBackground, but that still potentially
|
||||
// runs afoul of labeling while also making us dependent on unrelated
|
||||
// APIs.
|
||||
setTimeout(checkFunc, 0);
|
||||
}
|
||||
}
|
||||
checkFunc();
|
||||
});
|
||||
}
|
||||
|
||||
async function getStorageState(maybeSentinel) {
|
||||
if (maybeSentinel) {
|
||||
await waitForSentinelValue(maybeSentinel);
|
||||
}
|
||||
|
||||
let numKeys = localStorage.length;
|
||||
let state = {};
|
||||
for (var iKey = 0; iKey < numKeys; iKey++) {
|
||||
let key = localStorage.key(iKey);
|
||||
state[key] = localStorage.getItem(key);
|
||||
if (key !== SENTINEL_KEY) {
|
||||
state[key] = localStorage.getItem(key);
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
function returnAndClearStorageEvents() {
|
||||
let loggedEvents = recordedEvents;
|
||||
recordedEvents = [];
|
||||
return loggedEvents;
|
||||
return storageEventsPromise;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
|
|
@ -317,12 +317,6 @@ partial interface Window {
|
|||
attribute EventHandler onuserproximity;
|
||||
attribute EventHandler ondevicelight;
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
attribute EventHandler onmoztimechange;
|
||||
attribute EventHandler onmoznetworkupload;
|
||||
attribute EventHandler onmoznetworkdownload;
|
||||
#endif
|
||||
|
||||
void dump(DOMString str);
|
||||
|
||||
/**
|
||||
|
|
|
@ -1131,14 +1131,6 @@ if CONFIG['MOZ_BUILD_APP'] in ['browser', 'mobile/android', 'xulrunner']:
|
|||
'External.webidl',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_B2G']:
|
||||
WEBIDL_FILES += [
|
||||
'MozApplicationEvent.webidl'
|
||||
]
|
||||
GENERATED_EVENTS_WEBIDL_FILES += [
|
||||
'MozApplicationEvent.webidl'
|
||||
]
|
||||
|
||||
if CONFIG['ACCESSIBILITY']:
|
||||
WEBIDL_FILES += [
|
||||
'AccessibleNode.webidl',
|
||||
|
|
|
@ -1802,18 +1802,6 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
|
|||
id = aID;
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
// When we do the initial addition of the permissions we don't want to
|
||||
// inherit session specific permissions from other tabs or apps
|
||||
// so we ignore them and set the permission to PROMPT_ACTION if it was
|
||||
// previously allowed or denied by the user.
|
||||
if (aIgnoreSessionPermissions &&
|
||||
aExpireType == nsIPermissionManager::EXPIRE_SESSION) {
|
||||
aPermission = nsIPermissionManager::PROMPT_ACTION;
|
||||
aExpireType = nsIPermissionManager::EXPIRE_NEVER;
|
||||
}
|
||||
#endif // MOZ_B2G
|
||||
|
||||
entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission,
|
||||
aExpireType, aExpireTime,
|
||||
aModificationTime));
|
||||
|
|
|
@ -885,7 +885,7 @@ class DrawTargetCapture;
|
|||
* may be used either through a Snapshot or by flushing the target and directly
|
||||
* accessing the backing store a DrawTarget was created with.
|
||||
*/
|
||||
class DrawTarget : public RefCounted<DrawTarget>
|
||||
class DrawTarget : public external::AtomicRefCounted<DrawTarget>
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTarget)
|
||||
|
|
|
@ -35,9 +35,6 @@ namespace gl {
|
|||
|
||||
StaticMutex GLLibraryEGL::sMutex;
|
||||
GLLibraryEGL sEGLLibrary;
|
||||
#ifdef MOZ_B2G
|
||||
MOZ_THREAD_LOCAL(EGLContext) GLLibraryEGL::sCurrentContext;
|
||||
#endif
|
||||
|
||||
// should match the order of EGLExtensions, and be null-terminated.
|
||||
static const char* sEGLExtensionNames[] = {
|
||||
|
@ -320,11 +317,6 @@ GLLibraryEGL::EnsureInitialized(bool forceAccel, nsACString* const out_failureId
|
|||
|
||||
mozilla::ScopedGfxFeatureReporter reporter("EGL");
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
if (!sCurrentContext.init())
|
||||
MOZ_CRASH("GFX: Tls init failed");
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
if (!mEGLLibrary) {
|
||||
// On Windows, the GLESv2, EGL and DXSDK libraries are shipped with libxul and
|
||||
|
|
|
@ -486,32 +486,12 @@ private:
|
|||
} mSymbols;
|
||||
|
||||
public:
|
||||
#ifdef MOZ_B2G
|
||||
EGLContext CachedCurrentContext() {
|
||||
return sCurrentContext.get();
|
||||
}
|
||||
void UnsetCachedCurrentContext() {
|
||||
sCurrentContext.set(nullptr);
|
||||
}
|
||||
void SetCachedCurrentContext(EGLContext aCtx) {
|
||||
sCurrentContext.set(aCtx);
|
||||
}
|
||||
bool CachedCurrentContextMatches() {
|
||||
return sCurrentContext.get() == fGetCurrentContext();
|
||||
}
|
||||
|
||||
private:
|
||||
static MOZ_THREAD_LOCAL(EGLContext) sCurrentContext;
|
||||
public:
|
||||
|
||||
#else
|
||||
EGLContext CachedCurrentContext() {
|
||||
return nullptr;
|
||||
}
|
||||
void UnsetCachedCurrentContext() {}
|
||||
void SetCachedCurrentContext(EGLContext aCtx) { }
|
||||
bool CachedCurrentContextMatches() { return true; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool mInitialized;
|
||||
|
|
|
@ -35,6 +35,7 @@ var touch_action_prefs = basic_pan_prefs.slice(); // make a copy
|
|||
touch_action_prefs.push(["layout.css.touch_action.enabled", true]);
|
||||
|
||||
var isWindows = (getPlatform() == "windows");
|
||||
touch_action_prefs.push(["apz.test.fails_with_native_injection", isWindows]);
|
||||
|
||||
var subtests = [
|
||||
// Simple tests to exercise basic panning behaviour
|
||||
|
|
|
@ -1075,7 +1075,8 @@ TextureClient::CreateForDrawing(TextureForwarder* aAllocator,
|
|||
TextureData* data = nullptr;
|
||||
|
||||
#ifdef XP_WIN
|
||||
if (aLayersBackend == LayersBackend::LAYERS_D3D11 &&
|
||||
if ((aLayersBackend == LayersBackend::LAYERS_D3D11 ||
|
||||
aLayersBackend == LayersBackend::LAYERS_WR) &&
|
||||
(moz2DBackend == gfx::BackendType::DIRECT2D ||
|
||||
moz2DBackend == gfx::BackendType::DIRECT2D1_1 ||
|
||||
(!!(aAllocFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT) &&
|
||||
|
|
|
@ -327,6 +327,9 @@ DXGITextureData::PrepareDrawTargetInLock(OpenMode aMode)
|
|||
}
|
||||
}
|
||||
|
||||
// Reset transform
|
||||
mDrawTarget->SetTransform(Matrix());
|
||||
|
||||
if (mNeedsClear) {
|
||||
mDrawTarget->ClearRect(Rect(0, 0, mSize.width, mSize.height));
|
||||
mNeedsClear = false;
|
||||
|
|
|
@ -673,6 +673,14 @@ WebRenderLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback
|
|||
bool sync = mTarget != nullptr;
|
||||
mLatestTransactionId = mTransactionIdAllocator->GetTransactionId(/*aThrottle*/ true);
|
||||
|
||||
// Skip the synchronization for buffer since we also skip the painting during
|
||||
// device-reset status.
|
||||
if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
|
||||
if (WrBridge()->GetSyncObject() &&
|
||||
WrBridge()->GetSyncObject()->IsSyncObjectValid()) {
|
||||
WrBridge()->GetSyncObject()->Synchronize();
|
||||
}
|
||||
}
|
||||
{
|
||||
AutoProfilerTracing
|
||||
tracing("Paint", sync ? "ForwardDPTransactionSync":"ForwardDPTransaction");
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include <d3d10_1.h>
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/gfxVars.h"
|
||||
|
||||
#include "nsMemory.h"
|
||||
|
||||
|
@ -478,6 +479,10 @@ gfxWindowsPlatform::GetContentBackendFor(mozilla::layers::LayersBackend aLayers)
|
|||
return defaultBackend;
|
||||
}
|
||||
|
||||
if (aLayers == LayersBackend::LAYERS_WR && gfx::gfxVars::UseWebRenderANGLE()) {
|
||||
return defaultBackend;
|
||||
}
|
||||
|
||||
if (defaultBackend == BackendType::DIRECT2D1_1) {
|
||||
// We can't have D2D without D3D11 layers, so fallback to Skia.
|
||||
return BackendType::SKIA;
|
||||
|
|
|
@ -33,11 +33,11 @@ interface nsIStringBundle : nsISupports
|
|||
[noscript, binaryname(GetStringFromName)]
|
||||
AString GetStringFromNameCpp(in string aName);
|
||||
|
||||
// this is kind of like smprintf - except that you can
|
||||
// this is kind of like ssprintf - except that you can
|
||||
// only pass it unicode strings, using the %S formatting character.
|
||||
// the id or name should refer to a string in the bundle that
|
||||
// uses %S.. do NOT try to use any other types.
|
||||
// this uses nsTextFormatter::smprintf to do the dirty work.
|
||||
// this uses nsTextFormatter::ssprintf to do the dirty work.
|
||||
AString formatStringFromID(in long aID,
|
||||
[array, size_is(length)] in wstring params,
|
||||
in unsigned long length);
|
||||
|
|
|
@ -25,9 +25,11 @@
|
|||
#include "mozilla/dom/ipc/IPCBlobInputStreamChild.h"
|
||||
#include "mozilla/dom/ipc/PendingIPCBlobChild.h"
|
||||
#include "mozilla/dom/quota/PQuotaChild.h"
|
||||
#include "mozilla/dom/StorageIPC.h"
|
||||
#include "mozilla/dom/GamepadEventChannelChild.h"
|
||||
#include "mozilla/dom/GamepadTestChannelChild.h"
|
||||
#include "mozilla/dom/MessagePortChild.h"
|
||||
#include "mozilla/dom/LocalStorage.h"
|
||||
#include "mozilla/ipc/IPCStreamAlloc.h"
|
||||
#include "mozilla/ipc/PBackgroundTestChild.h"
|
||||
#include "mozilla/ipc/PChildToParentStreamChild.h"
|
||||
|
@ -77,6 +79,8 @@ using mozilla::dom::asmjscache::PAsmJSCacheEntryChild;
|
|||
using mozilla::dom::cache::PCacheChild;
|
||||
using mozilla::dom::cache::PCacheStorageChild;
|
||||
using mozilla::dom::cache::PCacheStreamControlChild;
|
||||
using mozilla::dom::LocalStorage;
|
||||
using mozilla::dom::StorageDBChild;
|
||||
|
||||
using mozilla::dom::WebAuthnTransactionChild;
|
||||
|
||||
|
@ -202,6 +206,23 @@ BackgroundChildImpl::DeallocPBackgroundIndexedDBUtilsChild(
|
|||
return true;
|
||||
}
|
||||
|
||||
BackgroundChildImpl::PBackgroundStorageChild*
|
||||
BackgroundChildImpl::AllocPBackgroundStorageChild(const nsString& aProfilePath)
|
||||
{
|
||||
MOZ_CRASH("PBackgroundStorageChild actors should be manually constructed!");
|
||||
}
|
||||
|
||||
bool
|
||||
BackgroundChildImpl::DeallocPBackgroundStorageChild(
|
||||
PBackgroundStorageChild* aActor)
|
||||
{
|
||||
MOZ_ASSERT(aActor);
|
||||
|
||||
StorageDBChild* child = static_cast<StorageDBChild*>(aActor);
|
||||
child->ReleaseIPDLReference();
|
||||
return true;
|
||||
}
|
||||
|
||||
PPendingIPCBlobChild*
|
||||
BackgroundChildImpl::AllocPPendingIPCBlobChild(const IPCBlob& aBlob)
|
||||
{
|
||||
|
@ -578,6 +599,32 @@ BackgroundChildImpl::DeallocPHttpBackgroundChannelChild(PHttpBackgroundChannelCh
|
|||
return true;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
BackgroundChildImpl::RecvDispatchLocalStorageChange(
|
||||
const nsString& aDocumentURI,
|
||||
const nsString& aKey,
|
||||
const nsString& aOldValue,
|
||||
const nsString& aNewValue,
|
||||
const PrincipalInfo& aPrincipalInfo,
|
||||
const bool& aIsPrivate)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIPrincipal> principal =
|
||||
PrincipalInfoToPrincipal(aPrincipalInfo, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
LocalStorage::DispatchStorageEvent(aDocumentURI, aKey, aOldValue, aNewValue,
|
||||
principal, aIsPrivate, nullptr, true);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -70,6 +70,12 @@ protected:
|
|||
DeallocPBackgroundIndexedDBUtilsChild(PBackgroundIndexedDBUtilsChild* aActor)
|
||||
override;
|
||||
|
||||
virtual PBackgroundStorageChild*
|
||||
AllocPBackgroundStorageChild(const nsString& aProfilePath) override;
|
||||
|
||||
virtual bool
|
||||
DeallocPBackgroundStorageChild(PBackgroundStorageChild* aActor) override;
|
||||
|
||||
virtual PPendingIPCBlobChild*
|
||||
AllocPPendingIPCBlobChild(const IPCBlob& aBlob) override;
|
||||
|
||||
|
@ -208,6 +214,14 @@ protected:
|
|||
|
||||
virtual bool
|
||||
DeallocPHttpBackgroundChannelChild(PHttpBackgroundChannelChild* aActor) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvDispatchLocalStorageChange(const nsString& aDocumentURI,
|
||||
const nsString& aKey,
|
||||
const nsString& aOldValue,
|
||||
const nsString& aNewValue,
|
||||
const PrincipalInfo& aPrincipalInfo,
|
||||
const bool& aIsPrivate) override;
|
||||
};
|
||||
|
||||
class BackgroundChildImpl::ThreadLocal final
|
||||
|
|
|
@ -214,6 +214,11 @@ private:
|
|||
static uint64_t
|
||||
GetChildID(PBackgroundParent* aBackgroundActor);
|
||||
|
||||
// Forwarded from BackgroundParent.
|
||||
static bool
|
||||
GetLiveActorArray(PBackgroundParent* aBackgroundActor,
|
||||
nsTArray<PBackgroundParent*>& aLiveActorArray);
|
||||
|
||||
// Forwarded from BackgroundParent.
|
||||
static bool
|
||||
Alloc(ContentParent* aContent,
|
||||
|
@ -703,6 +708,15 @@ BackgroundParent::GetChildID(PBackgroundParent* aBackgroundActor)
|
|||
return ParentImpl::GetChildID(aBackgroundActor);
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
BackgroundParent::GetLiveActorArray(
|
||||
PBackgroundParent* aBackgroundActor,
|
||||
nsTArray<PBackgroundParent*>& aLiveActorArray)
|
||||
{
|
||||
return ParentImpl::GetLiveActorArray(aBackgroundActor, aLiveActorArray);
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
BackgroundParent::Alloc(ContentParent* aContent,
|
||||
|
@ -871,6 +885,33 @@ ParentImpl::GetChildID(PBackgroundParent* aBackgroundActor)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
ParentImpl::GetLiveActorArray(PBackgroundParent* aBackgroundActor,
|
||||
nsTArray<PBackgroundParent*>& aLiveActorArray)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aBackgroundActor);
|
||||
MOZ_ASSERT(aLiveActorArray.IsEmpty());
|
||||
|
||||
auto actor = static_cast<ParentImpl*>(aBackgroundActor);
|
||||
if (actor->mActorDestroyed) {
|
||||
MOZ_ASSERT(false,
|
||||
"GetLiveActorArray called after ActorDestroy was called!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!actor->mLiveActorArray) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (ParentImpl* liveActor : *actor->mLiveActorArray) {
|
||||
aLiveActorArray.AppendElement(liveActor);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
ParentImpl::Alloc(ContentParent* aContent,
|
||||
|
|
|
@ -67,6 +67,10 @@ public:
|
|||
static uint64_t
|
||||
GetChildID(PBackgroundParent* aBackgroundActor);
|
||||
|
||||
static bool
|
||||
GetLiveActorArray(PBackgroundParent* aBackgroundActor,
|
||||
nsTArray<PBackgroundParent*>& aLiveActorArray);
|
||||
|
||||
private:
|
||||
// Only called by ContentParent for cross-process actors.
|
||||
static bool
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "mozilla/dom/ipc/IPCBlobInputStreamParent.h"
|
||||
#include "mozilla/dom/ipc/PendingIPCBlobParent.h"
|
||||
#include "mozilla/dom/quota/ActorsParent.h"
|
||||
#include "mozilla/dom/StorageIPC.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
#include "mozilla/ipc/BackgroundUtils.h"
|
||||
#include "mozilla/ipc/IPCStreamAlloc.h"
|
||||
|
@ -242,6 +243,64 @@ BackgroundParentImpl::RecvFlushPendingFileDeletions()
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
auto
|
||||
BackgroundParentImpl::AllocPBackgroundStorageParent(const nsString& aProfilePath)
|
||||
-> PBackgroundStorageParent*
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
return mozilla::dom::AllocPBackgroundStorageParent(aProfilePath);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
BackgroundParentImpl::RecvPBackgroundStorageConstructor(
|
||||
PBackgroundStorageParent* aActor,
|
||||
const nsString& aProfilePath)
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aActor);
|
||||
|
||||
return mozilla::dom::RecvPBackgroundStorageConstructor(aActor, aProfilePath);
|
||||
}
|
||||
|
||||
bool
|
||||
BackgroundParentImpl::DeallocPBackgroundStorageParent(
|
||||
PBackgroundStorageParent* aActor)
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aActor);
|
||||
|
||||
return mozilla::dom::DeallocPBackgroundStorageParent(aActor);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
BackgroundParentImpl::RecvBroadcastLocalStorageChange(
|
||||
const nsString& aDocumentURI,
|
||||
const nsString& aKey,
|
||||
const nsString& aOldValue,
|
||||
const nsString& aNewValue,
|
||||
const PrincipalInfo& aPrincipalInfo,
|
||||
const bool& aIsPrivate)
|
||||
{
|
||||
nsTArray<PBackgroundParent*> liveActorArray;
|
||||
if (NS_WARN_IF(!BackgroundParent::GetLiveActorArray(this, liveActorArray))) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
for (auto* liveActor : liveActorArray) {
|
||||
if (liveActor != this) {
|
||||
Unused << liveActor->SendDispatchLocalStorageChange(
|
||||
nsString(aDocumentURI), nsString(aKey), nsString(aOldValue),
|
||||
nsString(aNewValue), aPrincipalInfo, aIsPrivate);
|
||||
}
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
PPendingIPCBlobParent*
|
||||
BackgroundParentImpl::AllocPPendingIPCBlobParent(const IPCBlob& aBlob)
|
||||
{
|
||||
|
|
|
@ -63,6 +63,24 @@ protected:
|
|||
virtual mozilla::ipc::IPCResult
|
||||
RecvFlushPendingFileDeletions() override;
|
||||
|
||||
virtual PBackgroundStorageParent*
|
||||
AllocPBackgroundStorageParent(const nsString& aProfilePath) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvPBackgroundStorageConstructor(PBackgroundStorageParent* aActor,
|
||||
const nsString& aProfilePath) override;
|
||||
|
||||
virtual bool
|
||||
DeallocPBackgroundStorageParent(PBackgroundStorageParent* aActor) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvBroadcastLocalStorageChange(const nsString& aDocumentURI,
|
||||
const nsString& aKey,
|
||||
const nsString& aOldValue,
|
||||
const nsString& aNewValue,
|
||||
const PrincipalInfo& aPrincipalInfo,
|
||||
const bool& aIsPrivate) override;
|
||||
|
||||
virtual PPendingIPCBlobParent*
|
||||
AllocPPendingIPCBlobParent(const IPCBlob& aBlob) override;
|
||||
|
||||
|
|
|
@ -979,6 +979,42 @@ struct ParamTraits<mozilla::Variant<Ts...>>
|
|||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ParamTraits<mozilla::dom::Optional<T>>
|
||||
{
|
||||
typedef mozilla::dom::Optional<T> paramType;
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
if (aParam.WasPassed()) {
|
||||
WriteParam(aMsg, true);
|
||||
WriteParam(aMsg, aParam.Value());
|
||||
return;
|
||||
}
|
||||
|
||||
WriteParam(aMsg, false);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
bool wasPassed = false;
|
||||
|
||||
if (!ReadParam(aMsg, aIter, &wasPassed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aResult->Reset();
|
||||
|
||||
if (wasPassed) {
|
||||
if (!ReadParam(aMsg, aIter, &aResult->Construct())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace IPC */
|
||||
|
||||
#endif /* __IPC_GLUE_IPCMESSAGEUTILS_H__ */
|
||||
|
|
|
@ -33,13 +33,8 @@ using mozilla::Move;
|
|||
// Undo the damage done by mozzconf.h
|
||||
#undef compress
|
||||
|
||||
// Logging seems to be somewhat broken on b2g.
|
||||
#ifdef MOZ_B2G
|
||||
#define IPC_LOG(...)
|
||||
#else
|
||||
static mozilla::LazyLogModule sLogModule("ipc");
|
||||
#define IPC_LOG(...) MOZ_LOG(sLogModule, LogLevel::Debug, (__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* IPC design:
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
include protocol PAsmJSCacheEntry;
|
||||
include protocol PBackgroundIDBFactory;
|
||||
include protocol PBackgroundIndexedDBUtils;
|
||||
include protocol PBackgroundStorage;
|
||||
include protocol PBackgroundTest;
|
||||
include protocol PBroadcastChannel;
|
||||
include protocol PCache;
|
||||
|
@ -53,6 +54,7 @@ sync protocol PBackground
|
|||
manages PAsmJSCacheEntry;
|
||||
manages PBackgroundIDBFactory;
|
||||
manages PBackgroundIndexedDBUtils;
|
||||
manages PBackgroundStorage;
|
||||
manages PBackgroundTest;
|
||||
manages PBroadcastChannel;
|
||||
manages PCache;
|
||||
|
@ -86,6 +88,15 @@ parent:
|
|||
// Use only for testing!
|
||||
async FlushPendingFileDeletions();
|
||||
|
||||
async PBackgroundStorage(nsString profilePath);
|
||||
|
||||
async BroadcastLocalStorageChange(nsString documentURI,
|
||||
nsString key,
|
||||
nsString oldValue,
|
||||
nsString newValue,
|
||||
PrincipalInfo principalInfo,
|
||||
bool isPrivate);
|
||||
|
||||
async PVsync();
|
||||
|
||||
async PCameras();
|
||||
|
@ -129,6 +140,13 @@ child:
|
|||
|
||||
async PPendingIPCBlob(IPCBlob blob);
|
||||
|
||||
async DispatchLocalStorageChange(nsString documentURI,
|
||||
nsString key,
|
||||
nsString oldValue,
|
||||
nsString newValue,
|
||||
PrincipalInfo principalInfo,
|
||||
bool isPrivate);
|
||||
|
||||
both:
|
||||
// PIPCBlobInputStream is created on the parent side only if the child starts
|
||||
// a migration.
|
||||
|
|
|
@ -189,6 +189,7 @@ LOCAL_INCLUDES += [
|
|||
'/caps',
|
||||
'/dom/broadcastchannel',
|
||||
'/dom/indexedDB',
|
||||
'/dom/storage',
|
||||
'/dom/workers',
|
||||
'/media/webrtc/trunk',
|
||||
'/xpcom/build',
|
||||
|
|
|
@ -930,7 +930,7 @@ description =
|
|||
description =
|
||||
[PBrowserStream::NPN_RequestRead]
|
||||
description =
|
||||
[PStorage::Preload]
|
||||
[PBackgroundStorage::Preload]
|
||||
description =
|
||||
[PRemoteSpellcheckEngine::Check]
|
||||
description =
|
||||
|
|
|
@ -197,18 +197,19 @@ nsPageFrame::ProcessSpecialCodes(const nsString& aStr, nsString& aNewStr)
|
|||
// values
|
||||
NS_NAMED_LITERAL_STRING(kPageAndTotal, "&PT");
|
||||
if (aStr.Find(kPageAndTotal) != kNotFound) {
|
||||
char16_t * uStr = nsTextFormatter::smprintf(mPD->mPageNumAndTotalsFormat.get(), mPageNum, mTotNumPages);
|
||||
aNewStr.ReplaceSubstring(kPageAndTotal, nsDependentString(uStr));
|
||||
free(uStr);
|
||||
nsAutoString uStr;
|
||||
nsTextFormatter::ssprintf(uStr, mPD->mPageNumAndTotalsFormat.get(),
|
||||
mPageNum, mTotNumPages);
|
||||
aNewStr.ReplaceSubstring(kPageAndTotal, uStr);
|
||||
}
|
||||
|
||||
// Search to see if the page number code is in the string
|
||||
// and replace the page number code with the actual value
|
||||
NS_NAMED_LITERAL_STRING(kPage, "&P");
|
||||
if (aStr.Find(kPage) != kNotFound) {
|
||||
char16_t * uStr = nsTextFormatter::smprintf(mPD->mPageNumFormat.get(), mPageNum);
|
||||
aNewStr.ReplaceSubstring(kPage, nsDependentString(uStr));
|
||||
free(uStr);
|
||||
nsAutoString uStr;
|
||||
nsTextFormatter::ssprintf(uStr, mPD->mPageNumFormat.get(), mPageNum);
|
||||
aNewStr.ReplaceSubstring(kPage, uStr);
|
||||
}
|
||||
|
||||
NS_NAMED_LITERAL_STRING(kTitle, "&T");
|
||||
|
@ -223,9 +224,9 @@ nsPageFrame::ProcessSpecialCodes(const nsString& aStr, nsString& aNewStr)
|
|||
|
||||
NS_NAMED_LITERAL_STRING(kPageTotal, "&L");
|
||||
if (aStr.Find(kPageTotal) != kNotFound) {
|
||||
char16_t * uStr = nsTextFormatter::smprintf(mPD->mPageNumFormat.get(), mTotNumPages);
|
||||
aNewStr.ReplaceSubstring(kPageTotal, nsDependentString(uStr));
|
||||
free(uStr);
|
||||
nsAutoString uStr;
|
||||
nsTextFormatter::ssprintf(uStr, mPD->mPageNumFormat.get(), mTotNumPages);
|
||||
aNewStr.ReplaceSubstring(kPageTotal, uStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1892,7 +1892,8 @@ nsDisplayList::GetBounds(nsDisplayListBuilder* aBuilder) const {
|
|||
|
||||
nsRect
|
||||
nsDisplayList::GetClippedBoundsWithRespectToASR(nsDisplayListBuilder* aBuilder,
|
||||
const ActiveScrolledRoot* aASR) const {
|
||||
const ActiveScrolledRoot* aASR,
|
||||
nsRect* aVisibleRect) const {
|
||||
nsRect bounds;
|
||||
for (nsDisplayItem* i = GetBottom(); i != nullptr; i = i->GetAbove()) {
|
||||
nsRect r = i->GetClippedBounds(aBuilder);
|
||||
|
@ -1908,6 +1909,9 @@ nsDisplayList::GetClippedBoundsWithRespectToASR(nsDisplayListBuilder* aBuilder,
|
|||
r = clip->GetClipRect();
|
||||
}
|
||||
}
|
||||
if (aVisibleRect) {
|
||||
aVisibleRect->UnionRect(*aVisibleRect, i->GetVisibleRect());
|
||||
}
|
||||
bounds.UnionRect(bounds, r);
|
||||
}
|
||||
return bounds;
|
||||
|
|
|
@ -2517,9 +2517,14 @@ public:
|
|||
* If there is an item in this list which is not bounded with respect to
|
||||
* aASR (i.e. which does not have "finite bounds" with respect to aASR),
|
||||
* then this method trigger an assertion failure.
|
||||
* The optional aVisibleRect out argument can be set to non-null if the
|
||||
* caller is also interested to know the visible rect. This can be used
|
||||
* to get the visible rect efficiently without traversing the display list
|
||||
* twice.
|
||||
*/
|
||||
nsRect GetClippedBoundsWithRespectToASR(nsDisplayListBuilder* aBuilder,
|
||||
const ActiveScrolledRoot* aASR) const;
|
||||
const ActiveScrolledRoot* aASR,
|
||||
nsRect* aVisibleRect = nullptr) const;
|
||||
|
||||
/**
|
||||
* Find the topmost display item that returns a non-null frame, and return
|
||||
|
@ -3822,7 +3827,9 @@ public:
|
|||
*/
|
||||
virtual void UpdateBounds(nsDisplayListBuilder* aBuilder) override
|
||||
{
|
||||
mBounds = mList.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot);
|
||||
nsRect visibleRect;
|
||||
mBounds = mList.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot,
|
||||
&visibleRect);
|
||||
// The display list may contain content that's visible outside the visible
|
||||
// rect (i.e. the current dirty rect) passed in when the item was created.
|
||||
// This happens when the dirty rect has been restricted to the visual
|
||||
|
@ -3830,7 +3837,7 @@ public:
|
|||
// rects in nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay), but that
|
||||
// frame contains placeholders for out-of-flows that aren't descendants of
|
||||
// the frame.
|
||||
mVisibleRect.UnionRect(mBaseVisibleRect, mList.GetVisibleRect());
|
||||
mVisibleRect.UnionRect(mBaseVisibleRect, visibleRect);
|
||||
}
|
||||
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
|
||||
HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override;
|
||||
|
|
|
@ -649,9 +649,7 @@ Preferences::IsServiceAvailable()
|
|||
bool
|
||||
Preferences::InitStaticMembers()
|
||||
{
|
||||
#ifndef MOZ_B2G
|
||||
MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal());
|
||||
#endif
|
||||
|
||||
if (!sShutdown && !sPreferences) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
|
|
@ -1426,7 +1426,7 @@ pref("javascript.options.wasm", true);
|
|||
pref("javascript.options.wasm_baselinejit", false);
|
||||
pref("javascript.options.native_regexp", true);
|
||||
pref("javascript.options.parallel_parsing", true);
|
||||
#if !defined(RELEASE_OR_BETA) && !defined(ANDROID) && !defined(MOZ_B2G) && !defined(XP_IOS)
|
||||
#if !defined(RELEASE_OR_BETA) && !defined(ANDROID) && !defined(XP_IOS)
|
||||
pref("javascript.options.asyncstack", true);
|
||||
#else
|
||||
pref("javascript.options.asyncstack", false);
|
||||
|
@ -2495,14 +2495,6 @@ pref("font.name-list.monospace.x-math", "monospace");
|
|||
// These fonts are ignored the underline offset, instead of it, the underline is lowered to bottom of its em descent.
|
||||
pref("font.blacklist.underline_offset", "FangSong,Gulim,GulimChe,MingLiU,MingLiU-ExtB,MingLiU_HKSCS,MingLiU-HKSCS-ExtB,MS Gothic,MS Mincho,MS PGothic,MS PMincho,MS UI Gothic,PMingLiU,PMingLiU-ExtB,SimHei,SimSun,SimSun-ExtB,Hei,Kai,Apple LiGothic,Apple LiSung,Osaka");
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
// Whitelist of fonts that ship with B2G that do not include space lookups in
|
||||
// default features. This allows us to skip analyzing the GSUB/GPOS tables
|
||||
// unless features are explicitly enabled.
|
||||
// Use NSPR_LOG_MODULES=fontinit:5 to dump out details of space lookups
|
||||
pref("font.whitelist.skip_default_features_space_check", "Fira Sans,Fira Mono");
|
||||
#endif
|
||||
|
||||
pref("images.dither", "auto");
|
||||
pref("security.directory", "");
|
||||
|
||||
|
@ -4415,7 +4407,7 @@ pref("gfx.font_rendering.fontconfig.max_generic_substitutions", 3);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ANDROID) || defined(MOZ_B2G)
|
||||
#if defined(ANDROID)
|
||||
|
||||
pref("font.size.fixed.ar", 12);
|
||||
|
||||
|
@ -4433,65 +4425,10 @@ pref("font.size.fixed.x-unicode", 12);
|
|||
pref("font.default.x-western", "sans-serif");
|
||||
pref("font.size.fixed.x-western", 12);
|
||||
|
||||
# ANDROID || MOZ_B2G
|
||||
# ANDROID
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_B2G)
|
||||
// Gonk, FxOS Simulator, B2G Desktop and Mulet.
|
||||
|
||||
// TODO: some entries could probably be cleaned up.
|
||||
|
||||
// ar
|
||||
|
||||
pref("font.name-list.serif.el", "Droid Serif"); // not Charis SIL Compact, only has a few Greek chars
|
||||
pref("font.name-list.sans-serif.el", "Fira Sans");
|
||||
pref("font.name-list.monospace.el", "Fira Mono");
|
||||
|
||||
pref("font.name-list.serif.he", "Charis SIL Compact");
|
||||
pref("font.name-list.sans-serif.he", "Fira Sans, Droid Sans Hebrew");
|
||||
pref("font.name-list.monospace.he", "Fira Mono");
|
||||
|
||||
pref("font.name-list.serif.ja", "Charis SIL Compact");
|
||||
pref("font.name-list.sans-serif.ja", "Fira Sans, MotoyaLMaru, MotoyaLCedar, Droid Sans Japanese");
|
||||
pref("font.name-list.monospace.ja", "MotoyaLMaru, MotoyaLCedar, Fira Mono");
|
||||
|
||||
pref("font.name-list.serif.ko", "Charis SIL Compact");
|
||||
pref("font.name-list.sans-serif.ko", "Fira Sans");
|
||||
pref("font.name-list.monospace.ko", "Fira Mono");
|
||||
|
||||
pref("font.name-list.serif.th", "Charis SIL Compact");
|
||||
pref("font.name-list.sans-serif.th", "Fira Sans, Noto Sans Thai, Droid Sans Thai");
|
||||
pref("font.name-list.monospace.th", "Fira Mono");
|
||||
|
||||
pref("font.name-list.serif.x-cyrillic", "Charis SIL Compact");
|
||||
pref("font.name-list.sans-serif.x-cyrillic", "Fira Sans");
|
||||
pref("font.name-list.monospace.x-cyrillic", "Fira Mono");
|
||||
|
||||
pref("font.name-list.serif.x-unicode", "Charis SIL Compact");
|
||||
pref("font.name-list.sans-serif.x-unicode", "Fira Sans");
|
||||
pref("font.name-list.monospace.x-unicode", "Fira Mono");
|
||||
|
||||
pref("font.name-list.serif.x-western", "Charis SIL Compact");
|
||||
pref("font.name-list.sans-serif.x-western", "Fira Sans");
|
||||
pref("font.name-list.monospace.x-western", "Fira Mono");
|
||||
|
||||
pref("font.name-list.serif.zh-CN", "Charis SIL Compact");
|
||||
pref("font.name-list.sans-serif.zh-CN", "Fira Sans, Droid Sans Fallback");
|
||||
pref("font.name-list.monospace.zh-CN", "Fira Mono");
|
||||
|
||||
pref("font.name-list.serif.zh-HK", "Charis SIL Compact");
|
||||
pref("font.name-list.sans-serif.zh-HK", "Fira Sans, Droid Sans Fallback");
|
||||
pref("font.name-list.monospace.zh-HK", "Fira Mono");
|
||||
|
||||
pref("font.name-list.serif.zh-TW", "Charis SIL Compact");
|
||||
pref("font.name-list.sans-serif.zh-TW", "Fira Sans, Droid Sans Fallback");
|
||||
pref("font.name-list.monospace.zh-TW", "Fira Mono");
|
||||
|
||||
pref("font.name-list.serif.x-math", "Latin Modern Math, STIX Two Math, XITS Math, Cambria Math, Libertinus Math, DejaVu Math TeX Gyre, TeX Gyre Bonum Math, TeX Gyre Pagella Math, TeX Gyre Schola, TeX Gyre Termes Math, STIX Math, Asana Math, STIXGeneral, DejaVu Serif, DejaVu Sans, Charis SIL Compact");
|
||||
pref("font.name-list.sans-serif.x-math", "Fira Sans");
|
||||
pref("font.name-list.monospace.x-math", "Fira Mono");
|
||||
|
||||
#elif defined(ANDROID)
|
||||
#if defined(ANDROID)
|
||||
// We use the bundled fonts for Firefox for Android
|
||||
|
||||
pref("font.name-list.serif.ar", "Noto Naskh Arabic, Noto Serif, Droid Serif");
|
||||
|
|
|
@ -526,9 +526,7 @@ nsresult PREF_GetBoolPref(const char *pref_name, bool * return_value, bool get_d
|
|||
nsresult
|
||||
PREF_DeleteBranch(const char *branch_name)
|
||||
{
|
||||
#ifndef MOZ_B2G
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
#endif
|
||||
|
||||
int len = (int)strlen(branch_name);
|
||||
|
||||
|
@ -589,9 +587,7 @@ PREF_ClearUserPref(const char *pref_name)
|
|||
nsresult
|
||||
PREF_ClearAllUserPrefs()
|
||||
{
|
||||
#ifndef MOZ_B2G
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
#endif
|
||||
|
||||
if (!gHashTable)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
@ -735,9 +731,7 @@ inInitArray(const char* key)
|
|||
|
||||
PrefHashEntry* pref_HashTableLookup(const char *key)
|
||||
{
|
||||
#ifndef MOZ_B2G
|
||||
MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal());
|
||||
#endif
|
||||
MOZ_ASSERT((!XRE_IsContentProcess() || gPhase != START),
|
||||
"pref access before commandline prefs set");
|
||||
/* If you're hitting this assertion, you've added a pref access to start up.
|
||||
|
@ -756,9 +750,7 @@ PrefHashEntry* pref_HashTableLookup(const char *key)
|
|||
|
||||
nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags)
|
||||
{
|
||||
#ifndef MOZ_B2G
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
#endif
|
||||
|
||||
if (!gHashTable)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
|
|
@ -229,10 +229,7 @@ public:
|
|||
}
|
||||
double operator/(const BaseTimeDuration& aOther) const
|
||||
{
|
||||
#ifndef MOZ_B2G
|
||||
// Bug 1066388 - This fails on B2G ICS Emulator
|
||||
MOZ_ASSERT(aOther.mValue != 0, "Division by zero");
|
||||
#endif
|
||||
return ValueCalculator::DivideDouble(mValue, aOther.mValue);
|
||||
}
|
||||
BaseTimeDuration operator%(const BaseTimeDuration& aOther) const
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
// The tickler only applies to wifi on mobile right now. Hopefully it
|
||||
// can also be restricted to particular handset models in the future.
|
||||
|
||||
#if defined(ANDROID) && !defined(MOZ_B2G)
|
||||
#if defined(ANDROID)
|
||||
#define MOZ_USE_WIFI_TICKLER
|
||||
#endif
|
||||
|
||||
|
|
|
@ -9,8 +9,10 @@
|
|||
// http://wiki.mozilla.org/Gecko:Effective_TLD_Service
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "nsEffectiveTLDService.h"
|
||||
#include "nsIIDNService.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
@ -19,6 +21,13 @@
|
|||
#include "nsNetCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
namespace etld_dafsa {
|
||||
|
||||
// Generated file that includes kDafsa
|
||||
#include "etld_data.inc"
|
||||
|
||||
} // namespace etld_dafsa
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsEffectiveTLDService, nsIEffectiveTLDService,
|
||||
|
@ -26,56 +35,11 @@ NS_IMPL_ISUPPORTS(nsEffectiveTLDService, nsIEffectiveTLDService,
|
|||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#define ETLD_STR_NUM_1(line) str##line
|
||||
#define ETLD_STR_NUM(line) ETLD_STR_NUM_1(line)
|
||||
#define ETLD_ENTRY_OFFSET(name) offsetof(struct etld_string_list, ETLD_STR_NUM(__LINE__))
|
||||
|
||||
const ETLDEntry ETLDEntry::entries[] = {
|
||||
#define ETLD_ENTRY(name, ex, wild) { ETLD_ENTRY_OFFSET(name), ex, wild },
|
||||
#include "etld_data.inc"
|
||||
#undef ETLD_ENTRY
|
||||
};
|
||||
|
||||
const union ETLDEntry::etld_strings ETLDEntry::strings = {
|
||||
{
|
||||
#define ETLD_ENTRY(name, ex, wild) name,
|
||||
#include "etld_data.inc"
|
||||
#undef ETLD_ENTRY
|
||||
}
|
||||
};
|
||||
|
||||
/* static */ const ETLDEntry*
|
||||
ETLDEntry::GetEntry(const char* aDomain)
|
||||
{
|
||||
size_t i;
|
||||
if (BinarySearchIf(entries, 0, ArrayLength(ETLDEntry::entries),
|
||||
Cmp(aDomain), &i)) {
|
||||
return &entries[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Dummy function to statically ensure that our indices don't overflow
|
||||
// the storage provided for them.
|
||||
void
|
||||
ETLDEntry::FuncForStaticAsserts(void)
|
||||
{
|
||||
#define ETLD_ENTRY(name, ex, wild) \
|
||||
static_assert(ETLD_ENTRY_OFFSET(name) < (1 << ETLD_ENTRY_N_INDEX_BITS), \
|
||||
"invalid strtab index");
|
||||
#include "etld_data.inc"
|
||||
#undef ETLD_ENTRY
|
||||
}
|
||||
|
||||
#undef ETLD_ENTRY_OFFSET
|
||||
#undef ETLD_STR_NUM
|
||||
#undef ETLD_STR_NUM1
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
static nsEffectiveTLDService *gService = nullptr;
|
||||
|
||||
nsEffectiveTLDService::nsEffectiveTLDService()
|
||||
: mIDNService()
|
||||
, mGraph(etld_dafsa::kDafsa)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -86,24 +50,6 @@ nsEffectiveTLDService::Init()
|
|||
mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Sanity-check the eTLD entries.
|
||||
for (uint32_t i = 0; i < ArrayLength(ETLDEntry::entries); i++) {
|
||||
const char* domain = ETLDEntry::entries[i].GetEffectiveTLDName();
|
||||
nsDependentCString name(domain);
|
||||
nsAutoCString normalizedName(domain);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(NormalizeHostname(normalizedName)),
|
||||
"normalization failure!");
|
||||
MOZ_ASSERT(name.Equals(normalizedName), "domain not normalized!");
|
||||
|
||||
// Domains must be in sorted order for binary search to work.
|
||||
if (i > 0) {
|
||||
const char* domain0 = ETLDEntry::entries[i - 1].GetEffectiveTLDName();
|
||||
MOZ_ASSERT(strcmp(domain0, domain) < 0, "domains not in sorted order!");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT(!gService);
|
||||
gService = this;
|
||||
RegisterWeakMemoryReporter(this);
|
||||
|
@ -244,6 +190,9 @@ nsEffectiveTLDService::GetBaseDomainInternal(nsCString &aHostname,
|
|||
int32_t aAdditionalParts,
|
||||
nsACString &aBaseDomain)
|
||||
{
|
||||
const int kExceptionRule = 1;
|
||||
const int kWildcardRule = 2;
|
||||
|
||||
if (aHostname.IsEmpty())
|
||||
return NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS;
|
||||
|
||||
|
@ -263,6 +212,20 @@ nsEffectiveTLDService::GetBaseDomainInternal(nsCString &aHostname,
|
|||
if (result == PR_SUCCESS)
|
||||
return NS_ERROR_HOST_IS_IP_ADDRESS;
|
||||
|
||||
// Lookup in the cache if this is a normal query.
|
||||
TLDCacheEntry* entry = nullptr;
|
||||
if (aAdditionalParts == 1) {
|
||||
if (LookupForAdd(aHostname, &entry)) {
|
||||
// There was a match, just return the cached value.
|
||||
aBaseDomain = entry->mBaseDomain;
|
||||
if (trailingDot) {
|
||||
aBaseDomain.Append('.');
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Walk up the domain tree, most specific to least specific,
|
||||
// looking for matches at each level. Note that a given level may
|
||||
// have multiple attributes (e.g. IsWild() and IsNormal()).
|
||||
|
@ -280,19 +243,19 @@ nsEffectiveTLDService::GetBaseDomainInternal(nsCString &aHostname,
|
|||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
// Perform the lookup.
|
||||
const ETLDEntry* entry = ETLDEntry::GetEntry(currDomain);
|
||||
if (entry) {
|
||||
if (entry->IsWild() && prevDomain) {
|
||||
const int result = mGraph.Lookup(Substring(currDomain, end));
|
||||
if (result != Dafsa::kKeyNotFound) {
|
||||
if (result == kWildcardRule && prevDomain) {
|
||||
// wildcard rules imply an eTLD one level inferior to the match.
|
||||
eTLD = prevDomain;
|
||||
break;
|
||||
}
|
||||
if (entry->IsNormal() || !nextDot) {
|
||||
if ((result == kWildcardRule || result != kExceptionRule) || !nextDot) {
|
||||
// specific match, or we've hit the top domain level
|
||||
eTLD = currDomain;
|
||||
break;
|
||||
}
|
||||
if (entry->IsException()) {
|
||||
if (result == kExceptionRule) {
|
||||
// exception rules imply an eTLD one level superior to the match.
|
||||
eTLD = nextDot + 1;
|
||||
break;
|
||||
|
@ -343,6 +306,13 @@ nsEffectiveTLDService::GetBaseDomainInternal(nsCString &aHostname,
|
|||
return NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS;
|
||||
|
||||
aBaseDomain = Substring(iter, end);
|
||||
|
||||
// Update the MRU table if in use.
|
||||
if (entry) {
|
||||
entry->mHost = aHostname;
|
||||
entry->mBaseDomain = aBaseDomain;
|
||||
}
|
||||
|
||||
// add on the trailing dot, if applicable
|
||||
if (trailingDot)
|
||||
aBaseDomain.Append('.');
|
||||
|
@ -365,3 +335,13 @@ nsEffectiveTLDService::NormalizeHostname(nsCString &aHostname)
|
|||
ToLowerCase(aHostname);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsEffectiveTLDService::LookupForAdd(const nsACString& aHost, TLDCacheEntry** aEntry)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
const uint32_t hash = HashString(aHost.BeginReading(), aHost.Length());
|
||||
*aEntry = &mMruTable[hash % kTableSize];
|
||||
return (*aEntry)->mHost == aHost;
|
||||
}
|
||||
|
|
|
@ -12,67 +12,11 @@
|
|||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/BinarySearch.h"
|
||||
#include "mozilla/Dafsa.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
class nsIIDNService;
|
||||
|
||||
// struct for static data generated from effective_tld_names.dat
|
||||
struct ETLDEntry {
|
||||
friend class nsEffectiveTLDService;
|
||||
|
||||
public:
|
||||
bool IsNormal() const { return wild || !exception; }
|
||||
bool IsException() const { return exception; }
|
||||
bool IsWild() const { return wild; }
|
||||
|
||||
const char* GetEffectiveTLDName() const
|
||||
{
|
||||
return strings.strtab + strtab_index;
|
||||
}
|
||||
|
||||
static const ETLDEntry* GetEntry(const char* aDomain);
|
||||
|
||||
static const size_t ETLD_ENTRY_N_INDEX_BITS = 30;
|
||||
|
||||
// These fields must be public to allow static construction.
|
||||
uint32_t strtab_index : ETLD_ENTRY_N_INDEX_BITS;
|
||||
uint32_t exception : 1;
|
||||
uint32_t wild : 1;
|
||||
|
||||
private:
|
||||
struct Cmp {
|
||||
int operator()(const ETLDEntry aEntry) const
|
||||
{
|
||||
return strcmp(mName, aEntry.GetEffectiveTLDName());
|
||||
}
|
||||
explicit Cmp(const char* aName) : mName(aName) {}
|
||||
const char* mName;
|
||||
};
|
||||
|
||||
#define ETLD_STR_NUM_1(line) str##line
|
||||
#define ETLD_STR_NUM(line) ETLD_STR_NUM_1(line)
|
||||
struct etld_string_list {
|
||||
#define ETLD_ENTRY(name, ex, wild) char ETLD_STR_NUM(__LINE__)[sizeof(name)];
|
||||
#include "etld_data.inc"
|
||||
#undef ETLD_ENTRY
|
||||
};
|
||||
|
||||
// This static string table is all the eTLD domain names packed together.
|
||||
static const union etld_strings {
|
||||
struct etld_string_list list;
|
||||
char strtab[1];
|
||||
} strings;
|
||||
|
||||
// This is the static entries table. Each entry has an index into the string
|
||||
// table. The entries are in sorted order so that binary search can be used.
|
||||
static const ETLDEntry entries[];
|
||||
|
||||
void FuncForStaticAsserts(void);
|
||||
#undef ETLD_STR_NUM
|
||||
#undef ETLD_STR_NUM1
|
||||
};
|
||||
|
||||
class nsEffectiveTLDService final
|
||||
: public nsIEffectiveTLDService
|
||||
, public nsIMemoryReporter
|
||||
|
@ -93,6 +37,40 @@ private:
|
|||
~nsEffectiveTLDService();
|
||||
|
||||
nsCOMPtr<nsIIDNService> mIDNService;
|
||||
|
||||
// The DAFSA provides a compact encoding of the rather large eTLD list.
|
||||
mozilla::Dafsa mGraph;
|
||||
|
||||
struct TLDCacheEntry
|
||||
{
|
||||
nsCString mHost;
|
||||
nsCString mBaseDomain;
|
||||
};
|
||||
|
||||
// We use a small most recently used cache to compensate for DAFSA lookups
|
||||
// being slightly slower than a binary search on a larger table of strings.
|
||||
//
|
||||
// We first check the cache for a matching result and avoid a DAFSA lookup
|
||||
// if a match is found. Otherwise we lookup the domain in the DAFSA and then
|
||||
// cache the result. During standard browsing the same domains are repeatedly
|
||||
// fed into |GetBaseDomainInternal| so this ends up being an effective
|
||||
// mitigation getting about a 99% hit rate with four tabs open.
|
||||
//
|
||||
// A size of 31 is used rather than a more logical power-of-two such as 32
|
||||
// since it is a prime number and provides fewer collisions when when used
|
||||
// with our hash algorithms.
|
||||
static const uint32_t kTableSize = 31;
|
||||
TLDCacheEntry mMruTable[kTableSize];
|
||||
|
||||
/**
|
||||
* Performs a lookup on the MRU table and provides a pointer to the hash
|
||||
* entry that matched or should be used for adding this host.
|
||||
*
|
||||
* @param aHost The host to lookup.
|
||||
* @param aEntry Out param, the entry in the MRU table to use.
|
||||
* @return True if a match was found, false if there was a miss.
|
||||
*/
|
||||
inline bool LookupForAdd(const nsACString& aHost, TLDCacheEntry** aEntry);
|
||||
};
|
||||
|
||||
#endif // EffectiveTLDService_h
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
import codecs
|
||||
import encodings.idna
|
||||
import imp
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
@ -34,12 +36,7 @@ def getEffectiveTLDs(path):
|
|||
assert domain not in domains, \
|
||||
"repeating domain %s makes no sense" % domain
|
||||
domains.add(domain)
|
||||
entries.append(entry)
|
||||
|
||||
# Sort the entries so we can use binary search on them.
|
||||
entries.sort(key=EffectiveTLDEntry.domain)
|
||||
|
||||
return entries
|
||||
yield entry
|
||||
|
||||
def _normalizeHostname(domain):
|
||||
"""
|
||||
|
@ -103,19 +100,37 @@ class EffectiveTLDEntry:
|
|||
def main(output, effective_tld_filename):
|
||||
"""
|
||||
effective_tld_filename is the effective TLD file to parse.
|
||||
A C++ array of { domain, exception, wild } entries representing the
|
||||
A C++ array of a binary representation of a DAFSA representing the
|
||||
eTLD file is then printed to output.
|
||||
"""
|
||||
|
||||
def boolStr(b):
|
||||
if b:
|
||||
return "true"
|
||||
return "false"
|
||||
# Find and load the `make_dafsa.py` script under xpcom/ds.
|
||||
tld_dir = os.path.dirname(effective_tld_filename)
|
||||
make_dafsa_py = os.path.join(tld_dir, '../../xpcom/ds/make_dafsa.py')
|
||||
sys.path.append(os.path.dirname(make_dafsa_py))
|
||||
with open(make_dafsa_py, 'r') as fh:
|
||||
make_dafsa = imp.load_module('script', fh, make_dafsa_py,
|
||||
('.py', 'r', imp.PY_SOURCE))
|
||||
|
||||
for etld in getEffectiveTLDs(effective_tld_filename):
|
||||
exception = boolStr(etld.exception())
|
||||
wild = boolStr(etld.wild())
|
||||
output.write('ETLD_ENTRY("%s", %s, %s)\n' % (etld.domain(), exception, wild))
|
||||
def typeEnum(etld):
|
||||
"""
|
||||
Maps the flags to the DAFSA's enum types.
|
||||
"""
|
||||
if etld.exception():
|
||||
return 1
|
||||
elif etld.wild():
|
||||
return 2
|
||||
else:
|
||||
return 0
|
||||
|
||||
def dafsa_words():
|
||||
"""
|
||||
make_dafsa expects lines of the form "<domain_name><enum_value>"
|
||||
"""
|
||||
for etld in getEffectiveTLDs(effective_tld_filename):
|
||||
yield "%s%d" % (etld.domain(), typeEnum(etld))
|
||||
|
||||
output.write(make_dafsa.words_to_cxx(dafsa_words()))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.stdout, sys.argv[1])
|
||||
|
|
|
@ -2198,10 +2198,6 @@ xulrunner)
|
|||
;;
|
||||
esac
|
||||
|
||||
if test -n "$MOZ_B2G"; then
|
||||
AC_DEFINE(MOZ_B2G)
|
||||
fi
|
||||
|
||||
# Graphene is a desktop runtime for running applications with a HTML UI.
|
||||
if test -n "$MOZ_GRAPHENE"; then
|
||||
AC_DEFINE(MOZ_GRAPHENE)
|
||||
|
@ -2219,9 +2215,7 @@ fi
|
|||
|
||||
AC_SUBST(MOZ_PHOENIX)
|
||||
AC_SUBST(MOZ_XULRUNNER)
|
||||
AC_SUBST(MOZ_B2G)
|
||||
AC_SUBST(MOZ_MULET)
|
||||
AC_SUBST(MOZ_B2G_VERSION)
|
||||
|
||||
dnl ========================================================
|
||||
dnl Ensure Android SDK and build-tools versions depending on
|
||||
|
@ -4025,10 +4019,6 @@ MOZ_ARG_DISABLE_BOOL(startupcache,
|
|||
MOZ_DISABLE_STARTUPCACHE=1,
|
||||
MOZ_DISABLE_STARTUPCACHE=)
|
||||
|
||||
dnl bug 988880: disable startup cache on b2g
|
||||
if test -n "$MOZ_B2G"; then
|
||||
MOZ_DISABLE_STARTUPCACHE=1
|
||||
fi
|
||||
if test -n "$MOZ_DISABLE_STARTUPCACHE"; then
|
||||
AC_DEFINE(MOZ_DISABLE_STARTUPCACHE)
|
||||
fi
|
||||
|
@ -4742,10 +4732,6 @@ else
|
|||
MOZ_APP_MAXVERSION=$MOZ_APP_VERSION
|
||||
fi
|
||||
|
||||
MOZ_B2G_VERSION=${MOZ_B2G_VERSION:-"1.0.0"}
|
||||
AC_DEFINE_UNQUOTED(MOZ_B2G_VERSION,"$MOZ_B2G_VERSION")
|
||||
AC_DEFINE_UNQUOTED(MOZ_B2G_OS_NAME,"$MOZ_B2G_OS_NAME")
|
||||
|
||||
AC_SUBST(MOZ_APP_NAME)
|
||||
AC_SUBST(MOZ_APP_REMOTINGNAME)
|
||||
AC_SUBST(MOZ_APP_DISPLAYNAME)
|
||||
|
|
|
@ -827,16 +827,8 @@ CreateErrorText(const char16_t* aDescription,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// XML Parsing Error: %1$S\nLocation: %2$S\nLine Number %3$u, Column %4$u:
|
||||
char16_t *message = nsTextFormatter::smprintf(msg.get(), aDescription,
|
||||
aSourceURL, aLineNumber,
|
||||
aColNumber);
|
||||
if (!message) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
aErrorString.Assign(message);
|
||||
free(message);
|
||||
|
||||
nsTextFormatter::ssprintf(aErrorString, msg.get(), aDescription,
|
||||
aSourceURL, aLineNumber, aColNumber);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -919,14 +911,9 @@ nsExpatDriver::HandleError()
|
|||
"Expected", msg);
|
||||
|
||||
// . Expected: </%S>.
|
||||
char16_t *message = nsTextFormatter::smprintf(msg.get(), tagName.get());
|
||||
if (!message) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsAutoString message;
|
||||
nsTextFormatter::ssprintf(message, msg.get(), tagName.get());
|
||||
description.Append(message);
|
||||
|
||||
free(message);
|
||||
}
|
||||
|
||||
// Adjust the column number so that it is one based rather than zero based.
|
||||
|
|
|
@ -79,13 +79,20 @@ SandboxBrokerPolicyFactory::SandboxBrokerPolicyFactory()
|
|||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
// Bug 1321134: DConf's single bit of shared memory
|
||||
if (const auto userDir = g_get_user_runtime_dir()) {
|
||||
// Bug 1321134: DConf's single bit of shared memory
|
||||
// The leaf filename is "user" by default, but is configurable.
|
||||
nsPrintfCString shmPath("%s/dconf/", userDir);
|
||||
policy->AddPrefix(rdwrcr, shmPath.get());
|
||||
#ifdef MOZ_PULSEAUDIO
|
||||
// PulseAudio, if it can't get server info from X11, will break
|
||||
// unless it can open this directory (or create it, but in our use
|
||||
// case we know it already exists). See bug 1335329.
|
||||
nsPrintfCString pulsePath("%s/pulse", userDir);
|
||||
policy->AddPath(rdonly, pulsePath.get());
|
||||
#endif // MOZ_PULSEAUDIO
|
||||
}
|
||||
#endif
|
||||
#endif // MOZ_WIDGET_GTK
|
||||
|
||||
// Read permissions
|
||||
policy->AddPath(rdonly, "/dev/urandom");
|
||||
|
@ -111,6 +118,13 @@ SandboxBrokerPolicyFactory::SandboxBrokerPolicyFactory()
|
|||
// Bug 1385715: NVIDIA PRIME support
|
||||
policy->AddPath(rdonly, "/proc/modules");
|
||||
|
||||
#ifdef MOZ_PULSEAUDIO
|
||||
// See bug 1384986 comment #1.
|
||||
if (const auto xauth = PR_GetEnv("XAUTHORITY")) {
|
||||
policy->AddPath(rdonly, xauth);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Configuration dirs in the homedir that we want to allow read
|
||||
// access to.
|
||||
mozilla::Array<const char*, 3> confDirs = {
|
||||
|
|
|
@ -19,6 +19,8 @@ SOURCES += [
|
|||
|
||||
if CONFIG['MOZ_ALSA']:
|
||||
DEFINES['MOZ_ALSA'] = True
|
||||
if CONFIG['MOZ_PULSEAUDIO']:
|
||||
DEFINES['MOZ_PULSEAUDIO'] = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/security/sandbox/linux', # SandboxLogging.h, SandboxInfo.h
|
||||
|
|
|
@ -1,644 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Temporary abstraction layer for common Fx Accounts operations.
|
||||
* For now, we will be using this module only from B2G but in the end we might
|
||||
* want this to be merged with FxAccounts.jsm and let other products also use
|
||||
* it.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["FxAccountsManager"];
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccounts.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "permissionManager",
|
||||
"@mozilla.org/permissionmanager;1",
|
||||
"nsIPermissionManager");
|
||||
|
||||
this.FxAccountsManager = {
|
||||
|
||||
init() {
|
||||
Services.obs.addObserver(this, ONLOGOUT_NOTIFICATION);
|
||||
Services.obs.addObserver(this, ON_FXA_UPDATE_NOTIFICATION);
|
||||
},
|
||||
|
||||
observe(aSubject, aTopic, aData) {
|
||||
// Both topics indicate our cache is invalid
|
||||
this._activeSession = null;
|
||||
|
||||
if (aData == ONVERIFIED_NOTIFICATION) {
|
||||
log.debug("FxAccountsManager: cache cleared, broadcasting: " + aData);
|
||||
Services.obs.notifyObservers(null, aData);
|
||||
}
|
||||
},
|
||||
|
||||
// We don't really need to save fxAccounts instance but this way we allow
|
||||
// to mock FxAccounts from tests.
|
||||
_fxAccounts: fxAccounts,
|
||||
|
||||
// We keep the session details here so consumers don't need to deal with
|
||||
// session tokens and are only required to handle the email.
|
||||
_activeSession: null,
|
||||
|
||||
// Are we refreshing our authentication? If so, allow attempts to sign in
|
||||
// while we are already signed in.
|
||||
_refreshing: false,
|
||||
|
||||
// We only expose the email and the verified status so far.
|
||||
get _user() {
|
||||
if (!this._activeSession || !this._activeSession.email) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
email: this._activeSession.email,
|
||||
verified: this._activeSession.verified,
|
||||
profile: this._activeSession.profile,
|
||||
}
|
||||
},
|
||||
|
||||
_error(aError, aDetails) {
|
||||
log.error(aError);
|
||||
let reason = {
|
||||
error: aError
|
||||
};
|
||||
if (aDetails) {
|
||||
reason.details = aDetails;
|
||||
}
|
||||
return Promise.reject(reason);
|
||||
},
|
||||
|
||||
_getError(aServerResponse) {
|
||||
if (!aServerResponse || !aServerResponse.error || !aServerResponse.error.errno) {
|
||||
return null;
|
||||
}
|
||||
let error = SERVER_ERRNO_TO_ERROR[aServerResponse.error.errno];
|
||||
return error;
|
||||
},
|
||||
|
||||
_serverError(aServerResponse) {
|
||||
let error = this._getError({ error: aServerResponse });
|
||||
return this._error(error ? error : ERROR_SERVER_ERROR, aServerResponse);
|
||||
},
|
||||
|
||||
// As with _fxAccounts, we don't really need this method, but this way we
|
||||
// allow tests to mock FxAccountsClient. By default, we want to return the
|
||||
// client used by the fxAccounts object because deep down they should have
|
||||
// access to the same hawk request object which will enable them to share
|
||||
// local clock skeq data.
|
||||
_getFxAccountsClient() {
|
||||
return this._fxAccounts.getAccountsClient();
|
||||
},
|
||||
|
||||
_signInSignUp(aMethod, aEmail, aPassword, aFetchKeys) {
|
||||
if (Services.io.offline) {
|
||||
return this._error(ERROR_OFFLINE);
|
||||
}
|
||||
|
||||
if (!aEmail) {
|
||||
return this._error(ERROR_INVALID_EMAIL);
|
||||
}
|
||||
|
||||
if (!aPassword) {
|
||||
return this._error(ERROR_INVALID_PASSWORD);
|
||||
}
|
||||
|
||||
// Check that there is no signed in account first.
|
||||
if ((!this._refreshing) && this._activeSession) {
|
||||
return this._error(ERROR_ALREADY_SIGNED_IN_USER, {
|
||||
user: this._user
|
||||
});
|
||||
}
|
||||
|
||||
let client = this._getFxAccountsClient();
|
||||
return this._fxAccounts.getSignedInUser().then(
|
||||
user => {
|
||||
if ((!this._refreshing) && user) {
|
||||
return this._error(ERROR_ALREADY_SIGNED_IN_USER, {
|
||||
user: this._user
|
||||
});
|
||||
}
|
||||
return client[aMethod](aEmail, aPassword, aFetchKeys);
|
||||
}
|
||||
).then(
|
||||
user => {
|
||||
let error = this._getError(user);
|
||||
if (!user || !user.uid || !user.sessionToken || error) {
|
||||
return this._error(error ? error : ERROR_INTERNAL_INVALID_USER, {
|
||||
user
|
||||
});
|
||||
}
|
||||
|
||||
// If the user object includes an email field, it may differ in
|
||||
// capitalization from what we sent down. This is the server's
|
||||
// canonical capitalization and should be used instead.
|
||||
user.email = user.email || aEmail;
|
||||
|
||||
// If we're using server-side sign to refreshAuthentication
|
||||
// we don't need to update local state; also because of two
|
||||
// interacting glitches we need to bypass an event emission.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1031580
|
||||
if (this._refreshing) {
|
||||
return Promise.resolve({user: this._user});
|
||||
}
|
||||
|
||||
return this._fxAccounts.setSignedInUser(user).then(
|
||||
() => {
|
||||
this._activeSession = user;
|
||||
log.debug("User signed in: " + JSON.stringify(this._user) +
|
||||
" - Account created " + (aMethod == "signUp"));
|
||||
|
||||
// There is no way to obtain the key fetch token afterwards
|
||||
// without login out the user and asking her to log in again.
|
||||
// Also, key fetch tokens are designed to be short-lived, so
|
||||
// we need to fetch kB as soon as we have the key fetch token.
|
||||
if (aFetchKeys) {
|
||||
this._fxAccounts.getKeys();
|
||||
}
|
||||
|
||||
return this._fxAccounts.getSignedInUserProfile().catch(error => {
|
||||
// Not fetching the profile is sad but the FxA logs will already
|
||||
// have noise.
|
||||
return null;
|
||||
});
|
||||
}
|
||||
).then(profile => {
|
||||
if (profile) {
|
||||
this._activeSession.profile = profile;
|
||||
}
|
||||
|
||||
return Promise.resolve({
|
||||
accountCreated: aMethod === "signUp",
|
||||
user: this._user
|
||||
});
|
||||
});
|
||||
},
|
||||
reason => { return this._serverError(reason); }
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine whether the incoming error means that the current account
|
||||
* has new server-side state via deletion or password change, and if so,
|
||||
* spawn the appropriate UI (sign in or refresh); otherwise re-reject.
|
||||
*
|
||||
* As of May 2014, the only HTTP call triggered by this._getAssertion()
|
||||
* is to /certificate/sign via:
|
||||
* FxAccounts.getAssertion()
|
||||
* FxAccountsInternal.getCertificateSigned()
|
||||
* FxAccountsClient.signCertificate()
|
||||
* See the latter method for possible (error code, errno) pairs.
|
||||
*/
|
||||
_handleGetAssertionError(reason, aAudience, aPrincipal) {
|
||||
log.debug("FxAccountsManager._handleGetAssertionError()");
|
||||
let errno = (reason ? reason.errno : NaN) || NaN;
|
||||
// If the previously valid email/password pair is no longer valid ...
|
||||
if (errno == ERRNO_INVALID_AUTH_TOKEN) {
|
||||
return this._fxAccounts.accountStatus().then(
|
||||
(exists) => {
|
||||
// ... if the email still maps to an account, the password
|
||||
// must have changed, so ask the user to enter the new one ...
|
||||
if (exists) {
|
||||
return this.getAccount().then(
|
||||
(user) => {
|
||||
return this._refreshAuthentication(aAudience, user.email,
|
||||
aPrincipal,
|
||||
true /* logoutOnFailure */);
|
||||
}
|
||||
);
|
||||
}
|
||||
// ... otherwise, the account was deleted, so ask for Sign In/Up
|
||||
return this._localSignOut().then(
|
||||
() => {
|
||||
return this._uiRequest(UI_REQUEST_SIGN_IN_FLOW, aAudience,
|
||||
aPrincipal);
|
||||
},
|
||||
(reason) => {
|
||||
// reject primary problem, not signout failure
|
||||
log.error("Signing out in response to server error threw: " +
|
||||
reason);
|
||||
return this._error(reason);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
return Promise.reject(reason.message ? { error: reason.message } : reason);
|
||||
},
|
||||
|
||||
_getAssertion(aAudience, aPrincipal) {
|
||||
return this._fxAccounts.getAssertion(aAudience).then(
|
||||
(result) => {
|
||||
if (aPrincipal) {
|
||||
this._addPermission(aPrincipal);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
(reason) => {
|
||||
return this._handleGetAssertionError(reason, aAudience, aPrincipal);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* "Refresh authentication" means:
|
||||
* Interactively demonstrate knowledge of the FxA password
|
||||
* for the currently logged-in account.
|
||||
* There are two very different scenarios:
|
||||
* 1) The password has changed on the server. Failure should log
|
||||
* the current account OUT.
|
||||
* 2) The person typing can't prove knowledge of the password used
|
||||
* to log in. Failure should do nothing.
|
||||
*/
|
||||
_refreshAuthentication(aAudience, aEmail, aPrincipal,
|
||||
logoutOnFailure = false) {
|
||||
this._refreshing = true;
|
||||
return this._uiRequest(UI_REQUEST_REFRESH_AUTH,
|
||||
aAudience, aPrincipal, aEmail).then(
|
||||
(assertion) => {
|
||||
this._refreshing = false;
|
||||
return assertion;
|
||||
},
|
||||
(reason) => {
|
||||
this._refreshing = false;
|
||||
if (logoutOnFailure) {
|
||||
return this._signOut().then(
|
||||
() => {
|
||||
return this._error(reason);
|
||||
}
|
||||
);
|
||||
}
|
||||
return this._error(reason);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
_localSignOut() {
|
||||
return this._fxAccounts.signOut(true);
|
||||
},
|
||||
|
||||
_signOut() {
|
||||
if (!this._activeSession) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// We clear the local session cache as soon as we get the onlogout
|
||||
// notification triggered within FxAccounts.signOut, so we save the
|
||||
// session token value to be able to remove the remote server session
|
||||
// in case that we have network connection.
|
||||
let sessionToken = this._activeSession.sessionToken;
|
||||
|
||||
return this._localSignOut().then(
|
||||
() => {
|
||||
// At this point the local session should already be removed.
|
||||
|
||||
// The client can create new sessions up to the limit (100?).
|
||||
// Orphaned tokens on the server will eventually be garbage collected.
|
||||
if (Services.io.offline) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
// Otherwise, we try to remove the remote session.
|
||||
let client = this._getFxAccountsClient();
|
||||
return client.signOut(sessionToken).then(
|
||||
result => {
|
||||
let error = this._getError(result);
|
||||
if (error) {
|
||||
return this._error(error, result);
|
||||
}
|
||||
log.debug("Signed out");
|
||||
return Promise.resolve();
|
||||
},
|
||||
reason => {
|
||||
return this._serverError(reason);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
_uiRequest(aRequest, aAudience, aPrincipal, aParams) {
|
||||
if (Services.io.offline) {
|
||||
return this._error(ERROR_OFFLINE);
|
||||
}
|
||||
let ui = Cc["@mozilla.org/fxaccounts/fxaccounts-ui-glue;1"]
|
||||
.createInstance(Ci.nsIFxAccountsUIGlue);
|
||||
if (!ui[aRequest]) {
|
||||
return this._error(ERROR_UI_REQUEST);
|
||||
}
|
||||
|
||||
if (!aParams || !Array.isArray(aParams)) {
|
||||
aParams = [aParams];
|
||||
}
|
||||
|
||||
return ui[aRequest].apply(this, aParams).then(
|
||||
result => {
|
||||
// Even if we get a successful result from the UI, the account will
|
||||
// most likely be unverified, so we cannot get an assertion.
|
||||
if (result && result.verified) {
|
||||
return this._getAssertion(aAudience, aPrincipal);
|
||||
}
|
||||
|
||||
return this._error(ERROR_UNVERIFIED_ACCOUNT, {
|
||||
user: result
|
||||
});
|
||||
},
|
||||
error => {
|
||||
return this._error(ERROR_UI_ERROR, error);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
_addPermission(aPrincipal) {
|
||||
// This will fail from tests cause we are running them in the child
|
||||
// process until we have chrome tests in b2g. Bug 797164.
|
||||
try {
|
||||
permissionManager.addFromPrincipal(aPrincipal, FXACCOUNTS_PERMISSION,
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
} catch (e) {
|
||||
log.warn("Could not add permission " + e);
|
||||
}
|
||||
},
|
||||
|
||||
// -- API --
|
||||
|
||||
signIn(aEmail, aPassword, aFetchKeys) {
|
||||
return this._signInSignUp("signIn", aEmail, aPassword, aFetchKeys);
|
||||
},
|
||||
|
||||
signUp(aEmail, aPassword, aFetchKeys) {
|
||||
return this._signInSignUp("signUp", aEmail, aPassword, aFetchKeys);
|
||||
},
|
||||
|
||||
signOut() {
|
||||
if (!this._activeSession) {
|
||||
// If there is no cached active session, we try to get it from the
|
||||
// account storage.
|
||||
return this.getAccount().then(
|
||||
result => {
|
||||
if (!result) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this._signOut();
|
||||
}
|
||||
);
|
||||
}
|
||||
return this._signOut();
|
||||
},
|
||||
|
||||
resendVerificationEmail() {
|
||||
return this._fxAccounts.resendVerificationEmail().then(
|
||||
(result) => {
|
||||
return result;
|
||||
},
|
||||
(error) => {
|
||||
return this._error(ERROR_SERVER_ERROR, error);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
getAccount() {
|
||||
// We check first if we have session details cached.
|
||||
if (this._activeSession) {
|
||||
// If our cache says that the account is not yet verified,
|
||||
// we kick off verification before returning what we have.
|
||||
if (!this._activeSession.verified) {
|
||||
this.verificationStatus(this._activeSession);
|
||||
}
|
||||
log.debug("Account " + JSON.stringify(this._user));
|
||||
return Promise.resolve(this._user);
|
||||
}
|
||||
|
||||
// If no cached information, we try to get it from the persistent storage.
|
||||
return this._fxAccounts.getSignedInUser().then(
|
||||
user => {
|
||||
if (!user || !user.email) {
|
||||
log.debug("No signed in account");
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
this._activeSession = user;
|
||||
// If we get a stored information of a not yet verified account,
|
||||
// we kick off verification before returning what we have.
|
||||
if (!user.verified) {
|
||||
this.verificationStatus(user);
|
||||
// Trying to get the profile for unverified users will fail, so we
|
||||
// don't even try in that case.
|
||||
log.debug("Account ", this._user);
|
||||
return Promise.resolve(this._user);
|
||||
}
|
||||
|
||||
return this._fxAccounts.getSignedInUserProfile().then(profile => {
|
||||
if (profile) {
|
||||
this._activeSession.profile = profile;
|
||||
}
|
||||
log.debug("Account ", this._user);
|
||||
return Promise.resolve(this._user);
|
||||
}).catch(error => {
|
||||
// FxAccounts logs already inform about the error.
|
||||
log.debug("Account ", this._user);
|
||||
return Promise.resolve(this._user);
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
queryAccount(aEmail) {
|
||||
log.debug("queryAccount " + aEmail);
|
||||
if (Services.io.offline) {
|
||||
return this._error(ERROR_OFFLINE);
|
||||
}
|
||||
|
||||
if (!aEmail) {
|
||||
return this._error(ERROR_INVALID_EMAIL);
|
||||
}
|
||||
|
||||
let client = this._getFxAccountsClient();
|
||||
return client.accountExists(aEmail).then(
|
||||
result => {
|
||||
log.debug("Account " + (result ? "" : "does not ") + "exists");
|
||||
let error = this._getError(result);
|
||||
if (error) {
|
||||
return this._error(error, result);
|
||||
}
|
||||
|
||||
return Promise.resolve({
|
||||
registered: result
|
||||
});
|
||||
},
|
||||
reason => { this._serverError(reason); }
|
||||
);
|
||||
},
|
||||
|
||||
verificationStatus() {
|
||||
log.debug("verificationStatus");
|
||||
if (!this._activeSession || !this._activeSession.sessionToken) {
|
||||
this._error(ERROR_NO_TOKEN_SESSION);
|
||||
}
|
||||
|
||||
// There is no way to unverify an already verified account, so we just
|
||||
// return the account details of a verified account
|
||||
if (this._activeSession.verified) {
|
||||
log.debug("Account already verified");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Services.io.offline) {
|
||||
log.warn("Offline; skipping verification.");
|
||||
return;
|
||||
}
|
||||
|
||||
let client = this._getFxAccountsClient();
|
||||
client.recoveryEmailStatus(this._activeSession.sessionToken).then(
|
||||
data => {
|
||||
let error = this._getError(data);
|
||||
if (error) {
|
||||
this._error(error, data);
|
||||
}
|
||||
// If the verification status has changed, update state.
|
||||
if (this._activeSession.verified != data.verified) {
|
||||
this._activeSession.verified = data.verified;
|
||||
this._fxAccounts.setSignedInUser(this._activeSession);
|
||||
this._fxAccounts.getSignedInUserProfile().then(profile => {
|
||||
if (profile) {
|
||||
this._activeSession.profile = profile;
|
||||
}
|
||||
}).catch(error => {
|
||||
// FxAccounts logs already inform about the error.
|
||||
});
|
||||
}
|
||||
log.debug(JSON.stringify(this._user));
|
||||
},
|
||||
reason => { this._serverError(reason); }
|
||||
);
|
||||
},
|
||||
|
||||
/*
|
||||
* Try to get an assertion for the given audience. Here we implement
|
||||
* the heart of the response to navigator.mozId.request() on device.
|
||||
* (We can also be called via the IAC API, but it's request() that
|
||||
* makes this method complex.) The state machine looks like this,
|
||||
* ignoring simple errors:
|
||||
* If no one is signed in, and we aren't suppressing the UI:
|
||||
* trigger the sign in flow.
|
||||
* else if we were asked to refresh and the grace period is up:
|
||||
* trigger the refresh flow.
|
||||
* else:
|
||||
* request user permission to share an assertion if we don't have it
|
||||
* already and ask the core code for an assertion, which might itself
|
||||
* trigger either the sign in or refresh flows (if our account
|
||||
* changed on the server).
|
||||
*
|
||||
* aOptions can include:
|
||||
* refreshAuthentication - (bool) Force re-auth.
|
||||
* silent - (bool) Prevent any UI interaction.
|
||||
* I.e., try to get an automatic assertion.
|
||||
*/
|
||||
getAssertion(aAudience, aPrincipal, aOptions) {
|
||||
if (!aAudience) {
|
||||
return this._error(ERROR_INVALID_AUDIENCE);
|
||||
}
|
||||
|
||||
let principal = aPrincipal;
|
||||
log.debug("FxAccountsManager.getAssertion() aPrincipal: ",
|
||||
principal.origin, principal.appId,
|
||||
principal.isInIsolatedMozBrowserElement);
|
||||
|
||||
return this.getAccount().then(
|
||||
user => {
|
||||
if (user) {
|
||||
// Three have-user cases to consider. First: are we unverified?
|
||||
if (!user.verified) {
|
||||
return this._error(ERROR_UNVERIFIED_ACCOUNT, {
|
||||
user
|
||||
});
|
||||
}
|
||||
// Second case: do we need to refresh?
|
||||
if (aOptions &&
|
||||
(typeof(aOptions.refreshAuthentication) != "undefined")) {
|
||||
let gracePeriod = aOptions.refreshAuthentication;
|
||||
if (typeof(gracePeriod) !== "number" || isNaN(gracePeriod)) {
|
||||
return this._error(ERROR_INVALID_REFRESH_AUTH_VALUE);
|
||||
}
|
||||
// Forcing refreshAuth to silent is a contradiction in terms,
|
||||
// though it might succeed silently if we didn't reject here.
|
||||
if (aOptions.silent) {
|
||||
return this._error(ERROR_NO_SILENT_REFRESH_AUTH);
|
||||
}
|
||||
let secondsSinceAuth = (Date.now() / 1000) -
|
||||
this._activeSession.authAt;
|
||||
if (secondsSinceAuth > gracePeriod) {
|
||||
return this._refreshAuthentication(aAudience, user.email,
|
||||
principal,
|
||||
false /* logoutOnFailure */);
|
||||
}
|
||||
}
|
||||
// Third case: we are all set *locally*. Probably we just return
|
||||
// the assertion, but the attempt might lead to the server saying
|
||||
// we are deleted or have a new password, which will trigger a flow.
|
||||
// Also we need to check if we have permission to get the assertion,
|
||||
// otherwise we need to show the forceAuth UI to let the user know
|
||||
// that the RP with no fxa permissions is trying to obtain an
|
||||
// assertion. Once the user authenticates herself in the forceAuth UI
|
||||
// the permission will be remembered by default.
|
||||
let permission = permissionManager.testPermissionFromPrincipal(
|
||||
principal,
|
||||
FXACCOUNTS_PERMISSION
|
||||
);
|
||||
if (permission == Ci.nsIPermissionManager.PROMPT_ACTION &&
|
||||
!this._refreshing) {
|
||||
return this._refreshAuthentication(aAudience, user.email,
|
||||
principal,
|
||||
false /* logoutOnFailure */);
|
||||
} else if (permission == Ci.nsIPermissionManager.DENY_ACTION &&
|
||||
!this._refreshing) {
|
||||
return this._error(ERROR_PERMISSION_DENIED);
|
||||
} else if (this._refreshing) {
|
||||
// If we are blocked asking for a password we should not continue
|
||||
// the getAssertion process.
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
return this._getAssertion(aAudience, principal);
|
||||
}
|
||||
log.debug("No signed in user");
|
||||
if (aOptions && aOptions.silent) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
return this._uiRequest(UI_REQUEST_SIGN_IN_FLOW, aAudience, principal);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
getKeys() {
|
||||
if (!Services.prefs.getBoolPref("services.sync.enabled", false)) {
|
||||
return Promise.reject(ERROR_SYNC_DISABLED);
|
||||
}
|
||||
|
||||
return this.getAccount().then(
|
||||
user => {
|
||||
if (!user) {
|
||||
log.debug("No signed in user");
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
if (!user.verified) {
|
||||
return this._error(ERROR_UNVERIFIED_ACCOUNT, {
|
||||
user
|
||||
});
|
||||
}
|
||||
|
||||
return this._fxAccounts.getKeys();
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
FxAccountsManager.init();
|
|
@ -31,9 +31,8 @@ this.FxAccountsStorageManager = function(options = {}) {
|
|||
baseDir: options.baseDir || OS.Constants.Path.profileDir,
|
||||
}
|
||||
this.plainStorage = new JSONStorage(this.options);
|
||||
// On b2g we have no loginManager for secure storage, and tests may want
|
||||
// to pretend secure storage isn't available.
|
||||
let useSecure = "useSecure" in options ? options.useSecure : haveLoginManager;
|
||||
// Tests may want to pretend secure storage isn't available.
|
||||
let useSecure = "useSecure" in options ? options.useSecure : true;
|
||||
if (useSecure) {
|
||||
this.secureStorage = new LoginManagerStorage();
|
||||
} else {
|
||||
|
@ -601,9 +600,3 @@ LoginManagerStorage.prototype = {
|
|||
return null;
|
||||
},
|
||||
}
|
||||
|
||||
// A global variable to indicate if the login manager is available - it doesn't
|
||||
// exist on b2g. Defined here as the use of preprocessor directives skews line
|
||||
// numbers in the runtime, meaning stack-traces etc end up off by a few lines.
|
||||
// Doing it at the end of the file makes that less of a pita.
|
||||
var haveLoginManager = !AppConstants.MOZ_B2G;
|
||||
|
|
|
@ -32,7 +32,3 @@ EXTRA_JS_MODULES += [
|
|||
'FxAccountsStorage.jsm',
|
||||
'FxAccountsWebChannel.jsm',
|
||||
]
|
||||
|
||||
# For now, we will only be using the FxA manager in B2G.
|
||||
if CONFIG['MOZ_B2G']:
|
||||
EXTRA_JS_MODULES += ['FxAccountsManager.jsm']
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "XPCOM")
|
||||
|
||||
if not CONFIG['MOZ_B2G']:
|
||||
TEST_DIRS += ['test']
|
||||
TEST_DIRS += ['test']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIStartupCache.idl',
|
||||
|
|
|
@ -40,13 +40,12 @@ doc-upload:
|
|||
worker:
|
||||
docker-image: {in-tree: "lint"}
|
||||
max-run-time: 1800
|
||||
taskcluster-proxy: true
|
||||
run:
|
||||
using: run-task
|
||||
command: >
|
||||
cd /home/worker/checkouts/gecko &&
|
||||
./mach doc-upload --bucket gecko-docs.mozilla.org --region us-west-2
|
||||
command: cd /home/worker/checkouts/gecko && ./mach doc-upload
|
||||
scopes:
|
||||
- secrets:get:project/releng/gecko/build/level-3/gecko-docs-upload
|
||||
- secrets:get:project/releng/gecko/build/level-{level}/gecko-docs-upload
|
||||
when:
|
||||
files-changed:
|
||||
- '**/*.py'
|
||||
|
|
|
@ -34,6 +34,8 @@ def common_setup(config, job, taskdesc):
|
|||
if run['checkout']:
|
||||
support_vcs_checkout(config, job, taskdesc)
|
||||
|
||||
taskdesc['worker'].setdefault('env', {})['MOZ_SCM_LEVEL'] = config.params['level']
|
||||
|
||||
|
||||
@run_job_using("docker-worker", "run-task", schema=run_task_schema)
|
||||
def docker_worker_run_task(config, job, taskdesc):
|
||||
|
|
|
@ -57,7 +57,9 @@ task_description_schema = Schema({
|
|||
Optional('routes'): [basestring],
|
||||
|
||||
# custom scopes for this task; any scopes required for the worker will be
|
||||
# added automatically
|
||||
# added automatically. The following parameters will be substituted in each
|
||||
# scope:
|
||||
# {level} -- the scm level of this push
|
||||
Optional('scopes'): [basestring],
|
||||
|
||||
# Tags
|
||||
|
@ -950,11 +952,12 @@ def add_index_routes(config, tasks):
|
|||
@transforms.add
|
||||
def build_task(config, tasks):
|
||||
for task in tasks:
|
||||
worker_type = task['worker-type'].format(level=str(config.params['level']))
|
||||
level = str(config.params['level'])
|
||||
worker_type = task['worker-type'].format(level=level)
|
||||
provisioner_id, worker_type = worker_type.split('/', 1)
|
||||
|
||||
routes = task.get('routes', [])
|
||||
scopes = task.get('scopes', [])
|
||||
scopes = [s.format(level=level) for s in task.get('scopes', [])]
|
||||
|
||||
# set up extra
|
||||
extra = task.get('extra', {})
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче