Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/extensions/WebExtensionPolicy.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "mozilla/ExtensionPolicyService.h"
7
#include "mozilla/extensions/DocumentObserver.h"
8
#include "mozilla/extensions/WebExtensionContentScript.h"
9
#include "mozilla/extensions/WebExtensionPolicy.h"
10
11
#include "mozilla/AddonManagerWebAPI.h"
12
#include "mozilla/ResultExtensions.h"
13
#include "nsEscape.h"
14
#include "nsIDocShell.h"
15
#include "nsIObserver.h"
16
#include "nsISubstitutingProtocolHandler.h"
17
#include "nsNetUtil.h"
18
#include "nsPrintfCString.h"
19
20
namespace mozilla {
21
namespace extensions {
22
23
using namespace dom;
24
25
static const char kProto[] = "moz-extension";
26
27
static const char kBackgroundPageHTMLStart[] = "<!DOCTYPE html>\n\
28
<html>\n\
29
  <head><meta charset=\"utf-8\"></head>\n\
30
  <body>";
31
32
static const char kBackgroundPageHTMLScript[] = "\n\
33
    <script type=\"text/javascript\" src=\"%s\"></script>";
34
35
static const char kBackgroundPageHTMLEnd[] = "\n\
36
  </body>\n\
37
</html>";
38
39
static const char kRestrictedDomainPref[] =
40
  "extensions.webextensions.restrictedDomains";
41
42
static inline ExtensionPolicyService&
43
EPS()
44
0
{
45
0
  return ExtensionPolicyService::GetSingleton();
46
0
}
47
48
static nsISubstitutingProtocolHandler*
49
Proto()
50
0
{
51
0
  static nsCOMPtr<nsISubstitutingProtocolHandler> sHandler;
52
0
53
0
  if (MOZ_UNLIKELY(!sHandler)) {
54
0
    nsCOMPtr<nsIIOService> ios = do_GetIOService();
55
0
    MOZ_RELEASE_ASSERT(ios);
56
0
57
0
    nsCOMPtr<nsIProtocolHandler> handler;
58
0
    ios->GetProtocolHandler(kProto, getter_AddRefs(handler));
59
0
60
0
    sHandler = do_QueryInterface(handler);
61
0
    MOZ_RELEASE_ASSERT(sHandler);
62
0
63
0
    ClearOnShutdown(&sHandler);
64
0
  }
65
0
66
0
  return sHandler;
67
0
}
68
69
70
bool
71
ParseGlobs(GlobalObject& aGlobal, Sequence<OwningMatchGlobOrString> aGlobs,
72
           nsTArray<RefPtr<MatchGlob>>& aResult, ErrorResult& aRv)
73
0
{
74
0
  for (auto& elem : aGlobs) {
75
0
    if (elem.IsMatchGlob()) {
76
0
      aResult.AppendElement(elem.GetAsMatchGlob());
77
0
    } else {
78
0
      RefPtr<MatchGlob> glob = MatchGlob::Constructor(aGlobal,
79
0
                                                      elem.GetAsString(),
80
0
                                                      true, aRv);
81
0
      if (aRv.Failed()) {
82
0
        return false;
83
0
      }
84
0
      aResult.AppendElement(glob);
85
0
    }
86
0
  }
87
0
  return true;
88
0
}
89
90
enum class ErrorBehavior {
91
  CreateEmptyPattern,
92
  Fail,
93
};
94
95
already_AddRefed<MatchPatternSet>
96
ParseMatches(GlobalObject& aGlobal,
97
             const OwningMatchPatternSetOrStringSequence& aMatches,
98
             const MatchPatternOptions& aOptions,
99
             ErrorBehavior aErrorBehavior,
100
             ErrorResult& aRv)
101
0
{
102
0
  if (aMatches.IsMatchPatternSet()) {
103
0
    return do_AddRef(aMatches.GetAsMatchPatternSet().get());
104
0
  }
105
0
106
0
  const auto& strings = aMatches.GetAsStringSequence();
107
0
108
0
  nsTArray<OwningStringOrMatchPattern> patterns;
109
0
  if (!patterns.SetCapacity(strings.Length(), fallible)) {
110
0
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
111
0
    return nullptr;
112
0
  }
113
0
114
0
  for (auto& string : strings) {
115
0
    OwningStringOrMatchPattern elt;
116
0
    elt.SetAsString() = string;
117
0
    patterns.AppendElement(elt);
118
0
  }
119
0
120
0
  RefPtr<MatchPatternSet> result = MatchPatternSet::Constructor(
121
0
    aGlobal, patterns, aOptions, aRv);
122
0
123
0
  if (aRv.Failed() && aErrorBehavior == ErrorBehavior::CreateEmptyPattern) {
124
0
    aRv.SuppressException();
125
0
    result = MatchPatternSet::Constructor(aGlobal, {}, aOptions, aRv);
126
0
  }
127
0
128
0
  return result.forget();
129
0
}
130
131
132
/*****************************************************************************
133
 * WebExtensionPolicy
134
 *****************************************************************************/
135
136
WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
137
                                       const WebExtensionInit& aInit,
138
                                       ErrorResult& aRv)
139
  : mId(NS_AtomizeMainThread(aInit.mId))
140
  , mHostname(aInit.mMozExtensionHostname)
141
  , mName(aInit.mName)
142
  , mContentSecurityPolicy(aInit.mContentSecurityPolicy)
143
  , mLocalizeCallback(aInit.mLocalizeCallback)
144
  , mPermissions(new AtomSet(aInit.mPermissions))
145
0
{
146
0
  if (!ParseGlobs(aGlobal, aInit.mWebAccessibleResources, mWebAccessiblePaths,
147
0
                  aRv)) {
148
0
    return;
149
0
  }
150
0
151
0
  MatchPatternOptions options;
152
0
  options.mRestrictSchemes = !HasPermission(nsGkAtoms::mozillaAddons);
153
0
154
0
  mHostPermissions = ParseMatches(aGlobal, aInit.mAllowedOrigins, options,
155
0
                                  ErrorBehavior::CreateEmptyPattern, aRv);
156
0
  if (aRv.Failed()) {
157
0
    return;
158
0
  }
159
0
160
0
  if (!aInit.mBackgroundScripts.IsNull()) {
161
0
    mBackgroundScripts.SetValue().AppendElements(aInit.mBackgroundScripts.Value());
162
0
  }
163
0
164
0
  if (mContentSecurityPolicy.IsVoid()) {
165
0
    EPS().DefaultCSP(mContentSecurityPolicy);
166
0
  }
167
0
168
0
  mContentScripts.SetCapacity(aInit.mContentScripts.Length());
169
0
  for (const auto& scriptInit : aInit.mContentScripts) {
170
0
    // The activeTab permission is only for dynamically injected scripts,
171
0
    // it cannot be used for declarative content scripts.
172
0
    if (scriptInit.mHasActiveTabPermission) {
173
0
      aRv.Throw(NS_ERROR_INVALID_ARG);
174
0
      return;
175
0
    }
176
0
177
0
    RefPtr<WebExtensionContentScript> contentScript =
178
0
      new WebExtensionContentScript(aGlobal, *this, scriptInit, aRv);
179
0
    if (aRv.Failed()) {
180
0
      return;
181
0
    }
182
0
    mContentScripts.AppendElement(std::move(contentScript));
183
0
  }
184
0
185
0
  nsresult rv = NS_NewURI(getter_AddRefs(mBaseURI), aInit.mBaseURL);
186
0
  if (NS_FAILED(rv)) {
187
0
    aRv.Throw(rv);
188
0
  }
189
0
}
190
191
already_AddRefed<WebExtensionPolicy>
192
WebExtensionPolicy::Constructor(GlobalObject& aGlobal,
193
                                const WebExtensionInit& aInit,
194
                                ErrorResult& aRv)
195
0
{
196
0
  RefPtr<WebExtensionPolicy> policy = new WebExtensionPolicy(aGlobal, aInit, aRv);
197
0
  if (aRv.Failed()) {
198
0
    return nullptr;
199
0
  }
200
0
  return policy.forget();
201
0
}
202
203
204
/* static */ void
205
WebExtensionPolicy::GetActiveExtensions(dom::GlobalObject& aGlobal,
206
                                        nsTArray<RefPtr<WebExtensionPolicy>>& aResults)
207
0
{
208
0
  EPS().GetAll(aResults);
209
0
}
210
211
/* static */ already_AddRefed<WebExtensionPolicy>
212
WebExtensionPolicy::GetByID(dom::GlobalObject& aGlobal, const nsAString& aID)
213
0
{
214
0
  return do_AddRef(EPS().GetByID(aID));
215
0
}
216
217
/* static */ already_AddRefed<WebExtensionPolicy>
218
WebExtensionPolicy::GetByHostname(dom::GlobalObject& aGlobal, const nsACString& aHostname)
219
0
{
220
0
  return do_AddRef(EPS().GetByHost(aHostname));
221
0
}
222
223
/* static */ already_AddRefed<WebExtensionPolicy>
224
WebExtensionPolicy::GetByURI(dom::GlobalObject& aGlobal, nsIURI* aURI)
225
0
{
226
0
  return do_AddRef(EPS().GetByURL(aURI));
227
0
}
228
229
230
void
231
WebExtensionPolicy::SetActive(bool aActive, ErrorResult& aRv)
232
0
{
233
0
  if (aActive == mActive) {
234
0
    return;
235
0
  }
236
0
237
0
  bool ok = aActive ? Enable() : Disable();
238
0
239
0
  if (!ok) {
240
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
241
0
  }
242
0
}
243
244
bool
245
WebExtensionPolicy::Enable()
246
0
{
247
0
  MOZ_ASSERT(!mActive);
248
0
249
0
  if (!EPS().RegisterExtension(*this)) {
250
0
    return false;
251
0
  }
252
0
253
0
  Unused << Proto()->SetSubstitution(MozExtensionHostname(), mBaseURI);
254
0
255
0
  mActive = true;
256
0
  return true;
257
0
}
258
259
bool
260
WebExtensionPolicy::Disable()
261
0
{
262
0
  MOZ_ASSERT(mActive);
263
0
  MOZ_ASSERT(EPS().GetByID(Id()) == this);
264
0
265
0
  if (!EPS().UnregisterExtension(*this)) {
266
0
    return false;
267
0
  }
268
0
269
0
  Unused << Proto()->SetSubstitution(MozExtensionHostname(), nullptr);
270
0
271
0
  mActive = false;
272
0
  return true;
273
0
}
274
275
void
276
WebExtensionPolicy::GetURL(const nsAString& aPath,
277
                           nsAString& aResult,
278
                           ErrorResult& aRv) const
279
0
{
280
0
  auto result = GetURL(aPath);
281
0
  if (result.isOk()) {
282
0
    aResult = result.unwrap();
283
0
  } else {
284
0
    aRv.Throw(result.unwrapErr());
285
0
  }
286
0
}
287
288
Result<nsString, nsresult>
289
WebExtensionPolicy::GetURL(const nsAString& aPath) const
290
0
{
291
0
  nsPrintfCString spec("%s://%s/", kProto, mHostname.get());
292
0
293
0
  nsCOMPtr<nsIURI> uri;
294
0
  MOZ_TRY(NS_NewURI(getter_AddRefs(uri), spec));
295
0
296
0
  MOZ_TRY(uri->Resolve(NS_ConvertUTF16toUTF8(aPath), spec));
297
0
298
0
  return NS_ConvertUTF8toUTF16(spec);
299
0
}
300
301
void
302
WebExtensionPolicy::RegisterContentScript(WebExtensionContentScript& script,
303
                                          ErrorResult& aRv)
304
0
{
305
0
  // Raise an "invalid argument" error if the script is not related to
306
0
  // the expected extension or if it is already registered.
307
0
  if (script.mExtension != this || mContentScripts.Contains(&script)) {
308
0
    aRv.Throw(NS_ERROR_INVALID_ARG);
309
0
    return;
310
0
  }
311
0
312
0
  RefPtr<WebExtensionContentScript> newScript = &script;
313
0
314
0
  if (!mContentScripts.AppendElement(std::move(newScript), fallible)) {
315
0
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
316
0
    return;
317
0
  }
318
0
319
0
  WebExtensionPolicy_Binding::ClearCachedContentScriptsValue(this);
320
0
}
321
322
void
323
WebExtensionPolicy::UnregisterContentScript(const WebExtensionContentScript& script,
324
                                            ErrorResult& aRv)
325
0
{
326
0
  if (script.mExtension != this || !mContentScripts.RemoveElement(&script)) {
327
0
    aRv.Throw(NS_ERROR_INVALID_ARG);
328
0
    return;
329
0
  }
330
0
331
0
  WebExtensionPolicy_Binding::ClearCachedContentScriptsValue(this);
332
0
}
333
334
void
335
WebExtensionPolicy::InjectContentScripts(ErrorResult& aRv)
336
0
{
337
0
  nsresult rv = EPS().InjectContentScripts(this);
338
0
  if (NS_FAILED(rv)) {
339
0
    aRv.Throw(rv);
340
0
  }
341
0
}
342
343
/* static */ bool
344
WebExtensionPolicy::UseRemoteWebExtensions(GlobalObject& aGlobal)
345
0
{
346
0
  return EPS().UseRemoteExtensions();
347
0
}
348
349
/* static */ bool
350
WebExtensionPolicy::IsExtensionProcess(GlobalObject& aGlobal)
351
0
{
352
0
  return EPS().IsExtensionProcess();
353
0
}
354
355
namespace {
356
  /**
357
   * Maintains a dynamically updated AtomSet based on the comma-separated
358
   * values in the given string pref.
359
   */
360
  class AtomSetPref : public nsIObserver
361
                    , public nsSupportsWeakReference
362
  {
363
  public:
364
    NS_DECL_ISUPPORTS
365
    NS_DECL_NSIOBSERVER
366
367
    static already_AddRefed<AtomSetPref>
368
    Create(const nsCString& aPref)
369
0
    {
370
0
      RefPtr<AtomSetPref> self = new AtomSetPref(aPref.get());
371
0
      Preferences::AddWeakObserver(self, aPref);
372
0
      return self.forget();
373
0
    }
374
375
    const AtomSet& Get() const;
376
377
    bool Contains(const nsAtom* aAtom) const
378
0
    {
379
0
      return Get().Contains(aAtom);
380
0
    }
381
382
  protected:
383
0
    virtual ~AtomSetPref() = default;
384
385
    explicit AtomSetPref(const char* aPref) : mPref(aPref)
386
0
    {}
387
388
  private:
389
    mutable RefPtr<AtomSet> mAtomSet;
390
    const char* mPref;
391
  };
392
393
  const AtomSet&
394
  AtomSetPref::Get() const
395
0
  {
396
0
    if (!mAtomSet) {
397
0
      nsAutoCString eltsString;
398
0
      Unused << Preferences::GetCString(mPref, eltsString);
399
0
400
0
      AutoTArray<nsString, 32> elts;
401
0
      for (const nsACString& elt : eltsString.Split(',')) {
402
0
        elts.AppendElement(NS_ConvertUTF8toUTF16(elt));
403
0
        elts.LastElement().StripWhitespace();
404
0
      }
405
0
      mAtomSet = new AtomSet(elts);
406
0
    }
407
0
408
0
    return *mAtomSet;
409
0
  }
410
411
  NS_IMETHODIMP
412
  AtomSetPref::Observe(nsISupports *aSubject, const char *aTopic,
413
                       const char16_t *aData)
414
0
  {
415
0
    mAtomSet = nullptr;
416
0
    return NS_OK;
417
0
  }
418
419
  NS_IMPL_ISUPPORTS(AtomSetPref, nsIObserver, nsISupportsWeakReference)
420
};
421
422
/* static */ bool
423
WebExtensionPolicy::IsRestrictedDoc(const DocInfo& aDoc)
424
0
{
425
0
  // With the exception of top-level about:blank documents with null
426
0
  // principals, we never match documents that have non-codebase principals,
427
0
  // including those with null principals or system principals.
428
0
  if (aDoc.Principal() && !aDoc.Principal()->GetIsCodebasePrincipal()) {
429
0
    return true;
430
0
  }
431
0
432
0
  return IsRestrictedURI(aDoc.PrincipalURL());
433
0
}
434
435
/* static */ bool
436
WebExtensionPolicy::IsRestrictedURI(const URLInfo &aURI)
437
0
{
438
0
  static RefPtr<AtomSetPref> domains;
439
0
  if (!domains) {
440
0
    domains = AtomSetPref::Create(nsLiteralCString(kRestrictedDomainPref));
441
0
    ClearOnShutdown(&domains);
442
0
  }
443
0
444
0
  if (domains->Contains(aURI.HostAtom())) {
445
0
    return true;
446
0
  }
447
0
448
0
  if (AddonManagerWebAPI::IsValidSite(aURI.URI())) {
449
0
    return true;
450
0
  }
451
0
452
0
  return false;
453
0
}
454
455
nsCString
456
WebExtensionPolicy::BackgroundPageHTML() const
457
0
{
458
0
  nsCString result;
459
0
460
0
  if (mBackgroundScripts.IsNull()) {
461
0
    result.SetIsVoid(true);
462
0
    return result;
463
0
  }
464
0
465
0
  result.AppendLiteral(kBackgroundPageHTMLStart);
466
0
467
0
  for (auto& script : mBackgroundScripts.Value()) {
468
0
    nsCString escaped;
469
0
    nsAppendEscapedHTML(NS_ConvertUTF16toUTF8(script), escaped);
470
0
471
0
    result.AppendPrintf(kBackgroundPageHTMLScript, escaped.get());
472
0
  }
473
0
474
0
  result.AppendLiteral(kBackgroundPageHTMLEnd);
475
0
  return result;
476
0
}
477
478
void
479
WebExtensionPolicy::Localize(const nsAString& aInput, nsString& aOutput) const
480
0
{
481
0
  mLocalizeCallback->Call(aInput, aOutput);
482
0
}
483
484
485
JSObject*
486
WebExtensionPolicy::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
487
0
{
488
0
  return WebExtensionPolicy_Binding::Wrap(aCx, this, aGivenProto);
489
0
}
490
491
void
492
WebExtensionPolicy::GetContentScripts(nsTArray<RefPtr<WebExtensionContentScript>>& aScripts) const
493
0
{
494
0
  aScripts.AppendElements(mContentScripts);
495
0
}
496
497
498
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebExtensionPolicy, mParent,
499
                                      mLocalizeCallback,
500
                                      mHostPermissions,
501
                                      mWebAccessiblePaths,
502
                                      mContentScripts)
