Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/xbl/nsXBLDocumentInfo.cpp
Line
Count
Source (jump to first uncovered line)
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 "jsapi.h"
15
#include "jsfriendapi.h"
16
#include "nsIURI.h"
17
#include "nsIConsoleService.h"
18
#include "nsIScriptError.h"
19
#include "nsIChromeRegistry.h"
20
#include "nsIPrincipal.h"
21
#include "nsJSPrincipals.h"
22
#include "nsIScriptSecurityManager.h"
23
#include "nsContentUtils.h"
24
#include "nsDOMJSUtils.h"
25
#include "nsWindowSizes.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
0
    }
49
0
  }
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
0
  }
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
0
    }
63
0
  }
64
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
65
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXBLDocumentInfo)
66
0
  if (tmp->mBindingTable) {
67
0
    for (auto iter = tmp->mBindingTable->ConstIter();
68
0
         !iter.Done(); iter.Next()) {
69
0
      iter.UserData()->Trace(aCallbacks, aClosure);
70
0
    }
71
0
  }
72
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
73
74
static void
75
UnmarkXBLJSObject(JS::GCCellPtr aPtr, const char* aName, void* aClosure)
76
0
{
77
0
  JS::ExposeObjectToActiveJS(&aPtr.as<JSObject>());
78
0
}
79
80
void
81
nsXBLDocumentInfo::MarkInCCGeneration(uint32_t aGeneration)
82
0
{
83
0
  if (mDocument) {
84
0
    mDocument->MarkUncollectableForCCGeneration(aGeneration);
85
0
  }
86
0
  // 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
0
    }
91
0
  }
92
0
}
93
94
0
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
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXBLDocumentInfo)
100
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXBLDocumentInfo)
101
102
nsXBLDocumentInfo::nsXBLDocumentInfo(nsIDocument* aDocument)
103
  : mDocument(aDocument),
104
    mScriptAccess(true),
105
    mIsChrome(false),
106
    mFirstBinding(nullptr)
107
0
{
108
0
  nsIURI* uri = aDocument->GetDocumentURI();
109
0
  if (IsChromeURI(uri)) {
110
0
    // Cache whether or not this chrome XBL can execute scripts.
111
0
    nsCOMPtr<nsIXULChromeRegistry> reg =
112
0
      mozilla::services::GetXULChromeRegistryService();
113
0
    if (reg) {
114
0
      bool allow = true;
115
0
      reg->AllowScriptsForPackage(uri, &allow);
116
0
      mScriptAccess = allow;
117
0
    }
118
0
    mIsChrome = true;
119
0
  } else {
120
0
    // If this binding isn't running with system principal, then it's running
121
0
    // from a remote-XUL whitelisted domain. This is already a not-really-
122
0
    // supported configuration (among other things, we don't use XBL scopes in
123
0
    // that configuration for compatibility reasons). But we should still at
124
0
    // least make an effort to prevent binding code from running if content
125
0
    // script is disabled or if the source domain is blacklisted (since the
126
0
    // source domain for remote XBL must always be the same as the source domain
127
0
    // of the bound content).
128
0
    //
129
0
    // If we just ask the binding document if script is enabled, it will
130
0
    // discover that it has no inner window, and return false. So instead, we
131
0
    // short-circuit the normal compartment-managed script-disabling machinery,
132
0
    // and query the policy for the URI directly.
133
0
    bool allow;
134
0
    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
135
0
    nsresult rv = ssm->PolicyAllowsScript(uri, &allow);
136
0
    mScriptAccess = NS_SUCCEEDED(rv) && allow;
137
0
  }
138
0
}
139
140
nsXBLDocumentInfo::~nsXBLDocumentInfo()
141
0
{
142
0
  mozilla::DropJSObjects(this);
143
0
}
144
145
nsXBLPrototypeBinding*
146
nsXBLDocumentInfo::GetPrototypeBinding(const nsACString& aRef)
147
0
{
148
0
  if (!mBindingTable)
149
0
    return nullptr;
150
0
151
0
  if (aRef.IsEmpty()) {
152
0
    // Return our first binding
153
0
    return mFirstBinding;
154
0
  }
155
0
156
0
  return mBindingTable->Get(aRef);
157
0
}
158
159
nsresult
160
nsXBLDocumentInfo::SetPrototypeBinding(const nsACString& aRef, nsXBLPrototypeBinding* aBinding)
161
0
{
162
0
  if (!mBindingTable) {
163
0
    mBindingTable = new nsClassHashtable<nsCStringHashKey, nsXBLPrototypeBinding>();
164
0
    mozilla::HoldJSObjects(this);
165
0
  }
166
0
167
0
  NS_ENSURE_STATE(!mBindingTable->Get(aRef));
168
0
  mBindingTable->Put(aRef, aBinding);
169
0
170
0
  return NS_OK;
171
0
}
172
173
void
174
nsXBLDocumentInfo::RemovePrototypeBinding(const nsACString& aRef)
175
0
{
176
0
  if (mBindingTable) {
177
0
    nsAutoPtr<nsXBLPrototypeBinding> bindingToRemove;
178
0
    mBindingTable->Remove(aRef, &bindingToRemove);
179
0
180
0
    // We do not want to destroy the binding, so just forget it.
181
0
    bindingToRemove.forget();
182
0
  }
183
0
}
184
185
// static
186
nsresult
187
nsXBLDocumentInfo::ReadPrototypeBindings(nsIURI* aURI, nsXBLDocumentInfo** aDocInfo,
188
                                         nsIDocument* aBoundDocument)
