Bug 610670 - Reuse a single puppet widget. r=bz,cjones a=blocking-fennec
authorAlon Zakai <azakai@mozilla.com>
Wed, 09 Feb 2011 12:13:18 -0800
changeset 62248 e71960b6957c068088d74ee3cd0f5e16a38f6eb3
parent 62247 128423281c995525d1e88ff458544b70446b6309
child 62249 000682b4204a61fd6599c1d450c0f2c4a41e250d
push id18660
push userdholbert@mozilla.com
push dateWed, 09 Feb 2011 20:13:43 +0000
treeherdermozilla-central@e71960b6957c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, cjones, blocking-fennec
bugs610670
milestone2.0b12pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 610670 - Reuse a single puppet widget. r=bz,cjones a=blocking-fennec
layout/base/nsDocumentViewer.cpp
view/src/nsView.cpp
widget/public/nsIWidget.h
widget/src/xpwidgets/PuppetWidget.cpp
widget/src/xpwidgets/nsBaseWidget.cpp
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -409,16 +409,21 @@ private:
 #ifdef NS_PRINTING
   // Called when the DocViewer is notified that the state
   // of Printing or PP has changed
   void SetIsPrintingInDocShellTree(nsIDocShellTreeNode* aParentNode, 
                                    PRBool               aIsPrintingOrPP, 
                                    PRBool               aStartAtTop);
 #endif // NS_PRINTING
 
+  // Whether we should attach to the top level widget. This is true if we
+  // are sharing/recycling a single base widget and not creating multiple
+  // child widgets.
+  PRBool ShouldAttachToTopLevel();
+
 protected:
   // These return the current shell/prescontext etc.
   nsIPresShell* GetPresShell();
   nsPresContext* GetPresContext();
   nsIViewManager* GetViewManager();
 
   void DetachFromTopLevelWidget();
 
