зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1734394 - Make Geckoview use the session store collector r=geckoview-reviewers,agi,farre,peterv
When the session storage prefs are enabled, GeckoSession updateSessionState will provide the bundle of information, including zoom, scroll, and form data, to the delegate. Currently works for Fission and on Fenix. Differential Revision: https://phabricator.services.mozilla.com/D148215
This commit is contained in:
Родитель
5601c9d71d
Коммит
df9bafbf73
|
@ -1109,6 +1109,10 @@ pref("browser.sessionstore.upgradeBackup.maxUpgradeBackups", 3);
|
|||
pref("browser.sessionstore.debug", false);
|
||||
// Forget closed windows/tabs after two weeks
|
||||
pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000);
|
||||
// Platform collects data for session store
|
||||
pref("browser.sessionstore.platform_collection", true);
|
||||
// Platform collects session storage data for session store
|
||||
pref("browser.sessionstore.collect_session_storage", true);
|
||||
|
||||
// Don't quit the browser when Ctrl + Q is pressed.
|
||||
pref("browser.quitShortcut.disabled", false);
|
||||
|
|
|
@ -2445,7 +2445,7 @@ nsresult CanonicalBrowsingContext::WriteSessionStorageToSessionStore(
|
|||
|
||||
void CanonicalBrowsingContext::UpdateSessionStoreSessionStorage(
|
||||
const std::function<void()>& aDone) {
|
||||
if constexpr (!SessionStoreUtils::NATIVE_LISTENER) {
|
||||
if (!StaticPrefs::browser_sessionstore_collect_session_storage_AtStartup()) {
|
||||
aDone();
|
||||
return;
|
||||
}
|
||||
|
@ -2478,7 +2478,7 @@ void CanonicalBrowsingContext::UpdateSessionStoreForStorage(
|
|||
}
|
||||
|
||||
void CanonicalBrowsingContext::MaybeScheduleSessionStoreUpdate() {
|
||||
if constexpr (!SessionStoreUtils::NATIVE_LISTENER) {
|
||||
if (!StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -5781,7 +5781,7 @@ nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
|
|||
}
|
||||
}
|
||||
|
||||
if constexpr (SessionStoreUtils::NATIVE_LISTENER) {
|
||||
if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
|
||||
if (IsForceReloadType(mLoadType)) {
|
||||
if (WindowContext* windowContext =
|
||||
mBrowsingContext->GetCurrentWindowContext()) {
|
||||
|
@ -6525,7 +6525,7 @@ nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
|
|||
// incorrectly overrides session store data from the following load.
|
||||
return NS_OK;
|
||||
}
|
||||
if constexpr (SessionStoreUtils::NATIVE_LISTENER) {
|
||||
if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
|
||||
if (WindowContext* windowContext =
|
||||
mBrowsingContext->GetCurrentWindowContext()) {
|
||||
using Change = SessionStoreChangeListener::Change;
|
||||
|
|
|
@ -3089,7 +3089,7 @@ nsresult nsFrameLoader::EnsureMessageManager() {
|
|||
NS_ENSURE_TRUE(mChildMessageManager, NS_ERROR_UNEXPECTED);
|
||||
|
||||
// Set up session store
|
||||
if constexpr (SessionStoreUtils::NATIVE_LISTENER) {
|
||||
if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
|
||||
if (XRE_IsParentProcess() && mIsTopLevelContent) {
|
||||
mSessionStoreChild = SessionStoreChild::GetOrCreate(
|
||||
GetExtantBrowsingContext(), mOwnerContent);
|
||||
|
|
|
@ -26,6 +26,18 @@ interface SessionStoreFormData {
|
|||
object toJSON();
|
||||
};
|
||||
|
||||
[GenerateConversionToJS]
|
||||
dictionary SessionStoreDisplaySize {
|
||||
unsigned long width;
|
||||
unsigned long height;
|
||||
};
|
||||
|
||||
[GenerateConversionToJS]
|
||||
dictionary SessionStoreZoomData {
|
||||
double resolution;
|
||||
SessionStoreDisplaySize displaySize;
|
||||
};
|
||||
|
||||
[ChromeOnly, Exposed=Window]
|
||||
interface SessionStoreScrollData {
|
||||
[Cached, Pure]
|
||||
|
|
|
@ -519,7 +519,7 @@ nsresult BrowserChild::Init(mozIDOMWindowProxy* aParent,
|
|||
|
||||
mIPCOpen = true;
|
||||
|
||||
if constexpr (SessionStoreUtils::NATIVE_LISTENER) {
|
||||
if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
|
||||
mSessionStoreChild = SessionStoreChild::GetOrCreate(mBrowsingContext);
|
||||
}
|
||||
|
||||
|
|
|
@ -918,7 +918,7 @@ void BackgroundSessionStorageManager::SetCurrentBrowsingContextId(
|
|||
}
|
||||
|
||||
void BackgroundSessionStorageManager::MaybeScheduleSessionStoreUpdate() {
|
||||
if constexpr (!SessionStoreUtils::NATIVE_LISTENER) {
|
||||
if (!StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ const DEFAULT_INTERVAL_MS = 1500;
|
|||
const TIMEOUT_DISABLED_PREF = "browser.sessionstore.debug.no_auto_updates";
|
||||
|
||||
const PREF_INTERVAL = "browser.sessionstore.interval";
|
||||
const PREF_SESSION_COLLECTION = "browser.sessionstore.platform_collection";
|
||||
|
||||
class Handler {
|
||||
constructor(store) {
|
||||
|
@ -598,13 +599,18 @@ class SessionStateAggregator extends GeckoViewChildModule {
|
|||
this.stateChangeNotifier = new StateChangeNotifier(this);
|
||||
|
||||
this.handlers = [
|
||||
new FormDataListener(this),
|
||||
new SessionHistoryListener(this),
|
||||
new ScrollPositionListener(this),
|
||||
this.stateChangeNotifier,
|
||||
this.messageQueue,
|
||||
];
|
||||
|
||||
if (!Services.prefs.getBoolPref(PREF_SESSION_COLLECTION, false)) {
|
||||
this.handlers.push(
|
||||
new FormDataListener(this),
|
||||
new ScrollPositionListener(this)
|
||||
);
|
||||
}
|
||||
|
||||
this.messageManager.addMessageListener("GeckoView:FlushSessionState", this);
|
||||
}
|
||||
|
||||
|
|
|
@ -1311,6 +1311,36 @@ public class GeckoSession {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
@WrapForJNI(calledFrom = "gecko")
|
||||
private void onUpdateSessionStore(final GeckoBundle aBundle) {
|
||||
ThreadUtils.runOnUiThread(
|
||||
() -> {
|
||||
final GeckoSession session = mOwner.get();
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
GeckoBundle scroll = aBundle.getBundle("scroll");
|
||||
if (scroll == null) {
|
||||
scroll = new GeckoBundle();
|
||||
aBundle.putBundle("scroll", scroll);
|
||||
}
|
||||
|
||||
// Here we unfortunately need to do some re-mapping since `zoom` is passed in a separate
|
||||
// bunds and we wish to keep the bundle format.
|
||||
scroll.putBundle("zoom", aBundle.getBundle("zoom"));
|
||||
final SessionState stateCache = session.mStateCache;
|
||||
stateCache.updateSessionState(aBundle);
|
||||
final SessionState state = new SessionState(stateCache);
|
||||
if (!state.isEmpty()) {
|
||||
final ProgressDelegate progressDelegate = session.getProgressDelegate();
|
||||
if (progressDelegate != null) {
|
||||
progressDelegate.onSessionStateChange(session, state);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class Listener implements BundleEventListener {
|
||||
|
|
|
@ -1455,6 +1455,24 @@
|
|||
value: 15000
|
||||
mirror: always
|
||||
|
||||
# Platform collection of data for session store
|
||||
- name: browser.sessionstore.platform_collection
|
||||
type: bool
|
||||
value: @IS_NOT_ANDROID@
|
||||
mirror: once
|
||||
|
||||
# Platform collection of session storage data for session store
|
||||
- name: browser.sessionstore.collect_session_storage
|
||||
type: bool
|
||||
value: @IS_NOT_ANDROID@
|
||||
mirror: once
|
||||
|
||||
# Platform collection of zoom data for session store
|
||||
- name: browser.sessionstore.collect_zoom
|
||||
type: bool
|
||||
value: @IS_NOT_ANDROID@
|
||||
mirror: once
|
||||
|
||||
# Causes SessionStore to ignore non-final update messages from
|
||||
# browser tabs that were not caused by a flush from the parent.
|
||||
# This is a testing flag and should not be used by end-users.
|
||||
|
|
|
@ -9,6 +9,7 @@ include protocol PInProcess;
|
|||
include SessionStoreTypes;
|
||||
|
||||
using mozilla::dom::MaybeDiscardedBrowsingContext from "mozilla/dom/BrowsingContext.h";
|
||||
using mozilla::dom::MaybeSessionStoreZoom from "mozilla/dom/SessionStoreScrollData.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -31,8 +32,8 @@ parent:
|
|||
* collected incrementally.
|
||||
*/
|
||||
async SessionStoreUpdate(
|
||||
nsCString? aDocShellCaps, bool? aPrivateMode, bool aNeedCollectSHistory,
|
||||
uint32_t aEpoch);
|
||||
nsCString? aDocShellCaps, bool? aPrivateMode, MaybeSessionStoreZoom aZoom,
|
||||
bool aNeedCollectSHistory, uint32_t aEpoch);
|
||||
|
||||
/**
|
||||
* Sends data to be stored to the session store. The collected data
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "nsPIDOMWindow.h"
|
||||
#include "nsTHashMap.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -31,6 +32,7 @@ using namespace mozilla::dom;
|
|||
namespace {
|
||||
constexpr auto kInput = u"input"_ns;
|
||||
constexpr auto kScroll = u"mozvisualscroll"_ns;
|
||||
constexpr auto kResize = u"mozvisualresize"_ns;
|
||||
|
||||
static constexpr char kNoAutoUpdates[] =
|
||||
"browser.sessionstore.debug.no_auto_updates";
|
||||
|
@ -120,14 +122,16 @@ SessionStoreChangeListener::HandleEvent(dom::Event* aEvent) {
|
|||
RecordChange(windowContext, Change::Input);
|
||||
} else if (eventType == kScroll) {
|
||||
RecordChange(windowContext, Change::Scroll);
|
||||
} else if (eventType == kResize && browsingContext->IsTop()) {
|
||||
RecordChange(windowContext, Change::Resize);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<SessionStoreChangeListener>
|
||||
SessionStoreChangeListener::Create(BrowsingContext* aBrowsingContext) {
|
||||
MOZ_RELEASE_ASSERT(SessionStoreUtils::NATIVE_LISTENER);
|
||||
MOZ_RELEASE_ASSERT(
|
||||
StaticPrefs::browser_sessionstore_platform_collection_AtStartup());
|
||||
if (!aBrowsingContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -181,6 +185,29 @@ static void CollectFormData(Document* aDocument,
|
|||
}
|
||||
}
|
||||
|
||||
static void GetZoom(BrowsingContext* aBrowsingContext,
|
||||
Maybe<SessionStoreZoom>& aZoom) {
|
||||
nsIDocShell* docShell = aBrowsingContext->GetDocShell();
|
||||
if (!docShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
PresShell* presShell = docShell->GetPresShell();
|
||||
if (!presShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
LayoutDeviceIntSize displaySize;
|
||||
|
||||
if (!nsLayoutUtils::GetContentViewerSize(presShell->GetPresContext(),
|
||||
displaySize)) {
|
||||
return;
|
||||
}
|
||||
|
||||
aZoom.emplace(presShell->GetResolution(), displaySize.width,
|
||||
displaySize.height);
|
||||
}
|
||||
|
||||
void SessionStoreChangeListener::FlushSessionStore() {
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
|
@ -189,6 +216,7 @@ void SessionStoreChangeListener::FlushSessionStore() {
|
|||
|
||||
bool collectSessionHistory = false;
|
||||
bool collectWireFrame = false;
|
||||
bool didResize = false;
|
||||
|
||||
for (auto& iter : mSessionStoreChanges) {
|
||||
WindowContext* windowContext = iter.GetKey();
|
||||
|
@ -230,6 +258,10 @@ void SessionStoreChangeListener::FlushSessionStore() {
|
|||
collectSessionHistory =
|
||||
collectSessionHistory || changes.contains(Change::SessionHistory);
|
||||
|
||||
if (presShell && changes.contains(Change::Resize)) {
|
||||
didResize = true;
|
||||
}
|
||||
|
||||
mSessionStoreChild->IncrementalSessionStoreUpdate(
|
||||
browsingContext, maybeFormData, maybeScroll, mEpoch);
|
||||
}
|
||||
|
@ -239,7 +271,13 @@ void SessionStoreChangeListener::FlushSessionStore() {
|
|||
}
|
||||
|
||||
mSessionStoreChanges.Clear();
|
||||
mSessionStoreChild->UpdateSessionStore(collectSessionHistory);
|
||||
|
||||
Maybe<SessionStoreZoom> zoom;
|
||||
if (didResize) {
|
||||
GetZoom(mBrowsingContext->Top(), zoom);
|
||||
}
|
||||
|
||||
mSessionStoreChild->UpdateSessionStore(collectSessionHistory, zoom);
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -314,6 +352,9 @@ void SessionStoreChangeListener::AddEventListeners() {
|
|||
if (EventTarget* target = GetEventTarget()) {
|
||||
target->AddSystemEventListener(kInput, this, false);
|
||||
target->AddSystemEventListener(kScroll, this, false);
|
||||
if (StaticPrefs::browser_sessionstore_collect_zoom_AtStartup()) {
|
||||
target->AddSystemEventListener(kResize, this, false);
|
||||
}
|
||||
mCurrentEventTarget = target;
|
||||
}
|
||||
}
|
||||
|
@ -322,6 +363,9 @@ void SessionStoreChangeListener::RemoveEventListeners() {
|
|||
if (mCurrentEventTarget) {
|
||||
mCurrentEventTarget->RemoveSystemEventListener(kInput, this, false);
|
||||
mCurrentEventTarget->RemoveSystemEventListener(kScroll, this, false);
|
||||
if (StaticPrefs::browser_sessionstore_collect_zoom_AtStartup()) {
|
||||
mCurrentEventTarget->RemoveSystemEventListener(kResize, this, false);
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentEventTarget = nullptr;
|
||||
|
|
|
@ -56,7 +56,7 @@ class SessionStoreChangeListener final : public nsINamed,
|
|||
|
||||
void FlushSessionStore();
|
||||
|
||||
enum class Change { Input, Scroll, SessionHistory, WireFrame };
|
||||
enum class Change { Input, Scroll, SessionHistory, WireFrame, Resize };
|
||||
|
||||
static SessionStoreChangeListener* CollectSessionStoreData(
|
||||
WindowContext* aWindowContext, const EnumSet<Change>& aChanges);
|
||||
|
|
|
@ -167,10 +167,12 @@ void SessionStoreChild::UpdateEventTargets() {
|
|||
}
|
||||
}
|
||||
|
||||
void SessionStoreChild::UpdateSessionStore(bool aSessionHistoryUpdate) {
|
||||
void SessionStoreChild::UpdateSessionStore(bool aSessionHistoryUpdate,
|
||||
const MaybeSessionStoreZoom& aZoom) {
|
||||
if (!mSessionStoreListener) {
|
||||
// This is the case when we're shutting down, and expect a final update.
|
||||
SessionStoreUpdate(Nothing(), Nothing(), aSessionHistoryUpdate, 0);
|
||||
SessionStoreUpdate(Nothing(), Nothing(), Nothing(), aSessionHistoryUpdate,
|
||||
0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -187,7 +189,7 @@ void SessionStoreChild::UpdateSessionStore(bool aSessionHistoryUpdate) {
|
|||
}
|
||||
|
||||
SessionStoreUpdate(
|
||||
docShellCaps, privatedMode,
|
||||
docShellCaps, privatedMode, aZoom,
|
||||
store->GetAndClearSHistoryChanged() || aSessionHistoryUpdate,
|
||||
mSessionStoreListener->GetEpoch());
|
||||
}
|
||||
|
@ -216,14 +218,15 @@ mozilla::ipc::IPCResult SessionStoreChild::RecvFlushTabState(
|
|||
|
||||
void SessionStoreChild::SessionStoreUpdate(
|
||||
const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
|
||||
const bool aNeedCollectSHistory, const uint32_t& aEpoch) {
|
||||
const MaybeSessionStoreZoom& aZoom, const bool aNeedCollectSHistory,
|
||||
const uint32_t& aEpoch) {
|
||||
if (XRE_IsContentProcess()) {
|
||||
Unused << SendSessionStoreUpdate(aDocShellCaps, aPrivatedMode,
|
||||
Unused << SendSessionStoreUpdate(aDocShellCaps, aPrivatedMode, aZoom,
|
||||
aNeedCollectSHistory, aEpoch);
|
||||
} else if (SessionStoreParent* sessionStoreParent =
|
||||
static_cast<SessionStoreParent*>(
|
||||
InProcessChild::ParentActorFor(this))) {
|
||||
sessionStoreParent->SessionStoreUpdate(aDocShellCaps, aPrivatedMode,
|
||||
sessionStoreParent->SessionStoreUpdate(aDocShellCaps, aPrivatedMode, aZoom,
|
||||
aNeedCollectSHistory, aEpoch);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/dom/PSessionStoreChild.h"
|
||||
#include "mozilla/dom/SessionStoreScrollData.h"
|
||||
#include "mozilla/dom/SessionStoreChangeListener.h"
|
||||
#include "mozilla/dom/SessionStoreListener.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
@ -31,12 +32,14 @@ class SessionStoreChild final : public PSessionStoreChild {
|
|||
void SetOwnerContent(Element* aElement);
|
||||
void Stop();
|
||||
void UpdateEventTargets();
|
||||
void UpdateSessionStore(bool aSessionHistoryUpdate = false);
|
||||
void UpdateSessionStore(bool aSessionHistoryUpdate = false,
|
||||
const MaybeSessionStoreZoom& aZoom = Nothing());
|
||||
void FlushSessionStore();
|
||||
void UpdateSHistoryChanges();
|
||||
|
||||
void SessionStoreUpdate(const Maybe<nsCString>& aDocShellCaps,
|
||||
const Maybe<bool>& aPrivatedMode,
|
||||
const MaybeSessionStoreZoom& aZoom,
|
||||
const bool aNeedCollectSHistory,
|
||||
const uint32_t& aEpoch);
|
||||
|
||||
|
|
|
@ -25,6 +25,12 @@
|
|||
#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;
|
||||
|
||||
|
@ -33,9 +39,81 @@ SessionStoreParent::SessionStoreParent(
|
|||
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::RootedObject object(jsapi.cx());
|
||||
ErrorResult rv;
|
||||
aFormData->ToJSON(jsapi.cx(), &object);
|
||||
|
||||
JS::RootedValue value(jsapi.cx(), JS::ObjectValue(*object));
|
||||
|
||||
if (NS_FAILED(jni::BoxData(jsapi.cx(), value, formDataBundle, true))) {
|
||||
JS_ClearPendingException(jsapi.cx());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (aScroll) {
|
||||
JS::RootedObject object(jsapi.cx());
|
||||
ErrorResult rv;
|
||||
aScroll->ToJSON(jsapi.cx(), &object);
|
||||
JS::RootedValue 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()) {
|
||||
|
@ -54,11 +132,19 @@ static void DoSessionStoreUpdate(CanonicalBrowsingContext* aBrowsingContext,
|
|||
RefPtr<BrowserSessionStore> sessionStore =
|
||||
BrowserSessionStore::GetOrCreate(aBrowsingContext->Top());
|
||||
|
||||
SessionStoreFormData* formData = sessionStore->GetFormdata();
|
||||
data.mFormdata.Construct(formData);
|
||||
if (!aFormData) {
|
||||
SessionStoreFormData* formData = sessionStore->GetFormdata();
|
||||
data.mFormdata.Construct(formData);
|
||||
} else {
|
||||
data.mFormdata.Construct(aFormData);
|
||||
}
|
||||
|
||||
SessionStoreScrollData* scroll = sessionStore->GetScroll();
|
||||
data.mScroll.Construct(scroll);
|
||||
if (!aScroll) {
|
||||
SessionStoreScrollData* scroll = sessionStore->GetScroll();
|
||||
data.mScroll.Construct(scroll);
|
||||
} else {
|
||||
data.mScroll.Construct(aScroll);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISessionStoreFunctions> funcs = do_ImportModule(
|
||||
"resource://gre/modules/SessionStoreFunctions.jsm", fallible);
|
||||
|
@ -83,6 +169,7 @@ static void DoSessionStoreUpdate(CanonicalBrowsingContext* aBrowsingContext,
|
|||
Unused << funcs->UpdateSessionStore(nullptr, aBrowsingContext, key, aEpoch,
|
||||
aNeedCollectSHistory, update);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SessionStoreParent::FlushAllSessionStoreChildren(
|
||||
const std::function<void()>& aDone) {
|
||||
|
@ -158,13 +245,23 @@ void SessionStoreParent::FinalFlushAllSessionStoreChildren(
|
|||
|
||||
mozilla::ipc::IPCResult SessionStoreParent::RecvSessionStoreUpdate(
|
||||
const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
|
||||
const bool aNeedCollectSHistory, const uint32_t& aEpoch) {
|
||||
const MaybeSessionStoreZoom& aZoom, const bool aNeedCollectSHistory,
|
||||
const uint32_t& aEpoch) {
|
||||
if (!mBrowsingContext) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
DoSessionStoreUpdate(mBrowsingContext, aDocShellCaps, aPrivatedMode,
|
||||
aNeedCollectSHistory, aEpoch);
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -173,6 +270,13 @@ mozilla::ipc::IPCResult SessionStoreParent::RecvIncrementalSessionStoreUpdate(
|
|||
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);
|
||||
|
@ -192,8 +296,9 @@ mozilla::ipc::IPCResult SessionStoreParent::RecvResetSessionStore(
|
|||
|
||||
void SessionStoreParent::SessionStoreUpdate(
|
||||
const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
|
||||
const bool aNeedCollectSHistory, const uint32_t& aEpoch) {
|
||||
Unused << RecvSessionStoreUpdate(aDocShellCaps, aPrivatedMode,
|
||||
const MaybeSessionStoreZoom& aZoom, const bool aNeedCollectSHistory,
|
||||
const uint32_t& aEpoch) {
|
||||
Unused << RecvSessionStoreUpdate(aDocShellCaps, aPrivatedMode, aZoom,
|
||||
aNeedCollectSHistory, aEpoch);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/PSessionStoreParent.h"
|
||||
#include "mozilla/dom/WindowGlobalParent.h"
|
||||
#include "mozilla/dom/SessionStoreScrollData.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
class BrowserParent;
|
||||
|
@ -31,7 +32,8 @@ class SessionStoreParent final : public PSessionStoreParent {
|
|||
*/
|
||||
mozilla::ipc::IPCResult RecvSessionStoreUpdate(
|
||||
const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
|
||||
const bool aNeedCollectSHistory, const uint32_t& aEpoch);
|
||||
const MaybeSessionStoreZoom& aZoom, const bool aNeedCollectSHistory,
|
||||
const uint32_t& aEpoch);
|
||||
|
||||
mozilla::ipc::IPCResult RecvIncrementalSessionStoreUpdate(
|
||||
const MaybeDiscarded<BrowsingContext>& aBrowsingContext,
|
||||
|
@ -48,6 +50,7 @@ class SessionStoreParent final : public PSessionStoreParent {
|
|||
friend class SessionStoreChild;
|
||||
void SessionStoreUpdate(const Maybe<nsCString>& aDocShellCaps,
|
||||
const Maybe<bool>& aPrivatedMode,
|
||||
const MaybeSessionStoreZoom& aZoom,
|
||||
const bool aNeedCollectSHistory,
|
||||
const uint32_t& aEpoch);
|
||||
|
||||
|
@ -65,6 +68,9 @@ class SessionStoreParent final : public PSessionStoreParent {
|
|||
already_AddRefed<SessionStoreParent::FlushTabStatePromise>
|
||||
FlushSessionStore();
|
||||
|
||||
bool mHasNewFormData = false;
|
||||
bool mHasNewScrollPosition = false;
|
||||
|
||||
RefPtr<CanonicalBrowsingContext> mBrowsingContext;
|
||||
RefPtr<BrowserSessionStore> mSessionStore;
|
||||
};
|
||||
|
|
|
@ -7,17 +7,24 @@
|
|||
#ifndef mozilla_dom_SessionStoreScrollData_h
|
||||
#define mozilla_dom_SessionStoreScrollData_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "nsPoint.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/dom/WindowGlobalParent.h"
|
||||
#include "mozilla/dom/SessionStoreChangeListener.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class BrowsingContext;
|
||||
class WindowGlobalParent;
|
||||
class OwningByteStringOrObjectOrNull;
|
||||
struct SessionStoreZoomData;
|
||||
|
||||
using SessionStoreZoom = mozilla::Tuple<float, uint32_t, uint32_t>;
|
||||
using MaybeSessionStoreZoom =
|
||||
mozilla::Maybe<mozilla::Tuple<float, uint32_t, uint32_t>>;
|
||||
|
||||
class SessionStoreScrollData final : public nsISupports,
|
||||
public nsWrapperCache,
|
||||
|
@ -53,6 +60,7 @@ class SessionStoreScrollData final : public nsISupports,
|
|||
~SessionStoreScrollData() = default;
|
||||
|
||||
nsPoint mScroll;
|
||||
MaybeSessionStoreZoom mZoom;
|
||||
nsTArray<RefPtr<SessionStoreScrollData>> mChildren;
|
||||
};
|
||||
|
||||
|
|
|
@ -139,15 +139,8 @@ class SessionStoreUtils {
|
|||
const nsTArray<SSCacheCopy>& aValues,
|
||||
Record<nsCString, Record<nsString, nsString>>& aStorage);
|
||||
|
||||
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_THUNDERBIRD) || \
|
||||
defined(MOZ_SUITE)
|
||||
static constexpr bool NATIVE_LISTENER = false;
|
||||
#else
|
||||
static constexpr bool NATIVE_LISTENER = true;
|
||||
#endif
|
||||
|
||||
static bool CopyProperty(JSContext* aCx, JS::Handle<JSObject*> aDst,
|
||||
JS::Handle<JSObject*> aSrc, const nsAString& aName);
|
||||
static bool CopyProperty(JSContext* aCx, JS::HandleObject aDst,
|
||||
JS::HandleObject aSrc, const nsAString& aName);
|
||||
|
||||
template <typename T>
|
||||
static bool CopyChildren(JSContext* aCx, JS::Handle<JSObject*> aDst,
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/java/EventCallbackWrappers.h"
|
||||
#include "mozilla/jni/GeckoBundleUtils.h"
|
||||
|
||||
// Disable the C++ 2a warning. See bug #1509926
|
||||
#if defined(__clang__)
|
||||
|
@ -38,266 +39,9 @@ bool CheckJS(JSContext* aCx, bool aResult) {
|
|||
return aResult;
|
||||
}
|
||||
|
||||
nsresult BoxString(JSContext* aCx, JS::HandleValue aData,
|
||||
jni::Object::LocalRef& aOut) {
|
||||
if (aData.isNullOrUndefined()) {
|
||||
aOut = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aData.isString());
|
||||
|
||||
JS::RootedString str(aCx, aData.toString());
|
||||
|
||||
if (JS::StringHasLatin1Chars(str)) {
|
||||
nsAutoJSString autoStr;
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, autoStr.init(aCx, str)), NS_ERROR_FAILURE);
|
||||
|
||||
// StringParam can automatically convert a nsString to jstring.
|
||||
aOut = jni::StringParam(autoStr, aOut.Env(), fallible);
|
||||
if (!aOut) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Two-byte string
|
||||
JNIEnv* const env = aOut.Env();
|
||||
const char16_t* chars;
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
size_t len = 0;
|
||||
chars = JS_GetTwoByteStringCharsAndLength(aCx, nogc, str, &len);
|
||||
if (chars) {
|
||||
aOut = jni::String::LocalRef::Adopt(
|
||||
env, env->NewString(reinterpret_cast<const jchar*>(chars), len));
|
||||
}
|
||||
}
|
||||
if (NS_WARN_IF(!CheckJS(aCx, !!chars) || !aOut)) {
|
||||
env->ExceptionClear();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult BoxObject(JSContext* aCx, JS::HandleValue aData,
|
||||
jni::Object::LocalRef& aOut);
|
||||
|
||||
template <typename Type, bool (JS::Value::*IsType)() const,
|
||||
Type (JS::Value::*ToType)() const, class ArrayType,
|
||||
typename ArrayType::LocalRef (*NewArray)(const Type*, size_t)>
|
||||
nsresult BoxArrayPrimitive(JSContext* aCx, JS::HandleObject aData,
|
||||
jni::Object::LocalRef& aOut, size_t aLength,
|
||||
JS::HandleValue aElement) {
|
||||
JS::RootedValue element(aCx);
|
||||
auto data = MakeUnique<Type[]>(aLength);
|
||||
data[0] = (aElement.get().*ToType)();
|
||||
|
||||
for (size_t i = 1; i < aLength; i++) {
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS_GetElement(aCx, aData, i, &element)),
|
||||
NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE((element.get().*IsType)(), NS_ERROR_INVALID_ARG);
|
||||
|
||||
data[i] = (element.get().*ToType)();
|
||||
}
|
||||
aOut = (*NewArray)(data.get(), aLength);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <class Type,
|
||||
nsresult (*Box)(JSContext*, JS::HandleValue, jni::Object::LocalRef&),
|
||||
typename IsType>
|
||||
nsresult BoxArrayObject(JSContext* aCx, JS::HandleObject aData,
|
||||
jni::Object::LocalRef& aOut, size_t aLength,
|
||||
JS::HandleValue aElement, IsType&& aIsType) {
|
||||
auto out = jni::ObjectArray::New<Type>(aLength);
|
||||
JS::RootedValue element(aCx);
|
||||
jni::Object::LocalRef jniElement(aOut.Env());
|
||||
|
||||
nsresult rv = (*Box)(aCx, aElement, jniElement);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
out->SetElement(0, jniElement);
|
||||
|
||||
for (size_t i = 1; i < aLength; i++) {
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS_GetElement(aCx, aData, i, &element)),
|
||||
NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(element.isNullOrUndefined() || aIsType(element),
|
||||
NS_ERROR_INVALID_ARG);
|
||||
|
||||
rv = (*Box)(aCx, element, jniElement);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
out->SetElement(i, jniElement);
|
||||
}
|
||||
aOut = out;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult BoxArray(JSContext* aCx, JS::HandleObject aData,
|
||||
jni::Object::LocalRef& aOut) {
|
||||
uint32_t length = 0;
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS::GetArrayLength(aCx, aData, &length)),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
if (!length) {
|
||||
// Always represent empty arrays as an empty boolean array.
|
||||
aOut = java::GeckoBundle::EMPTY_BOOLEAN_ARRAY();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We only check the first element's type. If the array has mixed types,
|
||||
// we'll throw an error during actual conversion.
|
||||
JS::RootedValue element(aCx);
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS_GetElement(aCx, aData, 0, &element)),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
if (element.isBoolean()) {
|
||||
return BoxArrayPrimitive<bool, &JS::Value::isBoolean, &JS::Value::toBoolean,
|
||||
jni::BooleanArray, &jni::BooleanArray::New>(
|
||||
aCx, aData, aOut, length, element);
|
||||
}
|
||||
|
||||
if (element.isInt32()) {
|
||||
nsresult rv =
|
||||
BoxArrayPrimitive<int32_t, &JS::Value::isInt32, &JS::Value::toInt32,
|
||||
jni::IntArray, &jni::IntArray::New>(aCx, aData, aOut,
|
||||
length, element);
|
||||
if (rv != NS_ERROR_INVALID_ARG) {
|
||||
return rv;
|
||||
}
|
||||
// Not int32, but we can still try a double array.
|
||||
}
|
||||
|
||||
if (element.isNumber()) {
|
||||
return BoxArrayPrimitive<double, &JS::Value::isNumber, &JS::Value::toNumber,
|
||||
jni::DoubleArray, &jni::DoubleArray::New>(
|
||||
aCx, aData, aOut, length, element);
|
||||
}
|
||||
|
||||
if (element.isNullOrUndefined() || element.isString()) {
|
||||
const auto isString = [](JS::HandleValue val) -> bool {
|
||||
return val.isString();
|
||||
};
|
||||
nsresult rv = BoxArrayObject<jni::String, &BoxString>(
|
||||
aCx, aData, aOut, length, element, isString);
|
||||
if (element.isString() || rv != NS_ERROR_INVALID_ARG) {
|
||||
return rv;
|
||||
}
|
||||
// First element was null/undefined, so it may still be an object array.
|
||||
}
|
||||
|
||||
const auto isObject = [aCx](JS::HandleValue val) -> bool {
|
||||
if (!val.isObject()) {
|
||||
return false;
|
||||
}
|
||||
bool array = false;
|
||||
JS::RootedObject obj(aCx, &val.toObject());
|
||||
// We don't support array of arrays.
|
||||
return CheckJS(aCx, JS::IsArrayObject(aCx, obj, &array)) && !array;
|
||||
};
|
||||
|
||||
if (element.isNullOrUndefined() || isObject(element)) {
|
||||
return BoxArrayObject<java::GeckoBundle, &BoxObject>(
|
||||
aCx, aData, aOut, length, element, isObject);
|
||||
}
|
||||
|
||||
NS_WARNING("Unknown type");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsresult BoxValue(JSContext* aCx, JS::HandleValue aData,
|
||||
jni::Object::LocalRef& aOut);
|
||||
|
||||
nsresult BoxObject(JSContext* aCx, JS::HandleValue aData,
|
||||
jni::Object::LocalRef& aOut) {
|
||||
if (aData.isNullOrUndefined()) {
|
||||
aOut = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aData.isObject());
|
||||
|
||||
JS::Rooted<JS::IdVector> ids(aCx, JS::IdVector(aCx));
|
||||
JS::RootedObject obj(aCx, &aData.toObject());
|
||||
|
||||
bool isArray = false;
|
||||
if (CheckJS(aCx, JS::IsArrayObject(aCx, obj, &isArray)) && isArray) {
|
||||
return BoxArray(aCx, obj, aOut);
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS_Enumerate(aCx, obj, &ids)), NS_ERROR_FAILURE);
|
||||
|
||||
const size_t length = ids.length();
|
||||
auto keys = jni::ObjectArray::New<jni::String>(length);
|
||||
auto values = jni::ObjectArray::New<jni::Object>(length);
|
||||
|
||||
// Iterate through each property of the JS object.
|
||||
for (size_t i = 0; i < ids.length(); i++) {
|
||||
const JS::RootedId id(aCx, ids[i]);
|
||||
JS::RootedValue idVal(aCx);
|
||||
JS::RootedValue val(aCx);
|
||||
jni::Object::LocalRef key(aOut.Env());
|
||||
jni::Object::LocalRef value(aOut.Env());
|
||||
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS_IdToValue(aCx, id, &idVal)),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
JS::RootedString idStr(aCx, JS::ToString(aCx, idVal));
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, !!idStr), NS_ERROR_FAILURE);
|
||||
|
||||
idVal.setString(idStr);
|
||||
NS_ENSURE_SUCCESS(BoxString(aCx, idVal, key), NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS_GetPropertyById(aCx, obj, id, &val)),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
nsresult rv = BoxValue(aCx, val, value);
|
||||
if (rv == NS_ERROR_INVALID_ARG && !JS_IsExceptionPending(aCx)) {
|
||||
nsAutoJSString autoStr;
|
||||
if (CheckJS(aCx, autoStr.init(aCx, idVal.toString()))) {
|
||||
JS_ReportErrorUTF8(aCx, u8"Invalid event data property %s",
|
||||
NS_ConvertUTF16toUTF8(autoStr).get());
|
||||
}
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
keys->SetElement(i, key);
|
||||
values->SetElement(i, value);
|
||||
}
|
||||
|
||||
aOut = java::GeckoBundle::New(keys, values);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult BoxValue(JSContext* aCx, JS::HandleValue aData,
|
||||
jni::Object::LocalRef& aOut) {
|
||||
if (aData.isNullOrUndefined()) {
|
||||
aOut = nullptr;
|
||||
} else if (aData.isBoolean()) {
|
||||
aOut = aData.toBoolean() ? java::sdk::Boolean::TRUE()
|
||||
: java::sdk::Boolean::FALSE();
|
||||
} else if (aData.isInt32()) {
|
||||
aOut = java::sdk::Integer::ValueOf(aData.toInt32());
|
||||
} else if (aData.isNumber()) {
|
||||
aOut = java::sdk::Double::New(aData.toNumber());
|
||||
} else if (aData.isString()) {
|
||||
return BoxString(aCx, aData, aOut);
|
||||
} else if (aData.isObject()) {
|
||||
return BoxObject(aCx, aData, aOut);
|
||||
} else {
|
||||
NS_WARNING("Unknown type");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult BoxData(const nsAString& aEvent, JSContext* aCx, JS::HandleValue aData,
|
||||
jni::Object::LocalRef& aOut, bool aObjectOnly) {
|
||||
nsresult rv = NS_ERROR_INVALID_ARG;
|
||||
|
||||
if (!aObjectOnly) {
|
||||
rv = BoxValue(aCx, aData, aOut);
|
||||
} else if (aData.isObject() || aData.isNullOrUndefined()) {
|
||||
rv = BoxObject(aCx, aData, aOut);
|
||||
}
|
||||
nsresult rv = jni::BoxData(aCx, aData, aOut, aObjectOnly);
|
||||
if (rv != NS_ERROR_INVALID_ARG) {
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@ class GeckoViewSupport final
|
|||
|
||||
void OnShowDynamicToolbar() const;
|
||||
|
||||
void OnUpdateSessionStore(mozilla::jni::Object::Param aBundle);
|
||||
|
||||
void PassExternalResponse(java::WebResponse::Param aResponse);
|
||||
|
||||
void AttachMediaSessionController(
|
||||
|
|
|
@ -0,0 +1,289 @@
|
|||
/* -*- 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/jni/GeckoBundleUtils.h"
|
||||
|
||||
#include "JavaBuiltins.h"
|
||||
#include "js/Warnings.h"
|
||||
#include "nsJSUtils.h"
|
||||
|
||||
#include "js/Array.h"
|
||||
|
||||
namespace mozilla::jni {
|
||||
namespace detail {
|
||||
bool CheckJS(JSContext* aCx, bool aResult) {
|
||||
if (!aResult) {
|
||||
JS_ClearPendingException(aCx);
|
||||
}
|
||||
return aResult;
|
||||
}
|
||||
|
||||
nsresult BoxString(JSContext* aCx, JS::HandleValue aData,
|
||||
jni::Object::LocalRef& aOut) {
|
||||
if (aData.isNullOrUndefined()) {
|
||||
aOut = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aData.isString());
|
||||
|
||||
JS::RootedString str(aCx, aData.toString());
|
||||
|
||||
if (JS::StringHasLatin1Chars(str)) {
|
||||
nsAutoJSString autoStr;
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, autoStr.init(aCx, str)), NS_ERROR_FAILURE);
|
||||
|
||||
// StringParam can automatically convert a nsString to jstring.
|
||||
aOut = jni::StringParam(autoStr, aOut.Env(), fallible);
|
||||
if (!aOut) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Two-byte string
|
||||
JNIEnv* const env = aOut.Env();
|
||||
const char16_t* chars;
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
size_t len = 0;
|
||||
chars = JS_GetTwoByteStringCharsAndLength(aCx, nogc, str, &len);
|
||||
if (chars) {
|
||||
aOut = jni::String::LocalRef::Adopt(
|
||||
env, env->NewString(reinterpret_cast<const jchar*>(chars), len));
|
||||
}
|
||||
}
|
||||
if (NS_WARN_IF(!CheckJS(aCx, !!chars) || !aOut)) {
|
||||
env->ExceptionClear();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult BoxObject(JSContext* aCx, JS::HandleValue aData,
|
||||
jni::Object::LocalRef& aOut);
|
||||
|
||||
template <typename Type, bool (JS::Value::*IsType)() const,
|
||||
Type (JS::Value::*ToType)() const, class ArrayType,
|
||||
typename ArrayType::LocalRef (*NewArray)(const Type*, size_t)>
|
||||
nsresult BoxArrayPrimitive(JSContext* aCx, JS::HandleObject aData,
|
||||
jni::Object::LocalRef& aOut, size_t aLength,
|
||||
JS::HandleValue aElement) {
|
||||
JS::RootedValue element(aCx);
|
||||
auto data = MakeUnique<Type[]>(aLength);
|
||||
data[0] = (aElement.get().*ToType)();
|
||||
|
||||
for (size_t i = 1; i < aLength; i++) {
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS_GetElement(aCx, aData, i, &element)),
|
||||
NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE((element.get().*IsType)(), NS_ERROR_INVALID_ARG);
|
||||
|
||||
data[i] = (element.get().*ToType)();
|
||||
}
|
||||
aOut = (*NewArray)(data.get(), aLength);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <class Type,
|
||||
nsresult (*Box)(JSContext*, JS::HandleValue, jni::Object::LocalRef&),
|
||||
typename IsType>
|
||||
nsresult BoxArrayObject(JSContext* aCx, JS::HandleObject aData,
|
||||
jni::Object::LocalRef& aOut, size_t aLength,
|
||||
JS::HandleValue aElement, IsType&& aIsType) {
|
||||
auto out = jni::ObjectArray::New<Type>(aLength);
|
||||
JS::RootedValue element(aCx);
|
||||
jni::Object::LocalRef jniElement(aOut.Env());
|
||||
|
||||
nsresult rv = (*Box)(aCx, aElement, jniElement);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
out->SetElement(0, jniElement);
|
||||
|
||||
for (size_t i = 1; i < aLength; i++) {
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS_GetElement(aCx, aData, i, &element)),
|
||||
NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(element.isNullOrUndefined() || aIsType(element),
|
||||
NS_ERROR_INVALID_ARG);
|
||||
|
||||
rv = (*Box)(aCx, element, jniElement);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
out->SetElement(i, jniElement);
|
||||
}
|
||||
aOut = out;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult BoxArray(JSContext* aCx, JS::HandleObject aData,
|
||||
jni::Object::LocalRef& aOut) {
|
||||
uint32_t length = 0;
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS::GetArrayLength(aCx, aData, &length)),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
if (!length) {
|
||||
// Always represent empty arrays as an empty boolean array.
|
||||
aOut = java::GeckoBundle::EMPTY_BOOLEAN_ARRAY();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We only check the first element's type. If the array has mixed types,
|
||||
// we'll throw an error during actual conversion.
|
||||
JS::RootedValue element(aCx);
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS_GetElement(aCx, aData, 0, &element)),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
if (element.isBoolean()) {
|
||||
return BoxArrayPrimitive<bool, &JS::Value::isBoolean, &JS::Value::toBoolean,
|
||||
jni::BooleanArray, &jni::BooleanArray::New>(
|
||||
aCx, aData, aOut, length, element);
|
||||
}
|
||||
|
||||
if (element.isInt32()) {
|
||||
nsresult rv =
|
||||
BoxArrayPrimitive<int32_t, &JS::Value::isInt32, &JS::Value::toInt32,
|
||||
jni::IntArray, &jni::IntArray::New>(aCx, aData, aOut,
|
||||
length, element);
|
||||
if (rv != NS_ERROR_INVALID_ARG) {
|
||||
return rv;
|
||||
}
|
||||
// Not int32, but we can still try a double array.
|
||||
}
|
||||
|
||||
if (element.isNumber()) {
|
||||
return BoxArrayPrimitive<double, &JS::Value::isNumber, &JS::Value::toNumber,
|
||||
jni::DoubleArray, &jni::DoubleArray::New>(
|
||||
aCx, aData, aOut, length, element);
|
||||
}
|
||||
|
||||
if (element.isNullOrUndefined() || element.isString()) {
|
||||
const auto isString = [](JS::HandleValue val) -> bool {
|
||||
return val.isString();
|
||||
};
|
||||
nsresult rv = BoxArrayObject<jni::String, &BoxString>(
|
||||
aCx, aData, aOut, length, element, isString);
|
||||
if (element.isString() || rv != NS_ERROR_INVALID_ARG) {
|
||||
return rv;
|
||||
}
|
||||
// First element was null/undefined, so it may still be an object array.
|
||||
}
|
||||
|
||||
const auto isObject = [aCx](JS::HandleValue val) -> bool {
|
||||
if (!val.isObject()) {
|
||||
return false;
|
||||
}
|
||||
bool array = false;
|
||||
JS::RootedObject obj(aCx, &val.toObject());
|
||||
// We don't support array of arrays.
|
||||
return CheckJS(aCx, JS::IsArrayObject(aCx, obj, &array)) && !array;
|
||||
};
|
||||
|
||||
if (element.isNullOrUndefined() || isObject(element)) {
|
||||
return BoxArrayObject<java::GeckoBundle, &BoxObject>(
|
||||
aCx, aData, aOut, length, element, isObject);
|
||||
}
|
||||
|
||||
NS_WARNING("Unknown type");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsresult BoxValue(JSContext* aCx, JS::HandleValue aData,
|
||||
jni::Object::LocalRef& aOut);
|
||||
|
||||
nsresult BoxObject(JSContext* aCx, JS::HandleValue aData,
|
||||
jni::Object::LocalRef& aOut) {
|
||||
if (aData.isNullOrUndefined()) {
|
||||
aOut = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aData.isObject());
|
||||
|
||||
JS::Rooted<JS::IdVector> ids(aCx, JS::IdVector(aCx));
|
||||
JS::RootedObject obj(aCx, &aData.toObject());
|
||||
|
||||
bool isArray = false;
|
||||
if (CheckJS(aCx, JS::IsArrayObject(aCx, obj, &isArray)) && isArray) {
|
||||
return BoxArray(aCx, obj, aOut);
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS_Enumerate(aCx, obj, &ids)), NS_ERROR_FAILURE);
|
||||
|
||||
const size_t length = ids.length();
|
||||
auto keys = jni::ObjectArray::New<jni::String>(length);
|
||||
auto values = jni::ObjectArray::New<jni::Object>(length);
|
||||
|
||||
// Iterate through each property of the JS object.
|
||||
for (size_t i = 0; i < ids.length(); i++) {
|
||||
const JS::RootedId id(aCx, ids[i]);
|
||||
JS::RootedValue idVal(aCx);
|
||||
JS::RootedValue val(aCx);
|
||||
jni::Object::LocalRef key(aOut.Env());
|
||||
jni::Object::LocalRef value(aOut.Env());
|
||||
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS_IdToValue(aCx, id, &idVal)),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
JS::RootedString idStr(aCx, JS::ToString(aCx, idVal));
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, !!idStr), NS_ERROR_FAILURE);
|
||||
|
||||
idVal.setString(idStr);
|
||||
NS_ENSURE_SUCCESS(BoxString(aCx, idVal, key), NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(CheckJS(aCx, JS_GetPropertyById(aCx, obj, id, &val)),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
nsresult rv = BoxValue(aCx, val, value);
|
||||
if (rv == NS_ERROR_INVALID_ARG && !JS_IsExceptionPending(aCx)) {
|
||||
nsAutoJSString autoStr;
|
||||
if (CheckJS(aCx, autoStr.init(aCx, idVal.toString()))) {
|
||||
JS_ReportErrorUTF8(aCx, "Invalid event data property %s",
|
||||
NS_ConvertUTF16toUTF8(autoStr).get());
|
||||
}
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
keys->SetElement(i, key);
|
||||
values->SetElement(i, value);
|
||||
}
|
||||
|
||||
aOut = java::GeckoBundle::New(keys, values);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult BoxValue(JSContext* aCx, JS::HandleValue aData,
|
||||
jni::Object::LocalRef& aOut) {
|
||||
if (aData.isNullOrUndefined()) {
|
||||
aOut = nullptr;
|
||||
} else if (aData.isBoolean()) {
|
||||
aOut = aData.toBoolean() ? java::sdk::Boolean::TRUE()
|
||||
: java::sdk::Boolean::FALSE();
|
||||
} else if (aData.isInt32()) {
|
||||
aOut = java::sdk::Integer::ValueOf(aData.toInt32());
|
||||
} else if (aData.isNumber()) {
|
||||
aOut = java::sdk::Double::New(aData.toNumber());
|
||||
} else if (aData.isString()) {
|
||||
return BoxString(aCx, aData, aOut);
|
||||
} else if (aData.isObject()) {
|
||||
return BoxObject(aCx, aData, aOut);
|
||||
} else {
|
||||
NS_WARNING("Unknown type");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
nsresult BoxData(JSContext* aCx, JS::HandleValue aData,
|
||||
jni::Object::LocalRef& aOut, bool aObjectOnly) {
|
||||
nsresult rv = NS_ERROR_INVALID_ARG;
|
||||
|
||||
if (!aObjectOnly) {
|
||||
rv = detail::BoxValue(aCx, aData, aOut);
|
||||
} else if (aData.isObject() || aData.isNullOrUndefined()) {
|
||||
rv = detail::BoxObject(aCx, aData, aOut);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
} // namespace mozilla::jni
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include "mozilla/java/GeckoBundleWrappers.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace jni {
|
||||
|
||||
|
@ -35,6 +37,9 @@ namespace jni {
|
|||
auto name = \
|
||||
mozilla::java::GeckoBundle::New(_##name##_jkeys, _##name##_jvalues);
|
||||
|
||||
nsresult BoxData(JSContext* aCx, JS::HandleValue aData,
|
||||
jni::Object::LocalRef& aOut, bool aObjectOnly);
|
||||
|
||||
} // namespace jni
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ EXPORTS.mozilla.jni += [
|
|||
|
||||
UNIFIED_SOURCES += [
|
||||
"Conversions.cpp",
|
||||
"GeckoBundleUtils.cpp",
|
||||
"Utils.cpp",
|
||||
]
|
||||
|
||||
|
|
|
@ -2093,6 +2093,15 @@ RefPtr<MozPromise<bool, bool, false>> nsWindow::OnLoadRequest(
|
|||
: nullptr;
|
||||
}
|
||||
|
||||
void nsWindow::OnUpdateSessionStore(mozilla::jni::Object::Param aBundle) {
|
||||
auto geckoViewSupport(mGeckoViewSupport.Access());
|
||||
if (!geckoViewSupport) {
|
||||
return;
|
||||
}
|
||||
|
||||
geckoViewSupport->OnUpdateSessionStore(aBundle);
|
||||
}
|
||||
|
||||
float nsWindow::GetDPI() {
|
||||
float dpi = 160.0f;
|
||||
|
||||
|
@ -2430,6 +2439,16 @@ void nsWindow::ShowDynamicToolbar() {
|
|||
acc->OnShowDynamicToolbar();
|
||||
}
|
||||
|
||||
void GeckoViewSupport::OnUpdateSessionStore(
|
||||
mozilla::jni::Object::Param aBundle) {
|
||||
GeckoSession::Window::LocalRef window(mGeckoViewWindow);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
window->OnUpdateSessionStore(aBundle);
|
||||
}
|
||||
|
||||
void nsWindow::OnSizeChanged(const gfx::IntSize& aSize) {
|
||||
ALOG("nsWindow: %p OnSizeChanged [%d %d]", (void*)this, aSize.width,
|
||||
aSize.height);
|
||||
|
|
|
@ -69,6 +69,8 @@ class nsWindow final : public nsBaseWidget {
|
|||
nsIPrincipal* aTriggeringPrincipal, bool aHasUserGesture,
|
||||
bool aIsTopLevel);
|
||||
|
||||
void OnUpdateSessionStore(mozilla::jni::Object::Param aBundle);
|
||||
|
||||
private:
|
||||
// Unique ID given to each widget, used to map Surfaces to widgets
|
||||
// in the CompositorSurfaceManager.
|
||||
|
|
Загрузка…
Ссылка в новой задаче