Bumping manifests a=b2g-bump
[gecko.git] / dom / xbl / nsXBLDocumentInfo.cpp
bloba3dd2ad7e7e4eaa1d5574afbe416b5b035b72570
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/DebugOnly.h"
9 #include "nsXBLDocumentInfo.h"
10 #include "nsIDocument.h"
11 #include "nsXBLPrototypeBinding.h"
12 #include "nsIScriptObjectPrincipal.h"
13 #include "nsIScriptContext.h"
14 #include "nsIDOMDocument.h"
15 #include "nsIDOMScriptObjectFactory.h"
16 #include "jsapi.h"
17 #include "jsfriendapi.h"
18 #include "nsIURI.h"
19 #include "nsIConsoleService.h"
20 #include "nsIScriptError.h"
21 #include "nsIChromeRegistry.h"
22 #include "nsIPrincipal.h"
23 #include "nsJSPrincipals.h"
24 #include "nsIScriptSecurityManager.h"
25 #include "nsContentUtils.h"
26 #include "nsDOMJSUtils.h"
27 #include "mozilla/Services.h"
28 #include "xpcpublic.h"
29 #include "mozilla/scache/StartupCache.h"
30 #include "mozilla/scache/StartupCacheUtils.h"
31 #include "nsCCUncollectableMarker.h"
32 #include "mozilla/dom/BindingUtils.h"
33 #include "mozilla/dom/URL.h"
35 using namespace mozilla;
36 using namespace mozilla::scache;
37 using namespace mozilla::dom;
39 static const char kXBLCachePrefix[] = "xblcache";
41 /* Implementation file */
43 static PLDHashOperator
44 TraverseProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
46 nsCycleCollectionTraversalCallback *cb =
47 static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
48 aProto->Traverse(*cb);
49 return PL_DHASH_NEXT;
52 static PLDHashOperator
53 UnlinkProto(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
55 aProto->Unlink();
56 return PL_DHASH_NEXT;
59 struct ProtoTracer
61 const TraceCallbacks &mCallbacks;
62 void *mClosure;
65 static PLDHashOperator
66 TraceProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
68 ProtoTracer* closure = static_cast<ProtoTracer*>(aClosure);
69 aProto->Trace(closure->mCallbacks, closure->mClosure);
70 return PL_DHASH_NEXT;
73 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLDocumentInfo)
75 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLDocumentInfo)
76 if (tmp->mBindingTable) {
77 tmp->mBindingTable->EnumerateRead(UnlinkProto, nullptr);
79 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
80 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
81 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLDocumentInfo)
82 if (tmp->mDocument &&
83 nsCCUncollectableMarker::InGeneration(cb, tmp->mDocument->GetMarkedCCGeneration())) {
84 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
85 return NS_SUCCESS_INTERRUPTED_TRAVERSE;
87 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
88 if (tmp->mBindingTable) {
89 tmp->mBindingTable->EnumerateRead(TraverseProtos, &cb);
91 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
92 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
93 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXBLDocumentInfo)
94 if (tmp->mBindingTable) {
95 ProtoTracer closure = { aCallbacks, aClosure };
96 tmp->mBindingTable->EnumerateRead(TraceProtos, &closure);
98 NS_IMPL_CYCLE_COLLECTION_TRACE_END
100 static void
101 UnmarkXBLJSObject(JS::GCCellPtr aPtr, const char* aName, void* aClosure)
103 JS::ExposeObjectToActiveJS(aPtr.toObject());
106 static PLDHashOperator
107 UnmarkProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
109 aProto->Trace(TraceCallbackFunc(UnmarkXBLJSObject), nullptr);
110 return PL_DHASH_NEXT;
113 void
114 nsXBLDocumentInfo::MarkInCCGeneration(uint32_t aGeneration)
116 if (mDocument) {
117 mDocument->MarkUncollectableForCCGeneration(aGeneration);
119 // Unmark any JS we hold
120 if (mBindingTable) {
121 mBindingTable->EnumerateRead(UnmarkProtos, nullptr);
125 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXBLDocumentInfo)
126 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
127 NS_INTERFACE_MAP_ENTRY(nsISupports)
128 NS_INTERFACE_MAP_END
130 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXBLDocumentInfo)
131 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXBLDocumentInfo)
133 nsXBLDocumentInfo::nsXBLDocumentInfo(nsIDocument* aDocument)
134 : mDocument(aDocument),
135 mScriptAccess(true),
136 mIsChrome(false),
137 mFirstBinding(nullptr)
139 nsIURI* uri = aDocument->GetDocumentURI();
140 if (IsChromeURI(uri)) {
141 // Cache whether or not this chrome XBL can execute scripts.
142 nsCOMPtr<nsIXULChromeRegistry> reg =
143 mozilla::services::GetXULChromeRegistryService();
144 if (reg) {
145 bool allow = true;
146 reg->AllowScriptsForPackage(uri, &allow);
147 mScriptAccess = allow;
149 mIsChrome = true;
150 } else {
151 // If this binding isn't running with system principal, then it's running
152 // from a remote-XUL whitelisted domain. This is already a not-really-
153 // supported configuration (among other things, we don't use XBL scopes in
154 // that configuration for compatibility reasons). But we should still at
155 // least make an effort to prevent binding code from running if content
156 // script is disabled or if the source domain is blacklisted (since the
157 // source domain for remote XBL must always be the same as the source domain
158 // of the bound content).
160 // If we just ask the binding document if script is enabled, it will
161 // discover that it has no inner window, and return false. So instead, we
162 // short-circuit the normal compartment-managed script-disabling machinery,
163 // and query the policy for the URI directly.
164 bool allow;
165 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
166 nsresult rv = ssm->PolicyAllowsScript(uri, &allow);
167 mScriptAccess = NS_SUCCEEDED(rv) && allow;
171 nsXBLDocumentInfo::~nsXBLDocumentInfo()
173 mozilla::DropJSObjects(this);
176 nsXBLPrototypeBinding*
177 nsXBLDocumentInfo::GetPrototypeBinding(const nsACString& aRef)
179 if (!mBindingTable)
180 return nullptr;
182 if (aRef.IsEmpty()) {
183 // Return our first binding
184 return mFirstBinding;
187 return mBindingTable->Get(aRef);
190 nsresult
191 nsXBLDocumentInfo::SetPrototypeBinding(const nsACString& aRef, nsXBLPrototypeBinding* aBinding)
193 if (!mBindingTable) {
194 mBindingTable = new nsClassHashtable<nsCStringHashKey, nsXBLPrototypeBinding>();
195 mozilla::HoldJSObjects(this);
198 NS_ENSURE_STATE(!mBindingTable->Get(aRef));
199 mBindingTable->Put(aRef, aBinding);
201 return NS_OK;
204 void
205 nsXBLDocumentInfo::RemovePrototypeBinding(const nsACString& aRef)
207 if (mBindingTable) {
208 nsAutoPtr<nsXBLPrototypeBinding> bindingToRemove;
209 mBindingTable->RemoveAndForget(aRef, bindingToRemove);
211 // We do not want to destroy the binding, so just forget it.
212 bindingToRemove.forget();
216 // Callback to enumerate over the bindings from this document and write them
217 // out to the cache.
218 static PLDHashOperator
219 WriteBinding(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
221 aProto->Write((nsIObjectOutputStream*)aClosure);
223 return PL_DHASH_NEXT;
226 // static
227 nsresult
228 nsXBLDocumentInfo::ReadPrototypeBindings(nsIURI* aURI, nsXBLDocumentInfo** aDocInfo)
230 *aDocInfo = nullptr;
232 nsAutoCString spec(kXBLCachePrefix);
233 nsresult rv = PathifyURI(aURI, spec);
234 NS_ENSURE_SUCCESS(rv, rv);
236 StartupCache* startupCache = StartupCache::GetSingleton();
237 NS_ENSURE_TRUE(startupCache, NS_ERROR_FAILURE);
239 nsAutoArrayPtr<char> buf;
240 uint32_t len;
241 rv = startupCache->GetBuffer(spec.get(), getter_Transfers(buf), &len);
242 // GetBuffer will fail if the binding is not in the cache.
243 if (NS_FAILED(rv))
244 return rv;
246 nsCOMPtr<nsIObjectInputStream> stream;
247 rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(stream));
248 NS_ENSURE_SUCCESS(rv, rv);
249 buf.forget();
251 // The file compatibility.ini stores the build id. This is checked in
252 // nsAppRunner.cpp and will delete the cache if a different build is
253 // present. However, we check that the version matches here to be safe.
254 uint32_t version;
255 rv = stream->Read32(&version);
256 NS_ENSURE_SUCCESS(rv, rv);
257 if (version != XBLBinding_Serialize_Version) {
258 // The version that exists is different than expected, likely created with a
259 // different build, so invalidate the cache.
260 startupCache->InvalidateCache();
261 return NS_ERROR_NOT_AVAILABLE;
264 nsCOMPtr<nsIPrincipal> principal;
265 nsContentUtils::GetSecurityManager()->
266 GetSystemPrincipal(getter_AddRefs(principal));
268 nsCOMPtr<nsIDOMDocument> domdoc;
269 rv = NS_NewXBLDocument(getter_AddRefs(domdoc), aURI, nullptr, principal);
270 NS_ENSURE_SUCCESS(rv, rv);
272 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
273 NS_ASSERTION(doc, "Must have a document!");
274 nsRefPtr<nsXBLDocumentInfo> docInfo = new nsXBLDocumentInfo(doc);
276 while (1) {
277 uint8_t flags;
278 nsresult rv = stream->Read8(&flags);
279 NS_ENSURE_SUCCESS(rv, rv);
280 if (flags == XBLBinding_Serialize_NoMoreBindings)
281 break;
283 rv = nsXBLPrototypeBinding::ReadNewBinding(stream, docInfo, doc, flags);
284 if (NS_FAILED(rv)) {
285 return rv;
289 docInfo.swap(*aDocInfo);
290 return NS_OK;
293 nsresult
294 nsXBLDocumentInfo::WritePrototypeBindings()
296 // Only write out bindings with the system principal
297 if (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal()))
298 return NS_OK;
300 nsAutoCString spec(kXBLCachePrefix);
301 nsresult rv = PathifyURI(DocumentURI(), spec);
302 NS_ENSURE_SUCCESS(rv, rv);
304 StartupCache* startupCache = StartupCache::GetSingleton();
305 NS_ENSURE_TRUE(startupCache, rv);
307 nsCOMPtr<nsIObjectOutputStream> stream;
308 nsCOMPtr<nsIStorageStream> storageStream;
309 rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(stream),
310 getter_AddRefs(storageStream),
311 true);
312 NS_ENSURE_SUCCESS(rv, rv);
314 rv = stream->Write32(XBLBinding_Serialize_Version);
315 NS_ENSURE_SUCCESS(rv, rv);
317 if (mBindingTable) {
318 mBindingTable->EnumerateRead(WriteBinding, stream);
321 // write a end marker at the end
322 rv = stream->Write8(XBLBinding_Serialize_NoMoreBindings);
323 NS_ENSURE_SUCCESS(rv, rv);
325 stream->Close();
326 NS_ENSURE_SUCCESS(rv, rv);
328 uint32_t len;
329 nsAutoArrayPtr<char> buf;
330 rv = NewBufferFromStorageStream(storageStream, getter_Transfers(buf), &len);
331 NS_ENSURE_SUCCESS(rv, rv);
333 return startupCache->PutBuffer(spec.get(), buf, len);
336 void
337 nsXBLDocumentInfo::SetFirstPrototypeBinding(nsXBLPrototypeBinding* aBinding)
339 mFirstBinding = aBinding;
342 static PLDHashOperator
343 FlushScopedSkinSheets(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
345 aProto->FlushSkinSheets();
346 return PL_DHASH_NEXT;
349 void
350 nsXBLDocumentInfo::FlushSkinStylesheets()
352 if (mBindingTable) {
353 mBindingTable->EnumerateRead(FlushScopedSkinSheets, nullptr);
357 #ifdef DEBUG
358 void
359 AssertInCompilationScope()
361 AutoJSContext cx;
362 MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx));
364 #endif