|
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/. */ |
|
6 |
|
7 #include "mozilla/DebugOnly.h" |
|
8 |
|
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 "nsCxPusher.h" |
|
27 #include "nsDOMJSUtils.h" |
|
28 #include "mozilla/Services.h" |
|
29 #include "xpcpublic.h" |
|
30 #include "mozilla/scache/StartupCache.h" |
|
31 #include "mozilla/scache/StartupCacheUtils.h" |
|
32 #include "nsCCUncollectableMarker.h" |
|
33 #include "mozilla/dom/BindingUtils.h" |
|
34 #include "mozilla/dom/URL.h" |
|
35 |
|
36 using namespace mozilla; |
|
37 using namespace mozilla::scache; |
|
38 using namespace mozilla::dom; |
|
39 |
|
40 static const char kXBLCachePrefix[] = "xblcache"; |
|
41 |
|
42 /* Implementation file */ |
|
43 |
|
44 static PLDHashOperator |
|
45 TraverseProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure) |
|
46 { |
|
47 nsCycleCollectionTraversalCallback *cb = |
|
48 static_cast<nsCycleCollectionTraversalCallback*>(aClosure); |
|
49 aProto->Traverse(*cb); |
|
50 return PL_DHASH_NEXT; |
|
51 } |
|
52 |
|
53 static PLDHashOperator |
|
54 UnlinkProtoJSObjects(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure) |
|
55 { |
|
56 aProto->UnlinkJSObjects(); |
|
57 return PL_DHASH_NEXT; |
|
58 } |
|
59 |
|
60 struct ProtoTracer |
|
61 { |
|
62 const TraceCallbacks &mCallbacks; |
|
63 void *mClosure; |
|
64 }; |
|
65 |
|
66 static PLDHashOperator |
|
67 TraceProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure) |
|
68 { |
|
69 ProtoTracer* closure = static_cast<ProtoTracer*>(aClosure); |
|
70 aProto->Trace(closure->mCallbacks, closure->mClosure); |
|
71 return PL_DHASH_NEXT; |
|
72 } |
|
73 |
|
74 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLDocumentInfo) |
|
75 |
|
76 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLDocumentInfo) |
|
77 if (tmp->mBindingTable) { |
|
78 tmp->mBindingTable->EnumerateRead(UnlinkProtoJSObjects, nullptr); |
|
79 } |
|
80 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) |
|
81 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
82 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLDocumentInfo) |
|
83 if (tmp->mDocument && |
|
84 nsCCUncollectableMarker::InGeneration(cb, tmp->mDocument->GetMarkedCCGeneration())) { |
|
85 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
86 return NS_SUCCESS_INTERRUPTED_TRAVERSE; |
|
87 } |
|
88 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) |
|
89 if (tmp->mBindingTable) { |
|
90 tmp->mBindingTable->EnumerateRead(TraverseProtos, &cb); |
|
91 } |
|
92 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
93 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
94 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXBLDocumentInfo) |
|
95 if (tmp->mBindingTable) { |
|
96 ProtoTracer closure = { aCallbacks, aClosure }; |
|
97 tmp->mBindingTable->EnumerateRead(TraceProtos, &closure); |
|
98 } |
|
99 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
100 |
|
101 static void |
|
102 UnmarkXBLJSObject(void* aP, const char* aName, void* aClosure) |
|
103 { |
|
104 JS::ExposeObjectToActiveJS(static_cast<JSObject*>(aP)); |
|
105 } |
|
106 |
|
107 static PLDHashOperator |
|
108 UnmarkProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure) |
|
109 { |
|
110 aProto->Trace(TraceCallbackFunc(UnmarkXBLJSObject), nullptr); |
|
111 return PL_DHASH_NEXT; |
|
112 } |
|
113 |
|
114 void |
|
115 nsXBLDocumentInfo::MarkInCCGeneration(uint32_t aGeneration) |
|
116 { |
|
117 if (mDocument) { |
|
118 mDocument->MarkUncollectableForCCGeneration(aGeneration); |
|
119 } |
|
120 // Unmark any JS we hold |
|
121 if (mBindingTable) { |
|
122 mBindingTable->EnumerateRead(UnmarkProtos, nullptr); |
|
123 } |
|
124 } |
|
125 |
|
126 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXBLDocumentInfo) |
|
127 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) |
|
128 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
129 NS_INTERFACE_MAP_END |
|
130 |
|
131 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXBLDocumentInfo) |
|
132 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXBLDocumentInfo) |
|
133 |
|
134 nsXBLDocumentInfo::nsXBLDocumentInfo(nsIDocument* aDocument) |
|
135 : mDocument(aDocument), |
|
136 mScriptAccess(true), |
|
137 mIsChrome(false), |
|
138 mFirstBinding(nullptr) |
|
139 { |
|
140 nsIURI* uri = aDocument->GetDocumentURI(); |
|
141 if (IsChromeURI(uri)) { |
|
142 // Cache whether or not this chrome XBL can execute scripts. |
|
143 nsCOMPtr<nsIXULChromeRegistry> reg = |
|
144 mozilla::services::GetXULChromeRegistryService(); |
|
145 if (reg) { |
|
146 bool allow = true; |
|
147 reg->AllowScriptsForPackage(uri, &allow); |
|
148 mScriptAccess = allow; |
|
149 } |
|
150 mIsChrome = true; |
|
151 } else { |
|
152 // If this binding isn't running with system principal, then it's running |
|
153 // from a remote-XUL whitelisted domain. This is already a not-really- |
|
154 // supported configuration (among other things, we don't use XBL scopes in |
|
155 // that configuration for compatibility reasons). But we should still at |
|
156 // least make an effort to prevent binding code from running if content |
|
157 // script is disabled or if the source domain is blacklisted (since the |
|
158 // source domain for remote XBL must always be the same as the source domain |
|
159 // of the bound content). |
|
160 // |
|
161 // If we just ask the binding document if script is enabled, it will |
|
162 // discover that it has no inner window, and return false. So instead, we |
|
163 // short-circuit the normal compartment-managed script-disabling machinery, |
|
164 // and query the policy for the URI directly. |
|
165 bool allow; |
|
166 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); |
|
167 nsresult rv = ssm->PolicyAllowsScript(uri, &allow); |
|
168 mScriptAccess = NS_SUCCEEDED(rv) && allow; |
|
169 } |
|
170 } |
|
171 |
|
172 nsXBLDocumentInfo::~nsXBLDocumentInfo() |
|
173 { |
|
174 mozilla::DropJSObjects(this); |
|
175 } |
|
176 |
|
177 nsXBLPrototypeBinding* |
|
178 nsXBLDocumentInfo::GetPrototypeBinding(const nsACString& aRef) |
|
179 { |
|
180 if (!mBindingTable) |
|
181 return nullptr; |
|
182 |
|
183 if (aRef.IsEmpty()) { |
|
184 // Return our first binding |
|
185 return mFirstBinding; |
|
186 } |
|
187 |
|
188 return mBindingTable->Get(aRef); |
|
189 } |
|
190 |
|
191 nsresult |
|
192 nsXBLDocumentInfo::SetPrototypeBinding(const nsACString& aRef, nsXBLPrototypeBinding* aBinding) |
|
193 { |
|
194 if (!mBindingTable) { |
|
195 mBindingTable = new nsClassHashtable<nsCStringHashKey, nsXBLPrototypeBinding>(); |
|
196 mozilla::HoldJSObjects(this); |
|
197 } |
|
198 |
|
199 NS_ENSURE_STATE(!mBindingTable->Get(aRef)); |
|
200 mBindingTable->Put(aRef, aBinding); |
|
201 |
|
202 return NS_OK; |
|
203 } |
|
204 |
|
205 void |
|
206 nsXBLDocumentInfo::RemovePrototypeBinding(const nsACString& aRef) |
|
207 { |
|
208 if (mBindingTable) { |
|
209 nsAutoPtr<nsXBLPrototypeBinding> bindingToRemove; |
|
210 mBindingTable->RemoveAndForget(aRef, bindingToRemove); |
|
211 |
|
212 // We do not want to destroy the binding, so just forget it. |
|
213 bindingToRemove.forget(); |
|
214 } |
|
215 } |
|
216 |
|
217 // Callback to enumerate over the bindings from this document and write them |
|
218 // out to the cache. |
|
219 static PLDHashOperator |
|
220 WriteBinding(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure) |
|
221 { |
|
222 aProto->Write((nsIObjectOutputStream*)aClosure); |
|
223 |
|
224 return PL_DHASH_NEXT; |
|
225 } |
|
226 |
|
227 // static |
|
228 nsresult |
|
229 nsXBLDocumentInfo::ReadPrototypeBindings(nsIURI* aURI, nsXBLDocumentInfo** aDocInfo) |
|
230 { |
|
231 *aDocInfo = nullptr; |
|
232 |
|
233 nsAutoCString spec(kXBLCachePrefix); |
|
234 nsresult rv = PathifyURI(aURI, spec); |
|
235 NS_ENSURE_SUCCESS(rv, rv); |
|
236 |
|
237 StartupCache* startupCache = StartupCache::GetSingleton(); |
|
238 NS_ENSURE_TRUE(startupCache, NS_ERROR_FAILURE); |
|
239 |
|
240 nsAutoArrayPtr<char> buf; |
|
241 uint32_t len; |
|
242 rv = startupCache->GetBuffer(spec.get(), getter_Transfers(buf), &len); |
|
243 // GetBuffer will fail if the binding is not in the cache. |
|
244 if (NS_FAILED(rv)) |
|
245 return rv; |
|
246 |
|
247 nsCOMPtr<nsIObjectInputStream> stream; |
|
248 rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(stream)); |
|
249 NS_ENSURE_SUCCESS(rv, rv); |
|
250 buf.forget(); |
|
251 |
|
252 // The file compatibility.ini stores the build id. This is checked in |
|
253 // nsAppRunner.cpp and will delete the cache if a different build is |
|
254 // present. However, we check that the version matches here to be safe. |
|
255 uint32_t version; |
|
256 rv = stream->Read32(&version); |
|
257 NS_ENSURE_SUCCESS(rv, rv); |
|
258 if (version != XBLBinding_Serialize_Version) { |
|
259 // The version that exists is different than expected, likely created with a |
|
260 // different build, so invalidate the cache. |
|
261 startupCache->InvalidateCache(); |
|
262 return NS_ERROR_NOT_AVAILABLE; |
|
263 } |
|
264 |
|
265 nsCOMPtr<nsIPrincipal> principal; |
|
266 nsContentUtils::GetSecurityManager()-> |
|
267 GetSystemPrincipal(getter_AddRefs(principal)); |
|
268 |
|
269 nsCOMPtr<nsIDOMDocument> domdoc; |
|
270 rv = NS_NewXBLDocument(getter_AddRefs(domdoc), aURI, nullptr, principal); |
|
271 NS_ENSURE_SUCCESS(rv, rv); |
|
272 |
|
273 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc); |
|
274 NS_ASSERTION(doc, "Must have a document!"); |
|
275 nsRefPtr<nsXBLDocumentInfo> docInfo = new nsXBLDocumentInfo(doc); |
|
276 |
|
277 while (1) { |
|
278 uint8_t flags; |
|
279 nsresult rv = stream->Read8(&flags); |
|
280 NS_ENSURE_SUCCESS(rv, rv); |
|
281 if (flags == XBLBinding_Serialize_NoMoreBindings) |
|
282 break; |
|
283 |
|
284 rv = nsXBLPrototypeBinding::ReadNewBinding(stream, docInfo, doc, flags); |
|
285 if (NS_FAILED(rv)) { |
|
286 return rv; |
|
287 } |
|
288 } |
|
289 |
|
290 docInfo.swap(*aDocInfo); |
|
291 return NS_OK; |
|
292 } |
|
293 |
|
294 nsresult |
|
295 nsXBLDocumentInfo::WritePrototypeBindings() |
|
296 { |
|
297 // Only write out bindings with the system principal |
|
298 if (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal())) |
|
299 return NS_OK; |
|
300 |
|
301 nsAutoCString spec(kXBLCachePrefix); |
|
302 nsresult rv = PathifyURI(DocumentURI(), spec); |
|
303 NS_ENSURE_SUCCESS(rv, rv); |
|
304 |
|
305 StartupCache* startupCache = StartupCache::GetSingleton(); |
|
306 NS_ENSURE_TRUE(startupCache, rv); |
|
307 |
|
308 nsCOMPtr<nsIObjectOutputStream> stream; |
|
309 nsCOMPtr<nsIStorageStream> storageStream; |
|
310 rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(stream), |
|
311 getter_AddRefs(storageStream), |
|
312 true); |
|
313 NS_ENSURE_SUCCESS(rv, rv); |
|
314 |
|
315 rv = stream->Write32(XBLBinding_Serialize_Version); |
|
316 NS_ENSURE_SUCCESS(rv, rv); |
|
317 |
|
318 if (mBindingTable) { |
|
319 mBindingTable->EnumerateRead(WriteBinding, stream); |
|
320 } |
|
321 |
|
322 // write a end marker at the end |
|
323 rv = stream->Write8(XBLBinding_Serialize_NoMoreBindings); |
|
324 NS_ENSURE_SUCCESS(rv, rv); |
|
325 |
|
326 stream->Close(); |
|
327 NS_ENSURE_SUCCESS(rv, rv); |
|
328 |
|
329 uint32_t len; |
|
330 nsAutoArrayPtr<char> buf; |
|
331 rv = NewBufferFromStorageStream(storageStream, getter_Transfers(buf), &len); |
|
332 NS_ENSURE_SUCCESS(rv, rv); |
|
333 |
|
334 return startupCache->PutBuffer(spec.get(), buf, len); |
|
335 } |
|
336 |
|
337 void |
|
338 nsXBLDocumentInfo::SetFirstPrototypeBinding(nsXBLPrototypeBinding* aBinding) |
|
339 { |
|
340 mFirstBinding = aBinding; |
|
341 } |
|
342 |
|
343 static PLDHashOperator |
|
344 FlushScopedSkinSheets(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure) |
|
345 { |
|
346 aProto->FlushSkinSheets(); |
|
347 return PL_DHASH_NEXT; |
|
348 } |
|
349 |
|
350 void |
|
351 nsXBLDocumentInfo::FlushSkinStylesheets() |
|
352 { |
|
353 if (mBindingTable) { |
|
354 mBindingTable->EnumerateRead(FlushScopedSkinSheets, nullptr); |
|
355 } |
|
356 } |
|
357 |
|
358 #ifdef DEBUG |
|
359 void |
|
360 AssertInCompilationScope() |
|
361 { |
|
362 AutoJSContext cx; |
|
363 // Note - Inverting the order of these operands is a rooting hazard. |
|
364 MOZ_ASSERT(xpc::GetCompilationScope() == JS::CurrentGlobalOrNull(cx)); |
|
365 } |
|
366 #endif |