Relanding bug 433616 part 2 to see if it's the cause of a Tp regression
☠☠ backed out by 4e7a2d27d636 ☠ ☠
authorRobert O'Callahan <robert@ocallahan.org>
Tue, 30 Sep 2008 14:45:02 +1300
changeset 19896 c1f6a55626be9cf9fb4b5b35a90444c7d17f35bd
parent 19895 edc314aed893844100ff226d3780c886127b7a01
child 19897 38a48d48587618f7a96c879afc96184593c5d9ea
child 19919 4e7a2d27d63649d9b58a772f6f29903f2601d607
push id2525
push userrocallahan@mozilla.com
push dateTue, 30 Sep 2008 01:45:13 +0000
treeherdermozilla-central@c1f6a55626be [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs433616
milestone1.9.1b1pre
Relanding bug 433616 part 2 to see if it's the cause of a Tp regression
content/base/public/nsIDocument.h
content/base/src/nsContentUtils.cpp
content/base/src/nsDataDocumentContentPolicy.cpp
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsFrameLoader.cpp
content/svg/document/src/Makefile.in
content/xul/content/src/nsXULElement.cpp
layout/base/nsDocumentViewer.cpp
layout/generic/nsFrameFrame.cpp
layout/generic/nsObjectFrame.cpp
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -47,23 +47,23 @@
 #include "nsIWeakReferenceUtils.h"
 #include "nsILoadGroup.h"
 #include "nsCRT.h"
 #include "mozFlushType.h"
 #include "nsIAtom.h"
 #include "nsCompatibility.h"
 #include "nsTObserverArray.h"
 #include "nsNodeInfoManager.h"
+#include "nsIStreamListener.h"
+#include "nsIObserver.h"
 
 class nsIContent;
 class nsPresContext;
 class nsIPresShell;
 class nsIDocShell;
-class nsIStreamListener;
-class nsIStreamObserver;
 class nsStyleSet;
 class nsIStyleSheet;
 class nsIStyleRule;
 class nsIViewManager;
 class nsIScriptGlobalObject;
 class nsPIDOMWindow;
 class nsIDOMEvent;
 class nsIDeviceContext;
@@ -72,17 +72,16 @@ class nsIDOMNode;
 class nsIDOMDocumentFragment;
 class nsILineBreaker;
 class nsIWordBreaker;
 class nsISelection;
 class nsIChannel;
 class nsIPrincipal;
 class nsIDOMDocument;
 class nsIDOMDocumentType;
-class nsIObserver;
 class nsScriptLoader;
 class nsIContentSink;
 class nsIScriptEventManager;
 class nsICSSLoader;
 class nsHTMLStyleSheet;
 class nsIHTMLCSSStyleSheet;
 class nsILayoutHistoryState;
 class nsIVariant;
@@ -92,18 +91,18 @@ class nsIDocumentObserver;
 class nsBindingManager;
 class nsIDOMNodeList;
 class mozAutoSubtreeModified;
 struct JSObject;
 class nsFrameLoader;
 
 // IID for the nsIDocument interface
 #define NS_IDOCUMENT_IID      \
-  { 0xd5b1e3c5, 0x85dc, 0x403e, \
-    { 0xbb, 0x4a, 0x54, 0x66, 0xdc, 0xbe, 0x15, 0x69 } }
+{ 0x189ebc9e, 0x779b, 0x4c49, \
+ { 0x90, 0x8b, 0x9a, 0x80, 0x25, 0x9b, 0xaf, 0xa7 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 //----------------------------------------------------------------------
 
 // Document interface.  This is implemented by all document objects in
 // Gecko.
@@ -999,16 +998,108 @@ public:
   virtual nsresult InitializeFrameLoader(nsFrameLoader* aLoader) = 0;
   // In case of failure, the caller must handle the error, for example by
   // finalizing frame loader asynchronously.
   virtual nsresult FinalizeFrameLoader(nsFrameLoader* aLoader) = 0;
   // Removes the frame loader of aShell from the initialization list.
   virtual void TryCancelFrameLoaderInitialization(nsIDocShell* aShell) = 0;
   //  Returns true if the frame loader of aShell is in the finalization list.
   virtual PRBool FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell) = 0;
+
+  /**
+   * Check whether this document is a root document that is not an
+   * external resource.
+   */
+  PRBool IsRootDisplayDocument() const
+  {
+    return !mParentDocument && !mDisplayDocument;
+  }
+
+  /**
+   * Get the document for which this document is an external resource.  This
+   * will be null if this document is not an external resource.  Otherwise,
+   * GetDisplayDocument() will return a non-null document, and
+   * GetDisplayDocument()->GetDisplayDocument() is guaranteed to be null.
+   */
+  nsIDocument* GetDisplayDocument() const
+  {
+    return mDisplayDocument;
+  }
+  
+  /**
+   * Set the display document for this document.  aDisplayDocument must not be
+   * null.
+   */
+  void SetDisplayDocument(nsIDocument* aDisplayDocument)
+  {
+    NS_PRECONDITION(!GetPrimaryShell() &&
+                    !nsCOMPtr<nsISupports>(GetContainer()) &&
+                    !GetWindow() &&
+                    !GetScriptGlobalObject(),
+                    "Shouldn't set mDisplayDocument on documents that already "
+                    "have a presentation or a docshell or a window");
+    NS_PRECONDITION(aDisplayDocument != this, "Should be different document");
+    NS_PRECONDITION(!aDisplayDocument->GetDisplayDocument(),
+                    "Display documents should not nest");
+    mDisplayDocument = aDisplayDocument;
+  }
+
+  /**
+   * A class that represents an external resource load that has begun but
+   * doesn't have a document yet.  Observers can be registered on this object,
+   * and will be notified after the document is created.  Observers registered
+   * after the document has been created will NOT be notified.  When observers
+   * are notified, the subject will be the newly-created document, the topic
+   * will be "external-resource-document-created", and the data will be null.
+   * If document creation fails for some reason, observers will still be
+   * notified, with a null document pointer.
+   */
+  class ExternalResourceLoad : public nsISupports
+  {
+  public:
+    virtual ~ExternalResourceLoad() {}
+
+    void AddObserver(nsIObserver* aObserver) {
+      NS_PRECONDITION(aObserver, "Must have observer");
+      mObservers.AppendElement(aObserver);
+    }
+
+    const nsTArray< nsCOMPtr<nsIObserver> > & Observers() {
+      return mObservers;
+    }
+  protected:
+    nsAutoTArray< nsCOMPtr<nsIObserver>, 8 > mObservers;    
+  };
+
+  /**
+   * Request an external resource document for aURI.  This will return the
+   * resource document if available.  If one is not available yet, it will
+   * start loading as needed, and the pending load object will be returned in
+   * aPendingLoad so that the caller can register an observer to wait for the
+   * load.  If this function returns null and doesn't return a pending load,
+   * that means that there is no resource document for this URI and won't be
+   * one in the future.
+   *
+   * @param aURI the URI to get
+   * @param aRequestingNode the node making the request
+   * @param aPendingLoad the pending load for this request, if any
+   */
+  virtual nsIDocument*
+    RequestExternalResource(nsIURI* aURI,
+                            nsINode* aRequestingNode,
+                            ExternalResourceLoad** aPendingLoad) = 0;
+
+  /**
+   * Enumerate the external resource documents associated with this document.
+   * The enumerator callback should return PR_TRUE to continue enumerating, or
+   * PR_FALSE to stop.
+   */
+  virtual void EnumerateExternalResources(nsSubDocEnumFunc aCallback,
+                                          void* aData) = 0;
+  
 protected:
   ~nsIDocument()
   {
     // XXX The cleanup of mNodeInfoManager (calling DropDocumentReference and
     //     releasing it) happens in the nsDocument destructor. We'd prefer to
     //     do it here but nsNodeInfoManager is a concrete class that we don't
     //     want to expose to users of the nsIDocument API outside of Gecko.
   }
@@ -1095,16 +1186,21 @@ protected:
   // won't be collected
   PRUint32 mMarkedCCGeneration;
 
   nsTObserverArray<nsIPresShell*> mPresShells;
 
   nsCOMArray<nsINode> mSubtreeModifiedTargets;
   PRUint32            mSubtreeModifiedDepth;
 
+  // If we're an external resource document, this will be non-null and will
+  // point to our "display document": the one that all resource lookups should
+  // go to.
+  nsCOMPtr<nsIDocument> mDisplayDocument;
+
 private:
   // JSObject cache. Only to be used for performance
   // optimizations. This will be set once this document is touched
   // from JS, and it will be unset once the JSObject is finalized.
   JSObject *mJSObject;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -2906,16 +2906,20 @@ nsContentUtils::IsChromeDoc(nsIDocument 
 // static
 PRBool
 nsContentUtils::IsInChromeDocshell(nsIDocument *aDocument)
 {
   if (!aDocument) {
     return PR_FALSE;
   }
 
+  if (aDocument->GetDisplayDocument()) {
+    return IsInChromeDocshell(aDocument->GetDisplayDocument());
+  }
+
   nsCOMPtr<nsISupports> docContainer = aDocument->GetContainer();
   nsCOMPtr<nsIDocShellTreeItem> docShell(do_QueryInterface(docContainer));
   PRInt32 itemType = nsIDocShellTreeItem::typeContent;
   if (docShell) {
     docShell->GetItemType(&itemType);
   }
 
   return itemType == nsIDocShellTreeItem::typeChrome;
--- a/content/base/src/nsDataDocumentContentPolicy.cpp
+++ b/content/base/src/nsDataDocumentContentPolicy.cpp
@@ -67,17 +67,38 @@ nsDataDocumentContentPolicy::ShouldLoad(
   } else {
     nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(aRequestingContext);
     if (window) {
       nsCOMPtr<nsIDOMDocument> domDoc;
       window->GetDocument(getter_AddRefs(domDoc));
       doc = do_QueryInterface(domDoc);
     }
   }
-  if (aContentType != nsIContentPolicy::TYPE_DTD && doc && doc->IsLoadedAsData()) {
+
+  // DTDs are always OK to load
+  if (!doc || aContentType == nsIContentPolicy::TYPE_DTD) {
+    return NS_OK;
+  }
+
+  // Nothing else is OK to load for data documents
+  if (doc->IsLoadedAsData()) {
+    *aDecision = nsIContentPolicy::REJECT_TYPE;
+    return NS_OK;
+  }
+
+  // Allow all loads for non-external-resource documents
+  if (!doc->GetDisplayDocument()) {
+    return NS_OK;
+  }
+
+  // For external resources, blacklist some load types
+  if (aContentType == nsIContentPolicy::TYPE_OBJECT ||
+      aContentType == nsIContentPolicy::TYPE_DOCUMENT ||
+      aContentType == nsIContentPolicy::TYPE_SUBDOCUMENT ||
+      aContentType == nsIContentPolicy::TYPE_SCRIPT) {
     *aDecision = nsIContentPolicy::REJECT_TYPE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDataDocumentContentPolicy::ShouldProcess(PRUint32 aContentType,
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -152,21 +152,33 @@ static NS_DEFINE_CID(kDOMEventGroupCID, 
 #include "nsIDOMXPathEvaluator.h"
 #include "nsDOMCID.h"
 
 #include "nsIJSContextStack.h"
 #include "nsIXPConnect.h"
 #include "nsCycleCollector.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsIContentPolicy.h"
+#include "nsContentPolicyUtils.h"
+#include "nsICategoryManager.h"
+#include "nsIDocumentLoaderFactory.h"
+#include "nsIContentViewer.h"
+#include "nsIXMLContentSink.h"
+#include "nsIChannelEventSink.h"
+#include "nsContentErrors.h"
+#include "nsIXULDocument.h"
+#include "nsIProgressEventSink.h"
+#include "nsISecurityEventSink.h"
+#include "nsIPrompt.h"
 
 #include "nsFrameLoader.h"
 
 #include "mozAutoDocUpdate.h"
 
+
 #ifdef MOZ_LOGGING
 // so we can get logging even in release builds
 #define FORCE_PR_LOG 1
 #endif
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDocumentLeakPRLog;
@@ -714,16 +726,443 @@ nsOnloadBlocker::GetLoadFlags(nsLoadFlag
 
 NS_IMETHODIMP
 nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags)
 {
   return NS_OK;
 }
 
 // ==================================================================
+
+nsExternalResourceMap::nsExternalResourceMap()
+  : mHaveShutDown(PR_FALSE)
+{
+  mMap.Init();
+  mPendingLoads.Init();
+}
+
+nsIDocument*
+nsExternalResourceMap::RequestResource(nsIURI* aURI,
+                                       nsINode* aRequestingNode,
+                                       nsDocument* aDisplayDocument,
+                                       ExternalResourceLoad** aPendingLoad)
+{
+  // If we ever start allowing non-same-origin loads here, we might need to do
+  // something interesting with aRequestingPrincipal even for the hashtable
+  // gets.
+  NS_PRECONDITION(aURI, "Must have a URI");
+  NS_PRECONDITION(aRequestingNode, "Must have a node");
+  *aPendingLoad = nsnull;
+  if (mHaveShutDown) {
+    return nsnull;
+  }
+  
+  // First, make sure we strip the ref from aURI.
+  nsCOMPtr<nsIURI> clone;
+  aURI->Clone(getter_AddRefs(clone));
+  if (!clone) {
+    return nsnull;
+  }
+  nsCOMPtr<nsIURL> url(do_QueryInterface(clone));
+  if (url) {
+    url->SetRef(EmptyCString());
+  }
+  
+  ExternalResource* resource;
+  mMap.Get(clone, &resource);
+  if (resource) {
+    return resource->mDocument;
+  }
+
+  nsRefPtr<PendingLoad> load;
+  mPendingLoads.Get(clone, getter_AddRefs(load));
+  if (load) {
+    NS_ADDREF(*aPendingLoad = load);
+    return nsnull;
+  }
+
+  load = new PendingLoad(aDisplayDocument);
+  if (!load) {
+    return nsnull;
+  }
+
+  if (!mPendingLoads.Put(clone, load)) {
+    return nsnull;
+  }
+
+  if (NS_FAILED(load->StartLoad(clone, aRequestingNode))) {
+    // Make sure we don't thrash things by trying this load again, since
+    // chances are it failed for good reasons (security check, etc).
+    AddExternalResource(clone, nsnull, nsnull, aDisplayDocument);
+  } else {
+    NS_ADDREF(*aPendingLoad = load);
+  }
+
+  return nsnull;
+}
+
+struct
+nsExternalResourceEnumArgs
+{
+  nsIDocument::nsSubDocEnumFunc callback;
+  void *data;
+};
+
+PR_STATIC_CALLBACK(PLDHashOperator)
+ExternalResourceEnumerator(nsIURI* aKey,
+                           nsExternalResourceMap::ExternalResource* aData,
+                           void* aClosure)
+{
+  nsExternalResourceEnumArgs* args =
+    static_cast<nsExternalResourceEnumArgs*>(aClosure);
+  PRBool next = args->callback(aData->mDocument, args->data);
+  return next ? PL_DHASH_NEXT : PL_DHASH_STOP;
+}
+
+void
+nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback,
+                                          void* aData)
+{
+  nsExternalResourceEnumArgs args = { aCallback, aData };
+  mMap.EnumerateRead(ExternalResourceEnumerator, &args);
+}
+
+PR_STATIC_CALLBACK(PLDHashOperator)
+ExternalResourceTraverser(nsIURI* aKey,
+                          nsExternalResourceMap::ExternalResource* aData,
+                          void* aClosure)
+{
+  nsCycleCollectionTraversalCallback *cb = 
+    static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
+                                     "mExternalResourceMap.mMap entry"
+                                     "->mDocument");
+  cb->NoteXPCOMChild(aData->mDocument);
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
+                                     "mExternalResourceMap.mMap entry"
+                                     "->mViewer");
+  cb->NoteXPCOMChild(aData->mViewer);
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
+                                     "mExternalResourceMap.mMap entry"
+                                     "->mLoadGroup");
+  cb->NoteXPCOMChild(aData->mLoadGroup);
+
+  return PL_DHASH_NEXT;
+}
+
+void
+nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) const
+{
+  // mPendingLoads will get cleared out as the requests complete, so
+  // no need to worry about those here.
+  mMap.EnumerateRead(ExternalResourceTraverser, aCallback);
+}
+
+nsresult
+nsExternalResourceMap::AddExternalResource(nsIURI* aURI,
+                                           nsIDocumentViewer* aViewer,
+                                           nsILoadGroup* aLoadGroup,
+                                           nsIDocument* aDisplayDocument)
+{
+  NS_PRECONDITION(aURI, "Unexpected call");
+  NS_PRECONDITION((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup),
+                  "Must have both or neither");
+  
+  nsRefPtr<PendingLoad> load;
+  mPendingLoads.Get(aURI, getter_AddRefs(load));
+  mPendingLoads.Remove(aURI);
+
+  nsresult rv = NS_OK;
+  
+  nsCOMPtr<nsIDocument> doc;
+  if (aViewer) {
+    aViewer->GetDocument(getter_AddRefs(doc));
+    NS_ASSERTION(doc, "Must have a document");
+
+    nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(doc);
+    if (xulDoc) {
+      // We don't handle XUL stuff here yet.
+      rv = NS_ERROR_NOT_AVAILABLE;
+    } else {
+      doc->SetDisplayDocument(aDisplayDocument);
+
+      rv = aViewer->Init(nsnull, nsRect(0, 0, 0, 0));
+      if (NS_SUCCEEDED(rv)) {
+        rv = aViewer->Open(nsnull, nsnull);
+      }
+    }
+    
+    if (NS_FAILED(rv)) {
+      doc = nsnull;
+      aViewer = nsnull;
+      aLoadGroup = nsnull;
+    }
+  }
+
+  ExternalResource* newResource = new ExternalResource();
+  if (newResource && !mMap.Put(aURI, newResource)) {
+    delete newResource;
+    newResource = nsnull;
+    if (NS_SUCCEEDED(rv)) {
+      rv = NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  if (newResource) {
+    newResource->mDocument = doc;
+    newResource->mViewer = aViewer;
+    newResource->mLoadGroup = aLoadGroup;
+  }
+
+  const nsTArray< nsCOMPtr<nsIObserver> > & obs = load->Observers();
+  for (PRUint32 i = 0; i < obs.Length(); ++i) {
+    obs[i]->Observe(doc, "external-resource-document-created", nsnull);
+  }
+
+  return rv;
+}
+
+NS_IMPL_ISUPPORTS2(nsExternalResourceMap::PendingLoad,
+                   nsIStreamListener,
+                   nsIRequestObserver)
+
+NS_IMETHODIMP
+nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest *aRequest,
+                                                   nsISupports *aContext)
+{
+  nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap();
+  if (map.HaveShutDown()) {
+    return NS_BINDING_ABORTED;
+  }
+
+  nsCOMPtr<nsIDocumentViewer> viewer;
+  nsCOMPtr<nsILoadGroup> loadGroup;
+  nsresult rv = SetupViewer(aRequest, getter_AddRefs(viewer),
+                            getter_AddRefs(loadGroup));
+
+  // Make sure to do this no matter what
+  nsresult rv2 = map.AddExternalResource(mURI, viewer, loadGroup,
+                                         mDisplayDocument);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  if (NS_FAILED(rv2)) {
+    mTargetListener = nsnull;
+    return rv2;
+  }
+  
+  return mTargetListener->OnStartRequest(aRequest, aContext);
+}
+
+nsresult
+nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest,
+                                                nsIDocumentViewer** aViewer,
+                                                nsILoadGroup** aLoadGroup)
+{
+  NS_PRECONDITION(!mTargetListener, "Unexpected call to OnStartRequest");
+  *aViewer = nsnull;
+  *aLoadGroup = nsnull;
+  
+  nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
+  NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
+
+  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
+  if (httpChannel) {
+    PRBool requestSucceeded;
+    if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
+        !requestSucceeded) {
+      // Bail out on this load, since it looks like we have an HTTP error page
+      return NS_BINDING_ABORTED;
+    }
+  }
+ 
+  nsCAutoString type;
+  chan->GetContentType(type);
+
+  nsCOMPtr<nsILoadGroup> loadGroup;
+  chan->GetLoadGroup(getter_AddRefs(loadGroup));
+
+  // Give this document its own loadgroup
+  nsCOMPtr<nsILoadGroup> newLoadGroup =
+        do_CreateInstance(NS_LOADGROUP_CONTRACTID);
+  NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
+  newLoadGroup->SetLoadGroup(loadGroup);
+
+  nsCOMPtr<nsIInterfaceRequestor> callbacks;
+  loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+
+  nsCOMPtr<nsIInterfaceRequestor> newCallbacks =
+    new LoadgroupCallbacks(callbacks);
+  newLoadGroup->SetNotificationCallbacks(newCallbacks);
+
+  // This is some serious hackery cribbed from docshell
+  nsCOMPtr<nsICategoryManager> catMan =
+    do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+  NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
+  nsXPIDLCString contractId;
+  nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(),
+                                         getter_Copies(contractId));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
+    do_GetService(contractId);
+  NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
+
+  nsCOMPtr<nsIContentViewer> viewer;
+  nsCOMPtr<nsIStreamListener> listener;
+  rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup,
+                                        type.get(), nsnull, nsnull,
+                                        getter_AddRefs(listener),
+                                        getter_AddRefs(viewer));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDocumentViewer> docViewer = do_QueryInterface(viewer);
+  NS_ENSURE_TRUE(docViewer, NS_ERROR_UNEXPECTED);
+
+  nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
+  if (!parser) {
+    /// We don't want to deal with the various fake documents yet
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  // We can't handle HTML and other weird things here yet.
+  nsIContentSink* sink = parser->GetContentSink();
+  nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
+  if (!xmlSink) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  listener.swap(mTargetListener);
+  docViewer.swap(*aViewer);
+  newLoadGroup.swap(*aLoadGroup);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest,
+                                                    nsISupports* aContext,
+                                                    nsIInputStream* aStream,
+                                                    PRUint32 aOffset,
+                                                    PRUint32 aCount)
+{
+  NS_PRECONDITION(mTargetListener, "Shouldn't be getting called!");
+  if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
+    return NS_BINDING_ABORTED;
+  }
+  return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset,
+                                          aCount);
+}
+
+NS_IMETHODIMP
+nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
+                                                  nsISupports* aContext,
+                                                  nsresult aStatus)
+{
+  // mTargetListener might be null if SetupViewer or AddExternalResource failed
+  if (mTargetListener) {
+    nsCOMPtr<nsIStreamListener> listener;
+    mTargetListener.swap(listener);
+    return listener->OnStopRequest(aRequest, aContext, aStatus);
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI,
+                                              nsINode* aRequestingNode)
+{
+  NS_PRECONDITION(aURI, "Must have a URI");
+  NS_PRECONDITION(aRequestingNode, "Must have a node");
+
+  // Time to start a load.  First, the security checks.
+
+  nsIPrincipal* requestingPrincipal = aRequestingNode->NodePrincipal();
+
+  nsresult rv = nsContentUtils::GetSecurityManager()->
+    CheckLoadURIWithPrincipal(requestingPrincipal, aURI,
+                              nsIScriptSecurityManager::STANDARD);
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  rv = requestingPrincipal->CheckMayLoad(aURI, PR_TRUE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
+  rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OTHER,
+                                 aURI,
+                                 requestingPrincipal,
+                                 aRequestingNode,
+                                 EmptyCString(), //mime guess
+                                 nsnull,         //extra
+                                 &shouldLoad,
+                                 nsContentUtils::GetContentPolicy(),
+                                 nsContentUtils::GetSecurityManager());
+  if (NS_FAILED(rv)) return rv;
+  if (NS_CP_REJECTED(shouldLoad)) {
+    // Disallowed by content policy
+    return NS_ERROR_CONTENT_BLOCKED;
+  }
+
+  nsIDocument* doc = aRequestingNode->GetOwnerDoc();
+  if (!doc) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
+  nsCOMPtr<nsIChannel> channel;
+  rv = NS_NewChannel(getter_AddRefs(channel), aURI, nsnull, loadGroup);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mURI = aURI;
+
+  nsCOMPtr<nsIInterfaceRequestor> req = nsContentUtils::GetSameOriginChecker();
+  NS_ENSURE_TRUE(req, NS_ERROR_OUT_OF_MEMORY);
+
+  channel->SetNotificationCallbacks(req);
+  return channel->AsyncOpen(this, nsnull);
+}
+
+NS_IMPL_ISUPPORTS1(nsExternalResourceMap::LoadgroupCallbacks,
+                   nsIInterfaceRequestor)
+
+NS_IMETHODIMP
+nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID,
+                                                        void **aSink)
+{
+#define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
+  if (mCallbacks &&
+      (IID_IS(nsIProgressEventSink) ||
+       IID_IS(nsIChannelEventSink) ||
+       IID_IS(nsISecurityEventSink) ||
+       IID_IS(nsIPrompt) ||
+       IID_IS(nsIAuthPrompt) ||
+       IID_IS(nsIAuthPrompt2) ||
+       IID_IS(nsIApplicationCacheContainer) ||
+       // XXXbz evil hack for cookies for now
+       IID_IS(nsIDOMWindow) ||
+       IID_IS(nsIDocShellTreeItem))) {
+    return mCallbacks->GetInterface(aIID, aSink);
+  }
+#undef IID_IS
+
+  *aSink = nsnull;
+  return NS_NOINTERFACE;
+}
+
+nsExternalResourceMap::ExternalResource::~ExternalResource()
+{
+  if (mViewer) {
+    mViewer->Close(nsnull);
+    mViewer->Destroy();
+  }
+}
+
+// ==================================================================
 // =
 // ==================================================================
 
 // If we ever have an nsIDocumentObserver notification for stylesheet title
 // changes, we could make this inherit from nsDOMStringList instead of
 // reimplementing nsIDOMDOMStringList.
 class nsDOMStyleSheetSetList : public nsIDOMDOMStringList
                           
