toolkit/components/finalizationwitness/FinalizationWitnessService.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:3c3e583197ba
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include "FinalizationWitnessService.h"
6
7 #include "nsString.h"
8 #include "jsapi.h"
9 #include "js/CallNonGenericMethod.h"
10 #include "mozJSComponentLoader.h"
11 #include "nsZipArchive.h"
12
13 #include "mozilla/Scoped.h"
14 #include "mozilla/Services.h"
15 #include "mozilla/NullPtr.h"
16 #include "nsIObserverService.h"
17 #include "nsThreadUtils.h"
18
19
20 // Implementation of nsIFinalizationWitnessService
21
22 namespace mozilla {
23
24 namespace {
25
26 /**
27 * An event meant to be dispatched to the main thread upon finalization
28 * of a FinalizationWitness, unless method |forget()| has been called.
29 *
30 * Held as private data by each instance of FinalizationWitness.
31 * Important note: we maintain the invariant that these private data
32 * slots are already addrefed.
33 */
34 class FinalizationEvent MOZ_FINAL: public nsRunnable
35 {
36 public:
37 FinalizationEvent(const char* aTopic,
38 const jschar* aValue)
39 : mTopic(aTopic)
40 , mValue(aValue)
41 { }
42
43 NS_METHOD Run() {
44 nsCOMPtr<nsIObserverService> observerService =
45 mozilla::services::GetObserverService();
46 if (!observerService) {
47 // This is either too early or, more likely, too late for notifications.
48 // Bail out.
49 return NS_ERROR_NOT_AVAILABLE;
50 }
51 (void)observerService->
52 NotifyObservers(nullptr, mTopic.get(), mValue.get());
53 return NS_OK;
54 }
55 private:
56 /**
57 * The topic on which to broadcast the notification of finalization.
58 *
59 * Deallocated on the main thread.
60 */
61 const nsCString mTopic;
62
63 /**
64 * The result of converting the exception to a string.
65 *
66 * Deallocated on the main thread.
67 */
68 const nsString mValue;
69 };
70
71 enum {
72 WITNESS_SLOT_EVENT,
73 WITNESS_INSTANCES_SLOTS
74 };
75
76 /**
77 * Extract the FinalizationEvent from an instance of FinalizationWitness
78 * and clear the slot containing the FinalizationEvent.
79 */
80 already_AddRefed<FinalizationEvent>
81 ExtractFinalizationEvent(JSObject *objSelf)
82 {
83 JS::Value slotEvent = JS_GetReservedSlot(objSelf, WITNESS_SLOT_EVENT);
84 if (slotEvent.isUndefined()) {
85 // Forget() has been called
86 return nullptr;
87 }
88
89 JS_SetReservedSlot(objSelf, WITNESS_SLOT_EVENT, JS::UndefinedValue());
90
91 return dont_AddRef(static_cast<FinalizationEvent*>(slotEvent.toPrivate()));
92 }
93
94 /**
95 * Finalizer for instances of FinalizationWitness.
96 *
97 * Unless method Forget() has been called, the finalizer displays an error
98 * message.
99 */
100 void Finalize(JSFreeOp *fop, JSObject *objSelf)
101 {
102 nsRefPtr<FinalizationEvent> event = ExtractFinalizationEvent(objSelf);
103 if (event == nullptr) {
104 // Forget() has been called
105 return;
106 }
107
108 // Notify observers. Since we are executed during garbage-collection,
109 // we need to dispatch the notification to the main thread.
110 (void)NS_DispatchToMainThread(event);
111 // We may fail at dispatching to the main thread if we arrive too late
112 // during shutdown. In that case, there is not much we can do.
113 }
114
115 static const JSClass sWitnessClass = {
116 "FinalizationWitness",
117 JSCLASS_HAS_RESERVED_SLOTS(WITNESS_INSTANCES_SLOTS),
118 JS_PropertyStub /* addProperty */,
119 JS_DeletePropertyStub /* delProperty */,
120 JS_PropertyStub /* getProperty */,
121 JS_StrictPropertyStub /* setProperty */,
122 JS_EnumerateStub /* enumerate */,
123 JS_ResolveStub /* resolve */,
124 JS_ConvertStub /* convert */,
125 Finalize /* finalize */
126 };
127
128 bool IsWitness(JS::Handle<JS::Value> v)
129 {
130 return v.isObject() && JS_GetClass(&v.toObject()) == &sWitnessClass;
131 }
132
133
134 /**
135 * JS method |forget()|
136 *
137 * === JS documentation
138 *
139 * Neutralize the witness. Once this method is called, the witness will
140 * never report any error.
141 */
142 bool ForgetImpl(JSContext* cx, JS::CallArgs args)
143 {
144 if (args.length() != 0) {
145 JS_ReportError(cx, "forget() takes no arguments");
146 return false;
147 }
148 JS::Rooted<JS::Value> valSelf(cx, args.thisv());
149 JS::Rooted<JSObject*> objSelf(cx, &valSelf.toObject());
150
151 nsRefPtr<FinalizationEvent> event = ExtractFinalizationEvent(objSelf);
152 if (event == nullptr) {
153 JS_ReportError(cx, "forget() called twice");
154 return false;
155 }
156
157 args.rval().setUndefined();
158 return true;
159 }
160
161 bool Forget(JSContext *cx, unsigned argc, JS::Value *vp)
162 {
163 JS::CallArgs args = CallArgsFromVp(argc, vp);
164 return JS::CallNonGenericMethod<IsWitness, ForgetImpl>(cx, args);
165 }
166
167 static const JSFunctionSpec sWitnessClassFunctions[] = {
168 JS_FN("forget", Forget, 0, JSPROP_READONLY | JSPROP_PERMANENT),
169 JS_FS_END
170 };
171
172 }
173
174 NS_IMPL_ISUPPORTS(FinalizationWitnessService, nsIFinalizationWitnessService)
175
176 /**
177 * Create a new Finalization Witness.
178 *
179 * A finalization witness is an object whose sole role is to notify
180 * observers when it is gc-ed. Once the witness is created, call its
181 * method |forget()| to prevent the observers from being notified.
182 *
183 * @param aTopic The notification topic.
184 * @param aValue The notification value. Converted to a string.
185 *
186 * @constructor
187 */
188 NS_IMETHODIMP
189 FinalizationWitnessService::Make(const char* aTopic,
190 const char16_t* aValue,
191 JSContext* aCx,
192 JS::MutableHandle<JS::Value> aRetval)
193 {
194 JS::Rooted<JSObject*> objResult(aCx, JS_NewObject(aCx, &sWitnessClass, JS::NullPtr(),
195 JS::NullPtr()));
196 if (!objResult) {
197 return NS_ERROR_OUT_OF_MEMORY;
198 }
199 if (!JS_DefineFunctions(aCx, objResult, sWitnessClassFunctions)) {
200 return NS_ERROR_FAILURE;
201 }
202
203 nsRefPtr<FinalizationEvent> event = new FinalizationEvent(aTopic, aValue);
204
205 // Transfer ownership of the addrefed |event| to |objResult|.
206 JS_SetReservedSlot(objResult, WITNESS_SLOT_EVENT,
207 JS::PrivateValue(event.forget().take()));
208
209 aRetval.setObject(*objResult);
210 return NS_OK;
211 }
212
213 } // namespace mozilla

mercurial