503
504
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebExtensionPolicy)
505
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
506
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
507
0
NS_INTERFACE_MAP_END
508
509
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebExtensionPolicy)
510
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebExtensionPolicy)
511
512
513
/*****************************************************************************
514
 * WebExtensionContentScript / MozDocumentMatcher
515
 *****************************************************************************/
516
517
/* static */ already_AddRefed<MozDocumentMatcher>
518
MozDocumentMatcher::Constructor(GlobalObject& aGlobal,
519
                                const dom::MozDocumentMatcherInit& aInit,
520
                                ErrorResult& aRv)
521
0
{
522
0
  RefPtr<MozDocumentMatcher> matcher = new MozDocumentMatcher(aGlobal, aInit,
523
0
                                                              false, aRv);
524
0
  if (aRv.Failed()) {
525
0
    return nullptr;
526
0
  }
527
0
  return matcher.forget();
528
0
}
529
530
/* static */ already_AddRefed<WebExtensionContentScript>
531
WebExtensionContentScript::Constructor(GlobalObject& aGlobal,
532
                                       WebExtensionPolicy& aExtension,
533
                                       const ContentScriptInit& aInit,
534
                                       ErrorResult& aRv)
535
0
{
536
0
  RefPtr<WebExtensionContentScript> script = new WebExtensionContentScript(
537
0
      aGlobal, aExtension, aInit, aRv);
538
0
  if (aRv.Failed()) {
539
0
    return nullptr;
540
0
  }
541
0
  return script.forget();
542
0
}
543
544
MozDocumentMatcher::MozDocumentMatcher(GlobalObject& aGlobal,
545
                                       const dom::MozDocumentMatcherInit& aInit,
546
                                       bool aRestricted,
547
                                       ErrorResult& aRv)