@@ -1189,17 +1628,18 @@ SubDocTraverser(PLDHashTable *table, PLD
   cb->NoteXPCOMChild(entry->mKey);
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mSubDocument");
   cb->NoteXPCOMChild(entry->mSubDocument);
 
   return PL_DHASH_NEXT;
 }
 
 PR_STATIC_CALLBACK(PLDHashOperator)
-RadioGroupsTraverser(const nsAString& aKey, nsAutoPtr<nsRadioGroupStruct>& aData, void* aClosure)
+RadioGroupsTraverser(const nsAString& aKey, nsRadioGroupStruct* aData,
+                     void* aClosure)
 {
   nsCycleCollectionTraversalCallback *cb = 
     static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
 
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
                                    "mRadioGroups entry->mSelectedRadioButton");
   cb->NoteXPCOMChild(aData->mSelectedRadioButton);
 
@@ -1256,40 +1696,43 @@ IdentifierMapEntryTraverse(nsIdentifierM
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocument)
   if (nsCCUncollectableMarker::InGeneration(tmp->GetMarkedCCGeneration())) {
     return NS_OK;
   }
 
   tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb);
 
+  tmp->mExternalResourceMap.Traverse(&cb);
+
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNodeInfo)
 
   // Traverse the mChildren nsAttrAndChildArray.
   for (PRInt32 indx = PRInt32(tmp->mChildren.ChildCount()); indx > 0; --indx) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
     cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_USERDATA
 
   // Traverse all nsIDocument pointer members.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCachedRootContent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNodeInfoManager,
                                                   nsNodeInfoManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSecurityInfo)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDisplayDocument)
 
   // Traverse all nsDocument nsCOMPtrs.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mParser)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptGlobalObject)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mListenerManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDOMStyleSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptLoader)
 
