Bug 352093. Part 12: Teach nsDocShell/nsDocumentViewer to deal with not having a widget
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 22 Jul 2009 12:45:10 +1200
changeset 30537 9b8e2261d5fac7452bc770dea1187303ca89d949
parent 30536 9bc2931f032bfd12c9255ad21a9182035df5a68e
child 30538 b01f80d5166a24dbb926897911cc1dfc669f7252
push idunknown
push userunknown
push dateunknown
bugs352093
milestone1.9.2a1pre
Bug 352093. Part 12: Teach nsDocShell/nsDocumentViewer to deal with not having a widget
docshell/base/nsDocShell.cpp
layout/base/nsDocumentViewer.cpp
layout/generic/nsFrameFrame.cpp
widget/public/nsIBaseWindow.idl
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4136,18 +4136,16 @@ nsDocShell::GetCurrentDescriptor(nsISupp
 // nsDocShell::nsIBaseWindow
 //*****************************************************************************   
 
 NS_IMETHODIMP
 nsDocShell::InitWindow(nativeWindow parentNativeWindow,
                        nsIWidget * parentWidget, PRInt32 x, PRInt32 y,
                        PRInt32 cx, PRInt32 cy)
 {
-    NS_ENSURE_ARG(parentWidget);        // DocShells must get a widget for a parent
-
     SetParentWidget(parentWidget);
     SetPositionAndSize(x, y, cx, cy, PR_FALSE);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::Create()
@@ -6336,32 +6334,21 @@ nsDocShell::CaptureState()
     rv = mOSHE->SetWindowState(windowState);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Suspend refresh URIs and save off the timer queue
     rv = mOSHE->SetRefreshURIList(mSavedRefreshURIList);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Capture the current content viewer bounds.
-    nsCOMPtr<nsIPresShell> shell;
-    nsDocShell::GetPresShell(getter_AddRefs(shell));
-    if (shell) {
-        nsIViewManager *vm = shell->GetViewManager();
-        if (vm) {
-            nsIView *rootView = nsnull;
-            vm->GetRootView(rootView);
-            if (rootView) {
-                nsIWidget *widget = rootView->GetWidget();
-                if (widget) {
-                    nsIntRect bounds(0, 0, 0, 0);
-                    widget->GetBounds(bounds);
-                    rv = mOSHE->SetViewerBounds(bounds);
-                }
-            }
-        }
+    if (mContentViewer) {
+        nsIntRect bounds;
+        mContentViewer->GetBounds(bounds);
+        rv = mOSHE->SetViewerBounds(bounds);
+        NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // Capture the docshell hierarchy.
     mOSHE->ClearChildShells();
 
     PRInt32 childCount = mChildList.Count();
     for (PRInt32 i = 0; i < childCount; ++i) {
         nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
@@ -6685,20 +6672,17 @@ nsDocShell::RestoreFromHistory()
         if (vm) {
             nsIView *oldRootView = nsnull;
             vm->GetRootView(oldRootView);
 
             if (oldRootView) {
                 rootViewSibling = oldRootView->GetNextSibling();
                 rootViewParent = oldRootView->GetParent();
 
-                nsIWidget *widget = oldRootView->GetWidget();
-                if (widget) {
-                    widget->GetBounds(newBounds);
-                }
+                mContentViewer->GetBounds(newBounds);
             }
         }
     }
 
     // Transfer ownership to mContentViewer.  By ensuring that either the
     // docshell or the session history, but not both, have references to the
     // content viewer, we prevent the viewer from being torn down after
     // Destroy() is called.
@@ -6933,25 +6917,22 @@ nsDocShell::RestoreFromHistory()
     // presentation to that smaller size.  However, firing the locationchanged
     // event will hide the infobar, which will immediately resize the window
     // back to the larger size.  A future optimization might be to restore
     // the presentation at the "wrong" size, then fire the locationchanged
     // event and check whether the docshell's new size is the same as the
     // cached viewer size (skipping the resize if they are equal).
 
     if (newRootView) {
-        nsIWidget *widget = newRootView->GetWidget();
-        if (widget && !newBounds.IsEmpty() && newBounds != oldBounds) {
+        if (!newBounds.IsEmpty() && newBounds != oldBounds) {
 #ifdef DEBUG_PAGE_CACHE
             printf("resize widget(%d, %d, %d, %d)\n", newBounds.x,
                    newBounds.y, newBounds.width, newBounds.height);
 #endif
-
-            widget->Resize(newBounds.x, newBounds.y, newBounds.width,
-                           newBounds.height, PR_FALSE);
+            mContentViewer->SetBounds(newBounds);
         }
     }
 
     // Simulate the completion of the load.
     nsDocShell::FinishRestore();
 
     // Restart plugins, and paint the content.
     if (shell)
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -358,27 +358,31 @@ private:
    * mViewManager and mWindow.
    * @param aSize the initial size in appunits
    * @param aContainerView the container view to hook our root view up
    * to as a child, or null if this will be the root view manager
    */
   nsresult MakeWindow(const nsSize& aSize, nsIView* aContainerView);
 
   /**
-   * Find the view to use as the container view for MakeWindow.
+   * Find the view to use as the container view for MakeWindow. Returns
+   * null if this will be the root of a view manager hierarchy. In that
+   * case, if mParentWidget is null then this document should not even
+   * be displayed.
    */
   nsIView* FindContainerView();
 
   /**
    * Create our device context
    */
-  nsresult CreateDeviceContext(nsIWidget* aWidget);
+  nsresult CreateDeviceContext(nsIView* aContainerView);
 
   /**
-   * Make sure to set up mDeviceContext as needed before calling this
+   * If aDoCreation is true, this creates the device context, creates a
+   * prescontext if necessary, and calls MakeWindow.
    */
   nsresult InitInternal(nsIWidget* aParentWidget,
                         nsISupports *aState,
                         const nsIntRect& aBounds,
                         PRBool aDoCreation,
                         PRBool aInPrintPreview,
                         PRBool aNeedMakeCX = PR_TRUE);
   /**
@@ -426,29 +430,31 @@ protected:
   // class, please make the ownership explicit (pinkerton, scc).
 
   nsWeakPtr mContainer; // it owns me!
   nsCOMPtr<nsIDeviceContext> mDeviceContext;  // We create and own this baby
 
   // the following six items are explicitly in this order
   // so they will be destroyed in the reverse order (pinkerton, scc)
   nsCOMPtr<nsIDocument>    mDocument;
-  nsCOMPtr<nsIWidget>      mWindow;      // ??? should we really own it?
+  nsCOMPtr<nsIWidget>      mWindow;      // may be null
   nsCOMPtr<nsIViewManager> mViewManager;
   nsCOMPtr<nsPresContext> mPresContext;
   nsCOMPtr<nsIPresShell>   mPresShell;
 
   nsCOMPtr<nsISelectionListener> mSelectionListener;
   nsCOMPtr<nsIDOMFocusListener> mFocusListener;
 
   nsCOMPtr<nsIContentViewer> mPreviousViewer;
   nsCOMPtr<nsISHEntry> mSHEntry;
 
   nsIWidget* mParentWidget; // purposely won't be ref counted.  May be null
 
+  nsIntRect mBounds;
+
   // mTextZoom/mPageZoom record the textzoom/pagezoom of the first (galley)
   // presshell only.
   float mTextZoom;      // Text zoom, defaults to 1.0
   float mPageZoom;
 
   PRInt16 mNumURLStarts;
   PRInt16 mDestroyRefCount;    // a second "refcount" for the document viewer's "destroy"
 
@@ -682,19 +688,16 @@ DocumentViewerImpl::GetContainer(nsISupp
    container.swap(*aResult);
    return NS_OK;
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::Init(nsIWidget* aParentWidget,
                          const nsIntRect& aBounds)
 {
-  nsresult rv = CreateDeviceContext(aParentWidget);
-  NS_ENSURE_SUCCESS(rv, rv);
-  
   return InitInternal(aParentWidget, nsnull, aBounds, PR_TRUE, PR_FALSE);
 }
 
 nsresult
 DocumentViewerImpl::InitPresentationStuff(PRBool aDoInitialReflow, PRBool aReenableRefresh)
 {
   NS_ASSERTION(!mPresShell,
                "Someone should have destroyed the presshell!");
@@ -726,21 +729,18 @@ DocumentViewerImpl::InitPresentationStuf
     // to avoid bogus notifications.
 
     mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
   }
 
   mPresShell->BeginObservingDocument();
 
   // Initialize our view manager
-  nsIntRect bounds;
-  mWindow->GetBounds(bounds);
-
-  nscoord width = mPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel() * bounds.width;
-  nscoord height = mPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel() * bounds.height;
+  nscoord width = mPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel() * mBounds.width;
+  nscoord height = mPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel() * mBounds.height;
 
   mViewManager->DisableRefresh();
   mViewManager->SetWindowDimensions(width, height);
   mPresContext->SetTextZoom(mTextZoom);
   mPresContext->SetFullZoom(mPageZoom);
 
   if (aDoInitialReflow) {
     nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
@@ -834,29 +834,34 @@ nsresult
 DocumentViewerImpl::InitInternal(nsIWidget* aParentWidget,
                                  nsISupports *aState,
                                  const nsIntRect& aBounds,
                                  PRBool aDoCreation,
                                  PRBool aInPrintPreview,
                                  PRBool aNeedMakeCX /*= PR_TRUE*/)
 {
   mParentWidget = aParentWidget; // not ref counted
+  mBounds = aBounds;
 
   nsresult rv = NS_OK;
   NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
 
   nsIView* containerView = FindContainerView();
 
   PRBool makeCX = PR_FALSE;
   if (aDoCreation) {
+    nsresult rv = CreateDeviceContext(containerView);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     // XXXbz this is a nasty hack to do with the fact that we create
     // presentations both in Init() and in Show()...  Ideally we would only do
     // it in one place (Show()) and require that callers call init(), open(),
     // show() in that order or something.
-    if ((aParentWidget || mDocument->GetDisplayDocument()) && !mPresContext) {
+    if (!mPresContext &&
+        (aParentWidget || containerView || mDocument->GetDisplayDocument())) {
       // Create presentation context
       if (mIsPageMode) {
         //Presentation context already created in SetPageMode which is calling this method
       } else {
         mPresContext = CreatePresContext(mDocument,
             nsPresContext::eContext_Galley, containerView);
       }
       NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
@@ -1293,23 +1298,20 @@ AttachContainerRecurse(nsIDocShell* aShe
   }
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::Open(nsISupports *aState, nsISHEntry *aSHEntry)
 {
   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
 
-  nsIntRect bounds;
-  mWindow->GetBounds(bounds);
-
   if (mDocument)
     mDocument->SetContainer(nsCOMPtr<nsISupports>(do_QueryReferent(mContainer)));
 
-  nsresult rv = InitInternal(mParentWidget, aState, bounds, PR_FALSE, PR_FALSE);
+  nsresult rv = InitInternal(mParentWidget, aState, mBounds, PR_FALSE, PR_FALSE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (mPresShell)
     mPresShell->SetForwardingContainer(nsnull);
 
   // Rehook the child presentations.  The child shells are still in
   // session history, so get them from there.
 
@@ -1765,23 +1767,17 @@ DocumentViewerImpl::GetPresContext(nsPre
   NS_IF_ADDREF(*aResult = pc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::GetBounds(nsIntRect& aResult)
 {
   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
-  NS_PRECONDITION(mWindow, "null window");
-  if (mWindow) {
-    mWindow->GetBounds(aResult);
-  }
-  else {
-    aResult.SetRect(0, 0, 0, 0);
-  }
+  aResult = mBounds;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::GetPreviousViewer(nsIContentViewer** aViewer)
 {
   *aViewer = mPreviousViewer;
   NS_IF_ADDREF(*aViewer);
@@ -1821,21 +1817,26 @@ DocumentViewerImpl::SetPreviousViewer(ns
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::SetBounds(const nsIntRect& aBounds)
 {
   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
 
+  mBounds = aBounds;
   if (mWindow) {
     // Don't have the widget repaint. Layout will generate repaint requests
     // during reflow
     mWindow->Resize(aBounds.x, aBounds.y, aBounds.width, aBounds.height,
                     PR_FALSE);
+  } else if (mPresContext && mViewManager) {
+    PRInt32 p2a = mPresContext->AppUnitsPerDevPixel();
+    mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(mBounds.width, p2a),
+                                      NSIntPixelsToAppUnits(mBounds.height, p2a));
   }
 
   // If there's a previous viewer, it's the one that's actually showing,
   // so be sure to resize it as well so it paints over the right area.
   // This may slow down the performance of the new page load, but resize
   // during load is also probably a relatively unusual condition
   // relating to things being hidden while something is loaded.  It so
   // happens that Firefox does this a good bit with its infobar, and it
@@ -1852,17 +1853,17 @@ DocumentViewerImpl::SetBounds(const nsIn
 #endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::Move(PRInt32 aX, PRInt32 aY)
 {
   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
-  NS_PRECONDITION(mWindow, "null window");
+  mBounds.MoveTo(aX, aY);
   if (mWindow) {
     mWindow->Move(aX, aY);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::Show(void)
@@ -1902,51 +1903,46 @@ DocumentViewerImpl::Show(void)
       }
     }
   }
 
   if (mWindow) {
     mWindow->Show(PR_TRUE);
   }
 
-  if (mDocument && !mPresShell && !mWindow) {
+  if (mDocument && !mPresShell) {
+    NS_ASSERTION(!mWindow, "Window already created but no presshell?");
+
     nsCOMPtr<nsIBaseWindow> base_win(do_QueryReferent(mContainer));
     if (base_win) {
       base_win->GetParentWidget(&mParentWidget);
-      NS_ENSURE_TRUE(mParentWidget, NS_ERROR_UNEXPECTED);
-      mParentWidget->Release(); // GetParentWidget AddRefs, but mParentWidget is weak
+      if (mParentWidget) {
+        mParentWidget->Release(); // GetParentWidget AddRefs, but mParentWidget is weak
+      }
     }
 
-    nsresult rv = CreateDeviceContext(mParentWidget);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     nsIView* containerView = FindContainerView();
 
+    nsresult rv = CreateDeviceContext(containerView);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     // Create presentation context
     NS_ASSERTION(!mPresContext, "Shouldn't have a prescontext if we have no shell!");
     mPresContext = CreatePresContext(mDocument,
         nsPresContext::eContext_Galley, containerView);
     NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
 
     rv = mPresContext->Init(mDeviceContext);
     if (NS_FAILED(rv)) {
       mPresContext = nsnull;
       return rv;
     }
 
-    nsIntRect tbounds;
-    if (mParentWidget) {
-      mParentWidget->GetBounds(tbounds);
-    } else {
-      // No good default size; just size to 0 by 0 for lack of anything better.
-      tbounds = nsIntRect(0, 0, 0, 0);
-    }
-
-    rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(tbounds.width),
-                           mPresContext->DevPixelsToAppUnits(tbounds.height)),
+    rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(mBounds.width),
+                           mPresContext->DevPixelsToAppUnits(mBounds.height)),
                            containerView);
     if (NS_FAILED(rv))
       return rv;
 
     if (mPresContext && base_win) {
       nsCOMPtr<nsILinkHandler> linkHandler(do_GetInterface(base_win));
 
       if (linkHandler) {
@@ -1972,17 +1968,16 @@ DocumentViewerImpl::Show(void)
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::Hide(void)
 {
-  NS_PRECONDITION(mWindow, "null window");
   if (mWindow) {
     mWindow->Show(PR_FALSE);
   }
 
   if (!mPresShell)
     return NS_OK;
 
   NS_ASSERTION(mPresContext, "Can't have a presshell and no prescontext!");
@@ -2256,37 +2251,41 @@ DocumentViewerImpl::MakeWindow(const nsS
 
   // The root view is always at 0,0.
   nsRect tbounds(nsPoint(0, 0), aSize);
   // Create a view
   nsIView* view = mViewManager->CreateView(tbounds, aContainerView);
   if (!view)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  // pass in a native widget to be the parent widget ONLY if the view hierarchy will stand alone.
-  // otherwise the view will find its own parent widget and "do the right thing" to
-  // establish a parent/child widget relationship
-  nsWidgetInitData initData;
-  nsWidgetInitData* initDataPtr;
-  if (!mParentWidget) {
-    initDataPtr = &initData;
-    initData.mWindowType = eWindowType_invisible;
-
-    initData.mContentType =
-      nsContentUtils::IsInChromeDocshell(mDocument) ?
-        eContentTypeUI : eContentTypeContent;
-  } else {
-    initDataPtr = nsnull;
+  // Don't create a widget if we weren't given a parent widget but we
+  // have a container view we can hook up to without a widget
+  if (mParentWidget || !aContainerView) {
+    // pass in a native widget to be the parent widget ONLY if the view hierarchy will stand alone.
+    // otherwise the view will find its own parent widget and "do the right thing" to
+    // establish a parent/child widget relationship
+    nsWidgetInitData initData;
+    nsWidgetInitData* initDataPtr;
+    if (!mParentWidget) {
+      initDataPtr = &initData;
+      initData.mWindowType = eWindowType_invisible;
+
+      initData.mContentType =
+        nsContentUtils::IsInChromeDocshell(mDocument) ?
+          eContentTypeUI : eContentTypeContent;
+    } else {
+      initDataPtr = nsnull;
+    }
+    rv = view->CreateWidget(kWidgetCID, initDataPtr,
+                            (aContainerView != nsnull || !mParentWidget) ?
+                              nsnull : mParentWidget->GetNativeData(NS_NATIVE_WIDGET),
+                            PR_TRUE, PR_FALSE);
+    if (NS_FAILED(rv))
+      return rv;
   }
-  rv = view->CreateWidget(kWidgetCID, initDataPtr,
-                          (aContainerView != nsnull || !mParentWidget) ?
-                            nsnull : mParentWidget->GetNativeData(NS_NATIVE_WIDGET),
-                          PR_TRUE, PR_FALSE);
-  if (NS_FAILED(rv))
-    return rv;
 
   // Setup hierarchical relationship in view manager
   mViewManager->SetRootView(view);
 
   mWindow = view->GetWidget();
 
   // This SetFocus is necessary so the Arrow Key and Page Key events
   // go to the scrolled view as soon as the Window is created instead of going to
@@ -2294,24 +2293,60 @@ DocumentViewerImpl::MakeWindow(const nsS
   // mWindow->SetFocus();
 
   return rv;
 }
 
 nsIView*
 DocumentViewerImpl::FindContainerView()
 {
-  // If mParentWidget has a view, we can hook our view manager up
-  // to its view tree
-  if (!mParentWidget)
-    return nsnull;
-  nsIView* containerView = nsIView::GetViewFor(mParentWidget);
+  nsIView* containerView = nsnull;
+
+  if (mParentWidget) {
+    containerView = nsIView::GetViewFor(mParentWidget);
+  } else if (mContainer) {
+    nsCOMPtr<nsIDocShellTreeItem> docShellItem = do_QueryReferent(mContainer);
+    nsCOMPtr<nsPIDOMWindow> pwin(do_GetInterface(docShellItem));
+    if (pwin) {
+      nsCOMPtr<nsIContent> content = do_QueryInterface(pwin->GetFrameElementInternal());
+      nsCOMPtr<nsIPresShell> parentPresShell;
+      if (docShellItem) {
+        nsCOMPtr<nsIDocShellTreeItem> parentDocShellItem;
+        docShellItem->GetParent(getter_AddRefs(parentDocShellItem));
+        if (parentDocShellItem) {
+          nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentDocShellItem);
+          parentDocShell->GetPresShell(getter_AddRefs(parentPresShell));
+        }
+      }
+      if (content && parentPresShell) {
+        nsIFrame* f = parentPresShell->GetRealPrimaryFrameFor(content);
+        if (f) {
+          nsIFrame* subdocFrame = f->GetContentInsertionFrame();
+          // subdocFrame might not be a subdocument frame; the frame
+          // constructor can treat a <frame> as an inline in some XBL
+          // cases. Treat that as display:none, the document is not
+          // displayed.
+          if (subdocFrame->GetType() == nsGkAtoms::subDocumentFrame) {
+            nsIView* subdocFrameView = subdocFrame->GetView();
+            NS_ASSERTION(subdocFrameView, "Subdoc frames must have views");
+            nsIView* innerView = subdocFrameView->GetFirstChild();
+            NS_ASSERTION(innerView, "Subdoc frames must have an inner view too");
+            containerView = innerView;
+          }
+        }
+      }
+    }
+  }
+
   if (!containerView)
     return nsnull;
-  if (mParentWidget->GetTransparencyMode() == eTransparencyTransparent)
+
+  nsIWidget* outerWidget = containerView->GetNearestWidget(nsnull);
+  if (outerWidget &&
+      outerWidget->GetTransparencyMode() == eTransparencyTransparent)
     return containerView;
 
   // see if the containerView has already been hooked into a foreign view manager hierarchy
   // if it has, then we have to hook into the hierarchy too otherwise bad things will happen.
   nsIViewManager* containerVM = containerView->GetViewManager();
   nsIView* pView = containerView;
   do {
     pView = pView->GetParent();
@@ -2334,42 +2369,48 @@ DocumentViewerImpl::FindContainerView()
     container->GetSameTypeParent(getter_AddRefs(sameTypeParent));
     if (sameTypeParent)
       return containerView;
   }
   return nsnull;
 }
 
 nsresult
-DocumentViewerImpl::CreateDeviceContext(nsIWidget* aWidget)
+DocumentViewerImpl::CreateDeviceContext(nsIView* aContainerView)
 {
   NS_PRECONDITION(!mPresShell && !mPresContext && !mWindow,
                   "This will screw up our existing presentation");
   NS_PRECONDITION(mDocument, "Gotta have a document here");
   
   nsIDocument* doc = mDocument->GetDisplayDocument();
   if (doc) {
-    NS_ASSERTION(!aWidget, "Shouldn't have a widget here");
-    
+    NS_ASSERTION(!aContainerView, "External resource document embedded somewhere?");
     // We want to use our display document's device context if possible
     nsIPresShell* shell = doc->GetPrimaryShell();
     if (shell) {
       nsPresContext* ctx = shell->GetPresContext();
       if (ctx) {
         mDeviceContext = ctx->DeviceContext();
         return NS_OK;
       }
     }
   }
   
   // Create a device context even if we already have one, since our widget
   // might have changed.
   mDeviceContext = do_CreateInstance(kDeviceContextCID);
   NS_ENSURE_TRUE(mDeviceContext, NS_ERROR_FAILURE);
-  mDeviceContext->Init(aWidget ? aWidget->GetTopLevelWidget() : nsnull);
+  nsIWidget* widget = nsnull;
+  if (aContainerView) {
+    widget = aContainerView->GetNearestWidget(nsnull);
+    if (widget) {
+      widget = widget->GetTopLevelWidget();
+    }
+  }
+  mDeviceContext->Init(widget);
   return NS_OK;
 }
 
 // Return the selection for the document. Note that text fields have their
 // own selection, which cannot be accessed with this method. Use
 // mPresShell->GetSelectionForCopy() instead.
 nsresult DocumentViewerImpl::GetDocumentSelection(nsISelection **aSelection)
 {
@@ -4230,19 +4271,16 @@ DocumentViewerImpl::OnDonePrinting()
 #endif // NS_PRINTING && NS_PRINT_PREVIEW
 }
 
 NS_IMETHODIMP DocumentViewerImpl::SetPageMode(PRBool aPageMode, nsIPrintSettings* aPrintSettings)
 {
   // XXX Page mode is only partially working; it's currently used for
   // reftests that require a paginated context
   mIsPageMode = aPageMode;
-  // Get the current size of what is being viewed
-  nsIntRect bounds;
-  mWindow->GetBounds(bounds);
 
   if (mPresShell) {
     DestroyPresShell();
   }
 
   if (mPresContext) {
     mPresContext->SetContainer(nsnull);
     mPresContext->SetLinkHandler(nsnull);
@@ -4259,17 +4297,17 @@ NS_IMETHODIMP DocumentViewerImpl::SetPag
     mPresContext = CreatePresContext(mDocument,
         nsPresContext::eContext_PageLayout, FindContainerView());
     NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
     mPresContext->SetPaginatedScrolling(PR_TRUE);
     mPresContext->SetPrintSettings(aPrintSettings);
     nsresult rv = mPresContext->Init(mDeviceContext);
     NS_ENSURE_SUCCESS(rv, rv);
   }
-  InitInternal(mParentWidget, nsnull, bounds, PR_TRUE, PR_FALSE, PR_FALSE);
+  InitInternal(mParentWidget, nsnull, mBounds, PR_TRUE, PR_FALSE, PR_FALSE);
   mViewManager->EnableRefresh(NS_VMREFRESH_NO_SYNC);
 
   Show();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::GetHistoryEntry(nsISHEntry **aHistoryEntry)
--- a/layout/generic/nsFrameFrame.cpp
+++ b/layout/generic/nsFrameFrame.cpp
@@ -269,16 +269,21 @@ nsSubDocumentFrame::Init(nsIContent*    
   }
   nsIView* view = GetView();
 
   if (aParent->GetStyleDisplay()->mDisplay == NS_STYLE_DISPLAY_DECK
       && !view->HasWidget()) {
     view->CreateWidget(kCChildCID);
   }
 
+  // Set the primary frame now so that
+  // DocumentViewerImpl::FindContainerView called by ShowViewer below
+  // can find it if necessary.
+  PresContext()->FrameManager()->SetPrimaryFrameFor(aContent, this);
+
   ShowViewer();
   return NS_OK;
 }
 
 void
 nsSubDocumentFrame::ShowViewer()
 {
   if (!PresContext()->IsDynamic()) {
--- a/widget/public/nsIBaseWindow.idl
+++ b/widget/public/nsIBaseWindow.idl
@@ -55,29 +55,29 @@ typedef voidPtr nativeWindow;
  */
 
 [scriptable, uuid(046BC8A0-8015-11d3-AF70-00A024FFC08C)]
 interface nsIBaseWindow : nsISupports
 {
 	/*
 	Allows a client to initialize an object implementing this interface with
 	the usually required window setup information.
+	It is possible to pass null for both parentNativeWindow and parentWidget,
+	but only docshells support this.
 
 	@param parentNativeWindow - This allows a system to pass in the parenting
 		window as a native reference rather than relying on the calling
 		application to have created the parent window as an nsIWidget.  This 
 		value will be ignored (should be nsnull) if an nsIWidget is passed in to
-		the parentWidget parameter.  One of the two parameters however must be
-		passed.
+		the parentWidget parameter.
 
 	@param parentWidget - This allows a system to pass in the parenting widget.
 		This allows some objects to optimize themselves and rely on the view
 		system for event flow rather than creating numerous native windows.  If
-		one of these is not available, nsnull should be passed and a 
-		valid native window should be passed to the parentNativeWindow parameter.
+		one of these is not available, nsnull should be passed.
 
 	@param x - This is the x co-ordinate relative to the parent to place the
 		window.
 
 	@param y - This is the y co-ordinate relative to the parent to place the 
 		window.
 
 	@param cx - This is the width	for the window to be.
@@ -156,19 +156,20 @@ interface nsIBaseWindow : nsISupports
 	/** 
 	 * Tell the window to repaint itself
 	 * @param aForce - if true, repaint immediately
 	 *                 if false, the window may defer repainting as it sees fit.
 	 */
 	void repaint(in boolean force);
 
 	/*			  
-	This is the parenting widget for the control.  This may be null if only the
-	native window was handed in for the parent during initialization.  If this
-	is returned, it should refer to the same object as parentNativeWindow.
+	This is the parenting widget for the control.  This may be null if the
+	native window was handed in for the parent during initialization.
+	If this	is returned, it should refer to the same object as
+	parentNativeWindow.
 
 	Setting this after Create() has been called may not be supported by some
 	implementations.
 
 	On controls that don't support widgets, setting this will return a 
 	NS_ERROR_NOT_IMPLEMENTED error.
 	*/
 	[noscript] attribute nsIWidget parentWidget;