Bug 433616 part 2. Implement loading of external resource documents. r=jst, sr=roc

This commit is contained in:
Boris Zbarsky 2008-09-28 15:15:22 -04:00
Родитель a6aec54eaf
Коммит 72a7d1cba0
11 изменённых файлов: 847 добавлений и 35 удалений

Просмотреть файл

@ -52,13 +52,13 @@
#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;
@ -77,7 +77,6 @@ class nsIChannel;
class nsIPrincipal;
class nsIDOMDocument;
class nsIDOMDocumentType;
class nsIObserver;
class nsScriptLoader;
class nsIContentSink;
class nsIScriptEventManager;
@ -97,8 +96,8 @@ 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)
@ -1004,6 +1003,98 @@ public:
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()
{
@ -1100,6 +1191,11 @@ protected:
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

Просмотреть файл

@ -2911,6 +2911,10 @@ nsContentUtils::IsInChromeDocshell(nsIDocument *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;

Просмотреть файл

@ -72,7 +72,28 @@ nsDataDocumentContentPolicy::ShouldLoad(PRUint32 aContentType,
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;
}

Просмотреть файл

@ -157,11 +157,23 @@ static NS_DEFINE_CID(kDOMEventGroupCID, NS_DOMEVENTGROUP_CID);
#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
@ -718,6 +730,433 @@ 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();
}
}
// ==================================================================
// =
// ==================================================================
@ -1194,7 +1633,8 @@ SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number,
}
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);
@ -1261,6 +1701,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocument)
tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb);
tmp->mExternalResourceMap.Traverse(&cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNodeInfo)
// Traverse the mChildren nsAttrAndChildArray.
@ -1276,6 +1718,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocument)
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)
@ -1284,7 +1727,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocument)
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.
@ -1326,6 +1769,9 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
// from the doc.
tmp->DestroyLinkMap();
// Clear out our external resources
tmp->mExternalResourceMap.Shutdown();
nsAutoScriptBlocker scriptBlocker;
// Unlink the mChildren nsAttrAndChildArray.
@ -1336,6 +1782,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCachedRootContent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDisplayDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK_USERDATA
@ -1711,6 +2158,9 @@ nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
// 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;
@ -4703,6 +5153,29 @@ nsDocument::FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell)
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;
@ -5860,20 +6333,20 @@ 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;
}
@ -6396,6 +6869,11 @@ nsDocument::Destroy()
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
@ -6437,6 +6915,11 @@ nsDocument::GetLayoutHistoryState() const
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) {
@ -6451,6 +6934,11 @@ nsDocument::BlockOnload()
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;
@ -6498,12 +6986,14 @@ nsDocument::PostUnblockOnloadEvent()
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.

Просмотреть файл

@ -108,6 +108,8 @@
#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)
@ -383,6 +385,117 @@ 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
@ -802,6 +915,12 @@ public:
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)
@ -815,6 +934,11 @@ public:
void DoNotifyPossibleTitleChange();
nsExternalResourceMap& ExternalResourceMap()
{
return mExternalResourceMap;
}
void SetLoadedAsData(PRBool aLoadedAsData) { mLoadedAsData = aLoadedAsData; }
nsresult CloneDocHelper(nsDocument* clone) const;
@ -1087,6 +1211,8 @@ private:
nsTArray<nsRefPtr<nsFrameLoader> > mFinalizableFrameLoaders;
nsRevocableEventPtr<nsRunnableMethod<nsDocument> > mPendingTitleChangeEvent;
nsExternalResourceMap mExternalResourceMap;
};
#endif /* nsDocument_h___ */

Просмотреть файл

@ -727,6 +727,11 @@ nsFrameLoader::EnsureDocShell()
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());

Просмотреть файл

@ -54,6 +54,7 @@ REQUIRES = \
thebes \
dom \
webshell \
docshell \
htmlparser \
necko \
pref \

Просмотреть файл

@ -2488,7 +2488,7 @@ nsXULElement::HideWindowChrome(PRBool aShouldHide)
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();
@ -2522,7 +2522,7 @@ nsXULElement::SetTitlebarColor(nscolor aColor, PRBool aActive)
}
// 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) {

Просмотреть файл

@ -817,7 +817,11 @@ DocumentViewerImpl::InitInternal(nsIWidget* aParentWidget,
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
@ -1241,12 +1245,12 @@ DocumentViewerImpl::Open(nsISupports *aState, nsISHEntry *aSHEntry)
nsRect bounds;
mWindow->GetBounds(bounds);
nsresult rv = InitInternal(mParentWidget, aState, bounds, PR_FALSE, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
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 (mPresShell)
mPresShell->SetForwardingContainer(nsnull);
@ -2096,13 +2100,14 @@ DocumentViewerImpl::CreateStyleSet(nsIDocument* aDocument,
// 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 {
@ -2114,7 +2119,9 @@ DocumentViewerImpl::CreateStyleSet(nsIDocument* aDocument,
// 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;
@ -2291,6 +2298,23 @@ 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);
@ -2679,6 +2703,38 @@ SetChildFullZoom(nsIMarkupDocumentViewer* aChild, void* 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)
{
@ -2701,6 +2757,9 @@ DocumentViewerImpl::SetTextZoom(float aTextZoom)
pc->SetTextZoom(aTextZoom);
}
// And do the external resources
mDocument->EnumerateExternalResources(SetExtResourceTextZoom, &ZoomInfo);
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
return NS_OK;
@ -2732,6 +2791,9 @@ DocumentViewerImpl::SetFullZoom(float aFullZoom)
pc->SetFullZoom(aFullZoom);
}
// And do the external resources
mDocument->EnumerateExternalResources(SetExtResourceFullZoom, &ZoomInfo);
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
return NS_OK;

Просмотреть файл

@ -555,9 +555,11 @@ nsSubDocumentFrame::Reflow(nsPresContext* aPresContext,
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);

Просмотреть файл

@ -609,7 +609,9 @@ 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));
@ -620,7 +622,9 @@ nsObjectFrame::Init(nsIContent* aContent,
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.
@ -1718,6 +1722,7 @@ nsObjectFrame::Instantiate(const char* aMimeType, nsIURI* aURI)
return rv;
mInstanceOwner->SetPluginHost(pluginHost);
NS_ASSERTION(!mPreventInstantiation, "Say what?");
mPreventInstantiation = PR_TRUE;
rv = InstantiatePlugin(pluginHost, aMimeType, aURI);