-  tmp->mRadioGroups.Enumerate(RadioGroupsTraverser, &cb);
+  tmp->mRadioGroups.EnumerateRead(RadioGroupsTraverser, &cb);
 
   // The boxobject for an element will only exist as long as it's in the
   // document, so we'll traverse the table here instead of from the element.
   if (tmp->mBoxObjectTable) {
     tmp->mBoxObjectTable->EnumerateRead(BoxObjectTraverser, &cb);
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
@@ -1321,26 +1764,30 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
   // Tear down linkmap. This is a performance optimization so that we
   // don't waste time removing links one by one as they are removed
   // from the doc.
   tmp->DestroyLinkMap();
 
+  // Clear out our external resources
+  tmp->mExternalResourceMap.Shutdown();
+
   nsAutoScriptBlocker scriptBlocker;
 
   // Unlink the mChildren nsAttrAndChildArray.
   for (PRInt32 indx = PRInt32(tmp->mChildren.ChildCount()) - 1; 
        indx >= 0; --indx) {
     tmp->mChildren.ChildAt(indx)->UnbindFromTree();
     tmp->mChildren.RemoveChildAt(indx);
   }
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCachedRootContent)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDisplayDocument)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK_USERDATA
 
   // Drop the content hash.
   delete tmp->mContentWrapperHash;
   tmp->mContentWrapperHash = nsnull;
 
   tmp->mParentDocument = nsnull;
