Bug 1852754: part 9) Add tests for dynamically loading <link rel="prefetch"> elements...
[gecko.git] / dom / script / AutoEntryScript.cpp
blobf1afa25b484cd846ac13861e94d4825e69708161
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/. */
7 #include "mozilla/dom/AutoEntryScript.h"
9 #include <stdint.h>
10 #include <utility>
11 #include "js/ProfilingCategory.h"
12 #include "js/ProfilingStack.h"
13 #include "jsapi.h"
14 #include "mozilla/Assertions.h"
15 #include "mozilla/Maybe.h"
16 #include "mozilla/Span.h"
17 #include "nsCOMPtr.h"
18 #include "nsContentUtils.h"
19 #include "nsGlobalWindowInner.h"
20 #include "nsIDocShell.h"
21 #include "nsJSUtils.h"
22 #include "nsPIDOMWindow.h"
23 #include "nsPIDOMWindowInlines.h"
24 #include "nsString.h"
25 #include "xpcpublic.h"
27 namespace mozilla::dom {
29 namespace {
30 // Assert if it's not safe to run script. The helper class
31 // AutoAllowLegacyScriptExecution allows to allow-list
32 // legacy cases where it's actually not safe to run script.
33 #ifdef DEBUG
34 void AssertIfNotSafeToRunScript() {
35 // if it's safe to run script, then there is nothing to do here.
36 if (nsContentUtils::IsSafeToRunScript()) {
37 return;
40 // auto allowing legacy script execution is fine for now.
41 if (AutoAllowLegacyScriptExecution::IsAllowed()) {
42 return;
45 MOZ_ASSERT(false, "is it safe to run script?");
47 #endif
49 static unsigned long gRunToCompletionListeners = 0;
51 } // namespace
53 void UseEntryScriptProfiling() {
54 MOZ_ASSERT(NS_IsMainThread());
55 ++gRunToCompletionListeners;
58 void UnuseEntryScriptProfiling() {
59 MOZ_ASSERT(NS_IsMainThread());
60 MOZ_ASSERT(gRunToCompletionListeners > 0);
61 --gRunToCompletionListeners;
64 AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
65 const char* aReason, bool aIsMainThread)
66 : AutoJSAPI(aGlobalObject, aIsMainThread, eEntryScript),
67 mWebIDLCallerPrincipal(nullptr)
68 // This relies on us having a cx() because the AutoJSAPI constructor
69 // already ran.
71 mCallerOverride(cx()),
72 mAutoProfilerLabel(
73 "", aReason, JS::ProfilingCategoryPair::JS,
74 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS)),
75 mJSThreadExecution(aGlobalObject, aIsMainThread) {
76 MOZ_ASSERT(aGlobalObject);
78 if (aIsMainThread) {
79 #ifdef DEBUG
80 AssertIfNotSafeToRunScript();
81 #endif
82 if (gRunToCompletionListeners > 0) {
83 mDocShellEntryMonitor.emplace(cx(), aReason);
85 mScriptActivity.emplace(true);
89 AutoEntryScript::AutoEntryScript(JSObject* aObject, const char* aReason,
90 bool aIsMainThread)
91 : AutoEntryScript(xpc::NativeGlobal(aObject), aReason, aIsMainThread) {
92 // xpc::NativeGlobal uses JS::GetNonCCWObjectGlobal, which asserts that
93 // aObject is not a CCW.
96 AutoEntryScript::~AutoEntryScript() = default;
98 AutoEntryScript::DocshellEntryMonitor::DocshellEntryMonitor(JSContext* aCx,
99 const char* aReason)
100 : JS::dbg::AutoEntryMonitor(aCx), mReason(aReason) {}
102 void AutoEntryScript::DocshellEntryMonitor::Entry(
103 JSContext* aCx, JSFunction* aFunction, JSScript* aScript,
104 JS::Handle<JS::Value> aAsyncStack, const char* aAsyncCause) {
105 JS::Rooted<JSFunction*> rootedFunction(aCx);
106 if (aFunction) {
107 rootedFunction = aFunction;
109 JS::Rooted<JSScript*> rootedScript(aCx);
110 if (aScript) {
111 rootedScript = aScript;
114 nsCOMPtr<nsPIDOMWindowInner> window = xpc::CurrentWindowOrNull(aCx);
115 if (!window || !window->GetDocShell() ||
116 !window->GetDocShell()->GetRecordProfileTimelineMarkers()) {
117 return;
120 nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell();
122 nsAutoJSString functionName;
123 if (rootedFunction) {
124 JS::Rooted<JSString*> displayId(aCx,
125 JS_GetFunctionDisplayId(rootedFunction));
126 if (displayId) {
127 if (!functionName.init(aCx, displayId)) {
128 JS_ClearPendingException(aCx);
129 return;
134 nsString filename;
135 uint32_t lineNumber = 0;
136 if (!rootedScript) {
137 rootedScript = JS_GetFunctionScript(aCx, rootedFunction);
139 if (rootedScript) {
140 CopyUTF8toUTF16(MakeStringSpan(JS_GetScriptFilename(rootedScript)),
141 filename);
142 lineNumber = JS_GetScriptBaseLineNumber(aCx, rootedScript);
145 if (!filename.IsEmpty() || !functionName.IsEmpty()) {
146 docShellForJSRunToCompletion->NotifyJSRunToCompletionStart(
147 mReason, functionName, filename, lineNumber, aAsyncStack, aAsyncCause);
151 void AutoEntryScript::DocshellEntryMonitor::Exit(JSContext* aCx) {
152 nsCOMPtr<nsPIDOMWindowInner> window = xpc::CurrentWindowOrNull(aCx);
153 // Not really worth checking GetRecordProfileTimelineMarkers here.
154 if (window && window->GetDocShell()) {
155 nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell();
156 docShellForJSRunToCompletion->NotifyJSRunToCompletionStop();
160 } // namespace mozilla::dom