diff options
Diffstat (limited to 'dom/ipc/TabChild.cpp')
-rw-r--r-- | dom/ipc/TabChild.cpp | 3371 |
1 files changed, 3371 insertions, 0 deletions
diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp new file mode 100644 index 000000000..eaf4a32ed --- /dev/null +++ b/dom/ipc/TabChild.cpp @@ -0,0 +1,3371 @@ +/* -*- 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 "base/basictypes.h" + +#include "TabChild.h" + +#include "gfxPrefs.h" +#ifdef ACCESSIBILITY +#include "mozilla/a11y/DocAccessibleChild.h" +#endif +#include "Layers.h" +#include "ContentChild.h" +#include "TabParent.h" +#include "mozilla/Preferences.h" +#include "mozilla/BrowserElementParent.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/EventListenerManager.h" +#include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h" +#include "mozilla/plugins/PluginWidgetChild.h" +#include "mozilla/IMEStateManager.h" +#include "mozilla/ipc/DocumentRendererChild.h" +#include "mozilla/ipc/URIUtils.h" +#include "mozilla/layers/APZChild.h" +#include "mozilla/layers/APZCCallbackHelper.h" +#include "mozilla/layers/APZCTreeManager.h" +#include "mozilla/layers/APZEventState.h" +#include "mozilla/layers/ContentProcessController.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/DoubleTapToZoom.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/InputAPZContext.h" +#include "mozilla/layers/ShadowLayers.h" +#include "mozilla/layout/RenderFrameChild.h" +#include "mozilla/layout/RenderFrameParent.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/Move.h" +#include "mozilla/ProcessHangMonitor.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/TextEvents.h" +#include "mozilla/TouchEvents.h" +#include "mozilla/Unused.h" +#include "mozIApplication.h" +#include "nsContentUtils.h" +#include "nsCSSFrameConstructor.h" +#include "nsDocShell.h" +#include "nsEmbedCID.h" +#include "nsGlobalWindow.h" +#include <algorithm> +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#endif +#include "nsFilePickerProxy.h" +#include "mozilla/dom/Element.h" +#include "nsGlobalWindow.h" +#include "nsIBaseWindow.h" +#include "nsIBrowserDOMWindow.h" +#include "nsIDocumentInlines.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIDOMChromeWindow.h" +#include "nsIDOMDocument.h" +#include "nsIDOMEvent.h" +#include "nsIDOMWindow.h" +#include "nsIDOMWindowUtils.h" +#include "nsFocusManager.h" +#include "EventStateManager.h" +#include "nsIDocShell.h" +#include "nsIFrame.h" +#include "nsIURI.h" +#include "nsIURIFixup.h" +#include "nsCDefaultURIFixup.h" +#include "nsIWebBrowser.h" +#include "nsIWebBrowserFocus.h" +#include "nsIWebBrowserSetup.h" +#include "nsIWebProgress.h" +#include "nsIXULRuntime.h" +#include "nsPIDOMWindow.h" +#include "nsPIWindowRoot.h" +#include "nsLayoutUtils.h" +#include "nsPrintfCString.h" +#include "nsThreadUtils.h" +#include "nsViewManager.h" +#include "nsWeakReference.h" +#include "nsWindowWatcher.h" +#include "PermissionMessageUtils.h" +#include "PuppetWidget.h" +#include "StructuredCloneData.h" +#include "nsViewportInfo.h" +#include "nsILoadContext.h" +#include "ipc/nsGUIEventIPC.h" +#include "mozilla/gfx/Matrix.h" +#include "UnitTransforms.h" +#include "ClientLayerManager.h" +#include "LayersLogging.h" +#include "nsDOMClassInfoID.h" +#include "nsColorPickerProxy.h" +#include "nsDatePickerProxy.h" +#include "nsContentPermissionHelper.h" +#include "nsPresShell.h" +#include "nsIAppsService.h" +#include "nsNetUtil.h" +#include "nsIPermissionManager.h" +#include "nsIURILoader.h" +#include "nsIScriptError.h" +#include "mozilla/EventForwards.h" +#include "nsDeviceContext.h" +#include "nsSandboxFlags.h" +#include "FrameLayerBuilder.h" +#include "VRManagerChild.h" +#include "nsICommandParams.h" +#include "nsISHistory.h" +#include "nsQueryObject.h" +#include "GroupedSHistory.h" +#include "nsIHttpChannel.h" +#include "mozilla/dom/DocGroup.h" + +#ifdef NS_PRINTING +#include "nsIPrintSession.h" +#include "nsIPrintSettings.h" +#include "nsIPrintSettingsService.h" +#include "nsIWebBrowserPrint.h" +#endif + +#define BROWSER_ELEMENT_CHILD_SCRIPT \ + NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js") + +#define TABC_LOG(...) +// #define TABC_LOG(...) printf_stderr("TABC: " __VA_ARGS__) + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::dom::ipc; +using namespace mozilla::dom::workers; +using namespace mozilla::ipc; +using namespace mozilla::layers; +using namespace mozilla::layout; +using namespace mozilla::docshell; +using namespace mozilla::widget; +using namespace mozilla::jsipc; +using mozilla::layers::GeckoContentController; + +NS_IMPL_ISUPPORTS(ContentListener, nsIDOMEventListener) +NS_IMPL_ISUPPORTS(TabChildSHistoryListener, + nsISHistoryListener, + nsIPartialSHistoryListener, + nsISupportsWeakReference) + +static const CSSSize kDefaultViewportSize(980, 480); + +static const char BEFORE_FIRST_PAINT[] = "before-first-paint"; + +typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap; +static TabChildMap* sTabChildren; + +TabChildBase::TabChildBase() + : mTabChildGlobal(nullptr) +{ + mozilla::HoldJSObjects(this); +} + +TabChildBase::~TabChildBase() +{ + mAnonymousGlobalScopes.Clear(); + mozilla::DropJSObjects(this); +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(TabChildBase) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TabChildBase) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChildGlobal) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousGlobalScopes) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebBrowserChrome) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TabChildBase) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChildGlobal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebBrowserChrome) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(TabChildBase) + tmp->nsMessageManagerScriptExecutor::Trace(aCallbacks, aClosure); +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabChildBase) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(TabChildBase) +NS_IMPL_CYCLE_COLLECTING_RELEASE(TabChildBase) + +already_AddRefed<nsIDocument> +TabChildBase::GetDocument() const +{ + nsCOMPtr<nsIDOMDocument> domDoc; + WebNavigation()->GetDocument(getter_AddRefs(domDoc)); + nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc)); + return doc.forget(); +} + +already_AddRefed<nsIPresShell> +TabChildBase::GetPresShell() const +{ + nsCOMPtr<nsIPresShell> result; + if (nsCOMPtr<nsIDocument> doc = GetDocument()) { + result = doc->GetShell(); + } + return result.forget(); +} + +void +TabChildBase::DispatchMessageManagerMessage(const nsAString& aMessageName, + const nsAString& aJSONData) +{ + AutoSafeJSContext cx; + JS::Rooted<JS::Value> json(cx, JS::NullValue()); + StructuredCloneData data; + if (JS_ParseJSON(cx, + static_cast<const char16_t*>(aJSONData.BeginReading()), + aJSONData.Length(), + &json)) { + ErrorResult rv; + data.Write(cx, json, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return; + } + } + + nsCOMPtr<nsIXPConnectJSObjectHolder> kungFuDeathGrip(GetGlobal()); + // Let the BrowserElementScrolling helper (if it exists) for this + // content manipulate the frame state. + RefPtr<nsFrameMessageManager> mm = + static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get()); + mm->ReceiveMessage(static_cast<EventTarget*>(mTabChildGlobal), nullptr, + aMessageName, false, &data, nullptr, nullptr, nullptr); +} + +bool +TabChildBase::UpdateFrameHandler(const FrameMetrics& aFrameMetrics) +{ + MOZ_ASSERT(aFrameMetrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID); + + if (aFrameMetrics.IsRootContent()) { + if (nsCOMPtr<nsIPresShell> shell = GetPresShell()) { + // Guard against stale updates (updates meant for a pres shell which + // has since been torn down and destroyed). + if (aFrameMetrics.GetPresShellId() == shell->GetPresShellId()) { + ProcessUpdateFrame(aFrameMetrics); + return true; + } + } + } else { + // aFrameMetrics.mIsRoot is false, so we are trying to update a subframe. + // This requires special handling. + FrameMetrics newSubFrameMetrics(aFrameMetrics); + APZCCallbackHelper::UpdateSubFrame(newSubFrameMetrics); + return true; + } + return true; +} + +void +TabChildBase::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics) +{ + if (!mGlobal || !mTabChildGlobal) { + return; + } + + FrameMetrics newMetrics = aFrameMetrics; + APZCCallbackHelper::UpdateRootFrame(newMetrics); +} + +NS_IMETHODIMP +ContentListener::HandleEvent(nsIDOMEvent* aEvent) +{ + RemoteDOMEvent remoteEvent; + remoteEvent.mEvent = do_QueryInterface(aEvent); + NS_ENSURE_STATE(remoteEvent.mEvent); + mTabChild->SendEvent(remoteEvent); + return NS_OK; +} + +class TabChild::DelayedDeleteRunnable final + : public Runnable +{ + RefPtr<TabChild> mTabChild; + +public: + explicit DelayedDeleteRunnable(TabChild* aTabChild) + : mTabChild(aTabChild) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aTabChild); + } + +private: + ~DelayedDeleteRunnable() + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mTabChild); + } + + NS_IMETHOD + Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mTabChild); + + // Check in case ActorDestroy was called after RecvDestroy message. + if (mTabChild->IPCOpen()) { + Unused << PBrowserChild::Send__delete__(mTabChild); + } + + mTabChild = nullptr; + return NS_OK; + } +}; + +namespace { +StaticRefPtr<TabChild> sPreallocatedTab; + +std::map<TabId, RefPtr<TabChild>>& +NestedTabChildMap() +{ + MOZ_ASSERT(NS_IsMainThread()); + static std::map<TabId, RefPtr<TabChild>> sNestedTabChildMap; + return sNestedTabChildMap; +} +} // namespace + +already_AddRefed<TabChild> +TabChild::FindTabChild(const TabId& aTabId) +{ + auto iter = NestedTabChildMap().find(aTabId); + if (iter == NestedTabChildMap().end()) { + return nullptr; + } + RefPtr<TabChild> tabChild = iter->second; + return tabChild.forget(); +} + +static void +PreloadSlowThingsPostFork(void* aUnused) +{ + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + observerService->NotifyObservers(nullptr, "preload-postfork", nullptr); + + MOZ_ASSERT(sPreallocatedTab); + // Initialize initial reflow of the PresShell has to happen after fork + // because about:blank content viewer is created in the above observer + // notification. + nsCOMPtr<nsIDocShell> docShell = + do_GetInterface(sPreallocatedTab->WebNavigation()); + if (nsIPresShell* presShell = docShell->GetPresShell()) { + // Initialize and do an initial reflow of the about:blank + // PresShell to let it preload some things for us. + presShell->Initialize(0, 0); + nsIDocument* doc = presShell->GetDocument(); + doc->FlushPendingNotifications(Flush_Layout); + // ... but after it's done, make sure it doesn't do any more + // work. + presShell->MakeZombie(); + } + +} + +static bool sPreloaded = false; + +/*static*/ void +TabChild::PreloadSlowThings() +{ + if (sPreloaded) { + // If we are alredy initialized in Nuwa, don't redo preloading. + return; + } + sPreloaded = true; + + // Pass nullptr to aManager since at this point the TabChild is + // not connected to any manager. Any attempt to use the TabChild + // in IPC will crash. + RefPtr<TabChild> tab(new TabChild(nullptr, + TabId(0), + TabContext(), /* chromeFlags */ 0)); + if (!NS_SUCCEEDED(tab->Init()) || + !tab->InitTabChildGlobal(DONT_LOAD_SCRIPTS)) { + return; + } + + // Just load and compile these scripts, but don't run them. + tab->TryCacheLoadAndCompileScript(BROWSER_ELEMENT_CHILD_SCRIPT, true); + // Load, compile, and run these scripts. + tab->RecvLoadRemoteScript( + NS_LITERAL_STRING("chrome://global/content/preload.js"), + true); + + sPreallocatedTab = tab; + ClearOnShutdown(&sPreallocatedTab); + + PreloadSlowThingsPostFork(nullptr); +} + +/*static*/ already_AddRefed<TabChild> +TabChild::Create(nsIContentChild* aManager, + const TabId& aTabId, + const TabContext &aContext, + uint32_t aChromeFlags) +{ + if (sPreallocatedTab && + sPreallocatedTab->mChromeFlags == aChromeFlags && + aContext.IsMozBrowserOrApp()) { + + RefPtr<TabChild> child = sPreallocatedTab.get(); + sPreallocatedTab = nullptr; + + MOZ_ASSERT(!child->mTriedBrowserInit); + + child->mManager = aManager; + child->SetTabId(aTabId); + child->SetTabContext(aContext); + child->NotifyTabContextUpdated(true); + return child.forget(); + } + + RefPtr<TabChild> iframe = new TabChild(aManager, aTabId, + aContext, aChromeFlags); + return NS_SUCCEEDED(iframe->Init()) ? iframe.forget() : nullptr; +} + +TabChild::TabChild(nsIContentChild* aManager, + const TabId& aTabId, + const TabContext& aContext, + uint32_t aChromeFlags) + : TabContext(aContext) + , mRemoteFrame(nullptr) + , mManager(aManager) + , mChromeFlags(aChromeFlags) + , mActiveSuppressDisplayport(0) + , mLayersId(0) + , mDidFakeShow(false) + , mNotified(false) + , mTriedBrowserInit(false) + , mOrientation(eScreenOrientation_PortraitPrimary) + , mIgnoreKeyPressEvent(false) + , mHasValidInnerSize(false) + , mDestroyed(false) + , mUniqueId(aTabId) + , mDPI(0) + , mRounding(0) + , mDefaultScale(0) + , mIsTransparent(false) + , mIPCOpen(false) + , mParentIsActive(false) + , mDidSetRealShowInfo(false) + , mDidLoadURLInit(false) + , mIsFreshProcess(false) + , mLayerObserverEpoch(0) +#if defined(XP_WIN) && defined(ACCESSIBILITY) + , mNativeWindowHandle(0) +#endif +{ + // In the general case having the TabParent tell us if APZ is enabled or not + // doesn't really work because the TabParent itself may not have a reference + // to the owning widget during initialization. Instead we assume that this + // TabChild corresponds to a widget type that would have APZ enabled, and just + // check the other conditions necessary for enabling APZ. + mAsyncPanZoomEnabled = gfxPlatform::AsyncPanZoomEnabled(); + + nsWeakPtr weakPtrThis(do_GetWeakReference(static_cast<nsITabChild*>(this))); // for capture by the lambda + mSetAllowedTouchBehaviorCallback = [weakPtrThis](uint64_t aInputBlockId, + const nsTArray<TouchBehaviorFlags>& aFlags) + { + if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) { + static_cast<TabChild*>(tabChild.get())->SetAllowedTouchBehavior(aInputBlockId, aFlags); + } + }; + + // preloaded TabChild should not be added to child map + if (mUniqueId) { + MOZ_ASSERT(NestedTabChildMap().find(mUniqueId) == NestedTabChildMap().end()); + NestedTabChildMap()[mUniqueId] = this; + } + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + + if (observerService) { + const nsAttrValue::EnumTable* table = + AudioChannelService::GetAudioChannelTable(); + + nsAutoCString topic; + for (uint32_t i = 0; table[i].tag; ++i) { + topic.Assign("audiochannel-activity-"); + topic.Append(table[i].tag); + + observerService->AddObserver(this, topic.get(), false); + } + } + + for (uint32_t idx = 0; idx < NUMBER_OF_AUDIO_CHANNELS; idx++) { + mAudioChannelsActive.AppendElement(false); + } +} + +NS_IMETHODIMP +TabChild::Observe(nsISupports *aSubject, + const char *aTopic, + const char16_t *aData) +{ + if (!strcmp(aTopic, BEFORE_FIRST_PAINT)) { + if (AsyncPanZoomEnabled()) { + nsCOMPtr<nsIDocument> subject(do_QueryInterface(aSubject)); + nsCOMPtr<nsIDocument> doc(GetDocument()); + + if (SameCOMIdentity(subject, doc)) { + nsCOMPtr<nsIPresShell> shell(doc->GetShell()); + if (shell) { + shell->SetIsFirstPaint(true); + } + + APZCCallbackHelper::InitializeRootDisplayport(shell); + } + } + } + + const nsAttrValue::EnumTable* table = + AudioChannelService::GetAudioChannelTable(); + + nsAutoCString topic; + int16_t audioChannel = -1; + for (uint32_t i = 0; table[i].tag; ++i) { + topic.Assign("audiochannel-activity-"); + topic.Append(table[i].tag); + + if (topic.Equals(aTopic)) { + audioChannel = table[i].value; + break; + } + } + + if (audioChannel != -1 && mIPCOpen) { + // If the subject is not a wrapper, it is sent by the TabParent and we + // should ignore it. + nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject); + if (!wrapper) { + return NS_OK; + } + + // We must have a window in order to compare the windowID contained into the + // wrapper. + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + if (!window) { + return NS_OK; + } + + uint64_t windowID = 0; + nsresult rv = wrapper->GetData(&windowID); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // In theory a tabChild should contain just 1 top window, but let's double + // check it comparing the windowID. + if (window->WindowID() != windowID) { + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("TabChild, Observe, different windowID, owner ID = %lld, " + "ID from wrapper = %lld", window->WindowID(), windowID)); + return NS_OK; + } + + nsAutoString activeStr(aData); + bool active = activeStr.EqualsLiteral("active"); + if (active != mAudioChannelsActive[audioChannel]) { + mAudioChannelsActive[audioChannel] = active; + Unused << SendAudioChannelActivityNotification(audioChannel, active); + } + } + + return NS_OK; +} + +void +TabChild::ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId, + bool aPreventDefault) const +{ + if (mApzcTreeManager) { + mApzcTreeManager->ContentReceivedInputBlock(aInputBlockId, aPreventDefault); + } +} + +void +TabChild::SetTargetAPZC(uint64_t aInputBlockId, + const nsTArray<ScrollableLayerGuid>& aTargets) const +{ + if (mApzcTreeManager) { + mApzcTreeManager->SetTargetAPZC(aInputBlockId, aTargets); + } +} + +void +TabChild::SetAllowedTouchBehavior(uint64_t aInputBlockId, + const nsTArray<TouchBehaviorFlags>& aTargets) const +{ + if (mApzcTreeManager) { + mApzcTreeManager->SetAllowedTouchBehavior(aInputBlockId, aTargets); + } +} + +bool +TabChild::DoUpdateZoomConstraints(const uint32_t& aPresShellId, + const ViewID& aViewId, + const Maybe<ZoomConstraints>& aConstraints) +{ + if (sPreallocatedTab == this) { + // If we're the preallocated tab, bail out because doing IPC will crash. + // Once we get used for something we'll get another zoom constraints update + // and all will be well. + return true; + } + + if (!mApzcTreeManager) { + return false; + } + + ScrollableLayerGuid guid = ScrollableLayerGuid(mLayersId, aPresShellId, aViewId); + + mApzcTreeManager->UpdateZoomConstraints(guid, aConstraints); + return true; +} + +nsresult +TabChild::Init() +{ + nsCOMPtr<nsIWebBrowser> webBrowser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID); + if (!webBrowser) { + NS_ERROR("Couldn't create a nsWebBrowser?"); + return NS_ERROR_FAILURE; + } + + webBrowser->SetContainerWindow(this); + webBrowser->SetOriginAttributes(OriginAttributesRef()); + mWebNav = do_QueryInterface(webBrowser); + NS_ASSERTION(mWebNav, "nsWebBrowser doesn't implement nsIWebNavigation?"); + + nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(WebNavigation())); + docShellItem->SetItemType(nsIDocShellTreeItem::typeContentWrapper); + + nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation()); + if (!baseWindow) { + NS_ERROR("mWebNav doesn't QI to nsIBaseWindow"); + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget(this); + mPuppetWidget = static_cast<PuppetWidget*>(widget.get()); + if (!mPuppetWidget) { + NS_ERROR("couldn't create fake widget"); + return NS_ERROR_FAILURE; + } + mPuppetWidget->InfallibleCreate( + nullptr, 0, // no parents + LayoutDeviceIntRect(0, 0, 0, 0), + nullptr // HandleWidgetEvent + ); + + baseWindow->InitWindow(0, mPuppetWidget, 0, 0, 0, 0); + baseWindow->Create(); + + // Set the tab context attributes then pass to docShell + NotifyTabContextUpdated(false); + + // IPC uses a WebBrowser object for which DNS prefetching is turned off + // by default. But here we really want it, so enable it explicitly + nsCOMPtr<nsIWebBrowserSetup> webBrowserSetup = + do_QueryInterface(baseWindow); + if (webBrowserSetup) { + webBrowserSetup->SetProperty(nsIWebBrowserSetup::SETUP_ALLOW_DNS_PREFETCH, + true); + } else { + NS_WARNING("baseWindow doesn't QI to nsIWebBrowserSetup, skipping " + "DNS prefetching enable step."); + } + + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + MOZ_ASSERT(docShell); + + docShell->SetAffectPrivateSessionLifetime( + mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME); + nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(WebNavigation()); + MOZ_ASSERT(loadContext); + loadContext->SetPrivateBrowsing(OriginAttributesRef().mPrivateBrowsingId > 0); + loadContext->SetRemoteTabs( + mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW); + + // Few lines before, baseWindow->Create() will end up creating a new + // window root in nsGlobalWindow::SetDocShell. + // Then this chrome event handler, will be inherited to inner windows. + // We want to also set it to the docshell so that inner windows + // and any code that has access to the docshell + // can all listen to the same chrome event handler. + // XXX: ideally, we would set a chrome event handler earlier, + // and all windows, even the root one, will use the docshell one. + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); + nsCOMPtr<EventTarget> chromeHandler = + do_QueryInterface(window->GetChromeEventHandler()); + docShell->SetChromeEventHandler(chromeHandler); + + if (window->GetCurrentInnerWindow()) { + window->SetKeyboardIndicators(ShowAccelerators(), ShowFocusRings()); + } else { + // Skip ShouldShowFocusRing check if no inner window is available + window->SetInitialKeyboardIndicators(ShowAccelerators(), ShowFocusRings()); + } + + // Set prerender flag if necessary. + if (mIsPrerendered) { + docShell->SetIsPrerendered(); + } + + nsContentUtils::SetScrollbarsVisibility(window->GetDocShell(), + !!(mChromeFlags & nsIWebBrowserChrome::CHROME_SCROLLBARS)); + + nsWeakPtr weakPtrThis = do_GetWeakReference(static_cast<nsITabChild*>(this)); // for capture by the lambda + ContentReceivedInputBlockCallback callback( + [weakPtrThis](const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId, + bool aPreventDefault) + { + if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) { + static_cast<TabChild*>(tabChild.get())->ContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault); + } + }); + mAPZEventState = new APZEventState(mPuppetWidget, Move(callback)); + + mIPCOpen = true; + + if (GroupedSHistory::GroupedHistoryEnabled()) { + // Set session history listener. + nsCOMPtr<nsISHistory> shistory; + mWebNav->GetSessionHistory(getter_AddRefs(shistory)); + if (!shistory) { + return NS_ERROR_FAILURE; + } + mHistoryListener = new TabChildSHistoryListener(this); + nsCOMPtr<nsISHistoryListener> listener(do_QueryObject(mHistoryListener)); + shistory->AddSHistoryListener(listener); + nsCOMPtr<nsIPartialSHistoryListener> partialListener(do_QueryObject(mHistoryListener)); + shistory->SetPartialSHistoryListener(partialListener); + } + + return NS_OK; +} + +void +TabChild::NotifyTabContextUpdated(bool aIsPreallocated) +{ + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + MOZ_ASSERT(docShell); + + if (!docShell) { + return; + } + + UpdateFrameType(); + + if (aIsPreallocated) { + nsDocShell::Cast(docShell)->SetOriginAttributes(OriginAttributesRef()); + } + + // Set SANDBOXED_AUXILIARY_NAVIGATION flag if this is a receiver page. + if (!PresentationURL().IsEmpty()) { + docShell->SetSandboxFlags(SANDBOXED_AUXILIARY_NAVIGATION); + } +} + +void +TabChild::UpdateFrameType() +{ + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + MOZ_ASSERT(docShell); + + // TODO: Bug 1252794 - remove frameType from nsIDocShell.idl + docShell->SetFrameType(IsMozBrowserElement() ? nsIDocShell::FRAME_TYPE_BROWSER : + HasOwnApp() ? nsIDocShell::FRAME_TYPE_APP : + nsIDocShell::FRAME_TYPE_REGULAR); +} + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TabChild) + NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome) + NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2) + NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow) + NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus) + NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) + NS_INTERFACE_MAP_ENTRY(nsIWindowProvider) + NS_INTERFACE_MAP_ENTRY(nsITabChild) + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY(nsITooltipListener) +NS_INTERFACE_MAP_END_INHERITING(TabChildBase) + +NS_IMPL_ADDREF_INHERITED(TabChild, TabChildBase); +NS_IMPL_RELEASE_INHERITED(TabChild, TabChildBase); + +NS_IMETHODIMP +TabChild::SetStatus(uint32_t aStatusType, const char16_t* aStatus) +{ + return SetStatusWithContext(aStatusType, + aStatus ? static_cast<const nsString &>(nsDependentString(aStatus)) + : EmptyString(), + nullptr); +} + +NS_IMETHODIMP +TabChild::GetWebBrowser(nsIWebBrowser** aWebBrowser) +{ + NS_WARNING("TabChild::GetWebBrowser not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::SetWebBrowser(nsIWebBrowser* aWebBrowser) +{ + NS_WARNING("TabChild::SetWebBrowser not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::GetChromeFlags(uint32_t* aChromeFlags) +{ + *aChromeFlags = mChromeFlags; + return NS_OK; +} + +NS_IMETHODIMP +TabChild::SetChromeFlags(uint32_t aChromeFlags) +{ + NS_WARNING("trying to SetChromeFlags from content process?"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::DestroyBrowserWindow() +{ + NS_WARNING("TabChild::DestroyBrowserWindow not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::RemoteSizeShellTo(int32_t aWidth, int32_t aHeight, + int32_t aShellItemWidth, int32_t aShellItemHeight) +{ + nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation()); + nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(ourDocShell)); + int32_t width, height; + docShellAsWin->GetSize(&width, &height); + + uint32_t flags = 0; + if (width == aWidth) { + flags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX; + } + + if (height == aHeight) { + flags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY; + } + + bool sent = SendSizeShellTo(flags, aWidth, aHeight, aShellItemWidth, aShellItemHeight); + + return sent ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +TabChild::RemoteDropLinks(uint32_t aLinksCount, nsIDroppedLinkItem** aLinks) +{ + nsTArray<nsString> linksArray; + nsresult rv = NS_OK; + for (uint32_t i = 0; i < aLinksCount; i++) { + nsString tmp; + rv = aLinks[i]->GetUrl(tmp); + if (NS_FAILED(rv)) { + return rv; + } + linksArray.AppendElement(tmp); + + rv = aLinks[i]->GetName(tmp); + if (NS_FAILED(rv)) { + return rv; + } + linksArray.AppendElement(tmp); + + rv = aLinks[i]->GetType(tmp); + if (NS_FAILED(rv)) { + return rv; + } + linksArray.AppendElement(tmp); + } + + bool sent = SendDropLinks(linksArray); + + return sent ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +TabChild::SizeBrowserTo(int32_t aWidth, int32_t aHeight) +{ + NS_WARNING("TabChild::SizeBrowserTo not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::ShowAsModal() +{ + NS_WARNING("TabChild::ShowAsModal not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::IsWindowModal(bool* aRetVal) +{ + *aRetVal = false; + return NS_OK; +} + +NS_IMETHODIMP +TabChild::ExitModalEventLoop(nsresult aStatus) +{ + NS_WARNING("TabChild::ExitModalEventLoop not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::SetStatusWithContext(uint32_t aStatusType, + const nsAString& aStatusText, + nsISupports* aStatusContext) +{ + // We can only send the status after the ipc machinery is set up, + // mRemoteFrame is a good indicator. + if (mRemoteFrame) + SendSetStatus(aStatusType, nsString(aStatusText)); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY, + int32_t aCx, int32_t aCy) +{ + // The parent is in charge of the dimension changes. If JS code wants to + // change the dimensions (moveTo, screenX, etc.) we send a message to the + // parent about the new requested dimension, the parent does the resize/move + // then send a message to the child to update itself. For APIs like screenX + // this function is called with the current value for the non-changed values. + // In a series of calls like window.screenX = 10; window.screenY = 10; for + // the second call, since screenX is not yet updated we might accidentally + // reset back screenX to it's old value. To avoid this if a parameter did not + // change we want the parent to ignore its value. + int32_t x, y, cx, cy; + GetDimensions(aFlags, &x, &y, &cx, &cy); + + if (x == aX) { + aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_X; + } + + if (y == aY) { + aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_Y; + } + + if (cx == aCx) { + aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX; + } + + if (cy == aCy) { + aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY; + } + + Unused << SendSetDimensions(aFlags, aX, aY, aCx, aCy); + + return NS_OK; +} + +NS_IMETHODIMP +TabChild::GetDimensions(uint32_t aFlags, int32_t* aX, + int32_t* aY, int32_t* aCx, int32_t* aCy) +{ + ScreenIntRect rect = GetOuterRect(); + if (aX) { + *aX = rect.x; + } + if (aY) { + *aY = rect.y; + } + if (aCx) { + *aCx = rect.width; + } + if (aCy) { + *aCy = rect.height; + } + + return NS_OK; +} + +NS_IMETHODIMP +TabChild::SetFocus() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::GetVisibility(bool* aVisibility) +{ + *aVisibility = true; + return NS_OK; +} + +NS_IMETHODIMP +TabChild::SetVisibility(bool aVisibility) +{ + // should the platform support this? Bug 666365 + return NS_OK; +} + +NS_IMETHODIMP +TabChild::GetTitle(char16_t** aTitle) +{ + NS_WARNING("TabChild::GetTitle not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::SetTitle(const char16_t* aTitle) +{ + // JavaScript sends the "DOMTitleChanged" event to the parent + // via the message manager. + return NS_OK; +} + +NS_IMETHODIMP +TabChild::GetSiteWindow(void** aSiteWindow) +{ + NS_WARNING("TabChild::GetSiteWindow not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::Blur() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::FocusNextElement(bool aForDocumentNavigation) +{ + SendMoveFocus(true, aForDocumentNavigation); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::FocusPrevElement(bool aForDocumentNavigation) +{ + SendMoveFocus(false, aForDocumentNavigation); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::GetInterface(const nsIID & aIID, void **aSink) +{ + if (aIID.Equals(NS_GET_IID(nsIWebBrowserChrome3))) { + NS_IF_ADDREF(((nsISupports *) (*aSink = mWebBrowserChrome))); + return NS_OK; + } + + // XXXbz should we restrict the set of interfaces we hand out here? + // See bug 537429 + return QueryInterface(aIID, aSink); +} + +NS_IMETHODIMP +TabChild::ProvideWindow(mozIDOMWindowProxy* aParent, + uint32_t aChromeFlags, + bool aCalledFromJS, + bool aPositionSpecified, bool aSizeSpecified, + nsIURI* aURI, const nsAString& aName, + const nsACString& aFeatures, bool aForceNoOpener, + bool* aWindowIsNew, mozIDOMWindowProxy** aReturn) +{ + *aReturn = nullptr; + + // If aParent is inside an <iframe mozbrowser> or <iframe mozapp> and this + // isn't a request to open a modal-type window, we're going to create a new + // <iframe mozbrowser/mozapp> and return its window here. + nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent); + bool iframeMoz = (docshell && docshell->GetIsInMozBrowserOrApp() && + !(aChromeFlags & (nsIWebBrowserChrome::CHROME_MODAL | + nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | + nsIWebBrowserChrome::CHROME_OPENAS_CHROME))); + + if (!iframeMoz) { + int32_t openLocation = + nsWindowWatcher::GetWindowOpenLocation(nsPIDOMWindowOuter::From(aParent), + aChromeFlags, aCalledFromJS, + aPositionSpecified, aSizeSpecified); + + // If it turns out we're opening in the current browser, just hand over the + // current browser's docshell. + if (openLocation == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) { + nsCOMPtr<nsIWebBrowser> browser = do_GetInterface(WebNavigation()); + *aWindowIsNew = false; + return browser->GetContentDOMWindow(aReturn); + } + } + + // Note that ProvideWindowCommon may return NS_ERROR_ABORT if the + // open window call was canceled. It's important that we pass this error + // code back to our caller. + ContentChild* cc = ContentChild::GetSingleton(); + return cc->ProvideWindowCommon(this, + aParent, + iframeMoz, + aChromeFlags, + aCalledFromJS, + aPositionSpecified, + aSizeSpecified, + aURI, + aName, + aFeatures, + aForceNoOpener, + aWindowIsNew, + aReturn); +} + +void +TabChild::DestroyWindow() +{ + nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation()); + if (baseWindow) + baseWindow->Destroy(); + + // NB: the order of mPuppetWidget->Destroy() and mRemoteFrame->Destroy() + // is important: we want to kill off remote layers before their + // frames + if (mPuppetWidget) { + mPuppetWidget->Destroy(); + } + + if (mRemoteFrame) { + mRemoteFrame->Destroy(); + mRemoteFrame = nullptr; + } + + + if (mLayersId != 0) { + MOZ_ASSERT(sTabChildren); + sTabChildren->Remove(mLayersId); + if (!sTabChildren->Count()) { + delete sTabChildren; + sTabChildren = nullptr; + } + mLayersId = 0; + } +} + +void +TabChild::ActorDestroy(ActorDestroyReason why) +{ + mIPCOpen = false; + + DestroyWindow(); + + if (mTabChildGlobal) { + // We should have a message manager if the global is alive, but it + // seems sometimes we don't. Assert in aurora/nightly, but don't + // crash in release builds. + MOZ_DIAGNOSTIC_ASSERT(mTabChildGlobal->mMessageManager); + if (mTabChildGlobal->mMessageManager) { + // The messageManager relays messages via the TabChild which + // no longer exists. + static_cast<nsFrameMessageManager*> + (mTabChildGlobal->mMessageManager.get())->Disconnect(); + mTabChildGlobal->mMessageManager = nullptr; + } + } + + CompositorBridgeChild* compositorChild = static_cast<CompositorBridgeChild*>(CompositorBridgeChild::Get()); + compositorChild->CancelNotifyAfterRemotePaint(this); + + if (GetTabId() != 0) { + NestedTabChildMap().erase(GetTabId()); + } +} + +TabChild::~TabChild() +{ + DestroyWindow(); + + nsCOMPtr<nsIWebBrowser> webBrowser = do_QueryInterface(WebNavigation()); + if (webBrowser) { + webBrowser->SetContainerWindow(nullptr); + } + + if (mHistoryListener) { + mHistoryListener->ClearTabChild(); + } +} + +void +TabChild::SetProcessNameToAppName() +{ + nsCOMPtr<mozIApplication> app = GetOwnApp(); + if (!app) { + return; + } + + nsAutoString appName; + nsresult rv = app->GetName(appName); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to retrieve app name"); + return; + } + + ContentChild::GetSingleton()->SetProcessName(appName, true); +} + +bool +TabChild::RecvLoadURL(const nsCString& aURI, + const ShowInfo& aInfo) +{ + if (!mDidLoadURLInit) { + mDidLoadURLInit = true; + if (!InitTabChildGlobal()) { + return false; + } + + ApplyShowInfo(aInfo); + + SetProcessNameToAppName(); + } + + nsresult rv = + WebNavigation()->LoadURI(NS_ConvertUTF8toUTF16(aURI).get(), + nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | + nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL, + nullptr, nullptr, nullptr); + if (NS_FAILED(rv)) { + NS_WARNING("WebNavigation()->LoadURI failed. Eating exception, what else can I do?"); + } + +#ifdef MOZ_CRASHREPORTER + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("URL"), aURI); +#endif + + return true; +} + +void +TabChild::DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const uint64_t& aLayersId, + PRenderFrameChild* aRenderFrame, const ShowInfo& aShowInfo) +{ + RecvShow(ScreenIntSize(0, 0), aShowInfo, aTextureFactoryIdentifier, + aLayersId, aRenderFrame, mParentIsActive, nsSizeMode_Normal); + mDidFakeShow = true; +} + +void +TabChild::ApplyShowInfo(const ShowInfo& aInfo) +{ + if (mDidSetRealShowInfo) { + return; + } + + if (!aInfo.fakeShowInfo()) { + // Once we've got one ShowInfo from parent, no need to update the values + // anymore. + mDidSetRealShowInfo = true; + } + + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + if (docShell) { + nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(docShell); + if (IsMozBrowserOrApp()) { + // B2G allows window.name to be set by changing the name attribute on the + // <iframe mozbrowser> element. window.open calls cause this attribute to + // be set to the correct value. A normal <xul:browser> element has no such + // attribute. The data we get here comes from reading the attribute, so we + // shouldn't trust it for <xul:browser> elements. + item->SetName(aInfo.name()); + } + docShell->SetFullscreenAllowed(aInfo.fullscreenAllowed()); + if (aInfo.isPrivate()) { + nsCOMPtr<nsILoadContext> context = do_GetInterface(docShell); + // No need to re-set private browsing mode. + if (!context->UsePrivateBrowsing()) { + if (docShell->GetHasLoadedNonBlankURI()) { + nsContentUtils::ReportToConsoleNonLocalized( + NS_LITERAL_STRING("We should not switch to Private Browsing after loading a document."), + nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("mozprivatebrowsing"), + nullptr); + } else { + DocShellOriginAttributes attrs(nsDocShell::Cast(docShell)->GetOriginAttributes()); + attrs.SyncAttributesWithPrivateBrowsing(true); + nsDocShell::Cast(docShell)->SetOriginAttributes(attrs); + } + } + } + } + mDPI = aInfo.dpi(); + mRounding = aInfo.widgetRounding(); + mDefaultScale = aInfo.defaultScale(); + mIsTransparent = aInfo.isTransparent(); +} + +bool +TabChild::RecvShow(const ScreenIntSize& aSize, + const ShowInfo& aInfo, + const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const uint64_t& aLayersId, + PRenderFrameChild* aRenderFrame, + const bool& aParentIsActive, + const nsSizeMode& aSizeMode) +{ + MOZ_ASSERT((!mDidFakeShow && aRenderFrame) || (mDidFakeShow && !aRenderFrame)); + + mPuppetWidget->SetSizeMode(aSizeMode); + if (mDidFakeShow) { + ApplyShowInfo(aInfo); + RecvParentActivated(aParentIsActive); + return true; + } + + nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation()); + if (!baseWindow) { + NS_ERROR("WebNavigation() doesn't QI to nsIBaseWindow"); + return false; + } + + if (!InitRenderingState(aTextureFactoryIdentifier, aLayersId, aRenderFrame)) { + // We can fail to initialize our widget if the <browser + // remote> has already been destroyed, and we couldn't hook + // into the parent-process's layer system. That's not a fatal + // error. + return true; + } + + baseWindow->SetVisibility(true); + + bool res = InitTabChildGlobal(); + ApplyShowInfo(aInfo); + RecvParentActivated(aParentIsActive); + + return res; +} + +bool +TabChild::RecvUpdateDimensions(const CSSRect& rect, const CSSSize& size, + const ScreenOrientationInternal& orientation, + const LayoutDeviceIntPoint& clientOffset, + const LayoutDeviceIntPoint& chromeDisp) +{ + if (!mRemoteFrame) { + return true; + } + + mUnscaledOuterRect = rect; + mClientOffset = clientOffset; + mChromeDisp = chromeDisp; + + mOrientation = orientation; + SetUnscaledInnerSize(size); + if (!mHasValidInnerSize && size.width != 0 && size.height != 0) { + mHasValidInnerSize = true; + } + + ScreenIntSize screenSize = GetInnerSize(); + ScreenIntRect screenRect = GetOuterRect(); + + // Set the size on the document viewer before we update the widget and + // trigger a reflow. Otherwise the MobileViewportManager reads the stale + // size from the content viewer when it computes a new CSS viewport. + nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation()); + baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height, + nsIBaseWindow::eRepaint); + + mPuppetWidget->Resize(screenRect.x + clientOffset.x + chromeDisp.x, + screenRect.y + clientOffset.y + chromeDisp.y, + screenSize.width, screenSize.height, true); + + return true; +} + +bool +TabChild::RecvSizeModeChanged(const nsSizeMode& aSizeMode) +{ + mPuppetWidget->SetSizeMode(aSizeMode); + if (!mPuppetWidget->IsVisible()) { + return true; + } + nsCOMPtr<nsIDocument> document(GetDocument()); + nsCOMPtr<nsIPresShell> presShell = document->GetShell(); + if (presShell) { + nsPresContext* presContext = presShell->GetPresContext(); + if (presContext) { + presContext->SizeModeChanged(aSizeMode); + } + } + return true; +} + +bool +TabChild::UpdateFrame(const FrameMetrics& aFrameMetrics) +{ + return TabChildBase::UpdateFrameHandler(aFrameMetrics); +} + +bool +TabChild::RecvSuppressDisplayport(const bool& aEnabled) +{ + if (aEnabled) { + mActiveSuppressDisplayport++; + } else { + mActiveSuppressDisplayport--; + } + + MOZ_ASSERT(mActiveSuppressDisplayport >= 0); + APZCCallbackHelper::SuppressDisplayport(aEnabled, GetPresShell()); + return true; +} + +void +TabChild::HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid) +{ + TABC_LOG("Handling double tap at %s with %p %p\n", + Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get()); + + if (!mGlobal || !mTabChildGlobal) { + return; + } + + // Note: there is nothing to do with the modifiers here, as we are not + // synthesizing any sort of mouse event. + nsCOMPtr<nsIDocument> document = GetDocument(); + CSSRect zoomToRect = CalculateRectToZoomTo(document, aPoint); + // The double-tap can be dispatched by any scroll frame (so |aGuid| could be + // the guid of any scroll frame), but the zoom-to-rect operation must be + // performed by the root content scroll frame, so query its identifiers + // for the SendZoomToRect() call rather than using the ones from |aGuid|. + uint32_t presShellId; + ViewID viewId; + if (APZCCallbackHelper::GetOrCreateScrollIdentifiers( + document->GetDocumentElement(), &presShellId, &viewId) && mApzcTreeManager) { + ScrollableLayerGuid guid(mLayersId, presShellId, viewId); + + mApzcTreeManager->ZoomToRect(guid, zoomToRect, DEFAULT_BEHAVIOR); + } +} + +bool +TabChild::RecvHandleTap(const GeckoContentController::TapType& aType, + const LayoutDevicePoint& aPoint, + const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) +{ + nsCOMPtr<nsIPresShell> presShell = GetPresShell(); + if (!presShell) { + return true; + } + if (!presShell->GetPresContext()) { + return true; + } + CSSToLayoutDeviceScale scale(presShell->GetPresContext()->CSSToDevPixelScale()); + CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint / scale, aGuid); + + switch (aType) { + case GeckoContentController::TapType::eSingleTap: + if (mGlobal && mTabChildGlobal) { + mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 1); + } + break; + case GeckoContentController::TapType::eDoubleTap: + HandleDoubleTap(point, aModifiers, aGuid); + break; + case GeckoContentController::TapType::eSecondTap: + if (mGlobal && mTabChildGlobal) { + mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 2); + } + break; + case GeckoContentController::TapType::eLongTap: + if (mGlobal && mTabChildGlobal) { + mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid, + aInputBlockId); + } + break; + case GeckoContentController::TapType::eLongTapUp: + if (mGlobal && mTabChildGlobal) { + mAPZEventState->ProcessLongTapUp(presShell, point, scale, aModifiers); + } + break; + case GeckoContentController::TapType::eSentinel: + // Should never happen, but we need to handle this case to make the compiler + // happy. + MOZ_ASSERT(false); + break; + } + return true; +} + +bool +TabChild::NotifyAPZStateChange(const ViewID& aViewId, + const layers::GeckoContentController::APZStateChange& aChange, + const int& aArg) +{ + mAPZEventState->ProcessAPZStateChange(aViewId, aChange, aArg); + if (aChange == layers::GeckoContentController::APZStateChange::eTransformEnd) { + // This is used by tests to determine when the APZ is done doing whatever + // it's doing. XXX generify this as needed when writing additional tests. + nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); + observerService->NotifyObservers(nullptr, "APZ:TransformEnd", nullptr); + } + return true; +} + +void +TabChild::StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics) +{ + ScrollableLayerGuid guid(mLayersId, aDragMetrics.mPresShellId, + aDragMetrics.mViewId); + + if (mApzcTreeManager) { + mApzcTreeManager->StartScrollbarDrag(guid, aDragMetrics); + } +} + +void +TabChild::ZoomToRect(const uint32_t& aPresShellId, + const FrameMetrics::ViewID& aViewId, + const CSSRect& aRect, + const uint32_t& aFlags) +{ + ScrollableLayerGuid guid(mLayersId, aPresShellId, aViewId); + + if (mApzcTreeManager) { + mApzcTreeManager->ZoomToRect(guid, aRect, aFlags); + } +} + +bool +TabChild::RecvActivate() +{ + nsCOMPtr<nsIWebBrowserFocus> browser = do_QueryInterface(WebNavigation()); + browser->Activate(); + return true; +} + +bool TabChild::RecvDeactivate() +{ + nsCOMPtr<nsIWebBrowserFocus> browser = do_QueryInterface(WebNavigation()); + browser->Deactivate(); + return true; +} + +bool TabChild::RecvParentActivated(const bool& aActivated) +{ + mParentIsActive = aActivated; + + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + NS_ENSURE_TRUE(fm, true); + + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + fm->ParentActivated(window, aActivated); + return true; +} + +bool TabChild::RecvSetKeyboardIndicators(const UIStateChangeType& aShowAccelerators, + const UIStateChangeType& aShowFocusRings) +{ + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + NS_ENSURE_TRUE(window, true); + + window->SetKeyboardIndicators(aShowAccelerators, aShowFocusRings); + return true; +} + +bool +TabChild::RecvStopIMEStateManagement() +{ + IMEStateManager::StopIMEStateManagement(); + return true; +} + +bool +TabChild::RecvMenuKeyboardListenerInstalled(const bool& aInstalled) +{ + IMEStateManager::OnInstalledMenuKeyboardListener(aInstalled); + return true; +} + +bool +TabChild::RecvNotifyAttachGroupedSessionHistory(const uint32_t& aOffset) +{ + // nsISHistory uses int32_t + if (NS_WARN_IF(aOffset > INT32_MAX)) { + return false; + } + + nsCOMPtr<nsISHistory> shistory; + mWebNav->GetSessionHistory(getter_AddRefs(shistory)); + NS_ENSURE_TRUE(shistory, false); + + return NS_SUCCEEDED(shistory->OnAttachGroupedSessionHistory(aOffset)); +} + +bool +TabChild::RecvNotifyPartialSessionHistoryActive(const uint32_t& aGlobalLength, + const uint32_t& aTargetLocalIndex) +{ + // nsISHistory uses int32_t + if (NS_WARN_IF(aGlobalLength > INT32_MAX || aTargetLocalIndex > INT32_MAX)) { + return false; + } + + nsCOMPtr<nsISHistory> shistory; + mWebNav->GetSessionHistory(getter_AddRefs(shistory)); + NS_ENSURE_TRUE(shistory, false); + + return NS_SUCCEEDED(shistory->OnPartialSessionHistoryActive(aGlobalLength, + aTargetLocalIndex)); +} + +bool +TabChild::RecvNotifyPartialSessionHistoryDeactive() +{ + nsCOMPtr<nsISHistory> shistory; + mWebNav->GetSessionHistory(getter_AddRefs(shistory)); + NS_ENSURE_TRUE(shistory, false); + + return NS_SUCCEEDED(shistory->OnPartialSessionHistoryDeactive()); +} + +bool +TabChild::RecvMouseEvent(const nsString& aType, + const float& aX, + const float& aY, + const int32_t& aButton, + const int32_t& aClickCount, + const int32_t& aModifiers, + const bool& aIgnoreRootScrollFrame) +{ + APZCCallbackHelper::DispatchMouseEvent(GetPresShell(), aType, CSSPoint(aX, aY), + aButton, aClickCount, aModifiers, aIgnoreRootScrollFrame, nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN); + return true; +} + +bool +TabChild::RecvRealMouseMoveEvent(const WidgetMouseEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) +{ + return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId); +} + +bool +TabChild::RecvSynthMouseMoveEvent(const WidgetMouseEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) +{ + return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId); +} + +bool +TabChild::RecvRealMouseButtonEvent(const WidgetMouseEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) +{ + // Mouse events like eMouseEnterIntoWidget, that are created in the parent + // process EventStateManager code, have an input block id which they get from + // the InputAPZContext in the parent process stack. However, they did not + // actually go through the APZ code and so their mHandledByAPZ flag is false. + // Since thos events didn't go through APZ, we don't need to send notifications + // for them. + if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) { + nsCOMPtr<nsIDocument> document(GetDocument()); + APZCCallbackHelper::SendSetTargetAPZCNotification( + mPuppetWidget, document, aEvent, aGuid, aInputBlockId); + } + + nsEventStatus unused; + InputAPZContext context(aGuid, aInputBlockId, unused); + + WidgetMouseEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid, + mPuppetWidget->GetDefaultScale()); + APZCCallbackHelper::DispatchWidgetEvent(localEvent); + + if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) { + mAPZEventState->ProcessMouseEvent(aEvent, aGuid, aInputBlockId); + } + return true; +} + +bool +TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) +{ + if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) { + nsCOMPtr<nsIDocument> document(GetDocument()); + APZCCallbackHelper::SendSetTargetAPZCNotification( + mPuppetWidget, document, aEvent, aGuid, aInputBlockId); + } + + WidgetWheelEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid, + mPuppetWidget->GetDefaultScale()); + APZCCallbackHelper::DispatchWidgetEvent(localEvent); + + if (localEvent.mCanTriggerSwipe) { + SendRespondStartSwipeEvent(aInputBlockId, localEvent.TriggersSwipe()); + } + + if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) { + mAPZEventState->ProcessWheelEvent(localEvent, aGuid, aInputBlockId); + } + return true; +} + +bool +TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, + const nsEventStatus& aApzResponse) +{ + TABC_LOG("Receiving touch event of type %d\n", aEvent.mMessage); + + WidgetTouchEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + + APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid, + mPuppetWidget->GetDefaultScale()); + + if (localEvent.mMessage == eTouchStart && AsyncPanZoomEnabled()) { + nsCOMPtr<nsIDocument> document = GetDocument(); + if (gfxPrefs::TouchActionEnabled()) { + APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(mPuppetWidget, + document, localEvent, aInputBlockId, mSetAllowedTouchBehaviorCallback); + } + APZCCallbackHelper::SendSetTargetAPZCNotification(mPuppetWidget, document, + localEvent, aGuid, aInputBlockId); + } + + // Dispatch event to content (potentially a long-running operation) + nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent); + + if (!AsyncPanZoomEnabled()) { + // We shouldn't have any e10s platforms that have touch events enabled + // without APZ. + MOZ_ASSERT(false); + return true; + } + + mAPZEventState->ProcessTouchEvent(localEvent, aGuid, aInputBlockId, + aApzResponse, status); + return true; +} + +bool +TabChild::RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, + const nsEventStatus& aApzResponse) +{ + return RecvRealTouchEvent(aEvent, aGuid, aInputBlockId, aApzResponse); +} + +bool +TabChild::RecvRealDragEvent(const WidgetDragEvent& aEvent, + const uint32_t& aDragAction, + const uint32_t& aDropEffect) +{ + WidgetDragEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + + nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession(); + if (dragSession) { + dragSession->SetDragAction(aDragAction); + nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer; + dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer)); + if (initialDataTransfer) { + initialDataTransfer->SetDropEffectInt(aDropEffect); + } + } + + if (aEvent.mMessage == eDrop) { + bool canDrop = true; + if (!dragSession || NS_FAILED(dragSession->GetCanDrop(&canDrop)) || + !canDrop) { + localEvent.mMessage = eDragExit; + } + } else if (aEvent.mMessage == eDragOver) { + nsCOMPtr<nsIDragService> dragService = + do_GetService("@mozilla.org/widget/dragservice;1"); + if (dragService) { + // This will dispatch 'drag' event at the source if the + // drag transaction started in this process. + dragService->FireDragEventAtSource(eDrag); + } + } + + APZCCallbackHelper::DispatchWidgetEvent(localEvent); + return true; +} + +bool +TabChild::RecvPluginEvent(const WidgetPluginEvent& aEvent) +{ + WidgetPluginEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent); + if (status != nsEventStatus_eConsumeNoDefault) { + // If not consumed, we should call default action + SendDefaultProcOfPluginEvent(aEvent); + } + return true; +} + +void +TabChild::RequestNativeKeyBindings(AutoCacheNativeKeyCommands* aAutoCache, + const WidgetKeyboardEvent* aEvent) +{ + MaybeNativeKeyBinding maybeBindings; + if (!SendRequestNativeKeyBindings(*aEvent, &maybeBindings)) { + return; + } + + if (maybeBindings.type() == MaybeNativeKeyBinding::TNativeKeyBinding) { + const NativeKeyBinding& bindings = maybeBindings; + aAutoCache->Cache(bindings.singleLineCommands(), + bindings.multiLineCommands(), + bindings.richTextCommands()); + } else { + aAutoCache->CacheNoCommands(); + } +} + +bool +TabChild::RecvNativeSynthesisResponse(const uint64_t& aObserverId, + const nsCString& aResponse) +{ + mozilla::widget::AutoObserverNotifier::NotifySavedObserver(aObserverId, aResponse.get()); + return true; +} + +bool +TabChild::RecvRealKeyEvent(const WidgetKeyboardEvent& event, + const MaybeNativeKeyBinding& aBindings) +{ + AutoCacheNativeKeyCommands autoCache(mPuppetWidget); + + if (event.mMessage == eKeyPress) { + // If content code called preventDefault() on a keydown event, then we don't + // want to process any following keypress events. + if (mIgnoreKeyPressEvent) { + return true; + } + if (aBindings.type() == MaybeNativeKeyBinding::TNativeKeyBinding) { + const NativeKeyBinding& bindings = aBindings; + autoCache.Cache(bindings.singleLineCommands(), + bindings.multiLineCommands(), + bindings.richTextCommands()); + } else { + autoCache.CacheNoCommands(); + } + } + + WidgetKeyboardEvent localEvent(event); + localEvent.mWidget = mPuppetWidget; + nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent); + + if (event.mMessage == eKeyDown) { + mIgnoreKeyPressEvent = status == nsEventStatus_eConsumeNoDefault; + } + + if (localEvent.mFlags.mIsSuppressedOrDelayed) { + localEvent.PreventDefault(); + } + + // If a response is desired from the content process, resend the key event. + // If mAccessKeyForwardedToChild is set, then don't resend the key event yet + // as RecvHandleAccessKey will do this. + if (localEvent.mFlags.mWantReplyFromContentProcess) { + SendReplyKeyEvent(localEvent); + } + + if (localEvent.mAccessKeyForwardedToChild) { + SendAccessKeyNotHandled(localEvent); + } + + if (PresShell::BeforeAfterKeyboardEventEnabled()) { + SendDispatchAfterKeyboardEvent(localEvent); + } + + return true; +} + +bool +TabChild::RecvKeyEvent(const nsString& aType, + const int32_t& aKeyCode, + const int32_t& aCharCode, + const int32_t& aModifiers, + const bool& aPreventDefault) +{ + bool ignored = false; + nsContentUtils::SendKeyEvent(mPuppetWidget, aType, aKeyCode, aCharCode, + aModifiers, aPreventDefault, &ignored); + return true; +} + +bool +TabChild::RecvCompositionEvent(const WidgetCompositionEvent& event) +{ + WidgetCompositionEvent localEvent(event); + localEvent.mWidget = mPuppetWidget; + APZCCallbackHelper::DispatchWidgetEvent(localEvent); + Unused << SendOnEventNeedingAckHandled(event.mMessage); + return true; +} + +bool +TabChild::RecvSelectionEvent(const WidgetSelectionEvent& event) +{ + WidgetSelectionEvent localEvent(event); + localEvent.mWidget = mPuppetWidget; + APZCCallbackHelper::DispatchWidgetEvent(localEvent); + Unused << SendOnEventNeedingAckHandled(event.mMessage); + return true; +} + +bool +TabChild::RecvPasteTransferable(const IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + const IPC::Principal& aRequestingPrincipal) +{ + nsresult rv; + nsCOMPtr<nsITransferable> trans = + do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); + NS_ENSURE_SUCCESS(rv, true); + trans->Init(nullptr); + + rv = nsContentUtils::IPCTransferableToTransferable(aDataTransfer, + aIsPrivateData, + aRequestingPrincipal, + trans, nullptr, this); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!ourDocShell)) { + return true; + } + + nsCOMPtr<nsICommandParams> params = do_CreateInstance("@mozilla.org/embedcomp/command-params;1", &rv); + NS_ENSURE_SUCCESS(rv, true); + + rv = params->SetISupportsValue("transferable", trans); + NS_ENSURE_SUCCESS(rv, true); + + ourDocShell->DoCommandWithParams("cmd_pasteTransferable", params); + return true; +} + + +a11y::PDocAccessibleChild* +TabChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&, + const uint32_t&, const IAccessibleHolder&) +{ + MOZ_ASSERT(false, "should never call this!"); + return nullptr; +} + +bool +TabChild::DeallocPDocAccessibleChild(a11y::PDocAccessibleChild* aChild) +{ +#ifdef ACCESSIBILITY + delete static_cast<mozilla::a11y::DocAccessibleChild*>(aChild); +#endif + return true; +} + +PDocumentRendererChild* +TabChild::AllocPDocumentRendererChild(const nsRect& documentRect, + const mozilla::gfx::Matrix& transform, + const nsString& bgcolor, + const uint32_t& renderFlags, + const bool& flushLayout, + const nsIntSize& renderSize) +{ + return new DocumentRendererChild(); +} + +bool +TabChild::DeallocPDocumentRendererChild(PDocumentRendererChild* actor) +{ + delete actor; + return true; +} + +bool +TabChild::RecvPDocumentRendererConstructor(PDocumentRendererChild* actor, + const nsRect& documentRect, + const mozilla::gfx::Matrix& transform, + const nsString& bgcolor, + const uint32_t& renderFlags, + const bool& flushLayout, + const nsIntSize& renderSize) +{ + DocumentRendererChild *render = static_cast<DocumentRendererChild *>(actor); + + nsCOMPtr<nsIWebBrowser> browser = do_QueryInterface(WebNavigation()); + if (!browser) + return true; // silently ignore + nsCOMPtr<mozIDOMWindowProxy> window; + if (NS_FAILED(browser->GetContentDOMWindow(getter_AddRefs(window))) || + !window) + { + return true; // silently ignore + } + + nsCString data; + bool ret = render->RenderDocument(nsPIDOMWindowOuter::From(window), + documentRect, transform, + bgcolor, + renderFlags, flushLayout, + renderSize, data); + if (!ret) + return true; // silently ignore + + return PDocumentRendererChild::Send__delete__(actor, renderSize, data); +} + +PColorPickerChild* +TabChild::AllocPColorPickerChild(const nsString&, const nsString&) +{ + NS_RUNTIMEABORT("unused"); + return nullptr; +} + +bool +TabChild::DeallocPColorPickerChild(PColorPickerChild* aColorPicker) +{ + nsColorPickerProxy* picker = static_cast<nsColorPickerProxy*>(aColorPicker); + NS_RELEASE(picker); + return true; +} + +PDatePickerChild* +TabChild::AllocPDatePickerChild(const nsString&, const nsString&) +{ + NS_RUNTIMEABORT("unused"); + return nullptr; +} + +bool +TabChild::DeallocPDatePickerChild(PDatePickerChild* aDatePicker) +{ + nsDatePickerProxy* picker = static_cast<nsDatePickerProxy*>(aDatePicker); + NS_RELEASE(picker); + return true; +} + +PFilePickerChild* +TabChild::AllocPFilePickerChild(const nsString&, const int16_t&) +{ + NS_RUNTIMEABORT("unused"); + return nullptr; +} + +bool +TabChild::DeallocPFilePickerChild(PFilePickerChild* actor) +{ + nsFilePickerProxy* filePicker = static_cast<nsFilePickerProxy*>(actor); + NS_RELEASE(filePicker); + return true; +} + +auto +TabChild::AllocPIndexedDBPermissionRequestChild(const Principal& aPrincipal) + -> PIndexedDBPermissionRequestChild* +{ + MOZ_CRASH("PIndexedDBPermissionRequestChild actors should always be created " + "manually!"); +} + +bool +TabChild::DeallocPIndexedDBPermissionRequestChild( + PIndexedDBPermissionRequestChild* aActor) +{ + MOZ_ASSERT(aActor); + delete aActor; + return true; +} + +bool +TabChild::RecvActivateFrameEvent(const nsString& aType, const bool& capture) +{ + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + NS_ENSURE_TRUE(window, true); + nsCOMPtr<EventTarget> chromeHandler = + do_QueryInterface(window->GetChromeEventHandler()); + NS_ENSURE_TRUE(chromeHandler, true); + RefPtr<ContentListener> listener = new ContentListener(this); + chromeHandler->AddEventListener(aType, listener, capture); + return true; +} + +bool +TabChild::RecvLoadRemoteScript(const nsString& aURL, const bool& aRunInGlobalScope) +{ + if (!mGlobal && !InitTabChildGlobal()) + // This can happen if we're half-destroyed. It's not a fatal + // error. + return true; + + LoadScriptInternal(aURL, aRunInGlobalScope); + return true; +} + +bool +TabChild::RecvAsyncMessage(const nsString& aMessage, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) +{ + if (!mTabChildGlobal) { + return true; + } + + // We should have a message manager if the global is alive, but it + // seems sometimes we don't. Assert in aurora/nightly, but don't + // crash in release builds. + MOZ_DIAGNOSTIC_ASSERT(mTabChildGlobal->mMessageManager); + if (!mTabChildGlobal->mMessageManager) { + return true; + } + + nsCOMPtr<nsIXPConnectJSObjectHolder> kungFuDeathGrip(GetGlobal()); + StructuredCloneData data; + UnpackClonedMessageDataForChild(aData, data); + RefPtr<nsFrameMessageManager> mm = + static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get()); + CrossProcessCpowHolder cpows(Manager(), aCpows); + mm->ReceiveMessage(static_cast<EventTarget*>(mTabChildGlobal), nullptr, + aMessage, false, &data, &cpows, aPrincipal, nullptr); + return true; +} + +bool +TabChild::RecvSwappedWithOtherRemoteLoader(const IPCTabContext& aContext) +{ + nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!ourDocShell)) { + return true; + } + + nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocShell->GetWindow(); + if (NS_WARN_IF(!ourWindow)) { + return true; + } + + RefPtr<nsDocShell> docShell = static_cast<nsDocShell*>(ourDocShell.get()); + + nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget(); + + docShell->SetInFrameSwap(true); + + nsContentUtils::FirePageShowEvent(ourDocShell, ourEventTarget, false); + nsContentUtils::FirePageHideEvent(ourDocShell, ourEventTarget); + + // Owner content type may have changed, so store the possibly updated context + // and notify others. + MaybeInvalidTabContext maybeContext(aContext); + if (!maybeContext.IsValid()) { + NS_ERROR(nsPrintfCString("Received an invalid TabContext from " + "the parent process. (%s)", + maybeContext.GetInvalidReason()).get()); + MOZ_CRASH("Invalid TabContext received from the parent process."); + } + + if (!UpdateTabContextAfterSwap(maybeContext.GetTabContext())) { + MOZ_CRASH("Update to TabContext after swap was denied."); + } + + // Since mIsMozBrowserElement may change in UpdateTabContextAfterSwap, so we + // call UpdateFrameType here to make sure the frameType on the docshell is + // correct. + UpdateFrameType(); + + // Ignore previous value of mTriedBrowserInit since owner content has changed. + mTriedBrowserInit = true; + // Initialize the child side of the browser element machinery, if appropriate. + if (IsMozBrowserOrApp()) { + RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT, true); + } + + nsContentUtils::FirePageShowEvent(ourDocShell, ourEventTarget, true); + + docShell->SetInFrameSwap(false); + + return true; +} + +bool +TabChild::RecvHandleAccessKey(const WidgetKeyboardEvent& aEvent, + nsTArray<uint32_t>&& aCharCodes, + const int32_t& aModifierMask) +{ + nsCOMPtr<nsIDocument> document(GetDocument()); + nsCOMPtr<nsIPresShell> presShell = document->GetShell(); + if (presShell) { + nsPresContext* pc = presShell->GetPresContext(); + if (pc) { + if (!pc->EventStateManager()-> + HandleAccessKey(&(const_cast<WidgetKeyboardEvent&>(aEvent)), + pc, aCharCodes, + aModifierMask, true)) { + // If no accesskey was found, inform the parent so that accesskeys on + // menus can be handled. + WidgetKeyboardEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + SendAccessKeyNotHandled(localEvent); + } + } + } + + return true; +} + +bool +TabChild::RecvAudioChannelChangeNotification(const uint32_t& aAudioChannel, + const float& aVolume, + const bool& aMuted) +{ + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + if (window) { + RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); + MOZ_ASSERT(service); + + service->SetAudioChannelVolume(window, + static_cast<AudioChannel>(aAudioChannel), + aVolume); + service->SetAudioChannelMuted(window, + static_cast<AudioChannel>(aAudioChannel), + aMuted); + } + + return true; +} + +bool +TabChild::RecvSetUseGlobalHistory(const bool& aUse) +{ + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + MOZ_ASSERT(docShell); + + nsresult rv = docShell->SetUseGlobalHistory(aUse); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to set UseGlobalHistory on TabChild docShell"); + } + + return true; +} + +bool +TabChild::RecvPrint(const uint64_t& aOuterWindowID, const PrintData& aPrintData) +{ +#ifdef NS_PRINTING + nsGlobalWindow* outerWindow = + nsGlobalWindow::GetOuterWindowWithId(aOuterWindowID); + if (NS_WARN_IF(!outerWindow)) { + return true; + } + + nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = + do_GetInterface(outerWindow->AsOuter()); + if (NS_WARN_IF(!webBrowserPrint)) { + return true; + } + + nsCOMPtr<nsIPrintSettingsService> printSettingsSvc = + do_GetService("@mozilla.org/gfx/printsettings-service;1"); + if (NS_WARN_IF(!printSettingsSvc)) { + return true; + } + + nsCOMPtr<nsIPrintSettings> printSettings; + nsresult rv = + printSettingsSvc->GetNewPrintSettings(getter_AddRefs(printSettings)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return true; + } + + nsCOMPtr<nsIPrintSession> printSession = + do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return true; + } + + printSettings->SetPrintSession(printSession); + printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings); + rv = webBrowserPrint->Print(printSettings, nullptr); + if (NS_WARN_IF(NS_FAILED(rv))) { + return true; + } + +#endif + return true; +} + +bool +TabChild::RecvUpdateNativeWindowHandle(const uintptr_t& aNewHandle) +{ +#if defined(XP_WIN) && defined(ACCESSIBILITY) + mNativeWindowHandle = aNewHandle; + return true; +#else + return false; +#endif +} + +bool +TabChild::RecvDestroy() +{ + MOZ_ASSERT(mDestroyed == false); + mDestroyed = true; + + nsTArray<PContentPermissionRequestChild*> childArray = + nsContentPermissionUtils::GetContentPermissionRequestChildById(GetTabId()); + + // Need to close undeleted ContentPermissionRequestChilds before tab is closed. + for (auto& permissionRequestChild : childArray) { + auto child = static_cast<RemotePermissionRequest*>(permissionRequestChild); + child->Destroy(); + } + + while (mActiveSuppressDisplayport > 0) { + APZCCallbackHelper::SuppressDisplayport(false, nullptr); + mActiveSuppressDisplayport--; + } + + if (mTabChildGlobal) { + // Message handlers are called from the event loop, so it better be safe to + // run script. + MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); + mTabChildGlobal->DispatchTrustedEvent(NS_LITERAL_STRING("unload")); + } + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + + observerService->RemoveObserver(this, BEFORE_FIRST_PAINT); + + const nsAttrValue::EnumTable* table = + AudioChannelService::GetAudioChannelTable(); + + nsAutoCString topic; + for (uint32_t i = 0; table[i].tag; ++i) { + topic.Assign("audiochannel-activity-"); + topic.Append(table[i].tag); + + observerService->RemoveObserver(this, topic.get()); + } + + // XXX what other code in ~TabChild() should we be running here? + DestroyWindow(); + + // Bounce through the event loop once to allow any delayed teardown runnables + // that were just generated to have a chance to run. + nsCOMPtr<nsIRunnable> deleteRunnable = new DelayedDeleteRunnable(this); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(deleteRunnable)); + + return true; +} + +bool +TabChild::RecvSetDocShellIsActive(const bool& aIsActive, + const bool& aPreserveLayers, + const uint64_t& aLayerObserverEpoch) +{ + // Since SetDocShellIsActive requests come in from both the hang monitor + // channel and the PContent channel, we have an ordering problem. This code + // ensures that we respect the order in which the requests were made and + // ignore stale requests. + if (mLayerObserverEpoch >= aLayerObserverEpoch) { + return true; + } + mLayerObserverEpoch = aLayerObserverEpoch; + + MOZ_ASSERT(mPuppetWidget); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == + LayersBackend::LAYERS_CLIENT); + + auto clearForcePaint = MakeScopeExit([&] { + // We might force a paint, or we might already have painted and this is a + // no-op. In either case, once we exit this scope, we need to alert the + // ProcessHangMonitor that we've finished responding to what might have + // been a request to force paint. This is so that the BackgroundHangMonitor + // for force painting can be made to wait again. + if (aIsActive) { + ProcessHangMonitor::ClearForcePaint(); + } + }); + + // We send the current layer observer epoch to the compositor so that + // TabParent knows whether a layer update notification corresponds to the + // latest SetDocShellIsActive request that was made. + if (ClientLayerManager* clm = mPuppetWidget->GetLayerManager()->AsClientLayerManager()) { + clm->SetLayerObserverEpoch(aLayerObserverEpoch); + } + + // docshell is consider prerendered only if not active yet + mIsPrerendered &= !aIsActive; + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + if (docShell) { + bool wasActive; + docShell->GetIsActive(&wasActive); + if (aIsActive && wasActive) { + // This request is a no-op. In this case, we still want a MozLayerTreeReady + // notification to fire in the parent (so that it knows that the child has + // updated its epoch). ForcePaintNoOp does that. + if (IPCOpen()) { + Unused << SendForcePaintNoOp(aLayerObserverEpoch); + return true; + } + } + + docShell->SetIsActive(aIsActive); + } + + if (aIsActive) { + MakeVisible(); + + // We don't use TabChildBase::GetPresShell() here because that would create + // a content viewer if one doesn't exist yet. Creating a content viewer can + // cause JS to run, which we want to avoid. nsIDocShell::GetPresShell + // returns null if no content viewer exists yet. + if (nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell()) { + if (nsIFrame* root = presShell->FrameConstructor()->GetRootFrame()) { + FrameLayerBuilder::InvalidateAllLayersForFrame( + nsLayoutUtils::GetDisplayRootFrame(root)); + root->SchedulePaint(); + } + + Telemetry::AutoTimer<Telemetry::TABCHILD_PAINT_TIME> timer; + // If we need to repaint, let's do that right away. No sense waiting until + // we get back to the event loop again. We suppress the display port so that + // we only paint what's visible. This ensures that the tab we're switching + // to paints as quickly as possible. + APZCCallbackHelper::SuppressDisplayport(true, presShell); + if (nsContentUtils::IsSafeToRunScript()) { + WebWidget()->PaintNowIfNeeded(); + } else { + RefPtr<nsViewManager> vm = presShell->GetViewManager(); + if (nsView* view = vm->GetRootView()) { + presShell->Paint(view, view->GetBounds(), + nsIPresShell::PAINT_LAYERS); + } + } + APZCCallbackHelper::SuppressDisplayport(false, presShell); + } + } else if (!aPreserveLayers) { + MakeHidden(); + } + + return true; +} + +bool +TabChild::RecvNavigateByKey(const bool& aForward, const bool& aForDocumentNavigation) +{ + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) { + nsCOMPtr<nsIDOMElement> result; + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + + // Move to the first or last document. + uint32_t type = aForward ? + (aForDocumentNavigation ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FIRSTDOC) : + static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_ROOT)) : + (aForDocumentNavigation ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_LASTDOC) : + static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_LAST)); + fm->MoveFocus(window, nullptr, type, + nsIFocusManager::FLAG_BYKEY, getter_AddRefs(result)); + + // No valid root element was found, so move to the first focusable element. + if (!result && aForward && !aForDocumentNavigation) { + fm->MoveFocus(window, nullptr, nsIFocusManager::MOVEFOCUS_FIRST, + nsIFocusManager::FLAG_BYKEY, getter_AddRefs(result)); + } + + SendRequestFocus(false); + } + + return true; +} + +bool +TabChild::RecvHandledWindowedPluginKeyEvent( + const NativeEventData& aKeyEventData, + const bool& aIsConsumed) +{ + if (NS_WARN_IF(!mPuppetWidget)) { + return true; + } + mPuppetWidget->HandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed); + return true; +} + +PRenderFrameChild* +TabChild::AllocPRenderFrameChild() +{ + return new RenderFrameChild(); +} + +bool +TabChild::DeallocPRenderFrameChild(PRenderFrameChild* aFrame) +{ + delete aFrame; + return true; +} + +bool +TabChild::InitTabChildGlobal(FrameScriptLoading aScriptLoading) +{ + if (!mGlobal && !mTabChildGlobal) { + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + NS_ENSURE_TRUE(window, false); + nsCOMPtr<EventTarget> chromeHandler = + do_QueryInterface(window->GetChromeEventHandler()); + NS_ENSURE_TRUE(chromeHandler, false); + + RefPtr<TabChildGlobal> scope = new TabChildGlobal(this); + mTabChildGlobal = scope; + + nsISupports* scopeSupports = NS_ISUPPORTS_CAST(EventTarget*, scope); + + NS_NAMED_LITERAL_CSTRING(globalId, "outOfProcessTabChildGlobal"); + NS_ENSURE_TRUE(InitChildGlobalInternal(scopeSupports, globalId), false); + + scope->Init(); + + nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(chromeHandler); + NS_ENSURE_TRUE(root, false); + root->SetParentTarget(scope); + } + + if (aScriptLoading != DONT_LOAD_SCRIPTS && !mTriedBrowserInit) { + mTriedBrowserInit = true; + // Initialize the child side of the browser element machinery, + // if appropriate. + if (IsMozBrowserOrApp()) { + RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT, true); + } + } + + return true; +} + +bool +TabChild::InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const uint64_t& aLayersId, + PRenderFrameChild* aRenderFrame) +{ + mPuppetWidget->InitIMEState(); + + RenderFrameChild* remoteFrame = static_cast<RenderFrameChild*>(aRenderFrame); + if (!remoteFrame) { + NS_WARNING("failed to construct RenderFrame"); + return false; + } + + MOZ_ASSERT(aLayersId != 0); + mTextureFactoryIdentifier = aTextureFactoryIdentifier; + + // Pushing layers transactions directly to a separate + // compositor context. + PCompositorBridgeChild* compositorChild = CompositorBridgeChild::Get(); + if (!compositorChild) { + NS_WARNING("failed to get CompositorBridgeChild instance"); + PRenderFrameChild::Send__delete__(remoteFrame); + return false; + } + + ShadowLayerForwarder* lf = + mPuppetWidget->GetLayerManager( + nullptr, mTextureFactoryIdentifier.mParentBackend) + ->AsShadowForwarder(); + // As long as we are creating a ClientLayerManager for the puppet widget, + // lf must be non-null here. + MOZ_ASSERT(lf); + + if (lf) { + nsTArray<LayersBackend> backends; + backends.AppendElement(mTextureFactoryIdentifier.mParentBackend); + bool success; + PLayerTransactionChild* shadowManager = + compositorChild->SendPLayerTransactionConstructor(backends, + aLayersId, &mTextureFactoryIdentifier, &success); + if (!success) { + NS_WARNING("failed to properly allocate layer transaction"); + PRenderFrameChild::Send__delete__(remoteFrame); + return false; + } + + if (!shadowManager) { + NS_WARNING("failed to construct LayersChild"); + // This results in |remoteFrame| being deleted. + PRenderFrameChild::Send__delete__(remoteFrame); + return false; + } + + lf->SetShadowManager(shadowManager); + lf->IdentifyTextureHost(mTextureFactoryIdentifier); + ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier); + gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier); + } + + mRemoteFrame = remoteFrame; + if (aLayersId != 0) { + if (!sTabChildren) { + sTabChildren = new TabChildMap; + } + MOZ_ASSERT(!sTabChildren->Get(aLayersId)); + sTabChildren->Put(aLayersId, this); + mLayersId = aLayersId; + } + + mApzcTreeManager = CompositorBridgeChild::Get()->GetAPZCTreeManager(mLayersId); + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + + if (observerService) { + observerService->AddObserver(this, + BEFORE_FIRST_PAINT, + false); + } + return true; +} + +void +TabChild::GetDPI(float* aDPI) +{ + *aDPI = -1.0; + if (!mRemoteFrame) { + return; + } + + if (mDPI > 0) { + *aDPI = mDPI; + return; + } + + // Fallback to a sync call if needed. + SendGetDPI(aDPI); +} + +void +TabChild::GetDefaultScale(double* aScale) +{ + *aScale = -1.0; + if (!mRemoteFrame) { + return; + } + + if (mDefaultScale > 0) { + *aScale = mDefaultScale; + return; + } + + // Fallback to a sync call if needed. + SendGetDefaultScale(aScale); +} + +void +TabChild::GetWidgetRounding(int32_t* aRounding) +{ + *aRounding = 1; + if (!mRemoteFrame) { + return; + } + if (mRounding > 0) { + *aRounding = mRounding; + return; + } + + // Fallback to a sync call if needed. + SendGetWidgetRounding(aRounding); +} + +void +TabChild::GetMaxTouchPoints(uint32_t* aTouchPoints) +{ + // Fallback to a sync call. + SendGetMaxTouchPoints(aTouchPoints); +} + +void +TabChild::NotifyPainted() +{ + if (!mNotified) { + mRemoteFrame->SendNotifyCompositorTransaction(); + mNotified = true; + } +} + +void +TabChild::MakeVisible() +{ + if (mPuppetWidget && mPuppetWidget->IsVisible()) { + return; + } + + if (mPuppetWidget) { + mPuppetWidget->Show(true); + } +} + +void +TabChild::MakeHidden() +{ + if (mPuppetWidget && !mPuppetWidget->IsVisible()) { + return; + } + + CompositorBridgeChild* compositor = CompositorBridgeChild::Get(); + + // Clear cached resources directly. This avoids one extra IPC + // round-trip from CompositorBridgeChild to CompositorBridgeParent. + compositor->RecvClearCachedResources(mLayersId); + + if (mPuppetWidget) { + mPuppetWidget->Show(false); + } +} + +NS_IMETHODIMP +TabChild::GetMessageManager(nsIContentFrameMessageManager** aResult) +{ + if (mTabChildGlobal) { + NS_ADDREF(*aResult = mTabChildGlobal); + return NS_OK; + } + *aResult = nullptr; + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +TabChild::GetWebBrowserChrome(nsIWebBrowserChrome3** aWebBrowserChrome) +{ + NS_IF_ADDREF(*aWebBrowserChrome = mWebBrowserChrome); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::SetWebBrowserChrome(nsIWebBrowserChrome3* aWebBrowserChrome) +{ + mWebBrowserChrome = aWebBrowserChrome; + return NS_OK; +} + +void +TabChild::SendRequestFocus(bool aCanFocus) +{ + PBrowserChild::SendRequestFocus(aCanFocus); +} + +void +TabChild::SendGetTabCount(uint32_t* tabCount) +{ + PBrowserChild::SendGetTabCount(tabCount); +} + +void +TabChild::EnableDisableCommands(const nsAString& aAction, + nsTArray<nsCString>& aEnabledCommands, + nsTArray<nsCString>& aDisabledCommands) +{ + PBrowserChild::SendEnableDisableCommands(PromiseFlatString(aAction), + aEnabledCommands, aDisabledCommands); +} + +NS_IMETHODIMP +TabChild::GetTabId(uint64_t* aId) +{ + *aId = GetTabId(); + return NS_OK; +} + +void +TabChild::SetTabId(const TabId& aTabId) +{ + MOZ_ASSERT(mUniqueId == 0); + + mUniqueId = aTabId; + NestedTabChildMap()[mUniqueId] = this; +} + +bool +TabChild::DoSendBlockingMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle<JSObject *> aCpows, + nsIPrincipal* aPrincipal, + nsTArray<StructuredCloneData>* aRetVal, + bool aIsSync) +{ + ClonedMessageData data; + if (!BuildClonedMessageDataForChild(Manager(), aData, data)) { + return false; + } + InfallibleTArray<CpowEntry> cpows; + if (aCpows && !Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { + return false; + } + if (aIsSync) { + return SendSyncMessage(PromiseFlatString(aMessage), data, cpows, + Principal(aPrincipal), aRetVal); + } + + return SendRpcMessage(PromiseFlatString(aMessage), data, cpows, + Principal(aPrincipal), aRetVal); +} + +nsresult +TabChild::DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle<JSObject *> aCpows, + nsIPrincipal* aPrincipal) +{ + ClonedMessageData data; + if (!BuildClonedMessageDataForChild(Manager(), aData, data)) { + return NS_ERROR_DOM_DATA_CLONE_ERR; + } + InfallibleTArray<CpowEntry> cpows; + if (aCpows && !Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { + return NS_ERROR_UNEXPECTED; + } + if (!SendAsyncMessage(PromiseFlatString(aMessage), cpows, + Principal(aPrincipal), data)) { + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +/* static */ nsTArray<RefPtr<TabChild>> +TabChild::GetAll() +{ + nsTArray<RefPtr<TabChild>> list; + if (!sTabChildren) { + return list; + } + + for (auto iter = sTabChildren->Iter(); !iter.Done(); iter.Next()) { + list.AppendElement(iter.Data()); + } + + return list; +} + +TabChild* +TabChild::GetFrom(nsIPresShell* aPresShell) +{ + nsIDocument* doc = aPresShell->GetDocument(); + if (!doc) { + return nullptr; + } + nsCOMPtr<nsIDocShell> docShell(doc->GetDocShell()); + return GetFrom(docShell); +} + +TabChild* +TabChild::GetFrom(uint64_t aLayersId) +{ + if (!sTabChildren) { + return nullptr; + } + return sTabChildren->Get(aLayersId); +} + +void +TabChild::DidComposite(uint64_t aTransactionId, + const TimeStamp& aCompositeStart, + const TimeStamp& aCompositeEnd) +{ + MOZ_ASSERT(mPuppetWidget); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == + LayersBackend::LAYERS_CLIENT); + + RefPtr<ClientLayerManager> manager = mPuppetWidget->GetLayerManager()->AsClientLayerManager(); + + manager->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd); +} + +void +TabChild::DidRequestComposite(const TimeStamp& aCompositeReqStart, + const TimeStamp& aCompositeReqEnd) +{ + nsCOMPtr<nsIDocShell> docShellComPtr = do_GetInterface(WebNavigation()); + if (!docShellComPtr) { + return; + } + + nsDocShell* docShell = static_cast<nsDocShell*>(docShellComPtr.get()); + RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get(); + + if (timelines && timelines->HasConsumer(docShell)) { + // Since we're assuming that it's impossible for content JS to directly + // trigger a synchronous paint, we can avoid capturing a stack trace here, + // which means we won't run into JS engine reentrancy issues like bug + // 1310014. + timelines->AddMarkerForDocShell(docShell, + "CompositeForwardTransaction", aCompositeReqStart, + MarkerTracingType::START, MarkerStackRequest::NO_STACK); + timelines->AddMarkerForDocShell(docShell, + "CompositeForwardTransaction", aCompositeReqEnd, + MarkerTracingType::END, MarkerStackRequest::NO_STACK); + } +} + +void +TabChild::ClearCachedResources() +{ + MOZ_ASSERT(mPuppetWidget); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == + LayersBackend::LAYERS_CLIENT); + + ClientLayerManager *manager = mPuppetWidget->GetLayerManager()->AsClientLayerManager(); + manager->ClearCachedResources(); +} + +void +TabChild::InvalidateLayers() +{ + MOZ_ASSERT(mPuppetWidget); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == + LayersBackend::LAYERS_CLIENT); + + RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager(); + FrameLayerBuilder::InvalidateAllLayers(lm); +} + +void +TabChild::ReinitRendering() +{ + MOZ_ASSERT(mLayersId); + + // Before we establish a new PLayerTransaction, we must connect our layer tree + // id, CompositorBridge, and the widget compositor all together again. + // Normally this happens in TabParent before TabChild is given rendering + // information. + // + // In this case, we will send a sync message to our TabParent, which in turn + // will send a sync message to the Compositor of the widget owning this tab. + // This guarantees the correct association is in place before our + // PLayerTransaction constructor message arrives on the cross-process + // compositor bridge. + SendEnsureLayersConnected(); + + RefPtr<CompositorBridgeChild> cb = CompositorBridgeChild::Get(); + + bool success; + nsTArray<LayersBackend> ignored; + PLayerTransactionChild* shadowManager = + cb->SendPLayerTransactionConstructor(ignored, LayersId(), &mTextureFactoryIdentifier, &success); + if (!success) { + NS_WARNING("failed to re-allocate layer transaction"); + return; + } + + if (!shadowManager) { + NS_WARNING("failed to re-construct LayersChild"); + return; + } + + RefPtr<LayerManager> lm = mPuppetWidget->RecreateLayerManager(shadowManager); + ShadowLayerForwarder* lf = lm->AsShadowForwarder(); + lf->IdentifyTextureHost(mTextureFactoryIdentifier); + + mApzcTreeManager = CompositorBridgeChild::Get()->GetAPZCTreeManager(mLayersId); + if (mApzcTreeManager) { + APZChild* apz = ContentProcessController::Create(mUniqueId); + CompositorBridgeChild::Get()->SendPAPZConstructor(apz, mLayersId); + } + + nsCOMPtr<nsIDocument> doc(GetDocument()); + doc->NotifyLayerManagerRecreated(); +} + +void +TabChild::CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier) +{ + gfxPlatform::GetPlatform()->CompositorUpdated(); + + RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager(); + ClientLayerManager* clm = lm->AsClientLayerManager(); + MOZ_ASSERT(clm); + + mTextureFactoryIdentifier = aNewIdentifier; + clm->UpdateTextureFactoryIdentifier(aNewIdentifier); + FrameLayerBuilder::InvalidateAllLayers(clm); +} + +NS_IMETHODIMP +TabChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, const char16_t *aTipText, + const char16_t *aTipDir) +{ + nsString str(aTipText); + nsString dir(aTipDir); + SendShowTooltip(aXCoords, aYCoords, str, dir); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::OnHideTooltip() +{ + SendHideTooltip(); + return NS_OK; +} + +bool +TabChild::RecvRequestNotifyAfterRemotePaint() +{ + // Get the CompositorBridgeChild instance for this content thread. + CompositorBridgeChild* compositor = CompositorBridgeChild::Get(); + + // Tell the CompositorBridgeChild that, when it gets a RemotePaintIsReady + // message that it should forward it us so that we can bounce it to our + // RenderFrameParent. + compositor->RequestNotifyAfterRemotePaint(this); + return true; +} + +bool +TabChild::RecvUIResolutionChanged(const float& aDpi, + const int32_t& aRounding, + const double& aScale) +{ + ScreenIntSize oldScreenSize = GetInnerSize(); + mDPI = 0; + mRounding = 0; + mDefaultScale = 0; + static_cast<PuppetWidget*>(mPuppetWidget.get())->UpdateBackingScaleCache(aDpi, aRounding, aScale); + nsCOMPtr<nsIDocument> document(GetDocument()); + nsCOMPtr<nsIPresShell> presShell = document->GetShell(); + if (presShell) { + RefPtr<nsPresContext> presContext = presShell->GetPresContext(); + if (presContext) { + presContext->UIResolutionChangedSync(); + } + } + + ScreenIntSize screenSize = GetInnerSize(); + if (mHasValidInnerSize && oldScreenSize != screenSize) { + ScreenIntRect screenRect = GetOuterRect(); + mPuppetWidget->Resize(screenRect.x + mClientOffset.x + mChromeDisp.x, + screenRect.y + mClientOffset.y + mChromeDisp.y, + screenSize.width, screenSize.height, true); + + nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation()); + baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height, + nsIBaseWindow::eRepaint); + } + + return true; +} + +bool +TabChild::RecvThemeChanged(nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache) +{ + LookAndFeel::SetIntCache(aLookAndFeelIntCache); + nsCOMPtr<nsIDocument> document(GetDocument()); + nsCOMPtr<nsIPresShell> presShell = document->GetShell(); + if (presShell) { + RefPtr<nsPresContext> presContext = presShell->GetPresContext(); + if (presContext) { + presContext->ThemeChanged(); + } + } + return true; +} + +bool +TabChild::RecvSetFreshProcess() +{ + mIsFreshProcess = true; + return true; +} + +mozilla::plugins::PPluginWidgetChild* +TabChild::AllocPPluginWidgetChild() +{ + return new mozilla::plugins::PluginWidgetChild(); +} + +bool +TabChild::DeallocPPluginWidgetChild(mozilla::plugins::PPluginWidgetChild* aActor) +{ + delete aActor; + return true; +} + +nsresult +TabChild::CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut) +{ + *aOut = nullptr; + mozilla::plugins::PluginWidgetChild* child = + static_cast<mozilla::plugins::PluginWidgetChild*>(SendPPluginWidgetConstructor()); + if (!child) { + NS_ERROR("couldn't create PluginWidgetChild"); + return NS_ERROR_UNEXPECTED; + } + nsCOMPtr<nsIWidget> pluginWidget = nsIWidget::CreatePluginProxyWidget(this, child); + if (!pluginWidget) { + NS_ERROR("couldn't create PluginWidgetProxy"); + return NS_ERROR_UNEXPECTED; + } + + nsWidgetInitData initData; + initData.mWindowType = eWindowType_plugin_ipc_content; + initData.mUnicode = false; + initData.clipChildren = true; + initData.clipSiblings = true; + nsresult rv = pluginWidget->Create(aParent, nullptr, + LayoutDeviceIntRect(0, 0, 0, 0), + &initData); + if (NS_FAILED(rv)) { + NS_WARNING("Creating native plugin widget on the chrome side failed."); + } + pluginWidget.forget(aOut); + return rv; +} + +ScreenIntSize +TabChild::GetInnerSize() +{ + LayoutDeviceIntSize innerSize = + RoundedToInt(mUnscaledInnerSize * mPuppetWidget->GetDefaultScale()); + return ViewAs<ScreenPixel>(innerSize, PixelCastJustification::LayoutDeviceIsScreenForTabDims); +}; + +ScreenIntRect +TabChild::GetOuterRect() +{ + LayoutDeviceIntRect outerRect = + RoundedToInt(mUnscaledOuterRect * mPuppetWidget->GetDefaultScale()); + return ViewAs<ScreenPixel>(outerRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims); +} + +void +TabChild::ForcePaint(uint64_t aLayerObserverEpoch) +{ + if (!IPCOpen()) { + // Don't bother doing anything now. Better to wait until we receive the + // message on the PContent channel. + return; + } + + nsAutoScriptBlocker scriptBlocker; + RecvSetDocShellIsActive(true, false, aLayerObserverEpoch); +} + +/******************************************************************************* + * nsISHistoryListener + ******************************************************************************/ + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryNewEntry(nsIURI *aNewURI, int32_t aOldIndex) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryGoBack(nsIURI *aBackURI, bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryGoForward(nsIURI *aForwardURI, bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryReload(nsIURI *aReloadURI, uint32_t aReloadFlags, bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryGotoIndex(int32_t aIndex, nsIURI *aGotoURI, bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryPurge(int32_t aNumEntries, bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryReplaceEntry(int32_t aIndex) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnLengthChange(int32_t aCount) +{ + RefPtr<TabChild> tabChild(mTabChild); + if (!tabChild) { + return NS_ERROR_FAILURE; + } + + if (aCount < 0) { + return NS_ERROR_FAILURE; + } + + return tabChild->SendNotifySessionHistoryChange(aCount) ? + NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnRequestCrossBrowserNavigation(uint32_t aIndex) +{ + RefPtr<TabChild> tabChild(mTabChild); + if (!tabChild) { + return NS_ERROR_FAILURE; + } + + return tabChild->SendRequestCrossBrowserNavigation(aIndex) ? + NS_OK : NS_ERROR_FAILURE; +} + +TabChildGlobal::TabChildGlobal(TabChildBase* aTabChild) +: mTabChild(aTabChild) +{ + SetIsNotDOMBinding(); +} + +TabChildGlobal::~TabChildGlobal() +{ +} + +void +TabChildGlobal::Init() +{ + NS_ASSERTION(!mMessageManager, "Re-initializing?!?"); + mMessageManager = new nsFrameMessageManager(mTabChild, + nullptr, + MM_CHILD); +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(TabChildGlobal) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TabChildGlobal, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChild); + tmp->UnlinkHostObjectURIs(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TabChildGlobal, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChild) + tmp->TraverseHostObjectURIs(cb); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TabChildGlobal) + NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager) + NS_INTERFACE_MAP_ENTRY(nsIMessageSender) + NS_INTERFACE_MAP_ENTRY(nsISyncMessageSender) + NS_INTERFACE_MAP_ENTRY(nsIContentFrameMessageManager) + NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) + NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ContentFrameMessageManager) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(TabChildGlobal, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(TabChildGlobal, DOMEventTargetHelper) + +// This method isn't automatically forwarded safely because it's notxpcom, so +// the IDL binding doesn't know what value to return. +NS_IMETHODIMP_(bool) +TabChildGlobal::MarkForCC() +{ + if (mTabChild) { + mTabChild->MarkScopesForCC(); + } + EventListenerManager* elm = GetExistingListenerManager(); + if (elm) { + elm->MarkForCC(); + } + return mMessageManager ? mMessageManager->MarkForCC() : false; +} + +NS_IMETHODIMP +TabChildGlobal::GetContent(mozIDOMWindowProxy** aContent) +{ + *aContent = nullptr; + if (!mTabChild) + return NS_ERROR_NULL_POINTER; + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(mTabChild->WebNavigation()); + window.forget(aContent); + return NS_OK; +} + +NS_IMETHODIMP +TabChildGlobal::GetDocShell(nsIDocShell** aDocShell) +{ + *aDocShell = nullptr; + if (!mTabChild) + return NS_ERROR_NULL_POINTER; + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mTabChild->WebNavigation()); + docShell.swap(*aDocShell); + return NS_OK; +} + +nsIPrincipal* +TabChildGlobal::GetPrincipal() +{ + if (!mTabChild) + return nullptr; + return mTabChild->GetPrincipal(); +} + +JSObject* +TabChildGlobal::GetGlobalJSObject() +{ + NS_ENSURE_TRUE(mTabChild, nullptr); + nsCOMPtr<nsIXPConnectJSObjectHolder> ref = mTabChild->GetGlobal(); + NS_ENSURE_TRUE(ref, nullptr); + return ref->GetJSObject(); +} |