@@ -1387,16 +1392,42 @@ DocumentViewerImpl::Open(nsISupports *aS
   if (mFocusListener && mDocument) {
     mDocument->AddEventListenerByIID(mFocusListener,
                                      NS_GET_IID(nsIDOMFocusListener));
   }
 
   // XXX re-enable image animations once that works correctly
 
   PrepareToStartLoad();
+
+  // When loading a page from the bfcache with puppet widgets, we do the
+  // widget attachment here (it is otherwise done in MakeWindow, which is
+  // called for non-bfcache pages in the history, but not bfcache pages).
+  // Attachment is necessary, since we get detached when another page
+  // is browsed to. That is, if we are one page A, then when we go to
+  // page B, we detach. So page A's view has no widget. If we then go
+  // back to it, and it is in the bfcache, we will use that view, which
+  // doesn't have a widget. The attach call here will properly attach us.
+  if (nsIWidget::UsePuppetWidgets() && mPresContext &&
+      ShouldAttachToTopLevel()) {
+    // If the old view is already attached to our parent, detach
+    DetachFromTopLevelWidget();
+
+    nsIViewManager *vm = GetViewManager();
+    NS_ABORT_IF_FALSE(vm, "no view manager");
+    nsIView *v;
+    nsresult rv = vm->GetRootView(v);
+    NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "failed in getting the root view");
+    NS_ABORT_IF_FALSE(v, "no root view");
+    NS_ABORT_IF_FALSE(mParentWidget, "no mParentWidget to set");
+    v->AttachToTopLevelWidget(mParentWidget);
+
+    mAttachedToParent = PR_TRUE;
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::Close(nsISHEntry *aSHEntry)
 {
   // All callers are supposed to call close to break circular
   // references.  If we do this stuff in the destructor, the
@@ -2237,40 +2268,22 @@ DocumentViewerImpl::ClearHistoryEntry()
 //-------------------------------------------------------
 
 nsresult
 DocumentViewerImpl::MakeWindow(const nsSize& aSize, nsIView* aContainerView)
 {
   if (GetIsPrintPreview())
     return NS_OK;
 
-  // Prior to creating a new widget, check to see if our parent is a base
-  // chrome ui window. If so, drop the content into that widget instead of
-  // creating a new child widget. This eliminates the main content child
-  // widget we've had forever. Also allows for the recycling of the base
-  // widget of each window, vs. creating/discarding child widgets for each
-  // MakeWindow call. (Currently only implemented in windows widgets.)
-#ifdef XP_WIN
-  nsCOMPtr<nsIDocShellTreeItem> containerItem = do_QueryReferent(mContainer);
-  if (mParentWidget && containerItem) {
-    PRInt32 docType;
-    nsWindowType winType;
-    containerItem->GetItemType(&docType);
-    mParentWidget->GetWindowType(winType);
-    if ((winType == eWindowType_toplevel ||
-         winType == eWindowType_dialog ||
-         winType == eWindowType_invisible) &&
-        docType == nsIDocShellTreeItem::typeChrome) {
-      // If the old view is already attached to our parent, detach
-      DetachFromTopLevelWidget();
-      // Use the parent widget
-      mAttachedToParent = PR_TRUE;
-    }
+  PRBool shouldAttach = ShouldAttachToTopLevel();
+
+  if (shouldAttach) {
+    // If the old view is already attached to our parent, detach
+    DetachFromTopLevelWidget();
   }
-#endif
 
   nsresult rv;
   mViewManager = do_CreateInstance(kViewManagerCID, &rv);
   if (NS_FAILED(rv))
     return rv;
 
   nsIDeviceContext *dx = mPresContext->DeviceContext();
 
@@ -2299,19 +2312,20 @@ DocumentViewerImpl::MakeWindow(const nsS
     nsWidgetInitData* initDataPtr;
     if (!mParentWidget) {
       initDataPtr = &initData;
       initData.mWindowType = eWindowType_invisible;
     } else {
       initDataPtr = nsnull;
     }
 
-    if (mAttachedToParent) {
+    if (shouldAttach) {
       // Reuse the top level parent widget.
       rv = view->AttachToTopLevelWidget(mParentWidget);
+      mAttachedToParent = PR_TRUE;
     }
     else if (!aContainerView && mParentWidget) {
       rv = view->CreateWidgetForParent(mParentWidget, initDataPtr,
                                        PR_TRUE, PR_FALSE);
     }
     else {
       rv = view->CreateWidget(initDataPtr, PR_TRUE, PR_FALSE);
     }
@@ -4024,16 +4038,47 @@ DocumentViewerImpl::SetIsPrintingInDocSh
     if (childAsNode) {
       SetIsPrintingInDocShellTree(childAsNode, aIsPrintingOrPP, PR_FALSE);
     }
   }
 
 }
 #endif // NS_PRINTING
 
+PRBool
+DocumentViewerImpl::ShouldAttachToTopLevel()
+{
+  if (!mParentWidget)
+    return PR_FALSE;
+
+  nsCOMPtr<nsIDocShellTreeItem> containerItem = do_QueryReferent(mContainer);
+  if (!containerItem)
+    return PR_FALSE;
+
+  // We always attach when using puppet widgets
+  if (nsIWidget::UsePuppetWidgets())
+    return PR_TRUE;
+
+#ifdef XP_WIN
+  // On windows, in the parent process we also attach, but just to
+  // chrome items
+  PRInt32 docType;
+  nsWindowType winType;
+  containerItem->GetItemType(&docType);
+  mParentWidget->GetWindowType(winType);
+  if ((winType == eWindowType_toplevel ||
+       winType == eWindowType_dialog ||
+       winType == eWindowType_invisible) &&
+      docType == nsIDocShellTreeItem::typeChrome)
+    return PR_TRUE;
+#endif
+
+  return PR_FALSE;
+}
+
 //------------------------------------------------------------
 // XXX this always returns PR_FALSE for subdocuments
 PRBool
 DocumentViewerImpl::GetIsPrinting()
 {
 #ifdef NS_PRINTING
   if (mPrintEngine) {
     return mPrintEngine->GetIsPrinting();
--- a/view/src/nsView.cpp
+++ b/view/src/nsView.cpp
@@ -849,17 +849,18 @@ nsresult nsIView::AttachToTopLevelWidget
     oldView->DetachFromTopLevelWidget();
   }
 
   nsCOMPtr<nsIDeviceContext> dx;
   mViewManager->GetDeviceContext(*getter_AddRefs(dx));
 
   // Note, the previous device context will be released. Detaching
   // will not restore the old one.
-  nsresult rv = aWidget->AttachViewToTopLevel(::AttachedHandleEvent, dx);
+  nsresult rv = aWidget->AttachViewToTopLevel(
+    nsIWidget::UsePuppetWidgets() ? ::HandleEvent : ::AttachedHandleEvent, dx);
   if (NS_FAILED(rv))
     return rv;
 
   mWindow = aWidget;
   NS_ADDREF(mWindow);
 
   ViewWrapper* wrapper = new ViewWrapper(Impl());
   NS_ADDREF(wrapper);
--- a/widget/public/nsIWidget.h
+++ b/widget/public/nsIWidget.h
@@ -1325,27 +1325,33 @@ class nsIWidget : public nsISupports {
      *                         Otherwise, it's vertical.
      * @param aOverriddenDelta The overridden mouse scrolling speed.  This value
      *                         may be same as aOriginalDelta.
      */
     NS_IMETHOD OverrideSystemMouseScrollSpeed(PRInt32 aOriginalDelta,
                                               PRBool aIsHorizontal,
                                               PRInt32 &aOverriddenDelta) = 0;
 
-#ifdef MOZ_IPC
     /**
      * Return true if this process shouldn't use platform widgets, and
      * so should use PuppetWidgets instead.  If this returns true, the
      * result of creating and using a platform widget is undefined,
      * and likely to end in crashes or other buggy behavior.
      */
     static bool
     UsePuppetWidgets()
-    { return XRE_GetProcessType() == GeckoProcessType_Content; }
+    {
+#ifdef MOZ_IPC
+      return XRE_GetProcessType() == GeckoProcessType_Content;
+#else
+      return PR_FALSE;
+#endif
+    }
 
+#ifdef MOZ_IPC
     /**
      * Allocate and return a "puppet widget" that doesn't directly
      * correlate to a platform widget; platform events and data must
      * be fed to it.  Currently used in content processes.  NULL is
      * returned if puppet widgets aren't supported in this build
      * config, on this platform, or for this process type.
      *
      * This function is called "Create" to match CreateInstance().
--- a/widget/src/xpwidgets/PuppetWidget.cpp
+++ b/widget/src/xpwidgets/PuppetWidget.cpp
@@ -271,46 +271,47 @@ PuppetWidget::InitEvent(nsGUIEvent& even
 NS_IMETHODIMP
 PuppetWidget::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
 {
 #ifdef DEBUG
   debug_DumpEvent(stdout, event->widget, event,
                   nsCAutoString("PuppetWidget"), nsnull);
 #endif
 
+  NS_ABORT_IF_FALSE(!mChild || mChild->mWindowType == eWindowType_popup,
+                    "Unexpected event dispatch!");
+
   aStatus = nsEventStatus_eIgnore;
-  if (mEventCallback) {
-    if (event->message == NS_COMPOSITION_START) {
-      mIMEComposing = PR_TRUE;
-    }
-    switch (event->eventStructType) {
-    case NS_COMPOSITION_EVENT:
-      mIMELastReceivedSeqno = static_cast<nsCompositionEvent*>(event)->seqno;
-      if (mIMELastReceivedSeqno < mIMELastBlurSeqno)
-        return NS_OK;
-      break;
-    case NS_TEXT_EVENT:
-      mIMELastReceivedSeqno = static_cast<nsTextEvent*>(event)->seqno;
-      if (mIMELastReceivedSeqno < mIMELastBlurSeqno)
-        return NS_OK;
-      break;
-    case NS_SELECTION_EVENT:
-      mIMELastReceivedSeqno = static_cast<nsSelectionEvent*>(event)->seqno;
-      if (mIMELastReceivedSeqno < mIMELastBlurSeqno)
-        return NS_OK;
-      break;
-    }
-    aStatus = (*mEventCallback)(event);
+
+  NS_ABORT_IF_FALSE(mViewCallback, "No view callback!");
 
-    if (event->message == NS_COMPOSITION_END) {
-      mIMEComposing = PR_FALSE;
-    }
-  } else if (mChild) {
-    event->widget = mChild;
-    mChild->DispatchEvent(event, aStatus);
+  if (event->message == NS_COMPOSITION_START) {
+    mIMEComposing = PR_TRUE;
+  }
+  switch (event->eventStructType) {
+  case NS_COMPOSITION_EVENT:
+    mIMELastReceivedSeqno = static_cast<nsCompositionEvent*>(event)->seqno;
+    if (mIMELastReceivedSeqno < mIMELastBlurSeqno)
+      return NS_OK;
+    break;
+  case NS_TEXT_EVENT:
+    mIMELastReceivedSeqno = static_cast<nsTextEvent*>(event)->seqno;
+    if (mIMELastReceivedSeqno < mIMELastBlurSeqno)
+      return NS_OK;
+    break;
+  case NS_SELECTION_EVENT:
+    mIMELastReceivedSeqno = static_cast<nsSelectionEvent*>(event)->seqno;
+    if (mIMELastReceivedSeqno < mIMELastBlurSeqno)
+      return NS_OK;
+    break;
+  }
+  aStatus = (*mViewCallback)(event);
+
+  if (event->message == NS_COMPOSITION_END) {
+    mIMEComposing = PR_FALSE;
   }
 
   return NS_OK;
 }
 
 LayerManager*
 PuppetWidget::GetLayerManager(LayerManagerPersistence, bool* aAllowRetaining)
 {
--- a/widget/src/xpwidgets/nsBaseWidget.cpp
+++ b/widget/src/xpwidgets/nsBaseWidget.cpp
@@ -296,18 +296,19 @@ nsBaseWidget::CreateChild(const nsIntRec
 
 // Attach a view to our widget which we'll send events to. 
 NS_IMETHODIMP
 nsBaseWidget::AttachViewToTopLevel(EVENT_CALLBACK aViewEventFunction,
                                    nsIDeviceContext *aContext)
 {
   NS_ASSERTION((mWindowType == eWindowType_toplevel ||
                 mWindowType == eWindowType_dialog ||
-                mWindowType == eWindowType_invisible),
-               "Can't attach to child?");
+                mWindowType == eWindowType_invisible ||
+                mWindowType == eWindowType_child),
+               "Can't attach to window of that type");
 
   mViewCallback = aViewEventFunction;
 
   if (aContext) {
     if (mContext) {
       NS_IF_RELEASE(mContext);
     }
     mContext = aContext;