@@ -1706,16 +2153,19 @@ nsDocument::StartDocumentLoad(const char
     // We leave them disabled even in EndLoad(), and let anyone
     // who puts the document on display to worry about enabling.
 
     // Do not load/process scripts when loading as data
     ScriptLoader()->SetEnabled(PR_FALSE);
 
     // styles
     CSSLoader()->SetEnabled(PR_FALSE); // Do not load/process styles when loading as data
+  } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
+    // Allow CSS, but not scripts
+    ScriptLoader()->SetEnabled(PR_FALSE);
   }
 
   mMayStartLayout = PR_FALSE;
 
   mHaveInputEncoding = PR_TRUE;
 
   if (aReset) {
     Reset(aChannel, aLoadGroup);
@@ -4698,16 +5148,39 @@ nsDocument::FrameLoaderScheduledToBeFina
       if (mFinalizableFrameLoaders[i]->GetExistingDocShell() == aShell) {
         return PR_TRUE;
       }
     }
   }
   return PR_FALSE;
 }
 
+nsIDocument*
+nsDocument::RequestExternalResource(nsIURI* aURI,
+                                    nsINode* aRequestingNode,
+                                    ExternalResourceLoad** aPendingLoad)
+{
+  NS_PRECONDITION(aURI, "Must have a URI");
+  NS_PRECONDITION(aRequestingNode, "Must have a node");
+  if (mDisplayDocument) {
+    return mDisplayDocument->RequestExternalResource(aURI,
+                                                     aRequestingNode,
+                                                     aPendingLoad);
+  }
+
+  return mExternalResourceMap.RequestResource(aURI, aRequestingNode,
+                                              this, aPendingLoad);
+}
+
+void
+nsDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData)
+{
+  mExternalResourceMap.EnumerateResources(aCallback, aData);
+}
+
 struct DirTable {
   const char* mName;
   PRUint8     mValue;
 };
 
 static const DirTable dirAttributes[] = {
   {"ltr", IBMBIDI_TEXTDIRECTION_LTR},
   {"rtl", IBMBIDI_TEXTDIRECTION_RTL},
@@ -5855,30 +6328,30 @@ nsDocument::GetXMLDeclaration(nsAString&
     }
   }
 }
 
 PRBool
 nsDocument::IsScriptEnabled()
 {
   nsCOMPtr<nsIScriptSecurityManager> sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
-  NS_ENSURE_TRUE(sm, PR_TRUE);
+  NS_ENSURE_TRUE(sm, PR_FALSE);
 
   nsIScriptGlobalObject* globalObject = GetScriptGlobalObject();
-  NS_ENSURE_TRUE(globalObject, PR_TRUE);
+  NS_ENSURE_TRUE(globalObject, PR_FALSE);
 
   nsIScriptContext *scriptContext = globalObject->GetContext();
-  NS_ENSURE_TRUE(scriptContext, PR_TRUE);
+  NS_ENSURE_TRUE(scriptContext, PR_FALSE);
 
   JSContext* cx = (JSContext *) scriptContext->GetNativeContext();
-  NS_ENSURE_TRUE(cx, PR_TRUE);
+  NS_ENSURE_TRUE(cx, PR_FALSE);
 
   PRBool enabled;
   nsresult rv = sm->CanExecuteScripts(cx, NodePrincipal(), &enabled);
-  NS_ENSURE_SUCCESS(rv, PR_TRUE);
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
   return enabled;
 }
 
 nsresult
 nsDocument::GetRadioGroup(const nsAString& aName,
                           nsRadioGroupStruct **aRadioGroup)
 {
   nsAutoString tmKey(aName);
@@ -6391,16 +6864,21 @@ nsDocument::Destroy()
   for (i = 0; i < count; ++i) {
     mChildren.ChildAt(i)->DestroyContent();
   }
 
   mLayoutHistoryState = nsnull;
 
   nsContentList::OnDocumentDestroy(this);
 
+  // Shut down our external resource map.  We might not need this for
+  // leak-fixing if we fix DocumentViewerImpl to do cycle-collection, but
+  // tearing down all those frame trees right now is the right thing to do.
+  mExternalResourceMap.Shutdown();
+
   // XXX We really should let cycle collection do this, but that currently still
   //     leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
   //     When we start relying on cycle collection again we should remove the
   //     check for mScriptGlobalObject in AddReference.
   delete mContentWrapperHash;
   mContentWrapperHash = nsnull;
 }
 
