From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- docshell/base/WindowContext.cpp | 662 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 662 insertions(+) create mode 100644 docshell/base/WindowContext.cpp (limited to 'docshell/base/WindowContext.cpp') diff --git a/docshell/base/WindowContext.cpp b/docshell/base/WindowContext.cpp new file mode 100644 index 0000000000..02b4e7d4a8 --- /dev/null +++ b/docshell/base/WindowContext.cpp @@ -0,0 +1,662 @@ +/* -*- 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/WindowContext.h" +#include "mozilla/dom/WindowGlobalActorsBinding.h" +#include "mozilla/dom/WindowGlobalChild.h" +#include "mozilla/dom/WindowGlobalParent.h" +#include "mozilla/dom/SyncedContextInlines.h" +#include "mozilla/dom/BrowsingContext.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/UserActivationIPCUtils.h" +#include "mozilla/PermissionDelegateIPCUtils.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/ClearOnShutdown.h" +#include "nsGlobalWindowInner.h" +#include "nsIScriptError.h" +#include "nsIXULRuntime.h" +#include "nsRefPtrHashtable.h" +#include "nsContentUtils.h" + +namespace mozilla { +namespace dom { + +// Explicit specialization of the `Transaction` type. Required by the `extern +// template class` declaration in the header. +template class syncedcontext::Transaction; + +static LazyLogModule gWindowContextLog("WindowContext"); +static LazyLogModule gWindowContextSyncLog("WindowContextSync"); + +extern mozilla::LazyLogModule gUserInteractionPRLog; + +#define USER_ACTIVATION_LOG(msg, ...) \ + MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) + +using WindowContextByIdMap = nsTHashMap; +static StaticAutoPtr gWindowContexts; + +/* static */ +LogModule* WindowContext::GetLog() { return gWindowContextLog; } + +/* static */ +LogModule* WindowContext::GetSyncLog() { return gWindowContextSyncLog; } + +/* static */ +already_AddRefed WindowContext::GetById( + uint64_t aInnerWindowId) { + if (!gWindowContexts) { + return nullptr; + } + return do_AddRef(gWindowContexts->Get(aInnerWindowId)); +} + +BrowsingContextGroup* WindowContext::Group() const { + return mBrowsingContext->Group(); +} + +WindowGlobalParent* WindowContext::Canonical() { + MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); + return static_cast(this); +} + +bool WindowContext::IsCurrent() const { + return mBrowsingContext->mCurrentWindowContext == this; +} + +bool WindowContext::IsInBFCache() { + if (mozilla::SessionHistoryInParent()) { + return mBrowsingContext->IsInBFCache(); + } + return TopWindowContext()->GetWindowStateSaved(); +} + +nsGlobalWindowInner* WindowContext::GetInnerWindow() const { + return mWindowGlobalChild ? mWindowGlobalChild->GetWindowGlobal() : nullptr; +} + +Document* WindowContext::GetDocument() const { + nsGlobalWindowInner* innerWindow = GetInnerWindow(); + return innerWindow ? innerWindow->GetDocument() : nullptr; +} + +Document* WindowContext::GetExtantDoc() const { + nsGlobalWindowInner* innerWindow = GetInnerWindow(); + return innerWindow ? innerWindow->GetExtantDoc() : nullptr; +} + +WindowGlobalChild* WindowContext::GetWindowGlobalChild() const { + return mWindowGlobalChild; +} + +WindowContext* WindowContext::GetParentWindowContext() { + return mBrowsingContext->GetParentWindowContext(); +} + +WindowContext* WindowContext::TopWindowContext() { + WindowContext* current = this; + while (current->GetParentWindowContext()) { + current = current->GetParentWindowContext(); + } + return current; +} + +bool WindowContext::IsTop() const { return mBrowsingContext->IsTop(); } + +bool WindowContext::SameOriginWithTop() const { + return mBrowsingContext->SameOriginWithTop(); +} + +nsIGlobalObject* WindowContext::GetParentObject() const { + return xpc::NativeGlobal(xpc::PrivilegedJunkScope()); +} + +void WindowContext::AppendChildBrowsingContext( + BrowsingContext* aBrowsingContext) { + MOZ_DIAGNOSTIC_ASSERT(Group() == aBrowsingContext->Group(), + "Mismatched groups?"); + MOZ_DIAGNOSTIC_ASSERT(!mChildren.Contains(aBrowsingContext)); + + mChildren.AppendElement(aBrowsingContext); + if (!nsContentUtils::ShouldHideObjectOrEmbedImageDocument() || + !aBrowsingContext->IsEmbedderTypeObjectOrEmbed()) { + mNonSyntheticChildren.AppendElement(aBrowsingContext); + } + + // If we're the current WindowContext in our BrowsingContext, make sure to + // clear any cached `children` value. + if (IsCurrent()) { + BrowsingContext_Binding::ClearCachedChildrenValue(mBrowsingContext); + } +} + +void WindowContext::RemoveChildBrowsingContext( + BrowsingContext* aBrowsingContext) { + MOZ_DIAGNOSTIC_ASSERT(Group() == aBrowsingContext->Group(), + "Mismatched groups?"); + + mChildren.RemoveElement(aBrowsingContext); + mNonSyntheticChildren.RemoveElement(aBrowsingContext); + + // If we're the current WindowContext in our BrowsingContext, make sure to + // clear any cached `children` value. + if (IsCurrent()) { + BrowsingContext_Binding::ClearCachedChildrenValue(mBrowsingContext); + } +} + +void WindowContext::UpdateChildSynthetic(BrowsingContext* aBrowsingContext, + bool aIsSynthetic) { + MOZ_ASSERT(nsContentUtils::ShouldHideObjectOrEmbedImageDocument()); + if (aIsSynthetic) { + mNonSyntheticChildren.RemoveElement(aBrowsingContext); + } else { + // The same BrowsingContext will be reused for error pages, so it can be in + // the list already. + if (!mNonSyntheticChildren.Contains(aBrowsingContext)) { + mNonSyntheticChildren.AppendElement(aBrowsingContext); + } + } +} + +void WindowContext::SendCommitTransaction(ContentParent* aParent, + const BaseTransaction& aTxn, + uint64_t aEpoch) { + Unused << aParent->SendCommitWindowContextTransaction(this, aTxn, aEpoch); +} + +void WindowContext::SendCommitTransaction(ContentChild* aChild, + const BaseTransaction& aTxn, + uint64_t aEpoch) { + aChild->SendCommitWindowContextTransaction(this, aTxn, aEpoch); +} + +bool WindowContext::CheckOnlyOwningProcessCanSet(ContentParent* aSource) { + if (IsInProcess()) { + return true; + } + + if (XRE_IsParentProcess() && aSource) { + return Canonical()->GetContentParent() == aSource; + } + + return false; +} + +bool WindowContext::CanSet(FieldIndex, const bool& aIsSecure, + ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, + const bool& aAllowMixedContent, + ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, + const bool& aHasBeforeUnload, + ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, + const Maybe& aValue, + ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, + const bool& aValue, ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, + const bool& IsThirdPartyWindow, + ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, + const bool& aIsThirdPartyTrackingResourceWindow, + ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, + const bool& aShouldResistFingerprinting, + ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, + const bool& aIsSecureContext, + ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, + const bool& aIsOriginalFrameSource, + ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, const bool& aValue, + ContentParent* aSource) { + return IsTop(); +} + +bool WindowContext::CanSet(FieldIndex, + const uint32_t& aValue, ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, + const uint32_t& aValue, ContentParent* aSource) { + return IsTop() && CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, + const Maybe& aValue, + ContentParent* aSource) { + return IsTop(); +} + +bool WindowContext::CanSet(FieldIndex, const uint32_t&, + ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet( + FieldIndex, + const PermissionDelegateHandler::DelegatedPermissionList& aValue, + ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet( + FieldIndex, + const PermissionDelegateHandler::DelegatedPermissionList& aValue, + ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, const bool& aValue, + ContentParent* aSource) { + return CheckOnlyOwningProcessCanSet(aSource); +} + +bool WindowContext::CanSet(FieldIndex, bool aValue, + ContentParent* aSource) { + return (XRE_IsParentProcess() && !aSource) || + CheckOnlyOwningProcessCanSet(aSource); +} + +void WindowContext::DidSet(FieldIndex, bool aOldValue) { + RecomputeCanExecuteScripts(); +} + +bool WindowContext::CanSet(FieldIndex, bool, + ContentParent*) { + return XRE_IsParentProcess() && IsTop(); +} + +void WindowContext::RecomputeCanExecuteScripts(bool aApplyChanges) { + const bool old = mCanExecuteScripts; + if (!AllowJavascript()) { + // Scripting has been explicitly disabled on our WindowContext. + mCanExecuteScripts = false; + } else { + // Otherwise, inherit. + mCanExecuteScripts = mBrowsingContext->CanExecuteScripts(); + } + + if (aApplyChanges && old != mCanExecuteScripts) { + // Inform our active DOM window. + if (nsGlobalWindowInner* window = GetInnerWindow()) { + // Only update scriptability if the window is current. Windows will have + // scriptability disabled when entering the bfcache and updated when + // coming out. + if (window->IsCurrentInnerWindow()) { + auto& scriptability = + xpc::Scriptability::Get(window->GetGlobalJSObject()); + scriptability.SetWindowAllowsScript(mCanExecuteScripts); + } + } + + for (const RefPtr& child : Children()) { + child->RecomputeCanExecuteScripts(); + } + } +} + +void WindowContext::DidSet(FieldIndex, + bool aOldValue) { + MOZ_ASSERT( + TopWindowContext() == this, + "SHEntryHasUserInteraction can only be set on the top window context"); + // This field is set when the child notifies us of new user interaction, so we + // also set the currently active shentry in the parent as having interaction. + if (XRE_IsParentProcess() && mBrowsingContext) { + SessionHistoryEntry* activeEntry = + mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry(); + if (activeEntry && GetSHEntryHasUserInteraction()) { + activeEntry->SetHasUserInteraction(true); + } + } +} + +void WindowContext::DidSet(FieldIndex) { + MOZ_ASSERT_IF(!IsInProcess(), mUserGestureStart.IsNull()); + USER_ACTIVATION_LOG("Set user gesture activation %" PRIu8 + " for %s browsing context 0x%08" PRIx64, + static_cast(GetUserActivationState()), + XRE_IsParentProcess() ? "Parent" : "Child", Id()); + if (IsInProcess()) { + USER_ACTIVATION_LOG( + "Set user gesture start time for %s browsing context 0x%08" PRIx64, + XRE_IsParentProcess() ? "Parent" : "Child", Id()); + if (GetUserActivationState() == UserActivation::State::FullActivated) { + mUserGestureStart = TimeStamp::Now(); + } else if (GetUserActivationState() == UserActivation::State::None) { + mUserGestureStart = TimeStamp(); + } + } +} + +void WindowContext::DidSet(FieldIndex, + bool aOldValue) { + if (!aOldValue && GetHasReportedShadowDOMUsage() && IsInProcess()) { + MOZ_ASSERT(TopWindowContext() == this); + if (mBrowsingContext) { + Document* topLevelDoc = mBrowsingContext->GetDocument(); + if (topLevelDoc) { + nsAutoString uri; + Unused << topLevelDoc->GetDocumentURI(uri); + if (!uri.IsEmpty()) { + nsAutoString msg = u"Shadow DOM used in ["_ns + uri + + u"] or in some of its subdocuments."_ns; + nsContentUtils::ReportToConsoleNonLocalized( + msg, nsIScriptError::infoFlag, "DOM"_ns, topLevelDoc); + } + } + } + } +} + +bool WindowContext::CanSet(FieldIndex, bool aValue, + ContentParent* aSource) { + return !mozilla::SessionHistoryInParent() && IsTop() && + CheckOnlyOwningProcessCanSet(aSource); +} + +void WindowContext::CreateFromIPC(IPCInitializer&& aInit) { + MOZ_RELEASE_ASSERT(XRE_IsContentProcess(), + "Should be a WindowGlobalParent in the parent"); + + RefPtr bc = BrowsingContext::Get(aInit.mBrowsingContextId); + MOZ_RELEASE_ASSERT(bc); + + if (bc->IsDiscarded()) { + // If we have already closed our browsing context, the + // WindowGlobalChild actor is bound to be destroyed soon and it's + // safe to ignore creating the WindowContext. + return; + } + + RefPtr context = new WindowContext( + bc, aInit.mInnerWindowId, aInit.mOuterWindowId, std::move(aInit.mFields)); + context->Init(); +} + +void WindowContext::Init() { + MOZ_LOG(GetLog(), LogLevel::Debug, + ("Registering 0x%" PRIx64 " (bc=0x%" PRIx64 ")", mInnerWindowId, + mBrowsingContext->Id())); + + // Register the WindowContext in the `WindowContextByIdMap`. + if (!gWindowContexts) { + gWindowContexts = new WindowContextByIdMap(); + ClearOnShutdown(&gWindowContexts); + } + auto& entry = gWindowContexts->LookupOrInsert(mInnerWindowId); + MOZ_RELEASE_ASSERT(!entry, "Duplicate WindowContext for ID!"); + entry = this; + + // Register this to the browsing context. + mBrowsingContext->RegisterWindowContext(this); + Group()->Register(this); +} + +void WindowContext::Discard() { + MOZ_LOG(GetLog(), LogLevel::Debug, + ("Discarding 0x%" PRIx64 " (bc=0x%" PRIx64 ")", mInnerWindowId, + mBrowsingContext->Id())); + if (mIsDiscarded) { + return; + } + + mIsDiscarded = true; + if (gWindowContexts) { + gWindowContexts->Remove(InnerWindowId()); + } + mBrowsingContext->UnregisterWindowContext(this); + Group()->Unregister(this); +} + +void WindowContext::AddSecurityState(uint32_t aStateFlags) { + MOZ_ASSERT(TopWindowContext() == this); + MOZ_ASSERT((aStateFlags & + (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT | + nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT | + nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT | + nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT | + nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED | + nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED)) == + aStateFlags, + "Invalid flags specified!"); + + if (XRE_IsParentProcess()) { + Canonical()->AddSecurityState(aStateFlags); + } else { + ContentChild* child = ContentChild::GetSingleton(); + child->SendAddSecurityState(this, aStateFlags); + } +} + +void WindowContext::NotifyUserGestureActivation() { + Unused << SetUserActivationState(UserActivation::State::FullActivated); +} + +void WindowContext::NotifyResetUserGestureActivation() { + Unused << SetUserActivationState(UserActivation::State::None); +} + +bool WindowContext::HasBeenUserGestureActivated() { + return GetUserActivationState() != UserActivation::State::None; +} + +const TimeStamp& WindowContext::GetUserGestureStart() const { + MOZ_ASSERT(IsInProcess()); + return mUserGestureStart; +} + +bool WindowContext::HasValidTransientUserGestureActivation() { + MOZ_ASSERT(IsInProcess()); + + if (GetUserActivationState() != UserActivation::State::FullActivated) { + // mUserGestureStart should be null if the document hasn't ever been + // activated by user gesture + MOZ_ASSERT_IF(GetUserActivationState() == UserActivation::State::None, + mUserGestureStart.IsNull()); + return false; + } + + MOZ_ASSERT(!mUserGestureStart.IsNull(), + "mUserGestureStart shouldn't be null if the document has ever " + "been activated by user gesture"); + TimeDuration timeout = TimeDuration::FromMilliseconds( + StaticPrefs::dom_user_activation_transient_timeout()); + + return timeout <= TimeDuration() || + (TimeStamp::Now() - mUserGestureStart) <= timeout; +} + +bool WindowContext::ConsumeTransientUserGestureActivation() { + MOZ_ASSERT(IsInProcess()); + MOZ_ASSERT(IsCurrent()); + + if (!HasValidTransientUserGestureActivation()) { + return false; + } + + BrowsingContext* top = mBrowsingContext->Top(); + top->PreOrderWalk([&](BrowsingContext* aBrowsingContext) { + WindowContext* windowContext = aBrowsingContext->GetCurrentWindowContext(); + if (windowContext && windowContext->GetUserActivationState() == + UserActivation::State::FullActivated) { + Unused << windowContext->SetUserActivationState( + UserActivation::State::HasBeenActivated); + } + }); + + return true; +} + +bool WindowContext::CanShowPopup() { + uint32_t permit = GetPopupPermission(); + if (permit == nsIPermissionManager::ALLOW_ACTION) { + return true; + } + if (permit == nsIPermissionManager::DENY_ACTION) { + return false; + } + + return !StaticPrefs::dom_disable_open_during_load(); +} + +void WindowContext::TransientSetHasActivePeerConnections() { + if (!IsTop()) { + return; + } + + mFields.SetWithoutSyncing(true); +} + +WindowContext::IPCInitializer WindowContext::GetIPCInitializer() { + IPCInitializer init; + init.mInnerWindowId = mInnerWindowId; + init.mOuterWindowId = mOuterWindowId; + init.mBrowsingContextId = mBrowsingContext->Id(); + init.mFields = mFields.RawValues(); + return init; +} + +WindowContext::WindowContext(BrowsingContext* aBrowsingContext, + uint64_t aInnerWindowId, uint64_t aOuterWindowId, + FieldValues&& aInit) + : mFields(std::move(aInit)), + mInnerWindowId(aInnerWindowId), + mOuterWindowId(aOuterWindowId), + mBrowsingContext(aBrowsingContext) { + MOZ_ASSERT(mBrowsingContext); + MOZ_ASSERT(mInnerWindowId); + MOZ_ASSERT(mOuterWindowId); + RecomputeCanExecuteScripts(/* aApplyChanges */ false); +} + +WindowContext::~WindowContext() { + if (gWindowContexts) { + gWindowContexts->Remove(InnerWindowId()); + } +} + +JSObject* WindowContext::WrapObject(JSContext* cx, + JS::Handle aGivenProto) { + return WindowContext_Binding::Wrap(cx, this, aGivenProto); +} + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowContext) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(WindowContext) +NS_IMPL_CYCLE_COLLECTING_RELEASE(WindowContext) + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(WindowContext) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WindowContext) + if (gWindowContexts) { + gWindowContexts->Remove(tmp->InnerWindowId()); + } + + NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildren) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonSyntheticChildren) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WindowContext) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildren) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonSyntheticChildren) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +} // namespace dom + +namespace ipc { + +void IPDLParamTraits>::Write( + IPC::MessageWriter* aWriter, IProtocol* aActor, + const dom::MaybeDiscarded& aParam) { + uint64_t id = aParam.ContextId(); + WriteIPDLParam(aWriter, aActor, id); +} + +bool IPDLParamTraits>::Read( + IPC::MessageReader* aReader, IProtocol* aActor, + dom::MaybeDiscarded* aResult) { + uint64_t id = 0; + if (!ReadIPDLParam(aReader, aActor, &id)) { + return false; + } + + if (id == 0) { + *aResult = nullptr; + } else if (RefPtr wc = dom::WindowContext::GetById(id)) { + *aResult = std::move(wc); + } else { + aResult->SetDiscarded(id); + } + return true; +} + +void IPDLParamTraits::Write( + IPC::MessageWriter* aWriter, IProtocol* aActor, + const dom::WindowContext::IPCInitializer& aInit) { + // Write actor ID parameters. + WriteIPDLParam(aWriter, aActor, aInit.mInnerWindowId); + WriteIPDLParam(aWriter, aActor, aInit.mOuterWindowId); + WriteIPDLParam(aWriter, aActor, aInit.mBrowsingContextId); + WriteIPDLParam(aWriter, aActor, aInit.mFields); +} + +bool IPDLParamTraits::Read( + IPC::MessageReader* aReader, IProtocol* aActor, + dom::WindowContext::IPCInitializer* aInit) { + // Read actor ID parameters. + return ReadIPDLParam(aReader, aActor, &aInit->mInnerWindowId) && + ReadIPDLParam(aReader, aActor, &aInit->mOuterWindowId) && + ReadIPDLParam(aReader, aActor, &aInit->mBrowsingContextId) && + ReadIPDLParam(aReader, aActor, &aInit->mFields); +} + +template struct IPDLParamTraits; + +} // namespace ipc +} // namespace mozilla -- cgit v1.2.3