Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsJSUtils.h"
6 #include "nsString.h"
7 #include "nsIServiceManager.h"
8 #include "nsIScriptSecurityManager.h"
9 #include "nsIScriptContext.h"
10 #include "nsIScriptGlobalObject.h"
11 #include "nsIXPConnect.h"
12 #include "nsIMutableArray.h"
13 #include "nsVariant.h"
14 #include "nsIDOMBeforeUnloadEvent.h"
15 #include "nsGkAtoms.h"
16 #include "xpcpublic.h"
17 #include "nsJSEnvironment.h"
18 #include "nsDOMJSUtils.h"
19 #include "WorkerPrivate.h"
20 #include "mozilla/ContentEvents.h"
21 #include "mozilla/CycleCollectedJSRuntime.h"
22 #include "mozilla/HoldDropJSObjects.h"
23 #include "mozilla/JSEventHandler.h"
24 #include "mozilla/Likely.h"
25 #include "mozilla/dom/ErrorEvent.h"
26 #include "mozilla/dom/UnionTypes.h"
28 namespace mozilla {
30 using namespace dom;
32 JSEventHandler::JSEventHandler(nsISupports* aTarget,
33 nsIAtom* aType,
34 const TypedEventHandler& aTypedHandler)
35 : mEventName(aType)
36 , mTypedHandler(aTypedHandler)
37 {
38 nsCOMPtr<nsISupports> base = do_QueryInterface(aTarget);
39 mTarget = base.get();
40 // Note, we call HoldJSObjects to get CanSkip called before CC.
41 HoldJSObjects(this);
42 }
44 JSEventHandler::~JSEventHandler()
45 {
46 NS_ASSERTION(!mTarget, "Should have called Disconnect()!");
47 DropJSObjects(this);
48 }
50 NS_IMPL_CYCLE_COLLECTION_CLASS(JSEventHandler)
52 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(JSEventHandler)
53 tmp->mTypedHandler.ForgetHandler();
54 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
55 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(JSEventHandler)
56 if (MOZ_UNLIKELY(cb.WantDebugInfo()) && tmp->mEventName) {
57 nsAutoCString name;
58 name.AppendLiteral("JSEventHandler handlerName=");
59 name.Append(
60 NS_ConvertUTF16toUTF8(nsDependentAtomString(tmp->mEventName)).get());
61 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name.get());
62 } else {
63 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(JSEventHandler, tmp->mRefCnt.get())
64 }
65 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mTypedHandler.Ptr())
66 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
67 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
69 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(JSEventHandler)
70 if (tmp->IsBlackForCC()) {
71 return true;
72 }
73 // If we have a target, it is the one which has tmp as onfoo handler.
74 if (tmp->mTarget) {
75 nsXPCOMCycleCollectionParticipant* cp = nullptr;
76 CallQueryInterface(tmp->mTarget, &cp);
77 nsISupports* canonical = nullptr;
78 tmp->mTarget->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
79 reinterpret_cast<void**>(&canonical));
80 // Usually CanSkip ends up unmarking the event listeners of mTarget,
81 // so tmp may become black.
82 if (cp && canonical && cp->CanSkip(canonical, true)) {
83 return tmp->IsBlackForCC();
84 }
85 }
86 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
88 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(JSEventHandler)
89 return tmp->IsBlackForCC();
90 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
92 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(JSEventHandler)
93 return tmp->IsBlackForCC();
94 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
96 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSEventHandler)
97 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
98 NS_INTERFACE_MAP_ENTRY(nsISupports)
99 NS_INTERFACE_MAP_ENTRY(JSEventHandler)
100 NS_INTERFACE_MAP_END
102 NS_IMPL_CYCLE_COLLECTING_ADDREF(JSEventHandler)
103 NS_IMPL_CYCLE_COLLECTING_RELEASE(JSEventHandler)
105 bool
106 JSEventHandler::IsBlackForCC()
107 {
108 // We can claim to be black if all the things we reference are
109 // effectively black already.
110 return !mTypedHandler.HasEventHandler() ||
111 !mTypedHandler.Ptr()->HasGrayCallable();
112 }
114 nsresult
115 JSEventHandler::HandleEvent(nsIDOMEvent* aEvent)
116 {
117 nsCOMPtr<EventTarget> target = do_QueryInterface(mTarget);
118 if (!target || !mTypedHandler.HasEventHandler() ||
119 !GetTypedEventHandler().Ptr()->CallbackPreserveColor()) {
120 return NS_ERROR_FAILURE;
121 }
123 Event* event = aEvent->InternalDOMEvent();
124 bool isMainThread = event->IsMainThreadEvent();
125 bool isChromeHandler =
126 isMainThread ?
127 nsContentUtils::GetObjectPrincipal(
128 GetTypedEventHandler().Ptr()->CallbackPreserveColor()) ==
129 nsContentUtils::GetSystemPrincipal() :
130 mozilla::dom::workers::IsCurrentThreadRunningChromeWorker();
132 if (mTypedHandler.Type() == TypedEventHandler::eOnError) {
133 MOZ_ASSERT_IF(mEventName, mEventName == nsGkAtoms::onerror);
135 nsString errorMsg, file;
136 EventOrString msgOrEvent;
137 Optional<nsAString> fileName;
138 Optional<uint32_t> lineNumber;
139 Optional<uint32_t> columnNumber;
140 Optional<JS::Handle<JS::Value>> error;
142 NS_ENSURE_TRUE(aEvent, NS_ERROR_UNEXPECTED);
143 ErrorEvent* scriptEvent = aEvent->InternalDOMEvent()->AsErrorEvent();
144 if (scriptEvent) {
145 scriptEvent->GetMessage(errorMsg);
146 msgOrEvent.SetAsString().SetData(errorMsg.Data(), errorMsg.Length());
148 scriptEvent->GetFilename(file);
149 fileName = &file;
151 lineNumber.Construct();
152 lineNumber.Value() = scriptEvent->Lineno();
154 columnNumber.Construct();
155 columnNumber.Value() = scriptEvent->Colno();
157 ThreadsafeAutoJSContext cx;
158 error.Construct(cx);
159 scriptEvent->GetError(cx, &error.Value());
160 } else {
161 msgOrEvent.SetAsEvent() = aEvent->InternalDOMEvent();
162 }
164 nsRefPtr<OnErrorEventHandlerNonNull> handler =
165 mTypedHandler.OnErrorEventHandler();
166 ErrorResult rv;
167 bool handled = handler->Call(mTarget, msgOrEvent, fileName, lineNumber,
168 columnNumber, error, rv);
169 if (rv.Failed()) {
170 return rv.ErrorCode();
171 }
173 if (handled) {
174 event->PreventDefaultInternal(isChromeHandler);
175 }
176 return NS_OK;
177 }
179 if (mTypedHandler.Type() == TypedEventHandler::eOnBeforeUnload) {
180 MOZ_ASSERT(mEventName == nsGkAtoms::onbeforeunload);
182 nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handler =
183 mTypedHandler.OnBeforeUnloadEventHandler();
184 ErrorResult rv;
185 nsString retval;
186 handler->Call(mTarget, *(aEvent->InternalDOMEvent()), retval, rv);
187 if (rv.Failed()) {
188 return rv.ErrorCode();
189 }
191 nsCOMPtr<nsIDOMBeforeUnloadEvent> beforeUnload = do_QueryInterface(aEvent);
192 NS_ENSURE_STATE(beforeUnload);
194 if (!DOMStringIsNull(retval)) {
195 event->PreventDefaultInternal(isChromeHandler);
197 nsAutoString text;
198 beforeUnload->GetReturnValue(text);
200 // Set the text in the beforeUnload event as long as it wasn't
201 // already set (through event.returnValue, which takes
202 // precedence over a value returned from a JS function in IE)
203 if (text.IsEmpty()) {
204 beforeUnload->SetReturnValue(retval);
205 }
206 }
208 return NS_OK;
209 }
211 MOZ_ASSERT(mTypedHandler.Type() == TypedEventHandler::eNormal);
212 ErrorResult rv;
213 nsRefPtr<EventHandlerNonNull> handler = mTypedHandler.NormalEventHandler();
214 JS::Rooted<JS::Value> retval(CycleCollectedJSRuntime::Get()->Runtime());
215 handler->Call(mTarget, *(aEvent->InternalDOMEvent()), &retval, rv);
216 if (rv.Failed()) {
217 return rv.ErrorCode();
218 }
220 // If the handler returned false and its sense is not reversed,
221 // or the handler returned true and its sense is reversed from
222 // the usual (false means cancel), then prevent default.
223 if (retval.isBoolean() &&
224 retval.toBoolean() == (mEventName == nsGkAtoms::onerror ||
225 mEventName == nsGkAtoms::onmouseover)) {
226 event->PreventDefaultInternal(isChromeHandler);
227 }
229 return NS_OK;
230 }
232 } // namespace mozilla
234 using namespace mozilla;
236 /*
237 * Factory functions
238 */
240 nsresult
241 NS_NewJSEventHandler(nsISupports* aTarget,
242 nsIAtom* aEventType,
243 const TypedEventHandler& aTypedHandler,
244 JSEventHandler** aReturn)
245 {
246 NS_ENSURE_ARG(aEventType || !NS_IsMainThread());
247 JSEventHandler* it =
248 new JSEventHandler(aTarget, aEventType, aTypedHandler);
249 NS_ADDREF(*aReturn = it);
251 return NS_OK;
252 }