@@ -6432,30 +6910,40 @@ nsDocument::GetLayoutHistoryState() cons
   }
 
   return state;
 }
 
 void
 nsDocument::BlockOnload()
 {
+  if (mDisplayDocument) {
+    mDisplayDocument->BlockOnload();
+    return;
+  }
+  
   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
   // -- it's not ours.
   if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
     nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
     if (loadGroup) {
       loadGroup->AddRequest(mOnloadBlocker, nsnull);
     }
   }
   ++mOnloadBlockCount;      
 }
 
 void
 nsDocument::UnblockOnload(PRBool aFireSync)
 {
+  if (mDisplayDocument) {
+    mDisplayDocument->UnblockOnload(aFireSync);
+    return;
+  }
+
   if (mOnloadBlockCount == 0) {
     NS_NOTREACHED("More UnblockOnload() calls than BlockOnload() calls; dropping call");
     return;
   }
 
   --mOnloadBlockCount;
 
   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
@@ -6493,22 +6981,24 @@ nsDocument::PostUnblockOnloadEvent()
   } else {
     NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
   }
 }
 
 void
 nsDocument::DoUnblockOnload()
 {
-  NS_ASSERTION(mOnloadBlockCount != 0,
-               "Shouldn't have a count of zero here, since we stabilized in "
-               "PostUnblockOnloadEvent");
+  NS_PRECONDITION(!mDisplayDocument,
+                  "Shouldn't get here for resource document");
+  NS_PRECONDITION(mOnloadBlockCount != 0,
+                  "Shouldn't have a count of zero here, since we stabilized in "
+                  "PostUnblockOnloadEvent");
   
   --mOnloadBlockCount;
-  
+
   if (mOnloadBlockCount != 0) {
     // We blocked again after the last unblock.  Nothing to do here.  We'll
     // post a new event when we unblock again.
     return;
   }
 
   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
   // -- it's not ours.
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -103,16 +103,18 @@
 #include "nsStyleSet.h"
 #include "nsXMLEventsManager.h"
 #include "pldhash.h"
 #include "nsAttrAndChildArray.h"
 #include "nsDOMAttributeMap.h"
 #include "nsPresShellIterator.h"
 #include "nsContentUtils.h"
 #include "nsThreadUtils.h"
+#include "nsIDocumentViewer.h"
+#include "nsIInterfaceRequestor.h"
 
 #define XML_DECLARATION_BITS_DECLARATION_EXISTS   (1 << 0)
 #define XML_DECLARATION_BITS_ENCODING_EXISTS      (1 << 1)
 #define XML_DECLARATION_BITS_STANDALONE_EXISTS    (1 << 2)
 #define XML_DECLARATION_BITS_STANDALONE_YES       (1 << 3)
 
 
 class nsIEventListenerManager;
@@ -378,16 +380,127 @@ public:
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUEST
 
 private:
   ~nsOnloadBlocker() {}
 };
 
