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"
17 #include "jsfriendapi.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
);
52 static PLDHashOperator
53 UnlinkProto(const nsACString
&aKey
, nsXBLPrototypeBinding
*aProto
, void* aClosure
)
61 const TraceCallbacks
&mCallbacks
;
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
);
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
)
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
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
;
114 nsXBLDocumentInfo::MarkInCCGeneration(uint32_t aGeneration
)
117 mDocument
->MarkUncollectableForCCGeneration(aGeneration
);
119 // Unmark any JS we hold
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
)
130 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXBLDocumentInfo
)
131 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXBLDocumentInfo
)
133 nsXBLDocumentInfo::nsXBLDocumentInfo(nsIDocument
* aDocument
)
134 : mDocument(aDocument
),
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();
146 reg
->AllowScriptsForPackage(uri
, &allow
);
147 mScriptAccess
= allow
;
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.
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
)
182 if (aRef
.IsEmpty()) {
183 // Return our first binding
184 return mFirstBinding
;
187 return mBindingTable
->Get(aRef
);
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
);
205 nsXBLDocumentInfo::RemovePrototypeBinding(const nsACString
& aRef
)
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
218 static PLDHashOperator
219 WriteBinding(const nsACString
&aKey
, nsXBLPrototypeBinding
*aProto
, void* aClosure
)
221 aProto
->Write((nsIObjectOutputStream
*)aClosure
);
223 return PL_DHASH_NEXT
;
228 nsXBLDocumentInfo::ReadPrototypeBindings(nsIURI
* aURI
, nsXBLDocumentInfo
** aDocInfo
)
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
;
241 rv
= startupCache
->GetBuffer(spec
.get(), getter_Transfers(buf
), &len
);
242 // GetBuffer will fail if the binding is not in the cache.
246 nsCOMPtr
<nsIObjectInputStream
> stream
;
247 rv
= NewObjectInputStreamFromBuffer(buf
, len
, getter_AddRefs(stream
));
248 NS_ENSURE_SUCCESS(rv
, rv
);
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.
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
);
278 nsresult rv
= stream
->Read8(&flags
);
279 NS_ENSURE_SUCCESS(rv
, rv
);
280 if (flags
== XBLBinding_Serialize_NoMoreBindings
)
283 rv
= nsXBLPrototypeBinding::ReadNewBinding(stream
, docInfo
, doc
, flags
);
289 docInfo
.swap(*aDocInfo
);
294 nsXBLDocumentInfo::WritePrototypeBindings()
296 // Only write out bindings with the system principal
297 if (!nsContentUtils::IsSystemPrincipal(mDocument
->NodePrincipal()))
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
),
312 NS_ENSURE_SUCCESS(rv
, rv
);
314 rv
= stream
->Write32(XBLBinding_Serialize_Version
);
315 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
326 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
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
;
350 nsXBLDocumentInfo::FlushSkinStylesheets()
353 mBindingTable
->EnumerateRead(FlushScopedSkinSheets
, nullptr);
359 AssertInCompilationScope()
362 MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx
));