548
  : mHasActiveTabPermission(aInit.mHasActiveTabPermission)
549
  , mRestricted(aRestricted)
550
  , mAllFrames(aInit.mAllFrames)
551
  , mFrameID(aInit.mFrameID)
552
  , mMatchAboutBlank(aInit.mMatchAboutBlank)
553
0
{
554
0
  MatchPatternOptions options;
555
0
  options.mRestrictSchemes = mRestricted;
556
0
557
0
  mMatches = ParseMatches(aGlobal, aInit.mMatches, options,
558
0
                          ErrorBehavior::CreateEmptyPattern, aRv);
559
0
  if (aRv.Failed()) {
560
0
    return;
561
0
  }
562
0
563
0
  if (!aInit.mExcludeMatches.IsNull()) {
564
0
    mExcludeMatches = ParseMatches(aGlobal, aInit.mExcludeMatches.Value(),
565
0
                                   options, ErrorBehavior::CreateEmptyPattern,
566
0
                                   aRv);
567
0
    if (aRv.Failed()) {
568
0
      return;
569
0
    }
570
0
  }
571
0
572
0
  if (!aInit.mIncludeGlobs.IsNull()) {
573
0
    if (!ParseGlobs(aGlobal, aInit.mIncludeGlobs.Value(), mIncludeGlobs.SetValue(),
574
0
                    aRv)) {
575
0
      return;
576
0
    }
577
0
  }
578
0
579
0
  if (!aInit.mExcludeGlobs.IsNull()) {
580
0
    if (!ParseGlobs(aGlobal, aInit.mExcludeGlobs.Value(), mExcludeGlobs.SetValue(),
581
0
                    aRv)) {
582
0
      return;
583
0
    }
584
0
  }
585
0
}
586
587
WebExtensionContentScript::WebExtensionContentScript(GlobalObject& aGlobal,
588
                                                     WebExtensionPolicy& aExtension,
589
                                                     const ContentScriptInit& aInit,
590
                                                     ErrorResult& aRv)