+class nsExternalResourceMap
+{
+public:
+  typedef nsIDocument::ExternalResourceLoad ExternalResourceLoad;
+  nsExternalResourceMap();
+
+  /**
+   * Request an external resource document.  This does exactly what
+   * nsIDocument::RequestExternalResource is documented to do.
+   */
+  nsIDocument* RequestResource(nsIURI* aURI,
+                               nsINode* aRequestingNode,
+                               nsDocument* aDisplayDocument,
+                               ExternalResourceLoad** aPendingLoad);
+
+  /**
+   * Enumerate the resource documents.  See
+   * nsIDocument::EnumerateExternalResources.
+   */
+  void EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback, void* aData);
+
+  /**
+   * Traverse ourselves for cycle-collection
+   */
+  void Traverse(nsCycleCollectionTraversalCallback* aCallback) const;
+
+  /**
+   * Shut ourselves down (used for cycle-collection unlink), as well
+   * as for document destruction.
+   */
+  void Shutdown()
+  {
+    mPendingLoads.Clear();
+    mMap.Clear();
+    mHaveShutDown = PR_TRUE;
+  }
+
+  PRBool HaveShutDown() const
+  {
+    return mHaveShutDown;
+  }
+
+  // Needs to be public so we can traverse them sanely
+  struct ExternalResource
+  {
+    ~ExternalResource();
+    nsCOMPtr<nsIDocument> mDocument;
+    nsCOMPtr<nsIContentViewer> mViewer;
+    nsCOMPtr<nsILoadGroup> mLoadGroup;
+  };
+
+protected:
+  class PendingLoad : public ExternalResourceLoad,
+                      public nsIStreamListener
+  {
+  public:
+    PendingLoad(nsDocument* aDisplayDocument) :
+      mDisplayDocument(aDisplayDocument)
+    {}
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSISTREAMLISTENER
+    NS_DECL_NSIREQUESTOBSERVER
+
+    /**
+     * Start aURI loading.  This will perform the necessary security checks and
+     * so forth.
+     */
+    nsresult StartLoad(nsIURI* aURI, nsINode* aRequestingNode);
+
+    /**
+     * Set up an nsIDocumentViewer based on aRequest.  This is guaranteed to
+     * put null in *aViewer and *aLoadGroup on all failures.
+     */
+    nsresult SetupViewer(nsIRequest* aRequest, nsIDocumentViewer** aViewer,
+                         nsILoadGroup** aLoadGroup);
+
+  private:
+    nsRefPtr<nsDocument> mDisplayDocument;
+    nsCOMPtr<nsIStreamListener> mTargetListener;
+    nsCOMPtr<nsIURI> mURI;
+  };
+  friend class PendingLoad;
+
+  class LoadgroupCallbacks : public nsIInterfaceRequestor
+  {
+  public:
+    LoadgroupCallbacks(nsIInterfaceRequestor* aOtherCallbacks)
+      : mCallbacks(aOtherCallbacks)
+    {}
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIINTERFACEREQUESTOR
+  private:
+    nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
+  };
+  
+  /**
+   * Add an ExternalResource for aURI.  aViewer and aLoadGroup might be null
+   * when this is called if the URI didn't result in an XML document.  This
+   * function makes sure to remove the pending load for aURI, if any, from our
+   * hashtable, and to notify its observers, if any.
+   */
+  nsresult AddExternalResource(nsIURI* aURI, nsIDocumentViewer* aViewer,
+                               nsILoadGroup* aLoadGroup,
+                               nsIDocument* aDisplayDocument);
+  
+  nsClassHashtable<nsURIHashKey, ExternalResource> mMap;
+  nsRefPtrHashtable<nsURIHashKey, PendingLoad> mPendingLoads;
+  PRPackedBool mHaveShutDown;
+};
+
 // Base class for our document implementations.
 //
 // Note that this class *implements* nsIDOMXMLDocument, but it's not
 // really an nsIDOMXMLDocument. The reason for implementing
 // nsIDOMXMLDocument on this class is to avoid having to duplicate all
 // its inherited methods on document classes that *are*
 // nsIDOMXMLDocument's. nsDocument's QI should *not* claim to support
 // nsIDOMXMLDocument unless someone writes a real implementation of
@@ -797,29 +910,40 @@ public:
   virtual NS_HIDDEN_(nsresult) GetContentListFor(nsIContent* aContent,
                                                  nsIDOMNodeList** aResult);
   virtual NS_HIDDEN_(void) FlushSkinBindings();
 
   virtual NS_HIDDEN_(nsresult) InitializeFrameLoader(nsFrameLoader* aLoader);
   virtual NS_HIDDEN_(nsresult) FinalizeFrameLoader(nsFrameLoader* aLoader);
   virtual NS_HIDDEN_(void) TryCancelFrameLoaderInitialization(nsIDocShell* aShell);
   virtual NS_HIDDEN_(PRBool) FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell);
+  virtual NS_HIDDEN_(nsIDocument*)
+    RequestExternalResource(nsIURI* aURI,
+                            nsINode* aRequestingNode,
+                            ExternalResourceLoad** aPendingLoad);
+  virtual NS_HIDDEN_(void)
+    EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData);
 
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDocument, nsIDocument)
 
   /**
    * Utility method for getElementsByClassName.  aRootNode is the node (either
    * document or element), which getElementsByClassName was called on.
    */
   static nsresult GetElementsByClassNameHelper(nsINode* aRootNode,
                                                const nsAString& aClasses,
                                                nsIDOMNodeList** aReturn);
 
   void DoNotifyPossibleTitleChange();
 