189
0
{
190
0
  *aDocInfo = nullptr;
191
0
192
0
  nsAutoCString spec(kXBLCachePrefix);
193
0
  nsresult rv = PathifyURI(aURI, spec);
194
0
  NS_ENSURE_SUCCESS(rv, rv);
195
0
196
0
  StartupCache* startupCache = StartupCache::GetSingleton();
197
0
  if (!startupCache) {
198
0
    return NS_ERROR_FAILURE;
199
0
  }
200
0
201
0
  UniquePtr<char[]> buf;
202
0
  uint32_t len;
203
0
  rv = startupCache->GetBuffer(spec.get(), &buf, &len);
204
0
  // GetBuffer will fail if the binding is not in the cache.
205
0
  if (NS_FAILED(rv))
206
0
    return rv;
207
0
208
0
  nsCOMPtr<nsIObjectInputStream> stream;
209
0
  rv = NewObjectInputStreamFromBuffer(std::move(buf), len, getter_AddRefs(stream));
210
0
  NS_ENSURE_SUCCESS(rv, rv);
211
0
212
0
  // The file compatibility.ini stores the build id. This is checked in
213
0
  // nsAppRunner.cpp and will delete the cache if a different build is
214
0
  // present. However, we check that the version matches here to be safe.
215
0
  uint32_t version;
216
0
  rv = stream->Read32(&version);
217
0
  NS_ENSURE_SUCCESS(rv, rv);
218
0
  if (version != XBLBinding_Serialize_Version) {
219
0
    // The version that exists is different than expected, likely created with a
220
0
    // different build, so invalidate the cache.
221
0
    startupCache->InvalidateCache();
222
0
    return NS_ERROR_NOT_AVAILABLE;
223
0
  }
224
0
225
0
  nsCOMPtr<nsIPrincipal> principal;
226
0
  nsContentUtils::GetSecurityManager()->
227
0
    GetSystemPrincipal(getter_AddRefs(principal));
228
0
229
0
  nsCOMPtr<nsIDocument> doc;
230
0
  rv = NS_NewXBLDocument(getter_AddRefs(doc), aURI, nullptr, principal);
231
0
  NS_ENSURE_SUCCESS(rv, rv);
232
0
233
0
  RefPtr<nsXBLDocumentInfo> docInfo = new nsXBLDocumentInfo(doc);
234
0
235
0
  while (1) {
236
0
    uint8_t flags;
237
0
    nsresult rv = stream->Read8(&flags);
238
0
    NS_ENSURE_SUCCESS(rv, rv);
239
0
    if (flags == XBLBinding_Serialize_NoMoreBindings)
240
0
      break;
241
0
242
0
    rv = nsXBLPrototypeBinding::ReadNewBinding(stream, docInfo, doc, flags);
243
0
    if (NS_FAILED(rv)) {
244
0
      return rv;
245
0
    }
246
0
  }
247
0
248
0
  docInfo.forget(aDocInfo);
249
0
  return NS_OK;
250
0
}
251
252
nsresult
253
nsXBLDocumentInfo::WritePrototypeBindings()
254
0
{
255
0
  // Only write out bindings with the system principal
256
0
  if (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal()))