591
  : MozDocumentMatcher(aGlobal, aInit, !aExtension.HasPermission(nsGkAtoms::mozillaAddons), aRv)
592
  , mCssPaths(aInit.mCssPaths)
593
  , mJsPaths(aInit.mJsPaths)
594
  , mRunAt(aInit.mRunAt)
595
0
{
596
0
  mExtension = &aExtension;
597
0
}
598
599
bool
600
MozDocumentMatcher::Matches(const DocInfo& aDoc) const
601
0
{
602
0
  if (!mFrameID.IsNull()) {
603
0
    if (aDoc.FrameID() != mFrameID.Value()) {
604
0
      return false;
605
0
    }
606
0
  } else {
607
0
    if (!mAllFrames && !aDoc.IsTopLevel()) {
608
0
      return false;
609
0
    }
610
0
  }
611
0
612
0
  if (!mMatchAboutBlank && aDoc.URL().InheritsPrincipal()) {
613
0
    return false;
614
0
  }
615
0
616
0
  // Top-level about:blank is a special case. We treat it as a match if
617
0
  // matchAboutBlank is true and it has the null principal. In all other
618
0
  // cases, we test the URL of the principal that it inherits.
619
0
  if (mMatchAboutBlank && aDoc.IsTopLevel() &&
620
0
      aDoc.URL().Spec().EqualsLiteral("about:blank") &&
621
0
      aDoc.Principal() && aDoc.Principal()->GetIsNullPrincipal()) {
622
0
    return true;
623
0
  }
624
0
625
0
  if (mRestricted && mExtension->IsRestrictedDoc(aDoc)) {
626
0
    return false;
627
0
  }
628
0
629
0
  auto& urlinfo = aDoc.PrincipalURL();
630
0
  if (mHasActiveTabPermission && aDoc.ShouldMatchActiveTabPermission() &&
631
0
      MatchPattern::MatchesAllURLs(urlinfo)) {
632
0
    return true;
633
0
  }
634
0
635
0
  return MatchesURI(urlinfo);
636
0
}
637
638
bool
639
MozDocumentMatcher::MatchesURI(const URLInfo& aURL) const
640
0
{
641
0
  if (!mMatches->Matches(aURL)) {
642
0
    return false;
643
0
  }
644
0
645
0
  if (mExcludeMatches && mExcludeMatches->Matches(aURL)) {
646
0
    return false;
647
0
  }
648
0
649
0
  if (!mIncludeGlobs.IsNull() && !mIncludeGlobs.Value().Matches(aURL.Spec())) {
650
0
    return false;
651
0
  }
652
0
653
0
  if (!mExcludeGlobs.IsNull() && mExcludeGlobs.Value().Matches(aURL.Spec())) {
654
0
    return false;
655
0
  }
656
0
657
0
  if (mRestricted && mExtension->IsRestrictedURI(aURL)) {
658
0
    return false;
659
0
  }
660
0
661
0
  return true;
662
0
}
663
664
665
JSObject*
666
MozDocumentMatcher::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
667
0
{
668
0
  return MozDocumentMatcher_Binding::Wrap(aCx, this, aGivenProto);
669
0
}
670
671
JSObject*
672
WebExtensionContentScript::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
673
0
{
674
0
  return WebExtensionContentScript_Binding::Wrap(aCx, this, aGivenProto);
675
0
}
676
677
678
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MozDocumentMatcher,
679
                                      mMatches, mExcludeMatches,
