diff options
Diffstat (limited to 'toolkit/components/sessionstore/SessionStoreParent.cpp')
-rw-r--r-- | toolkit/components/sessionstore/SessionStoreParent.cpp | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/toolkit/components/sessionstore/SessionStoreParent.cpp b/toolkit/components/sessionstore/SessionStoreParent.cpp new file mode 100644 index 0000000000..15b930eb01 --- /dev/null +++ b/toolkit/components/sessionstore/SessionStoreParent.cpp @@ -0,0 +1,318 @@ +/* -*- 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<nsCString>& aDocShellCaps, + const Maybe<bool>& aPrivatedMode, + SessionStoreFormData* aFormData, + SessionStoreScrollData* aScroll, + const MaybeSessionStoreZoom& aZoom, + bool aNeedCollectSHistory, uint32_t aEpoch) { + RefPtr<BrowserSessionStore> sessionStore = + BrowserSessionStore::GetOrCreate(aBrowsingContext->Top()); + + nsCOMPtr<nsIWidget> widget = + aBrowsingContext->GetParentProcessWidgetContaining(); + if (RefPtr<nsWindow> 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<JSObject*> object(jsapi.cx()); + ErrorResult rv; + aFormData->ToJSON(jsapi.cx(), &object); + + JS::Rooted<JS::Value> 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<JSObject*> object(jsapi.cx()); + ErrorResult rv; + aScroll->ToJSON(jsapi.cx(), &object); + JS::Rooted<JS::Value> 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<nsCString>& aDocShellCaps, + const Maybe<bool>& 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<BrowserSessionStore> 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<nsISessionStoreFunctions> funcs = do_ImportESModule( + "resource://gre/modules/SessionStoreFunctions.sys.mjs", fallible); + nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(funcs); + if (!wrapped) { + return; + } + + AutoJSAPI jsapi; + if (!jsapi.Init(wrapped->GetJSObjectGlobal())) { + return; + } + + JS::Rooted<JS::Value> update(jsapi.cx()); + if (!ToJSValue(jsapi.cx(), data, &update)) { + return; + } + + JS::Rooted<JS::Value> key(jsapi.cx(), + aBrowsingContext->Top()->PermanentKey()); + + Unused << funcs->UpdateSessionStore(nullptr, aBrowsingContext, key, aEpoch, + aNeedCollectSHistory, update); +} +#endif + +void SessionStoreParent::FlushAllSessionStoreChildren( + const std::function<void()>& aDone) { + if (!mBrowsingContext) { + aDone(); + return; + } + + nsTArray<RefPtr<SessionStoreParent::FlushTabStatePromise>> 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<BrowserParent*>(Manager()); + browserParent->VisitAll([&flushPromises](BrowserParent* aBrowser) { + if (PSessionStoreParent* sessionStoreParent = + SingleManagedOrNull(aBrowser->ManagedPSessionStoreParent())) { + flushPromises.AppendElement( + static_cast<SessionStoreParent*>(sessionStoreParent) + ->FlushSessionStore()); + } + }); + } + + RefPtr<SessionStoreParent::FlushTabStatePromise::AllPromiseType> + flushPromise = SessionStoreParent::FlushTabStatePromise::All( + GetMainThreadSerialEventTarget(), flushPromises); + + mBrowsingContext->UpdateSessionStoreSessionStorage([aDone, flushPromise]() { + flushPromise->Then(GetCurrentSerialEventTarget(), __func__, + [aDone]() { aDone(); }); + }); +} + +already_AddRefed<SessionStoreParent::FlushTabStatePromise> +SessionStoreParent::FlushSessionStore() { + if (!mBrowsingContext) { + return nullptr; + } + + RefPtr<SessionStoreParent::FlushTabStatePromise> promise = + SendFlushTabState(); + return promise.forget(); +} + +void SessionStoreParent::FinalFlushAllSessionStoreChildren( + const std::function<void()>& aDone) { + if (!mBrowsingContext) { + aDone(); + return; + } + + SessionStoreChild* sessionStoreChild = + static_cast<SessionStoreChild*>(InProcessParent::ChildActorFor(this)); + if (!sessionStoreChild || mozilla::SessionHistoryInParent()) { + return FlushAllSessionStoreChildren(aDone); + } + + sessionStoreChild->FlushSessionStore(); + mBrowsingContext->UpdateSessionStoreSessionStorage(aDone); +} + +mozilla::ipc::IPCResult SessionStoreParent::RecvSessionStoreUpdate( + const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode, + const MaybeSessionStoreZoom& aZoom, const bool aNeedCollectSHistory, + const uint32_t& aEpoch) { + if (!mBrowsingContext) { + return IPC_OK(); + } + + RefPtr<SessionStoreFormData> formData = + mHasNewFormData ? mSessionStore->GetFormdata() : nullptr; + RefPtr<SessionStoreScrollData> 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<BrowsingContext>& aBrowsingContext, + const Maybe<FormData>& aFormData, const Maybe<nsPoint>& 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<BrowsingContext>& aBrowsingContext, uint32_t aEpoch) { + if (!aBrowsingContext.IsNull()) { + mSessionStore->RemoveSessionStore( + aBrowsingContext.GetMaybeDiscarded()->Canonical()); + } + return IPC_OK(); +} + +void SessionStoreParent::SessionStoreUpdate( + const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode, + const MaybeSessionStoreZoom& aZoom, const bool aNeedCollectSHistory, + const uint32_t& aEpoch) { + Unused << RecvSessionStoreUpdate(aDocShellCaps, aPrivatedMode, aZoom, + aNeedCollectSHistory, aEpoch); +} + +void SessionStoreParent::IncrementalSessionStoreUpdate( + const MaybeDiscarded<BrowsingContext>& aBrowsingContext, + const Maybe<FormData>& aFormData, const Maybe<nsPoint>& aScrollPosition, + uint32_t aEpoch) { + Unused << RecvIncrementalSessionStoreUpdate(aBrowsingContext, aFormData, + aScrollPosition, aEpoch); +} + +void SessionStoreParent::ResetSessionStore( + const MaybeDiscarded<BrowsingContext>& aBrowsingContext, uint32_t aEpoch) { + Unused << RecvResetSessionStore(aBrowsingContext, aEpoch); +} + +NS_IMPL_CYCLE_COLLECTION(SessionStoreParent, mBrowsingContext, mSessionStore) |