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