+  nsExternalResourceMap& ExternalResourceMap()
+  {
+    return mExternalResourceMap;
+  }
+
   void SetLoadedAsData(PRBool aLoadedAsData) { mLoadedAsData = aLoadedAsData; }
 
   nsresult CloneDocHelper(nsDocument* clone) const;
 
 protected:
 
   void RegisterNamedItems(nsIContent *aContent);
   void UnregisterNamedItems(nsIContent *aContent);
@@ -1082,11 +1206,13 @@ private:
 
   // Member to store out last-selected stylesheet set.
   nsString mLastStyleSheetSet;
 
   nsTArray<nsRefPtr<nsFrameLoader> > mInitializableFrameLoaders;
   nsTArray<nsRefPtr<nsFrameLoader> > mFinalizableFrameLoaders;
 
   nsRevocableEventPtr<nsRunnableMethod<nsDocument> > mPendingTitleChangeEvent;
+
+  nsExternalResourceMap mExternalResourceMap;
 };
 
 #endif /* nsDocument_h___ */
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -722,16 +722,21 @@ nsFrameLoader::EnsureDocShell()
   // Get our parent docshell off the document of mOwnerContent
   // XXXbz this is such a total hack.... We really need to have a
   // better setup for doing this.
   nsIDocument* doc = mOwnerContent->GetDocument();
   if (!doc) {
     return NS_ERROR_UNEXPECTED;
   }
 
