diff -r 000000000000 -r 6474c204b198 dom/ipc/TabChild.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/ipc/TabChild.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,2811 @@ +/* -*- 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() + : mOldViewportWidth(0.0f) + , 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)); +} + +bool +TabChildBase::HasValidInnerSize() +{ + return (mInnerSize.width != 0) && (mInnerSize.height != 0); +} + +void +TabChildBase::SetCSSViewport(const CSSSize& aSize) +{ + mOldViewportWidth = aSize.width; + + 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::GetScrollIdentifiers( + 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; + } + + float oldBrowserWidth = mOldViewportWidth; + mLastRootMetrics.mViewport.SizeTo(viewport); + if (!oldBrowserWidth) { + oldBrowserWidth = kDefaultViewportSize.width; + } + 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; + } + + float oldScreenWidth = mLastRootMetrics.mCompositionBounds.width; + if (!oldScreenWidth) { + oldScreenWidth = mInnerSize.width; + } + + 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 = oldScreenWidth / oldBrowserWidth; + 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); + + 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) + , mWaitingTouchListeners(false) + , mIgnoreKeyPressEvent(false) + , mActiveElementManager(new ActiveElementManager()) +{ + 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::GetScrollIdentifiers(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