/src/mozilla-central/dom/bindings/BindingUtils.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 file, |
5 | | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "BindingUtils.h" |
8 | | |
9 | | #include <algorithm> |
10 | | #include <stdarg.h> |
11 | | |
12 | | #include "mozilla/Assertions.h" |
13 | | #include "mozilla/DebugOnly.h" |
14 | | #include "mozilla/FloatingPoint.h" |
15 | | #include "mozilla/Preferences.h" |
16 | | #include "mozilla/Unused.h" |
17 | | #include "mozilla/UseCounter.h" |
18 | | |
19 | | #include "AccessCheck.h" |
20 | | #include "js/JSON.h" |
21 | | #include "js/StableStringChars.h" |
22 | | #include "jsfriendapi.h" |
23 | | #include "nsContentCreatorFunctions.h" |
24 | | #include "nsContentUtils.h" |
25 | | #include "nsGlobalWindow.h" |
26 | | #include "nsHTMLTags.h" |
27 | | #include "nsIDocShell.h" |
28 | | #include "nsIDOMGlobalPropertyInitializer.h" |
29 | | #include "nsINode.h" |
30 | | #include "nsIPermissionManager.h" |
31 | | #include "nsIPrincipal.h" |
32 | | #include "nsIXPConnect.h" |
33 | | #include "nsUTF8Utils.h" |
34 | | #include "WorkerPrivate.h" |
35 | | #include "WorkerRunnable.h" |
36 | | #include "WrapperFactory.h" |
37 | | #include "xpcprivate.h" |
38 | | #include "XrayWrapper.h" |
39 | | #include "nsPrintfCString.h" |
40 | | #include "mozilla/Sprintf.h" |
41 | | #include "nsGlobalWindow.h" |
42 | | #include "nsReadableUtils.h" |
43 | | |
44 | | #include "mozilla/dom/ScriptSettings.h" |
45 | | #include "mozilla/dom/CustomElementRegistry.h" |
46 | | #include "mozilla/dom/DOMException.h" |
47 | | #include "mozilla/dom/ElementBinding.h" |
48 | | #include "mozilla/dom/HTMLObjectElement.h" |
49 | | #include "mozilla/dom/HTMLObjectElementBinding.h" |
50 | | #include "mozilla/dom/HTMLEmbedElement.h" |
51 | | #include "mozilla/dom/HTMLElementBinding.h" |
52 | | #include "mozilla/dom/HTMLEmbedElementBinding.h" |
53 | | #include "mozilla/dom/XULElementBinding.h" |
54 | | #include "mozilla/dom/XULFrameElementBinding.h" |
55 | | #include "mozilla/dom/XULMenuElementBinding.h" |
56 | | #include "mozilla/dom/XULPopupElementBinding.h" |
57 | | #include "mozilla/dom/Promise.h" |
58 | | #include "mozilla/dom/ResolveSystemBinding.h" |
59 | | #include "mozilla/dom/WebIDLGlobalNameHash.h" |
60 | | #include "mozilla/dom/WorkerPrivate.h" |
61 | | #include "mozilla/dom/WorkerScope.h" |
62 | | #include "mozilla/dom/XrayExpandoClass.h" |
63 | | #include "mozilla/dom/XULScrollElementBinding.h" |
64 | | #include "mozilla/jsipc/CrossProcessObjectWrappers.h" |
65 | | #include "ipc/ErrorIPCUtils.h" |
66 | | #include "mozilla/UseCounter.h" |
67 | | #include "mozilla/dom/DocGroup.h" |
68 | | #include "nsXULElement.h" |
69 | | |
70 | | namespace mozilla { |
71 | | namespace dom { |
72 | | |
73 | | // Forward declare GetConstructorObject methods. |
74 | | #define HTML_TAG(_tag, _classname, _interfacename) \ |
75 | | namespace HTML##_interfacename##Element_Binding { \ |
76 | | JSObject* GetConstructorObject(JSContext*); \ |
77 | | } |
78 | | #define HTML_OTHER(_tag) |
79 | | #include "nsHTMLTagList.h" |
80 | | #undef HTML_TAG |
81 | | #undef HTML_OTHER |
82 | | |
83 | | typedef JSObject* (*constructorGetterCallback)(JSContext*); |
84 | | |
85 | | // Mapping of html tag and GetConstructorObject methods. |
86 | | #define HTML_TAG(_tag, _classname, _interfacename) HTML##_interfacename##Element_Binding::GetConstructorObject, |
87 | | #define HTML_OTHER(_tag) nullptr, |
88 | | // We use eHTMLTag_foo (where foo is the tag) which is defined in nsHTMLTags.h |
89 | | // to index into this array. |
90 | | static const constructorGetterCallback sConstructorGetterCallback[] = { |
91 | | HTMLUnknownElement_Binding::GetConstructorObject, |
92 | | #include "nsHTMLTagList.h" |
93 | | #undef HTML_TAG |
94 | | #undef HTML_OTHER |
95 | | }; |
96 | | |
97 | | const JSErrorFormatString ErrorFormatString[] = { |
98 | | #define MSG_DEF(_name, _argc, _exn, _str) \ |
99 | | { #_name, _str, _argc, _exn }, |
100 | | #include "mozilla/dom/Errors.msg" |
101 | | #undef MSG_DEF |
102 | | }; |
103 | | |
104 | | #define MSG_DEF(_name, _argc, _exn, _str) \ |
105 | | static_assert(_argc < JS::MaxNumErrorArguments, \ |
106 | | #_name " must only have as many error arguments as the JS engine can support"); |
107 | | #include "mozilla/dom/Errors.msg" |
108 | | #undef MSG_DEF |
109 | | |
110 | | const JSErrorFormatString* |
111 | | GetErrorMessage(void* aUserRef, const unsigned aErrorNumber) |
112 | 0 | { |
113 | 0 | MOZ_ASSERT(aErrorNumber < ArrayLength(ErrorFormatString)); |
114 | 0 | return &ErrorFormatString[aErrorNumber]; |
115 | 0 | } |
116 | | |
117 | | uint16_t |
118 | | GetErrorArgCount(const ErrNum aErrorNumber) |
119 | 0 | { |
120 | 0 | return GetErrorMessage(nullptr, aErrorNumber)->argCount; |
121 | 0 | } |
122 | | |
123 | | void |
124 | | binding_detail::ThrowErrorMessage(JSContext* aCx, const unsigned aErrorNumber, ...) |
125 | 0 | { |
126 | 0 | va_list ap; |
127 | 0 | va_start(ap, aErrorNumber); |
128 | 0 | JS_ReportErrorNumberUTF8VA(aCx, GetErrorMessage, nullptr, aErrorNumber, ap); |
129 | 0 | va_end(ap); |
130 | 0 | } |
131 | | |
132 | | bool |
133 | | ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, |
134 | | bool aSecurityError, const char* aInterfaceName) |
135 | 0 | { |
136 | 0 | NS_ConvertASCIItoUTF16 ifaceName(aInterfaceName); |
137 | 0 | // This should only be called for DOM methods/getters/setters, which |
138 | 0 | // are JSNative-backed functions, so we can assume that |
139 | 0 | // JS_ValueToFunction and JS_GetFunctionDisplayId will both return |
140 | 0 | // non-null and that JS_GetStringCharsZ returns non-null. |
141 | 0 | JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, aArgs.calleev())); |
142 | 0 | MOZ_ASSERT(func); |
143 | 0 | JS::Rooted<JSString*> funcName(aCx, JS_GetFunctionDisplayId(func)); |
144 | 0 | MOZ_ASSERT(funcName); |
145 | 0 | nsAutoJSString funcNameStr; |
146 | 0 | if (!funcNameStr.init(aCx, funcName)) { |
147 | 0 | return false; |
148 | 0 | } |
149 | 0 | const ErrNum errorNumber = aSecurityError ? |
150 | 0 | MSG_METHOD_THIS_UNWRAPPING_DENIED : |
151 | 0 | MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE; |
152 | 0 | MOZ_RELEASE_ASSERT(GetErrorArgCount(errorNumber) <= 2); |
153 | 0 | JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr, |
154 | 0 | static_cast<unsigned>(errorNumber), |
155 | 0 | funcNameStr.get(), ifaceName.get()); |
156 | 0 | return false; |
157 | 0 | } |
158 | | |
159 | | bool |
160 | | ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, |
161 | | bool aSecurityError, |
162 | | prototypes::ID aProtoId) |
163 | 0 | { |
164 | 0 | return ThrowInvalidThis(aCx, aArgs, aSecurityError, |
165 | 0 | NamesOfInterfacesWithProtos(aProtoId)); |
166 | 0 | } |
167 | | |
168 | | bool |
169 | | ThrowNoSetterArg(JSContext* aCx, prototypes::ID aProtoId) |
170 | 0 | { |
171 | 0 | nsPrintfCString errorMessage("%s attribute setter", |
172 | 0 | NamesOfInterfacesWithProtos(aProtoId)); |
173 | 0 | return ThrowErrorMessage(aCx, MSG_MISSING_ARGUMENTS, errorMessage.get()); |
174 | 0 | } |
175 | | |
176 | | } // namespace dom |
177 | | |
178 | | namespace binding_danger { |
179 | | |
180 | | template<typename CleanupPolicy> |
181 | | struct TErrorResult<CleanupPolicy>::Message { |
182 | | Message() |
183 | | : mErrorNumber(dom::Err_Limit) |
184 | 0 | { |
185 | 0 | MOZ_COUNT_CTOR(TErrorResult::Message); |
186 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::Message::Message() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::Message::Message() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::Message::Message() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::Message::Message() |
187 | 0 | ~Message() { MOZ_COUNT_DTOR(TErrorResult::Message); } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::Message::~Message() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::Message::~Message() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::Message::~Message() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::Message::~Message() |
188 | | |
189 | | nsTArray<nsString> mArgs; |
190 | | dom::ErrNum mErrorNumber; |
191 | | |
192 | | bool HasCorrectNumberOfArguments() |
193 | 0 | { |
194 | 0 | return GetErrorArgCount(mErrorNumber) == mArgs.Length(); |
195 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::Message::HasCorrectNumberOfArguments() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::Message::HasCorrectNumberOfArguments() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::Message::HasCorrectNumberOfArguments() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::Message::HasCorrectNumberOfArguments() |
196 | | |
197 | | bool operator==(const TErrorResult<CleanupPolicy>::Message& aRight) const |
198 | 0 | { |
199 | 0 | return mErrorNumber == aRight.mErrorNumber && |
200 | 0 | mArgs == aRight.mArgs; |
201 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::Message::operator==(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::Message const&) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::Message::operator==(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::Message const&) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::Message::operator==(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::Message const&) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::Message::operator==(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::Message const&) const |
202 | | }; |
203 | | |
204 | | template<typename CleanupPolicy> |
205 | | nsTArray<nsString>& |
206 | | TErrorResult<CleanupPolicy>::CreateErrorMessageHelper(const dom::ErrNum errorNumber, |
207 | | nsresult errorType) |
208 | 0 | { |
209 | 0 | AssertInOwningThread(); |
210 | 0 | mResult = errorType; |
211 | 0 |
|
212 | 0 | Message* message = InitMessage(new Message()); |
213 | 0 | message->mErrorNumber = errorNumber; |
214 | 0 | return message->mArgs; |
215 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::CreateErrorMessageHelper(mozilla::dom::ErrNum, nsresult) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::CreateErrorMessageHelper(mozilla::dom::ErrNum, nsresult) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::CreateErrorMessageHelper(mozilla::dom::ErrNum, nsresult) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::CreateErrorMessageHelper(mozilla::dom::ErrNum, nsresult) |
216 | | |
217 | | template<typename CleanupPolicy> |
218 | | void |
219 | | TErrorResult<CleanupPolicy>::SerializeMessage(IPC::Message* aMsg) const |
220 | 0 | { |
221 | 0 | using namespace IPC; |
222 | 0 | AssertInOwningThread(); |
223 | 0 | MOZ_ASSERT(mUnionState == HasMessage); |
224 | 0 | MOZ_ASSERT(mExtra.mMessage); |
225 | 0 | WriteParam(aMsg, mExtra.mMessage->mArgs); |
226 | 0 | WriteParam(aMsg, mExtra.mMessage->mErrorNumber); |
227 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::SerializeMessage(IPC::Message*) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::SerializeMessage(IPC::Message*) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::SerializeMessage(IPC::Message*) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::SerializeMessage(IPC::Message*) const |
228 | | |
229 | | template<typename CleanupPolicy> |
230 | | bool |
231 | | TErrorResult<CleanupPolicy>::DeserializeMessage(const IPC::Message* aMsg, |
232 | | PickleIterator* aIter) |
233 | 0 | { |
234 | 0 | using namespace IPC; |
235 | 0 | AssertInOwningThread(); |
236 | 0 | nsAutoPtr<Message> readMessage(new Message()); |
237 | 0 | if (!ReadParam(aMsg, aIter, &readMessage->mArgs) || |
238 | 0 | !ReadParam(aMsg, aIter, &readMessage->mErrorNumber)) { |
239 | 0 | return false; |
240 | 0 | } |
241 | 0 | if (!readMessage->HasCorrectNumberOfArguments()) { |
242 | 0 | return false; |
243 | 0 | } |
244 | 0 | |
245 | 0 | MOZ_ASSERT(mUnionState == HasNothing); |
246 | 0 | InitMessage(readMessage.forget()); |
247 | | #ifdef DEBUG |
248 | | mUnionState = HasMessage; |
249 | | #endif // DEBUG |
250 | | return true; |
251 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::DeserializeMessage(IPC::Message const*, PickleIterator*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::DeserializeMessage(IPC::Message const*, PickleIterator*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::DeserializeMessage(IPC::Message const*, PickleIterator*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::DeserializeMessage(IPC::Message const*, PickleIterator*) |
252 | | |
253 | | template<typename CleanupPolicy> |
254 | | void |
255 | | TErrorResult<CleanupPolicy>::SetPendingExceptionWithMessage(JSContext* aCx) |
256 | 0 | { |
257 | 0 | AssertInOwningThread(); |
258 | 0 | MOZ_ASSERT(mUnionState == HasMessage); |
259 | 0 | MOZ_ASSERT(mExtra.mMessage, |
260 | 0 | "SetPendingExceptionWithMessage() can be called only once"); |
261 | 0 |
|
262 | 0 | Message* message = mExtra.mMessage; |
263 | 0 | MOZ_RELEASE_ASSERT(message->HasCorrectNumberOfArguments()); |
264 | 0 | const uint32_t argCount = message->mArgs.Length(); |
265 | 0 | const char16_t* args[JS::MaxNumErrorArguments + 1]; |
266 | 0 | for (uint32_t i = 0; i < argCount; ++i) { |
267 | 0 | args[i] = message->mArgs.ElementAt(i).get(); |
268 | 0 | } |
269 | 0 | args[argCount] = nullptr; |
270 | 0 |
|
271 | 0 | JS_ReportErrorNumberUCArray(aCx, dom::GetErrorMessage, nullptr, |
272 | 0 | static_cast<unsigned>(message->mErrorNumber), |
273 | 0 | argCount > 0 ? args : nullptr); |
274 | 0 |
|
275 | 0 | ClearMessage(); |
276 | 0 | mResult = NS_OK; |
277 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::SetPendingExceptionWithMessage(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::SetPendingExceptionWithMessage(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::SetPendingExceptionWithMessage(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::SetPendingExceptionWithMessage(JSContext*) |
278 | | |
279 | | template<typename CleanupPolicy> |
280 | | void |
281 | | TErrorResult<CleanupPolicy>::ClearMessage() |
282 | 0 | { |
283 | 0 | AssertInOwningThread(); |
284 | 0 | MOZ_ASSERT(IsErrorWithMessage()); |
285 | 0 | MOZ_ASSERT(mUnionState == HasMessage); |
286 | 0 | delete mExtra.mMessage; |
287 | 0 | mExtra.mMessage = nullptr; |
288 | | #ifdef DEBUG |
289 | | mUnionState = HasNothing; |
290 | | #endif // DEBUG |
291 | | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::ClearMessage() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::ClearMessage() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::ClearMessage() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::ClearMessage() |
292 | | |
293 | | template<typename CleanupPolicy> |
294 | | void |
295 | | TErrorResult<CleanupPolicy>::ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn) |
296 | 0 | { |
297 | 0 | AssertInOwningThread(); |
298 | 0 | MOZ_ASSERT(mMightHaveUnreportedJSException, |
299 | 0 | "Why didn't you tell us you planned to throw a JS exception?"); |
300 | 0 |
|
301 | 0 | ClearUnionData(); |
302 | 0 |
|
303 | 0 | // Make sure mExtra.mJSException is initialized _before_ we try to root it. |
304 | 0 | // But don't set it to exn yet, because we don't want to do that until after |
305 | 0 | // we root. |
306 | 0 | JS::Value& exc = InitJSException(); |
307 | 0 | if (!js::AddRawValueRoot(cx, &exc, "TErrorResult::mExtra::mJSException")) { |
308 | 0 | // Don't use NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION, because that |
309 | 0 | // indicates we have in fact rooted mExtra.mJSException. |
310 | 0 | mResult = NS_ERROR_OUT_OF_MEMORY; |
311 | 0 | } else { |
312 | 0 | exc = exn; |
313 | 0 | mResult = NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION; |
314 | | #ifdef DEBUG |
315 | | mUnionState = HasJSException; |
316 | | #endif // DEBUG |
317 | | } |
318 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::ThrowJSException(JSContext*, JS::Handle<JS::Value>) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::ThrowJSException(JSContext*, JS::Handle<JS::Value>) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::ThrowJSException(JSContext*, JS::Handle<JS::Value>) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::ThrowJSException(JSContext*, JS::Handle<JS::Value>) |
319 | | |
320 | | template<typename CleanupPolicy> |
321 | | void |
322 | | TErrorResult<CleanupPolicy>::SetPendingJSException(JSContext* cx) |
323 | 0 | { |
324 | 0 | AssertInOwningThread(); |
325 | 0 | MOZ_ASSERT(!mMightHaveUnreportedJSException, |
326 | 0 | "Why didn't you tell us you planned to handle JS exceptions?"); |
327 | 0 | MOZ_ASSERT(mUnionState == HasJSException); |
328 | 0 |
|
329 | 0 | JS::Rooted<JS::Value> exception(cx, mExtra.mJSException); |
330 | 0 | if (JS_WrapValue(cx, &exception)) { |
331 | 0 | JS_SetPendingException(cx, exception); |
332 | 0 | } |
333 | 0 | mExtra.mJSException = exception; |
334 | 0 | // If JS_WrapValue failed, not much we can do about it... No matter |
335 | 0 | // what, go ahead and unroot mExtra.mJSException. |
336 | 0 | js::RemoveRawValueRoot(cx, &mExtra.mJSException); |
337 | 0 |
|
338 | 0 | mResult = NS_OK; |
339 | | #ifdef DEBUG |
340 | | mUnionState = HasNothing; |
341 | | #endif // DEBUG |
342 | | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::SetPendingJSException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::SetPendingJSException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::SetPendingJSException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::SetPendingJSException(JSContext*) |
343 | | |
344 | | template<typename CleanupPolicy> |
345 | | struct TErrorResult<CleanupPolicy>::DOMExceptionInfo { |
346 | | DOMExceptionInfo(nsresult rv, const nsACString& message) |
347 | | : mMessage(message) |
348 | | , mRv(rv) |
349 | 0 | {} Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::DOMExceptionInfo::DOMExceptionInfo(nsresult, nsTSubstring<char> const&) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::DOMExceptionInfo::DOMExceptionInfo(nsresult, nsTSubstring<char> const&) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::DOMExceptionInfo::DOMExceptionInfo(nsresult, nsTSubstring<char> const&) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::DOMExceptionInfo::DOMExceptionInfo(nsresult, nsTSubstring<char> const&) |
350 | | |
351 | | nsCString mMessage; |
352 | | nsresult mRv; |
353 | | |
354 | | bool operator==(const TErrorResult<CleanupPolicy>::DOMExceptionInfo& aRight) const |
355 | 0 | { |
356 | 0 | return mRv == aRight.mRv && |
357 | 0 | mMessage == aRight.mMessage; |
358 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::DOMExceptionInfo::operator==(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::DOMExceptionInfo const&) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::DOMExceptionInfo::operator==(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::DOMExceptionInfo const&) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::DOMExceptionInfo::operator==(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::DOMExceptionInfo const&) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::DOMExceptionInfo::operator==(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::DOMExceptionInfo const&) const |
359 | | }; |
360 | | |
361 | | template<typename CleanupPolicy> |
362 | | void |
363 | | TErrorResult<CleanupPolicy>::SerializeDOMExceptionInfo(IPC::Message* aMsg) const |
364 | 0 | { |
365 | 0 | using namespace IPC; |
366 | 0 | AssertInOwningThread(); |
367 | 0 | MOZ_ASSERT(mUnionState == HasDOMExceptionInfo); |
368 | 0 | MOZ_ASSERT(mExtra.mDOMExceptionInfo); |
369 | 0 | WriteParam(aMsg, mExtra.mDOMExceptionInfo->mMessage); |
370 | 0 | WriteParam(aMsg, mExtra.mDOMExceptionInfo->mRv); |
371 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::SerializeDOMExceptionInfo(IPC::Message*) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::SerializeDOMExceptionInfo(IPC::Message*) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::SerializeDOMExceptionInfo(IPC::Message*) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::SerializeDOMExceptionInfo(IPC::Message*) const |
372 | | |
373 | | template<typename CleanupPolicy> |
374 | | bool |
375 | | TErrorResult<CleanupPolicy>::DeserializeDOMExceptionInfo(const IPC::Message* aMsg, |
376 | | PickleIterator* aIter) |
377 | 0 | { |
378 | 0 | using namespace IPC; |
379 | 0 | AssertInOwningThread(); |
380 | 0 | nsCString message; |
381 | 0 | nsresult rv; |
382 | 0 | if (!ReadParam(aMsg, aIter, &message) || |
383 | 0 | !ReadParam(aMsg, aIter, &rv)) { |
384 | 0 | return false; |
385 | 0 | } |
386 | 0 | |
387 | 0 | MOZ_ASSERT(mUnionState == HasNothing); |
388 | 0 | MOZ_ASSERT(IsDOMException()); |
389 | 0 | InitDOMExceptionInfo(new DOMExceptionInfo(rv, message)); |
390 | | #ifdef DEBUG |
391 | | mUnionState = HasDOMExceptionInfo; |
392 | | #endif // DEBUG |
393 | | return true; |
394 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::DeserializeDOMExceptionInfo(IPC::Message const*, PickleIterator*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::DeserializeDOMExceptionInfo(IPC::Message const*, PickleIterator*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::DeserializeDOMExceptionInfo(IPC::Message const*, PickleIterator*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::DeserializeDOMExceptionInfo(IPC::Message const*, PickleIterator*) |
395 | | |
396 | | template<typename CleanupPolicy> |
397 | | void |
398 | | TErrorResult<CleanupPolicy>::ThrowDOMException(nsresult rv, |
399 | | const nsACString& message) |
400 | 0 | { |
401 | 0 | AssertInOwningThread(); |
402 | 0 | ClearUnionData(); |
403 | 0 |
|
404 | 0 | mResult = NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION; |
405 | 0 | InitDOMExceptionInfo(new DOMExceptionInfo(rv, message)); |
406 | | #ifdef DEBUG |
407 | | mUnionState = HasDOMExceptionInfo; |
408 | | #endif |
409 | | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::ThrowDOMException(nsresult, nsTSubstring<char> const&) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::ThrowDOMException(nsresult, nsTSubstring<char> const&) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::ThrowDOMException(nsresult, nsTSubstring<char> const&) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::ThrowDOMException(nsresult, nsTSubstring<char> const&) |
410 | | |
411 | | template<typename CleanupPolicy> |
412 | | void |
413 | | TErrorResult<CleanupPolicy>::SetPendingDOMException(JSContext* cx) |
414 | 0 | { |
415 | 0 | AssertInOwningThread(); |
416 | 0 | MOZ_ASSERT(mUnionState == HasDOMExceptionInfo); |
417 | 0 | MOZ_ASSERT(mExtra.mDOMExceptionInfo, |
418 | 0 | "SetPendingDOMException() can be called only once"); |
419 | 0 |
|
420 | 0 | dom::Throw(cx, mExtra.mDOMExceptionInfo->mRv, |
421 | 0 | mExtra.mDOMExceptionInfo->mMessage); |
422 | 0 |
|
423 | 0 | ClearDOMExceptionInfo(); |
424 | 0 | mResult = NS_OK; |
425 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::SetPendingDOMException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::SetPendingDOMException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::SetPendingDOMException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::SetPendingDOMException(JSContext*) |
426 | | |
427 | | template<typename CleanupPolicy> |
428 | | void |
429 | | TErrorResult<CleanupPolicy>::ClearDOMExceptionInfo() |
430 | 0 | { |
431 | 0 | AssertInOwningThread(); |
432 | 0 | MOZ_ASSERT(IsDOMException()); |
433 | 0 | MOZ_ASSERT(mUnionState == HasDOMExceptionInfo); |
434 | 0 | delete mExtra.mDOMExceptionInfo; |
435 | 0 | mExtra.mDOMExceptionInfo = nullptr; |
436 | | #ifdef DEBUG |
437 | | mUnionState = HasNothing; |
438 | | #endif // DEBUG |
439 | | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::ClearDOMExceptionInfo() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::ClearDOMExceptionInfo() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::ClearDOMExceptionInfo() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::ClearDOMExceptionInfo() |
440 | | |
441 | | template<typename CleanupPolicy> |
442 | | void |
443 | | TErrorResult<CleanupPolicy>::ClearUnionData() |
444 | 0 | { |
445 | 0 | AssertInOwningThread(); |
446 | 0 | if (IsJSException()) { |
447 | 0 | JSContext* cx = dom::danger::GetJSContext(); |
448 | 0 | MOZ_ASSERT(cx); |
449 | 0 | mExtra.mJSException.setUndefined(); |
450 | 0 | js::RemoveRawValueRoot(cx, &mExtra.mJSException); |
451 | | #ifdef DEBUG |
452 | | mUnionState = HasNothing; |
453 | | #endif // DEBUG |
454 | 0 | } else if (IsErrorWithMessage()) { |
455 | 0 | ClearMessage(); |
456 | 0 | } else if (IsDOMException()) { |
457 | 0 | ClearDOMExceptionInfo(); |
458 | 0 | } |
459 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::ClearUnionData() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::ClearUnionData() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::ClearUnionData() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::ClearUnionData() |
460 | | |
461 | | template<typename CleanupPolicy> |
462 | | void |
463 | | TErrorResult<CleanupPolicy>::SetPendingGenericErrorException(JSContext* cx) |
464 | 0 | { |
465 | 0 | AssertInOwningThread(); |
466 | 0 | MOZ_ASSERT(!IsErrorWithMessage()); |
467 | 0 | MOZ_ASSERT(!IsJSException()); |
468 | 0 | MOZ_ASSERT(!IsDOMException()); |
469 | 0 | dom::Throw(cx, ErrorCode()); |
470 | 0 | mResult = NS_OK; |
471 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::SetPendingGenericErrorException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::SetPendingGenericErrorException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::SetPendingGenericErrorException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::SetPendingGenericErrorException(JSContext*) |
472 | | |
473 | | template<typename CleanupPolicy> |
474 | | TErrorResult<CleanupPolicy>& |
475 | | TErrorResult<CleanupPolicy>::operator=(TErrorResult<CleanupPolicy>&& aRHS) |
476 | 0 | { |
477 | 0 | AssertInOwningThread(); |
478 | 0 | aRHS.AssertInOwningThread(); |
479 | 0 | // Clear out any union members we may have right now, before we |
480 | 0 | // start writing to it. |
481 | 0 | ClearUnionData(); |
482 | 0 |
|
483 | | #ifdef DEBUG |
484 | | mMightHaveUnreportedJSException = aRHS.mMightHaveUnreportedJSException; |
485 | | aRHS.mMightHaveUnreportedJSException = false; |
486 | | #endif |
487 | 0 | if (aRHS.IsErrorWithMessage()) { |
488 | 0 | InitMessage(aRHS.mExtra.mMessage); |
489 | 0 | aRHS.mExtra.mMessage = nullptr; |
490 | 0 | } else if (aRHS.IsJSException()) { |
491 | 0 | JSContext* cx = dom::danger::GetJSContext(); |
492 | 0 | MOZ_ASSERT(cx); |
493 | 0 | JS::Value& exn = InitJSException(); |
494 | 0 | if (!js::AddRawValueRoot(cx, &exn, |
495 | 0 | "TErrorResult::mExtra::mJSException")) { |
496 | 0 | MOZ_CRASH("Could not root mExtra.mJSException, we're about to OOM"); |
497 | 0 | } |
498 | 0 | mExtra.mJSException = aRHS.mExtra.mJSException; |
499 | 0 | aRHS.mExtra.mJSException.setUndefined(); |
500 | 0 | js::RemoveRawValueRoot(cx, &aRHS.mExtra.mJSException); |
501 | 0 | } else if (aRHS.IsDOMException()) { |
502 | 0 | InitDOMExceptionInfo(aRHS.mExtra.mDOMExceptionInfo); |
503 | 0 | aRHS.mExtra.mDOMExceptionInfo = nullptr; |
504 | 0 | } else { |
505 | 0 | // Null out the union on both sides for hygiene purposes. This is purely |
506 | 0 | // precautionary, so InitMessage/placement-new is unnecessary. |
507 | 0 | mExtra.mMessage = aRHS.mExtra.mMessage = nullptr; |
508 | 0 | } |
509 | 0 |
|
510 | | #ifdef DEBUG |
511 | | mUnionState = aRHS.mUnionState; |
512 | | aRHS.mUnionState = HasNothing; |
513 | | #endif // DEBUG |
514 | |
|
515 | 0 | // Note: It's important to do this last, since this affects the condition |
516 | 0 | // checks above! |
517 | 0 | mResult = aRHS.mResult; |
518 | 0 | aRHS.mResult = NS_OK; |
519 | 0 | return *this; |
520 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::operator=(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>&&) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::operator=(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>&&) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::operator=(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>&&) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::operator=(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>&&) |
521 | | |
522 | | template<typename CleanupPolicy> |
523 | | bool |
524 | | TErrorResult<CleanupPolicy>::operator==(const ErrorResult& aRight) const |
525 | 0 | { |
526 | 0 | auto right = reinterpret_cast<const TErrorResult<CleanupPolicy>*>(&aRight); |
527 | 0 |
|
528 | 0 | if (mResult != right->mResult) { |
529 | 0 | return false; |
530 | 0 | } |
531 | 0 | |
532 | 0 | if (IsJSException()) { |
533 | 0 | // js exceptions are always non-equal |
534 | 0 | return false; |
535 | 0 | } |
536 | 0 | |
537 | 0 | if (IsErrorWithMessage()) { |
538 | 0 | return *mExtra.mMessage == *right->mExtra.mMessage; |
539 | 0 | } |
540 | 0 | |
541 | 0 | if (IsDOMException()) { |
542 | 0 | return *mExtra.mDOMExceptionInfo == *right->mExtra.mDOMExceptionInfo; |
543 | 0 | } |
544 | 0 | |
545 | 0 | return true; |
546 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::operator==(mozilla::ErrorResult const&) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::operator==(mozilla::ErrorResult const&) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::operator==(mozilla::ErrorResult const&) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::operator==(mozilla::ErrorResult const&) const |
547 | | |
548 | | template<typename CleanupPolicy> |
549 | | void |
550 | | TErrorResult<CleanupPolicy>::CloneTo(TErrorResult& aRv) const |
551 | 0 | { |
552 | 0 | AssertInOwningThread(); |
553 | 0 | aRv.AssertInOwningThread(); |
554 | 0 | aRv.ClearUnionData(); |
555 | 0 | aRv.mResult = mResult; |
556 | | #ifdef DEBUG |
557 | | aRv.mMightHaveUnreportedJSException = mMightHaveUnreportedJSException; |
558 | | #endif |
559 | |
|
560 | 0 | if (IsErrorWithMessage()) { |
561 | | #ifdef DEBUG |
562 | | aRv.mUnionState = HasMessage; |
563 | | #endif |
564 | | Message* message = aRv.InitMessage(new Message()); |
565 | 0 | message->mArgs = mExtra.mMessage->mArgs; |
566 | 0 | message->mErrorNumber = mExtra.mMessage->mErrorNumber; |
567 | 0 | } else if (IsDOMException()) { |
568 | | #ifdef DEBUG |
569 | | aRv.mUnionState = HasDOMExceptionInfo; |
570 | | #endif |
571 | | auto* exnInfo = new DOMExceptionInfo(mExtra.mDOMExceptionInfo->mRv, |
572 | 0 | mExtra.mDOMExceptionInfo->mMessage); |
573 | 0 | aRv.InitDOMExceptionInfo(exnInfo); |
574 | 0 | } else if (IsJSException()) { |
575 | | #ifdef DEBUG |
576 | | aRv.mUnionState = HasJSException; |
577 | | #endif |
578 | | JSContext* cx = dom::danger::GetJSContext(); |
579 | 0 | JS::Rooted<JS::Value> exception(cx, mExtra.mJSException); |
580 | 0 | aRv.ThrowJSException(cx, exception); |
581 | 0 | } |
582 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::CloneTo(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>&) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::CloneTo(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>&) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::CloneTo(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>&) const Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::CloneTo(mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>&) const |
583 | | |
584 | | template<typename CleanupPolicy> |
585 | | void |
586 | | TErrorResult<CleanupPolicy>::SuppressException() |
587 | 0 | { |
588 | 0 | AssertInOwningThread(); |
589 | 0 | WouldReportJSException(); |
590 | 0 | ClearUnionData(); |
591 | 0 | // We don't use AssignErrorCode, because we want to override existing error |
592 | 0 | // states, which AssignErrorCode is not allowed to do. |
593 | 0 | mResult = NS_OK; |
594 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::SuppressException() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::SuppressException() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::SuppressException() Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::SuppressException() |
595 | | |
596 | | template<typename CleanupPolicy> |
597 | | void |
598 | | TErrorResult<CleanupPolicy>::SetPendingException(JSContext* cx) |
599 | 0 | { |
600 | 0 | AssertInOwningThread(); |
601 | 0 | if (IsUncatchableException()) { |
602 | 0 | // Nuke any existing exception on cx, to make sure we're uncatchable. |
603 | 0 | JS_ClearPendingException(cx); |
604 | 0 | // Don't do any reporting. Just return, to create an |
605 | 0 | // uncatchable exception. |
606 | 0 | mResult = NS_OK; |
607 | 0 | return; |
608 | 0 | } |
609 | 0 | if (IsJSContextException()) { |
610 | 0 | // Whatever we need to throw is on the JSContext already. |
611 | 0 | MOZ_ASSERT(JS_IsExceptionPending(cx)); |
612 | 0 | mResult = NS_OK; |
613 | 0 | return; |
614 | 0 | } |
615 | 0 | if (IsErrorWithMessage()) { |
616 | 0 | SetPendingExceptionWithMessage(cx); |
617 | 0 | return; |
618 | 0 | } |
619 | 0 | if (IsJSException()) { |
620 | 0 | SetPendingJSException(cx); |
621 | 0 | return; |
622 | 0 | } |
623 | 0 | if (IsDOMException()) { |
624 | 0 | SetPendingDOMException(cx); |
625 | 0 | return; |
626 | 0 | } |
627 | 0 | SetPendingGenericErrorException(cx); |
628 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::SetPendingException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::SetPendingException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::SetPendingException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::SetPendingException(JSContext*) |
629 | | |
630 | | template<typename CleanupPolicy> |
631 | | void |
632 | | TErrorResult<CleanupPolicy>::StealExceptionFromJSContext(JSContext* cx) |
633 | 0 | { |
634 | 0 | AssertInOwningThread(); |
635 | 0 | MOZ_ASSERT(mMightHaveUnreportedJSException, |
636 | 0 | "Why didn't you tell us you planned to throw a JS exception?"); |
637 | 0 |
|
638 | 0 | JS::Rooted<JS::Value> exn(cx); |
639 | 0 | if (!JS_GetPendingException(cx, &exn)) { |
640 | 0 | ThrowUncatchableException(); |
641 | 0 | return; |
642 | 0 | } |
643 | 0 | |
644 | 0 | ThrowJSException(cx, exn); |
645 | 0 | JS_ClearPendingException(cx); |
646 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::StealExceptionFromJSContext(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::StealExceptionFromJSContext(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::StealExceptionFromJSContext(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::StealExceptionFromJSContext(JSContext*) |
647 | | |
648 | | template<typename CleanupPolicy> |
649 | | void |
650 | | TErrorResult<CleanupPolicy>::NoteJSContextException(JSContext* aCx) |
651 | 0 | { |
652 | 0 | AssertInOwningThread(); |
653 | 0 | if (JS_IsExceptionPending(aCx)) { |
654 | 0 | mResult = NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT; |
655 | 0 | } else { |
656 | 0 | mResult = NS_ERROR_UNCATCHABLE_EXCEPTION; |
657 | 0 | } |
658 | 0 | } Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustAssertCleanupPolicy>::NoteJSContextException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::AssertAndSuppressCleanupPolicy>::NoteJSContextException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::JustSuppressCleanupPolicy>::NoteJSContextException(JSContext*) Unexecuted instantiation: mozilla::binding_danger::TErrorResult<mozilla::binding_danger::ThreadSafeJustSuppressCleanupPolicy>::NoteJSContextException(JSContext*) |
659 | | |
660 | | template class TErrorResult<JustAssertCleanupPolicy>; |
661 | | template class TErrorResult<AssertAndSuppressCleanupPolicy>; |
662 | | template class TErrorResult<JustSuppressCleanupPolicy>; |
663 | | template class TErrorResult<ThreadSafeJustSuppressCleanupPolicy>; |
664 | | |
665 | | } // namespace binding_danger |
666 | | |
667 | | namespace dom { |
668 | | |
669 | | bool |
670 | | DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj, |
671 | | const ConstantSpec* cs) |
672 | 0 | { |
673 | 0 | JS::Rooted<JS::Value> value(cx); |
674 | 0 | for (; cs->name; ++cs) { |
675 | 0 | value = cs->value; |
676 | 0 | bool ok = |
677 | 0 | JS_DefineProperty(cx, obj, cs->name, value, |
678 | 0 | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); |
679 | 0 | if (!ok) { |
680 | 0 | return false; |
681 | 0 | } |
682 | 0 | } |
683 | 0 | return true; |
684 | 0 | } |
685 | | |
686 | | static inline bool |
687 | 4 | Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSFunctionSpec* spec) { |
688 | 4 | return JS_DefineFunctions(cx, obj, spec); |
689 | 4 | } |
690 | | static inline bool |
691 | 1 | Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSPropertySpec* spec) { |
692 | 1 | return JS_DefineProperties(cx, obj, spec); |
693 | 1 | } |
694 | | static inline bool |
695 | 0 | Define(JSContext* cx, JS::Handle<JSObject*> obj, const ConstantSpec* spec) { |
696 | 0 | return DefineConstants(cx, obj, spec); |
697 | 0 | } |
698 | | |
699 | | template<typename T> |
700 | | bool |
701 | | DefinePrefable(JSContext* cx, JS::Handle<JSObject*> obj, |
702 | | const Prefable<T>* props) |
703 | 3 | { |
704 | 3 | MOZ_ASSERT(props); |
705 | 3 | MOZ_ASSERT(props->specs); |
706 | 6 | do { |
707 | 6 | // Define if enabled |
708 | 6 | if (props->isEnabled(cx, obj)) { |
709 | 5 | if (!Define(cx, obj, props->specs)) { |
710 | 0 | return false; |
711 | 0 | } |
712 | 6 | } |
713 | 6 | } while ((++props)->specs); |
714 | 3 | return true; |
715 | 3 | } bool mozilla::dom::DefinePrefable<JSFunctionSpec const>(JSContext*, JS::Handle<JSObject*>, mozilla::dom::Prefable<JSFunctionSpec const> const*) Line | Count | Source | 703 | 2 | { | 704 | 2 | MOZ_ASSERT(props); | 705 | 2 | MOZ_ASSERT(props->specs); | 706 | 5 | do { | 707 | 5 | // Define if enabled | 708 | 5 | if (props->isEnabled(cx, obj)) { | 709 | 4 | if (!Define(cx, obj, props->specs)) { | 710 | 0 | return false; | 711 | 0 | } | 712 | 5 | } | 713 | 5 | } while ((++props)->specs); | 714 | 2 | return true; | 715 | 2 | } |
bool mozilla::dom::DefinePrefable<JSPropertySpec const>(JSContext*, JS::Handle<JSObject*>, mozilla::dom::Prefable<JSPropertySpec const> const*) Line | Count | Source | 703 | 1 | { | 704 | 1 | MOZ_ASSERT(props); | 705 | 1 | MOZ_ASSERT(props->specs); | 706 | 1 | do { | 707 | 1 | // Define if enabled | 708 | 1 | if (props->isEnabled(cx, obj)) { | 709 | 1 | if (!Define(cx, obj, props->specs)) { | 710 | 0 | return false; | 711 | 0 | } | 712 | 1 | } | 713 | 1 | } while ((++props)->specs); | 714 | 1 | return true; | 715 | 1 | } |
Unexecuted instantiation: bool mozilla::dom::DefinePrefable<mozilla::dom::ConstantSpec const>(JSContext*, JS::Handle<JSObject*>, mozilla::dom::Prefable<mozilla::dom::ConstantSpec const> const*) |
716 | | |
717 | | bool |
718 | | DefineUnforgeableMethods(JSContext* cx, JS::Handle<JSObject*> obj, |
719 | | const Prefable<const JSFunctionSpec>* props) |
720 | 0 | { |
721 | 0 | return DefinePrefable(cx, obj, props); |
722 | 0 | } |
723 | | |
724 | | bool |
725 | | DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj, |
726 | | const Prefable<const JSPropertySpec>* props) |
727 | 0 | { |
728 | 0 | return DefinePrefable(cx, obj, props); |
729 | 0 | } |
730 | | |
731 | | |
732 | | // We should use JSFunction objects for interface objects, but we need a custom |
733 | | // hasInstance hook because we have new interface objects on prototype chains of |
734 | | // old (XPConnect-based) bindings. We also need Xrays and arbitrary numbers of |
735 | | // reserved slots (e.g. for named constructors). So we define a custom |
736 | | // funToString ObjectOps member for interface objects. |
737 | | JSString* |
738 | | InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject, |
739 | | bool /* isToSource */) |
740 | 0 | { |
741 | 0 | const js::Class* clasp = js::GetObjectClass(aObject); |
742 | 0 | MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp)); |
743 | 0 |
|
744 | 0 | const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass = |
745 | 0 | DOMIfaceAndProtoJSClass::FromJSClass(clasp); |
746 | 0 | return JS_NewStringCopyZ(aCx, ifaceAndProtoJSClass->mToString); |
747 | 0 | } |
748 | | |
749 | | bool |
750 | | Constructor(JSContext* cx, unsigned argc, JS::Value* vp) |
751 | 0 | { |
752 | 0 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
753 | 0 | const JS::Value& v = |
754 | 0 | js::GetFunctionNativeReserved(&args.callee(), |
755 | 0 | CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT); |
756 | 0 | const JSNativeHolder* nativeHolder = |
757 | 0 | static_cast<const JSNativeHolder*>(v.toPrivate()); |
758 | 0 | return (nativeHolder->mNative)(cx, argc, vp); |
759 | 0 | } |
760 | | |
761 | | static JSObject* |
762 | | CreateConstructor(JSContext* cx, JS::Handle<JSObject*> global, const char* name, |
763 | | const JSNativeHolder* nativeHolder, unsigned ctorNargs) |
764 | 0 | { |
765 | 0 | JSFunction* fun = js::NewFunctionWithReserved(cx, Constructor, ctorNargs, |
766 | 0 | JSFUN_CONSTRUCTOR, name); |
767 | 0 | if (!fun) { |
768 | 0 | return nullptr; |
769 | 0 | } |
770 | 0 | |
771 | 0 | JSObject* constructor = JS_GetFunctionObject(fun); |
772 | 0 | js::SetFunctionNativeReserved(constructor, |
773 | 0 | CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT, |
774 | 0 | js::PrivateValue(const_cast<JSNativeHolder*>(nativeHolder))); |
775 | 0 | return constructor; |
776 | 0 | } |
777 | | |
778 | | static bool |
779 | | DefineConstructor(JSContext* cx, JS::Handle<JSObject*> global, const char* name, |
780 | | JS::Handle<JSObject*> constructor) |
781 | 2 | { |
782 | 2 | bool alreadyDefined; |
783 | 2 | if (!JS_AlreadyHasOwnProperty(cx, global, name, &alreadyDefined)) { |
784 | 0 | return false; |
785 | 0 | } |
786 | 2 | |
787 | 2 | // This is Enumerable: False per spec. |
788 | 2 | return alreadyDefined || |
789 | 2 | JS_DefineProperty(cx, global, name, constructor, JSPROP_RESOLVING); |
790 | 2 | } |
791 | | |
792 | | static JSObject* |
793 | | CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global, |
794 | | JS::Handle<JSObject*> constructorProto, |
795 | | const js::Class* constructorClass, |
796 | | unsigned ctorNargs, const NamedConstructor* namedConstructors, |
797 | | JS::Handle<JSObject*> proto, |
798 | | const NativeProperties* properties, |
799 | | const NativeProperties* chromeOnlyProperties, |
800 | | const char* name, bool isChrome, bool defineOnGlobal) |
801 | 2 | { |
802 | 2 | JS::Rooted<JSObject*> constructor(cx); |
803 | 2 | MOZ_ASSERT(constructorProto); |
804 | 2 | MOZ_ASSERT(constructorClass); |
805 | 2 | constructor = JS_NewObjectWithGivenProto(cx, Jsvalify(constructorClass), |
806 | 2 | constructorProto); |
807 | 2 | if (!constructor) { |
808 | 0 | return nullptr; |
809 | 0 | } |
810 | 2 | |
811 | 2 | if (!JS_DefineProperty(cx, constructor, "length", ctorNargs, |
812 | 2 | JSPROP_READONLY)) { |
813 | 0 | return nullptr; |
814 | 0 | } |
815 | 2 | |
816 | 2 | // Might as well intern, since we're going to need an atomized |
817 | 2 | // version of name anyway when we stick our constructor on the |
818 | 2 | // global. |
819 | 2 | JS::Rooted<JSString*> nameStr(cx, JS_AtomizeAndPinString(cx, name)); |
820 | 2 | if (!nameStr) { |
821 | 0 | return nullptr; |
822 | 0 | } |
823 | 2 | |
824 | 2 | if (!JS_DefineProperty(cx, constructor, "name", nameStr, JSPROP_READONLY)) { |
825 | 0 | return nullptr; |
826 | 0 | } |
827 | 2 | |
828 | 2 | if (DOMIfaceAndProtoJSClass::FromJSClass(constructorClass)->wantsInterfaceHasInstance) { |
829 | 1 | JS::Rooted<jsid> hasInstanceId(cx, |
830 | 1 | SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::hasInstance))); |
831 | 1 | if (!JS_DefineFunctionById(cx, constructor, hasInstanceId, |
832 | 1 | InterfaceHasInstance, 1, |
833 | 1 | // Flags match those of Function[Symbol.hasInstance] |
834 | 1 | JSPROP_READONLY | JSPROP_PERMANENT)) { |
835 | 0 | return nullptr; |
836 | 0 | } |
837 | 1 | |
838 | 1 | if (isChrome && !JS_DefineFunction(cx, constructor, "isInstance", |
839 | 1 | InterfaceIsInstance, 1, |
840 | 1 | // Don't bother making it enumerable |
841 | 1 | 0)) { |
842 | 0 | return nullptr; |
843 | 0 | } |
844 | 2 | } |
845 | 2 | |
846 | 2 | if (properties) { |
847 | 1 | if (properties->HasStaticMethods() && |
848 | 1 | !DefinePrefable(cx, constructor, properties->StaticMethods())) { |
849 | 0 | return nullptr; |
850 | 0 | } |
851 | 1 | |
852 | 1 | if (properties->HasStaticAttributes() && |
853 | 1 | !DefinePrefable(cx, constructor, properties->StaticAttributes())) { |
854 | 0 | return nullptr; |
855 | 0 | } |
856 | 1 | |
857 | 1 | if (properties->HasConstants() && |
858 | 1 | !DefinePrefable(cx, constructor, properties->Constants())) { |
859 | 0 | return nullptr; |
860 | 0 | } |
861 | 2 | } |
862 | 2 | |
863 | 2 | if (chromeOnlyProperties && isChrome) { |
864 | 1 | if (chromeOnlyProperties->HasStaticMethods() && |
865 | 1 | !DefinePrefable(cx, constructor, |
866 | 1 | chromeOnlyProperties->StaticMethods())) { |
867 | 0 | return nullptr; |
868 | 0 | } |
869 | 1 | |
870 | 1 | if (chromeOnlyProperties->HasStaticAttributes() && |
871 | 1 | !DefinePrefable(cx, constructor, |
872 | 0 | chromeOnlyProperties->StaticAttributes())) { |
873 | 0 | return nullptr; |
874 | 0 | } |
875 | 1 | |
876 | 1 | if (chromeOnlyProperties->HasConstants() && |
877 | 1 | !DefinePrefable(cx, constructor, chromeOnlyProperties->Constants())) { |
878 | 0 | return nullptr; |
879 | 0 | } |
880 | 2 | } |
881 | 2 | |
882 | 2 | if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) { |
883 | 0 | return nullptr; |
884 | 0 | } |
885 | 2 | |
886 | 2 | if (defineOnGlobal && !DefineConstructor(cx, global, name, constructor)) { |
887 | 0 | return nullptr; |
888 | 0 | } |
889 | 2 | |
890 | 2 | if (namedConstructors) { |
891 | 0 | int namedConstructorSlot = DOM_INTERFACE_SLOTS_BASE; |
892 | 0 | while (namedConstructors->mName) { |
893 | 0 | JS::Rooted<JSObject*> namedConstructor(cx, |
894 | 0 | CreateConstructor(cx, global, namedConstructors->mName, |
895 | 0 | &namedConstructors->mHolder, |
896 | 0 | namedConstructors->mNargs)); |
897 | 0 | if (!namedConstructor || |
898 | 0 | !JS_DefineProperty(cx, namedConstructor, "prototype", |
899 | 0 | proto, |
900 | 0 | JSPROP_PERMANENT | JSPROP_READONLY) || |
901 | 0 | (defineOnGlobal && |
902 | 0 | !DefineConstructor(cx, global, namedConstructors->mName, |
903 | 0 | namedConstructor))) { |
904 | 0 | return nullptr; |
905 | 0 | } |
906 | 0 | js::SetReservedSlot(constructor, namedConstructorSlot++, |
907 | 0 | JS::ObjectValue(*namedConstructor)); |
908 | 0 | ++namedConstructors; |
909 | 0 | } |
910 | 0 | } |
911 | 2 | |
912 | 2 | return constructor; |
913 | 2 | } |
914 | | |
915 | | static JSObject* |
916 | | CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global, |
917 | | JS::Handle<JSObject*> parentProto, |
918 | | const js::Class* protoClass, |
919 | | const NativeProperties* properties, |
920 | | const NativeProperties* chromeOnlyProperties, |
921 | | const char* const* unscopableNames, |
922 | | const char* toStringTag, |
923 | | bool isGlobal) |
924 | 1 | { |
925 | 1 | JS::Rooted<JSObject*> ourProto(cx, |
926 | 1 | JS_NewObjectWithUniqueType(cx, Jsvalify(protoClass), parentProto)); |
927 | 1 | if (!ourProto || |
928 | 1 | // We don't try to define properties on the global's prototype; those |
929 | 1 | // properties go on the global itself. |
930 | 1 | (!isGlobal && |
931 | 1 | !DefineProperties(cx, ourProto, properties, chromeOnlyProperties))) { |
932 | 0 | return nullptr; |
933 | 0 | } |
934 | 1 | |
935 | 1 | if (unscopableNames) { |
936 | 0 | JS::Rooted<JSObject*> unscopableObj(cx, |
937 | 0 | JS_NewObjectWithGivenProto(cx, nullptr, nullptr)); |
938 | 0 | if (!unscopableObj) { |
939 | 0 | return nullptr; |
940 | 0 | } |
941 | 0 | |
942 | 0 | for (; *unscopableNames; ++unscopableNames) { |
943 | 0 | if (!JS_DefineProperty(cx, unscopableObj, *unscopableNames, |
944 | 0 | JS::TrueHandleValue, JSPROP_ENUMERATE)) { |
945 | 0 | return nullptr; |
946 | 0 | } |
947 | 0 | } |
948 | 0 |
|
949 | 0 | JS::Rooted<jsid> unscopableId(cx, |
950 | 0 | SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::unscopables))); |
951 | 0 | // Readonly and non-enumerable to match Array.prototype. |
952 | 0 | if (!JS_DefinePropertyById(cx, ourProto, unscopableId, unscopableObj, |
953 | 0 | JSPROP_READONLY)) { |
954 | 0 | return nullptr; |
955 | 0 | } |
956 | 1 | } |
957 | 1 | |
958 | 1 | if (toStringTag) { |
959 | 0 | JS::Rooted<JSString*> toStringTagStr(cx, |
960 | 0 | JS_NewStringCopyZ(cx, toStringTag)); |
961 | 0 | if (!toStringTagStr) { |
962 | 0 | return nullptr; |
963 | 0 | } |
964 | 0 | |
965 | 0 | JS::Rooted<jsid> toStringTagId(cx, |
966 | 0 | SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::toStringTag))); |
967 | 0 | if (!JS_DefinePropertyById(cx, ourProto, toStringTagId, toStringTagStr, |
968 | 0 | JSPROP_READONLY)) { |
969 | 0 | return nullptr; |
970 | 0 | } |
971 | 1 | } |
972 | 1 | |
973 | 1 | return ourProto; |
974 | 1 | } |
975 | | |
976 | | bool |
977 | | DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj, |
978 | | const NativeProperties* properties, |
979 | | const NativeProperties* chromeOnlyProperties) |
980 | 1 | { |
981 | 1 | if (properties) { |
982 | 0 | if (properties->HasMethods() && |
983 | 0 | !DefinePrefable(cx, obj, properties->Methods())) { |
984 | 0 | return false; |
985 | 0 | } |
986 | 0 | |
987 | 0 | if (properties->HasAttributes() && |
988 | 0 | !DefinePrefable(cx, obj, properties->Attributes())) { |
989 | 0 | return false; |
990 | 0 | } |
991 | 0 | |
992 | 0 | if (properties->HasConstants() && |
993 | 0 | !DefinePrefable(cx, obj, properties->Constants())) { |
994 | 0 | return false; |
995 | 0 | } |
996 | 1 | } |
997 | 1 | |
998 | 1 | if (chromeOnlyProperties) { |
999 | 0 | if (chromeOnlyProperties->HasMethods() && |
1000 | 0 | !DefinePrefable(cx, obj, chromeOnlyProperties->Methods())) { |
1001 | 0 | return false; |
1002 | 0 | } |
1003 | 0 | |
1004 | 0 | if (chromeOnlyProperties->HasAttributes() && |
1005 | 0 | !DefinePrefable(cx, obj, chromeOnlyProperties->Attributes())) { |
1006 | 0 | return false; |
1007 | 0 | } |
1008 | 0 | |
1009 | 0 | if (chromeOnlyProperties->HasConstants() && |
1010 | 0 | !DefinePrefable(cx, obj, chromeOnlyProperties->Constants())) { |
1011 | 0 | return false; |
1012 | 0 | } |
1013 | 1 | } |
1014 | 1 | |
1015 | 1 | return true; |
1016 | 1 | } |
1017 | | |
1018 | | void |
1019 | | CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global, |
1020 | | JS::Handle<JSObject*> protoProto, |
1021 | | const js::Class* protoClass, JS::Heap<JSObject*>* protoCache, |
1022 | | const char* toStringTag, |
1023 | | JS::Handle<JSObject*> constructorProto, |
1024 | | const js::Class* constructorClass, |
1025 | | unsigned ctorNargs, const NamedConstructor* namedConstructors, |
1026 | | JS::Heap<JSObject*>* constructorCache, |
1027 | | const NativeProperties* properties, |
1028 | | const NativeProperties* chromeOnlyProperties, |
1029 | | const char* name, bool defineOnGlobal, |
1030 | | const char* const* unscopableNames, |
1031 | | bool isGlobal) |
1032 | 2 | { |
1033 | 2 | MOZ_ASSERT(protoClass || constructorClass, |
1034 | 2 | "Need at least one class!"); |
1035 | 2 | MOZ_ASSERT(!((properties && |
1036 | 2 | (properties->HasMethods() || properties->HasAttributes())) || |
1037 | 2 | (chromeOnlyProperties && |
1038 | 2 | (chromeOnlyProperties->HasMethods() || |
1039 | 2 | chromeOnlyProperties->HasAttributes()))) || protoClass, |
1040 | 2 | "Methods or properties but no protoClass!"); |
1041 | 2 | MOZ_ASSERT(!((properties && |
1042 | 2 | (properties->HasStaticMethods() || |
1043 | 2 | properties->HasStaticAttributes())) || |
1044 | 2 | (chromeOnlyProperties && |
1045 | 2 | (chromeOnlyProperties->HasStaticMethods() || |
1046 | 2 | chromeOnlyProperties->HasStaticAttributes()))) || |
1047 | 2 | constructorClass, |
1048 | 2 | "Static methods but no constructorClass!"); |
1049 | 2 | MOZ_ASSERT(bool(name) == bool(constructorClass), |
1050 | 2 | "Must have name precisely when we have an interface object"); |
1051 | 2 | MOZ_ASSERT(!protoClass == !protoCache, |
1052 | 2 | "If, and only if, there is an interface prototype object we need " |
1053 | 2 | "to cache it"); |
1054 | 2 | MOZ_ASSERT(bool(constructorClass) == bool(constructorCache), |
1055 | 2 | "If, and only if, there is an interface object we need to cache " |
1056 | 2 | "it"); |
1057 | 2 | MOZ_ASSERT(constructorProto || !constructorClass, |
1058 | 2 | "Must have a constructor proto if we plan to create a constructor " |
1059 | 2 | "object"); |
1060 | 2 | MOZ_ASSERT(protoClass || !toStringTag, |
1061 | 2 | "Must have a prototype object if we have a @@toStringTag"); |
1062 | 2 | |
1063 | 2 | bool isChrome = nsContentUtils::ThreadsafeIsSystemCaller(cx); |
1064 | 2 | |
1065 | 2 | JS::Rooted<JSObject*> proto(cx); |
1066 | 2 | if (protoClass) { |
1067 | 1 | proto = |
1068 | 1 | CreateInterfacePrototypeObject(cx, global, protoProto, protoClass, |
1069 | 1 | properties, |
1070 | 1 | isChrome ? chromeOnlyProperties : nullptr, |
1071 | 1 | unscopableNames, toStringTag, isGlobal); |
1072 | 1 | if (!proto) { |
1073 | 0 | return; |
1074 | 0 | } |
1075 | 1 | |
1076 | 1 | *protoCache = proto; |
1077 | 1 | } |
1078 | 1 | else { |
1079 | 1 | MOZ_ASSERT(!proto); |
1080 | 1 | } |
1081 | 2 | |
1082 | 2 | JSObject* interface; |
1083 | 2 | if (constructorClass) { |
1084 | 2 | interface = CreateInterfaceObject(cx, global, constructorProto, |
1085 | 2 | constructorClass, ctorNargs, |
1086 | 2 | namedConstructors, proto, properties, |
1087 | 2 | chromeOnlyProperties, name, |
1088 | 2 | isChrome, |
1089 | 2 | defineOnGlobal); |
1090 | 2 | if (!interface) { |
1091 | 0 | if (protoCache) { |
1092 | 0 | // If we fail we need to make sure to clear the value of protoCache we |
1093 | 0 | // set above. |
1094 | 0 | *protoCache = nullptr; |
1095 | 0 | } |
1096 | 0 | return; |
1097 | 0 | } |
1098 | 2 | *constructorCache = interface; |
1099 | 2 | } |
1100 | 2 | } |
1101 | | |
1102 | | // Only set aAllowNativeWrapper to false if you really know you need it; if in |
1103 | | // doubt use true. Setting it to false disables security wrappers. |
1104 | | static bool |
1105 | | NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx, |
1106 | | JS::Handle<JSObject*> aScope, |
1107 | | JS::MutableHandle<JS::Value> aRetval, |
1108 | | xpcObjectHelper& aHelper, |
1109 | | const nsIID* aIID, |
1110 | | bool aAllowNativeWrapper) |
1111 | 0 | { |
1112 | 0 | js::AssertSameCompartment(aCx, aScope); |
1113 | 0 | nsresult rv; |
1114 | 0 | // Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need |
1115 | 0 | // on all threads. |
1116 | 0 | nsWrapperCache *cache = aHelper.GetWrapperCache(); |
1117 | 0 |
|
1118 | 0 | if (cache) { |
1119 | 0 | JS::Rooted<JSObject*> obj(aCx, cache->GetWrapper()); |
1120 | 0 | if (!obj) { |
1121 | 0 | obj = cache->WrapObject(aCx, nullptr); |
1122 | 0 | if (!obj) { |
1123 | 0 | return Throw(aCx, NS_ERROR_UNEXPECTED); |
1124 | 0 | } |
1125 | 0 | } |
1126 | 0 | |
1127 | 0 | if (aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) { |
1128 | 0 | return false; |
1129 | 0 | } |
1130 | 0 | |
1131 | 0 | aRetval.setObject(*obj); |
1132 | 0 | return true; |
1133 | 0 | } |
1134 | 0 | |
1135 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1136 | 0 |
|
1137 | 0 | if (!XPCConvert::NativeInterface2JSObject(aRetval, aHelper, aIID, |
1138 | 0 | aAllowNativeWrapper, &rv)) { |
1139 | 0 | // I can't tell if NativeInterface2JSObject throws JS exceptions |
1140 | 0 | // or not. This is a sloppy stab at the right semantics; the |
1141 | 0 | // method really ought to be fixed to behave consistently. |
1142 | 0 | if (!JS_IsExceptionPending(aCx)) { |
1143 | 0 | Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED); |
1144 | 0 | } |
1145 | 0 | return false; |
1146 | 0 | } |
1147 | 0 | return true; |
1148 | 0 | } |
1149 | | |
1150 | | bool |
1151 | | TryPreserveWrapper(JS::Handle<JSObject*> obj) |
1152 | 0 | { |
1153 | 0 | MOZ_ASSERT(IsDOMObject(obj)); |
1154 | 0 |
|
1155 | 0 | // nsISupports objects are special cased because DOM proxies are nsISupports |
1156 | 0 | // and have addProperty hooks that do more than wrapper preservation (so we |
1157 | 0 | // don't want to call them). |
1158 | 0 | if (nsISupports* native = UnwrapDOMObjectToISupports(obj)) { |
1159 | 0 | nsWrapperCache* cache = nullptr; |
1160 | 0 | CallQueryInterface(native, &cache); |
1161 | 0 | if (cache) { |
1162 | 0 | cache->PreserveWrapper(native); |
1163 | 0 | } |
1164 | 0 | return true; |
1165 | 0 | } |
1166 | 0 |
|
1167 | 0 | // The addProperty hook for WebIDL classes does wrapper preservation, and |
1168 | 0 | // nothing else, so call it, if present. |
1169 | 0 | const DOMJSClass* domClass = GetDOMClass(obj); |
1170 | 0 | const JSClass* clasp = domClass->ToJSClass(); |
1171 | 0 | JSAddPropertyOp addProperty = clasp->getAddProperty(); |
1172 | 0 |
|
1173 | 0 | // We expect all proxies to be nsISupports. |
1174 | 0 | MOZ_RELEASE_ASSERT(!js::Valueify(clasp)->isProxy(), "Should not call addProperty for proxies."); |
1175 | 0 |
|
1176 | 0 | // The class should have an addProperty hook iff it is a CC participant. |
1177 | 0 | MOZ_RELEASE_ASSERT(bool(domClass->mParticipant) == bool(addProperty)); |
1178 | 0 |
|
1179 | 0 | if (!addProperty) { |
1180 | 0 | return true; |
1181 | 0 | } |
1182 | 0 | |
1183 | 0 | JS::Rooted<jsid> dummyId(RootingCx()); |
1184 | 0 | JS::Rooted<JS::Value> dummyValue(RootingCx()); |
1185 | 0 | return addProperty(nullptr, obj, dummyId, dummyValue); |
1186 | 0 | } |
1187 | | |
1188 | | // Can only be called with a DOM JSClass. |
1189 | | bool |
1190 | | InstanceClassHasProtoAtDepth(const js::Class* clasp, |
1191 | | uint32_t protoID, uint32_t depth) |
1192 | 0 | { |
1193 | 0 | const DOMJSClass* domClass = DOMJSClass::FromJSClass(clasp); |
1194 | 0 | return static_cast<uint32_t>(domClass->mInterfaceChain[depth]) == protoID; |
1195 | 0 | } |
1196 | | |
1197 | | // Only set allowNativeWrapper to false if you really know you need it; if in |
1198 | | // doubt use true. Setting it to false disables security wrappers. |
1199 | | bool |
1200 | | XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope, |
1201 | | xpcObjectHelper& helper, const nsIID* iid, |
1202 | | bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval) |
1203 | 0 | { |
1204 | 0 | return NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, helper, iid, |
1205 | 0 | allowNativeWrapper); |
1206 | 0 | } |
1207 | | |
1208 | | bool |
1209 | | VariantToJsval(JSContext* aCx, nsIVariant* aVariant, |
1210 | | JS::MutableHandle<JS::Value> aRetval) |
1211 | 0 | { |
1212 | 0 | nsresult rv; |
1213 | 0 | if (!XPCVariant::VariantDataToJS(aVariant, &rv, aRetval)) { |
1214 | 0 | // Does it throw? Who knows |
1215 | 0 | if (!JS_IsExceptionPending(aCx)) { |
1216 | 0 | Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED); |
1217 | 0 | } |
1218 | 0 | return false; |
1219 | 0 | } |
1220 | 0 |
|
1221 | 0 | return true; |
1222 | 0 | } |
1223 | | |
1224 | | static int |
1225 | | CompareIdsAtIndices(const void* aElement1, const void* aElement2, void* aClosure) |
1226 | 100 | { |
1227 | 100 | const uint16_t index1 = *static_cast<const uint16_t*>(aElement1); |
1228 | 100 | const uint16_t index2 = *static_cast<const uint16_t*>(aElement2); |
1229 | 100 | const PropertyInfo* infos = static_cast<PropertyInfo*>(aClosure); |
1230 | 100 | |
1231 | 100 | MOZ_ASSERT(JSID_BITS(infos[index1].Id()) != JSID_BITS(infos[index2].Id())); |
1232 | 100 | |
1233 | 100 | return JSID_BITS(infos[index1].Id()) < JSID_BITS(infos[index2].Id()) ? -1 : 1; |
1234 | 100 | } |
1235 | | |
1236 | | template <typename SpecT> |
1237 | | static bool |
1238 | | InitIdsInternal(JSContext* cx, const Prefable<SpecT>* pref, PropertyInfo* infos, |
1239 | | PropertyType type) |
1240 | 3 | { |
1241 | 3 | MOZ_ASSERT(pref); |
1242 | 3 | MOZ_ASSERT(pref->specs); |
1243 | 3 | |
1244 | 3 | // Index of the Prefable that contains the id for the current PropertyInfo. |
1245 | 3 | uint32_t prefIndex = 0; |
1246 | 3 | |
1247 | 6 | do { |
1248 | 6 | // We ignore whether the set of ids is enabled and just intern all the IDs, |
1249 | 6 | // because this is only done once per application runtime. |
1250 | 6 | const SpecT* spec = pref->specs; |
1251 | 6 | // Index of the property/function/constant spec for our current PropertyInfo |
1252 | 6 | // in the "specs" array of the relevant Prefable. |
1253 | 6 | uint32_t specIndex = 0; |
1254 | 28 | do { |
1255 | 28 | jsid id; |
1256 | 28 | if (!JS::PropertySpecNameToPermanentId(cx, spec->name, &id)) { |
1257 | 0 | return false; |
1258 | 0 | } |
1259 | 28 | infos->SetId(id); |
1260 | 28 | infos->type = type; |
1261 | 28 | infos->prefIndex = prefIndex; |
1262 | 28 | infos->specIndex = specIndex++; |
1263 | 28 | ++infos; |
1264 | 28 | } while ((++spec)->name); |
1265 | 6 | ++prefIndex; |
1266 | 6 | } while ((++pref)->specs); |
1267 | 3 | |
1268 | 3 | return true; |
1269 | 3 | } Unified_cpp_dom_bindings0.cpp:bool mozilla::dom::InitIdsInternal<JSFunctionSpec const>(JSContext*, mozilla::dom::Prefable<JSFunctionSpec const> const*, mozilla::dom::PropertyInfo*, mozilla::dom::PropertyType) Line | Count | Source | 1240 | 2 | { | 1241 | 2 | MOZ_ASSERT(pref); | 1242 | 2 | MOZ_ASSERT(pref->specs); | 1243 | 2 | | 1244 | 2 | // Index of the Prefable that contains the id for the current PropertyInfo. | 1245 | 2 | uint32_t prefIndex = 0; | 1246 | 2 | | 1247 | 5 | do { | 1248 | 5 | // We ignore whether the set of ids is enabled and just intern all the IDs, | 1249 | 5 | // because this is only done once per application runtime. | 1250 | 5 | const SpecT* spec = pref->specs; | 1251 | 5 | // Index of the property/function/constant spec for our current PropertyInfo | 1252 | 5 | // in the "specs" array of the relevant Prefable. | 1253 | 5 | uint32_t specIndex = 0; | 1254 | 27 | do { | 1255 | 27 | jsid id; | 1256 | 27 | if (!JS::PropertySpecNameToPermanentId(cx, spec->name, &id)) { | 1257 | 0 | return false; | 1258 | 0 | } | 1259 | 27 | infos->SetId(id); | 1260 | 27 | infos->type = type; | 1261 | 27 | infos->prefIndex = prefIndex; | 1262 | 27 | infos->specIndex = specIndex++; | 1263 | 27 | ++infos; | 1264 | 27 | } while ((++spec)->name); | 1265 | 5 | ++prefIndex; | 1266 | 5 | } while ((++pref)->specs); | 1267 | 2 | | 1268 | 2 | return true; | 1269 | 2 | } |
Unified_cpp_dom_bindings0.cpp:bool mozilla::dom::InitIdsInternal<JSPropertySpec const>(JSContext*, mozilla::dom::Prefable<JSPropertySpec const> const*, mozilla::dom::PropertyInfo*, mozilla::dom::PropertyType) Line | Count | Source | 1240 | 1 | { | 1241 | 1 | MOZ_ASSERT(pref); | 1242 | 1 | MOZ_ASSERT(pref->specs); | 1243 | 1 | | 1244 | 1 | // Index of the Prefable that contains the id for the current PropertyInfo. | 1245 | 1 | uint32_t prefIndex = 0; | 1246 | 1 | | 1247 | 1 | do { | 1248 | 1 | // We ignore whether the set of ids is enabled and just intern all the IDs, | 1249 | 1 | // because this is only done once per application runtime. | 1250 | 1 | const SpecT* spec = pref->specs; | 1251 | 1 | // Index of the property/function/constant spec for our current PropertyInfo | 1252 | 1 | // in the "specs" array of the relevant Prefable. | 1253 | 1 | uint32_t specIndex = 0; | 1254 | 1 | do { | 1255 | 1 | jsid id; | 1256 | 1 | if (!JS::PropertySpecNameToPermanentId(cx, spec->name, &id)) { | 1257 | 0 | return false; | 1258 | 0 | } | 1259 | 1 | infos->SetId(id); | 1260 | 1 | infos->type = type; | 1261 | 1 | infos->prefIndex = prefIndex; | 1262 | 1 | infos->specIndex = specIndex++; | 1263 | 1 | ++infos; | 1264 | 1 | } while ((++spec)->name); | 1265 | 1 | ++prefIndex; | 1266 | 1 | } while ((++pref)->specs); | 1267 | 1 | | 1268 | 1 | return true; | 1269 | 1 | } |
Unexecuted instantiation: Unified_cpp_dom_bindings0.cpp:bool mozilla::dom::InitIdsInternal<mozilla::dom::ConstantSpec const>(JSContext*, mozilla::dom::Prefable<mozilla::dom::ConstantSpec const> const*, mozilla::dom::PropertyInfo*, mozilla::dom::PropertyType) |
1270 | | |
1271 | 14 | #define INIT_IDS_IF_DEFINED(TypeName) { \ |
1272 | 14 | if (nativeProperties->Has##TypeName##s() && \ |
1273 | 14 | !InitIdsInternal(cx, \ |
1274 | 3 | nativeProperties->TypeName##s(), \ |
1275 | 3 | nativeProperties->TypeName##PropertyInfos(), \ |
1276 | 3 | e##TypeName)) { \ |
1277 | 0 | return false; \ |
1278 | 0 | } \ |
1279 | 14 | } |
1280 | | |
1281 | | bool |
1282 | | InitIds(JSContext* cx, const NativeProperties* nativeProperties) |
1283 | 2 | { |
1284 | 2 | INIT_IDS_IF_DEFINED(StaticMethod); |
1285 | 2 | INIT_IDS_IF_DEFINED(StaticAttribute); |
1286 | 2 | INIT_IDS_IF_DEFINED(Method); |
1287 | 2 | INIT_IDS_IF_DEFINED(Attribute); |
1288 | 2 | INIT_IDS_IF_DEFINED(UnforgeableMethod); |
1289 | 2 | INIT_IDS_IF_DEFINED(UnforgeableAttribute); |
1290 | 2 | INIT_IDS_IF_DEFINED(Constant); |
1291 | 2 | |
1292 | 2 | // Initialize and sort the index array. |
1293 | 2 | uint16_t* indices = nativeProperties->sortedPropertyIndices; |
1294 | 30 | for (unsigned int i = 0; i < nativeProperties->propertyInfoCount; ++i) { |
1295 | 28 | indices[i] = i; |
1296 | 28 | } |
1297 | 2 | // CompareIdsAtIndices() doesn't actually modify the PropertyInfo array, so |
1298 | 2 | // the const_cast here is OK in spite of the signature of NS_QuickSort(). |
1299 | 2 | NS_QuickSort(indices, nativeProperties->propertyInfoCount, sizeof(uint16_t), |
1300 | 2 | CompareIdsAtIndices, |
1301 | 2 | const_cast<PropertyInfo*>(nativeProperties->PropertyInfos())); |
1302 | 2 | |
1303 | 2 | return true; |
1304 | 2 | } |
1305 | | |
1306 | | #undef INIT_IDS_IF_DEFINED |
1307 | | |
1308 | | bool |
1309 | | QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp) |
1310 | 0 | { |
1311 | 0 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
1312 | 0 | if (!args.thisv().isObject()) { |
1313 | 0 | JS_ReportErrorASCII(cx, "QueryInterface called on incompatible non-object"); |
1314 | 0 | return false; |
1315 | 0 | } |
1316 | 0 | |
1317 | 0 | // Get the object. It might be a security wrapper, in which case we do a checked |
1318 | 0 | // unwrap. |
1319 | 0 | JS::Rooted<JSObject*> origObj(cx, &args.thisv().toObject()); |
1320 | 0 | JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(origObj, |
1321 | 0 | /* stopAtWindowProxy = */ false)); |
1322 | 0 | if (!obj) { |
1323 | 0 | JS_ReportErrorASCII(cx, "Permission denied to access object"); |
1324 | 0 | return false; |
1325 | 0 | } |
1326 | 0 | |
1327 | 0 | nsCOMPtr<nsISupports> native = UnwrapDOMObjectToISupports(obj); |
1328 | 0 | if (!native) { |
1329 | 0 | return Throw(cx, NS_ERROR_FAILURE); |
1330 | 0 | } |
1331 | 0 | |
1332 | 0 | if (argc < 1) { |
1333 | 0 | return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS); |
1334 | 0 | } |
1335 | 0 | |
1336 | 0 | if (!args[0].isObject()) { |
1337 | 0 | return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS); |
1338 | 0 | } |
1339 | 0 | |
1340 | 0 | nsCOMPtr<nsIJSID> iid; |
1341 | 0 | obj = &args[0].toObject(); |
1342 | 0 | if (NS_FAILED(UnwrapArg<nsIJSID>(cx, obj, getter_AddRefs(iid)))) { |
1343 | 0 | return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS); |
1344 | 0 | } |
1345 | 0 | MOZ_ASSERT(iid); |
1346 | 0 |
|
1347 | 0 | if (iid->GetID()->Equals(NS_GET_IID(nsIClassInfo))) { |
1348 | 0 | nsresult rv; |
1349 | 0 | nsCOMPtr<nsIClassInfo> ci = do_QueryInterface(native, &rv); |
1350 | 0 | if (NS_FAILED(rv)) { |
1351 | 0 | return Throw(cx, rv); |
1352 | 0 | } |
1353 | 0 | |
1354 | 0 | return WrapObject(cx, ci, &NS_GET_IID(nsIClassInfo), args.rval()); |
1355 | 0 | } |
1356 | 0 |
|
1357 | 0 | nsCOMPtr<nsISupports> unused; |
1358 | 0 | nsresult rv = native->QueryInterface(*iid->GetID(), getter_AddRefs(unused)); |
1359 | 0 | if (NS_FAILED(rv)) { |
1360 | 0 | return Throw(cx, rv); |
1361 | 0 | } |
1362 | 0 | |
1363 | 0 | args.rval().set(args.thisv()); |
1364 | 0 | return true; |
1365 | 0 | } |
1366 | | |
1367 | | void |
1368 | | GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor, |
1369 | | nsWrapperCache* aCache, nsIJSID* aIID, |
1370 | | JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError) |
1371 | 0 | { |
1372 | 0 | const nsID* iid = aIID->GetID(); |
1373 | 0 |
|
1374 | 0 | RefPtr<nsISupports> result; |
1375 | 0 | aError = aRequestor->GetInterface(*iid, getter_AddRefs(result)); |
1376 | 0 | if (aError.Failed()) { |
1377 | 0 | return; |
1378 | 0 | } |
1379 | 0 | |
1380 | 0 | if (!WrapObject(aCx, result, iid, aRetval)) { |
1381 | 0 | aError.Throw(NS_ERROR_FAILURE); |
1382 | 0 | } |
1383 | 0 | } |
1384 | | |
1385 | | bool |
1386 | | ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp) |
1387 | 0 | { |
1388 | 0 | return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR); |
1389 | 0 | } |
1390 | | |
1391 | | bool |
1392 | | ThrowConstructorWithoutNew(JSContext* cx, const char* name) |
1393 | 0 | { |
1394 | 0 | return ThrowErrorMessage(cx, MSG_CONSTRUCTOR_WITHOUT_NEW, name); |
1395 | 0 | } |
1396 | | |
1397 | | inline const NativePropertyHooks* |
1398 | | GetNativePropertyHooksFromConstructorFunction(JS::Handle<JSObject*> obj) |
1399 | 0 | { |
1400 | 0 | MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor)); |
1401 | 0 | const JS::Value& v = |
1402 | 0 | js::GetFunctionNativeReserved(obj, |
1403 | 0 | CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT); |
1404 | 0 | const JSNativeHolder* nativeHolder = |
1405 | 0 | static_cast<const JSNativeHolder*>(v.toPrivate()); |
1406 | 0 | return nativeHolder->mPropertyHooks; |
1407 | 0 | } |
1408 | | |
1409 | | inline const NativePropertyHooks* |
1410 | | GetNativePropertyHooks(JSContext *cx, JS::Handle<JSObject*> obj, |
1411 | | DOMObjectType& type) |
1412 | 0 | { |
1413 | 0 | const js::Class* clasp = js::GetObjectClass(obj); |
1414 | 0 |
|
1415 | 0 | const DOMJSClass* domClass = GetDOMClass(clasp); |
1416 | 0 | if (domClass) { |
1417 | 0 | bool isGlobal = (clasp->flags & JSCLASS_DOM_GLOBAL) != 0; |
1418 | 0 | type = isGlobal ? eGlobalInstance : eInstance; |
1419 | 0 | return domClass->mNativeHooks; |
1420 | 0 | } |
1421 | 0 |
|
1422 | 0 | if (JS_ObjectIsFunction(cx, obj)) { |
1423 | 0 | type = eInterface; |
1424 | 0 | return GetNativePropertyHooksFromConstructorFunction(obj); |
1425 | 0 | } |
1426 | 0 | |
1427 | 0 | MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(obj))); |
1428 | 0 | const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass = |
1429 | 0 | DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj)); |
1430 | 0 | type = ifaceAndProtoJSClass->mType; |
1431 | 0 | return ifaceAndProtoJSClass->mNativeHooks; |
1432 | 0 | } |
1433 | | |
1434 | | static JSObject* |
1435 | | XrayCreateFunction(JSContext* cx, JS::Handle<JSObject*> wrapper, |
1436 | | JSNativeWrapper native, unsigned nargs, JS::Handle<jsid> id) |
1437 | 0 | { |
1438 | 0 | JSFunction* fun; |
1439 | 0 | if (JSID_IS_STRING(id)) { |
1440 | 0 | fun = js::NewFunctionByIdWithReserved(cx, native.op, nargs, 0, id); |
1441 | 0 | } else { |
1442 | 0 | // Can't pass this id (probably a symbol) to NewFunctionByIdWithReserved; |
1443 | 0 | // just use an empty name for lack of anything better. |
1444 | 0 | fun = js::NewFunctionWithReserved(cx, native.op, nargs, 0, nullptr); |
1445 | 0 | } |
1446 | 0 |
|
1447 | 0 | if (!fun) { |
1448 | 0 | return nullptr; |
1449 | 0 | } |
1450 | 0 | |
1451 | 0 | SET_JITINFO(fun, native.info); |
1452 | 0 | JSObject* obj = JS_GetFunctionObject(fun); |
1453 | 0 | js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT, |
1454 | 0 | JS::ObjectValue(*wrapper)); |
1455 | | #ifdef DEBUG |
1456 | | js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF, |
1457 | | JS::ObjectValue(*obj)); |
1458 | | #endif |
1459 | | return obj; |
1460 | 0 | } |
1461 | | |
1462 | | struct IdToIndexComparator |
1463 | | { |
1464 | | // The id we're searching for. |
1465 | | const jsid& mId; |
1466 | | // The list of ids we're searching in. |
1467 | | const PropertyInfo* mInfos; |
1468 | | |
1469 | | explicit IdToIndexComparator(const jsid& aId, const PropertyInfo* aInfos) : |
1470 | 0 | mId(aId), mInfos(aInfos) {} |
1471 | 0 | int operator()(const uint16_t aIndex) const { |
1472 | 0 | if (JSID_BITS(mId) == JSID_BITS(mInfos[aIndex].Id())) { |
1473 | 0 | return 0; |
1474 | 0 | } |
1475 | 0 | return JSID_BITS(mId) < JSID_BITS(mInfos[aIndex].Id()) ? -1 : 1; |
1476 | 0 | } |
1477 | | }; |
1478 | | |
1479 | | static const PropertyInfo* |
1480 | | XrayFindOwnPropertyInfo(JSContext* cx, JS::Handle<jsid> id, |
1481 | | const NativeProperties* nativeProperties) |
1482 | 0 | { |
1483 | 0 | if (MOZ_UNLIKELY(nativeProperties->iteratorAliasMethodIndex >= 0) && |
1484 | 0 | id == SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::iterator))) { |
1485 | 0 | return nativeProperties->MethodPropertyInfos() + |
1486 | 0 | nativeProperties->iteratorAliasMethodIndex; |
1487 | 0 | } |
1488 | 0 | |
1489 | 0 | size_t idx; |
1490 | 0 | const uint16_t* sortedPropertyIndices = nativeProperties->sortedPropertyIndices; |
1491 | 0 | const PropertyInfo* propertyInfos = nativeProperties->PropertyInfos(); |
1492 | 0 |
|
1493 | 0 | if (BinarySearchIf(sortedPropertyIndices, 0, |
1494 | 0 | nativeProperties->propertyInfoCount, |
1495 | 0 | IdToIndexComparator(id, propertyInfos), &idx)) { |
1496 | 0 | return propertyInfos + sortedPropertyIndices[idx]; |
1497 | 0 | } |
1498 | 0 | |
1499 | 0 | return nullptr; |
1500 | 0 | } |
1501 | | |
1502 | | static bool |
1503 | | XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper, |
1504 | | JS::Handle<JSObject*> obj, JS::Handle<jsid> id, |
1505 | | const Prefable<const JSPropertySpec>& pref, |
1506 | | const JSPropertySpec& attrSpec, |
1507 | | JS::MutableHandle<JS::PropertyDescriptor> desc, |
1508 | | bool& cacheOnHolder) |
1509 | 0 | { |
1510 | 0 | if (!pref.isEnabled(cx, obj)) { |
1511 | 0 | return true; |
1512 | 0 | } |
1513 | 0 | |
1514 | 0 | cacheOnHolder = true; |
1515 | 0 |
|
1516 | 0 | // Because of centralization, we need to make sure we fault in the JitInfos as |
1517 | 0 | // well. At present, until the JSAPI changes, the easiest way to do this is |
1518 | 0 | // wrap them up as functions ourselves. |
1519 | 0 | desc.setAttributes(attrSpec.flags); |
1520 | 0 | // They all have getters, so we can just make it. |
1521 | 0 | JS::Rooted<JSObject*> funobj(cx, |
1522 | 0 | XrayCreateFunction(cx, wrapper, attrSpec.accessors.getter.native, 0, id)); |
1523 | 0 | if (!funobj) |
1524 | 0 | return false; |
1525 | 0 | desc.setGetterObject(funobj); |
1526 | 0 | desc.attributesRef() |= JSPROP_GETTER; |
1527 | 0 | if (attrSpec.accessors.setter.native.op) { |
1528 | 0 | // We have a setter! Make it. |
1529 | 0 | funobj = |
1530 | 0 | XrayCreateFunction(cx, wrapper, attrSpec.accessors.setter.native, 1, id); |
1531 | 0 | if (!funobj) |
1532 | 0 | return false; |
1533 | 0 | desc.setSetterObject(funobj); |
1534 | 0 | desc.attributesRef() |= JSPROP_SETTER; |
1535 | 0 | } else { |
1536 | 0 | desc.setSetter(nullptr); |
1537 | 0 | } |
1538 | 0 | desc.object().set(wrapper); |
1539 | 0 | desc.value().setUndefined(); |
1540 | 0 |
|
1541 | 0 | return true; |
1542 | 0 | } |
1543 | | |
1544 | | static bool |
1545 | | XrayResolveMethod(JSContext* cx, JS::Handle<JSObject*> wrapper, |
1546 | | JS::Handle<JSObject*> obj, JS::Handle<jsid> id, |
1547 | | const Prefable<const JSFunctionSpec>& pref, |
1548 | | const JSFunctionSpec& methodSpec, |
1549 | | JS::MutableHandle<JS::PropertyDescriptor> desc, |
1550 | | bool& cacheOnHolder) |
1551 | 0 | { |
1552 | 0 | if (!pref.isEnabled(cx, obj)) { |
1553 | 0 | return true; |
1554 | 0 | } |
1555 | 0 | |
1556 | 0 | cacheOnHolder = true; |
1557 | 0 |
|
1558 | 0 | JSObject *funobj; |
1559 | 0 | if (methodSpec.selfHostedName) { |
1560 | 0 | JSFunction* fun = |
1561 | 0 | JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, id, |
1562 | 0 | methodSpec.nargs); |
1563 | 0 | if (!fun) { |
1564 | 0 | return false; |
1565 | 0 | } |
1566 | 0 | MOZ_ASSERT(!methodSpec.call.op, "Bad FunctionSpec declaration: non-null native"); |
1567 | 0 | MOZ_ASSERT(!methodSpec.call.info, "Bad FunctionSpec declaration: non-null jitinfo"); |
1568 | 0 | funobj = JS_GetFunctionObject(fun); |
1569 | 0 | } else { |
1570 | 0 | funobj = XrayCreateFunction(cx, wrapper, methodSpec.call, |
1571 | 0 | methodSpec.nargs, id); |
1572 | 0 | if (!funobj) { |
1573 | 0 | return false; |
1574 | 0 | } |
1575 | 0 | } |
1576 | 0 | desc.value().setObject(*funobj); |
1577 | 0 | desc.setAttributes(methodSpec.flags); |
1578 | 0 | desc.object().set(wrapper); |
1579 | 0 | desc.setSetter(nullptr); |
1580 | 0 | desc.setGetter(nullptr); |
1581 | 0 |
|
1582 | 0 | return true; |
1583 | 0 | } |
1584 | | |
1585 | | static bool |
1586 | | XrayResolveConstant(JSContext* cx, JS::Handle<JSObject*> wrapper, |
1587 | | JS::Handle<JSObject*> obj, JS::Handle<jsid>, |
1588 | | const Prefable<const ConstantSpec>& pref, |
1589 | | const ConstantSpec& constantSpec, |
1590 | | JS::MutableHandle<JS::PropertyDescriptor> desc, |
1591 | | bool& cacheOnHolder) |
1592 | 0 | { |
1593 | 0 | if (!pref.isEnabled(cx, obj)) { |
1594 | 0 | return true; |
1595 | 0 | } |
1596 | 0 | |
1597 | 0 | cacheOnHolder = true; |
1598 | 0 |
|
1599 | 0 | desc.setAttributes(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); |
1600 | 0 | desc.object().set(wrapper); |
1601 | 0 | desc.value().set(constantSpec.value); |
1602 | 0 |
|
1603 | 0 | return true; |
1604 | 0 | } |
1605 | | |
1606 | | #define RESOLVE_CASE(PropType, SpecType, Resolver) \ |
1607 | 0 | case e##PropType: { \ |
1608 | 0 | MOZ_ASSERT(nativeProperties->Has##PropType##s()); \ |
1609 | 0 | const Prefable<const SpecType>& pref = \ |
1610 | 0 | nativeProperties->PropType##s()[propertyInfo.prefIndex]; \ |
1611 | 0 | return Resolver(cx, wrapper, obj, id, pref, \ |
1612 | 0 | pref.specs[propertyInfo.specIndex], desc, cacheOnHolder); \ |
1613 | 0 | } |
1614 | | |
1615 | | static bool |
1616 | | XrayResolveProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
1617 | | JS::Handle<JSObject*> obj, JS::Handle<jsid> id, |
1618 | | JS::MutableHandle<JS::PropertyDescriptor> desc, |
1619 | | bool& cacheOnHolder, DOMObjectType type, |
1620 | | const NativeProperties* nativeProperties, |
1621 | | const PropertyInfo& propertyInfo) |
1622 | 0 | { |
1623 | 0 | MOZ_ASSERT(type != eGlobalInterfacePrototype); |
1624 | 0 |
|
1625 | 0 | // Make sure we resolve for matched object type. |
1626 | 0 | switch (propertyInfo.type) { |
1627 | 0 | case eStaticMethod: |
1628 | 0 | case eStaticAttribute: |
1629 | 0 | if (type != eInterface) { |
1630 | 0 | return true; |
1631 | 0 | } |
1632 | 0 | break; |
1633 | 0 | case eMethod: |
1634 | 0 | case eAttribute: |
1635 | 0 | if (type != eGlobalInstance && type != eInterfacePrototype) { |
1636 | 0 | return true; |
1637 | 0 | } |
1638 | 0 | break; |
1639 | 0 | case eUnforgeableMethod: |
1640 | 0 | case eUnforgeableAttribute: |
1641 | 0 | if (!IsInstance(type)) { |
1642 | 0 | return true; |
1643 | 0 | } |
1644 | 0 | break; |
1645 | 0 | case eConstant: |
1646 | 0 | if (IsInstance(type)) { |
1647 | 0 | return true; |
1648 | 0 | } |
1649 | 0 | break; |
1650 | 0 | } |
1651 | 0 | |
1652 | 0 | switch (propertyInfo.type) { |
1653 | 0 | RESOLVE_CASE(StaticMethod, JSFunctionSpec, XrayResolveMethod) |
1654 | 0 | RESOLVE_CASE(StaticAttribute, JSPropertySpec, XrayResolveAttribute) |
1655 | 0 | RESOLVE_CASE(Method, JSFunctionSpec, XrayResolveMethod) |
1656 | 0 | RESOLVE_CASE(Attribute, JSPropertySpec, XrayResolveAttribute) |
1657 | 0 | RESOLVE_CASE(UnforgeableMethod, JSFunctionSpec, XrayResolveMethod) |
1658 | 0 | RESOLVE_CASE(UnforgeableAttribute, JSPropertySpec, XrayResolveAttribute) |
1659 | 0 | RESOLVE_CASE(Constant, ConstantSpec, XrayResolveConstant) |
1660 | 0 | } |
1661 | 0 |
|
1662 | 0 | return true; |
1663 | 0 | } |
1664 | | |
1665 | | #undef RESOLVE_CASE |
1666 | | |
1667 | | static bool |
1668 | | ResolvePrototypeOrConstructor(JSContext* cx, JS::Handle<JSObject*> wrapper, |
1669 | | JS::Handle<JSObject*> obj, |
1670 | | size_t protoAndIfaceCacheIndex, unsigned attrs, |
1671 | | JS::MutableHandle<JS::PropertyDescriptor> desc, |
1672 | | bool& cacheOnHolder) |
1673 | 0 | { |
1674 | 0 | JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(obj)); |
1675 | 0 | { |
1676 | 0 | JSAutoRealm ar(cx, global); |
1677 | 0 | ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global); |
1678 | 0 | // This function is called when resolving the "constructor" and "prototype" |
1679 | 0 | // properties of Xrays for DOM prototypes and constructors respectively. |
1680 | 0 | // This means the relevant Xray exists, which means its _target_ exists. |
1681 | 0 | // And that means we managed to successfullly create the prototype or |
1682 | 0 | // constructor, respectively, and hence must have managed to create the |
1683 | 0 | // thing it's pointing to as well. So our entry slot must exist. |
1684 | 0 | JSObject* protoOrIface = |
1685 | 0 | protoAndIfaceCache.EntrySlotMustExist(protoAndIfaceCacheIndex); |
1686 | 0 | MOZ_RELEASE_ASSERT(protoOrIface, "How can this object not exist?"); |
1687 | 0 |
|
1688 | 0 | cacheOnHolder = true; |
1689 | 0 |
|
1690 | 0 | desc.object().set(wrapper); |
1691 | 0 | desc.setAttributes(attrs); |
1692 | 0 | desc.setGetter(nullptr); |
1693 | 0 | desc.setSetter(nullptr); |
1694 | 0 | desc.value().set(JS::ObjectValue(*protoOrIface)); |
1695 | 0 | } |
1696 | 0 | return JS_WrapPropertyDescriptor(cx, desc); |
1697 | 0 | } |
1698 | | |
1699 | | #ifdef DEBUG |
1700 | | |
1701 | | static void |
1702 | | DEBUG_CheckXBLCallable(JSContext *cx, JSObject *obj) |
1703 | | { |
1704 | | // In general, we shouldn't have cross-compartment wrappers here, because |
1705 | | // we should be running in an XBL scope, and the content prototype should |
1706 | | // contain wrappers to functions defined in the XBL scope. But if the node |
1707 | | // has been adopted into another compartment, those prototypes will now point |
1708 | | // to a different XBL scope (which is ok). |
1709 | | MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(obj), |
1710 | | xpc::IsInContentXBLScope(js::UncheckedUnwrap(obj))); |
1711 | | MOZ_ASSERT(JS::IsCallable(obj)); |
1712 | | } |
1713 | | |
1714 | | static void |
1715 | | DEBUG_CheckXBLLookup(JSContext *cx, JS::PropertyDescriptor *desc) |
1716 | | { |
1717 | | if (!desc->obj) |
1718 | | return; |
1719 | | if (!desc->value.isUndefined()) { |
1720 | | MOZ_ASSERT(desc->value.isObject()); |
1721 | | DEBUG_CheckXBLCallable(cx, &desc->value.toObject()); |
1722 | | } |
1723 | | if (desc->getter) { |
1724 | | MOZ_ASSERT(desc->attrs & JSPROP_GETTER); |
1725 | | DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject *, desc->getter)); |
1726 | | } |
1727 | | if (desc->setter) { |
1728 | | MOZ_ASSERT(desc->attrs & JSPROP_SETTER); |
1729 | | DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject *, desc->setter)); |
1730 | | } |
1731 | | } |
1732 | | #else |
1733 | 0 | #define DEBUG_CheckXBLLookup(a, b) {} |
1734 | | #endif |
1735 | | |
1736 | | /* static */ bool |
1737 | | XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
1738 | | JS::Handle<JSObject*> obj, JS::Handle<jsid> id, |
1739 | | JS::MutableHandle<JS::PropertyDescriptor> desc, |
1740 | | bool& cacheOnHolder) |
1741 | 0 | { |
1742 | 0 | cacheOnHolder = false; |
1743 | 0 |
|
1744 | 0 | DOMObjectType type; |
1745 | 0 | const NativePropertyHooks *nativePropertyHooks = |
1746 | 0 | GetNativePropertyHooks(cx, obj, type); |
1747 | 0 | ResolveOwnProperty resolveOwnProperty = |
1748 | 0 | nativePropertyHooks->mResolveOwnProperty; |
1749 | 0 |
|
1750 | 0 | if (type == eNamedPropertiesObject) { |
1751 | 0 | MOZ_ASSERT(!resolveOwnProperty, |
1752 | 0 | "Shouldn't have any Xray-visible properties"); |
1753 | 0 | return true; |
1754 | 0 | } |
1755 | 0 |
|
1756 | 0 | const NativePropertiesHolder& nativePropertiesHolder = |
1757 | 0 | nativePropertyHooks->mNativeProperties; |
1758 | 0 | const NativeProperties* nativeProperties = nullptr; |
1759 | 0 | const PropertyInfo* found = nullptr; |
1760 | 0 |
|
1761 | 0 | if ((nativeProperties = nativePropertiesHolder.regular)) { |
1762 | 0 | found = XrayFindOwnPropertyInfo(cx, id, nativeProperties); |
1763 | 0 | } |
1764 | 0 | if (!found && |
1765 | 0 | (nativeProperties = nativePropertiesHolder.chromeOnly) && |
1766 | 0 | xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper))) { |
1767 | 0 | found = XrayFindOwnPropertyInfo(cx, id, nativeProperties); |
1768 | 0 | } |
1769 | 0 |
|
1770 | 0 | if (IsInstance(type)) { |
1771 | 0 | // Check for unforgeable properties first to prevent names provided by |
1772 | 0 | // resolveOwnProperty callback from shadowing them. |
1773 | 0 | if (found && (found->type == eUnforgeableMethod || |
1774 | 0 | found->type == eUnforgeableAttribute)) { |
1775 | 0 | if (!XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder, type, |
1776 | 0 | nativeProperties, *found)) { |
1777 | 0 | return false; |
1778 | 0 | } |
1779 | 0 | |
1780 | 0 | if (desc.object()) { |
1781 | 0 | return true; |
1782 | 0 | } |
1783 | 0 | } |
1784 | 0 | |
1785 | 0 | if (resolveOwnProperty) { |
1786 | 0 | if (!resolveOwnProperty(cx, wrapper, obj, id, desc)) { |
1787 | 0 | return false; |
1788 | 0 | } |
1789 | 0 | |
1790 | 0 | if (desc.object()) { |
1791 | 0 | // None of these should be cached on the holder, since they're dynamic. |
1792 | 0 | return true; |
1793 | 0 | } |
1794 | 0 | } |
1795 | 0 | |
1796 | 0 | // If we're a special scope for in-content XBL, our script expects to see |
1797 | 0 | // the bound XBL methods and attributes when accessing content. However, |
1798 | 0 | // these members are implemented in content via custom-spliced prototypes, |
1799 | 0 | // and thus aren't visible through Xray wrappers unless we handle them |
1800 | 0 | // explicitly. So we check if we're running in such a scope, and if so, |
1801 | 0 | // whether the wrappee is a bound element. If it is, we do a lookup via |
1802 | 0 | // specialized XBL machinery. |
1803 | 0 | // |
1804 | 0 | // While we have to do some sketchy walking through content land, we should |
1805 | 0 | // be protected by read-only/non-configurable properties, and any functions |
1806 | 0 | // we end up with should _always_ be living in our own scope (the XBL scope). |
1807 | 0 | // Make sure to assert that. |
1808 | 0 | JS::Rooted<JSObject*> maybeElement(cx, obj); |
1809 | 0 | Element* element; |
1810 | 0 | if (xpc::IsInContentXBLScope(wrapper) && |
1811 | 0 | NS_SUCCEEDED(UNWRAP_OBJECT(Element, &maybeElement, element))) { |
1812 | 0 | if (!nsContentUtils::LookupBindingMember(cx, element, id, desc)) { |
1813 | 0 | return false; |
1814 | 0 | } |
1815 | 0 | |
1816 | 0 | DEBUG_CheckXBLLookup(cx, desc.address()); |
1817 | 0 |
|
1818 | 0 | if (desc.object()) { |
1819 | 0 | // XBL properties shouldn't be cached on the holder, as they might be |
1820 | 0 | // shadowed by own properties returned from mResolveOwnProperty. |
1821 | 0 | desc.object().set(wrapper); |
1822 | 0 |
|
1823 | 0 | return true; |
1824 | 0 | } |
1825 | 0 | } |
1826 | 0 | |
1827 | 0 | // For non-global instance Xrays there are no other properties, so return |
1828 | 0 | // here for them. |
1829 | 0 | if (type != eGlobalInstance) { |
1830 | 0 | return true; |
1831 | 0 | } |
1832 | 0 | } else if (type == eInterface) { |
1833 | 0 | if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_PROTOTYPE)) { |
1834 | 0 | return nativePropertyHooks->mPrototypeID == prototypes::id::_ID_Count || |
1835 | 0 | ResolvePrototypeOrConstructor(cx, wrapper, obj, |
1836 | 0 | nativePropertyHooks->mPrototypeID, |
1837 | 0 | JSPROP_PERMANENT | JSPROP_READONLY, |
1838 | 0 | desc, cacheOnHolder); |
1839 | 0 | } |
1840 | 0 |
|
1841 | 0 | if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_ISINSTANCE) && |
1842 | 0 | DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj))-> |
1843 | 0 | wantsInterfaceHasInstance) { |
1844 | 0 | cacheOnHolder = true; |
1845 | 0 | JSNativeWrapper interfaceIsInstanceWrapper = { InterfaceIsInstance, |
1846 | 0 | nullptr }; |
1847 | 0 | JSObject* funObj = XrayCreateFunction(cx, wrapper, |
1848 | 0 | interfaceIsInstanceWrapper, 1, id); |
1849 | 0 | if (!funObj) { |
1850 | 0 | return false; |
1851 | 0 | } |
1852 | 0 | |
1853 | 0 | desc.value().setObject(*funObj); |
1854 | 0 | desc.setAttributes(0); |
1855 | 0 | desc.object().set(wrapper); |
1856 | 0 | desc.setSetter(nullptr); |
1857 | 0 | desc.setGetter(nullptr); |
1858 | 0 | return true; |
1859 | 0 | } |
1860 | 0 | |
1861 | 0 | if (id == SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::hasInstance)) && |
1862 | 0 | DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj))-> |
1863 | 0 | wantsInterfaceHasInstance) { |
1864 | 0 | cacheOnHolder = true; |
1865 | 0 | JSNativeWrapper interfaceHasInstanceWrapper = { InterfaceHasInstance, |
1866 | 0 | nullptr }; |
1867 | 0 | JSObject* funObj = XrayCreateFunction(cx, wrapper, |
1868 | 0 | interfaceHasInstanceWrapper, 1, id); |
1869 | 0 | if (!funObj) { |
1870 | 0 | return false; |
1871 | 0 | } |
1872 | 0 | |
1873 | 0 | desc.value().setObject(*funObj); |
1874 | 0 | desc.setAttributes(JSPROP_READONLY | JSPROP_PERMANENT); |
1875 | 0 | desc.object().set(wrapper); |
1876 | 0 | desc.setSetter(nullptr); |
1877 | 0 | desc.setGetter(nullptr); |
1878 | 0 | return true; |
1879 | 0 | } |
1880 | 0 | } else { |
1881 | 0 | MOZ_ASSERT(IsInterfacePrototype(type)); |
1882 | 0 |
|
1883 | 0 | if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_CONSTRUCTOR)) { |
1884 | 0 | return nativePropertyHooks->mConstructorID == constructors::id::_ID_Count || |
1885 | 0 | ResolvePrototypeOrConstructor(cx, wrapper, obj, |
1886 | 0 | nativePropertyHooks->mConstructorID, |
1887 | 0 | 0, desc, cacheOnHolder); |
1888 | 0 | } |
1889 | 0 |
|
1890 | 0 | // The properties for globals live on the instance, so return here as there |
1891 | 0 | // are no properties on their interface prototype object. |
1892 | 0 | if (type == eGlobalInterfacePrototype) { |
1893 | 0 | return true; |
1894 | 0 | } |
1895 | 0 | } |
1896 | 0 | |
1897 | 0 | if (found && |
1898 | 0 | !XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder, type, |
1899 | 0 | nativeProperties, *found)) { |
1900 | 0 | return false; |
1901 | 0 | } |
1902 | 0 | |
1903 | 0 | return true; |
1904 | 0 | } |
1905 | | |
1906 | | bool |
1907 | | XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
1908 | | JS::Handle<JSObject*> obj, JS::Handle<jsid> id, |
1909 | | JS::Handle<JS::PropertyDescriptor> desc, |
1910 | | JS::ObjectOpResult &result, bool *defined) |
1911 | 0 | { |
1912 | 0 | if (!js::IsProxy(obj)) |
1913 | 0 | return true; |
1914 | 0 | |
1915 | 0 | const DOMProxyHandler* handler = GetDOMProxyHandler(obj); |
1916 | 0 | return handler->defineProperty(cx, wrapper, id, desc, result, defined); |
1917 | 0 | } |
1918 | | |
1919 | | template<typename SpecType> |
1920 | | bool |
1921 | | XrayAppendPropertyKeys(JSContext* cx, JS::Handle<JSObject*> obj, |
1922 | | const Prefable<const SpecType>* pref, |
1923 | | const PropertyInfo* infos, unsigned flags, |
1924 | | JS::AutoIdVector& props) |
1925 | 0 | { |
1926 | 0 | do { |
1927 | 0 | bool prefIsEnabled = pref->isEnabled(cx, obj); |
1928 | 0 | if (prefIsEnabled) { |
1929 | 0 | const SpecType* spec = pref->specs; |
1930 | 0 | do { |
1931 | 0 | const jsid id = infos++->Id(); |
1932 | 0 | if (((flags & JSITER_HIDDEN) || |
1933 | 0 | (spec->flags & JSPROP_ENUMERATE)) && |
1934 | 0 | ((flags & JSITER_SYMBOLS) || !JSID_IS_SYMBOL(id)) && |
1935 | 0 | !props.append(id)) { |
1936 | 0 | return false; |
1937 | 0 | } |
1938 | 0 | } while ((++spec)->name); |
1939 | 0 | } |
1940 | 0 | // Break if we have reached the end of pref. |
1941 | 0 | if (!(++pref)->specs) { |
1942 | 0 | break; |
1943 | 0 | } |
1944 | 0 | // Advance infos if the previous pref is disabled. The -1 is required |
1945 | 0 | // because there is an end-of-list terminator between pref->specs and |
1946 | 0 | // (pref - 1)->specs. |
1947 | 0 | if (!prefIsEnabled) { |
1948 | 0 | infos += pref->specs - (pref - 1)->specs - 1; |
1949 | 0 | } |
1950 | 0 | } while (1); |
1951 | 0 |
|
1952 | 0 | return true; |
1953 | 0 | } Unexecuted instantiation: bool mozilla::dom::XrayAppendPropertyKeys<JSFunctionSpec>(JSContext*, JS::Handle<JSObject*>, mozilla::dom::Prefable<JSFunctionSpec const> const*, mozilla::dom::PropertyInfo const*, unsigned int, JS::AutoVector<jsid>&) Unexecuted instantiation: bool mozilla::dom::XrayAppendPropertyKeys<JSPropertySpec>(JSContext*, JS::Handle<JSObject*>, mozilla::dom::Prefable<JSPropertySpec const> const*, mozilla::dom::PropertyInfo const*, unsigned int, JS::AutoVector<jsid>&) |
1954 | | |
1955 | | template<> |
1956 | | bool |
1957 | | XrayAppendPropertyKeys<ConstantSpec>(JSContext* cx, JS::Handle<JSObject*> obj, |
1958 | | const Prefable<const ConstantSpec>* pref, |
1959 | | const PropertyInfo* infos, unsigned flags, |
1960 | | JS::AutoIdVector& props) |
1961 | 0 | { |
1962 | 0 | do { |
1963 | 0 | bool prefIsEnabled = pref->isEnabled(cx, obj); |
1964 | 0 | if (prefIsEnabled) { |
1965 | 0 | const ConstantSpec* spec = pref->specs; |
1966 | 0 | do { |
1967 | 0 | if (!props.append(infos++->Id())) { |
1968 | 0 | return false; |
1969 | 0 | } |
1970 | 0 | } while ((++spec)->name); |
1971 | 0 | } |
1972 | 0 | // Break if we have reached the end of pref. |
1973 | 0 | if (!(++pref)->specs) { |
1974 | 0 | break; |
1975 | 0 | } |
1976 | 0 | // Advance infos if the previous pref is disabled. The -1 is required |
1977 | 0 | // because there is an end-of-list terminator between pref->specs and |
1978 | 0 | // (pref - 1)->specs. |
1979 | 0 | if (!prefIsEnabled) { |
1980 | 0 | infos += pref->specs - (pref - 1)->specs - 1; |
1981 | 0 | } |
1982 | 0 | } while (1); |
1983 | 0 |
|
1984 | 0 | return true; |
1985 | 0 | } |
1986 | | |
1987 | 0 | #define ADD_KEYS_IF_DEFINED(FieldName) { \ |
1988 | 0 | if (nativeProperties->Has##FieldName##s() && \ |
1989 | 0 | !XrayAppendPropertyKeys(cx, obj, \ |
1990 | 0 | nativeProperties->FieldName##s(), \ |
1991 | 0 | nativeProperties->FieldName##PropertyInfos(), \ |
1992 | 0 | flags, props)) { \ |
1993 | 0 | return false; \ |
1994 | 0 | } \ |
1995 | 0 | } |
1996 | | |
1997 | | bool |
1998 | | XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper, |
1999 | | JS::Handle<JSObject*> obj, |
2000 | | unsigned flags, JS::AutoIdVector& props, |
2001 | | DOMObjectType type, |
2002 | | const NativeProperties* nativeProperties) |
2003 | 0 | { |
2004 | 0 | MOZ_ASSERT(type != eNamedPropertiesObject); |
2005 | 0 |
|
2006 | 0 | if (IsInstance(type)) { |
2007 | 0 | ADD_KEYS_IF_DEFINED(UnforgeableMethod); |
2008 | 0 | ADD_KEYS_IF_DEFINED(UnforgeableAttribute); |
2009 | 0 | if (type == eGlobalInstance) { |
2010 | 0 | ADD_KEYS_IF_DEFINED(Method); |
2011 | 0 | ADD_KEYS_IF_DEFINED(Attribute); |
2012 | 0 | } |
2013 | 0 | } else { |
2014 | 0 | MOZ_ASSERT(type != eGlobalInterfacePrototype); |
2015 | 0 | if (type == eInterface) { |
2016 | 0 | ADD_KEYS_IF_DEFINED(StaticMethod); |
2017 | 0 | ADD_KEYS_IF_DEFINED(StaticAttribute); |
2018 | 0 | } else { |
2019 | 0 | MOZ_ASSERT(type == eInterfacePrototype); |
2020 | 0 | ADD_KEYS_IF_DEFINED(Method); |
2021 | 0 | ADD_KEYS_IF_DEFINED(Attribute); |
2022 | 0 | } |
2023 | 0 | ADD_KEYS_IF_DEFINED(Constant); |
2024 | 0 | } |
2025 | 0 |
|
2026 | 0 | return true; |
2027 | 0 | } |
2028 | | |
2029 | | #undef ADD_KEYS_IF_DEFINED |
2030 | | |
2031 | | bool |
2032 | | XrayOwnNativePropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper, |
2033 | | const NativePropertyHooks* nativePropertyHooks, |
2034 | | DOMObjectType type, JS::Handle<JSObject*> obj, |
2035 | | unsigned flags, JS::AutoIdVector& props) |
2036 | 0 | { |
2037 | 0 | MOZ_ASSERT(type != eNamedPropertiesObject); |
2038 | 0 |
|
2039 | 0 | if (type == eInterface && |
2040 | 0 | nativePropertyHooks->mPrototypeID != prototypes::id::_ID_Count && |
2041 | 0 | !AddStringToIDVector(cx, props, "prototype")) { |
2042 | 0 | return false; |
2043 | 0 | } |
2044 | 0 | |
2045 | 0 | if (IsInterfacePrototype(type) && |
2046 | 0 | nativePropertyHooks->mConstructorID != constructors::id::_ID_Count && |
2047 | 0 | (flags & JSITER_HIDDEN) && |
2048 | 0 | !AddStringToIDVector(cx, props, "constructor")) { |
2049 | 0 | return false; |
2050 | 0 | } |
2051 | 0 | |
2052 | 0 | const NativePropertiesHolder& nativeProperties = |
2053 | 0 | nativePropertyHooks->mNativeProperties; |
2054 | 0 |
|
2055 | 0 | if (nativeProperties.regular && |
2056 | 0 | !XrayOwnPropertyKeys(cx, wrapper, obj, flags, props, type, |
2057 | 0 | nativeProperties.regular)) { |
2058 | 0 | return false; |
2059 | 0 | } |
2060 | 0 | |
2061 | 0 | if (nativeProperties.chromeOnly && |
2062 | 0 | xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) && |
2063 | 0 | !XrayOwnPropertyKeys(cx, wrapper, obj, flags, props, type, |
2064 | 0 | nativeProperties.chromeOnly)) { |
2065 | 0 | return false; |
2066 | 0 | } |
2067 | 0 | |
2068 | 0 | return true; |
2069 | 0 | } |
2070 | | |
2071 | | bool |
2072 | | XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper, |
2073 | | JS::Handle<JSObject*> obj, |
2074 | | unsigned flags, JS::AutoIdVector& props) |
2075 | 0 | { |
2076 | 0 | DOMObjectType type; |
2077 | 0 | const NativePropertyHooks* nativePropertyHooks = |
2078 | 0 | GetNativePropertyHooks(cx, obj, type); |
2079 | 0 | EnumerateOwnProperties enumerateOwnProperties = |
2080 | 0 | nativePropertyHooks->mEnumerateOwnProperties; |
2081 | 0 |
|
2082 | 0 | if (type == eNamedPropertiesObject) { |
2083 | 0 | MOZ_ASSERT(!enumerateOwnProperties, |
2084 | 0 | "Shouldn't have any Xray-visible properties"); |
2085 | 0 | return true; |
2086 | 0 | } |
2087 | 0 |
|
2088 | 0 | if (IsInstance(type)) { |
2089 | 0 | // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1071189 |
2090 | 0 | // Should do something about XBL properties too. |
2091 | 0 | if (enumerateOwnProperties && |
2092 | 0 | !enumerateOwnProperties(cx, wrapper, obj, props)) { |
2093 | 0 | return false; |
2094 | 0 | } |
2095 | 0 | } |
2096 | 0 | |
2097 | 0 | return type == eGlobalInterfacePrototype || |
2098 | 0 | XrayOwnNativePropertyKeys(cx, wrapper, nativePropertyHooks, type, |
2099 | 0 | obj, flags, props); |
2100 | 0 | } |
2101 | | |
2102 | | const JSClass* |
2103 | | XrayGetExpandoClass(JSContext* cx, JS::Handle<JSObject*> obj) |
2104 | 0 | { |
2105 | 0 | DOMObjectType type; |
2106 | 0 | const NativePropertyHooks* nativePropertyHooks = |
2107 | 0 | GetNativePropertyHooks(cx, obj, type); |
2108 | 0 | if (!IsInstance(type)) { |
2109 | 0 | // Non-instances don't need any special expando classes. |
2110 | 0 | return &DefaultXrayExpandoObjectClass; |
2111 | 0 | } |
2112 | 0 | |
2113 | 0 | return nativePropertyHooks->mXrayExpandoClass; |
2114 | 0 | } |
2115 | | |
2116 | | bool |
2117 | | XrayDeleteNamedProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
2118 | | JS::Handle<JSObject*> obj, JS::Handle<jsid> id, |
2119 | | JS::ObjectOpResult& opresult) |
2120 | 0 | { |
2121 | 0 | DOMObjectType type; |
2122 | 0 | const NativePropertyHooks* nativePropertyHooks = |
2123 | 0 | GetNativePropertyHooks(cx, obj, type); |
2124 | 0 | if (!IsInstance(type) || !nativePropertyHooks->mDeleteNamedProperty) { |
2125 | 0 | return opresult.succeed(); |
2126 | 0 | } |
2127 | 0 | return nativePropertyHooks->mDeleteNamedProperty(cx, wrapper, obj, id, |
2128 | 0 | opresult); |
2129 | 0 | } |
2130 | | |
2131 | | JSObject* |
2132 | | GetCachedSlotStorageObjectSlow(JSContext* cx, JS::Handle<JSObject*> obj, |
2133 | | bool* isXray) |
2134 | 0 | { |
2135 | 0 | if (!xpc::WrapperFactory::IsXrayWrapper(obj)) { |
2136 | 0 | JSObject* retval = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false); |
2137 | 0 | MOZ_ASSERT(IsDOMObject(retval)); |
2138 | 0 | *isXray = false; |
2139 | 0 | return retval; |
2140 | 0 | } |
2141 | 0 |
|
2142 | 0 | *isXray = true; |
2143 | 0 | return xpc::EnsureXrayExpandoObject(cx, obj);; |
2144 | 0 | } |
2145 | | |
2146 | | DEFINE_XRAY_EXPANDO_CLASS(, DefaultXrayExpandoObjectClass, 0); |
2147 | | |
2148 | | NativePropertyHooks sEmptyNativePropertyHooks = { |
2149 | | nullptr, |
2150 | | nullptr, |
2151 | | nullptr, |
2152 | | { |
2153 | | nullptr, |
2154 | | nullptr |
2155 | | }, |
2156 | | prototypes::id::_ID_Count, |
2157 | | constructors::id::_ID_Count, |
2158 | | nullptr |
2159 | | }; |
2160 | | |
2161 | | const js::ClassOps sBoringInterfaceObjectClassClassOps = { |
2162 | | nullptr, /* addProperty */ |
2163 | | nullptr, /* delProperty */ |
2164 | | nullptr, /* enumerate */ |
2165 | | nullptr, /* newEnumerate */ |
2166 | | nullptr, /* resolve */ |
2167 | | nullptr, /* mayResolve */ |
2168 | | nullptr, /* finalize */ |
2169 | | ThrowingConstructor, /* call */ |
2170 | | nullptr, /* hasInstance */ |
2171 | | ThrowingConstructor, /* construct */ |
2172 | | nullptr, /* trace */ |
2173 | | }; |
2174 | | |
2175 | | const js::ObjectOps sInterfaceObjectClassObjectOps = { |
2176 | | nullptr, /* lookupProperty */ |
2177 | | nullptr, /* defineProperty */ |
2178 | | nullptr, /* hasProperty */ |
2179 | | nullptr, /* getProperty */ |
2180 | | nullptr, /* setProperty */ |
2181 | | nullptr, /* getOwnPropertyDescriptor */ |
2182 | | nullptr, /* deleteProperty */ |
2183 | | nullptr, /* getElements */ |
2184 | | InterfaceObjectToString, /* funToString */ |
2185 | | }; |
2186 | | |
2187 | | bool |
2188 | | GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy, |
2189 | | JS::Handle<JS::Value> receiver, JS::Handle<jsid> id, |
2190 | | bool* found, JS::MutableHandle<JS::Value> vp) |
2191 | 0 | { |
2192 | 0 | JS::Rooted<JSObject*> proto(cx); |
2193 | 0 | if (!js::GetObjectProto(cx, proxy, &proto)) { |
2194 | 0 | return false; |
2195 | 0 | } |
2196 | 0 | if (!proto) { |
2197 | 0 | *found = false; |
2198 | 0 | return true; |
2199 | 0 | } |
2200 | 0 | |
2201 | 0 | if (!JS_HasPropertyById(cx, proto, id, found)) { |
2202 | 0 | return false; |
2203 | 0 | } |
2204 | 0 | |
2205 | 0 | if (!*found) { |
2206 | 0 | return true; |
2207 | 0 | } |
2208 | 0 | |
2209 | 0 | return JS_ForwardGetPropertyTo(cx, proto, id, receiver, vp); |
2210 | 0 | } |
2211 | | |
2212 | | bool |
2213 | | HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy, |
2214 | | JS::Handle<jsid> id, bool* has) |
2215 | 0 | { |
2216 | 0 | JS::Rooted<JSObject*> proto(cx); |
2217 | 0 | if (!js::GetObjectProto(cx, proxy, &proto)) { |
2218 | 0 | return false; |
2219 | 0 | } |
2220 | 0 | if (!proto) { |
2221 | 0 | *has = false; |
2222 | 0 | return true; |
2223 | 0 | } |
2224 | 0 | |
2225 | 0 | return JS_HasPropertyById(cx, proto, id, has); |
2226 | 0 | } |
2227 | | |
2228 | | bool |
2229 | | AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy, |
2230 | | nsTArray<nsString>& names, |
2231 | | bool shadowPrototypeProperties, |
2232 | | JS::AutoIdVector& props) |
2233 | 0 | { |
2234 | 0 | for (uint32_t i = 0; i < names.Length(); ++i) { |
2235 | 0 | JS::Rooted<JS::Value> v(cx); |
2236 | 0 | if (!xpc::NonVoidStringToJsval(cx, names[i], &v)) { |
2237 | 0 | return false; |
2238 | 0 | } |
2239 | 0 | |
2240 | 0 | JS::Rooted<jsid> id(cx); |
2241 | 0 | if (!JS_ValueToId(cx, v, &id)) { |
2242 | 0 | return false; |
2243 | 0 | } |
2244 | 0 | |
2245 | 0 | bool shouldAppend = shadowPrototypeProperties; |
2246 | 0 | if (!shouldAppend) { |
2247 | 0 | bool has; |
2248 | 0 | if (!HasPropertyOnPrototype(cx, proxy, id, &has)) { |
2249 | 0 | return false; |
2250 | 0 | } |
2251 | 0 | shouldAppend = !has; |
2252 | 0 | } |
2253 | 0 |
|
2254 | 0 | if (shouldAppend) { |
2255 | 0 | if (!props.append(id)) { |
2256 | 0 | return false; |
2257 | 0 | } |
2258 | 0 | } |
2259 | 0 | } |
2260 | 0 |
|
2261 | 0 | return true; |
2262 | 0 | } |
2263 | | |
2264 | | bool |
2265 | | DictionaryBase::ParseJSON(JSContext* aCx, |
2266 | | const nsAString& aJSON, |
2267 | | JS::MutableHandle<JS::Value> aVal) |
2268 | 0 | { |
2269 | 0 | if (aJSON.IsEmpty()) { |
2270 | 0 | return true; |
2271 | 0 | } |
2272 | 0 | return JS_ParseJSON(aCx, PromiseFlatString(aJSON).get(), aJSON.Length(), aVal); |
2273 | 0 | } |
2274 | | |
2275 | | bool |
2276 | | DictionaryBase::StringifyToJSON(JSContext* aCx, |
2277 | | JS::Handle<JSObject*> aObj, |
2278 | | nsAString& aJSON) const |
2279 | 0 | { |
2280 | 0 | return JS::ToJSONMaybeSafely(aCx, aObj, AppendJSONToString, &aJSON); |
2281 | 0 | } |
2282 | | |
2283 | | /* static */ |
2284 | | bool |
2285 | | DictionaryBase::AppendJSONToString(const char16_t* aJSONData, |
2286 | | uint32_t aDataLength, |
2287 | | void* aString) |
2288 | 0 | { |
2289 | 0 | nsAString* string = static_cast<nsAString*>(aString); |
2290 | 0 | string->Append(aJSONData, aDataLength); |
2291 | 0 | return true; |
2292 | 0 | } |
2293 | | |
2294 | | void |
2295 | | ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg, ErrorResult& aError) |
2296 | 0 | { |
2297 | 0 | js::AssertSameCompartment(aCx, aObjArg); |
2298 | 0 |
|
2299 | 0 | aError.MightThrowJSException(); |
2300 | 0 |
|
2301 | 0 | // Check if we're anywhere near the stack limit before we reach the |
2302 | 0 | // transplanting code, since it has no good way to handle errors. This uses |
2303 | 0 | // the untrusted script limit, which is not strictly necessary since no |
2304 | 0 | // actual script should run. |
2305 | 0 | if (!js::CheckRecursionLimitConservative(aCx)) { |
2306 | 0 | aError.StealExceptionFromJSContext(aCx); |
2307 | 0 | return; |
2308 | 0 | } |
2309 | 0 | |
2310 | 0 | JS::Rooted<JSObject*> aObj(aCx, aObjArg); |
2311 | 0 | const DOMJSClass* domClass = GetDOMClass(aObj); |
2312 | 0 |
|
2313 | 0 | // DOM things are always parented to globals. |
2314 | 0 | JS::Rooted<JSObject*> oldParent(aCx, JS::GetNonCCWObjectGlobal(aObj)); |
2315 | 0 | MOZ_ASSERT(JS_IsGlobalObject(oldParent)); |
2316 | 0 |
|
2317 | 0 | JS::Rooted<JSObject*> newParent(aCx, |
2318 | 0 | domClass->mGetAssociatedGlobal(aCx, aObj)); |
2319 | 0 | MOZ_ASSERT(JS_IsGlobalObject(newParent)); |
2320 | 0 |
|
2321 | 0 | JSAutoRealm oldAr(aCx, oldParent); |
2322 | 0 |
|
2323 | 0 | JS::Compartment* oldCompartment = js::GetObjectCompartment(oldParent); |
2324 | 0 | JS::Compartment* newCompartment = js::GetObjectCompartment(newParent); |
2325 | 0 | if (oldCompartment == newCompartment) { |
2326 | 0 | MOZ_ASSERT(oldParent == newParent); |
2327 | 0 | return; |
2328 | 0 | } |
2329 | 0 |
|
2330 | 0 | nsISupports* native = UnwrapDOMObjectToISupports(aObj); |
2331 | 0 | if (!native) { |
2332 | 0 | return; |
2333 | 0 | } |
2334 | 0 | |
2335 | 0 | bool isProxy = js::IsProxy(aObj); |
2336 | 0 | JS::Rooted<JSObject*> expandoObject(aCx); |
2337 | 0 | if (isProxy) { |
2338 | 0 | expandoObject = DOMProxyHandler::GetAndClearExpandoObject(aObj); |
2339 | 0 | } |
2340 | 0 |
|
2341 | 0 | JSAutoRealm newAr(aCx, newParent); |
2342 | 0 |
|
2343 | 0 | // First we clone the reflector. We get a copy of its properties and clone its |
2344 | 0 | // expando chain. |
2345 | 0 |
|
2346 | 0 | JS::Handle<JSObject*> proto = (domClass->mGetProto)(aCx); |
2347 | 0 | if (!proto) { |
2348 | 0 | aError.StealExceptionFromJSContext(aCx); |
2349 | 0 | return; |
2350 | 0 | } |
2351 | 0 | |
2352 | 0 | JS::Rooted<JSObject*> newobj(aCx, JS_CloneObject(aCx, aObj, proto)); |
2353 | 0 | if (!newobj) { |
2354 | 0 | aError.StealExceptionFromJSContext(aCx); |
2355 | 0 | return; |
2356 | 0 | } |
2357 | 0 | |
2358 | 0 | JS::Rooted<JSObject*> propertyHolder(aCx); |
2359 | 0 | JS::Rooted<JSObject*> copyFrom(aCx, isProxy ? expandoObject : aObj); |
2360 | 0 | if (copyFrom) { |
2361 | 0 | propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, nullptr); |
2362 | 0 | if (!propertyHolder) { |
2363 | 0 | aError.StealExceptionFromJSContext(aCx); |
2364 | 0 | return; |
2365 | 0 | } |
2366 | 0 | |
2367 | 0 | if (!JS_CopyPropertiesFrom(aCx, propertyHolder, copyFrom)) { |
2368 | 0 | aError.StealExceptionFromJSContext(aCx); |
2369 | 0 | return; |
2370 | 0 | } |
2371 | 0 | } else { |
2372 | 0 | propertyHolder = nullptr; |
2373 | 0 | } |
2374 | 0 |
|
2375 | 0 | // We've set up |newobj|, so we make it own the native by setting its reserved |
2376 | 0 | // slot and nulling out the reserved slot of |obj|. |
2377 | 0 | // |
2378 | 0 | // NB: It's important to do this _after_ copying the properties to |
2379 | 0 | // propertyHolder. Otherwise, an object with |foo.x === foo| will |
2380 | 0 | // crash when JS_CopyPropertiesFrom tries to call wrap() on foo.x. |
2381 | 0 | js::SetReservedSlot(newobj, DOM_OBJECT_SLOT, |
2382 | 0 | js::GetReservedSlot(aObj, DOM_OBJECT_SLOT)); |
2383 | 0 | js::SetReservedSlot(aObj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr)); |
2384 | 0 |
|
2385 | 0 | aObj = xpc::TransplantObjectRetainingXrayExpandos(aCx, aObj, newobj); |
2386 | 0 | if (!aObj) { |
2387 | 0 | MOZ_CRASH(); |
2388 | 0 | } |
2389 | 0 |
|
2390 | 0 | nsWrapperCache* cache = nullptr; |
2391 | 0 | CallQueryInterface(native, &cache); |
2392 | 0 | bool preserving = cache->PreservingWrapper(); |
2393 | 0 | cache->SetPreservingWrapper(false); |
2394 | 0 | cache->SetWrapper(aObj); |
2395 | 0 | cache->SetPreservingWrapper(preserving); |
2396 | 0 |
|
2397 | 0 | if (propertyHolder) { |
2398 | 0 | JS::Rooted<JSObject*> copyTo(aCx); |
2399 | 0 | if (isProxy) { |
2400 | 0 | copyTo = DOMProxyHandler::EnsureExpandoObject(aCx, aObj); |
2401 | 0 | } else { |
2402 | 0 | copyTo = aObj; |
2403 | 0 | } |
2404 | 0 |
|
2405 | 0 | if (!copyTo || !JS_CopyPropertiesFrom(aCx, copyTo, propertyHolder)) { |
2406 | 0 | MOZ_CRASH(); |
2407 | 0 | } |
2408 | 0 | } |
2409 | 0 |
|
2410 | 0 | JS::Rooted<JSObject*> maybeObjLC(aCx, aObj); |
2411 | 0 | nsObjectLoadingContent* htmlobject; |
2412 | 0 | nsresult rv = UNWRAP_OBJECT(HTMLObjectElement, &maybeObjLC, htmlobject); |
2413 | 0 | if (NS_FAILED(rv)) { |
2414 | 0 | rv = UNWRAP_OBJECT(HTMLEmbedElement, &maybeObjLC, htmlobject); |
2415 | 0 | if (NS_FAILED(rv)) { |
2416 | 0 | htmlobject = nullptr; |
2417 | 0 | } |
2418 | 0 | } |
2419 | 0 | if (htmlobject) { |
2420 | 0 | htmlobject->SetupProtoChain(aCx, aObj); |
2421 | 0 | } |
2422 | 0 | } |
2423 | | |
2424 | | GlobalObject::GlobalObject(JSContext* aCx, JSObject* aObject) |
2425 | | : mGlobalJSObject(aCx), |
2426 | | mCx(aCx), |
2427 | | mGlobalObject(nullptr) |
2428 | 14 | { |
2429 | 14 | MOZ_ASSERT(mCx); |
2430 | 14 | JS::Rooted<JSObject*> obj(aCx, aObject); |
2431 | 14 | if (js::IsWrapper(obj)) { |
2432 | 0 | obj = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false); |
2433 | 0 | if (!obj) { |
2434 | 0 | // We should never end up here on a worker thread, since there shouldn't |
2435 | 0 | // be any security wrappers to worry about. |
2436 | 0 | if (!MOZ_LIKELY(NS_IsMainThread())) { |
2437 | 0 | MOZ_CRASH(); |
2438 | 0 | } |
2439 | 0 |
|
2440 | 0 | Throw(aCx, NS_ERROR_XPC_SECURITY_MANAGER_VETO); |
2441 | 0 | return; |
2442 | 14 | } |
2443 | 0 | } |
2444 | 14 | |
2445 | 14 | mGlobalJSObject = JS::GetNonCCWObjectGlobal(obj); |
2446 | 14 | } |
2447 | | |
2448 | | nsISupports* |
2449 | | GlobalObject::GetAsSupports() const |
2450 | 0 | { |
2451 | 0 | if (mGlobalObject) { |
2452 | 0 | return mGlobalObject; |
2453 | 0 | } |
2454 | 0 | |
2455 | 0 | MOZ_ASSERT(!js::IsWrapper(mGlobalJSObject)); |
2456 | 0 |
|
2457 | 0 | // Most of our globals are DOM objects. Try that first. Note that this |
2458 | 0 | // assumes that either the first nsISupports in the object is the canonical |
2459 | 0 | // one or that we don't care about the canonical nsISupports here. |
2460 | 0 | mGlobalObject = UnwrapDOMObjectToISupports(mGlobalJSObject); |
2461 | 0 | if (mGlobalObject) { |
2462 | 0 | return mGlobalObject; |
2463 | 0 | } |
2464 | 0 | |
2465 | 0 | MOZ_ASSERT(NS_IsMainThread(), "All our worker globals are DOM objects"); |
2466 | 0 |
|
2467 | 0 | // Remove everything below here once all our global objects are using new |
2468 | 0 | // bindings. If that ever happens; it would need to include Sandbox and |
2469 | 0 | // BackstagePass. |
2470 | 0 |
|
2471 | 0 | // See whether mGlobalJSObject is an XPCWrappedNative. This will redo the |
2472 | 0 | // IsWrapper bit above and the UnwrapDOMObjectToISupports in the case when |
2473 | 0 | // we're not actually an XPCWrappedNative, but this should be a rare-ish case |
2474 | 0 | // anyway. |
2475 | 0 | nsCOMPtr<nsISupports> supp = xpc::UnwrapReflectorToISupports(mGlobalJSObject); |
2476 | 0 | if (supp) { |
2477 | 0 | // See documentation for mGlobalJSObject for why this assignment is OK. |
2478 | 0 | mGlobalObject = supp; |
2479 | 0 | return mGlobalObject; |
2480 | 0 | } |
2481 | 0 | |
2482 | 0 | // And now a final hack. Sandbox is not a reflector, but it does have an |
2483 | 0 | // nsIGlobalObject hanging out in its private slot. Handle that case here, |
2484 | 0 | // (though again, this will do the useless UnwrapDOMObjectToISupports if we |
2485 | 0 | // got here for something that is somehow not a DOM object, not an |
2486 | 0 | // XPCWrappedNative _and_ not a Sandbox). |
2487 | 0 | if (XPCConvert::GetISupportsFromJSObject(mGlobalJSObject, &mGlobalObject)) { |
2488 | 0 | return mGlobalObject; |
2489 | 0 | } |
2490 | 0 | |
2491 | 0 | MOZ_ASSERT(!mGlobalObject); |
2492 | 0 |
|
2493 | 0 | Throw(mCx, NS_ERROR_XPC_BAD_CONVERT_JS); |
2494 | 0 | return nullptr; |
2495 | 0 | } |
2496 | | |
2497 | | nsIPrincipal* |
2498 | | GlobalObject::GetSubjectPrincipal() const |
2499 | 0 | { |
2500 | 0 | if (!NS_IsMainThread()) { |
2501 | 0 | return nullptr; |
2502 | 0 | } |
2503 | 0 | |
2504 | 0 | JS::Realm* realm = js::GetContextRealm(mCx); |
2505 | 0 | MOZ_ASSERT(realm); |
2506 | 0 | JSPrincipals* principals = JS::GetRealmPrincipals(realm); |
2507 | 0 | return nsJSPrincipals::get(principals); |
2508 | 0 | } |
2509 | | |
2510 | | CallerType |
2511 | | GlobalObject::CallerType() const |
2512 | 0 | { |
2513 | 0 | return nsContentUtils::ThreadsafeIsSystemCaller(mCx) ? |
2514 | 0 | dom::CallerType::System : dom::CallerType::NonSystem; |
2515 | 0 | } |
2516 | | |
2517 | | static bool |
2518 | | CallOrdinaryHasInstance(JSContext* cx, JS::CallArgs& args) |
2519 | 0 | { |
2520 | 0 | JS::Rooted<JSObject*> thisObj(cx, &args.thisv().toObject()); |
2521 | 0 | bool isInstance; |
2522 | 0 | if (!JS::OrdinaryHasInstance(cx, thisObj, args.get(0), &isInstance)) { |
2523 | 0 | return false; |
2524 | 0 | } |
2525 | 0 | args.rval().setBoolean(isInstance); |
2526 | 0 | return true; |
2527 | 0 | } |
2528 | | |
2529 | | bool |
2530 | | InterfaceHasInstance(JSContext* cx, unsigned argc, JS::Value* vp) |
2531 | 0 | { |
2532 | 0 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
2533 | 0 | // If the thing we were passed is not an object, return false like |
2534 | 0 | // OrdinaryHasInstance does. |
2535 | 0 | if (!args.get(0).isObject()) { |
2536 | 0 | args.rval().setBoolean(false); |
2537 | 0 | return true; |
2538 | 0 | } |
2539 | 0 | |
2540 | 0 | // If "this" is not an object, likewise return false (again, like |
2541 | 0 | // OrdinaryHasInstance). |
2542 | 0 | if (!args.thisv().isObject()) { |
2543 | 0 | args.rval().setBoolean(false); |
2544 | 0 | return true; |
2545 | 0 | } |
2546 | 0 | |
2547 | 0 | // If "this" doesn't have a DOMIfaceAndProtoJSClass, it's not a DOM |
2548 | 0 | // constructor, so just fall back to OrdinaryHasInstance. But note that we |
2549 | 0 | // should CheckedUnwrap here, because otherwise we won't get the right |
2550 | 0 | // answers. |
2551 | 0 | JS::Rooted<JSObject*> thisObj(cx, js::CheckedUnwrap(&args.thisv().toObject())); |
2552 | 0 | if (!thisObj) { |
2553 | 0 | // Just fall back on the normal thing, in case it still happens to work. |
2554 | 0 | return CallOrdinaryHasInstance(cx, args); |
2555 | 0 | } |
2556 | 0 | |
2557 | 0 | const js::Class* thisClass = js::GetObjectClass(thisObj); |
2558 | 0 |
|
2559 | 0 | if (!IsDOMIfaceAndProtoClass(thisClass)) { |
2560 | 0 | return CallOrdinaryHasInstance(cx, args); |
2561 | 0 | } |
2562 | 0 | |
2563 | 0 | const DOMIfaceAndProtoJSClass* clasp = |
2564 | 0 | DOMIfaceAndProtoJSClass::FromJSClass(thisClass); |
2565 | 0 |
|
2566 | 0 | // If "this" isn't a DOM constructor or is a constructor for an interface |
2567 | 0 | // without a prototype, just fall back to OrdinaryHasInstance. |
2568 | 0 | if (clasp->mType != eInterface || |
2569 | 0 | clasp->mPrototypeID == prototypes::id::_ID_Count) { |
2570 | 0 | return CallOrdinaryHasInstance(cx, args); |
2571 | 0 | } |
2572 | 0 | |
2573 | 0 | JS::Rooted<JSObject*> instance(cx, &args[0].toObject()); |
2574 | 0 | const DOMJSClass* domClass = |
2575 | 0 | GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false)); |
2576 | 0 |
|
2577 | 0 | if (domClass && |
2578 | 0 | domClass->mInterfaceChain[clasp->mDepth] == clasp->mPrototypeID) { |
2579 | 0 | args.rval().setBoolean(true); |
2580 | 0 | return true; |
2581 | 0 | } |
2582 | 0 | |
2583 | 0 | if (jsipc::IsWrappedCPOW(instance)) { |
2584 | 0 | bool boolp = false; |
2585 | 0 | if (!jsipc::DOMInstanceOf(cx, js::UncheckedUnwrap(instance), clasp->mPrototypeID, |
2586 | 0 | clasp->mDepth, &boolp)) { |
2587 | 0 | return false; |
2588 | 0 | } |
2589 | 0 | args.rval().setBoolean(boolp); |
2590 | 0 | return true; |
2591 | 0 | } |
2592 | 0 | |
2593 | 0 | return CallOrdinaryHasInstance(cx, args); |
2594 | 0 | } |
2595 | | |
2596 | | bool |
2597 | | InterfaceHasInstance(JSContext* cx, int prototypeID, int depth, |
2598 | | JS::Handle<JSObject*> instance, |
2599 | | bool* bp) |
2600 | 0 | { |
2601 | 0 | const DOMJSClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance)); |
2602 | 0 |
|
2603 | 0 | MOZ_ASSERT(!domClass || prototypeID != prototypes::id::_ID_Count, |
2604 | 0 | "Why do we have a hasInstance hook if we don't have a prototype " |
2605 | 0 | "ID?"); |
2606 | 0 |
|
2607 | 0 | *bp = (domClass && domClass->mInterfaceChain[depth] == prototypeID); |
2608 | 0 | return true; |
2609 | 0 | } |
2610 | | |
2611 | | bool |
2612 | | InterfaceIsInstance(JSContext* cx, unsigned argc, JS::Value* vp) |
2613 | 0 | { |
2614 | 0 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
2615 | 0 |
|
2616 | 0 | // If the thing we were passed is not an object, return false. |
2617 | 0 | if (!args.get(0).isObject()) { |
2618 | 0 | args.rval().setBoolean(false); |
2619 | 0 | return true; |
2620 | 0 | } |
2621 | 0 | |
2622 | 0 | // If "this" isn't a DOM constructor or is a constructor for an interface |
2623 | 0 | // without a prototype, return false. |
2624 | 0 | if (!args.thisv().isObject()) { |
2625 | 0 | args.rval().setBoolean(false); |
2626 | 0 | return true; |
2627 | 0 | } |
2628 | 0 | |
2629 | 0 | JS::Rooted<JSObject*> thisObj(cx, js::CheckedUnwrap(&args.thisv().toObject())); |
2630 | 0 | if (!thisObj) { |
2631 | 0 | args.rval().setBoolean(false); |
2632 | 0 | return true; |
2633 | 0 | } |
2634 | 0 | |
2635 | 0 | const js::Class* thisClass = js::GetObjectClass(thisObj); |
2636 | 0 | if (!IsDOMIfaceAndProtoClass(thisClass)) { |
2637 | 0 | args.rval().setBoolean(false); |
2638 | 0 | return true; |
2639 | 0 | } |
2640 | 0 | |
2641 | 0 | const DOMIfaceAndProtoJSClass* clasp = |
2642 | 0 | DOMIfaceAndProtoJSClass::FromJSClass(thisClass); |
2643 | 0 |
|
2644 | 0 | if (clasp->mType != eInterface || |
2645 | 0 | clasp->mPrototypeID == prototypes::id::_ID_Count) { |
2646 | 0 | args.rval().setBoolean(false); |
2647 | 0 | return true; |
2648 | 0 | } |
2649 | 0 | |
2650 | 0 | JS::Rooted<JSObject*> instance(cx, &args[0].toObject()); |
2651 | 0 | const DOMJSClass* domClass = |
2652 | 0 | GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false)); |
2653 | 0 |
|
2654 | 0 | bool isInstance = |
2655 | 0 | domClass && |
2656 | 0 | domClass->mInterfaceChain[clasp->mDepth] == clasp->mPrototypeID; |
2657 | 0 |
|
2658 | 0 | args.rval().setBoolean(isInstance); |
2659 | 0 | return true; |
2660 | 0 | } |
2661 | | |
2662 | | bool |
2663 | | ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj) |
2664 | 0 | { |
2665 | 0 | JS::Rooted<JSObject*> rootedObj(cx, obj); |
2666 | 0 | GlobalObject global(cx, rootedObj); |
2667 | 0 | if (global.Failed()) { |
2668 | 0 | return false; |
2669 | 0 | } |
2670 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global.GetAsSupports()); |
2671 | 0 | if (window && window->GetDoc()) { |
2672 | 0 | window->GetDoc()->WarnOnceAbout(nsIDocument::eLenientThis); |
2673 | 0 | } |
2674 | 0 | return true; |
2675 | 0 | } |
2676 | | |
2677 | | bool |
2678 | | GetContentGlobalForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj, |
2679 | | nsIGlobalObject** globalObj) |
2680 | 0 | { |
2681 | 0 | // Be very careful to not get tricked here. |
2682 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2683 | 0 | if (!xpc::AccessCheck::isChrome(js::GetObjectCompartment(obj))) { |
2684 | 0 | MOZ_CRASH("Should have a chrome object here"); |
2685 | 0 | } |
2686 | 0 |
|
2687 | 0 | // Look up the content-side object. |
2688 | 0 | JS::Rooted<JS::Value> domImplVal(cx); |
2689 | 0 | if (!JS_GetProperty(cx, obj, "__DOM_IMPL__", &domImplVal)) { |
2690 | 0 | return false; |
2691 | 0 | } |
2692 | 0 | |
2693 | 0 | if (!domImplVal.isObject()) { |
2694 | 0 | ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Value"); |
2695 | 0 | return false; |
2696 | 0 | } |
2697 | 0 | |
2698 | 0 | // Go ahead and get the global from it. GlobalObject will handle |
2699 | 0 | // doing unwrapping as needed. |
2700 | 0 | GlobalObject global(cx, &domImplVal.toObject()); |
2701 | 0 | if (global.Failed()) { |
2702 | 0 | return false; |
2703 | 0 | } |
2704 | 0 | |
2705 | 0 | DebugOnly<nsresult> rv = CallQueryInterface(global.GetAsSupports(), globalObj); |
2706 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
2707 | 0 | MOZ_ASSERT(*globalObj); |
2708 | 0 | return true; |
2709 | 0 | } |
2710 | | |
2711 | | already_AddRefed<nsIGlobalObject> |
2712 | | ConstructJSImplementation(const char* aContractId, |
2713 | | const GlobalObject& aGlobal, |
2714 | | JS::MutableHandle<JSObject*> aObject, |
2715 | | ErrorResult& aRv) |
2716 | 0 | { |
2717 | 0 | // Get the global object to use as a parent and for initialization. |
2718 | 0 | nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); |
2719 | 0 | if (!global) { |
2720 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
2721 | 0 | return nullptr; |
2722 | 0 | } |
2723 | 0 | |
2724 | 0 | ConstructJSImplementation(aContractId, global, aObject, aRv); |
2725 | 0 |
|
2726 | 0 | if (aRv.Failed()) { |
2727 | 0 | return nullptr; |
2728 | 0 | } |
2729 | 0 | return global.forget(); |
2730 | 0 | } |
2731 | | |
2732 | | void |
2733 | | ConstructJSImplementation(const char* aContractId, |
2734 | | nsIGlobalObject* aGlobal, |
2735 | | JS::MutableHandle<JSObject*> aObject, |
2736 | | ErrorResult& aRv) |
2737 | 0 | { |
2738 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2739 | 0 |
|
2740 | 0 | // Make sure to divorce ourselves from the calling JS while creating and |
2741 | 0 | // initializing the object, so exceptions from that will get reported |
2742 | 0 | // properly, since those are never exceptions that a spec wants to be thrown. |
2743 | 0 | { |
2744 | 0 | AutoNoJSAPI nojsapi; |
2745 | 0 |
|
2746 | 0 | // Get the XPCOM component containing the JS implementation. |
2747 | 0 | nsresult rv; |
2748 | 0 | nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId, &rv); |
2749 | 0 | if (!implISupports) { |
2750 | 0 | nsPrintfCString msg("Failed to get JS implementation for contract \"%s\"", |
2751 | 0 | aContractId); |
2752 | 0 | NS_WARNING(msg.get()); |
2753 | 0 | aRv.Throw(rv); |
2754 | 0 | return; |
2755 | 0 | } |
2756 | 0 | // Initialize the object, if it implements nsIDOMGlobalPropertyInitializer |
2757 | 0 | // and our global is a window. |
2758 | 0 | nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi = |
2759 | 0 | do_QueryInterface(implISupports); |
2760 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal); |
2761 | 0 | if (gpi) { |
2762 | 0 | JS::Rooted<JS::Value> initReturn(RootingCx()); |
2763 | 0 | rv = gpi->Init(window, &initReturn); |
2764 | 0 | if (NS_FAILED(rv)) { |
2765 | 0 | aRv.Throw(rv); |
2766 | 0 | return; |
2767 | 0 | } |
2768 | 0 | // With JS-implemented WebIDL, the return value of init() is not used to determine |
2769 | 0 | // if init() failed, so init() should only return undefined. Any kind of permission |
2770 | 0 | // or pref checking must happen by adding an attribute to the WebIDL interface. |
2771 | 0 | if (!initReturn.isUndefined()) { |
2772 | 0 | MOZ_ASSERT(false, "The init() method for JS-implemented WebIDL should not return anything"); |
2773 | 0 | MOZ_CRASH(); |
2774 | 0 | } |
2775 | 0 | } |
2776 | 0 | // Extract the JS implementation from the XPCOM object. |
2777 | 0 | nsCOMPtr<nsIXPConnectWrappedJS> implWrapped = |
2778 | 0 | do_QueryInterface(implISupports, &rv); |
2779 | 0 | MOZ_ASSERT(implWrapped, "Failed to get wrapped JS from XPCOM component."); |
2780 | 0 | if (!implWrapped) { |
2781 | 0 | aRv.Throw(rv); |
2782 | 0 | return; |
2783 | 0 | } |
2784 | 0 | aObject.set(implWrapped->GetJSObject()); |
2785 | 0 | if (!aObject) { |
2786 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
2787 | 0 | } |
2788 | 0 | } |
2789 | 0 | } |
2790 | | |
2791 | | bool |
2792 | | NonVoidByteStringToJsval(JSContext *cx, const nsACString &str, |
2793 | | JS::MutableHandle<JS::Value> rval) |
2794 | 0 | { |
2795 | 0 | // ByteStrings are not UTF-8 encoded. |
2796 | 0 | JSString* jsStr = JS_NewStringCopyN(cx, str.Data(), str.Length()); |
2797 | 0 |
|
2798 | 0 | if (!jsStr) |
2799 | 0 | return false; |
2800 | 0 | |
2801 | 0 | rval.setString(jsStr); |
2802 | 0 | return true; |
2803 | 0 | } |
2804 | | |
2805 | | void |
2806 | | NormalizeUSVString(nsAString& aString) |
2807 | 0 | { |
2808 | 0 | EnsureUTF16Validity(aString); |
2809 | 0 | } |
2810 | | |
2811 | | void |
2812 | | NormalizeUSVString(binding_detail::FakeString& aString) |
2813 | 0 | { |
2814 | 0 | EnsureUTF16ValiditySpan(aString); |
2815 | 0 | } |
2816 | | |
2817 | | bool |
2818 | | ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v, |
2819 | | bool nullable, nsACString& result) |
2820 | 0 | { |
2821 | 0 | JS::Rooted<JSString*> s(cx); |
2822 | 0 | if (v.isString()) { |
2823 | 0 | s = v.toString(); |
2824 | 0 | } else { |
2825 | 0 |
|
2826 | 0 | if (nullable && v.isNullOrUndefined()) { |
2827 | 0 | result.SetIsVoid(true); |
2828 | 0 | return true; |
2829 | 0 | } |
2830 | 0 | |
2831 | 0 | s = JS::ToString(cx, v); |
2832 | 0 | if (!s) { |
2833 | 0 | return false; |
2834 | 0 | } |
2835 | 0 | } |
2836 | 0 | |
2837 | 0 | // Conversion from Javascript string to ByteString is only valid if all |
2838 | 0 | // characters < 256. This is always the case for Latin1 strings. |
2839 | 0 | size_t length; |
2840 | 0 | if (!js::StringHasLatin1Chars(s)) { |
2841 | 0 | // ThrowErrorMessage can GC, so we first scan the string for bad chars |
2842 | 0 | // and report the error outside the AutoCheckCannotGC scope. |
2843 | 0 | bool foundBadChar = false; |
2844 | 0 | size_t badCharIndex; |
2845 | 0 | char16_t badChar; |
2846 | 0 | { |
2847 | 0 | JS::AutoCheckCannotGC nogc; |
2848 | 0 | const char16_t* chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, s, &length); |
2849 | 0 | if (!chars) { |
2850 | 0 | return false; |
2851 | 0 | } |
2852 | 0 | |
2853 | 0 | for (size_t i = 0; i < length; i++) { |
2854 | 0 | if (chars[i] > 255) { |
2855 | 0 | badCharIndex = i; |
2856 | 0 | badChar = chars[i]; |
2857 | 0 | foundBadChar = true; |
2858 | 0 | break; |
2859 | 0 | } |
2860 | 0 | } |
2861 | 0 | } |
2862 | 0 |
|
2863 | 0 | if (foundBadChar) { |
2864 | 0 | MOZ_ASSERT(badCharIndex < length); |
2865 | 0 | MOZ_ASSERT(badChar > 255); |
2866 | 0 | // The largest unsigned 64 bit number (18,446,744,073,709,551,615) has |
2867 | 0 | // 20 digits, plus one more for the null terminator. |
2868 | 0 | char index[21]; |
2869 | 0 | static_assert(sizeof(size_t) <= 8, "index array too small"); |
2870 | 0 | SprintfLiteral(index, "%zu", badCharIndex); |
2871 | 0 | // A char16_t is 16 bits long. The biggest unsigned 16 bit |
2872 | 0 | // number (65,535) has 5 digits, plus one more for the null |
2873 | 0 | // terminator. |
2874 | 0 | char badCharArray[6]; |
2875 | 0 | static_assert(sizeof(char16_t) <= 2, "badCharArray too small"); |
2876 | 0 | SprintfLiteral(badCharArray, "%d", badChar); |
2877 | 0 | ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badCharArray); |
2878 | 0 | return false; |
2879 | 0 | } |
2880 | 0 | } else { |
2881 | 0 | length = JS::GetStringLength(s); |
2882 | 0 | } |
2883 | 0 |
|
2884 | 0 | static_assert(js::MaxStringLength < UINT32_MAX, |
2885 | 0 | "length+1 shouldn't overflow"); |
2886 | 0 |
|
2887 | 0 | if (!result.SetLength(length, fallible)) { |
2888 | 0 | return false; |
2889 | 0 | } |
2890 | 0 | |
2891 | 0 | if (!JS_EncodeStringToBuffer(cx, s, result.BeginWriting(), length)) { |
2892 | 0 | return false; |
2893 | 0 | } |
2894 | 0 | |
2895 | 0 | return true; |
2896 | 0 | } |
2897 | | |
2898 | | void |
2899 | | FinalizeGlobal(JSFreeOp* aFreeOp, JSObject* aObj) |
2900 | 0 | { |
2901 | 0 | MOZ_ASSERT(js::GetObjectClass(aObj)->flags & JSCLASS_DOM_GLOBAL); |
2902 | 0 | mozilla::dom::DestroyProtoAndIfaceCache(aObj); |
2903 | 0 | } |
2904 | | |
2905 | | bool |
2906 | | ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj, |
2907 | | JS::Handle<jsid> aId, bool* aResolvedp) |
2908 | 50 | { |
2909 | 50 | MOZ_ASSERT(JS_IsGlobalObject(aObj), |
2910 | 50 | "Should have a global here, since we plan to resolve standard " |
2911 | 50 | "classes!"); |
2912 | 50 | |
2913 | 50 | return JS_ResolveStandardClass(aCx, aObj, aId, aResolvedp); |
2914 | 50 | } |
2915 | | |
2916 | | bool |
2917 | | MayResolveGlobal(const JSAtomState& aNames, jsid aId, JSObject* aMaybeObj) |
2918 | 0 | { |
2919 | 0 | return JS_MayResolveStandardClass(aNames, aId, aMaybeObj); |
2920 | 0 | } |
2921 | | |
2922 | | bool |
2923 | | EnumerateGlobal(JSContext* aCx, JS::HandleObject aObj, |
2924 | | JS::AutoIdVector& aProperties, bool aEnumerableOnly) |
2925 | 0 | { |
2926 | 0 | MOZ_ASSERT(JS_IsGlobalObject(aObj), |
2927 | 0 | "Should have a global here, since we plan to enumerate standard " |
2928 | 0 | "classes!"); |
2929 | 0 |
|
2930 | 0 | return JS_NewEnumerateStandardClasses(aCx, aObj, aProperties, |
2931 | 0 | aEnumerableOnly); |
2932 | 0 | } |
2933 | | |
2934 | | bool |
2935 | | IsNonExposedGlobal(JSContext* aCx, JSObject* aGlobal, |
2936 | | uint32_t aNonExposedGlobals) |
2937 | 4 | { |
2938 | 4 | MOZ_ASSERT(aNonExposedGlobals, "Why did we get called?"); |
2939 | 4 | MOZ_ASSERT((aNonExposedGlobals & |
2940 | 4 | ~(GlobalNames::Window | |
2941 | 4 | GlobalNames::BackstagePass | |
2942 | 4 | GlobalNames::DedicatedWorkerGlobalScope | |
2943 | 4 | GlobalNames::SharedWorkerGlobalScope | |
2944 | 4 | GlobalNames::ServiceWorkerGlobalScope | |
2945 | 4 | GlobalNames::WorkerDebuggerGlobalScope | |
2946 | 4 | GlobalNames::WorkletGlobalScope | |
2947 | 4 | GlobalNames::AudioWorkletGlobalScope)) == 0, |
2948 | 4 | "Unknown non-exposed global type"); |
2949 | 4 | |
2950 | 4 | const char* name = js::GetObjectClass(aGlobal)->name; |
2951 | 4 | |
2952 | 4 | if ((aNonExposedGlobals & GlobalNames::Window) && |
2953 | 4 | !strcmp(name, "Window")) { |
2954 | 0 | return true; |
2955 | 0 | } |
2956 | 4 | |
2957 | 4 | if ((aNonExposedGlobals & GlobalNames::BackstagePass) && |
2958 | 4 | !strcmp(name, "BackstagePass")) { |
2959 | 0 | return true; |
2960 | 0 | } |
2961 | 4 | |
2962 | 4 | if ((aNonExposedGlobals & GlobalNames::DedicatedWorkerGlobalScope) && |
2963 | 4 | !strcmp(name, "DedicatedWorkerGlobalScope")) { |
2964 | 0 | return true; |
2965 | 0 | } |
2966 | 4 | |
2967 | 4 | if ((aNonExposedGlobals & GlobalNames::SharedWorkerGlobalScope) && |
2968 | 4 | !strcmp(name, "SharedWorkerGlobalScope")) { |
2969 | 0 | return true; |
2970 | 0 | } |
2971 | 4 | |
2972 | 4 | if ((aNonExposedGlobals & GlobalNames::ServiceWorkerGlobalScope) && |
2973 | 4 | !strcmp(name, "ServiceWorkerGlobalScope")) { |
2974 | 0 | return true; |
2975 | 0 | } |
2976 | 4 | |
2977 | 4 | if ((aNonExposedGlobals & GlobalNames::WorkerDebuggerGlobalScope) && |
2978 | 4 | !strcmp(name, "WorkerDebuggerGlobalScopex")) { |
2979 | 0 | return true; |
2980 | 0 | } |
2981 | 4 | |
2982 | 4 | if ((aNonExposedGlobals & GlobalNames::WorkletGlobalScope) && |
2983 | 4 | !strcmp(name, "WorkletGlobalScope")) { |
2984 | 0 | return true; |
2985 | 0 | } |
2986 | 4 | |
2987 | 4 | if ((aNonExposedGlobals & GlobalNames::AudioWorkletGlobalScope) && |
2988 | 4 | !strcmp(name, "AudioWorkletGlobalScope")) { |
2989 | 0 | return true; |
2990 | 0 | } |
2991 | 4 | |
2992 | 4 | return false; |
2993 | 4 | } |
2994 | | |
2995 | | namespace binding_detail { |
2996 | | |
2997 | | /** |
2998 | | * A ThisPolicy struct needs to provide the following methods: |
2999 | | * |
3000 | | * HasValidThisValue: Takes a CallArgs and returns a boolean indicating whether |
3001 | | * the thisv() is valid in the sense of being the right type |
3002 | | * of Value. It does not check whether it's the right sort |
3003 | | * of object if the Value is a JSObject*. |
3004 | | * |
3005 | | * ExtractThisObject: Takes a CallArgs for which HasValidThisValue was true and |
3006 | | * returns the JSObject* to use for getting |this|. |
3007 | | * |
3008 | | * MaybeUnwrapThisObject: If our |this| is a JSObject* that this policy wants to |
3009 | | * allow unchecked access to for this |
3010 | | * getter/setter/method, unwrap it. Otherwise just |
3011 | | * return the given object. |
3012 | | * |
3013 | | * HandleInvalidThis: If the |this| is not valid (wrong type of value, wrong |
3014 | | * object, etc), decide what to do about it. Returns a |
3015 | | * boolean to return from the JSNative (false for failure, |
3016 | | * true for succcess). |
3017 | | */ |
3018 | | struct NormalThisPolicy |
3019 | | { |
3020 | | // This needs to be inlined because it's called on no-exceptions fast-paths. |
3021 | | static MOZ_ALWAYS_INLINE bool HasValidThisValue(const JS::CallArgs& aArgs) |
3022 | 0 | { |
3023 | 0 | // Per WebIDL spec, all getters/setters/methods allow null/undefined "this" |
3024 | 0 | // and coerce it to the global. Then the "is this the right interface?" |
3025 | 0 | // check fails if the interface involved is not one that the global |
3026 | 0 | // implements. |
3027 | 0 | // |
3028 | 0 | // As an optimization, we skip doing the null/undefined stuff if we know our |
3029 | 0 | // interface is not implemented by the global. |
3030 | 0 | return aArgs.thisv().isObject(); |
3031 | 0 | } |
3032 | | |
3033 | | static MOZ_ALWAYS_INLINE JSObject* ExtractThisObject(const JS::CallArgs& aArgs) |
3034 | 0 | { |
3035 | 0 | return &aArgs.thisv().toObject(); |
3036 | 0 | } |
3037 | | |
3038 | | static MOZ_ALWAYS_INLINE JSObject* MaybeUnwrapThisObject(JSObject* aObj) |
3039 | 0 | { |
3040 | 0 | return aObj; |
3041 | 0 | } |
3042 | | |
3043 | | static bool HandleInvalidThis(JSContext* aCx, JS::CallArgs& aArgs, |
3044 | | bool aSecurityError, |
3045 | | prototypes::ID aProtoId) |
3046 | 0 | { |
3047 | 0 | return ThrowInvalidThis(aCx, aArgs, aSecurityError, aProtoId); |
3048 | 0 | } |
3049 | | }; |
3050 | | |
3051 | | struct MaybeGlobalThisPolicy : public NormalThisPolicy |
3052 | | { |
3053 | | static MOZ_ALWAYS_INLINE bool HasValidThisValue(const JS::CallArgs& aArgs) |
3054 | 0 | { |
3055 | 0 | // Here we have to allow null/undefined. |
3056 | 0 | return aArgs.thisv().isObject() || aArgs.thisv().isNullOrUndefined(); |
3057 | 0 | } |
3058 | | |
3059 | | static MOZ_ALWAYS_INLINE JSObject* ExtractThisObject(const JS::CallArgs& aArgs) |
3060 | 0 | { |
3061 | 0 | return aArgs.thisv().isObject() ? |
3062 | 0 | &aArgs.thisv().toObject() : |
3063 | 0 | JS::GetNonCCWObjectGlobal(&aArgs.callee()); |
3064 | 0 | } |
3065 | | |
3066 | | // We want the MaybeUnwrapThisObject of NormalThisPolicy. |
3067 | | |
3068 | | // We want the HandleInvalidThis of NormalThisPolicy. |
3069 | | }; |
3070 | | |
3071 | | // There are some LenientThis things on globals, so we inherit from |
3072 | | // MaybeGlobalThisPolicy. |
3073 | | struct LenientThisPolicy : public MaybeGlobalThisPolicy |
3074 | | { |
3075 | | // We want the HasValidThisValue of MaybeGlobalThisPolicy. |
3076 | | |
3077 | | // We want the ExtractThisObject of MaybeGlobalThisPolicy. |
3078 | | |
3079 | | // We want the MaybeUnwrapThisObject of MaybeGlobalThisPolicy. |
3080 | | |
3081 | | static bool HandleInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, |
3082 | | bool aSecurityError, |
3083 | | prototypes::ID aProtoId) |
3084 | 0 | { |
3085 | 0 | MOZ_ASSERT(!JS_IsExceptionPending(aCx)); |
3086 | 0 | if (!ReportLenientThisUnwrappingFailure(aCx, &aArgs.callee())) { |
3087 | 0 | return false; |
3088 | 0 | } |
3089 | 0 | aArgs.rval().set(JS::UndefinedValue()); |
3090 | 0 | return true; |
3091 | 0 | } |
3092 | | }; |
3093 | | |
3094 | | // There are some cross-origin things on globals, so we inherit from |
3095 | | // MaybeGlobalThisPolicy. |
3096 | | struct CrossOriginThisPolicy : public MaybeGlobalThisPolicy |
3097 | | { |
3098 | | // We want the HasValidThisValue of MaybeGlobalThisPolicy. |
3099 | | |
3100 | | // We want the ExtractThisObject of MaybeGlobalThisPolicy. |
3101 | | |
3102 | | static MOZ_ALWAYS_INLINE JSObject* MaybeUnwrapThisObject(JSObject* aObj) |
3103 | 0 | { |
3104 | 0 | if (xpc::WrapperFactory::IsXrayWrapper(aObj)) { |
3105 | 0 | return js::UncheckedUnwrap(aObj); |
3106 | 0 | } |
3107 | 0 | |
3108 | 0 | // Else just return aObj; our UnwrapObjectInternal call will try to |
3109 | 0 | // CheckedUnwrap it, and eitehr succeed or get a security error as needed. |
3110 | 0 | return aObj; |
3111 | 0 | } |
3112 | | |
3113 | | // We want the HandleInvalidThis of MaybeGlobalThisPolicy. |
3114 | | }; |
3115 | | |
3116 | | /** |
3117 | | * An ExceptionPolicy struct provides a single HandleException method which is |
3118 | | * used to handle an exception, if any. The method is given the current |
3119 | | * success/failure boolean so it can decide whether there is in fact an |
3120 | | * exception involved. |
3121 | | */ |
3122 | | struct ThrowExceptions |
3123 | | { |
3124 | | // This needs to be inlined because it's called even on no-exceptions |
3125 | | // fast-paths. |
3126 | | static MOZ_ALWAYS_INLINE bool HandleException(JSContext* aCx, |
3127 | | JS::CallArgs& aArgs, |
3128 | | const JSJitInfo* aInfo, |
3129 | | bool aOK) |
3130 | 0 | { |
3131 | 0 | return aOK; |
3132 | 0 | } |
3133 | | }; |
3134 | | |
3135 | | struct ConvertExceptionsToPromises |
3136 | | { |
3137 | | // This needs to be inlined because it's called even on no-exceptions |
3138 | | // fast-paths. |
3139 | | static MOZ_ALWAYS_INLINE bool HandleException(JSContext* aCx, |
3140 | | JS::CallArgs& aArgs, |
3141 | | const JSJitInfo* aInfo, |
3142 | | bool aOK) |
3143 | 0 | { |
3144 | 0 | // Promise-returning getters/methods always return objects. |
3145 | 0 | MOZ_ASSERT(aInfo->returnType() == JSVAL_TYPE_OBJECT); |
3146 | 0 |
|
3147 | 0 | if (aOK) { |
3148 | 0 | return true; |
3149 | 0 | } |
3150 | 0 | |
3151 | 0 | return ConvertExceptionToPromise(aCx, aArgs.rval()); |
3152 | 0 | } |
3153 | | }; |
3154 | | |
3155 | | template<typename ThisPolicy, typename ExceptionPolicy> |
3156 | | bool |
3157 | | GenericGetter(JSContext* cx, unsigned argc, JS::Value* vp) |
3158 | 0 | { |
3159 | 0 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
3160 | 0 | const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); |
3161 | 0 | prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID); |
3162 | 0 | if (!ThisPolicy::HasValidThisValue(args)) { |
3163 | 0 | bool ok = ThisPolicy::HandleInvalidThis(cx, args, false, protoID); |
3164 | 0 | return ExceptionPolicy::HandleException(cx, args, info, ok); |
3165 | 0 | } |
3166 | 0 | JS::Rooted<JSObject*> obj(cx, ThisPolicy::ExtractThisObject(args)); |
3167 | 0 |
|
3168 | 0 | // NOTE: we want to leave obj in its initial compartment, so don't want to |
3169 | 0 | // pass it to UnwrapObjectInternal. Also, the thing we pass to |
3170 | 0 | // UnwrapObjectInternal may be affected by our ThisPolicy. |
3171 | 0 | JS::Rooted<JSObject*> rootSelf(cx, ThisPolicy::MaybeUnwrapThisObject(obj)); |
3172 | 0 | void* self; |
3173 | 0 | { |
3174 | 0 | binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf); |
3175 | 0 | nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(wrapper, |
3176 | 0 | self, |
3177 | 0 | protoID, |
3178 | 0 | info->depth); |
3179 | 0 | if (NS_FAILED(rv)) { |
3180 | 0 | bool ok = |
3181 | 0 | ThisPolicy::HandleInvalidThis(cx, args, |
3182 | 0 | rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, |
3183 | 0 | protoID); |
3184 | 0 | return ExceptionPolicy::HandleException(cx, args, info, ok); |
3185 | 0 | } |
3186 | 0 | } |
3187 | 0 | |
3188 | 0 | MOZ_ASSERT(info->type() == JSJitInfo::Getter); |
3189 | 0 | JSJitGetterOp getter = info->getter; |
3190 | 0 | bool ok = getter(cx, obj, self, JSJitGetterCallArgs(args)); |
3191 | | #ifdef DEBUG |
3192 | | if (ok) { |
3193 | | AssertReturnTypeMatchesJitinfo(info, args.rval()); |
3194 | | } |
3195 | | #endif |
3196 | | return ExceptionPolicy::HandleException(cx, args, info, ok); |
3197 | 0 | } Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericGetter<mozilla::dom::binding_detail::NormalThisPolicy, mozilla::dom::binding_detail::ThrowExceptions>(JSContext*, unsigned int, JS::Value*) Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericGetter<mozilla::dom::binding_detail::NormalThisPolicy, mozilla::dom::binding_detail::ConvertExceptionsToPromises>(JSContext*, unsigned int, JS::Value*) Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericGetter<mozilla::dom::binding_detail::MaybeGlobalThisPolicy, mozilla::dom::binding_detail::ThrowExceptions>(JSContext*, unsigned int, JS::Value*) Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericGetter<mozilla::dom::binding_detail::MaybeGlobalThisPolicy, mozilla::dom::binding_detail::ConvertExceptionsToPromises>(JSContext*, unsigned int, JS::Value*) Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericGetter<mozilla::dom::binding_detail::LenientThisPolicy, mozilla::dom::binding_detail::ThrowExceptions>(JSContext*, unsigned int, JS::Value*) Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericGetter<mozilla::dom::binding_detail::CrossOriginThisPolicy, mozilla::dom::binding_detail::ThrowExceptions>(JSContext*, unsigned int, JS::Value*) |
3198 | | |
3199 | | // Force instantiation of the specializations of GenericGetter we need here. |
3200 | | template bool |
3201 | | GenericGetter<NormalThisPolicy, ThrowExceptions>( |
3202 | | JSContext* cx, unsigned argc, JS::Value* vp); |
3203 | | template bool |
3204 | | GenericGetter<NormalThisPolicy, ConvertExceptionsToPromises>( |
3205 | | JSContext* cx, unsigned argc, JS::Value* vp); |
3206 | | template bool |
3207 | | GenericGetter<MaybeGlobalThisPolicy, ThrowExceptions>( |
3208 | | JSContext* cx, unsigned argc, JS::Value* vp); |
3209 | | template bool |
3210 | | GenericGetter<MaybeGlobalThisPolicy, ConvertExceptionsToPromises>( |
3211 | | JSContext* cx, unsigned argc, JS::Value* vp); |
3212 | | template bool |
3213 | | GenericGetter<LenientThisPolicy, ThrowExceptions>( |
3214 | | JSContext* cx, unsigned argc, JS::Value* vp); |
3215 | | // There aren't any [LenientThis] Promise-returning getters, so don't |
3216 | | // bother instantiating that specialization. |
3217 | | template bool |
3218 | | GenericGetter<CrossOriginThisPolicy, ThrowExceptions>( |
3219 | | JSContext* cx, unsigned argc, JS::Value* vp); |
3220 | | // There aren't any cross-origin Promise-returning getters, so don't |
3221 | | // bother instantiating that specialization. |
3222 | | |
3223 | | template<typename ThisPolicy> |
3224 | | bool |
3225 | | GenericSetter(JSContext* cx, unsigned argc, JS::Value* vp) |
3226 | 0 | { |
3227 | 0 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
3228 | 0 | const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); |
3229 | 0 | prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID); |
3230 | 0 | if (!ThisPolicy::HasValidThisValue(args)) { |
3231 | 0 | return ThisPolicy::HandleInvalidThis(cx, args, false, protoID); |
3232 | 0 | } |
3233 | 0 | JS::Rooted<JSObject*> obj(cx, ThisPolicy::ExtractThisObject(args)); |
3234 | 0 |
|
3235 | 0 | // NOTE: we want to leave obj in its initial compartment, so don't want to |
3236 | 0 | // pass it to UnwrapObject. Also the thing we pass to UnwrapObjectInternal |
3237 | 0 | // may be affected by our ThisPolicy. |
3238 | 0 | JS::Rooted<JSObject*> rootSelf(cx, ThisPolicy::MaybeUnwrapThisObject(obj)); |
3239 | 0 | void* self; |
3240 | 0 | { |
3241 | 0 | binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf); |
3242 | 0 | nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(wrapper, |
3243 | 0 | self, |
3244 | 0 | protoID, |
3245 | 0 | info->depth); |
3246 | 0 | if (NS_FAILED(rv)) { |
3247 | 0 | return |
3248 | 0 | ThisPolicy::HandleInvalidThis(cx, args, |
3249 | 0 | rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, |
3250 | 0 | protoID); |
3251 | 0 | } |
3252 | 0 | } |
3253 | 0 | if (args.length() == 0) { |
3254 | 0 | return ThrowNoSetterArg(cx, protoID); |
3255 | 0 | } |
3256 | 0 | MOZ_ASSERT(info->type() == JSJitInfo::Setter); |
3257 | 0 | JSJitSetterOp setter = info->setter; |
3258 | 0 | if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) { |
3259 | 0 | return false; |
3260 | 0 | } |
3261 | 0 | args.rval().setUndefined(); |
3262 | | #ifdef DEBUG |
3263 | | AssertReturnTypeMatchesJitinfo(info, args.rval()); |
3264 | | #endif |
3265 | | return true; |
3266 | 0 | } Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericSetter<mozilla::dom::binding_detail::NormalThisPolicy>(JSContext*, unsigned int, JS::Value*) Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericSetter<mozilla::dom::binding_detail::MaybeGlobalThisPolicy>(JSContext*, unsigned int, JS::Value*) Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericSetter<mozilla::dom::binding_detail::LenientThisPolicy>(JSContext*, unsigned int, JS::Value*) Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericSetter<mozilla::dom::binding_detail::CrossOriginThisPolicy>(JSContext*, unsigned int, JS::Value*) |
3267 | | |
3268 | | // Force instantiation of the specializations of GenericSetter we need here. |
3269 | | template bool |
3270 | | GenericSetter<NormalThisPolicy>(JSContext* cx, unsigned argc, JS::Value* vp); |
3271 | | template bool |
3272 | | GenericSetter<MaybeGlobalThisPolicy>(JSContext* cx, unsigned argc, |
3273 | | JS::Value* vp); |
3274 | | template bool |
3275 | | GenericSetter<LenientThisPolicy>(JSContext* cx, unsigned argc, JS::Value* vp); |
3276 | | template bool |
3277 | | GenericSetter<CrossOriginThisPolicy>(JSContext* cx, unsigned argc, |
3278 | | JS::Value* vp); |
3279 | | |
3280 | | template<typename ThisPolicy, typename ExceptionPolicy> |
3281 | | bool |
3282 | | GenericMethod(JSContext* cx, unsigned argc, JS::Value* vp) |
3283 | 0 | { |
3284 | 0 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
3285 | 0 | const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); |
3286 | 0 | prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID); |
3287 | 0 | if (!ThisPolicy::HasValidThisValue(args)) { |
3288 | 0 | bool ok = ThisPolicy::HandleInvalidThis(cx, args, false, protoID); |
3289 | 0 | return ExceptionPolicy::HandleException(cx, args, info, ok); |
3290 | 0 | } |
3291 | 0 | JS::Rooted<JSObject*> obj(cx, ThisPolicy::ExtractThisObject(args)); |
3292 | 0 |
|
3293 | 0 | // NOTE: we want to leave obj in its initial compartment, so don't want to |
3294 | 0 | // pass it to UnwrapObjectInternal. Also, the thing we pass to |
3295 | 0 | // UnwrapObjectInternal may be affected by our ThisPolicy. |
3296 | 0 | JS::Rooted<JSObject*> rootSelf(cx, ThisPolicy::MaybeUnwrapThisObject(obj)); |
3297 | 0 | void* self; |
3298 | 0 | { |
3299 | 0 | binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf); |
3300 | 0 | nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(wrapper, |
3301 | 0 | self, |
3302 | 0 | protoID, |
3303 | 0 | info->depth); |
3304 | 0 | if (NS_FAILED(rv)) { |
3305 | 0 | bool ok = |
3306 | 0 | ThisPolicy::HandleInvalidThis(cx, args, |
3307 | 0 | rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, |
3308 | 0 | protoID); |
3309 | 0 | return ExceptionPolicy::HandleException(cx, args, info, ok); |
3310 | 0 | } |
3311 | 0 | } |
3312 | 0 | MOZ_ASSERT(info->type() == JSJitInfo::Method); |
3313 | 0 | JSJitMethodOp method = info->method; |
3314 | 0 | bool ok = method(cx, obj, self, JSJitMethodCallArgs(args)); |
3315 | | #ifdef DEBUG |
3316 | | if (ok) { |
3317 | | AssertReturnTypeMatchesJitinfo(info, args.rval()); |
3318 | | } |
3319 | | #endif |
3320 | | return ExceptionPolicy::HandleException(cx, args, info, ok); |
3321 | 0 | } Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericMethod<mozilla::dom::binding_detail::NormalThisPolicy, mozilla::dom::binding_detail::ThrowExceptions>(JSContext*, unsigned int, JS::Value*) Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericMethod<mozilla::dom::binding_detail::NormalThisPolicy, mozilla::dom::binding_detail::ConvertExceptionsToPromises>(JSContext*, unsigned int, JS::Value*) Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericMethod<mozilla::dom::binding_detail::MaybeGlobalThisPolicy, mozilla::dom::binding_detail::ThrowExceptions>(JSContext*, unsigned int, JS::Value*) Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericMethod<mozilla::dom::binding_detail::MaybeGlobalThisPolicy, mozilla::dom::binding_detail::ConvertExceptionsToPromises>(JSContext*, unsigned int, JS::Value*) Unexecuted instantiation: bool mozilla::dom::binding_detail::GenericMethod<mozilla::dom::binding_detail::CrossOriginThisPolicy, mozilla::dom::binding_detail::ThrowExceptions>(JSContext*, unsigned int, JS::Value*) |
3322 | | |
3323 | | // Force instantiation of the specializations of GenericMethod we need here. |
3324 | | template bool |
3325 | | GenericMethod<NormalThisPolicy, ThrowExceptions>( |
3326 | | JSContext* cx, unsigned argc, JS::Value* vp); |
3327 | | template bool |
3328 | | GenericMethod<NormalThisPolicy, ConvertExceptionsToPromises>( |
3329 | | JSContext* cx, unsigned argc, JS::Value* vp); |
3330 | | template bool |
3331 | | GenericMethod<MaybeGlobalThisPolicy, ThrowExceptions>( |
3332 | | JSContext* cx, unsigned argc, JS::Value* vp); |
3333 | | template bool |
3334 | | GenericMethod<MaybeGlobalThisPolicy, ConvertExceptionsToPromises>( |
3335 | | JSContext* cx, unsigned argc, JS::Value* vp); |
3336 | | template bool |
3337 | | GenericMethod<CrossOriginThisPolicy, ThrowExceptions>( |
3338 | | JSContext* cx, unsigned argc, JS::Value* vp); |
3339 | | // There aren't any cross-origin Promise-returning methods, so don't |
3340 | | // bother instantiating that specialization. |
3341 | | |
3342 | | } // namespace binding_detail |
3343 | | |
3344 | | bool |
3345 | | StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp) |
3346 | 0 | { |
3347 | 0 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
3348 | 0 |
|
3349 | 0 | const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); |
3350 | 0 | MOZ_ASSERT(info); |
3351 | 0 | MOZ_ASSERT(info->type() == JSJitInfo::StaticMethod); |
3352 | 0 |
|
3353 | 0 | bool ok = info->staticMethod(cx, argc, vp); |
3354 | 0 | if (ok) { |
3355 | 0 | return true; |
3356 | 0 | } |
3357 | 0 | |
3358 | 0 | return ConvertExceptionToPromise(cx, args.rval()); |
3359 | 0 | } |
3360 | | |
3361 | | bool |
3362 | | ConvertExceptionToPromise(JSContext* cx, |
3363 | | JS::MutableHandle<JS::Value> rval) |
3364 | 0 | { |
3365 | 0 | JS::Rooted<JS::Value> exn(cx); |
3366 | 0 | if (!JS_GetPendingException(cx, &exn)) { |
3367 | 0 | // This is very important: if there is no pending exception here but we're |
3368 | 0 | // ending up in this code, that means the callee threw an uncatchable |
3369 | 0 | // exception. Just propagate that out as-is. |
3370 | 0 | return false; |
3371 | 0 | } |
3372 | 0 | |
3373 | 0 | JS_ClearPendingException(cx); |
3374 | 0 |
|
3375 | 0 | JSObject* promise = JS::CallOriginalPromiseReject(cx, exn); |
3376 | 0 | if (!promise) { |
3377 | 0 | // We just give up. Put the exception back. |
3378 | 0 | JS_SetPendingException(cx, exn); |
3379 | 0 | return false; |
3380 | 0 | } |
3381 | 0 | |
3382 | 0 | rval.setObject(*promise); |
3383 | 0 | return true; |
3384 | 0 | } |
3385 | | |
3386 | | /* static */ |
3387 | | void |
3388 | | CreateGlobalOptionsWithXPConnect::TraceGlobal(JSTracer* aTrc, JSObject* aObj) |
3389 | 0 | { |
3390 | 0 | xpc::TraceXPCGlobal(aTrc, aObj); |
3391 | 0 | } |
3392 | | |
3393 | | /* static */ |
3394 | | bool |
3395 | | CreateGlobalOptionsWithXPConnect::PostCreateGlobal(JSContext* aCx, |
3396 | | JS::Handle<JSObject*> aGlobal) |
3397 | 0 | { |
3398 | 0 | JSPrincipals* principals = |
3399 | 0 | JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(aGlobal)); |
3400 | 0 | nsIPrincipal* principal = nsJSPrincipals::get(principals); |
3401 | 0 |
|
3402 | 0 | // We create the SiteIdentifier here instead of in the XPCWrappedNativeScope |
3403 | 0 | // constructor because this is fallible. |
3404 | 0 | SiteIdentifier site; |
3405 | 0 | nsresult rv = BasePrincipal::Cast(principal)->GetSiteIdentifier(site); |
3406 | 0 | NS_ENSURE_SUCCESS(rv, false); |
3407 | 0 |
|
3408 | 0 | // Invoking the XPCWrappedNativeScope constructor automatically hooks it |
3409 | 0 | // up to the realm of aGlobal. |
3410 | 0 | (void) new XPCWrappedNativeScope(aCx, aGlobal, site); |
3411 | 0 | return true; |
3412 | 0 | } |
3413 | | |
3414 | | static bool sRegisteredDOMNames = false; |
3415 | | |
3416 | | static void |
3417 | | RegisterDOMNames() |
3418 | 0 | { |
3419 | 0 | if (sRegisteredDOMNames) { |
3420 | 0 | return; |
3421 | 0 | } |
3422 | 0 | |
3423 | 0 | // Register new DOM bindings |
3424 | 0 | WebIDLGlobalNameHash::Init(); |
3425 | 0 |
|
3426 | 0 | sRegisteredDOMNames = true; |
3427 | 0 | } |
3428 | | |
3429 | | /* static */ |
3430 | | bool |
3431 | | CreateGlobalOptions<nsGlobalWindowInner>::PostCreateGlobal(JSContext* aCx, |
3432 | | JS::Handle<JSObject*> aGlobal) |
3433 | 0 | { |
3434 | 0 | RegisterDOMNames(); |
3435 | 0 |
|
3436 | 0 | return CreateGlobalOptionsWithXPConnect::PostCreateGlobal(aCx, aGlobal); |
3437 | 0 | } |
3438 | | |
3439 | | #ifdef DEBUG |
3440 | | void |
3441 | | AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitInfo, |
3442 | | JS::Handle<JS::Value> aValue) |
3443 | | { |
3444 | | switch (aJitInfo->returnType()) { |
3445 | | case JSVAL_TYPE_UNKNOWN: |
3446 | | // Any value is good. |
3447 | | break; |
3448 | | case JSVAL_TYPE_DOUBLE: |
3449 | | // The value could actually be an int32 value as well. |
3450 | | MOZ_ASSERT(aValue.isNumber()); |
3451 | | break; |
3452 | | case JSVAL_TYPE_INT32: |
3453 | | MOZ_ASSERT(aValue.isInt32()); |
3454 | | break; |
3455 | | case JSVAL_TYPE_UNDEFINED: |
3456 | | MOZ_ASSERT(aValue.isUndefined()); |
3457 | | break; |
3458 | | case JSVAL_TYPE_BOOLEAN: |
3459 | | MOZ_ASSERT(aValue.isBoolean()); |
3460 | | break; |
3461 | | case JSVAL_TYPE_STRING: |
3462 | | MOZ_ASSERT(aValue.isString()); |
3463 | | break; |
3464 | | case JSVAL_TYPE_NULL: |
3465 | | MOZ_ASSERT(aValue.isNull()); |
3466 | | break; |
3467 | | case JSVAL_TYPE_OBJECT: |
3468 | | MOZ_ASSERT(aValue.isObject()); |
3469 | | break; |
3470 | | default: |
3471 | | // Someone messed up their jitinfo type. |
3472 | | MOZ_ASSERT(false, "Unexpected JSValueType stored in jitinfo"); |
3473 | | break; |
3474 | | } |
3475 | | } |
3476 | | #endif |
3477 | | |
3478 | | bool |
3479 | | CallerSubsumes(JSObject *aObject) |
3480 | 0 | { |
3481 | 0 | nsIPrincipal* objPrin = nsContentUtils::ObjectPrincipal(js::UncheckedUnwrap(aObject)); |
3482 | 0 | return nsContentUtils::SubjectPrincipal()->Subsumes(objPrin); |
3483 | 0 | } |
3484 | | |
3485 | | nsresult |
3486 | | UnwrapArgImpl(JSContext* cx, |
3487 | | JS::Handle<JSObject*> src, |
3488 | | const nsIID &iid, |
3489 | | void **ppArg) |
3490 | 5 | { |
3491 | 5 | if (!NS_IsMainThread()) { |
3492 | 0 | return NS_ERROR_NOT_AVAILABLE; |
3493 | 0 | } |
3494 | 5 | |
3495 | 5 | nsCOMPtr<nsISupports> iface = xpc::UnwrapReflectorToISupports(src); |
3496 | 5 | if (iface) { |
3497 | 5 | if (NS_FAILED(iface->QueryInterface(iid, ppArg))) { |
3498 | 0 | return NS_ERROR_XPC_BAD_CONVERT_JS; |
3499 | 0 | } |
3500 | 5 | |
3501 | 5 | return NS_OK; |
3502 | 5 | } |
3503 | 0 | |
3504 | 0 | // Only allow XPCWrappedJS stuff in system code. Ideally we would remove this |
3505 | 0 | // even there, but that involves converting some things to WebIDL callback |
3506 | 0 | // interfaces and making some other things builtinclass... |
3507 | 0 | if (!nsContentUtils::IsSystemCaller(cx)) { |
3508 | 0 | return NS_ERROR_XPC_BAD_CONVERT_JS; |
3509 | 0 | } |
3510 | 0 | |
3511 | 0 | RefPtr<nsXPCWrappedJS> wrappedJS; |
3512 | 0 | nsresult rv = |
3513 | 0 | nsXPCWrappedJS::GetNewOrUsed(cx, src, iid, getter_AddRefs(wrappedJS)); |
3514 | 0 | if (NS_FAILED(rv) || !wrappedJS) { |
3515 | 0 | return rv; |
3516 | 0 | } |
3517 | 0 | |
3518 | 0 | // We need to go through the QueryInterface logic to make this return |
3519 | 0 | // the right thing for the various 'special' interfaces; e.g. |
3520 | 0 | // nsIPropertyBag. We must use AggregatedQueryInterface in cases where |
3521 | 0 | // there is an outer to avoid nasty recursion. |
3522 | 0 | return wrappedJS->QueryInterface(iid, ppArg); |
3523 | 0 | } |
3524 | | |
3525 | | nsresult |
3526 | | UnwrapWindowProxyImpl(JSContext* cx, |
3527 | | JS::Handle<JSObject*> src, |
3528 | | nsPIDOMWindowOuter** ppArg) |
3529 | 0 | { |
3530 | 0 | nsCOMPtr<nsPIDOMWindowInner> inner; |
3531 | 0 | nsresult rv = UnwrapArg<nsPIDOMWindowInner>(cx, src, getter_AddRefs(inner)); |
3532 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
3533 | 0 |
|
3534 | 0 | nsCOMPtr<nsPIDOMWindowOuter> outer = inner->GetOuterWindow(); |
3535 | 0 | outer.forget(ppArg); |
3536 | 0 | return NS_OK; |
3537 | 0 | } |
3538 | | |
3539 | | bool |
3540 | | SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj, |
3541 | | JS::Handle<jsid> id, bool* resolvedp) |
3542 | 50 | { |
3543 | 50 | if (!ResolveGlobal(cx, obj, id, resolvedp)) { |
3544 | 0 | return false; |
3545 | 0 | } |
3546 | 50 | |
3547 | 50 | if (*resolvedp) { |
3548 | 1 | return true; |
3549 | 1 | } |
3550 | 49 | |
3551 | 49 | return ResolveSystemBinding(cx, obj, id, resolvedp); |
3552 | 49 | } |
3553 | | |
3554 | | bool |
3555 | | SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj) |
3556 | 0 | { |
3557 | 0 | bool ignored = false; |
3558 | 0 | return JS_EnumerateStandardClasses(cx, obj) && |
3559 | 0 | ResolveSystemBinding(cx, obj, JSID_VOIDHANDLE, &ignored); |
3560 | 0 | } |
3561 | | |
3562 | | template<decltype(JS::NewMapObject) Method> |
3563 | | bool |
3564 | | GetMaplikeSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj, |
3565 | | size_t aSlotIndex, |
3566 | | JS::MutableHandle<JSObject*> aBackingObj, |
3567 | | bool* aBackingObjCreated) |
3568 | 0 | { |
3569 | 0 | JS::Rooted<JSObject*> reflector(aCx); |
3570 | 0 | reflector = IsDOMObject(aObj) ? aObj : js::UncheckedUnwrap(aObj, |
3571 | 0 | /* stopAtWindowProxy = */ false); |
3572 | 0 |
|
3573 | 0 | // Retrieve the backing object from the reserved slot on the maplike/setlike |
3574 | 0 | // object. If it doesn't exist yet, create it. |
3575 | 0 | JS::Rooted<JS::Value> slotValue(aCx); |
3576 | 0 | slotValue = js::GetReservedSlot(reflector, aSlotIndex); |
3577 | 0 | if (slotValue.isUndefined()) { |
3578 | 0 | // Since backing object access can happen in non-originating realms, |
3579 | 0 | // make sure to create the backing object in reflector realm. |
3580 | 0 | { |
3581 | 0 | JSAutoRealm ar(aCx, reflector); |
3582 | 0 | JS::Rooted<JSObject*> newBackingObj(aCx); |
3583 | 0 | newBackingObj.set(Method(aCx)); |
3584 | 0 | if (NS_WARN_IF(!newBackingObj)) { |
3585 | 0 | return false; |
3586 | 0 | } |
3587 | 0 | js::SetReservedSlot(reflector, aSlotIndex, JS::ObjectValue(*newBackingObj)); |
3588 | 0 | } |
3589 | 0 | slotValue = js::GetReservedSlot(reflector, aSlotIndex); |
3590 | 0 | *aBackingObjCreated = true; |
3591 | 0 | } else { |
3592 | 0 | *aBackingObjCreated = false; |
3593 | 0 | } |
3594 | 0 | if (!MaybeWrapNonDOMObjectValue(aCx, &slotValue)) { |
3595 | 0 | return false; |
3596 | 0 | } |
3597 | 0 | aBackingObj.set(&slotValue.toObject()); |
3598 | 0 | return true; |
3599 | 0 | } Unexecuted instantiation: bool mozilla::dom::GetMaplikeSetlikeBackingObject<&JS::NewMapObject>(JSContext*, JS::Handle<JSObject*>, unsigned long, JS::MutableHandle<JSObject*>, bool*) Unexecuted instantiation: bool mozilla::dom::GetMaplikeSetlikeBackingObject<&JS::NewSetObject>(JSContext*, JS::Handle<JSObject*>, unsigned long, JS::MutableHandle<JSObject*>, bool*) |
3600 | | |
3601 | | bool |
3602 | | GetMaplikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj, |
3603 | | size_t aSlotIndex, |
3604 | | JS::MutableHandle<JSObject*> aBackingObj, |
3605 | | bool* aBackingObjCreated) |
3606 | 0 | { |
3607 | 0 | return GetMaplikeSetlikeBackingObject<JS::NewMapObject>(aCx, aObj, aSlotIndex, |
3608 | 0 | aBackingObj, |
3609 | 0 | aBackingObjCreated); |
3610 | 0 | } |
3611 | | |
3612 | | bool |
3613 | | GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj, |
3614 | | size_t aSlotIndex, |
3615 | | JS::MutableHandle<JSObject*> aBackingObj, |
3616 | | bool* aBackingObjCreated) |
3617 | 0 | { |
3618 | 0 | return GetMaplikeSetlikeBackingObject<JS::NewSetObject>(aCx, aObj, aSlotIndex, |
3619 | 0 | aBackingObj, |
3620 | 0 | aBackingObjCreated); |
3621 | 0 | } |
3622 | | |
3623 | | bool |
3624 | | ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp) |
3625 | 0 | { |
3626 | 0 | JS::CallArgs args = CallArgsFromVp(aArgc, aVp); |
3627 | 0 | // Unpack callback and object from slots |
3628 | 0 | JS::Rooted<JS::Value> |
3629 | 0 | callbackFn(aCx, js::GetFunctionNativeReserved(&args.callee(), |
3630 | 0 | FOREACH_CALLBACK_SLOT)); |
3631 | 0 | JS::Rooted<JS::Value> |
3632 | 0 | maplikeOrSetlikeObj(aCx, |
3633 | 0 | js::GetFunctionNativeReserved(&args.callee(), |
3634 | 0 | FOREACH_MAPLIKEORSETLIKEOBJ_SLOT)); |
3635 | 0 | MOZ_ASSERT(aArgc == 3); |
3636 | 0 | JS::AutoValueVector newArgs(aCx); |
3637 | 0 | // Arguments are passed in as value, key, object. Keep value and key, replace |
3638 | 0 | // object with the maplike/setlike object. |
3639 | 0 | if (!newArgs.append(args.get(0))) { |
3640 | 0 | return false; |
3641 | 0 | } |
3642 | 0 | if (!newArgs.append(args.get(1))) { |
3643 | 0 | return false; |
3644 | 0 | } |
3645 | 0 | if (!newArgs.append(maplikeOrSetlikeObj)) { |
3646 | 0 | return false; |
3647 | 0 | } |
3648 | 0 | JS::Rooted<JS::Value> rval(aCx, JS::UndefinedValue()); |
3649 | 0 | // Now actually call the user specified callback |
3650 | 0 | return JS::Call(aCx, args.thisv(), callbackFn, newArgs, &rval); |
3651 | 0 | } |
3652 | | |
3653 | | static inline prototypes::ID |
3654 | | GetProtoIdForNewtarget(JS::Handle<JSObject*> aNewTarget) |
3655 | 0 | { |
3656 | 0 | const js::Class* newTargetClass = js::GetObjectClass(aNewTarget); |
3657 | 0 | if (IsDOMIfaceAndProtoClass(newTargetClass)) { |
3658 | 0 | const DOMIfaceAndProtoJSClass* newTargetIfaceClass = |
3659 | 0 | DOMIfaceAndProtoJSClass::FromJSClass(newTargetClass); |
3660 | 0 | if (newTargetIfaceClass->mType == eInterface) { |
3661 | 0 | return newTargetIfaceClass->mPrototypeID; |
3662 | 0 | } |
3663 | 0 | } else if (JS_IsNativeFunction(aNewTarget, Constructor)) { |
3664 | 0 | return GetNativePropertyHooksFromConstructorFunction(aNewTarget)->mPrototypeID; |
3665 | 0 | } |
3666 | 0 | |
3667 | 0 | return prototypes::id::_ID_Count; |
3668 | 0 | } |
3669 | | |
3670 | | bool |
3671 | | GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs, |
3672 | | JS::MutableHandle<JSObject*> aDesiredProto) |
3673 | 0 | { |
3674 | 0 | if (!aCallArgs.isConstructing()) { |
3675 | 0 | aDesiredProto.set(nullptr); |
3676 | 0 | return true; |
3677 | 0 | } |
3678 | 0 | |
3679 | 0 | // The desired prototype depends on the actual constructor that was invoked, |
3680 | 0 | // which is passed to us as the newTarget in the callargs. We want to do |
3681 | 0 | // something akin to the ES6 specification's GetProtototypeFromConstructor (so |
3682 | 0 | // get .prototype on the newTarget, with a fallback to some sort of default). |
3683 | 0 | |
3684 | 0 | // First, a fast path for the case when the the constructor is in fact one of |
3685 | 0 | // our DOM constructors. This is safe because on those the "constructor" |
3686 | 0 | // property is non-configurable and non-writable, so we don't have to do the |
3687 | 0 | // slow JS_GetProperty call. |
3688 | 0 | JS::Rooted<JSObject*> newTarget(aCx, &aCallArgs.newTarget().toObject()); |
3689 | 0 | JS::Rooted<JSObject*> originalNewTarget(aCx, newTarget); |
3690 | 0 | // See whether we have a known DOM constructor here, such that we can take a |
3691 | 0 | // fast path. |
3692 | 0 | prototypes::ID protoID = GetProtoIdForNewtarget(newTarget); |
3693 | 0 | if (protoID == prototypes::id::_ID_Count) { |
3694 | 0 | // We might still have a cross-compartment wrapper for a known DOM |
3695 | 0 | // constructor. |
3696 | 0 | newTarget = js::CheckedUnwrap(newTarget); |
3697 | 0 | if (newTarget && newTarget != originalNewTarget) { |
3698 | 0 | protoID = GetProtoIdForNewtarget(newTarget); |
3699 | 0 | } |
3700 | 0 | } |
3701 | 0 |
|
3702 | 0 | if (protoID != prototypes::id::_ID_Count) { |
3703 | 0 | ProtoAndIfaceCache& protoAndIfaceCache = |
3704 | 0 | *GetProtoAndIfaceCache(JS::GetNonCCWObjectGlobal(newTarget)); |
3705 | 0 | aDesiredProto.set(protoAndIfaceCache.EntrySlotMustExist(protoID)); |
3706 | 0 | if (newTarget != originalNewTarget) { |
3707 | 0 | return JS_WrapObject(aCx, aDesiredProto); |
3708 | 0 | } |
3709 | 0 | return true; |
3710 | 0 | } |
3711 | 0 | |
3712 | 0 | // Slow path. This basically duplicates the ES6 spec's |
3713 | 0 | // GetPrototypeFromConstructor except that instead of taking a string naming |
3714 | 0 | // the fallback prototype we just fall back to using null and assume that our |
3715 | 0 | // caller will then pick the right default. The actual defaulting behavior |
3716 | 0 | // here still needs to be defined in the Web IDL specification. |
3717 | 0 | // |
3718 | 0 | // Note that it's very important to do this property get on originalNewTarget, |
3719 | 0 | // not our unwrapped newTarget, since we want to get Xray behavior here as |
3720 | 0 | // needed. |
3721 | 0 | // XXXbz for speed purposes, using a preinterned id here sure would be nice. |
3722 | 0 | JS::Rooted<JS::Value> protoVal(aCx); |
3723 | 0 | if (!JS_GetProperty(aCx, originalNewTarget, "prototype", &protoVal)) { |
3724 | 0 | return false; |
3725 | 0 | } |
3726 | 0 | |
3727 | 0 | if (!protoVal.isObject()) { |
3728 | 0 | aDesiredProto.set(nullptr); |
3729 | 0 | return true; |
3730 | 0 | } |
3731 | 0 | |
3732 | 0 | aDesiredProto.set(&protoVal.toObject()); |
3733 | 0 | return true; |
3734 | 0 | } |
3735 | | |
3736 | | namespace { |
3737 | | |
3738 | | class MOZ_RAII AutoConstructionDepth final |
3739 | | { |
3740 | | public: |
3741 | | MOZ_IMPLICIT AutoConstructionDepth(CustomElementDefinition* aDefinition) |
3742 | | : mDefinition(aDefinition) |
3743 | 0 | { |
3744 | 0 | MOZ_ASSERT(mDefinition->mConstructionStack.IsEmpty()); |
3745 | 0 |
|
3746 | 0 | mDefinition->mConstructionDepth++; |
3747 | 0 | // If the mConstructionDepth isn't matched with the length of mPrefixStack, |
3748 | 0 | // this means the constructor is called directly from JS, i.e. |
3749 | 0 | // 'new CustomElementConstructor()', we have to push a dummy prefix into |
3750 | 0 | // stack. |
3751 | 0 | if (mDefinition->mConstructionDepth > mDefinition->mPrefixStack.Length()) { |
3752 | 0 | mDidPush = true; |
3753 | 0 | mDefinition->mPrefixStack.AppendElement(nullptr); |
3754 | 0 | } |
3755 | 0 |
|
3756 | 0 | MOZ_ASSERT(mDefinition->mConstructionDepth == mDefinition->mPrefixStack.Length()); |
3757 | 0 | } |
3758 | | |
3759 | | ~AutoConstructionDepth() |
3760 | 0 | { |
3761 | 0 | MOZ_ASSERT(mDefinition->mConstructionDepth > 0); |
3762 | 0 | MOZ_ASSERT(mDefinition->mConstructionDepth == mDefinition->mPrefixStack.Length()); |
3763 | 0 |
|
3764 | 0 | if (mDidPush) { |
3765 | 0 | MOZ_ASSERT(mDefinition->mPrefixStack.LastElement() == nullptr); |
3766 | 0 | mDefinition->mPrefixStack.RemoveLastElement(); |
3767 | 0 | } |
3768 | 0 | mDefinition->mConstructionDepth--; |
3769 | 0 | } |
3770 | | |
3771 | | private: |
3772 | | CustomElementDefinition* mDefinition; |
3773 | | bool mDidPush = false; |
3774 | | }; |
3775 | | |
3776 | | } // anonymous namespace |
3777 | | |
3778 | | // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor |
3779 | | namespace binding_detail { |
3780 | | bool |
3781 | | HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp, |
3782 | | constructors::id::ID aConstructorId, |
3783 | | prototypes::id::ID aProtoId, |
3784 | | CreateInterfaceObjectsMethod aCreator) |
3785 | 0 | { |
3786 | 0 | JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); |
3787 | 0 |
|
3788 | 0 | // Per spec, this is technically part of step 3, but doing the check |
3789 | 0 | // directly lets us provide a better error message. And then in |
3790 | 0 | // step 2 we can work with newTarget in a simpler way because we |
3791 | 0 | // know it's an object. |
3792 | 0 | if (!args.isConstructing()) { |
3793 | 0 | return ThrowConstructorWithoutNew(aCx, |
3794 | 0 | NamesOfInterfacesWithProtos(aProtoId)); |
3795 | 0 | } |
3796 | 0 | |
3797 | 0 | JS::Rooted<JSObject*> callee(aCx, &args.callee()); |
3798 | 0 | // 'callee' is not a function here; it's either an Xray for our interface |
3799 | 0 | // object or the interface object itself. So caling XrayAwareCalleeGlobal on |
3800 | 0 | // it is not safe. But since in the Xray case it's a wrapper for our |
3801 | 0 | // interface object, we can just construct our GlobalObject from it and end |
3802 | 0 | // up with the right thing. |
3803 | 0 | GlobalObject global(aCx, callee); |
3804 | 0 | if (global.Failed()) { |
3805 | 0 | return false; |
3806 | 0 | } |
3807 | 0 | |
3808 | 0 | // Now we start the [HTMLConstructor] algorithm steps from |
3809 | 0 | // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor |
3810 | 0 | |
3811 | 0 | // Step 1. |
3812 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global.GetAsSupports()); |
3813 | 0 | if (!window) { |
3814 | 0 | // This means we ended up with an HTML Element interface object defined in |
3815 | 0 | // a non-Window scope. That's ... pretty unexpected. |
3816 | 0 | return Throw(aCx, NS_ERROR_UNEXPECTED); |
3817 | 0 | } |
3818 | 0 | RefPtr<mozilla::dom::CustomElementRegistry> registry(window->CustomElements()); |
3819 | 0 |
|
3820 | 0 | // Technically, per spec, a window always has a document. In Gecko, a |
3821 | 0 | // sufficiently torn-down window might not, so check for that case. We're |
3822 | 0 | // going to need a document to create an element. |
3823 | 0 | nsIDocument* doc = window->GetExtantDoc(); |
3824 | 0 | if (!doc) { |
3825 | 0 | return Throw(aCx, NS_ERROR_UNEXPECTED); |
3826 | 0 | } |
3827 | 0 | |
3828 | 0 | // Step 2. |
3829 | 0 | |
3830 | 0 | // The newTarget might be a cross-compartment wrapper. Get the underlying |
3831 | 0 | // object so we can do the spec's object-identity checks. If we ever stop |
3832 | 0 | // unwrapping here, carefully audit uses of newTarget below! |
3833 | 0 | JS::Rooted<JSObject*> newTarget(aCx, js::CheckedUnwrap(&args.newTarget().toObject())); |
3834 | 0 | if (!newTarget) { |
3835 | 0 | return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR); |
3836 | 0 | } |
3837 | 0 | |
3838 | 0 | // Enter the compartment of our underlying newTarget object, so we end |
3839 | 0 | // up comparing to the constructor object for our interface from that global. |
3840 | 0 | // XXXbz This is not what the spec says to do, and it's not super-clear to me |
3841 | 0 | // at this point why we're doing it. Why not just compare |newTarget| and |
3842 | 0 | // |callee| if the intent is just to prevent registration of HTML interface |
3843 | 0 | // objects as constructors? Of course it's not clear that the spec check |
3844 | 0 | // makes sense to start with: https://github.com/whatwg/html/issues/3575 |
3845 | 0 | { |
3846 | 0 | JSAutoRealm ar(aCx, newTarget); |
3847 | 0 | JS::Handle<JSObject*> constructor = |
3848 | 0 | GetPerInterfaceObjectHandle(aCx, aConstructorId, aCreator, |
3849 | 0 | true); |
3850 | 0 | if (!constructor) { |
3851 | 0 | return false; |
3852 | 0 | } |
3853 | 0 | if (newTarget == constructor) { |
3854 | 0 | return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR); |
3855 | 0 | } |
3856 | 0 | } |
3857 | 0 | |
3858 | 0 | // Step 3. |
3859 | 0 | CustomElementDefinition* definition = |
3860 | 0 | registry->LookupCustomElementDefinition(aCx, newTarget); |
3861 | 0 | if (!definition) { |
3862 | 0 | return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR); |
3863 | 0 | } |
3864 | 0 | |
3865 | 0 | // Steps 4 and 5 do some sanity checks on our callee. We add to those a |
3866 | 0 | // determination of what sort of element we're planning to construct. |
3867 | 0 | // Technically, this should happen (implicitly) in step 8, but this |
3868 | 0 | // determination is side-effect-free, so it's OK. |
3869 | 0 | int32_t ns = definition->mNamespaceID; |
3870 | 0 |
|
3871 | 0 | constructorGetterCallback cb = nullptr; |
3872 | 0 | if (ns == kNameSpaceID_XUL) { |
3873 | 0 | if (definition->mLocalName == nsGkAtoms::menupopup || |
3874 | 0 | definition->mLocalName == nsGkAtoms::popup || |
3875 | 0 | definition->mLocalName == nsGkAtoms::panel || |
3876 | 0 | definition->mLocalName == nsGkAtoms::tooltip) { |
3877 | 0 | cb = XULPopupElement_Binding::GetConstructorObject; |
3878 | 0 | } else if (definition->mLocalName == nsGkAtoms::iframe || |
3879 | 0 | definition->mLocalName == nsGkAtoms::browser || |
3880 | 0 | definition->mLocalName == nsGkAtoms::editor) { |
3881 | 0 | cb = XULFrameElement_Binding::GetConstructorObject; |
3882 | 0 | } else if (definition->mLocalName == nsGkAtoms::menu || |
3883 | 0 | definition->mLocalName == nsGkAtoms::menulist) { |
3884 | 0 | cb = XULMenuElement_Binding::GetConstructorObject; |
3885 | 0 | } else if (definition->mLocalName == nsGkAtoms::scrollbox) { |
3886 | 0 | cb = XULScrollElement_Binding::GetConstructorObject; |
3887 | 0 | } else { |
3888 | 0 | cb = XULElement_Binding::GetConstructorObject; |
3889 | 0 | } |
3890 | 0 | } |
3891 | 0 |
|
3892 | 0 | int32_t tag = eHTMLTag_userdefined; |
3893 | 0 | if (!definition->IsCustomBuiltIn()) { |
3894 | 0 | // Step 4. |
3895 | 0 | // If the definition is for an autonomous custom element, the active |
3896 | 0 | // function should be HTMLElement or extend from XULElement. |
3897 | 0 | if (!cb) { |
3898 | 0 | cb = HTMLElement_Binding::GetConstructorObject; |
3899 | 0 | } |
3900 | 0 |
|
3901 | 0 | // We want to get the constructor from our global's realm, not the |
3902 | 0 | // caller realm. |
3903 | 0 | JSAutoRealm ar(aCx, global.Get()); |
3904 | 0 | JS::Rooted<JSObject*> constructor(aCx, cb(aCx)); |
3905 | 0 |
|
3906 | 0 | if (constructor != js::CheckedUnwrap(callee)) { |
3907 | 0 | return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR); |
3908 | 0 | } |
3909 | 0 | } else { |
3910 | 0 | if (ns == kNameSpaceID_XHTML) { |
3911 | 0 | // Step 5. |
3912 | 0 | // If the definition is for a customized built-in element, the localName |
3913 | 0 | // should be one of the ones defined in the specification for this interface. |
3914 | 0 | tag = nsHTMLTags::CaseSensitiveAtomTagToId(definition->mLocalName); |
3915 | 0 | if (tag == eHTMLTag_userdefined) { |
3916 | 0 | return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR); |
3917 | 0 | } |
3918 | 0 | |
3919 | 0 | MOZ_ASSERT(tag <= NS_HTML_TAG_MAX, "tag is out of bounds"); |
3920 | 0 |
|
3921 | 0 | // If the definition is for a customized built-in element, the active |
3922 | 0 | // function should be the localname's element interface. |
3923 | 0 | cb = sConstructorGetterCallback[tag]; |
3924 | 0 | } |
3925 | 0 |
|
3926 | 0 | if (!cb) { |
3927 | 0 | return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR); |
3928 | 0 | } |
3929 | 0 | |
3930 | 0 | // We want to get the constructor from our global's realm, not the |
3931 | 0 | // caller realm. |
3932 | 0 | JSAutoRealm ar(aCx, global.Get()); |
3933 | 0 | JS::Rooted<JSObject*> constructor(aCx, cb(aCx)); |
3934 | 0 | if (!constructor) { |
3935 | 0 | return false; |
3936 | 0 | } |
3937 | 0 | |
3938 | 0 | if (constructor != js::CheckedUnwrap(callee)) { |
3939 | 0 | return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR); |
3940 | 0 | } |
3941 | 0 | } |
3942 | 0 | |
3943 | 0 | // Step 6. |
3944 | 0 | JS::Rooted<JSObject*> desiredProto(aCx); |
3945 | 0 | if (!GetDesiredProto(aCx, args, &desiredProto)) { |
3946 | 0 | return false; |
3947 | 0 | } |
3948 | 0 | |
3949 | 0 | // Step 7. |
3950 | 0 | if (!desiredProto) { |
3951 | 0 | // This fallback behavior is designed to match analogous behavior for the |
3952 | 0 | // JavaScript built-ins. So we enter the realm of our underlying newTarget |
3953 | 0 | // object and fall back to the prototype object from that global. |
3954 | 0 | // XXX The spec says to use GetFunctionRealm(), which is not actually |
3955 | 0 | // the same thing as what we have here (e.g. in the case of scripted callable proxies |
3956 | 0 | // whose target is not same-realm with the proxy, or bound functions, etc). |
3957 | 0 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1317658 |
3958 | 0 | { |
3959 | 0 | JSAutoRealm ar(aCx, newTarget); |
3960 | 0 | desiredProto = GetPerInterfaceObjectHandle(aCx, aProtoId, aCreator, true); |
3961 | 0 | if (!desiredProto) { |
3962 | 0 | return false; |
3963 | 0 | } |
3964 | 0 | } |
3965 | 0 | |
3966 | 0 | // desiredProto is in the realm of the underlying newTarget object. |
3967 | 0 | // Wrap it into the context realm. |
3968 | 0 | if (!JS_WrapObject(aCx, &desiredProto)) { |
3969 | 0 | return false; |
3970 | 0 | } |
3971 | 0 | } |
3972 | 0 | |
3973 | 0 | // We need to do some work to actually return an Element, so we do step 8 on |
3974 | 0 | // one branch and steps 9-12 on another branch, then common up the "return |
3975 | 0 | // element" work. |
3976 | 0 | RefPtr<Element> element; |
3977 | 0 | nsTArray<RefPtr<Element>>& constructionStack = |
3978 | 0 | definition->mConstructionStack; |
3979 | 0 | if (constructionStack.IsEmpty()) { |
3980 | 0 | // Step 8. |
3981 | 0 | // Now we go to construct an element. We want to do this in global's |
3982 | 0 | // realm, not caller realm (the normal constructor behavior), |
3983 | 0 | // just in case those elements create JS things. |
3984 | 0 | JSAutoRealm ar(aCx, global.Get()); |
3985 | 0 | AutoConstructionDepth acd(definition); |
3986 | 0 |
|
3987 | 0 | RefPtr<NodeInfo> nodeInfo = |
3988 | 0 | doc->NodeInfoManager()->GetNodeInfo(definition->mLocalName, |
3989 | 0 | definition->mPrefixStack.LastElement(), |
3990 | 0 | ns, |
3991 | 0 | nsINode::ELEMENT_NODE); |
3992 | 0 | MOZ_ASSERT(nodeInfo); |
3993 | 0 |
|
3994 | 0 | if (ns == kNameSpaceID_XUL) { |
3995 | 0 | element = nsXULElement::Construct(nodeInfo.forget()); |
3996 | 0 |
|
3997 | 0 | } else { |
3998 | 0 | if (tag == eHTMLTag_userdefined) { |
3999 | 0 | // Autonomous custom element. |
4000 | 0 | element = NS_NewHTMLElement(nodeInfo.forget()); |
4001 | 0 | } else { |
4002 | 0 | // Customized built-in element. |
4003 | 0 | element = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER); |
4004 | 0 | } |
4005 | 0 | } |
4006 | 0 |
|
4007 | 0 | element->SetCustomElementData( |
4008 | 0 | new CustomElementData(definition->mType, CustomElementData::State::eCustom)); |
4009 | 0 |
|
4010 | 0 | element->SetCustomElementDefinition(definition); |
4011 | 0 | } else { |
4012 | 0 | // Step 9. |
4013 | 0 | element = constructionStack.LastElement(); |
4014 | 0 |
|
4015 | 0 | // Step 10. |
4016 | 0 | if (element == ALREADY_CONSTRUCTED_MARKER) { |
4017 | 0 | return Throw(aCx, NS_ERROR_DOM_INVALID_STATE_ERR); |
4018 | 0 | } |
4019 | 0 | |
4020 | 0 | // Step 11. |
4021 | 0 | // Do prototype swizzling for upgrading a custom element here, for cases |
4022 | 0 | // when we have a reflector already. If we don't have one yet, we will |
4023 | 0 | // create it with the right proto (by calling DoGetOrCreateDOMReflector with |
4024 | 0 | // that proto). |
4025 | 0 | JS::Rooted<JSObject*> reflector(aCx, element->GetWrapper()); |
4026 | 0 | if (reflector) { |
4027 | 0 | // reflector might be in different realm. |
4028 | 0 | JSAutoRealm ar(aCx, reflector); |
4029 | 0 | JS::Rooted<JSObject*> givenProto(aCx, desiredProto); |
4030 | 0 | if (!JS_WrapObject(aCx, &givenProto) || |
4031 | 0 | !JS_SetPrototype(aCx, reflector, givenProto)) { |
4032 | 0 | return false; |
4033 | 0 | } |
4034 | 0 | } |
4035 | 0 | |
4036 | 0 | // Step 12. |
4037 | 0 | constructionStack.LastElement() = ALREADY_CONSTRUCTED_MARKER; |
4038 | 0 | } |
4039 | 0 |
|
4040 | 0 | // Tail end of step 8 and step 13: returning the element. We want to do this |
4041 | 0 | // part in the global's realm, though in practice it won't matter much |
4042 | 0 | // because Element always knows which realm it should be created in. |
4043 | 0 | JSAutoRealm ar(aCx, global.Get()); |
4044 | 0 | if (!js::IsObjectInContextCompartment(desiredProto, aCx) && |
4045 | 0 | !JS_WrapObject(aCx, &desiredProto)) { |
4046 | 0 | return false; |
4047 | 0 | } |
4048 | 0 | |
4049 | 0 | return GetOrCreateDOMReflector(aCx, element, args.rval(), desiredProto); |
4050 | 0 | } |
4051 | | } // namespace binding_detail |
4052 | | |
4053 | | #ifdef DEBUG |
4054 | | namespace binding_detail { |
4055 | | void |
4056 | | AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector, |
4057 | | JS::Handle<JSObject*> aGivenProto) |
4058 | | { |
4059 | | if (!aGivenProto) { |
4060 | | // Nothing to assert here |
4061 | | return; |
4062 | | } |
4063 | | |
4064 | | JS::Rooted<JSObject*> reflector(aCx, aReflector); |
4065 | | JSAutoRealm ar(aCx, reflector); |
4066 | | JS::Rooted<JSObject*> reflectorProto(aCx); |
4067 | | bool ok = JS_GetPrototype(aCx, reflector, &reflectorProto); |
4068 | | MOZ_ASSERT(ok); |
4069 | | // aGivenProto may not be in the right realm here, so we |
4070 | | // have to wrap it to compare. |
4071 | | JS::Rooted<JSObject*> givenProto(aCx, aGivenProto); |
4072 | | ok = JS_WrapObject(aCx, &givenProto); |
4073 | | MOZ_ASSERT(ok); |
4074 | | MOZ_ASSERT(givenProto == reflectorProto, |
4075 | | "How are we supposed to change the proto now?"); |
4076 | | } |
4077 | | } // namespace binding_detail |
4078 | | #endif // DEBUG |
4079 | | |
4080 | | void |
4081 | | SetDocumentAndPageUseCounter(JSObject* aObject, UseCounter aUseCounter) |
4082 | 0 | { |
4083 | 0 | nsGlobalWindowInner* win = xpc::WindowGlobalOrNull(js::UncheckedUnwrap(aObject)); |
4084 | 0 | if (win && win->GetDocument()) { |
4085 | 0 | win->GetDocument()->SetDocumentAndPageUseCounter(aUseCounter); |
4086 | 0 | } |
4087 | 0 | } |
4088 | | |
4089 | | namespace { |
4090 | | |
4091 | | // This runnable is used to write a deprecation message from a worker to the |
4092 | | // console running on the main-thread. |
4093 | | class DeprecationWarningRunnable final : public WorkerProxyToMainThreadRunnable |
4094 | | { |
4095 | | nsIDocument::DeprecatedOperations mOperation; |
4096 | | |
4097 | | public: |
4098 | | explicit DeprecationWarningRunnable(nsIDocument::DeprecatedOperations aOperation) |
4099 | | : mOperation(aOperation) |
4100 | 0 | {} |
4101 | | |
4102 | | private: |
4103 | | void |
4104 | | RunOnMainThread(WorkerPrivate* aWorkerPrivate) override |
4105 | 0 | { |
4106 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
4107 | 0 | MOZ_ASSERT(aWorkerPrivate); |
4108 | 0 |
|
4109 | 0 | // Walk up to our containing page |
4110 | 0 | WorkerPrivate* wp = aWorkerPrivate; |
4111 | 0 | while (wp->GetParent()) { |
4112 | 0 | wp = wp->GetParent(); |
4113 | 0 | } |
4114 | 0 |
|
4115 | 0 | nsPIDOMWindowInner* window = wp->GetWindow(); |
4116 | 0 | if (window && window->GetExtantDoc()) { |
4117 | 0 | window->GetExtantDoc()->WarnOnceAbout(mOperation); |
4118 | 0 | } |
4119 | 0 | } |
4120 | | |
4121 | | void |
4122 | | RunBackOnWorkerThreadForCleanup(WorkerPrivate* aWorkerPrivate) override |
4123 | 0 | {} |
4124 | | }; |
4125 | | |
4126 | | } // anonymous namespace |
4127 | | |
4128 | | void |
4129 | | DeprecationWarning(JSContext* aCx, JSObject* aObject, |
4130 | | nsIDocument::DeprecatedOperations aOperation) |
4131 | 0 | { |
4132 | 0 | GlobalObject global(aCx, aObject); |
4133 | 0 | if (global.Failed()) { |
4134 | 0 | NS_ERROR("Could not create global for DeprecationWarning"); |
4135 | 0 | return; |
4136 | 0 | } |
4137 | 0 |
|
4138 | 0 | DeprecationWarning(global, aOperation); |
4139 | 0 | } |
4140 | | |
4141 | | void |
4142 | | DeprecationWarning(const GlobalObject& aGlobal, |
4143 | | nsIDocument::DeprecatedOperations aOperation) |
4144 | 0 | { |
4145 | 0 | if (NS_IsMainThread()) { |
4146 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports()); |
4147 | 0 | if (window && window->GetExtantDoc()) { |
4148 | 0 | window->GetExtantDoc()->WarnOnceAbout(aOperation); |
4149 | 0 | } |
4150 | 0 |
|
4151 | 0 | return; |
4152 | 0 | } |
4153 | 0 |
|
4154 | 0 | WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aGlobal.Context()); |
4155 | 0 | if (!workerPrivate) { |
4156 | 0 | return; |
4157 | 0 | } |
4158 | 0 | |
4159 | 0 | RefPtr<DeprecationWarningRunnable> runnable = |
4160 | 0 | new DeprecationWarningRunnable(aOperation); |
4161 | 0 | runnable->Dispatch(workerPrivate); |
4162 | 0 | } |
4163 | | |
4164 | | namespace binding_detail { |
4165 | | JSObject* |
4166 | | UnprivilegedJunkScopeOrWorkerGlobal() |
4167 | 0 | { |
4168 | 0 | if (NS_IsMainThread()) { |
4169 | 0 | return xpc::UnprivilegedJunkScope(); |
4170 | 0 | } |
4171 | 0 | |
4172 | 0 | return GetCurrentThreadWorkerGlobal(); |
4173 | 0 | } |
4174 | | } // namespace binding_detail |
4175 | | |
4176 | | JS::Handle<JSObject*> |
4177 | | GetPerInterfaceObjectHandle(JSContext* aCx, |
4178 | | size_t aSlotId, |
4179 | | CreateInterfaceObjectsMethod aCreator, |
4180 | | bool aDefineOnGlobal) |
4181 | 4 | { |
4182 | 4 | /* Make sure our global is sane. Hopefully we can remove this sometime */ |
4183 | 4 | JSObject* global = JS::CurrentGlobalOrNull(aCx); |
4184 | 4 | if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) { |
4185 | 0 | return nullptr; |
4186 | 0 | } |
4187 | 4 | |
4188 | 4 | /* Check to see whether the interface objects are already installed */ |
4189 | 4 | ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global); |
4190 | 4 | if (!protoAndIfaceCache.HasEntryInSlot(aSlotId)) { |
4191 | 2 | JS::Rooted<JSObject*> rootedGlobal(aCx, global); |
4192 | 2 | aCreator(aCx, rootedGlobal, protoAndIfaceCache, aDefineOnGlobal); |
4193 | 2 | } |
4194 | 4 | |
4195 | 4 | /* |
4196 | 4 | * The object might _still_ be null, but that's OK. |
4197 | 4 | * |
4198 | 4 | * Calling fromMarkedLocation() is safe because protoAndIfaceCache is |
4199 | 4 | * traced by TraceProtoAndIfaceCache() and its contents are never |
4200 | 4 | * changed after they have been set. |
4201 | 4 | * |
4202 | 4 | * Calling address() avoids the read barrier that does gray unmarking, but |
4203 | 4 | * it's not possible for the object to be gray here. |
4204 | 4 | */ |
4205 | 4 | |
4206 | 4 | const JS::Heap<JSObject*>& entrySlot = |
4207 | 4 | protoAndIfaceCache.EntrySlotMustExist(aSlotId); |
4208 | 4 | MOZ_ASSERT(JS::ObjectIsNotGray(entrySlot)); |
4209 | 4 | return JS::Handle<JSObject*>::fromMarkedLocation(entrySlot.address()); |
4210 | 4 | } |
4211 | | |
4212 | | namespace binding_detail { |
4213 | | bool |
4214 | | IsGetterEnabled(JSContext* aCx, JS::Handle<JSObject*> aObj, |
4215 | | JSJitGetterOp aGetter, |
4216 | | const Prefable<const JSPropertySpec>* aAttributes) |
4217 | 0 | { |
4218 | 0 | MOZ_ASSERT(aAttributes); |
4219 | 0 | MOZ_ASSERT(aAttributes->specs); |
4220 | 0 | do { |
4221 | 0 | if (aAttributes->isEnabled(aCx, aObj)) { |
4222 | 0 | const JSPropertySpec* specs = aAttributes->specs; |
4223 | 0 | do { |
4224 | 0 | MOZ_ASSERT(specs->isAccessor()); |
4225 | 0 | if (specs->isSelfHosted()) { |
4226 | 0 | // It won't have a JSJitGetterOp. |
4227 | 0 | continue; |
4228 | 0 | } |
4229 | 0 | const JSJitInfo* info = specs->accessors.getter.native.info; |
4230 | 0 | if (!info) { |
4231 | 0 | continue; |
4232 | 0 | } |
4233 | 0 | MOZ_ASSERT(info->type() == JSJitInfo::OpType::Getter); |
4234 | 0 | if (info->getter == aGetter) { |
4235 | 0 | return true; |
4236 | 0 | } |
4237 | 0 | } while ((++specs)->name); |
4238 | 0 | } |
4239 | 0 | } while ((++aAttributes)->specs); |
4240 | 0 |
|
4241 | 0 | // Didn't find it. |
4242 | 0 | return false; |
4243 | 0 | } |
4244 | | |
4245 | | } // namespace binding_detail |
4246 | | |
4247 | | } // namespace dom |
4248 | | } // namespace mozilla |