/src/mozilla-central/toolkit/components/extensions/ExtensionPolicyService.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/ClearOnShutdown.h" |
12 | | #include "mozilla/Preferences.h" |
13 | | #include "mozilla/ResultExtensions.h" |
14 | | #include "mozilla/Services.h" |
15 | | #include "mozilla/SimpleEnumerator.h" |
16 | | #include "mozilla/dom/ContentChild.h" |
17 | | #include "mozilla/dom/ContentFrameMessageManager.h" |
18 | | #include "mozilla/dom/ContentParent.h" |
19 | | #include "mozilla/dom/Promise.h" |
20 | | #include "mozilla/dom/Promise-inl.h" |
21 | | #include "mozIExtensionProcessScript.h" |
22 | | #include "nsEscape.h" |
23 | | #include "nsGkAtoms.h" |
24 | | #include "nsIChannel.h" |
25 | | #include "nsIContentPolicy.h" |
26 | | #include "nsIDocShell.h" |
27 | | #include "nsIDocument.h" |
28 | | #include "nsGlobalWindowOuter.h" |
29 | | #include "nsILoadInfo.h" |
30 | | #include "nsIXULRuntime.h" |
31 | | #include "nsNetUtil.h" |
32 | | #include "nsPIDOMWindow.h" |
33 | | #include "nsXULAppAPI.h" |
34 | | #include "nsQueryObject.h" |
35 | | |
36 | | namespace mozilla { |
37 | | |
38 | | using namespace extensions; |
39 | | |
40 | | using dom::AutoJSAPI; |
41 | | using dom::ContentFrameMessageManager; |
42 | | using dom::Promise; |
43 | | |
44 | | #define DEFAULT_BASE_CSP \ |
45 | 0 | "script-src 'self' https://* moz-extension: blob: filesystem: 'unsafe-eval' 'unsafe-inline'; " \ |
46 | 0 | "object-src 'self' https://* moz-extension: blob: filesystem:;" |
47 | | |
48 | | #define DEFAULT_DEFAULT_CSP \ |
49 | 0 | "script-src 'self'; object-src 'self';" |
50 | | |
51 | | |
52 | | #define OBS_TOPIC_PRELOAD_SCRIPT "web-extension-preload-content-script" |
53 | | #define OBS_TOPIC_LOAD_SCRIPT "web-extension-load-content-script" |
54 | | |
55 | | |
56 | | static mozIExtensionProcessScript& |
57 | | ProcessScript() |
58 | 0 | { |
59 | 0 | static nsCOMPtr<mozIExtensionProcessScript> sProcessScript; |
60 | 0 |
|
61 | 0 | if (MOZ_UNLIKELY(!sProcessScript)) { |
62 | 0 | sProcessScript = do_GetService("@mozilla.org/webextensions/extension-process-script;1"); |
63 | 0 | MOZ_RELEASE_ASSERT(sProcessScript); |
64 | 0 | ClearOnShutdown(&sProcessScript); |
65 | 0 | } |
66 | 0 | return *sProcessScript; |
67 | 0 | } |
68 | | |
69 | | /***************************************************************************** |
70 | | * ExtensionPolicyService |
71 | | *****************************************************************************/ |
72 | | |
73 | | /* static */ bool ExtensionPolicyService::sRemoteExtensions; |
74 | | |
75 | | /* static */ ExtensionPolicyService& |
76 | | ExtensionPolicyService::GetSingleton() |
77 | 0 | { |
78 | 0 | static RefPtr<ExtensionPolicyService> sExtensionPolicyService; |
79 | 0 |
|
80 | 0 | if (MOZ_UNLIKELY(!sExtensionPolicyService)) { |
81 | 0 | sExtensionPolicyService = new ExtensionPolicyService(); |
82 | 0 | RegisterWeakMemoryReporter(sExtensionPolicyService); |
83 | 0 | ClearOnShutdown(&sExtensionPolicyService); |
84 | 0 | } |
85 | 0 | return *sExtensionPolicyService.get(); |
86 | 0 | } |
87 | | |
88 | | ExtensionPolicyService::ExtensionPolicyService() |
89 | 0 | { |
90 | 0 | mObs = services::GetObserverService(); |
91 | 0 | MOZ_RELEASE_ASSERT(mObs); |
92 | 0 |
|
93 | 0 | Preferences::AddBoolVarCache(&sRemoteExtensions, "extensions.webextensions.remote", false); |
94 | 0 |
|
95 | 0 | RegisterObservers(); |
96 | 0 | } |
97 | | |
98 | | ExtensionPolicyService::~ExtensionPolicyService() |
99 | 0 | { |
100 | 0 | UnregisterWeakMemoryReporter(this); |
101 | 0 | } |
102 | | |
103 | | bool |
104 | | ExtensionPolicyService::UseRemoteExtensions() const |
105 | 0 | { |
106 | 0 | return sRemoteExtensions && BrowserTabsRemoteAutostart(); |
107 | 0 | } |
108 | | |
109 | | bool |
110 | | ExtensionPolicyService::IsExtensionProcess() const |
111 | 0 | { |
112 | 0 | bool isRemote = UseRemoteExtensions(); |
113 | 0 |
|
114 | 0 | if (isRemote && XRE_IsContentProcess()) { |
115 | 0 | auto& remoteType = dom::ContentChild::GetSingleton()->GetRemoteType(); |
116 | 0 | return remoteType.EqualsLiteral(EXTENSION_REMOTE_TYPE); |
117 | 0 | } |
118 | 0 | return !isRemote && XRE_IsParentProcess(); |
119 | 0 | } |
120 | | |
121 | | |
122 | | WebExtensionPolicy* |
123 | | ExtensionPolicyService::GetByURL(const URLInfo& aURL) |
124 | 0 | { |
125 | 0 | if (aURL.Scheme() == nsGkAtoms::moz_extension) { |
126 | 0 | return GetByHost(aURL.Host()); |
127 | 0 | } |
128 | 0 | return nullptr; |
129 | 0 | } |
130 | | |
131 | | void |
132 | | ExtensionPolicyService::GetAll(nsTArray<RefPtr<WebExtensionPolicy>>& aResult) |
133 | 0 | { |
134 | 0 | for (auto iter = mExtensions.Iter(); !iter.Done(); iter.Next()) { |
135 | 0 | aResult.AppendElement(iter.Data()); |
136 | 0 | } |
137 | 0 | } |
138 | | |
139 | | bool |
140 | | ExtensionPolicyService::RegisterExtension(WebExtensionPolicy& aPolicy) |
141 | 0 | { |
142 | 0 | bool ok = (!GetByID(aPolicy.Id()) && |
143 | 0 | !GetByHost(aPolicy.MozExtensionHostname())); |
144 | 0 | MOZ_ASSERT(ok); |
145 | 0 |
|
146 | 0 | if (!ok) { |
147 | 0 | return false; |
148 | 0 | } |
149 | 0 | |
150 | 0 | mExtensions.Put(aPolicy.Id(), &aPolicy); |
151 | 0 | mExtensionHosts.Put(aPolicy.MozExtensionHostname(), &aPolicy); |
152 | 0 | return true; |
153 | 0 | } |
154 | | |
155 | | bool |
156 | | ExtensionPolicyService::UnregisterExtension(WebExtensionPolicy& aPolicy) |
157 | 0 | { |
158 | 0 | bool ok = (GetByID(aPolicy.Id()) == &aPolicy && |
159 | 0 | GetByHost(aPolicy.MozExtensionHostname()) == &aPolicy); |
160 | 0 | MOZ_ASSERT(ok); |
161 | 0 |
|
162 | 0 | if (!ok) { |
163 | 0 | return false; |
164 | 0 | } |
165 | 0 | |
166 | 0 | mExtensions.Remove(aPolicy.Id()); |
167 | 0 | mExtensionHosts.Remove(aPolicy.MozExtensionHostname()); |
168 | 0 | return true; |
169 | 0 | } |
170 | | |
171 | | bool |
172 | | ExtensionPolicyService::RegisterObserver(DocumentObserver& aObserver) |
173 | 0 | { |
174 | 0 | if (mObservers.GetWeak(&aObserver)) { |
175 | 0 | return false; |
176 | 0 | } |
177 | 0 | |
178 | 0 | mObservers.Put(&aObserver, &aObserver); |
179 | 0 | return true; |
180 | 0 | } |
181 | | |
182 | | bool |
183 | | ExtensionPolicyService::UnregisterObserver(DocumentObserver& aObserver) |
184 | 0 | { |
185 | 0 | if (!mObservers.GetWeak(&aObserver)) { |
186 | 0 | return false; |
187 | 0 | } |
188 | 0 | |
189 | 0 | mObservers.Remove(&aObserver); |
190 | 0 | return true; |
191 | 0 | } |
192 | | |
193 | | |
194 | | void |
195 | | ExtensionPolicyService::BaseCSP(nsAString& aBaseCSP) const |
196 | 0 | { |
197 | 0 | nsresult rv; |
198 | 0 |
|
199 | 0 | rv = Preferences::GetString("extensions.webextensions.base-content-security-policy", aBaseCSP); |
200 | 0 | if (NS_FAILED(rv)) { |
201 | 0 | aBaseCSP.AssignLiteral(DEFAULT_BASE_CSP); |
202 | 0 | } |
203 | 0 | } |
204 | | |
205 | | void |
206 | | ExtensionPolicyService::DefaultCSP(nsAString& aDefaultCSP) const |
207 | 0 | { |
208 | 0 | nsresult rv; |
209 | 0 |
|
210 | 0 | rv = Preferences::GetString("extensions.webextensions.default-content-security-policy", aDefaultCSP); |
211 | 0 | if (NS_FAILED(rv)) { |
212 | 0 | aDefaultCSP.AssignLiteral(DEFAULT_DEFAULT_CSP); |
213 | 0 | } |
214 | 0 | } |
215 | | |
216 | | |
217 | | /***************************************************************************** |
218 | | * nsIMemoryReporter |
219 | | *****************************************************************************/ |
220 | | |
221 | | NS_IMETHODIMP |
222 | | ExtensionPolicyService::CollectReports(nsIHandleReportCallback* aHandleReport, |
223 | | nsISupports* aData, bool aAnonymize) |
224 | 0 | { |
225 | 0 | for (auto iter = mExtensions.Iter(); !iter.Done(); iter.Next()) { |
226 | 0 | auto& ext = iter.Data(); |
227 | 0 |
|
228 | 0 | nsAtomCString id(ext->Id()); |
229 | 0 |
|
230 | 0 | NS_ConvertUTF16toUTF8 name(ext->Name()); |
231 | 0 | name.ReplaceSubstring("\"", ""); |
232 | 0 | name.ReplaceSubstring("\\", ""); |
233 | 0 |
|
234 | 0 | nsString url; |
235 | 0 | MOZ_TRY_VAR(url, ext->GetURL(NS_LITERAL_STRING(""))); |
236 | 0 |
|
237 | 0 | nsPrintfCString desc("Extension(id=%s, name=\"%s\", baseURL=%s)", |
238 | 0 | id.get(), name.get(), |
239 | 0 | NS_ConvertUTF16toUTF8(url).get()); |
240 | 0 | desc.ReplaceChar('/', '\\'); |
241 | 0 |
|
242 | 0 | nsCString path("extensions/"); |
243 | 0 | path.Append(desc); |
244 | 0 |
|
245 | 0 | aHandleReport->Callback( |
246 | 0 | EmptyCString(), path, |
247 | 0 | KIND_NONHEAP, UNITS_COUNT, 1, |
248 | 0 | NS_LITERAL_CSTRING("WebExtensions that are active in this session"), |
249 | 0 | aData); |
250 | 0 | } |
251 | 0 |
|
252 | 0 | return NS_OK; |
253 | 0 | } |
254 | | |
255 | | |
256 | | /***************************************************************************** |
257 | | * Content script management |
258 | | *****************************************************************************/ |
259 | | |
260 | | void |
261 | | ExtensionPolicyService::RegisterObservers() |
262 | 0 | { |
263 | 0 | mObs->AddObserver(this, "content-document-global-created", false); |
264 | 0 | mObs->AddObserver(this, "document-element-inserted", false); |
265 | 0 | mObs->AddObserver(this, "tab-content-frameloader-created", false); |
266 | 0 | if (XRE_IsContentProcess()) { |
267 | 0 | mObs->AddObserver(this, "http-on-opening-request", false); |
268 | 0 | } |
269 | 0 | } |
270 | | |
271 | | void |
272 | | ExtensionPolicyService::UnregisterObservers() |
273 | 0 | { |
274 | 0 | mObs->RemoveObserver(this, "content-document-global-created"); |
275 | 0 | mObs->RemoveObserver(this, "document-element-inserted"); |
276 | 0 | mObs->RemoveObserver(this, "tab-content-frameloader-created"); |
277 | 0 | if (XRE_IsContentProcess()) { |
278 | 0 | mObs->RemoveObserver(this, "http-on-opening-request"); |
279 | 0 | } |
280 | 0 | } |
281 | | |
282 | | nsresult |
283 | | ExtensionPolicyService::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) |
284 | 0 | { |
285 | 0 | if (!strcmp(aTopic, "content-document-global-created")) { |
286 | 0 | nsCOMPtr<nsPIDOMWindowOuter> win = do_QueryInterface(aSubject); |
287 | 0 | if (win) { |
288 | 0 | CheckWindow(win); |
289 | 0 | } |
290 | 0 | } else if (!strcmp(aTopic, "document-element-inserted")) { |
291 | 0 | nsCOMPtr<nsIDocument> doc = do_QueryInterface(aSubject); |
292 | 0 | if (doc) { |
293 | 0 | CheckDocument(doc); |
294 | 0 | } |
295 | 0 | } else if (!strcmp(aTopic, "http-on-opening-request")) { |
296 | 0 | nsCOMPtr<nsIChannel> chan = do_QueryInterface(aSubject); |
297 | 0 | if (chan) { |
298 | 0 | CheckRequest(chan); |
299 | 0 | } |
300 | 0 | } else if (!strcmp(aTopic, "tab-content-frameloader-created")) { |
301 | 0 | RefPtr<ContentFrameMessageManager> mm = do_QueryObject(aSubject); |
302 | 0 | NS_ENSURE_TRUE(mm, NS_ERROR_UNEXPECTED); |
303 | 0 |
|
304 | 0 | mMessageManagers.PutEntry(mm); |
305 | 0 |
|
306 | 0 | mm->AddSystemEventListener(NS_LITERAL_STRING("unload"), this, |
307 | 0 | false, false); |
308 | 0 | } |
309 | 0 | return NS_OK; |
310 | 0 | } |
311 | | |
312 | | nsresult |
313 | | ExtensionPolicyService::HandleEvent(dom::Event* aEvent) |
314 | 0 | { |
315 | 0 | RefPtr<ContentFrameMessageManager> mm = do_QueryObject(aEvent->GetTarget()); |
316 | 0 | MOZ_ASSERT(mm); |
317 | 0 | if (mm) { |
318 | 0 | mMessageManagers.RemoveEntry(mm); |
319 | 0 | } |
320 | 0 | return NS_OK; |
321 | 0 | } |
322 | | |
323 | | nsresult |
324 | | ForEachDocShell(nsIDocShell* aDocShell, |
325 | | const std::function<nsresult(nsIDocShell*)>& aCallback) |
326 | 0 | { |
327 | 0 | nsCOMPtr<nsISimpleEnumerator> iter; |
328 | 0 | MOZ_TRY(aDocShell->GetDocShellEnumerator(nsIDocShell::typeContent, |
329 | 0 | nsIDocShell::ENUMERATE_FORWARDS, |
330 | 0 | getter_AddRefs(iter))); |
331 | 0 |
|
332 | 0 | for (auto& docShell : SimpleEnumerator<nsIDocShell>(iter)) { |
333 | 0 | MOZ_TRY(aCallback(docShell)); |
334 | 0 | } |
335 | 0 | return NS_OK; |
336 | 0 | } |
337 | | |
338 | | |
339 | | already_AddRefed<Promise> |
340 | | ExtensionPolicyService::ExecuteContentScript(nsPIDOMWindowInner* aWindow, |
341 | | WebExtensionContentScript& aScript) |
342 | 0 | { |
343 | 0 | if (!aWindow->IsCurrentInnerWindow()) { |
344 | 0 | return nullptr; |
345 | 0 | } |
346 | 0 | |
347 | 0 | RefPtr<Promise> promise; |
348 | 0 | ProcessScript().LoadContentScript(&aScript, aWindow, getter_AddRefs(promise)); |
349 | 0 | return promise.forget(); |
350 | 0 | } |
351 | | |
352 | | RefPtr<Promise> |
353 | | ExtensionPolicyService::ExecuteContentScripts(JSContext* aCx, nsPIDOMWindowInner* aWindow, |
354 | | const nsTArray<RefPtr<WebExtensionContentScript>>& aScripts) |
355 | 0 | { |
356 | 0 | AutoTArray<RefPtr<Promise>, 8> promises; |
357 | 0 |
|
358 | 0 | for (auto& script : aScripts) { |
359 | 0 | if (RefPtr<Promise> promise = ExecuteContentScript(aWindow, *script)) { |
360 | 0 | promises.AppendElement(std::move(promise)); |
361 | 0 | } |
362 | 0 | } |
363 | 0 |
|
364 | 0 | RefPtr<Promise> promise = Promise::All(aCx, promises, IgnoreErrors()); |
365 | 0 | MOZ_RELEASE_ASSERT(promise); |
366 | 0 | return promise; |
367 | 0 | } |
368 | | |
369 | | nsresult |
370 | | ExtensionPolicyService::InjectContentScripts(WebExtensionPolicy* aExtension) |
371 | 0 | { |
372 | 0 | AutoJSAPI jsapi; |
373 | 0 | MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope())); |
374 | 0 |
|
375 | 0 | for (auto iter = mMessageManagers.ConstIter(); !iter.Done(); iter.Next()) { |
376 | 0 | ContentFrameMessageManager* mm = iter.Get()->GetKey(); |
377 | 0 |
|
378 | 0 | nsCOMPtr<nsIDocShell> docShell = mm->GetDocShell(IgnoreErrors()); |
379 | 0 | NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED); |
380 | 0 |
|
381 | 0 | auto result = ForEachDocShell(docShell, [&](nsIDocShell* aDocShell) -> nsresult { |
382 | 0 | nsCOMPtr<nsPIDOMWindowOuter> win = aDocShell->GetWindow(); |
383 | 0 | if (!win->GetDocumentURI()) { |
384 | 0 | return NS_OK; |
385 | 0 | } |
386 | 0 | DocInfo docInfo(win); |
387 | 0 |
|
388 | 0 | using RunAt = dom::ContentScriptRunAt; |
389 | 0 | using Scripts = AutoTArray<RefPtr<WebExtensionContentScript>, 8>; |
390 | 0 |
|
391 | 0 | constexpr uint8_t n = uint8_t(RunAt::EndGuard_); |
392 | 0 | Scripts scripts[n]; |
393 | 0 |
|
394 | 0 | auto GetScripts = [&](RunAt aRunAt) -> Scripts&& { |
395 | 0 | return std::move(scripts[uint8_t(aRunAt)]); |
396 | 0 | }; |
397 | 0 |
|
398 | 0 | for (const auto& script : aExtension->ContentScripts()) { |
399 | 0 | if (script->Matches(docInfo)) { |
400 | 0 | GetScripts(script->RunAt()).AppendElement(script); |
401 | 0 | } |
402 | 0 | } |
403 | 0 |
|
404 | 0 | nsCOMPtr<nsPIDOMWindowInner> inner = win->GetCurrentInnerWindow(); |
405 | 0 |
|
406 | 0 | MOZ_TRY(ExecuteContentScripts(jsapi.cx(), inner, GetScripts(RunAt::Document_start)) |
407 | 0 | ->ThenWithCycleCollectedArgs([](JSContext* aCx, JS::HandleValue aValue, |
408 | 0 | ExtensionPolicyService* aSelf, |
409 | 0 | nsPIDOMWindowInner* aInner, |
410 | 0 | Scripts&& aScripts) { |
411 | 0 | return aSelf->ExecuteContentScripts(aCx, aInner, aScripts).forget(); |
412 | 0 | }, |
413 | 0 | this, inner, GetScripts(RunAt::Document_end)) |
414 | 0 | .andThen([&](auto aPromise) { |
415 | 0 | return aPromise->ThenWithCycleCollectedArgs([](JSContext* aCx, |
416 | 0 | JS::HandleValue aValue, |
417 | 0 | ExtensionPolicyService* aSelf, |
418 | 0 | nsPIDOMWindowInner* aInner, |
419 | 0 | Scripts&& aScripts) { |
420 | 0 | return aSelf->ExecuteContentScripts(aCx, aInner, aScripts).forget(); |
421 | 0 | }, |
422 | 0 | this, inner, GetScripts(RunAt::Document_idle)); |
423 | 0 | })); |
424 | 0 |
|
425 | 0 | return NS_OK; |
426 | 0 | }); |
427 | 0 | MOZ_TRY(result); |
428 | 0 | } |
429 | 0 | return NS_OK; |
430 | 0 | } |
431 | | |
432 | | // Checks a request for matching content scripts, and begins pre-loading them |
433 | | // if necessary. |
434 | | void |
435 | | ExtensionPolicyService::CheckRequest(nsIChannel* aChannel) |
436 | 0 | { |
437 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo(); |
438 | 0 | if (!loadInfo) { |
439 | 0 | return; |
440 | 0 | } |
441 | 0 | |
442 | 0 | auto loadType = loadInfo->GetExternalContentPolicyType(); |
443 | 0 | if (loadType != nsIContentPolicy::TYPE_DOCUMENT && |
444 | 0 | loadType != nsIContentPolicy::TYPE_SUBDOCUMENT) { |
445 | 0 | return; |
446 | 0 | } |
447 | 0 | |
448 | 0 | nsCOMPtr<nsIURI> uri; |
449 | 0 | if (NS_FAILED(aChannel->GetURI(getter_AddRefs(uri)))) { |
450 | 0 | return; |
451 | 0 | } |
452 | 0 | |
453 | 0 | CheckContentScripts({uri.get(), loadInfo}, true); |
454 | 0 | } |
455 | | |
456 | | static bool |
457 | | CheckParentFrames(nsPIDOMWindowOuter* aWindow, WebExtensionPolicy& aPolicy) |
458 | 0 | { |
459 | 0 | nsCOMPtr<nsIURI> aboutAddons; |
460 | 0 | if (NS_FAILED(NS_NewURI(getter_AddRefs(aboutAddons), "about:addons"))) { |
461 | 0 | return false; |
462 | 0 | } |
463 | 0 | |
464 | 0 | auto* piWin = aWindow; |
465 | 0 | while ((piWin = piWin->GetScriptableParentOrNull())) { |
466 | 0 | auto* win = nsGlobalWindowOuter::Cast(piWin); |
467 | 0 |
|
468 | 0 | auto* principal = BasePrincipal::Cast(win->GetPrincipal()); |
469 | 0 | if (nsContentUtils::IsSystemPrincipal(principal)) { |
470 | 0 | // The add-on manager is a special case, since it contains extension |
471 | 0 | // options pages in same-type <browser> frames. |
472 | 0 | bool equals; |
473 | 0 | if (NS_SUCCEEDED(win->GetDocumentURI()->Equals(aboutAddons, &equals)) && |
474 | 0 | equals) { |
475 | 0 | return true; |
476 | 0 | } |
477 | 0 | } |
478 | 0 | |
479 | 0 | if (principal->AddonPolicy() != &aPolicy) { |
480 | 0 | return false; |
481 | 0 | } |
482 | 0 | } |
483 | 0 |
|
484 | 0 | return true; |
485 | 0 | } |
486 | | |
487 | | // Checks a document, just after the document element has been inserted, for |
488 | | // matching content scripts or extension principals, and loads them if |
489 | | // necessary. |
490 | | void |
491 | | ExtensionPolicyService::CheckDocument(nsIDocument* aDocument) |
492 | 0 | { |
493 | 0 | nsCOMPtr<nsPIDOMWindowOuter> win = aDocument->GetWindow(); |
494 | 0 | if (win) { |
495 | 0 | nsIDocShell* docShell = win->GetDocShell(); |
496 | 0 | RefPtr<ContentFrameMessageManager> mm = docShell->GetMessageManager(); |
497 | 0 | if (!mm || !mMessageManagers.Contains(mm)) { |
498 | 0 | return; |
499 | 0 | } |
500 | 0 | |
501 | 0 | if (win->GetDocumentURI()) { |
502 | 0 | CheckContentScripts(win.get(), false); |
503 | 0 | } |
504 | 0 |
|
505 | 0 | nsIPrincipal* principal = aDocument->NodePrincipal(); |
506 | 0 |
|
507 | 0 | RefPtr<WebExtensionPolicy> policy = BasePrincipal::Cast(principal)->AddonPolicy(); |
508 | 0 | if (policy) { |
509 | 0 | bool privileged = IsExtensionProcess() && CheckParentFrames(win, *policy); |
510 | 0 |
|
511 | 0 | ProcessScript().InitExtensionDocument(policy, aDocument, privileged); |
512 | 0 | } |
513 | 0 | } |
514 | 0 | } |
515 | | |
516 | | // Checks for loads of about:blank into new window globals, and loads any |
517 | | // matching content scripts. about:blank loads do not trigger document element |
518 | | // inserted events, so they're the only load type that are special cased this |
519 | | // way. |
520 | | void |
521 | | ExtensionPolicyService::CheckWindow(nsPIDOMWindowOuter* aWindow) |
522 | 0 | { |
523 | 0 | // We only care about non-initial document loads here. The initial |
524 | 0 | // about:blank document will usually be re-used to load another document. |
525 | 0 | nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc(); |
526 | 0 | if (!doc || doc->IsInitialDocument() || |
527 | 0 | doc->GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED) { |
528 | 0 | return; |
529 | 0 | } |
530 | 0 | |
531 | 0 | nsCOMPtr<nsIURI> docUri = doc->GetDocumentURI(); |
532 | 0 | nsCOMPtr<nsIURI> uri; |
533 | 0 | if (!docUri || NS_FAILED(NS_GetURIWithoutRef(docUri, getter_AddRefs(uri))) || |
534 | 0 | !NS_IsAboutBlank(uri)) { |
535 | 0 | return; |
536 | 0 | } |
537 | 0 | |
538 | 0 | nsIDocShell* docShell = aWindow->GetDocShell(); |
539 | 0 | if (RefPtr<ContentFrameMessageManager> mm = docShell->GetMessageManager()) { |
540 | 0 | if (mMessageManagers.Contains(mm)) { |
541 | 0 | CheckContentScripts(aWindow, false); |
542 | 0 | } |
543 | 0 | } |
544 | 0 | } |
545 | | |
546 | | void |
547 | | ExtensionPolicyService::CheckContentScripts(const DocInfo& aDocInfo, bool aIsPreload) |
548 | 0 | { |
549 | 0 | nsCOMPtr<nsPIDOMWindowInner> win; |
550 | 0 | if (!aIsPreload) { |
551 | 0 | win = aDocInfo.GetWindow()->GetCurrentInnerWindow(); |
552 | 0 | } |
553 | 0 |
|
554 | 0 | for (auto iter = mExtensions.Iter(); !iter.Done(); iter.Next()) { |
555 | 0 | RefPtr<WebExtensionPolicy> policy = iter.Data(); |
556 | 0 |
|
557 | 0 | for (auto& script : policy->ContentScripts()) { |
558 | 0 | if (script->Matches(aDocInfo)) { |
559 | 0 | if (aIsPreload) { |
560 | 0 | ProcessScript().PreloadContentScript(script); |
561 | 0 | } else { |
562 | 0 | if (!win->IsCurrentInnerWindow()) { |
563 | 0 | break; |
564 | 0 | } |
565 | 0 | RefPtr<Promise> promise; |
566 | 0 | ProcessScript().LoadContentScript(script, win, getter_AddRefs(promise)); |
567 | 0 | } |
568 | 0 | } |
569 | 0 | } |
570 | 0 | } |
571 | 0 |
|
572 | 0 | for (auto iter = mObservers.Iter(); !iter.Done(); iter.Next()) { |
573 | 0 | RefPtr<DocumentObserver> observer = iter.Data(); |
574 | 0 |
|
575 | 0 | for (auto& matcher : observer->Matchers()) { |
576 | 0 | if (matcher->Matches(aDocInfo)) { |
577 | 0 | if (aIsPreload) { |
578 | 0 | observer->NotifyMatch(*matcher, aDocInfo.GetLoadInfo()); |
579 | 0 | } else { |
580 | 0 | observer->NotifyMatch(*matcher, aDocInfo.GetWindow()); |
581 | 0 | } |
582 | 0 | } |
583 | 0 | } |
584 | 0 | } |
585 | 0 | } |
586 | | |
587 | | |
588 | | /***************************************************************************** |
589 | | * nsIAddonPolicyService |
590 | | *****************************************************************************/ |
591 | | |
592 | | nsresult |
593 | | ExtensionPolicyService::GetBaseCSP(nsAString& aBaseCSP) |
594 | 0 | { |
595 | 0 | BaseCSP(aBaseCSP); |
596 | 0 | return NS_OK; |
597 | 0 | } |
598 | | |
599 | | nsresult |
600 | | ExtensionPolicyService::GetDefaultCSP(nsAString& aDefaultCSP) |
601 | 0 | { |
602 | 0 | DefaultCSP(aDefaultCSP); |
603 | 0 | return NS_OK; |
604 | 0 | } |
605 | | |
606 | | nsresult |
607 | | ExtensionPolicyService::GetAddonCSP(const nsAString& aAddonId, |
608 | | nsAString& aResult) |
609 | 0 | { |
610 | 0 | if (WebExtensionPolicy* policy = GetByID(aAddonId)) { |
611 | 0 | policy->GetContentSecurityPolicy(aResult); |
612 | 0 | return NS_OK; |
613 | 0 | } |
614 | 0 | return NS_ERROR_INVALID_ARG; |
615 | 0 | } |
616 | | |
617 | | nsresult |
618 | | ExtensionPolicyService::GetGeneratedBackgroundPageUrl(const nsACString& aHostname, |
619 | | nsACString& aResult) |
620 | 0 | { |
621 | 0 | if (WebExtensionPolicy* policy = GetByHost(aHostname)) { |
622 | 0 | nsAutoCString url("data:text/html,"); |
623 | 0 |
|
624 | 0 | nsCString html = policy->BackgroundPageHTML(); |
625 | 0 | nsAutoCString escaped; |
626 | 0 |
|
627 | 0 | url.Append(NS_EscapeURL(html, esc_Minimal, escaped)); |
628 | 0 |
|
629 | 0 | aResult = url; |
630 | 0 | return NS_OK; |
631 | 0 | } |
632 | 0 | return NS_ERROR_INVALID_ARG; |
633 | 0 | } |
634 | | |
635 | | nsresult |
636 | | ExtensionPolicyService::AddonHasPermission(const nsAString& aAddonId, |
637 | | const nsAString& aPerm, |
638 | | bool* aResult) |
639 | 0 | { |
640 | 0 | if (WebExtensionPolicy* policy = GetByID(aAddonId)) { |
641 | 0 | *aResult = policy->HasPermission(aPerm); |
642 | 0 | return NS_OK; |
643 | 0 | } |
644 | 0 | return NS_ERROR_INVALID_ARG; |
645 | 0 | } |
646 | | |
647 | | nsresult |
648 | | ExtensionPolicyService::AddonMayLoadURI(const nsAString& aAddonId, |
649 | | nsIURI* aURI, |
650 | | bool aExplicit, |
651 | | bool* aResult) |
652 | 0 | { |
653 | 0 | if (WebExtensionPolicy* policy = GetByID(aAddonId)) { |
654 | 0 | *aResult = policy->CanAccessURI(aURI, aExplicit); |
655 | 0 | return NS_OK; |
656 | 0 | } |
657 | 0 | return NS_ERROR_INVALID_ARG; |
658 | 0 | } |
659 | | |
660 | | nsresult |
661 | | ExtensionPolicyService::GetExtensionName(const nsAString& aAddonId, |
662 | | nsAString& aName) |
663 | 0 | { |
664 | 0 | if (WebExtensionPolicy* policy = GetByID(aAddonId)) { |
665 | 0 | aName.Assign(policy->Name()); |
666 | 0 | return NS_OK; |
667 | 0 | } |
668 | 0 | return NS_ERROR_INVALID_ARG; |
669 | 0 | } |
670 | | |
671 | | nsresult |
672 | | ExtensionPolicyService::ExtensionURILoadableByAnyone(nsIURI* aURI, bool* aResult) |
673 | 0 | { |
674 | 0 | URLInfo url(aURI); |
675 | 0 | if (WebExtensionPolicy* policy = GetByURL(url)) { |
676 | 0 | *aResult = policy->IsPathWebAccessible(url.FilePath()); |
677 | 0 | return NS_OK; |
678 | 0 | } |
679 | 0 | return NS_ERROR_INVALID_ARG; |
680 | 0 | } |
681 | | |
682 | | nsresult |
683 | | ExtensionPolicyService::ExtensionURIToAddonId(nsIURI* aURI, nsAString& aResult) |
684 | 0 | { |
685 | 0 | if (WebExtensionPolicy* policy = GetByURL(aURI)) { |
686 | 0 | policy->GetId(aResult); |
687 | 0 | } else { |
688 | 0 | aResult.SetIsVoid(true); |
689 | 0 | } |
690 | 0 | return NS_OK; |
691 | 0 | } |
692 | | |
693 | | |
694 | | NS_IMPL_CYCLE_COLLECTION(ExtensionPolicyService, mExtensions, mExtensionHosts, |
695 | | mObservers) |
696 | | |
697 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtensionPolicyService) |
698 | 0 | NS_INTERFACE_MAP_ENTRY(nsIAddonPolicyService) |
699 | 0 | NS_INTERFACE_MAP_ENTRY(nsIObserver) |
700 | 0 | NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) |
701 | 0 | NS_INTERFACE_MAP_ENTRY(nsIMemoryReporter) |
702 | 0 | NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAddonPolicyService) |
703 | 0 | NS_INTERFACE_MAP_END |
704 | | |
705 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(ExtensionPolicyService) |
706 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(ExtensionPolicyService) |
707 | | |
708 | | } // namespace mozilla |