/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8; -*- */ /* vim: set sw=2 sts=2 ts=8 et 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 "Layers.h" #include "ContentChild.h" #include "IndexedDBChild.h" #include "mozilla/Preferences.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/EventListenerManager.h" #include "mozilla/IntentionalCrash.h" #include "mozilla/docshell/OfflineCacheUpdateChild.h" #include "mozilla/ipc/DocumentRendererChild.h" #include "mozilla/ipc/FileDescriptorUtils.h" #include "mozilla/layers/ActiveElementManager.h" #include "mozilla/layers/APZCCallbackHelper.h" #include "mozilla/layers/AsyncPanZoomController.h" #include "mozilla/layers/CompositorChild.h" #include "mozilla/layers/ImageBridgeChild.h" #include "mozilla/layers/ShadowLayers.h" #include "mozilla/layout/RenderFrameChild.h" #include "mozilla/MouseEvents.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 "nsCxPusher.h" #include "nsEmbedCID.h" #include #ifdef MOZ_CRASHREPORTER #include "nsExceptionHandler.h" #endif #include "nsFilePickerProxy.h" #include "mozilla/dom/Element.h" #include "nsIBaseWindow.h" #include "nsICachedFileDescriptorListener.h" #include "nsIDocumentInlines.h" #include "nsIDocShellTreeOwner.h" #include "nsIDOMEvent.h" #include "nsIDOMWindow.h" #include "nsIDOMWindowUtils.h" #include "nsIDocShell.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 "nsInterfaceHashtable.h" #include "nsPIDOMWindow.h" #include "nsPIWindowRoot.h" #include "nsLayoutUtils.h" #include "nsPrintfCString.h" #include "nsThreadUtils.h" #include "nsWeakReference.h" #include "PermissionMessageUtils.h" #include "PCOMContentPermissionRequestChild.h" #include "PuppetWidget.h" #include "StructuredCloneUtils.h" #include "nsViewportInfo.h" #include "JavaScriptChild.h" #include "nsILoadContext.h" #include "ipc/nsGUIEventIPC.h" #include "mozilla/gfx/Matrix.h" #include "UnitTransforms.h" #include "ClientLayerManager.h" #include "nsColorPickerProxy.h" #ifdef DEBUG #include "PCOMContentPermissionRequestChild.h" #endif /* DEBUG */ #define BROWSER_ELEMENT_CHILD_SCRIPT \ NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js") using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::dom::ipc; using namespace mozilla::ipc; using namespace mozilla::layers; using namespace mozilla::layout; using namespace mozilla::docshell; using namespace mozilla::dom::indexedDB; using namespace mozilla::widget; using namespace mozilla::jsipc; NS_IMPL_ISUPPORTS(ContentListener, nsIDOMEventListener) static const CSSSize kDefaultViewportSize(980, 480); static const char BROWSER_ZOOM_TO_RECT[] = "browser-zoom-to-rect"; static const char BEFORE_FIRST_PAINT[] = "before-first-paint"; static bool sCpowsEnabled = false; static int32_t sActiveDurationMs = 10; static bool sActiveDurationMsSet = false; typedef nsDataHashtable TabChildMap; static TabChildMap* sTabChildren; TabChildBase::TabChildBase() : mContentDocumentIsDisplayed(false) , mTabChildGlobal(nullptr) , mInnerSize(0, 0) { } NS_IMPL_CYCLE_COLLECTING_ADDREF(TabChildBase) NS_IMPL_CYCLE_COLLECTING_RELEASE(TabChildBase) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabChildBase) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION(TabChildBase, mTabChildGlobal, mGlobal) void TabChildBase::InitializeRootMetrics() { // Calculate a really simple resolution that we probably won't // be keeping, as well as putting the scroll offset back to // the top-left of the page. mLastRootMetrics.mViewport = CSSRect(CSSPoint(), kDefaultViewportSize); mLastRootMetrics.mCompositionBounds = ParentLayerIntRect( ParentLayerIntPoint(), ViewAs(mInnerSize, PixelCastJustification::ScreenToParentLayerForRoot)); mLastRootMetrics.SetZoom(mLastRootMetrics.CalculateIntrinsicScale()); mLastRootMetrics.mDevPixelsPerCSSPixel = WebWidget()->GetDefaultScale(); // We use ScreenToLayerScale(1) below in order to turn the // async zoom amount into the gecko zoom amount. mLastRootMetrics.mCumulativeResolution = mLastRootMetrics.GetZoom() / mLastRootMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1); // This is the root layer, so the cumulative resolution is the same // as the resolution. mLastRootMetrics.mResolution = mLastRootMetrics.mCumulativeResolution / LayoutDeviceToParentLayerScale(1); mLastRootMetrics.SetScrollOffset(CSSPoint(0, 0)); } void TabChildBase::SetCSSViewport(const CSSSize& aSize) { mOldViewportSize = aSize; if (mContentDocumentIsDisplayed) { nsCOMPtr utils(GetDOMWindowUtils()); utils->SetCSSViewport(aSize.width, aSize.height); } } CSSSize TabChildBase::GetPageSize(nsCOMPtr aDocument, const CSSSize& aViewport) { nsCOMPtr htmlDOMElement = aDocument->GetHtmlElement(); HTMLBodyElement* bodyDOMElement = aDocument->GetBodyElement(); if (!htmlDOMElement && !bodyDOMElement) { // For non-HTML content (e.g. SVG), just assume page size == viewport size. return aViewport; } int32_t htmlWidth = 0, htmlHeight = 0; if (htmlDOMElement) { htmlWidth = htmlDOMElement->ScrollWidth(); htmlHeight = htmlDOMElement->ScrollHeight(); } int32_t bodyWidth = 0, bodyHeight = 0; if (bodyDOMElement) { bodyWidth = bodyDOMElement->ScrollWidth(); bodyHeight = bodyDOMElement->ScrollHeight(); } return CSSSize(std::max(htmlWidth, bodyWidth), std::max(htmlHeight, bodyHeight)); } bool TabChildBase::HandlePossibleViewportChange() { if (!IsAsyncPanZoomEnabled()) { return false; } nsCOMPtr document(GetDocument()); nsCOMPtr utils(GetDOMWindowUtils()); nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, mInnerSize); uint32_t presShellId; mozilla::layers::FrameMetrics::ViewID viewId; bool scrollIdentifiersValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers( document->GetDocumentElement(), &presShellId, &viewId); if (scrollIdentifiersValid) { ZoomConstraints constraints( viewportInfo.IsZoomAllowed(), viewportInfo.IsDoubleTapZoomAllowed(), viewportInfo.GetMinZoom(), viewportInfo.GetMaxZoom()); DoUpdateZoomConstraints(presShellId, viewId, /* isRoot = */ true, constraints); } float screenW = mInnerSize.width; float screenH = mInnerSize.height; CSSSize viewport(viewportInfo.GetSize()); // We're not being displayed in any way; don't bother doing anything because // that will just confuse future adjustments. if (!screenW || !screenH) { return false; } CSSSize oldBrowserSize = mOldViewportSize; mLastRootMetrics.mViewport.SizeTo(viewport); if (oldBrowserSize == CSSSize()) { oldBrowserSize = kDefaultViewportSize; } SetCSSViewport(viewport); // If this page has not been painted yet, then this must be getting run // because a meta-viewport element was added (via the DOMMetaAdded handler). // in this case, we should not do anything that forces a reflow (see bug // 759678) such as requesting the page size or sending a viewport update. this // code will get run again in the before-first-paint handler and that point we // will run though all of it. the reason we even bother executing up to this // point on the DOMMetaAdded handler is so that scripts that use // window.innerWidth before they are painted have a correct value (bug // 771575). if (!mContentDocumentIsDisplayed) { return false; } ScreenIntSize oldScreenSize = ViewAs( mLastRootMetrics.mCompositionBounds.Size(), PixelCastJustification::ScreenToParentLayerForRoot); if (oldScreenSize == ScreenIntSize()) { oldScreenSize = mInnerSize; } FrameMetrics metrics(mLastRootMetrics); metrics.mViewport = CSSRect(CSSPoint(), viewport); metrics.mCompositionBounds = ParentLayerIntRect( ParentLayerIntPoint(), ViewAs(mInnerSize, PixelCastJustification::ScreenToParentLayerForRoot)); metrics.SetRootCompositionSize( ScreenSize(mInnerSize) * ScreenToLayoutDeviceScale(1.0f) / metrics.mDevPixelsPerCSSPixel); // This change to the zoom accounts for all types of changes I can conceive: // 1. screen size changes, CSS viewport does not (pages with no meta viewport // or a fixed size viewport) // 2. screen size changes, CSS viewport also does (pages with a device-width // viewport) // 3. screen size remains constant, but CSS viewport changes (meta viewport // tag is added or removed) // 4. neither screen size nor CSS viewport changes // // In all of these cases, we maintain how much actual content is visible // within the screen width. Note that "actual content" may be different with // respect to CSS pixels because of the CSS viewport size changing. float oldIntrinsicScale = std::max(oldScreenSize.width / oldBrowserSize.width, oldScreenSize.height / oldBrowserSize.height); metrics.ZoomBy(metrics.CalculateIntrinsicScale().scale / oldIntrinsicScale); // Changing the zoom when we're not doing a first paint will get ignored // by AsyncPanZoomController and causes a blurry flash. bool isFirstPaint; nsresult rv = utils->GetIsFirstPaint(&isFirstPaint); if (NS_FAILED(rv) || isFirstPaint) { // FIXME/bug 799585(?): GetViewportInfo() returns a defaultZoom of // 0.0 to mean "did not calculate a zoom". In that case, we default // it to the intrinsic scale. if (viewportInfo.GetDefaultZoom().scale < 0.01f) { viewportInfo.SetDefaultZoom(metrics.CalculateIntrinsicScale()); } CSSToScreenScale defaultZoom = viewportInfo.GetDefaultZoom(); MOZ_ASSERT(viewportInfo.GetMinZoom() <= defaultZoom && defaultZoom <= viewportInfo.GetMaxZoom()); metrics.SetZoom(defaultZoom); metrics.SetScrollId(viewId); } metrics.mCumulativeResolution = metrics.GetZoom() / metrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1); // This is the root layer, so the cumulative resolution is the same // as the resolution. metrics.mResolution = metrics.mCumulativeResolution / LayoutDeviceToParentLayerScale(1); utils->SetResolution(metrics.mResolution.scale, metrics.mResolution.scale); CSSSize scrollPort = metrics.CalculateCompositedSizeInCssPixels(); utils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height); // The call to GetPageSize forces a resize event to content, so we need to // make sure that we have the right CSS viewport and // scrollPositionClampingScrollPortSize set up before that happens. CSSSize pageSize = GetPageSize(document, viewport); if (!pageSize.width) { // Return early rather than divide by 0. return false; } metrics.mScrollableRect = CSSRect(CSSPoint(), pageSize); // Calculate a display port _after_ having a scrollable rect because the // display port is clamped to the scrollable rect. metrics.SetDisplayPortMargins(AsyncPanZoomController::CalculatePendingDisplayPort( // The page must have been refreshed in some way such as a new document or // new CSS viewport, so we know that there's no velocity, acceleration, and // we have no idea how long painting will take. metrics, ScreenPoint(0.0f, 0.0f), 0.0)); metrics.SetUseDisplayPortMargins(); // Force a repaint with these metrics. This, among other things, sets the // displayport, so we start with async painting. mLastRootMetrics = ProcessUpdateFrame(metrics); if (viewportInfo.IsZoomAllowed() && scrollIdentifiersValid) { // If the CSS viewport is narrower than the screen (i.e. width <= device-width) // then we disable double-tap-to-zoom behaviour. bool allowDoubleTapZoom = (viewport.width > screenW / metrics.mDevPixelsPerCSSPixel.scale); if (allowDoubleTapZoom != viewportInfo.IsDoubleTapZoomAllowed()) { viewportInfo.SetAllowDoubleTapZoom(allowDoubleTapZoom); ZoomConstraints constraints( viewportInfo.IsZoomAllowed(), viewportInfo.IsDoubleTapZoomAllowed(), viewportInfo.GetMinZoom(), viewportInfo.GetMaxZoom()); DoUpdateZoomConstraints(presShellId, viewId, /* isRoot = */ true, constraints); } } return true; } already_AddRefed TabChildBase::GetDOMWindowUtils() { nsCOMPtr window = do_GetInterface(WebNavigation()); nsCOMPtr utils = do_GetInterface(window); return utils.forget(); } already_AddRefed TabChildBase::GetDocument() { nsCOMPtr domDoc; WebNavigation()->GetDocument(getter_AddRefs(domDoc)); nsCOMPtr doc(do_QueryInterface(domDoc)); return doc.forget(); } void TabChildBase::DispatchMessageManagerMessage(const nsAString& aMessageName, const nsAString& aJSONData) { AutoSafeJSContext cx; JS::Rooted json(cx, JSVAL_NULL); StructuredCloneData cloneData; JSAutoStructuredCloneBuffer buffer; if (JS_ParseJSON(cx, static_cast(aJSONData.BeginReading()), aJSONData.Length(), &json)) { WriteStructuredClone(cx, json, buffer, cloneData.mClosure); cloneData.mData = buffer.data(); cloneData.mDataLength = buffer.nbytes(); } nsCOMPtr kungFuDeathGrip(GetGlobal()); // Let the BrowserElementScrolling helper (if it exists) for this // content manipulate the frame state. nsRefPtr mm = static_cast(mTabChildGlobal->mMessageManager.get()); mm->ReceiveMessage(static_cast(mTabChildGlobal), aMessageName, false, &cloneData, nullptr, nullptr, nullptr); } bool TabChildBase::UpdateFrameHandler(const FrameMetrics& aFrameMetrics) { MOZ_ASSERT(aFrameMetrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID); if (aFrameMetrics.mIsRoot) { nsCOMPtr utils(GetDOMWindowUtils()); if (APZCCallbackHelper::HasValidPresShellId(utils, aFrameMetrics)) { mLastRootMetrics = ProcessUpdateFrame(aFrameMetrics); APZCCallbackHelper::UpdateCallbackTransform(aFrameMetrics, mLastRootMetrics); return true; } } else { // aFrameMetrics.mIsRoot is false, so we are trying to update a subframe. // This requires special handling. nsCOMPtr content = nsLayoutUtils::FindContentFor( aFrameMetrics.GetScrollId()); if (content) { FrameMetrics newSubFrameMetrics(aFrameMetrics); APZCCallbackHelper::UpdateSubFrame(content, newSubFrameMetrics); APZCCallbackHelper::UpdateCallbackTransform(aFrameMetrics, newSubFrameMetrics); return true; } } // We've recieved a message that is out of date and we want to ignore. // However we can't reply without painting so we reply by painting the // exact same thing as we did before. mLastRootMetrics = ProcessUpdateFrame(mLastRootMetrics); return true; } FrameMetrics TabChildBase::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics) { if (!mGlobal || !mTabChildGlobal) { return aFrameMetrics; } nsCOMPtr utils(GetDOMWindowUtils()); FrameMetrics newMetrics = aFrameMetrics; APZCCallbackHelper::UpdateRootFrame(utils, newMetrics); CSSSize cssCompositedSize = newMetrics.CalculateCompositedSizeInCssPixels(); // The BrowserElementScrolling helper must know about these updated metrics // for other functions it performs, such as double tap handling. // Note, %f must not be used because it is locale specific! nsString data; data.AppendPrintf("{ \"x\" : %d", NS_lround(newMetrics.GetScrollOffset().x)); data.AppendPrintf(", \"y\" : %d", NS_lround(newMetrics.GetScrollOffset().y)); data.AppendLiteral(", \"viewport\" : "); data.AppendLiteral("{ \"width\" : "); data.AppendFloat(newMetrics.mViewport.width); data.AppendLiteral(", \"height\" : "); data.AppendFloat(newMetrics.mViewport.height); data.AppendLiteral(" }"); data.AppendLiteral(", \"cssPageRect\" : "); data.AppendLiteral("{ \"x\" : "); data.AppendFloat(newMetrics.mScrollableRect.x); data.AppendLiteral(", \"y\" : "); data.AppendFloat(newMetrics.mScrollableRect.y); data.AppendLiteral(", \"width\" : "); data.AppendFloat(newMetrics.mScrollableRect.width); data.AppendLiteral(", \"height\" : "); data.AppendFloat(newMetrics.mScrollableRect.height); data.AppendLiteral(" }"); data.AppendPrintf(", \"resolution\" : "); // TODO: check if it's actually used? data.AppendPrintf("{ \"width\" : "); data.AppendFloat(newMetrics.CalculateIntrinsicScale().scale); data.AppendPrintf(" }"); data.AppendLiteral(", \"cssCompositedRect\" : "); data.AppendLiteral("{ \"width\" : "); data.AppendFloat(cssCompositedSize.width); data.AppendLiteral(", \"height\" : "); data.AppendFloat(cssCompositedSize.height); data.AppendLiteral(" }"); data.AppendLiteral(" }"); DispatchMessageManagerMessage(NS_LITERAL_STRING("Viewport:Change"), data); return newMetrics; } nsEventStatus TabChildBase::DispatchSynthesizedMouseEvent(uint32_t aMsg, uint64_t aTime, const LayoutDevicePoint& aRefPoint, nsIWidget* aWidget) { MOZ_ASSERT(aMsg == NS_MOUSE_MOVE || aMsg == NS_MOUSE_BUTTON_DOWN || aMsg == NS_MOUSE_BUTTON_UP || aMsg == NS_MOUSE_MOZLONGTAP); WidgetMouseEvent event(true, aMsg, nullptr, WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); event.refPoint = LayoutDeviceIntPoint(aRefPoint.x, aRefPoint.y); event.time = aTime; event.button = WidgetMouseEvent::eLeftButton; event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; if (aMsg != NS_MOUSE_MOVE) { event.clickCount = 1; } event.widget = aWidget; return DispatchWidgetEvent(event); } nsEventStatus TabChildBase::DispatchWidgetEvent(WidgetGUIEvent& event) { if (!event.widget) return nsEventStatus_eConsumeNoDefault; nsEventStatus status; NS_ENSURE_SUCCESS(event.widget->DispatchEvent(&event, status), nsEventStatus_eConsumeNoDefault); return status; } bool TabChildBase::IsAsyncPanZoomEnabled() { return mScrolling == ASYNC_PAN_ZOOM; } 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::CachedFileDescriptorInfo { struct PathOnlyComparatorHelper { bool Equals(const nsAutoPtr& a, const CachedFileDescriptorInfo& b) const { return a->mPath == b.mPath; } }; struct PathAndCallbackComparatorHelper { bool Equals(const nsAutoPtr& a, const CachedFileDescriptorInfo& b) const { return a->mPath == b.mPath && a->mCallback == b.mCallback; } }; public: nsString mPath; FileDescriptor mFileDescriptor; nsCOMPtr mCallback; bool mCanceled; CachedFileDescriptorInfo(const nsAString& aPath) : mPath(aPath), mCanceled(false) { } CachedFileDescriptorInfo(const nsAString& aPath, const FileDescriptor& aFileDescriptor) : mPath(aPath), mFileDescriptor(aFileDescriptor), mCanceled(false) { } CachedFileDescriptorInfo(const nsAString& aPath, nsICachedFileDescriptorListener* aCallback) : mPath(aPath), mCallback(aCallback), mCanceled(false) { } PathOnlyComparatorHelper PathOnlyComparator() const { return PathOnlyComparatorHelper(); } PathAndCallbackComparatorHelper PathAndCallbackComparator() const { return PathAndCallbackComparatorHelper(); } void FireCallback() const { mCallback->OnCachedFileDescriptor(mPath, mFileDescriptor); } }; class TabChild::CachedFileDescriptorCallbackRunnable : public nsRunnable { typedef TabChild::CachedFileDescriptorInfo CachedFileDescriptorInfo; nsAutoPtr mInfo; public: CachedFileDescriptorCallbackRunnable(CachedFileDescriptorInfo* aInfo) : mInfo(aInfo) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aInfo); MOZ_ASSERT(!aInfo->mPath.IsEmpty()); MOZ_ASSERT(aInfo->mCallback); } void Dispatch() { MOZ_ASSERT(NS_IsMainThread()); nsresult rv = NS_DispatchToCurrentThread(this); NS_ENSURE_SUCCESS_VOID(rv); } private: NS_IMETHOD Run() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mInfo); mInfo->FireCallback(); return NS_OK; } }; StaticRefPtr sPreallocatedTab; /*static*/ void TabChild::PreloadSlowThings() { MOZ_ASSERT(!sPreallocatedTab); nsRefPtr tab(new TabChild(ContentChild::GetSingleton(), 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); nsCOMPtr docShell = do_GetInterface(tab->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(); } sPreallocatedTab = tab; ClearOnShutdown(&sPreallocatedTab); } /*static*/ already_AddRefed TabChild::Create(ContentChild* aManager, const TabContext &aContext, uint32_t aChromeFlags) { if (sPreallocatedTab && sPreallocatedTab->mChromeFlags == aChromeFlags && aContext.IsBrowserOrApp()) { nsRefPtr child = sPreallocatedTab.get(); sPreallocatedTab = nullptr; MOZ_ASSERT(!child->mTriedBrowserInit); child->SetTabContext(aContext); child->NotifyTabContextUpdated(); return child.forget(); } nsRefPtr iframe = new TabChild(aManager, aContext, aChromeFlags); return NS_SUCCEEDED(iframe->Init()) ? iframe.forget() : nullptr; } TabChild::TabChild(ContentChild* aManager, const TabContext& aContext, uint32_t aChromeFlags) : TabContext(aContext) , mRemoteFrame(nullptr) , mManager(aManager) , mChromeFlags(aChromeFlags) , mLayersId(0) , mOuterRect(0, 0, 0, 0) , mActivePointerId(-1) , mTapHoldTimer(nullptr) , mAppPackageFileDescriptorRecved(false) , mLastBackgroundColor(NS_RGB(255, 255, 255)) , mDidFakeShow(false) , mNotified(false) , mTriedBrowserInit(false) , mOrientation(eScreenOrientation_PortraitPrimary) , mUpdateHitRegion(false) , mContextMenuHandled(false) , mLongTapEventHandled(false) , mWaitingTouchListeners(false) , mIgnoreKeyPressEvent(false) , mActiveElementManager(new ActiveElementManager()) , mHasValidInnerSize(false) { if (!sActiveDurationMsSet) { Preferences::AddIntVarCache(&sActiveDurationMs, "ui.touch_activation.duration_ms", sActiveDurationMs); sActiveDurationMsSet = true; } } NS_IMETHODIMP TabChild::HandleEvent(nsIDOMEvent* aEvent) { nsAutoString eventType; aEvent->GetType(eventType); if (eventType.EqualsLiteral("DOMMetaAdded")) { // This meta data may or may not have been a meta viewport tag. If it was, // we should handle it immediately. HandlePossibleViewportChange(); } return NS_OK; } NS_IMETHODIMP TabChild::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) { if (!strcmp(aTopic, BROWSER_ZOOM_TO_RECT)) { nsCOMPtr docShell(do_QueryInterface(aSubject)); nsCOMPtr tabChild(TabChild::GetFrom(docShell)); if (tabChild == this) { nsCOMPtr doc(GetDocument()); uint32_t presShellId; ViewID viewId; if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(doc->GetDocumentElement(), &presShellId, &viewId)) { CSSRect rect; sscanf(NS_ConvertUTF16toUTF8(aData).get(), "{\"x\":%f,\"y\":%f,\"w\":%f,\"h\":%f}", &rect.x, &rect.y, &rect.width, &rect.height); SendZoomToRect(presShellId, viewId, rect); } } } else if (!strcmp(aTopic, BEFORE_FIRST_PAINT)) { if (IsAsyncPanZoomEnabled()) { nsCOMPtr subject(do_QueryInterface(aSubject)); nsCOMPtr doc(GetDocument()); if (SameCOMIdentity(subject, doc)) { nsCOMPtr utils(GetDOMWindowUtils()); utils->SetIsFirstPaint(true); mContentDocumentIsDisplayed = true; // Reset CSS viewport and zoom to default on new page, then // calculate them properly using the actual metadata from the // page. SetCSSViewport(kDefaultViewportSize); // In some cases before-first-paint gets called before // RecvUpdateDimensions is called and therefore before we have an // mInnerSize value set. In such cases defer initializing the viewport // until we we get an inner size. if (HasValidInnerSize()) { InitializeRootMetrics(); utils->SetResolution(mLastRootMetrics.mResolution.scale, mLastRootMetrics.mResolution.scale); HandlePossibleViewportChange(); } } } } return NS_OK; } NS_IMETHODIMP TabChild::OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t aStateFlags, nsresult aStatus) { NS_NOTREACHED("not implemented in TabChild"); return NS_OK; } NS_IMETHODIMP TabChild::OnProgressChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, int32_t aCurSelfProgress, int32_t aMaxSelfProgress, int32_t aCurTotalProgress, int32_t aMaxTotalProgress) { NS_NOTREACHED("not implemented in TabChild"); return NS_OK; } NS_IMETHODIMP TabChild::OnLocationChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, nsIURI *aLocation, uint32_t aFlags) { if (!IsAsyncPanZoomEnabled()) { return NS_OK; } nsCOMPtr window; aWebProgress->GetDOMWindow(getter_AddRefs(window)); if (!window) { return NS_OK; } nsCOMPtr progressDoc; window->GetDocument(getter_AddRefs(progressDoc)); if (!progressDoc) { return NS_OK; } nsCOMPtr domDoc; WebNavigation()->GetDocument(getter_AddRefs(domDoc)); if (!domDoc || !SameCOMIdentity(domDoc, progressDoc)) { return NS_OK; } nsCOMPtr urifixup(do_GetService(NS_URIFIXUP_CONTRACTID)); if (!urifixup) { return NS_OK; } nsCOMPtr exposableURI; urifixup->CreateExposableURI(aLocation, getter_AddRefs(exposableURI)); if (!exposableURI) { return NS_OK; } if (!(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT)) { mContentDocumentIsDisplayed = false; } else if (mLastURI != nullptr) { bool exposableEqualsLast, exposableEqualsNew; exposableURI->Equals(mLastURI.get(), &exposableEqualsLast); exposableURI->Equals(aLocation, &exposableEqualsNew); if (exposableEqualsLast && !exposableEqualsNew) { mContentDocumentIsDisplayed = false; } } return NS_OK; } NS_IMETHODIMP TabChild::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, nsresult aStatus, const char16_t* aMessage) { NS_NOTREACHED("not implemented in TabChild"); return NS_OK; } NS_IMETHODIMP TabChild::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t aState) { NS_NOTREACHED("not implemented in TabChild"); return NS_OK; } bool TabChild::DoUpdateZoomConstraints(const uint32_t& aPresShellId, const ViewID& aViewId, const bool& aIsRoot, const ZoomConstraints& aConstraints) { return SendUpdateZoomConstraints(aPresShellId, aViewId, aIsRoot, aConstraints); } nsresult TabChild::Init() { nsCOMPtr webBrowser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID); if (!webBrowser) { NS_ERROR("Couldn't create a nsWebBrowser?"); return NS_ERROR_FAILURE; } webBrowser->SetContainerWindow(this); mWebNav = do_QueryInterface(webBrowser); NS_ASSERTION(mWebNav, "nsWebBrowser doesn't implement nsIWebNavigation?"); nsCOMPtr docShellItem(do_QueryInterface(WebNavigation())); docShellItem->SetItemType(nsIDocShellTreeItem::typeContentWrapper); nsCOMPtr baseWindow = do_QueryInterface(WebNavigation()); if (!baseWindow) { NS_ERROR("mWebNav doesn't QI to nsIBaseWindow"); return NS_ERROR_FAILURE; } mWidget = nsIWidget::CreatePuppetWidget(this); if (!mWidget) { NS_ERROR("couldn't create fake widget"); return NS_ERROR_FAILURE; } mWidget->Create( nullptr, 0, // no parents nsIntRect(nsIntPoint(0, 0), nsIntSize(0, 0)), nullptr, // HandleWidgetEvent nullptr // nsDeviceContext ); baseWindow->InitWindow(0, mWidget, 0, 0, 0, 0); baseWindow->Create(); NotifyTabContextUpdated(); // 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 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 docShell = do_GetInterface(WebNavigation()); MOZ_ASSERT(docShell); docShell->SetAffectPrivateSessionLifetime( mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME); nsCOMPtr loadContext = do_GetInterface(WebNavigation()); MOZ_ASSERT(loadContext); loadContext->SetPrivateBrowsing( mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW); loadContext->SetRemoteTabs( mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW); nsCOMPtr webProgress = do_GetInterface(docShell); NS_ENSURE_TRUE(webProgress, NS_ERROR_FAILURE); webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_LOCATION); // 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 window = do_GetInterface(WebNavigation()); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); nsCOMPtr chromeHandler = do_QueryInterface(window->GetChromeEventHandler()); docShell->SetChromeEventHandler(chromeHandler); return NS_OK; } void TabChild::NotifyTabContextUpdated() { nsCOMPtr docShell = do_GetInterface(WebNavigation()); MOZ_ASSERT(docShell); if (docShell) { // nsDocShell will do the right thing if we pass NO_APP_ID or // UNKNOWN_APP_ID for aOwnOrContainingAppId. if (IsBrowserElement()) { docShell->SetIsBrowserInsideApp(BrowserOwnerAppId()); } else { docShell->SetIsApp(OwnAppId()); } } } 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(nsIDOMEventListener) NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) 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(nsDependentString(aStatus)) : EmptyString(), nullptr); } NS_IMETHODIMP TabChild::GetWebBrowser(nsIWebBrowser** aWebBrowser) { NS_NOTREACHED("TabChild::GetWebBrowser not supported in TabChild"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP TabChild::SetWebBrowser(nsIWebBrowser* aWebBrowser) { NS_NOTREACHED("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_NOTREACHED("trying to SetChromeFlags from content process?"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP TabChild::DestroyBrowserWindow() { NS_NOTREACHED("TabChild::DestroyBrowserWindow not supported in TabChild"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP TabChild::SizeBrowserTo(int32_t aCX, int32_t aCY) { NS_NOTREACHED("TabChild::SizeBrowserTo not supported in TabChild"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP TabChild::ShowAsModal() { NS_NOTREACHED("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_NOTREACHED("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) { NS_NOTREACHED("TabChild::SetDimensions not supported in TabChild"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP TabChild::GetDimensions(uint32_t aFlags, int32_t* aX, int32_t* aY, int32_t* aCx, int32_t* aCy) { if (aX) { *aX = mOuterRect.x; } if (aY) { *aY = mOuterRect.y; } if (aCx) { *aCx = mOuterRect.width; } if (aCy) { *aCy = mOuterRect.height; } return NS_OK; } NS_IMETHODIMP TabChild::SetFocus() { NS_WARNING("TabChild::SetFocus not supported in TabChild"); 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_NOTREACHED("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_NOTREACHED("TabChild::GetSiteWindow not supported in TabChild"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP TabChild::Blur() { NS_WARNING("TabChild::Blur not supported in TabChild"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP TabChild::FocusNextElement() { SendMoveFocus(true); return NS_OK; } NS_IMETHODIMP TabChild::FocusPrevElement() { SendMoveFocus(false); return NS_OK; } NS_IMETHODIMP TabChild::GetInterface(const nsIID & aIID, void **aSink) { // XXXbz should we restrict the set of interfaces we hand out here? // See bug 537429 return QueryInterface(aIID, aSink); } NS_IMETHODIMP TabChild::ProvideWindow(nsIDOMWindow* aParent, uint32_t aChromeFlags, bool aCalledFromJS, bool aPositionSpecified, bool aSizeSpecified, nsIURI* aURI, const nsAString& aName, const nsACString& aFeatures, bool* aWindowIsNew, nsIDOMWindow** aReturn) { *aReturn = nullptr; // If aParent is inside an