/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/SessionStoreParent.h" #include "mozilla/AlreadyAddRefed.h" #include "mozilla/Assertions.h" #include "mozilla/Maybe.h" #include "mozilla/RefPtr.h" #include "mozilla/ScopeExit.h" #include "mozilla/dom/BrowserParent.h" #include "mozilla/dom/BrowserSessionStore.h" #include "mozilla/dom/BrowserSessionStoreBinding.h" #include "mozilla/dom/BrowsingContext.h" #include "mozilla/dom/InProcessChild.h" #include "mozilla/dom/InProcessParent.h" #include "mozilla/dom/SessionStoreChild.h" #include "mozilla/dom/SessionStoreUtilsBinding.h" #include "SessionStoreFunctions.h" #include "nsISupports.h" #include "nsIXULRuntime.h" #include "nsImportModule.h" #include "nsIXPConnect.h" #ifdef ANDROID # include "mozilla/widget/nsWindow.h" # include "mozilla/jni/GeckoBundleUtils.h" # include "JavaBuiltins.h" #endif /* ANDROID */ using namespace mozilla; using namespace mozilla::dom; SessionStoreParent::SessionStoreParent( CanonicalBrowsingContext* aBrowsingContext, BrowserSessionStore* aSessionStore) : mBrowsingContext(aBrowsingContext), mSessionStore(aSessionStore) {} #ifdef ANDROID static void DoSessionStoreUpdate(CanonicalBrowsingContext* aBrowsingContext, const Maybe& aDocShellCaps, const Maybe& aPrivatedMode, SessionStoreFormData* aFormData, SessionStoreScrollData* aScroll, const MaybeSessionStoreZoom& aZoom, bool aNeedCollectSHistory, uint32_t aEpoch) { RefPtr sessionStore = BrowserSessionStore::GetOrCreate(aBrowsingContext->Top()); nsCOMPtr widget = aBrowsingContext->GetParentProcessWidgetContaining(); if (RefPtr window = nsWindow::From(widget)) { AutoJSAPI jsapi; if (!jsapi.Init(xpc::PrivilegedJunkScope())) { return; } jni::Object::LocalRef formDataBundle(jni::GetGeckoThreadEnv()); jni::Object::LocalRef scrollBundle(jni::GetGeckoThreadEnv()); if (aFormData) { JS::Rooted object(jsapi.cx()); ErrorResult rv; aFormData->ToJSON(jsapi.cx(), &object); JS::Rooted value(jsapi.cx(), JS::ObjectValue(*object)); if (NS_FAILED(jni::BoxData(jsapi.cx(), value, formDataBundle, true))) { JS_ClearPendingException(jsapi.cx()); return; } } if (aScroll) { JS::Rooted object(jsapi.cx()); ErrorResult rv; aScroll->ToJSON(jsapi.cx(), &object); JS::Rooted value(jsapi.cx(), JS::ObjectValue(*object)); if (NS_FAILED(jni::BoxData(jsapi.cx(), value, scrollBundle, true))) { JS_ClearPendingException(jsapi.cx()); return; } } GECKOBUNDLE_START(update); GECKOBUNDLE_PUT(update, "formdata", formDataBundle); GECKOBUNDLE_PUT(update, "scroll", scrollBundle); if (aZoom) { GECKOBUNDLE_START(zoomBundle); GECKOBUNDLE_PUT(zoomBundle, "resolution", java::sdk::Double::New(Get<0>(*aZoom))); GECKOBUNDLE_START(displaySizeBundle); GECKOBUNDLE_PUT(displaySizeBundle, "width", java::sdk::Integer::ValueOf(Get<1>(*aZoom))); GECKOBUNDLE_PUT(displaySizeBundle, "height", java::sdk::Integer::ValueOf(Get<2>(*aZoom))); GECKOBUNDLE_FINISH(displaySizeBundle); GECKOBUNDLE_PUT(zoomBundle, "displaySize", displaySizeBundle); GECKOBUNDLE_FINISH(zoomBundle); GECKOBUNDLE_PUT(update, "zoom", zoomBundle); } GECKOBUNDLE_FINISH(update); window->OnUpdateSessionStore(update); } } #else static void DoSessionStoreUpdate(CanonicalBrowsingContext* aBrowsingContext, const Maybe& aDocShellCaps, const Maybe& aPrivatedMode, SessionStoreFormData* aFormData, SessionStoreScrollData* aScroll, const MaybeSessionStoreZoom& aZoom, bool aNeedCollectSHistory, uint32_t aEpoch) { UpdateSessionStoreData data; if (aDocShellCaps.isSome()) { auto& disallow = data.mDisallow.Construct(); if (!aDocShellCaps->IsEmpty()) { disallow = aDocShellCaps.value(); } else { disallow.SetIsVoid(true); } } if (aPrivatedMode.isSome()) { data.mIsPrivate.Construct() = aPrivatedMode.value(); } RefPtr sessionStore = BrowserSessionStore::GetOrCreate(aBrowsingContext->Top()); if (!aFormData) { SessionStoreFormData* formData = sessionStore->GetFormdata(); data.mFormdata.Construct(formData); } else { data.mFormdata.Construct(aFormData); } if (!aScroll) { SessionStoreScrollData* scroll = sessionStore->GetScroll(); data.mScroll.Construct(scroll); } else { data.mScroll.Construct(aScroll); } nsCOMPtr funcs = do_ImportESModule( "resource://gre/modules/SessionStoreFunctions.sys.mjs", fallible); nsCOMPtr wrapped = do_QueryInterface(funcs); if (!wrapped) { return; } AutoJSAPI jsapi; if (!jsapi.Init(wrapped->GetJSObjectGlobal())) { return; } JS::Rooted update(jsapi.cx()); if (!ToJSValue(jsapi.cx(), data, &update)) { return; } JS::Rooted key(jsapi.cx(), aBrowsingContext->Top()->PermanentKey()); Unused << funcs->UpdateSessionStore(nullptr, aBrowsingContext, key, aEpoch, aNeedCollectSHistory, update); } #endif void SessionStoreParent::FlushAllSessionStoreChildren( const std::function& aDone) { if (!mBrowsingContext) { aDone(); return; } nsTArray> flushPromises; // We're special casing this for when the SessionStore{Child, Parent} have // been created in the same process. This is only ever true for the parent // process session store actor, and is needed because // nsFrameLoader::RequestTabStateFlush expect flushes to happen faster than we // can manage by using the common path of sending a message the // SessionStoreChild. Ideally we should be able to do just that, but not // without more work. if (InProcessParent::ChildActorFor(this)) { // Here we assume that the session store data collection only collect for in // (parent-)process content type browsing contexts, which we only flush one // session store actor. flushPromises.AppendElement(FlushSessionStore()); } else { // While here we flush all participating actors. BrowserParent* browserParent = static_cast(Manager()); browserParent->VisitAll([&flushPromises](BrowserParent* aBrowser) { if (PSessionStoreParent* sessionStoreParent = SingleManagedOrNull(aBrowser->ManagedPSessionStoreParent())) { flushPromises.AppendElement( static_cast(sessionStoreParent) ->FlushSessionStore()); } }); } RefPtr flushPromise = SessionStoreParent::FlushTabStatePromise::All( GetMainThreadSerialEventTarget(), flushPromises); mBrowsingContext->UpdateSessionStoreSessionStorage([aDone, flushPromise]() { flushPromise->Then(GetCurrentSerialEventTarget(), __func__, [aDone]() { aDone(); }); }); } already_AddRefed SessionStoreParent::FlushSessionStore() { if (!mBrowsingContext) { return nullptr; } RefPtr promise = SendFlushTabState(); return promise.forget(); } void SessionStoreParent::FinalFlushAllSessionStoreChildren( const std::function& aDone) { if (!mBrowsingContext) { aDone(); return; } SessionStoreChild* sessionStoreChild = static_cast(InProcessParent::ChildActorFor(this)); if (!sessionStoreChild || mozilla::SessionHistoryInParent()) { return FlushAllSessionStoreChildren(aDone); } sessionStoreChild->FlushSessionStore(); mBrowsingContext->UpdateSessionStoreSessionStorage(aDone); } mozilla::ipc::IPCResult SessionStoreParent::RecvSessionStoreUpdate( const Maybe& aDocShellCaps, const Maybe& aPrivatedMode, const MaybeSessionStoreZoom& aZoom, const bool aNeedCollectSHistory, const uint32_t& aEpoch) { if (!mBrowsingContext) { return IPC_OK(); } RefPtr formData = mHasNewFormData ? mSessionStore->GetFormdata() : nullptr; RefPtr scroll = mHasNewScrollPosition ? mSessionStore->GetScroll() : nullptr; DoSessionStoreUpdate(mBrowsingContext, aDocShellCaps, aPrivatedMode, formData, scroll, aZoom, aNeedCollectSHistory, aEpoch); mHasNewFormData = false; mHasNewScrollPosition = false; return IPC_OK(); } mozilla::ipc::IPCResult SessionStoreParent::RecvIncrementalSessionStoreUpdate( const MaybeDiscarded& aBrowsingContext, const Maybe& aFormData, const Maybe& aScrollPosition, uint32_t aEpoch) { if (!aBrowsingContext.IsNull()) { if (aFormData.isSome()) { mHasNewFormData = true; } if (aScrollPosition.isSome()) { mHasNewScrollPosition = true; } mSessionStore->UpdateSessionStore( aBrowsingContext.GetMaybeDiscarded()->Canonical(), aFormData, aScrollPosition, aEpoch); } return IPC_OK(); } mozilla::ipc::IPCResult SessionStoreParent::RecvResetSessionStore( const MaybeDiscarded& aBrowsingContext, uint32_t aEpoch) { if (!aBrowsingContext.IsNull()) { mSessionStore->RemoveSessionStore( aBrowsingContext.GetMaybeDiscarded()->Canonical()); } return IPC_OK(); } void SessionStoreParent::SessionStoreUpdate( const Maybe& aDocShellCaps, const Maybe& aPrivatedMode, const MaybeSessionStoreZoom& aZoom, const bool aNeedCollectSHistory, const uint32_t& aEpoch) { Unused << RecvSessionStoreUpdate(aDocShellCaps, aPrivatedMode, aZoom, aNeedCollectSHistory, aEpoch); } void SessionStoreParent::IncrementalSessionStoreUpdate( const MaybeDiscarded& aBrowsingContext, const Maybe& aFormData, const Maybe& aScrollPosition, uint32_t aEpoch) { Unused << RecvIncrementalSessionStoreUpdate(aBrowsingContext, aFormData, aScrollPosition, aEpoch); } void SessionStoreParent::ResetSessionStore( const MaybeDiscarded& aBrowsingContext, uint32_t aEpoch) { Unused << RecvResetSessionStore(aBrowsingContext, aEpoch); } NS_IMPL_CYCLE_COLLECTION(SessionStoreParent, mBrowsingContext, mSessionStore)