680
                                      mIncludeGlobs, mExcludeGlobs,
681
                                      mExtension)
682
683
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MozDocumentMatcher)
684
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
685
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
686
0
NS_INTERFACE_MAP_END
687
688
NS_IMPL_CYCLE_COLLECTING_ADDREF(MozDocumentMatcher)
689
NS_IMPL_CYCLE_COLLECTING_RELEASE(MozDocumentMatcher)
690
691
/*****************************************************************************
692
 * MozDocumentObserver
693
 *****************************************************************************/
694
695
/* static */ already_AddRefed<DocumentObserver>
696
DocumentObserver::Constructor(GlobalObject& aGlobal,
697
                              dom::MozDocumentCallback& aCallbacks,
698
                              ErrorResult& aRv)
699
0
{
700
0
  RefPtr<DocumentObserver> matcher = new DocumentObserver(aGlobal.GetAsSupports(), aCallbacks);
701
0
  return matcher.forget();
702
0
}
703
704
705
void
706
DocumentObserver::Observe(const dom::Sequence<OwningNonNull<MozDocumentMatcher>>& matchers, ErrorResult& aRv)
707
0
{
708
0
  if (!EPS().RegisterObserver(*this)) {
709
0
    aRv.Throw(NS_ERROR_FAILURE);
710
0
    return;
711
0
  }
712
0
  mMatchers.Clear();
713
0
  for (auto& matcher : matchers) {
714
0
    if (!mMatchers.AppendElement(matcher, fallible)) {
715
0
      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
716
0
      return;
717
0
    }
718
0
  }
719
0
}
720
721
void
722
DocumentObserver::Disconnect()
723
0
{
724
0
  Unused << EPS().UnregisterObserver(*this);
725
0
}
726
727
728
void
729
DocumentObserver::NotifyMatch(MozDocumentMatcher& aMatcher, nsPIDOMWindowOuter* aWindow)
730
0
{
731
0
  IgnoredErrorResult rv;
732
0
  mCallbacks->OnNewDocument(aMatcher, aWindow, rv);
733
0
}
734
735
void
736
DocumentObserver::NotifyMatch(MozDocumentMatcher& aMatcher, nsILoadInfo* aLoadInfo)
737
0
{
738
0
  IgnoredErrorResult rv;
739
0
  mCallbacks->OnPreloadDocument(aMatcher, aLoadInfo, rv);
740
0
}
741
742
743
JSObject*
744
DocumentObserver::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
745
0
{
746
0
  return MozDocumentObserver_Binding::Wrap(aCx, this, aGivenProto);
747
0
}
748
749
750
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DocumentObserver, mCallbacks, mMatchers, mParent)
751
752
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DocumentObserver)
753
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
754
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
755
0
NS_INTERFACE_MAP_END
756
757
NS_IMPL_CYCLE_COLLECTING_ADDREF(DocumentObserver)
758
NS_IMPL_CYCLE_COLLECTING_RELEASE(DocumentObserver)
759
760
/*****************************************************************************
761
 * DocInfo
762
 *****************************************************************************/
