TimeoutHandler.cpp (8035B)
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 "TimeoutHandler.h" 8 9 #include "mozilla/Assertions.h" 10 #include "mozilla/HoldDropJSObjects.h" 11 12 namespace mozilla::dom { 13 14 //----------------------------------------------------------------------------- 15 // TimeoutHandler 16 //----------------------------------------------------------------------------- 17 18 bool TimeoutHandler::Call(const char* /* unused */) { return false; } 19 20 void TimeoutHandler::GetDescription(nsACString& aOutString) { 21 aOutString.AppendPrintf("<generic handler> (%s:%d:%d)", 22 mCaller.FileName().get(), mCaller.mLine, 23 mCaller.mColumn); 24 } 25 26 //----------------------------------------------------------------------------- 27 // ScriptTimeoutHandler 28 //----------------------------------------------------------------------------- 29 30 ScriptTimeoutHandler::ScriptTimeoutHandler(JSContext* aCx, 31 nsIGlobalObject* aGlobal, 32 const nsAString& aExpression) 33 : TimeoutHandler(aCx), mGlobal(aGlobal), mExpr(aExpression) {} 34 35 NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptTimeoutHandler) 36 37 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptTimeoutHandler) 38 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) 39 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 40 41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(ScriptTimeoutHandler) 42 if (MOZ_UNLIKELY(cb.WantDebugInfo())) { 43 nsAutoCString name("ScriptTimeoutHandler"); 44 name.AppendLiteral(" ["); 45 name.Append(tmp->mCaller.FileName()); 46 name.Append(':'); 47 name.AppendInt(tmp->mCaller.mLine); 48 name.Append(':'); 49 name.AppendInt(tmp->mCaller.mColumn); 50 name.Append(']'); 51 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name.get()); 52 } else { 53 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(ScriptTimeoutHandler, tmp->mRefCnt.get()) 54 } 55 56 // If we need to make TimeoutHandler CCed, don't call its Traverse method 57 // here, otherwise we ends up report same object twice if logging is on. See 58 // https://bugzilla.mozilla.org/show_bug.cgi?id=1588208. 59 60 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) 61 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 62 63 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScriptTimeoutHandler) 64 NS_INTERFACE_MAP_ENTRY(nsISupports) 65 NS_INTERFACE_MAP_END 66 67 NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptTimeoutHandler) 68 NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptTimeoutHandler) 69 70 void ScriptTimeoutHandler::GetDescription(nsACString& aOutString) { 71 if (mExpr.Length() > 15) { 72 aOutString.AppendPrintf( 73 "<string handler (truncated): \"%s...\"> (%s:%d:%d)", 74 NS_ConvertUTF16toUTF8(Substring(mExpr, 0, 13)).get(), 75 mCaller.FileName().get(), mCaller.mLine, mCaller.mColumn); 76 } else { 77 aOutString.AppendPrintf("<string handler: \"%s\"> (%s:%d:%d)", 78 NS_ConvertUTF16toUTF8(mExpr).get(), 79 mCaller.FileName().get(), mCaller.mLine, 80 mCaller.mColumn); 81 } 82 } 83 84 //----------------------------------------------------------------------------- 85 // CallbackTimeoutHandler 86 //----------------------------------------------------------------------------- 87 88 CallbackTimeoutHandler::CallbackTimeoutHandler( 89 JSContext* aCx, nsIGlobalObject* aGlobal, Function* aFunction, 90 nsTArray<JS::Heap<JS::Value>>&& aArguments) 91 : TimeoutHandler(aCx), mGlobal(aGlobal), mFunction(aFunction) { 92 mozilla::HoldJSObjectsWithKey(this); 93 mArgs = std::move(aArguments); 94 } 95 96 NS_IMPL_CYCLE_COLLECTION_CLASS(CallbackTimeoutHandler) 97 98 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CallbackTimeoutHandler) 99 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) 100 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFunction) 101 tmp->ReleaseJSObjects(); 102 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 103 104 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(CallbackTimeoutHandler) 105 if (MOZ_UNLIKELY(cb.WantDebugInfo())) { 106 nsAutoCString name("CallbackTimeoutHandler"); 107 JSObject* obj = tmp->mFunction->CallablePreserveColor(); 108 JSFunction* fun = 109 JS_GetObjectFunction(js::UncheckedUnwrapWithoutExpose(obj)); 110 if (fun && JS_GetMaybePartialFunctionId(fun)) { 111 JSLinearString* funId = 112 JS_ASSERT_STRING_IS_LINEAR(JS_GetMaybePartialFunctionId(fun)); 113 size_t size = 1 + JS_PutEscapedLinearString(nullptr, 0, funId, 0); 114 char* funIdName = new char[size]; 115 if (funIdName) { 116 JS_PutEscapedLinearString(funIdName, size, funId, 0); 117 name.AppendLiteral(" ["); 118 name.Append(funIdName); 119 delete[] funIdName; 120 name.Append(']'); 121 } 122 } 123 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name.get()); 124 } else { 125 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(CallbackTimeoutHandler, 126 tmp->mRefCnt.get()) 127 } 128 129 // If we need to make TimeoutHandler CCed, don't call its Traverse method 130 // here, otherwise we ends up report same object twice if logging is on. See 131 // https://bugzilla.mozilla.org/show_bug.cgi?id=1588208. 132 133 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) 134 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFunction) 135 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 136 137 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CallbackTimeoutHandler) 138 for (size_t i = 0; i < tmp->mArgs.Length(); ++i) { 139 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgs[i]) 140 } 141 NS_IMPL_CYCLE_COLLECTION_TRACE_END 142 143 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CallbackTimeoutHandler) 144 NS_INTERFACE_MAP_ENTRY(nsISupports) 145 NS_INTERFACE_MAP_END 146 147 NS_IMPL_CYCLE_COLLECTING_ADDREF(CallbackTimeoutHandler) 148 NS_IMPL_CYCLE_COLLECTING_RELEASE(CallbackTimeoutHandler) 149 150 void CallbackTimeoutHandler::ReleaseJSObjects() { 151 mArgs.Clear(); 152 mozilla::DropJSObjectsWithKey(this); 153 } 154 155 // This conversion is safe because JS::Heap<T> and T share the same 156 // representation, and the codegen for Call() takes care of the Heap<>'s read 157 // barrier. 158 // TODO(emilio, bug 1986753): Remove this. 159 static const nsTArray<JS::Value>& CastArgs( 160 const nsTArray<JS::Heap<JS::Value>>& aArgs) { 161 static_assert(sizeof(JS::Value) == sizeof(JS::Heap<JS::Value>), 162 "JS::Heap<Value> must be binary compatible with Value."); 163 return *reinterpret_cast<const nsTArray<JS::Value>*>(&aArgs); 164 } 165 166 bool CallbackTimeoutHandler::Call(const char* aExecutionReason) { 167 IgnoredErrorResult rv; 168 JS::Rooted<JS::Value> ignoredVal(RootingCx()); 169 MOZ_KnownLive(mFunction)->Call(MOZ_KnownLive(mGlobal), CastArgs(mArgs), 170 &ignoredVal, rv, aExecutionReason); 171 return !rv.IsUncatchableException(); 172 } 173 174 void CallbackTimeoutHandler::MarkForCC() { mFunction->MarkForCC(); } 175 176 void CallbackTimeoutHandler::GetDescription(nsACString& aOutString) { 177 mFunction->GetDescription(aOutString); 178 } 179 180 //----------------------------------------------------------------------------- 181 // DelayedJSDispatchableHandler 182 //----------------------------------------------------------------------------- 183 184 MOZ_CAN_RUN_SCRIPT bool DelayedJSDispatchableHandler::Call( 185 const char* /* unused */) { 186 MOZ_ASSERT(mDispatchable); 187 188 // We get the cx in whatever state, as if we have already shutdown 189 // then the notify task will already be cleared. 190 JSContext* cx = danger::GetJSContext(); 191 192 JS::Dispatchable::Run(cx, std::move(mDispatchable), 193 JS::Dispatchable::NotShuttingDown); 194 return true; 195 } 196 197 DelayedJSDispatchableHandler::~DelayedJSDispatchableHandler() { 198 if (mDispatchable) { 199 // If we shutdown with the DelayedJSDispatchableHandler still holding 200 // the reference to mDispatchable, release it to the engine for cleanup. 201 // In the case of WaitAsyncTimeoutTask, this will clear the task, and 202 // delete itself. 203 JS::Dispatchable::ReleaseFailedTask(std::move(mDispatchable)); 204 } 205 } 206 207 NS_IMPL_ISUPPORTS(DelayedJSDispatchableHandler, nsISupports) 208 209 } // namespace mozilla::dom