257
0
    return NS_OK;
258
0
259
0
  nsAutoCString spec(kXBLCachePrefix);
260
0
  nsresult rv = PathifyURI(DocumentURI(), spec);
261
0
  NS_ENSURE_SUCCESS(rv, rv);
262
0
263
0
  StartupCache* startupCache = StartupCache::GetSingleton();
264
0
  if (!startupCache) {
265
0
    return rv;
266
0
  }
267
0
268
0
  nsCOMPtr<nsIObjectOutputStream> stream;
269
0
  nsCOMPtr<nsIStorageStream> storageStream;
270
0
  rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(stream),
271
0
                                           getter_AddRefs(storageStream),
272
0
                                           true);
273
0
  NS_ENSURE_SUCCESS(rv, rv);
274
0
275
0
  rv = stream->Write32(XBLBinding_Serialize_Version);
276
0
  NS_ENSURE_SUCCESS(rv, rv);
277
0
278
0
  if (mBindingTable) {
279
0
    for (auto iter = mBindingTable->Iter(); !iter.Done(); iter.Next()) {
280
0
      iter.UserData()->Write(stream);
281
0
    }
282
0
  }
283
0
284
0
  // write a end marker at the end
285
0
  rv = stream->Write8(XBLBinding_Serialize_NoMoreBindings);
286
0
  NS_ENSURE_SUCCESS(rv, rv);
287
0
288
0
  stream->Close();
289
0
  NS_ENSURE_SUCCESS(rv, rv);
290
0
291
0
  uint32_t len;
292
0
  UniquePtr<char[]> buf;
293
0
  rv = NewBufferFromStorageStream(storageStream, &buf, &len);
294
0
  NS_ENSURE_SUCCESS(rv, rv);
295
0
296
0
  return startupCache->PutBuffer(spec.get(), std::move(buf), len);
297
0
}
298
299
void
300
nsXBLDocumentInfo::SetFirstPrototypeBinding(nsXBLPrototypeBinding* aBinding)
301
0
{
302
0
  mFirstBinding = aBinding;
303
0
}
304
305
void
306
nsXBLDocumentInfo::FlushSkinStylesheets()
307
0
{
308
0
  if (mBindingTable) {
309
0
    for (auto iter = mBindingTable->Iter(); !iter.Done(); iter.Next()) {
310
0
      iter.UserData()->FlushSkinSheets();
311
0
    }
312
0
  }
313
0
}
314
315
#ifdef DEBUG
316
void
317
AssertInCompilationScope()
318
{
319
  AutoJSContext cx;
320
  MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx));
321
}
322
#endif
323
324
size_t
325
nsXBLDocumentInfo::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
326
0
{
327
0
  size_t n = aMallocSizeOf(this);
328
0
  if (mDocument) {
329
0
    SizeOfState state(aMallocSizeOf);
330
0
    nsWindowSizes windowSizes(state);
331
0
    mDocument->DocAddSizeOfIncludingThis(windowSizes);
332
0
    n += windowSizes.getTotalSize();
333
0
  }
334
0
  if (mBindingTable) {
335
0
    n += mBindingTable->ShallowSizeOfIncludingThis(aMallocSizeOf);
336
0
    for (auto iter = mBindingTable->Iter(); !iter.Done(); iter.Next()) {
337
0
      nsXBLPrototypeBinding* binding = iter.UserData();
338
0
      n += binding->SizeOfIncludingThis(aMallocSizeOf);
339
0
    }
340
0
  }
341
0
  return n;
342
0
}