Mypal/dom/ipc/TabChild.cpp

3288 lines
98 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/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>
#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 "nsISupportsPrimitives.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 "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 "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(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());
}
}
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?");
}
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;
}
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;
}
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();
}
// 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);
}
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();
}