763
764
DocInfo::DocInfo(const URLInfo& aURL, nsILoadInfo* aLoadInfo)
765
  : mURL(aURL)
766
  , mObj(AsVariant(aLoadInfo))
767
0
{}
768
769
DocInfo::DocInfo(nsPIDOMWindowOuter* aWindow)
770
  : mURL(aWindow->GetDocumentURI())
771
  , mObj(AsVariant(aWindow))
772
0
{}
773
774
bool
775
DocInfo::IsTopLevel() const
776
0
{
777
0
  if (mIsTopLevel.isNothing()) {
778
0
    struct Matcher
779
0
    {
780
0
      bool match(Window aWin) { return aWin->IsTopLevelWindow(); }
781
0
      bool match(LoadInfo aLoadInfo) { return aLoadInfo->GetIsTopLevelLoad(); }
782
0
    };
783
0
    mIsTopLevel.emplace(mObj.match(Matcher()));
784
0
  }
785
0
  return mIsTopLevel.ref();
786
0
}
787
788
bool
789
WindowShouldMatchActiveTab(nsPIDOMWindowOuter* aWin)
790
0
{
791
0
  if (aWin->IsTopLevelWindow()) {
792
0
    return true;
793
0
  }
794
0
795
0
  nsIDocShell* docshell = aWin->GetDocShell();
796
0
  if (!docshell || docshell->GetCreatedDynamically()) {
797
0
    return false;
798
0
  }
799
0
800
0
  nsIDocument* doc = aWin->GetExtantDoc();
801
0
  if (!doc) {
802
0
    return false;
803
0
  }
804
0
805
0
  nsIChannel* channel = doc->GetChannel();
806
0
  if (!channel) {
807
0
    return false;
808
0
  }
809
0
810
0
  nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
811
0
812
0
  if (!loadInfo) {
813
0
    return false;
814
0
  }
815
0
816
0
  if (!loadInfo->GetOriginalFrameSrcLoad()) {
817
0
    return false;
818
0
  }
819
0
820
0
  nsCOMPtr<nsPIDOMWindowOuter> parent = aWin->GetParent();
821
0
  MOZ_ASSERT(parent != nullptr);
822
0
  return WindowShouldMatchActiveTab(parent);
823
0
}
824
825
bool
826
DocInfo::ShouldMatchActiveTabPermission() const
827
0
{
828
0
  struct Matcher
829
0
  {
830
0
    bool match(Window aWin) { return WindowShouldMatchActiveTab(aWin); }
831
0
    bool match(LoadInfo aLoadInfo) { return false; }
832
0
  };
833
0
  return mObj.match(Matcher());
834
0
}
835
836
uint64_t
837
DocInfo::FrameID() const
838
0
{
839
0
  if (mFrameID.isNothing()) {
840
0
    if (IsTopLevel()) {
841
0
      mFrameID.emplace(0);
842
0
    } else {
843
0
      struct Matcher
844
0
      {
845
0
        uint64_t match(Window aWin) { return aWin->WindowID(); }
846
0
        uint64_t match(LoadInfo aLoadInfo) { return aLoadInfo->GetOuterWindowID(); }
847
0
      };
848
0
      mFrameID.emplace(mObj.match(Matcher()));
849
0
    }
850
0
  }
851
0
  return mFrameID.ref();
852
0
}
853
854
nsIPrincipal*
855
DocInfo::Principal() const
856
0
{
857
0
  if (mPrincipal.isNothing()) {
858
0
    struct Matcher
859
0
    {
860
0
      explicit Matcher(const DocInfo& aThis) : mThis(aThis) {}
861
0
      const DocInfo& mThis;
862
0
863
0
      nsIPrincipal* match(Window aWin)
864
0
      {
865
0
        nsCOMPtr<nsIDocument> doc = aWin->GetDoc();
866
0
        return doc->NodePrincipal();
867
0
      }
868
0
      nsIPrincipal* match(LoadInfo aLoadInfo)
869
0
      {
870
0
        if (!(mThis.URL().InheritsPrincipal() || aLoadInfo->GetForceInheritPrincipal())) {
871
0
          return nullptr;
872
0
        }
873
0
        if (auto principal = aLoadInfo->PrincipalToInherit()) {
874
0
          return principal;
875
0
        }
876
0
        return aLoadInfo->TriggeringPrincipal();
877
0
      }
878
0
    };
879
0
    mPrincipal.emplace(mObj.match(Matcher(*this)));
880
0
  }
881
0
  return mPrincipal.ref();
882
0
}
883
884
const URLInfo&
885
DocInfo::PrincipalURL() const
886
0
{
887
0
  if (!(Principal() && Principal()->GetIsCodebasePrincipal())) {
888
0
    return URL();
889
0
  }
890
0
891
0
  if (mPrincipalURL.isNothing()) {
892
0
    nsIPrincipal* prin = Principal();
893
0
    nsCOMPtr<nsIURI> uri;
894
0
    if (NS_SUCCEEDED(prin->GetURI(getter_AddRefs(uri)))) {
895
0
      MOZ_DIAGNOSTIC_ASSERT(uri);
896
0
      mPrincipalURL.emplace(uri);
897
0
    } else {
898
0
      mPrincipalURL.emplace(URL());
899
0
    }
900
0
  }
901
0
902
0
  return mPrincipalURL.ref();
903
0
}
904
905
} // namespace extensions
906
} // namespace mozilla