+  if (doc->GetDisplayDocument()) {
+    // Don't allow subframe loads in external reference documents
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   nsCOMPtr<nsIWebNavigation> parentAsWebNav =
     do_GetInterface(doc->GetScriptGlobalObject());
 
   // Create the docshell...
   mDocShell = do_CreateInstance("@mozilla.org/webshell;1");
   NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
 
   // Get the frame name and tell the docshell about it.
--- a/content/svg/document/src/Makefile.in
+++ b/content/svg/document/src/Makefile.in
@@ -49,16 +49,17 @@ REQUIRES	= \
 		  layout \
 		  widget \
 		  xpcom \
 		  string \
 		  gfx \
 		  thebes \
 		  dom \
 		  webshell \
+		  docshell \
 		  htmlparser \
 		  necko \
 		  pref \
 		  js \
 		  caps \
 		  locale \
 		  $(NULL)
 
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2483,17 +2483,17 @@ nsresult nsXULElement::MakeHeavyweight()
 nsresult
 nsXULElement::HideWindowChrome(PRBool aShouldHide)
 {
     nsIDocument* doc = GetCurrentDoc();
     if (!doc || doc->GetRootContent() != this)
       return NS_ERROR_UNEXPECTED;
 
     // only top level chrome documents can hide the window chrome
-    if (doc->GetParentDocument())
+    if (!doc->IsRootDisplayDocument())
       return NS_OK;
 
     nsIPresShell *shell = doc->GetPrimaryShell();
 
     if (shell) {
         nsIContent* content = static_cast<nsIContent*>(this);
         nsIFrame* frame = shell->GetPrimaryFrameFor(content);
 
@@ -2517,17 +2517,17 @@ void
 nsXULElement::SetTitlebarColor(nscolor aColor, PRBool aActive)
 {
     nsIDocument* doc = GetCurrentDoc();
     if (!doc || doc->GetRootContent() != this) {
         return;
     }
 
     // only top level chrome documents can set the titlebar color
-    if (!doc->GetParentDocument()) {
+    if (doc->IsRootDisplayDocument()) {
         nsCOMPtr<nsISupports> container = doc->GetContainer();
         nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
         if (baseWindow) {
             nsCOMPtr<nsIWidget> mainWidget;
             baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
             if (mainWidget) {
                 mainWidget->SetWindowTitlebarColor(aColor, aActive);
             }
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -812,17 +812,21 @@ DocumentViewerImpl::InitInternal(nsIWidg
 {
   mParentWidget = aParentWidget; // not ref counted
 
   nsresult rv = NS_OK;
   NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
 
   PRBool makeCX = PR_FALSE;
   if (aDoCreation) {
-    if (aParentWidget && !mPresContext) {
+    // 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) {
       // Create presentation context
       if (mIsPageMode) {
         //Presentation context already created in SetPageMode which is calling this method
       }
       else
         mPresContext =
             new nsPresContext(mDocument, nsPresContext::eContext_Galley);
       NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
@@ -1236,22 +1240,22 @@ AttachContainerRecurse(nsIDocShell* aShe
 NS_IMETHODIMP
 DocumentViewerImpl::Open(nsISupports *aState, nsISHEntry *aSHEntry)
 {
   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
 
   nsRect bounds;
   mWindow->GetBounds(bounds);
 
+  if (mDocument)
+    mDocument->SetContainer(nsCOMPtr<nsISupports>(do_QueryReferent(mContainer)));
+
   nsresult rv = InitInternal(mParentWidget, aState, bounds, PR_FALSE, PR_FALSE);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (mDocument)
-    mDocument->SetContainer(nsCOMPtr<nsISupports>(do_QueryReferent(mContainer)));
-
   if (mPresShell)
     mPresShell->SetForwardingContainer(nsnull);
 
   // Rehook the child presentations.  The child shells are still in
   // session history, so get them from there.
 
   if (aSHEntry) {
     nsCOMPtr<nsIDocShellTreeItem> item;
@@ -2096,35 +2100,38 @@ DocumentViewerImpl::CreateStyleSet(nsIDo
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   styleSet->BeginUpdate();
   
   // The document will fill in the document sheets when we create the presshell
   
   // Handle the user sheets.
-  PRInt32 shellType = nsIDocShellTreeItem::typeContent;;
-  nsCOMPtr<nsIDocShellTreeItem> docShell(do_QueryReferent(mContainer));
-  if (docShell) {
-    docShell->GetItemType(&shellType);
-  }
+#ifdef DEBUG
+  nsCOMPtr<nsISupports> debugDocContainer = aDocument->GetContainer();
+  nsCOMPtr<nsIDocShellTreeItem> debugDocShell(do_QueryReferent(mContainer));
+  NS_ASSERTION(SameCOMIdentity(debugDocContainer, debugDocShell),
+               "Unexpected containers");
+#endif
   nsICSSStyleSheet* sheet = nsnull;
-  if (shellType == nsIDocShellTreeItem::typeChrome) {
+  if (nsContentUtils::IsInChromeDocshell(aDocument)) {
     sheet = nsLayoutStylesheetCache::UserChromeSheet();
   }
   else {
     sheet = nsLayoutStylesheetCache::UserContentSheet();
   }
 
   if (sheet)
     styleSet->AppendStyleSheet(nsStyleSet::eUserSheet, sheet);
 
   // Append chrome sheets (scrollbars + forms).
   PRBool shouldOverride = PR_FALSE;
-  nsCOMPtr<nsIDocShell> ds(do_QueryInterface(docShell));
+  // We don't want a docshell here for external resource docs, so just
+  // look at mContainer.
+  nsCOMPtr<nsIDocShell> ds(do_QueryReferent(mContainer));
   nsCOMPtr<nsIDOMEventTarget> chromeHandler;
   nsCOMPtr<nsIURI> uri;
   nsCOMPtr<nsICSSStyleSheet> csssheet;
 
   if (ds) {
     ds->GetChromeEventHandler(getter_AddRefs(chromeHandler));
   }
   if (chromeHandler) {
@@ -2291,16 +2298,33 @@ DocumentViewerImpl::MakeWindow(const nsS
   return rv;
 }
 
 nsresult
 DocumentViewerImpl::CreateDeviceContext(nsIWidget* aWidget)
 {
   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");
+    
+    // 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->GetNativeData(NS_NATIVE_WIDGET) : nsnull);
   return NS_OK;
 }
@@ -2679,16 +2703,48 @@ SetChildTextZoom(nsIMarkupDocumentViewer
 
 static void
 SetChildFullZoom(nsIMarkupDocumentViewer* aChild, void* aClosure)
 {
   struct ZoomInfo* ZoomInfo = (struct ZoomInfo*) aClosure;
   aChild->SetFullZoom(ZoomInfo->mZoom);
 }
 
+PR_STATIC_CALLBACK(PRBool)
+SetExtResourceTextZoom(nsIDocument* aDocument, void* aClosure)
+{
+  // Would it be better to enumerate external resource viewers instead?
+  nsIPresShell* shell = aDocument->GetPrimaryShell();
+  if (shell) {
+    nsPresContext* ctxt = shell->GetPresContext();
+    if (ctxt) {
+      struct ZoomInfo* ZoomInfo = static_cast<struct ZoomInfo*>(aClosure);
+      ctxt->SetTextZoom(ZoomInfo->mZoom);
+    }
+  }
+
+  return PR_TRUE;
+}
+
+PR_STATIC_CALLBACK(PRBool)
+SetExtResourceFullZoom(nsIDocument* aDocument, void* aClosure)
+{
+  // Would it be better to enumerate external resource viewers instead?
+  nsIPresShell* shell = aDocument->GetPrimaryShell();
+  if (shell) {
+    nsPresContext* ctxt = shell->GetPresContext();
+    if (ctxt) {
+      struct ZoomInfo* ZoomInfo = static_cast<struct ZoomInfo*>(aClosure);
+      ctxt->SetFullZoom(ZoomInfo->mZoom);
+    }
+  }
+
+  return PR_TRUE;
+}
+
 NS_IMETHODIMP
 DocumentViewerImpl::SetTextZoom(float aTextZoom)
 {
   if (!GetIsPrintPreview()) {
     mTextZoom = aTextZoom;
   }
 
   nsIViewManager::UpdateViewBatch batch(GetViewManager());
@@ -2701,16 +2757,19 @@ DocumentViewerImpl::SetTextZoom(float aT
   CallChildren(SetChildTextZoom, &ZoomInfo);
 
   // Now change our own zoom
   nsPresContext* pc = GetPresContext();
   if (pc && aTextZoom != mPresContext->TextZoom()) {
       pc->SetTextZoom(aTextZoom);
   }
 
+  // And do the external resources
+  mDocument->EnumerateExternalResources(SetExtResourceTextZoom, &ZoomInfo);
+
   batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
   
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::GetTextZoom(float* aTextZoom)
 {
@@ -2732,16 +2791,19 @@ DocumentViewerImpl::SetFullZoom(float aF
   struct ZoomInfo ZoomInfo = { aFullZoom };
   CallChildren(SetChildFullZoom, &ZoomInfo);
 
   nsPresContext* pc = GetPresContext();
   if (pc) {
     pc->SetFullZoom(aFullZoom);
   }
 
+  // And do the external resources
+  mDocument->EnumerateExternalResources(SetExtResourceFullZoom, &ZoomInfo);
+
   batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::GetFullZoom(float* aFullZoom)
 {
--- a/layout/generic/nsFrameFrame.cpp
+++ b/layout/generic/nsFrameFrame.cpp
@@ -550,19 +550,21 @@ nsSubDocumentFrame::Reflow(nsPresContext
   }
 
   nsSize innerSize(aDesiredSize.width, aDesiredSize.height);
   if (IsInline()) {
     innerSize.width  -= aReflowState.mComputedBorderPadding.LeftRight();
     innerSize.height -= aReflowState.mComputedBorderPadding.TopBottom();
   }
 
-  nsIViewManager* vm = mInnerView->GetViewManager();
-  vm->MoveViewTo(mInnerView, offset.x, offset.y);
-  vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), innerSize), PR_TRUE);
+  if (mInnerView) {
+    nsIViewManager* vm = mInnerView->GetViewManager();
+    vm->MoveViewTo(mInnerView, offset.x, offset.y);
+    vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), innerSize), PR_TRUE);
+  }
 
   // Determine if we need to repaint our border, background or outline
   CheckInvalidateSizeChange(aDesiredSize);
 
   FinishAndStoreOverflow(&aDesiredSize);
 
   // Invalidate the frame contents
   // XXX is this really needed?
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -604,28 +604,32 @@ static NS_DEFINE_CID(kCPluginManagerCID,
 
 // #define DO_DIRTY_INTERSECT 1   // enable dirty rect intersection during paint
 
 NS_IMETHODIMP 
 nsObjectFrame::Init(nsIContent*      aContent,
                     nsIFrame*        aParent,
                     nsIFrame*        aPrevInFlow)
 {
-  mPreventInstantiation = PR_FALSE;
+  NS_PRECONDITION(aContent, "How did that happen?");
+  mPreventInstantiation =
+    (aContent->GetCurrentDoc()->GetDisplayDocument() != nsnull);
 
   PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG,
          ("Initializing nsObjectFrame %p for content %p\n", this, aContent));
 
   return nsObjectFrameSuper::Init(aContent, aParent, aPrevInFlow);
 }
 
 void
 nsObjectFrame::Destroy()
 {
-  NS_ASSERTION(!mPreventInstantiation, "about to crash due to bug 136927");
+  NS_ASSERTION(!mPreventInstantiation ||
+               mContent && mContent->GetCurrentDoc()->GetDisplayDocument(),
+               "about to crash due to bug 136927");
 
   // we need to finish with the plugin before native window is destroyed
   // doing this in the destructor is too late.
   StopPluginInternal(PR_TRUE);
   
   nsObjectFrameSuper::Destroy();
 }
 
@@ -1713,16 +1717,17 @@ nsObjectFrame::Instantiate(const char* a
   FixupWindow(mRect.Size());
 
   // get the nsIPluginHost service
   nsCOMPtr<nsIPluginHost> pluginHost(do_GetService(kCPluginManagerCID, &rv));
   if (NS_FAILED(rv))
     return rv;
   mInstanceOwner->SetPluginHost(pluginHost);
 
+  NS_ASSERTION(!mPreventInstantiation, "Say what?");
   mPreventInstantiation = PR_TRUE;
 
   rv = InstantiatePlugin(pluginHost, aMimeType, aURI);
 
   if (!weakFrame.IsAlive()) {
     return NS_ERROR_NOT_AVAILABLE;
   }