Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/. */
6 /*
7 * Maintains a circular buffer of recent messages, and notifies
8 * listeners when new messages are logged.
9 */
11 /* Threadsafe. */
13 #include "nsMemory.h"
14 #include "nsCOMArray.h"
15 #include "nsThreadUtils.h"
17 #include "nsConsoleService.h"
18 #include "nsConsoleMessage.h"
19 #include "nsIClassInfoImpl.h"
20 #include "nsIConsoleListener.h"
21 #include "nsPrintfCString.h"
23 #include "mozilla/Preferences.h"
25 #if defined(ANDROID)
26 #include <android/log.h>
27 #endif
28 #ifdef XP_WIN
29 #include <windows.h>
30 #endif
32 using namespace mozilla;
34 NS_IMPL_ADDREF(nsConsoleService)
35 NS_IMPL_RELEASE(nsConsoleService)
36 NS_IMPL_CLASSINFO(nsConsoleService, nullptr, nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON, NS_CONSOLESERVICE_CID)
37 NS_IMPL_QUERY_INTERFACE_CI(nsConsoleService, nsIConsoleService)
38 NS_IMPL_CI_INTERFACE_GETTER(nsConsoleService, nsIConsoleService)
40 static bool sLoggingEnabled = true;
41 static bool sLoggingBuffered = true;
43 nsConsoleService::nsConsoleService()
44 : mMessages(nullptr)
45 , mCurrent(0)
46 , mFull(false)
47 , mDeliveringMessage(false)
48 , mLock("nsConsoleService.mLock")
49 {
50 // XXX grab this from a pref!
51 // hm, but worry about circularity, bc we want to be able to report
52 // prefs errs...
53 mBufferSize = 250;
54 }
56 nsConsoleService::~nsConsoleService()
57 {
58 uint32_t i = 0;
59 while (i < mBufferSize && mMessages[i] != nullptr) {
60 NS_RELEASE(mMessages[i]);
61 i++;
62 }
64 if (mMessages)
65 nsMemory::Free(mMessages);
66 }
68 class AddConsolePrefWatchers : public nsRunnable
69 {
70 public:
71 AddConsolePrefWatchers(nsConsoleService* aConsole) : mConsole(aConsole) {}
73 NS_IMETHOD Run()
74 {
75 Preferences::AddBoolVarCache(&sLoggingEnabled, "consoleservice.enabled", true);
76 Preferences::AddBoolVarCache(&sLoggingBuffered, "consoleservice.buffered", true);
77 if (!sLoggingBuffered) {
78 mConsole->Reset();
79 }
80 return NS_OK;
81 }
83 private:
84 nsRefPtr<nsConsoleService> mConsole;
85 };
87 nsresult
88 nsConsoleService::Init()
89 {
90 mMessages = (nsIConsoleMessage **)
91 nsMemory::Alloc(mBufferSize * sizeof(nsIConsoleMessage *));
92 if (!mMessages)
93 return NS_ERROR_OUT_OF_MEMORY;
95 // Array elements should be 0 initially for circular buffer algorithm.
96 memset(mMessages, 0, mBufferSize * sizeof(nsIConsoleMessage *));
98 NS_DispatchToMainThread(new AddConsolePrefWatchers(this));
100 return NS_OK;
101 }
103 namespace {
105 class LogMessageRunnable : public nsRunnable
106 {
107 public:
108 LogMessageRunnable(nsIConsoleMessage* message, nsConsoleService* service)
109 : mMessage(message)
110 , mService(service)
111 { }
113 NS_DECL_NSIRUNNABLE
115 private:
116 nsCOMPtr<nsIConsoleMessage> mMessage;
117 nsRefPtr<nsConsoleService> mService;
118 };
120 typedef nsCOMArray<nsIConsoleListener> ListenerArrayType;
122 PLDHashOperator
123 CollectCurrentListeners(nsISupports* aKey, nsIConsoleListener* aValue,
124 void* closure)
125 {
126 ListenerArrayType* listeners = static_cast<ListenerArrayType*>(closure);
127 listeners->AppendObject(aValue);
128 return PL_DHASH_NEXT;
129 }
131 NS_IMETHODIMP
132 LogMessageRunnable::Run()
133 {
134 MOZ_ASSERT(NS_IsMainThread());
136 // Snapshot of listeners so that we don't reenter this hash during
137 // enumeration.
138 nsCOMArray<nsIConsoleListener> listeners;
139 mService->EnumerateListeners(CollectCurrentListeners, &listeners);
141 mService->SetIsDelivering();
143 for (int32_t i = 0; i < listeners.Count(); ++i)
144 listeners[i]->Observe(mMessage);
146 mService->SetDoneDelivering();
148 return NS_OK;
149 }
151 } // anonymous namespace
153 // nsIConsoleService methods
154 NS_IMETHODIMP
155 nsConsoleService::LogMessage(nsIConsoleMessage *message)
156 {
157 return LogMessageWithMode(message, OutputToLog);
158 }
160 nsresult
161 nsConsoleService::LogMessageWithMode(nsIConsoleMessage *message, nsConsoleService::OutputMode outputMode)
162 {
163 if (message == nullptr)
164 return NS_ERROR_INVALID_ARG;
166 if (!sLoggingEnabled) {
167 return NS_OK;
168 }
170 if (NS_IsMainThread() && mDeliveringMessage) {
171 nsCString msg;
172 message->ToString(msg);
173 NS_WARNING(nsPrintfCString("Reentrancy error: some client attempted "
174 "to display a message to the console while in a console listener. "
175 "The following message was discarded: \"%s\"", msg.get()).get());
176 return NS_ERROR_FAILURE;
177 }
179 nsRefPtr<LogMessageRunnable> r;
180 nsIConsoleMessage *retiredMessage;
182 if (sLoggingBuffered) {
183 NS_ADDREF(message); // early, in case it's same as replaced below.
184 }
186 /*
187 * Lock while updating buffer, and while taking snapshot of
188 * listeners array.
189 */
190 {
191 MutexAutoLock lock(mLock);
193 #if defined(ANDROID)
194 if (outputMode == OutputToLog)
195 {
196 nsCString msg;
197 message->ToString(msg);
198 __android_log_print(ANDROID_LOG_ERROR, "GeckoConsole",
199 "%s", msg.get());
200 }
201 #endif
202 #ifdef XP_WIN
203 if (IsDebuggerPresent()) {
204 nsString msg;
205 message->GetMessageMoz(getter_Copies(msg));
206 msg.AppendLiteral("\n");
207 OutputDebugStringW(msg.get());
208 }
209 #endif
211 /*
212 * If there's already a message in the slot we're about to replace,
213 * we've wrapped around, and we need to release the old message. We
214 * save a pointer to it, so we can release below outside the lock.
215 */
216 retiredMessage = mMessages[mCurrent];
218 if (sLoggingBuffered) {
219 mMessages[mCurrent++] = message;
220 if (mCurrent == mBufferSize) {
221 mCurrent = 0; // wrap around.
222 mFull = true;
223 }
224 }
226 if (mListeners.Count() > 0) {
227 r = new LogMessageRunnable(message, this);
228 }
229 }
231 if (retiredMessage != nullptr)
232 NS_RELEASE(retiredMessage);
234 if (r)
235 NS_DispatchToMainThread(r);
237 return NS_OK;
238 }
240 void
241 nsConsoleService::EnumerateListeners(ListenerHash::EnumReadFunction aFunction,
242 void* aClosure)
243 {
244 MutexAutoLock lock(mLock);
245 mListeners.EnumerateRead(aFunction, aClosure);
246 }
248 NS_IMETHODIMP
249 nsConsoleService::LogStringMessage(const char16_t *message)
250 {
251 if (!sLoggingEnabled) {
252 return NS_OK;
253 }
255 nsRefPtr<nsConsoleMessage> msg(new nsConsoleMessage(message));
256 return this->LogMessage(msg);
257 }
259 NS_IMETHODIMP
260 nsConsoleService::GetMessageArray(uint32_t *count, nsIConsoleMessage ***messages)
261 {
262 nsIConsoleMessage **messageArray;
264 /*
265 * Lock the whole method, as we don't want anyone mucking with mCurrent or
266 * mFull while we're copying out the buffer.
267 */
268 MutexAutoLock lock(mLock);
270 if (mCurrent == 0 && !mFull) {
271 /*
272 * Make a 1-length output array so that nobody gets confused,
273 * and return a count of 0. This should result in a 0-length
274 * array object when called from script.
275 */
276 messageArray = (nsIConsoleMessage **)
277 nsMemory::Alloc(sizeof (nsIConsoleMessage *));
278 *messageArray = nullptr;
279 *messages = messageArray;
280 *count = 0;
282 return NS_OK;
283 }
285 uint32_t resultSize = mFull ? mBufferSize : mCurrent;
286 messageArray =
287 (nsIConsoleMessage **)nsMemory::Alloc((sizeof (nsIConsoleMessage *))
288 * resultSize);
290 if (messageArray == nullptr) {
291 *messages = nullptr;
292 *count = 0;
293 return NS_ERROR_FAILURE;
294 }
296 uint32_t i;
297 if (mFull) {
298 for (i = 0; i < mBufferSize; i++) {
299 // if full, fill the buffer starting from mCurrent (which'll be
300 // oldest) wrapping around the buffer to the most recent.
301 messageArray[i] = mMessages[(mCurrent + i) % mBufferSize];
302 NS_ADDREF(messageArray[i]);
303 }
304 } else {
305 for (i = 0; i < mCurrent; i++) {
306 messageArray[i] = mMessages[i];
307 NS_ADDREF(messageArray[i]);
308 }
309 }
310 *count = resultSize;
311 *messages = messageArray;
313 return NS_OK;
314 }
316 NS_IMETHODIMP
317 nsConsoleService::RegisterListener(nsIConsoleListener *listener)
318 {
319 if (!NS_IsMainThread()) {
320 NS_ERROR("nsConsoleService::RegisterListener is main thread only.");
321 return NS_ERROR_NOT_SAME_THREAD;
322 }
324 nsCOMPtr<nsISupports> canonical = do_QueryInterface(listener);
326 MutexAutoLock lock(mLock);
327 if (mListeners.GetWeak(canonical)) {
328 // Reregistering a listener isn't good
329 return NS_ERROR_FAILURE;
330 }
331 mListeners.Put(canonical, listener);
332 return NS_OK;
333 }
335 NS_IMETHODIMP
336 nsConsoleService::UnregisterListener(nsIConsoleListener *listener)
337 {
338 if (!NS_IsMainThread()) {
339 NS_ERROR("nsConsoleService::UnregisterListener is main thread only.");
340 return NS_ERROR_NOT_SAME_THREAD;
341 }
343 nsCOMPtr<nsISupports> canonical = do_QueryInterface(listener);
345 MutexAutoLock lock(mLock);
347 if (!mListeners.GetWeak(canonical)) {
348 // Unregistering a listener that was never registered?
349 return NS_ERROR_FAILURE;
350 }
351 mListeners.Remove(canonical);
352 return NS_OK;
353 }
355 NS_IMETHODIMP
356 nsConsoleService::Reset()
357 {
358 /*
359 * Make sure nobody trips into the buffer while it's being reset
360 */
361 MutexAutoLock lock(mLock);
363 mCurrent = 0;
364 mFull = false;
366 /*
367 * Free all messages stored so far (cf. destructor)
368 */
369 for (uint32_t i = 0; i < mBufferSize && mMessages[i] != nullptr; i++)
370 NS_RELEASE(mMessages[i]);
372 return NS_OK;
373 }