1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsReferencedElement.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,240 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=78: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsReferencedElement.h" 1.11 +#include "nsContentUtils.h" 1.12 +#include "nsIURI.h" 1.13 +#include "nsBindingManager.h" 1.14 +#include "nsEscape.h" 1.15 +#include "nsXBLPrototypeBinding.h" 1.16 +#include "nsIDOMNode.h" 1.17 +#include "nsIDOMElement.h" 1.18 +#include "nsCycleCollectionParticipant.h" 1.19 + 1.20 +void 1.21 +nsReferencedElement::Reset(nsIContent* aFromContent, nsIURI* aURI, 1.22 + bool aWatch, bool aReferenceImage) 1.23 +{ 1.24 + NS_ABORT_IF_FALSE(aFromContent, "Reset() expects non-null content pointer"); 1.25 + 1.26 + Unlink(); 1.27 + 1.28 + if (!aURI) 1.29 + return; 1.30 + 1.31 + nsAutoCString refPart; 1.32 + aURI->GetRef(refPart); 1.33 + // Unescape %-escapes in the reference. The result will be in the 1.34 + // origin charset of the URL, hopefully... 1.35 + NS_UnescapeURL(refPart); 1.36 + 1.37 + nsAutoCString charset; 1.38 + aURI->GetOriginCharset(charset); 1.39 + nsAutoString ref; 1.40 + nsresult rv = nsContentUtils::ConvertStringFromEncoding(charset, 1.41 + refPart, 1.42 + ref); 1.43 + if (NS_FAILED(rv)) { 1.44 + // XXX Eww. If fallible malloc failed, using a conversion method that 1.45 + // assumes UTF-8 and doesn't handle UTF-8 errors. 1.46 + // https://bugzilla.mozilla.org/show_bug.cgi?id=951082 1.47 + CopyUTF8toUTF16(refPart, ref); 1.48 + } 1.49 + if (ref.IsEmpty()) 1.50 + return; 1.51 + 1.52 + // Get the current document 1.53 + nsIDocument *doc = aFromContent->GetCurrentDoc(); 1.54 + if (!doc) 1.55 + return; 1.56 + 1.57 + nsIContent* bindingParent = aFromContent->GetBindingParent(); 1.58 + if (bindingParent) { 1.59 + nsXBLBinding* binding = bindingParent->GetXBLBinding(); 1.60 + if (binding) { 1.61 + bool isEqualExceptRef; 1.62 + rv = aURI->EqualsExceptRef(binding->PrototypeBinding()->DocURI(), 1.63 + &isEqualExceptRef); 1.64 + if (NS_SUCCEEDED(rv) && isEqualExceptRef) { 1.65 + // XXX sXBL/XBL2 issue 1.66 + // Our content is an anonymous XBL element from a binding inside the 1.67 + // same document that the referenced URI points to. In order to avoid 1.68 + // the risk of ID collisions we restrict ourselves to anonymous 1.69 + // elements from this binding; specifically, URIs that are relative to 1.70 + // the binding document should resolve to the copy of the target 1.71 + // element that has been inserted into the bound document. 1.72 + // If the URI points to a different document we don't need this 1.73 + // restriction. 1.74 + nsINodeList* anonymousChildren = 1.75 + doc->BindingManager()->GetAnonymousNodesFor(bindingParent); 1.76 + 1.77 + if (anonymousChildren) { 1.78 + uint32_t length; 1.79 + anonymousChildren->GetLength(&length); 1.80 + for (uint32_t i = 0; i < length && !mElement; ++i) { 1.81 + mElement = 1.82 + nsContentUtils::MatchElementId(anonymousChildren->Item(i), ref); 1.83 + } 1.84 + } 1.85 + 1.86 + // We don't have watching working yet for XBL, so bail out here. 1.87 + return; 1.88 + } 1.89 + } 1.90 + } 1.91 + 1.92 + bool isEqualExceptRef; 1.93 + rv = aURI->EqualsExceptRef(doc->GetDocumentURI(), &isEqualExceptRef); 1.94 + if (NS_FAILED(rv) || !isEqualExceptRef) { 1.95 + nsRefPtr<nsIDocument::ExternalResourceLoad> load; 1.96 + doc = doc->RequestExternalResource(aURI, aFromContent, 1.97 + getter_AddRefs(load)); 1.98 + if (!doc) { 1.99 + if (!load || !aWatch) { 1.100 + // Nothing will ever happen here 1.101 + return; 1.102 + } 1.103 + 1.104 + DocumentLoadNotification* observer = 1.105 + new DocumentLoadNotification(this, ref); 1.106 + mPendingNotification = observer; 1.107 + if (observer) { 1.108 + load->AddObserver(observer); 1.109 + } 1.110 + // Keep going so we set up our watching stuff a bit 1.111 + } 1.112 + } 1.113 + 1.114 + if (aWatch) { 1.115 + nsCOMPtr<nsIAtom> atom = do_GetAtom(ref); 1.116 + if (!atom) 1.117 + return; 1.118 + atom.swap(mWatchID); 1.119 + } 1.120 + 1.121 + mReferencingImage = aReferenceImage; 1.122 + 1.123 + HaveNewDocument(doc, aWatch, ref); 1.124 +} 1.125 + 1.126 +void 1.127 +nsReferencedElement::ResetWithID(nsIContent* aFromContent, const nsString& aID, 1.128 + bool aWatch) 1.129 +{ 1.130 + nsIDocument *doc = aFromContent->GetCurrentDoc(); 1.131 + if (!doc) 1.132 + return; 1.133 + 1.134 + // XXX Need to take care of XBL/XBL2 1.135 + 1.136 + if (aWatch) { 1.137 + nsCOMPtr<nsIAtom> atom = do_GetAtom(aID); 1.138 + if (!atom) 1.139 + return; 1.140 + atom.swap(mWatchID); 1.141 + } 1.142 + 1.143 + mReferencingImage = false; 1.144 + 1.145 + HaveNewDocument(doc, aWatch, aID); 1.146 +} 1.147 + 1.148 +void 1.149 +nsReferencedElement::HaveNewDocument(nsIDocument* aDocument, bool aWatch, 1.150 + const nsString& aRef) 1.151 +{ 1.152 + if (aWatch) { 1.153 + mWatchDocument = aDocument; 1.154 + if (mWatchDocument) { 1.155 + mElement = mWatchDocument->AddIDTargetObserver(mWatchID, Observe, this, 1.156 + mReferencingImage); 1.157 + } 1.158 + return; 1.159 + } 1.160 + 1.161 + if (!aDocument) { 1.162 + return; 1.163 + } 1.164 + 1.165 + Element *e = mReferencingImage ? aDocument->LookupImageElement(aRef) : 1.166 + aDocument->GetElementById(aRef); 1.167 + if (e) { 1.168 + mElement = e; 1.169 + } 1.170 +} 1.171 + 1.172 +void 1.173 +nsReferencedElement::Traverse(nsCycleCollectionTraversalCallback* aCB) 1.174 +{ 1.175 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCB, "mWatchDocument"); 1.176 + aCB->NoteXPCOMChild(mWatchDocument); 1.177 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCB, "mContent"); 1.178 + aCB->NoteXPCOMChild(mElement); 1.179 +} 1.180 + 1.181 +void 1.182 +nsReferencedElement::Unlink() 1.183 +{ 1.184 + if (mWatchDocument && mWatchID) { 1.185 + mWatchDocument->RemoveIDTargetObserver(mWatchID, Observe, this, 1.186 + mReferencingImage); 1.187 + } 1.188 + if (mPendingNotification) { 1.189 + mPendingNotification->Clear(); 1.190 + mPendingNotification = nullptr; 1.191 + } 1.192 + mWatchDocument = nullptr; 1.193 + mWatchID = nullptr; 1.194 + mElement = nullptr; 1.195 + mReferencingImage = false; 1.196 +} 1.197 + 1.198 +bool 1.199 +nsReferencedElement::Observe(Element* aOldElement, 1.200 + Element* aNewElement, void* aData) 1.201 +{ 1.202 + nsReferencedElement* p = static_cast<nsReferencedElement*>(aData); 1.203 + if (p->mPendingNotification) { 1.204 + p->mPendingNotification->SetTo(aNewElement); 1.205 + } else { 1.206 + NS_ASSERTION(aOldElement == p->mElement, "Failed to track content!"); 1.207 + ChangeNotification* watcher = 1.208 + new ChangeNotification(p, aOldElement, aNewElement); 1.209 + p->mPendingNotification = watcher; 1.210 + nsContentUtils::AddScriptRunner(watcher); 1.211 + } 1.212 + bool keepTracking = p->IsPersistent(); 1.213 + if (!keepTracking) { 1.214 + p->mWatchDocument = nullptr; 1.215 + p->mWatchID = nullptr; 1.216 + } 1.217 + return keepTracking; 1.218 +} 1.219 + 1.220 +NS_IMPL_ISUPPORTS_INHERITED0(nsReferencedElement::ChangeNotification, 1.221 + nsRunnable) 1.222 + 1.223 +NS_IMPL_ISUPPORTS(nsReferencedElement::DocumentLoadNotification, 1.224 + nsIObserver) 1.225 + 1.226 +NS_IMETHODIMP 1.227 +nsReferencedElement::DocumentLoadNotification::Observe(nsISupports* aSubject, 1.228 + const char* aTopic, 1.229 + const char16_t* aData) 1.230 +{ 1.231 + NS_ASSERTION(PL_strcmp(aTopic, "external-resource-document-created") == 0, 1.232 + "Unexpected topic"); 1.233 + if (mTarget) { 1.234 + nsCOMPtr<nsIDocument> doc = do_QueryInterface(aSubject); 1.235 + mTarget->mPendingNotification = nullptr; 1.236 + NS_ASSERTION(!mTarget->mElement, "Why do we have content here?"); 1.237 + // If we got here, that means we had Reset() called with aWatch == 1.238 + // true. So keep watching if IsPersistent(). 1.239 + mTarget->HaveNewDocument(doc, mTarget->IsPersistent(), mRef); 1.240 + mTarget->ElementChanged(nullptr, mTarget->mElement); 1.241 + } 1.242 + return NS_OK; 1.243 +}