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