dom/base/Console.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 #include "mozilla/dom/Console.h"
michael@0 7 #include "mozilla/dom/ConsoleBinding.h"
michael@0 8
michael@0 9 #include "mozilla/dom/Exceptions.h"
michael@0 10 #include "mozilla/dom/ToJSValue.h"
michael@0 11 #include "mozilla/Maybe.h"
michael@0 12 #include "nsCycleCollectionParticipant.h"
michael@0 13 #include "nsDocument.h"
michael@0 14 #include "nsDOMNavigationTiming.h"
michael@0 15 #include "nsGlobalWindow.h"
michael@0 16 #include "nsJSUtils.h"
michael@0 17 #include "nsPerformance.h"
michael@0 18 #include "WorkerPrivate.h"
michael@0 19 #include "WorkerRunnable.h"
michael@0 20 #include "xpcprivate.h"
michael@0 21 #include "nsContentUtils.h"
michael@0 22
michael@0 23 #include "nsIConsoleAPIStorage.h"
michael@0 24 #include "nsIDOMWindowUtils.h"
michael@0 25 #include "nsIInterfaceRequestorUtils.h"
michael@0 26 #include "nsILoadContext.h"
michael@0 27 #include "nsIServiceManager.h"
michael@0 28 #include "nsISupportsPrimitives.h"
michael@0 29 #include "nsIWebNavigation.h"
michael@0 30
michael@0 31 // The maximum allowed number of concurrent timers per page.
michael@0 32 #define MAX_PAGE_TIMERS 10000
michael@0 33
michael@0 34 // The maximum allowed number of concurrent counters per page.
michael@0 35 #define MAX_PAGE_COUNTERS 10000
michael@0 36
michael@0 37 // The maximum stacktrace depth when populating the stacktrace array used for
michael@0 38 // console.trace().
michael@0 39 #define DEFAULT_MAX_STACKTRACE_DEPTH 200
michael@0 40
michael@0 41 // The console API methods are async and their action is executed later. This
michael@0 42 // delay tells how much later.
michael@0 43 #define CALL_DELAY 15 // milliseconds
michael@0 44
michael@0 45 // This constant tells how many messages to process in a single timer execution.
michael@0 46 #define MESSAGES_IN_INTERVAL 1500
michael@0 47
michael@0 48 // This tag is used in the Structured Clone Algorithm to move js values from
michael@0 49 // worker thread to main thread
michael@0 50 #define CONSOLE_TAG JS_SCTAG_USER_MIN
michael@0 51
michael@0 52 using namespace mozilla::dom::exceptions;
michael@0 53 using namespace mozilla::dom::workers;
michael@0 54
michael@0 55 namespace mozilla {
michael@0 56 namespace dom {
michael@0 57
michael@0 58 /**
michael@0 59 * Console API in workers uses the Structured Clone Algorithm to move any value
michael@0 60 * from the worker thread to the main-thread. Some object cannot be moved and,
michael@0 61 * in these cases, we convert them to strings.
michael@0 62 * It's not the best, but at least we are able to show something.
michael@0 63 */
michael@0 64
michael@0 65 // This method is called by the Structured Clone Algorithm when some data has
michael@0 66 // to be read.
michael@0 67 static JSObject*
michael@0 68 ConsoleStructuredCloneCallbacksRead(JSContext* aCx,
michael@0 69 JSStructuredCloneReader* /* unused */,
michael@0 70 uint32_t aTag, uint32_t aData,
michael@0 71 void* aClosure)
michael@0 72 {
michael@0 73 AssertIsOnMainThread();
michael@0 74
michael@0 75 if (aTag != CONSOLE_TAG) {
michael@0 76 return nullptr;
michael@0 77 }
michael@0 78
michael@0 79 nsTArray<nsString>* strings = static_cast<nsTArray<nsString>*>(aClosure);
michael@0 80 MOZ_ASSERT(strings->Length() > aData);
michael@0 81
michael@0 82 JS::Rooted<JS::Value> value(aCx);
michael@0 83 if (!xpc::StringToJsval(aCx, strings->ElementAt(aData), &value)) {
michael@0 84 return nullptr;
michael@0 85 }
michael@0 86
michael@0 87 JS::Rooted<JSObject*> obj(aCx);
michael@0 88 if (!JS_ValueToObject(aCx, value, &obj)) {
michael@0 89 return nullptr;
michael@0 90 }
michael@0 91
michael@0 92 return obj;
michael@0 93 }
michael@0 94
michael@0 95 // This method is called by the Structured Clone Algorithm when some data has
michael@0 96 // to be written.
michael@0 97 static bool
michael@0 98 ConsoleStructuredCloneCallbacksWrite(JSContext* aCx,
michael@0 99 JSStructuredCloneWriter* aWriter,
michael@0 100 JS::Handle<JSObject*> aObj,
michael@0 101 void* aClosure)
michael@0 102 {
michael@0 103 JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aObj));
michael@0 104 JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
michael@0 105 if (!jsString) {
michael@0 106 return false;
michael@0 107 }
michael@0 108
michael@0 109 nsDependentJSString string;
michael@0 110 if (!string.init(aCx, jsString)) {
michael@0 111 return false;
michael@0 112 }
michael@0 113
michael@0 114 nsTArray<nsString>* strings = static_cast<nsTArray<nsString>*>(aClosure);
michael@0 115
michael@0 116 if (!JS_WriteUint32Pair(aWriter, CONSOLE_TAG, strings->Length())) {
michael@0 117 return false;
michael@0 118 }
michael@0 119
michael@0 120 strings->AppendElement(string);
michael@0 121
michael@0 122 return true;
michael@0 123 }
michael@0 124
michael@0 125 static void
michael@0 126 ConsoleStructuredCloneCallbacksError(JSContext* /* aCx */,
michael@0 127 uint32_t /* aErrorId */)
michael@0 128 {
michael@0 129 NS_WARNING("Failed to clone data for the Console API in workers.");
michael@0 130 }
michael@0 131
michael@0 132 JSStructuredCloneCallbacks gConsoleCallbacks = {
michael@0 133 ConsoleStructuredCloneCallbacksRead,
michael@0 134 ConsoleStructuredCloneCallbacksWrite,
michael@0 135 ConsoleStructuredCloneCallbacksError
michael@0 136 };
michael@0 137
michael@0 138 class ConsoleCallData MOZ_FINAL : public LinkedListElement<ConsoleCallData>
michael@0 139 {
michael@0 140 public:
michael@0 141 ConsoleCallData()
michael@0 142 : mMethodName(Console::MethodLog)
michael@0 143 , mPrivate(false)
michael@0 144 , mTimeStamp(JS_Now() / PR_USEC_PER_MSEC)
michael@0 145 , mMonotonicTimer(0)
michael@0 146 {
michael@0 147 MOZ_COUNT_CTOR(ConsoleCallData);
michael@0 148 }
michael@0 149
michael@0 150 ~ConsoleCallData()
michael@0 151 {
michael@0 152 MOZ_COUNT_DTOR(ConsoleCallData);
michael@0 153 }
michael@0 154
michael@0 155 void
michael@0 156 Initialize(JSContext* aCx, Console::MethodName aName,
michael@0 157 const nsAString& aString, const Sequence<JS::Value>& aArguments)
michael@0 158 {
michael@0 159 mGlobal = JS::CurrentGlobalOrNull(aCx);
michael@0 160 mMethodName = aName;
michael@0 161 mMethodString = aString;
michael@0 162
michael@0 163 for (uint32_t i = 0; i < aArguments.Length(); ++i) {
michael@0 164 mArguments.AppendElement(aArguments[i]);
michael@0 165 }
michael@0 166 }
michael@0 167
michael@0 168 JS::Heap<JSObject*> mGlobal;
michael@0 169
michael@0 170 Console::MethodName mMethodName;
michael@0 171 bool mPrivate;
michael@0 172 int64_t mTimeStamp;
michael@0 173 DOMHighResTimeStamp mMonotonicTimer;
michael@0 174
michael@0 175 nsString mMethodString;
michael@0 176 nsTArray<JS::Heap<JS::Value>> mArguments;
michael@0 177
michael@0 178 // Stack management is complicated, because we want to do it as
michael@0 179 // lazily as possible. Therefore, we have the following behavior:
michael@0 180 // 1) mTopStackFrame is initialized whenever we have any JS on the stack
michael@0 181 // 2) mReifiedStack is initialized if we're created in a worker.
michael@0 182 // 3) mStack is set (possibly to null if there is no JS on the stack) if
michael@0 183 // we're created on main thread.
michael@0 184 Maybe<ConsoleStackEntry> mTopStackFrame;
michael@0 185 Maybe<nsTArray<ConsoleStackEntry>> mReifiedStack;
michael@0 186 nsCOMPtr<nsIStackFrame> mStack;
michael@0 187 };
michael@0 188
michael@0 189 // This class is used to clear any exception at the end of this method.
michael@0 190 class ClearException
michael@0 191 {
michael@0 192 public:
michael@0 193 ClearException(JSContext* aCx)
michael@0 194 : mCx(aCx)
michael@0 195 {
michael@0 196 }
michael@0 197
michael@0 198 ~ClearException()
michael@0 199 {
michael@0 200 JS_ClearPendingException(mCx);
michael@0 201 }
michael@0 202
michael@0 203 private:
michael@0 204 JSContext* mCx;
michael@0 205 };
michael@0 206
michael@0 207 class ConsoleRunnable : public nsRunnable
michael@0 208 {
michael@0 209 public:
michael@0 210 ConsoleRunnable()
michael@0 211 : mWorkerPrivate(GetCurrentThreadWorkerPrivate())
michael@0 212 {
michael@0 213 MOZ_ASSERT(mWorkerPrivate);
michael@0 214 }
michael@0 215
michael@0 216 virtual
michael@0 217 ~ConsoleRunnable()
michael@0 218 {
michael@0 219 }
michael@0 220
michael@0 221 bool
michael@0 222 Dispatch()
michael@0 223 {
michael@0 224 mWorkerPrivate->AssertIsOnWorkerThread();
michael@0 225
michael@0 226 JSContext* cx = mWorkerPrivate->GetJSContext();
michael@0 227
michael@0 228 if (!PreDispatch(cx)) {
michael@0 229 return false;
michael@0 230 }
michael@0 231
michael@0 232 AutoSyncLoopHolder syncLoop(mWorkerPrivate);
michael@0 233 mSyncLoopTarget = syncLoop.EventTarget();
michael@0 234
michael@0 235 if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
michael@0 236 JS_ReportError(cx,
michael@0 237 "Failed to dispatch to main thread for the Console API!");
michael@0 238 return false;
michael@0 239 }
michael@0 240
michael@0 241 return syncLoop.Run();
michael@0 242 }
michael@0 243
michael@0 244 private:
michael@0 245 NS_IMETHOD Run()
michael@0 246 {
michael@0 247 AssertIsOnMainThread();
michael@0 248
michael@0 249 RunConsole();
michael@0 250
michael@0 251 nsRefPtr<MainThreadStopSyncLoopRunnable> response =
michael@0 252 new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
michael@0 253 mSyncLoopTarget.forget(),
michael@0 254 true);
michael@0 255 if (!response->Dispatch(nullptr)) {
michael@0 256 NS_WARNING("Failed to dispatch response!");
michael@0 257 }
michael@0 258
michael@0 259 return NS_OK;
michael@0 260 }
michael@0 261
michael@0 262 protected:
michael@0 263 virtual bool
michael@0 264 PreDispatch(JSContext* aCx) = 0;
michael@0 265
michael@0 266 virtual void
michael@0 267 RunConsole() = 0;
michael@0 268
michael@0 269 WorkerPrivate* mWorkerPrivate;
michael@0 270
michael@0 271 private:
michael@0 272 nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
michael@0 273 };
michael@0 274
michael@0 275 // This runnable appends a CallData object into the Console queue running on
michael@0 276 // the main-thread.
michael@0 277 class ConsoleCallDataRunnable MOZ_FINAL : public ConsoleRunnable
michael@0 278 {
michael@0 279 public:
michael@0 280 ConsoleCallDataRunnable(ConsoleCallData* aCallData)
michael@0 281 : mCallData(aCallData)
michael@0 282 {
michael@0 283 }
michael@0 284
michael@0 285 private:
michael@0 286 bool
michael@0 287 PreDispatch(JSContext* aCx) MOZ_OVERRIDE
michael@0 288 {
michael@0 289 ClearException ce(aCx);
michael@0 290 JSAutoCompartment ac(aCx, mCallData->mGlobal);
michael@0 291
michael@0 292 JS::Rooted<JSObject*> arguments(aCx,
michael@0 293 JS_NewArrayObject(aCx, mCallData->mArguments.Length()));
michael@0 294 if (!arguments) {
michael@0 295 return false;
michael@0 296 }
michael@0 297
michael@0 298 for (uint32_t i = 0; i < mCallData->mArguments.Length(); ++i) {
michael@0 299 if (!JS_DefineElement(aCx, arguments, i, mCallData->mArguments[i],
michael@0 300 nullptr, nullptr, JSPROP_ENUMERATE)) {
michael@0 301 return false;
michael@0 302 }
michael@0 303 }
michael@0 304
michael@0 305 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
michael@0 306
michael@0 307 if (!mArguments.write(aCx, value, &gConsoleCallbacks, &mStrings)) {
michael@0 308 return false;
michael@0 309 }
michael@0 310
michael@0 311 mCallData->mArguments.Clear();
michael@0 312 mCallData->mGlobal = nullptr;
michael@0 313 return true;
michael@0 314 }
michael@0 315
michael@0 316 void
michael@0 317 RunConsole() MOZ_OVERRIDE
michael@0 318 {
michael@0 319 // Walk up to our containing page
michael@0 320 WorkerPrivate* wp = mWorkerPrivate;
michael@0 321 while (wp->GetParent()) {
michael@0 322 wp = wp->GetParent();
michael@0 323 }
michael@0 324
michael@0 325 AutoPushJSContext cx(wp->ParentJSContext());
michael@0 326 ClearException ce(cx);
michael@0 327
michael@0 328 nsPIDOMWindow* window = wp->GetWindow();
michael@0 329 NS_ENSURE_TRUE_VOID(window);
michael@0 330
michael@0 331 nsRefPtr<nsGlobalWindow> win = static_cast<nsGlobalWindow*>(window);
michael@0 332 NS_ENSURE_TRUE_VOID(win);
michael@0 333
michael@0 334 ErrorResult error;
michael@0 335 nsRefPtr<Console> console = win->GetConsole(error);
michael@0 336 if (error.Failed()) {
michael@0 337 NS_WARNING("Failed to get console from the window.");
michael@0 338 return;
michael@0 339 }
michael@0 340
michael@0 341 JS::Rooted<JS::Value> argumentsValue(cx);
michael@0 342 if (!mArguments.read(cx, &argumentsValue, &gConsoleCallbacks, &mStrings)) {
michael@0 343 return;
michael@0 344 }
michael@0 345
michael@0 346 MOZ_ASSERT(argumentsValue.isObject());
michael@0 347 JS::Rooted<JSObject*> argumentsObj(cx, &argumentsValue.toObject());
michael@0 348 MOZ_ASSERT(JS_IsArrayObject(cx, argumentsObj));
michael@0 349
michael@0 350 uint32_t length;
michael@0 351 if (!JS_GetArrayLength(cx, argumentsObj, &length)) {
michael@0 352 return;
michael@0 353 }
michael@0 354
michael@0 355 for (uint32_t i = 0; i < length; ++i) {
michael@0 356 JS::Rooted<JS::Value> value(cx);
michael@0 357
michael@0 358 if (!JS_GetElement(cx, argumentsObj, i, &value)) {
michael@0 359 return;
michael@0 360 }
michael@0 361
michael@0 362 mCallData->mArguments.AppendElement(value);
michael@0 363 }
michael@0 364
michael@0 365 MOZ_ASSERT(mCallData->mArguments.Length() == length);
michael@0 366
michael@0 367 mCallData->mGlobal = JS::CurrentGlobalOrNull(cx);
michael@0 368 console->AppendCallData(mCallData.forget());
michael@0 369 }
michael@0 370
michael@0 371 private:
michael@0 372 nsAutoPtr<ConsoleCallData> mCallData;
michael@0 373
michael@0 374 JSAutoStructuredCloneBuffer mArguments;
michael@0 375 nsTArray<nsString> mStrings;
michael@0 376 };
michael@0 377
michael@0 378 // This runnable calls ProfileMethod() on the console on the main-thread.
michael@0 379 class ConsoleProfileRunnable MOZ_FINAL : public ConsoleRunnable
michael@0 380 {
michael@0 381 public:
michael@0 382 ConsoleProfileRunnable(const nsAString& aAction,
michael@0 383 const Sequence<JS::Value>& aArguments)
michael@0 384 : mAction(aAction)
michael@0 385 , mArguments(aArguments)
michael@0 386 {
michael@0 387 }
michael@0 388
michael@0 389 private:
michael@0 390 bool
michael@0 391 PreDispatch(JSContext* aCx) MOZ_OVERRIDE
michael@0 392 {
michael@0 393 ClearException ce(aCx);
michael@0 394
michael@0 395 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
michael@0 396 if (!global) {
michael@0 397 return false;
michael@0 398 }
michael@0 399
michael@0 400 JSAutoCompartment ac(aCx, global);
michael@0 401
michael@0 402 JS::Rooted<JSObject*> arguments(aCx,
michael@0 403 JS_NewArrayObject(aCx, mArguments.Length()));
michael@0 404 if (!arguments) {
michael@0 405 return false;
michael@0 406 }
michael@0 407
michael@0 408 for (uint32_t i = 0; i < mArguments.Length(); ++i) {
michael@0 409 if (!JS_DefineElement(aCx, arguments, i, mArguments[i], nullptr, nullptr,
michael@0 410 JSPROP_ENUMERATE)) {
michael@0 411 return false;
michael@0 412 }
michael@0 413 }
michael@0 414
michael@0 415 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
michael@0 416
michael@0 417 if (!mBuffer.write(aCx, value, &gConsoleCallbacks, &mStrings)) {
michael@0 418 return false;
michael@0 419 }
michael@0 420
michael@0 421 return true;
michael@0 422 }
michael@0 423
michael@0 424 void
michael@0 425 RunConsole() MOZ_OVERRIDE
michael@0 426 {
michael@0 427 // Walk up to our containing page
michael@0 428 WorkerPrivate* wp = mWorkerPrivate;
michael@0 429 while (wp->GetParent()) {
michael@0 430 wp = wp->GetParent();
michael@0 431 }
michael@0 432
michael@0 433 AutoPushJSContext cx(wp->ParentJSContext());
michael@0 434 ClearException ce(cx);
michael@0 435
michael@0 436 JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
michael@0 437 NS_ENSURE_TRUE_VOID(global);
michael@0 438 JSAutoCompartment ac(cx, global);
michael@0 439
michael@0 440 nsPIDOMWindow* window = wp->GetWindow();
michael@0 441 NS_ENSURE_TRUE_VOID(window);
michael@0 442
michael@0 443 nsRefPtr<nsGlobalWindow> win = static_cast<nsGlobalWindow*>(window);
michael@0 444 NS_ENSURE_TRUE_VOID(win);
michael@0 445
michael@0 446 ErrorResult error;
michael@0 447 nsRefPtr<Console> console = win->GetConsole(error);
michael@0 448 if (error.Failed()) {
michael@0 449 NS_WARNING("Failed to get console from the window.");
michael@0 450 return;
michael@0 451 }
michael@0 452
michael@0 453 JS::Rooted<JS::Value> argumentsValue(cx);
michael@0 454 if (!mBuffer.read(cx, &argumentsValue, &gConsoleCallbacks, &mStrings)) {
michael@0 455 return;
michael@0 456 }
michael@0 457
michael@0 458 MOZ_ASSERT(argumentsValue.isObject());
michael@0 459 JS::Rooted<JSObject*> argumentsObj(cx, &argumentsValue.toObject());
michael@0 460 MOZ_ASSERT(JS_IsArrayObject(cx, argumentsObj));
michael@0 461
michael@0 462 uint32_t length;
michael@0 463 if (!JS_GetArrayLength(cx, argumentsObj, &length)) {
michael@0 464 return;
michael@0 465 }
michael@0 466
michael@0 467 Sequence<JS::Value> arguments;
michael@0 468
michael@0 469 for (uint32_t i = 0; i < length; ++i) {
michael@0 470 JS::Rooted<JS::Value> value(cx);
michael@0 471
michael@0 472 if (!JS_GetElement(cx, argumentsObj, i, &value)) {
michael@0 473 return;
michael@0 474 }
michael@0 475
michael@0 476 arguments.AppendElement(value);
michael@0 477 }
michael@0 478
michael@0 479 console->ProfileMethod(cx, mAction, arguments);
michael@0 480 }
michael@0 481
michael@0 482 private:
michael@0 483 nsString mAction;
michael@0 484 Sequence<JS::Value> mArguments;
michael@0 485
michael@0 486 JSAutoStructuredCloneBuffer mBuffer;
michael@0 487 nsTArray<nsString> mStrings;
michael@0 488 };
michael@0 489
michael@0 490 NS_IMPL_CYCLE_COLLECTION_CLASS(Console)
michael@0 491
michael@0 492 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Console)
michael@0 493 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
michael@0 494 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTimer)
michael@0 495 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStorage)
michael@0 496 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
michael@0 497
michael@0 498 tmp->ClearConsoleData();
michael@0 499
michael@0 500 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 501
michael@0 502 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Console)
michael@0 503 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
michael@0 504 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimer)
michael@0 505 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorage)
michael@0 506 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
michael@0 507 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 508
michael@0 509 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Console)
michael@0 510 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
michael@0 511
michael@0 512 for (ConsoleCallData* data = tmp->mQueuedCalls.getFirst(); data != nullptr;
michael@0 513 data = data->getNext()) {
michael@0 514 if (data->mGlobal) {
michael@0 515 aCallbacks.Trace(&data->mGlobal, "data->mGlobal", aClosure);
michael@0 516 }
michael@0 517
michael@0 518 for (uint32_t i = 0; i < data->mArguments.Length(); ++i) {
michael@0 519 aCallbacks.Trace(&data->mArguments[i], "data->mArguments[i]", aClosure);
michael@0 520 }
michael@0 521 }
michael@0 522
michael@0 523 NS_IMPL_CYCLE_COLLECTION_TRACE_END
michael@0 524
michael@0 525 NS_IMPL_CYCLE_COLLECTING_ADDREF(Console)
michael@0 526 NS_IMPL_CYCLE_COLLECTING_RELEASE(Console)
michael@0 527
michael@0 528 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Console)
michael@0 529 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
michael@0 530 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
michael@0 531 NS_INTERFACE_MAP_ENTRY(nsIObserver)
michael@0 532 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
michael@0 533 NS_INTERFACE_MAP_END
michael@0 534
michael@0 535 Console::Console(nsPIDOMWindow* aWindow)
michael@0 536 : mWindow(aWindow)
michael@0 537 , mOuterID(0)
michael@0 538 , mInnerID(0)
michael@0 539 {
michael@0 540 if (mWindow) {
michael@0 541 MOZ_ASSERT(mWindow->IsInnerWindow());
michael@0 542 mInnerID = mWindow->WindowID();
michael@0 543
michael@0 544 nsPIDOMWindow* outerWindow = mWindow->GetOuterWindow();
michael@0 545 MOZ_ASSERT(outerWindow);
michael@0 546 mOuterID = outerWindow->WindowID();
michael@0 547 }
michael@0 548
michael@0 549 if (NS_IsMainThread()) {
michael@0 550 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
michael@0 551 if (obs) {
michael@0 552 obs->AddObserver(this, "inner-window-destroyed", false);
michael@0 553 }
michael@0 554 }
michael@0 555
michael@0 556 SetIsDOMBinding();
michael@0 557 mozilla::HoldJSObjects(this);
michael@0 558 }
michael@0 559
michael@0 560 Console::~Console()
michael@0 561 {
michael@0 562 mozilla::DropJSObjects(this);
michael@0 563 }
michael@0 564
michael@0 565 NS_IMETHODIMP
michael@0 566 Console::Observe(nsISupports* aSubject, const char* aTopic,
michael@0 567 const char16_t* aData)
michael@0 568 {
michael@0 569 MOZ_ASSERT(NS_IsMainThread());
michael@0 570
michael@0 571 if (strcmp(aTopic, "inner-window-destroyed")) {
michael@0 572 return NS_OK;
michael@0 573 }
michael@0 574
michael@0 575 nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
michael@0 576 NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
michael@0 577
michael@0 578 uint64_t innerID;
michael@0 579 nsresult rv = wrapper->GetData(&innerID);
michael@0 580 NS_ENSURE_SUCCESS(rv, rv);
michael@0 581
michael@0 582 if (innerID == mInnerID) {
michael@0 583 nsCOMPtr<nsIObserverService> obs =
michael@0 584 do_GetService("@mozilla.org/observer-service;1");
michael@0 585 if (obs) {
michael@0 586 obs->RemoveObserver(this, "inner-window-destroyed");
michael@0 587 }
michael@0 588
michael@0 589 ClearConsoleData();
michael@0 590 mTimerRegistry.Clear();
michael@0 591
michael@0 592 if (mTimer) {
michael@0 593 mTimer->Cancel();
michael@0 594 mTimer = nullptr;
michael@0 595 }
michael@0 596 }
michael@0 597
michael@0 598 return NS_OK;
michael@0 599 }
michael@0 600
michael@0 601 JSObject*
michael@0 602 Console::WrapObject(JSContext* aCx)
michael@0 603 {
michael@0 604 return ConsoleBinding::Wrap(aCx, this);
michael@0 605 }
michael@0 606
michael@0 607 #define METHOD(name, string) \
michael@0 608 void \
michael@0 609 Console::name(JSContext* aCx, const Sequence<JS::Value>& aData) \
michael@0 610 { \
michael@0 611 Method(aCx, Method##name, NS_LITERAL_STRING(string), aData); \
michael@0 612 }
michael@0 613
michael@0 614 METHOD(Log, "log")
michael@0 615 METHOD(Info, "info")
michael@0 616 METHOD(Warn, "warn")
michael@0 617 METHOD(Error, "error")
michael@0 618 METHOD(Exception, "exception")
michael@0 619 METHOD(Debug, "debug")
michael@0 620
michael@0 621 void
michael@0 622 Console::Trace(JSContext* aCx)
michael@0 623 {
michael@0 624 const Sequence<JS::Value> data;
michael@0 625 Method(aCx, MethodTrace, NS_LITERAL_STRING("trace"), data);
michael@0 626 }
michael@0 627
michael@0 628 // Displays an interactive listing of all the properties of an object.
michael@0 629 METHOD(Dir, "dir");
michael@0 630
michael@0 631 METHOD(Group, "group")
michael@0 632 METHOD(GroupCollapsed, "groupCollapsed")
michael@0 633 METHOD(GroupEnd, "groupEnd")
michael@0 634
michael@0 635 void
michael@0 636 Console::Time(JSContext* aCx, const JS::Handle<JS::Value> aTime)
michael@0 637 {
michael@0 638 Sequence<JS::Value> data;
michael@0 639 SequenceRooter<JS::Value> rooter(aCx, &data);
michael@0 640
michael@0 641 if (!aTime.isUndefined()) {
michael@0 642 data.AppendElement(aTime);
michael@0 643 }
michael@0 644
michael@0 645 Method(aCx, MethodTime, NS_LITERAL_STRING("time"), data);
michael@0 646 }
michael@0 647
michael@0 648 void
michael@0 649 Console::TimeEnd(JSContext* aCx, const JS::Handle<JS::Value> aTime)
michael@0 650 {
michael@0 651 Sequence<JS::Value> data;
michael@0 652 SequenceRooter<JS::Value> rooter(aCx, &data);
michael@0 653
michael@0 654 if (!aTime.isUndefined()) {
michael@0 655 data.AppendElement(aTime);
michael@0 656 }
michael@0 657
michael@0 658 Method(aCx, MethodTimeEnd, NS_LITERAL_STRING("timeEnd"), data);
michael@0 659 }
michael@0 660
michael@0 661 void
michael@0 662 Console::Profile(JSContext* aCx, const Sequence<JS::Value>& aData)
michael@0 663 {
michael@0 664 ProfileMethod(aCx, NS_LITERAL_STRING("profile"), aData);
michael@0 665 }
michael@0 666
michael@0 667 void
michael@0 668 Console::ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData)
michael@0 669 {
michael@0 670 ProfileMethod(aCx, NS_LITERAL_STRING("profileEnd"), aData);
michael@0 671 }
michael@0 672
michael@0 673 void
michael@0 674 Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
michael@0 675 const Sequence<JS::Value>& aData)
michael@0 676 {
michael@0 677 if (!NS_IsMainThread()) {
michael@0 678 // Here we are in a worker thread.
michael@0 679 nsRefPtr<ConsoleProfileRunnable> runnable =
michael@0 680 new ConsoleProfileRunnable(aAction, aData);
michael@0 681 runnable->Dispatch();
michael@0 682 return;
michael@0 683 }
michael@0 684
michael@0 685 ClearException ce(aCx);
michael@0 686
michael@0 687 RootedDictionary<ConsoleProfileEvent> event(aCx);
michael@0 688 event.mAction = aAction;
michael@0 689
michael@0 690 event.mArguments.Construct();
michael@0 691 Sequence<JS::Value>& sequence = event.mArguments.Value();
michael@0 692
michael@0 693 for (uint32_t i = 0; i < aData.Length(); ++i) {
michael@0 694 sequence.AppendElement(aData[i]);
michael@0 695 }
michael@0 696
michael@0 697 JS::Rooted<JS::Value> eventValue(aCx);
michael@0 698 if (!event.ToObject(aCx, &eventValue)) {
michael@0 699 return;
michael@0 700 }
michael@0 701
michael@0 702 JS::Rooted<JSObject*> eventObj(aCx, &eventValue.toObject());
michael@0 703 MOZ_ASSERT(eventObj);
michael@0 704
michael@0 705 if (!JS_DefineProperty(aCx, eventObj, "wrappedJSObject", eventValue,
michael@0 706 JSPROP_ENUMERATE)) {
michael@0 707 return;
michael@0 708 }
michael@0 709
michael@0 710 nsXPConnect* xpc = nsXPConnect::XPConnect();
michael@0 711 nsCOMPtr<nsISupports> wrapper;
michael@0 712 const nsIID& iid = NS_GET_IID(nsISupports);
michael@0 713
michael@0 714 if (NS_FAILED(xpc->WrapJS(aCx, eventObj, iid, getter_AddRefs(wrapper)))) {
michael@0 715 return;
michael@0 716 }
michael@0 717
michael@0 718 nsCOMPtr<nsIObserverService> obs =
michael@0 719 do_GetService("@mozilla.org/observer-service;1");
michael@0 720 if (obs) {
michael@0 721 obs->NotifyObservers(wrapper, "console-api-profiler", nullptr);
michael@0 722 }
michael@0 723 }
michael@0 724
michael@0 725 void
michael@0 726 Console::Assert(JSContext* aCx, bool aCondition,
michael@0 727 const Sequence<JS::Value>& aData)
michael@0 728 {
michael@0 729 if (!aCondition) {
michael@0 730 Method(aCx, MethodAssert, NS_LITERAL_STRING("assert"), aData);
michael@0 731 }
michael@0 732 }
michael@0 733
michael@0 734 METHOD(Count, "count")
michael@0 735
michael@0 736 void
michael@0 737 Console::__noSuchMethod__()
michael@0 738 {
michael@0 739 // Nothing to do.
michael@0 740 }
michael@0 741
michael@0 742 static
michael@0 743 nsresult
michael@0 744 StackFrameToStackEntry(nsIStackFrame* aStackFrame,
michael@0 745 ConsoleStackEntry& aStackEntry,
michael@0 746 uint32_t aLanguage)
michael@0 747 {
michael@0 748 MOZ_ASSERT(aStackFrame);
michael@0 749
michael@0 750 nsresult rv = aStackFrame->GetFilename(aStackEntry.mFilename);
michael@0 751 NS_ENSURE_SUCCESS(rv, rv);
michael@0 752
michael@0 753 int32_t lineNumber;
michael@0 754 rv = aStackFrame->GetLineNumber(&lineNumber);
michael@0 755 NS_ENSURE_SUCCESS(rv, rv);
michael@0 756
michael@0 757 aStackEntry.mLineNumber = lineNumber;
michael@0 758
michael@0 759 rv = aStackFrame->GetName(aStackEntry.mFunctionName);
michael@0 760 NS_ENSURE_SUCCESS(rv, rv);
michael@0 761
michael@0 762 aStackEntry.mLanguage = aLanguage;
michael@0 763 return NS_OK;
michael@0 764 }
michael@0 765
michael@0 766 static
michael@0 767 nsresult
michael@0 768 ReifyStack(nsIStackFrame* aStack, nsTArray<ConsoleStackEntry>& aRefiedStack)
michael@0 769 {
michael@0 770 nsCOMPtr<nsIStackFrame> stack(aStack);
michael@0 771
michael@0 772 while (stack) {
michael@0 773 uint32_t language;
michael@0 774 nsresult rv = stack->GetLanguage(&language);
michael@0 775 NS_ENSURE_SUCCESS(rv, rv);
michael@0 776
michael@0 777 if (language == nsIProgrammingLanguage::JAVASCRIPT ||
michael@0 778 language == nsIProgrammingLanguage::JAVASCRIPT2) {
michael@0 779 ConsoleStackEntry& data = *aRefiedStack.AppendElement();
michael@0 780 rv = StackFrameToStackEntry(stack, data, language);
michael@0 781 NS_ENSURE_SUCCESS(rv, rv);
michael@0 782 }
michael@0 783
michael@0 784 nsCOMPtr<nsIStackFrame> caller;
michael@0 785 rv = stack->GetCaller(getter_AddRefs(caller));
michael@0 786 NS_ENSURE_SUCCESS(rv, rv);
michael@0 787
michael@0 788 stack.swap(caller);
michael@0 789 }
michael@0 790
michael@0 791 return NS_OK;
michael@0 792 }
michael@0 793
michael@0 794 // Queue a call to a console method. See the CALL_DELAY constant.
michael@0 795 void
michael@0 796 Console::Method(JSContext* aCx, MethodName aMethodName,
michael@0 797 const nsAString& aMethodString,
michael@0 798 const Sequence<JS::Value>& aData)
michael@0 799 {
michael@0 800 // This RAII class removes the last element of the mQueuedCalls if something
michael@0 801 // goes wrong.
michael@0 802 class RAII {
michael@0 803 public:
michael@0 804 RAII(LinkedList<ConsoleCallData>& aList)
michael@0 805 : mList(aList)
michael@0 806 , mUnfinished(true)
michael@0 807 {
michael@0 808 }
michael@0 809
michael@0 810 ~RAII()
michael@0 811 {
michael@0 812 if (mUnfinished) {
michael@0 813 ConsoleCallData* data = mList.popLast();
michael@0 814 MOZ_ASSERT(data);
michael@0 815 delete data;
michael@0 816 }
michael@0 817 }
michael@0 818
michael@0 819 void
michael@0 820 Finished()
michael@0 821 {
michael@0 822 mUnfinished = false;
michael@0 823 }
michael@0 824
michael@0 825 private:
michael@0 826 LinkedList<ConsoleCallData>& mList;
michael@0 827 bool mUnfinished;
michael@0 828 };
michael@0 829
michael@0 830 ConsoleCallData* callData = new ConsoleCallData();
michael@0 831 mQueuedCalls.insertBack(callData);
michael@0 832
michael@0 833 ClearException ce(aCx);
michael@0 834
michael@0 835 callData->Initialize(aCx, aMethodName, aMethodString, aData);
michael@0 836 RAII raii(mQueuedCalls);
michael@0 837
michael@0 838 if (mWindow) {
michael@0 839 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
michael@0 840 if (!webNav) {
michael@0 841 return;
michael@0 842 }
michael@0 843
michael@0 844 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
michael@0 845 MOZ_ASSERT(loadContext);
michael@0 846
michael@0 847 loadContext->GetUsePrivateBrowsing(&callData->mPrivate);
michael@0 848 }
michael@0 849
michael@0 850 uint32_t maxDepth = ShouldIncludeStackrace(aMethodName) ?
michael@0 851 DEFAULT_MAX_STACKTRACE_DEPTH : 1;
michael@0 852 nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, maxDepth);
michael@0 853
michael@0 854 if (!stack) {
michael@0 855 return;
michael@0 856 }
michael@0 857
michael@0 858 // Walk up to the first JS stack frame and save it if we find it.
michael@0 859 do {
michael@0 860 uint32_t language;
michael@0 861 nsresult rv = stack->GetLanguage(&language);
michael@0 862 if (NS_FAILED(rv)) {
michael@0 863 return;
michael@0 864 }
michael@0 865
michael@0 866 if (language == nsIProgrammingLanguage::JAVASCRIPT ||
michael@0 867 language == nsIProgrammingLanguage::JAVASCRIPT2) {
michael@0 868 callData->mTopStackFrame.construct();
michael@0 869 nsresult rv = StackFrameToStackEntry(stack,
michael@0 870 callData->mTopStackFrame.ref(),
michael@0 871 language);
michael@0 872 if (NS_FAILED(rv)) {
michael@0 873 return;
michael@0 874 }
michael@0 875
michael@0 876 break;
michael@0 877 }
michael@0 878
michael@0 879 nsCOMPtr<nsIStackFrame> caller;
michael@0 880 rv = stack->GetCaller(getter_AddRefs(caller));
michael@0 881 if (NS_FAILED(rv)) {
michael@0 882 return;
michael@0 883 }
michael@0 884
michael@0 885 stack.swap(caller);
michael@0 886 } while (stack);
michael@0 887
michael@0 888 if (NS_IsMainThread()) {
michael@0 889 callData->mStack = stack;
michael@0 890 } else {
michael@0 891 // nsIStackFrame is not threadsafe, so we need to snapshot it now,
michael@0 892 // before we post our runnable to the main thread.
michael@0 893 callData->mReifiedStack.construct();
michael@0 894 nsresult rv = ReifyStack(stack, callData->mReifiedStack.ref());
michael@0 895 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 896 return;
michael@0 897 }
michael@0 898 }
michael@0 899
michael@0 900 // Monotonic timer for 'time' and 'timeEnd'
michael@0 901 if ((aMethodName == MethodTime || aMethodName == MethodTimeEnd)) {
michael@0 902 if (mWindow) {
michael@0 903 nsGlobalWindow *win = static_cast<nsGlobalWindow*>(mWindow.get());
michael@0 904 MOZ_ASSERT(win);
michael@0 905
michael@0 906 ErrorResult rv;
michael@0 907 nsRefPtr<nsPerformance> performance = win->GetPerformance(rv);
michael@0 908 if (rv.Failed()) {
michael@0 909 return;
michael@0 910 }
michael@0 911
michael@0 912 callData->mMonotonicTimer = performance->Now();
michael@0 913 } else {
michael@0 914 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
michael@0 915 MOZ_ASSERT(workerPrivate);
michael@0 916
michael@0 917 TimeDuration duration =
michael@0 918 mozilla::TimeStamp::Now() - workerPrivate->CreationTimeStamp();
michael@0 919
michael@0 920 callData->mMonotonicTimer = duration.ToMilliseconds();
michael@0 921 }
michael@0 922 }
michael@0 923
michael@0 924 // The operation is completed. RAII class has to be disabled.
michael@0 925 raii.Finished();
michael@0 926
michael@0 927 if (!NS_IsMainThread()) {
michael@0 928 // Here we are in a worker thread. The ConsoleCallData has to been removed
michael@0 929 // from the list and it will be deleted by the ConsoleCallDataRunnable or
michael@0 930 // by the Main-Thread Console object.
michael@0 931 mQueuedCalls.popLast();
michael@0 932
michael@0 933 nsRefPtr<ConsoleCallDataRunnable> runnable =
michael@0 934 new ConsoleCallDataRunnable(callData);
michael@0 935 runnable->Dispatch();
michael@0 936 return;
michael@0 937 }
michael@0 938
michael@0 939 if (!mTimer) {
michael@0 940 mTimer = do_CreateInstance("@mozilla.org/timer;1");
michael@0 941 mTimer->InitWithCallback(this, CALL_DELAY,
michael@0 942 nsITimer::TYPE_REPEATING_SLACK);
michael@0 943 }
michael@0 944 }
michael@0 945
michael@0 946 void
michael@0 947 Console::AppendCallData(ConsoleCallData* aCallData)
michael@0 948 {
michael@0 949 mQueuedCalls.insertBack(aCallData);
michael@0 950
michael@0 951 if (!mTimer) {
michael@0 952 mTimer = do_CreateInstance("@mozilla.org/timer;1");
michael@0 953 mTimer->InitWithCallback(this, CALL_DELAY,
michael@0 954 nsITimer::TYPE_REPEATING_SLACK);
michael@0 955 }
michael@0 956 }
michael@0 957
michael@0 958 // Timer callback used to process each of the queued calls.
michael@0 959 NS_IMETHODIMP
michael@0 960 Console::Notify(nsITimer *timer)
michael@0 961 {
michael@0 962 MOZ_ASSERT(!mQueuedCalls.isEmpty());
michael@0 963
michael@0 964 for (uint32_t i = 0; i < MESSAGES_IN_INTERVAL; ++i) {
michael@0 965 ConsoleCallData* data = mQueuedCalls.popFirst();
michael@0 966 if (!data) {
michael@0 967 break;
michael@0 968 }
michael@0 969
michael@0 970 ProcessCallData(data);
michael@0 971 delete data;
michael@0 972 }
michael@0 973
michael@0 974 if (mQueuedCalls.isEmpty() && mTimer) {
michael@0 975 mTimer->Cancel();
michael@0 976 mTimer = nullptr;
michael@0 977 }
michael@0 978
michael@0 979 return NS_OK;
michael@0 980 }
michael@0 981
michael@0 982 // We store information to lazily compute the stack in the reserved slots of
michael@0 983 // LazyStackGetter. The first slot always stores a JS object: it's either the
michael@0 984 // JS wrapper of the nsIStackFrame or the actual reified stack representation.
michael@0 985 // The second slot is a PrivateValue() holding an nsIStackFrame* when we haven't
michael@0 986 // reified the stack yet, or an UndefinedValue() otherwise.
michael@0 987 enum {
michael@0 988 SLOT_STACKOBJ,
michael@0 989 SLOT_RAW_STACK
michael@0 990 };
michael@0 991
michael@0 992 bool
michael@0 993 LazyStackGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
michael@0 994 {
michael@0 995 JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
michael@0 996 JS::Rooted<JSObject*> callee(aCx, &args.callee());
michael@0 997
michael@0 998 JS::Value v = js::GetFunctionNativeReserved(&args.callee(), SLOT_RAW_STACK);
michael@0 999 if (v.isUndefined()) {
michael@0 1000 // Already reified.
michael@0 1001 args.rval().set(js::GetFunctionNativeReserved(callee, SLOT_STACKOBJ));
michael@0 1002 return true;
michael@0 1003 }
michael@0 1004
michael@0 1005 nsIStackFrame* stack = reinterpret_cast<nsIStackFrame*>(v.toPrivate());
michael@0 1006 nsTArray<ConsoleStackEntry> reifiedStack;
michael@0 1007 nsresult rv = ReifyStack(stack, reifiedStack);
michael@0 1008 if (NS_FAILED(rv)) {
michael@0 1009 Throw(aCx, rv);
michael@0 1010 return false;
michael@0 1011 }
michael@0 1012
michael@0 1013 JS::Rooted<JS::Value> stackVal(aCx);
michael@0 1014 if (!ToJSValue(aCx, reifiedStack, &stackVal)) {
michael@0 1015 return false;
michael@0 1016 }
michael@0 1017
michael@0 1018 MOZ_ASSERT(stackVal.isObject());
michael@0 1019
michael@0 1020 js::SetFunctionNativeReserved(callee, SLOT_STACKOBJ, stackVal);
michael@0 1021 js::SetFunctionNativeReserved(callee, SLOT_RAW_STACK, JS::UndefinedValue());
michael@0 1022
michael@0 1023 args.rval().set(stackVal);
michael@0 1024 return true;
michael@0 1025 }
michael@0 1026
michael@0 1027 void
michael@0 1028 Console::ProcessCallData(ConsoleCallData* aData)
michael@0 1029 {
michael@0 1030 MOZ_ASSERT(aData);
michael@0 1031 MOZ_ASSERT(NS_IsMainThread());
michael@0 1032
michael@0 1033 ConsoleStackEntry frame;
michael@0 1034 if (!aData->mTopStackFrame.empty()) {
michael@0 1035 frame = aData->mTopStackFrame.ref();
michael@0 1036 }
michael@0 1037
michael@0 1038 AutoSafeJSContext cx;
michael@0 1039 ClearException ce(cx);
michael@0 1040 RootedDictionary<ConsoleEvent> event(cx);
michael@0 1041
michael@0 1042 JSAutoCompartment ac(cx, aData->mGlobal);
michael@0 1043
michael@0 1044 event.mID.Construct();
michael@0 1045 event.mInnerID.Construct();
michael@0 1046 if (mWindow) {
michael@0 1047 event.mID.Value().SetAsUnsignedLong() = mOuterID;
michael@0 1048 event.mInnerID.Value().SetAsUnsignedLong() = mInnerID;
michael@0 1049 } else {
michael@0 1050 // If we are in a JSM, the window doesn't exist.
michael@0 1051 event.mID.Value().SetAsString() = NS_LITERAL_STRING("jsm");
michael@0 1052 event.mInnerID.Value().SetAsString() = frame.mFilename;
michael@0 1053 }
michael@0 1054
michael@0 1055 event.mLevel = aData->mMethodString;
michael@0 1056 event.mFilename = frame.mFilename;
michael@0 1057 event.mLineNumber = frame.mLineNumber;
michael@0 1058 event.mFunctionName = frame.mFunctionName;
michael@0 1059 event.mTimeStamp = aData->mTimeStamp;
michael@0 1060 event.mPrivate = aData->mPrivate;
michael@0 1061
michael@0 1062 switch (aData->mMethodName) {
michael@0 1063 case MethodLog:
michael@0 1064 case MethodInfo:
michael@0 1065 case MethodWarn:
michael@0 1066 case MethodError:
michael@0 1067 case MethodException:
michael@0 1068 case MethodDebug:
michael@0 1069 case MethodAssert:
michael@0 1070 event.mArguments.Construct();
michael@0 1071 event.mStyles.Construct();
michael@0 1072 ProcessArguments(cx, aData->mArguments, event.mArguments.Value(),
michael@0 1073 event.mStyles.Value());
michael@0 1074 break;
michael@0 1075
michael@0 1076 default:
michael@0 1077 event.mArguments.Construct();
michael@0 1078 ArgumentsToValueList(aData->mArguments, event.mArguments.Value());
michael@0 1079 }
michael@0 1080
michael@0 1081 if (aData->mMethodName == MethodGroup ||
michael@0 1082 aData->mMethodName == MethodGroupCollapsed ||
michael@0 1083 aData->mMethodName == MethodGroupEnd) {
michael@0 1084 ComposeGroupName(cx, aData->mArguments, event.mGroupName);
michael@0 1085 }
michael@0 1086
michael@0 1087 else if (aData->mMethodName == MethodTime && !aData->mArguments.IsEmpty()) {
michael@0 1088 event.mTimer = StartTimer(cx, aData->mArguments[0], aData->mMonotonicTimer);
michael@0 1089 }
michael@0 1090
michael@0 1091 else if (aData->mMethodName == MethodTimeEnd && !aData->mArguments.IsEmpty()) {
michael@0 1092 event.mTimer = StopTimer(cx, aData->mArguments[0], aData->mMonotonicTimer);
michael@0 1093 }
michael@0 1094
michael@0 1095 else if (aData->mMethodName == MethodCount) {
michael@0 1096 event.mCounter = IncreaseCounter(cx, frame, aData->mArguments);
michael@0 1097 }
michael@0 1098
michael@0 1099 // We want to create a console event object and pass it to our
michael@0 1100 // nsIConsoleAPIStorage implementation. We want to define some accessor
michael@0 1101 // properties on this object, and those will need to keep an nsIStackFrame
michael@0 1102 // alive. But nsIStackFrame cannot be wrapped in an untrusted scope. And
michael@0 1103 // further, passing untrusted objects to system code is likely to run afoul of
michael@0 1104 // Object Xrays. So we want to wrap in a system-principal scope here. But
michael@0 1105 // which one? We could cheat and try to get the underlying JSObject* of
michael@0 1106 // mStorage, but that's a bit fragile. Instead, we just use the junk scope,
michael@0 1107 // with explicit permission from the XPConnect module owner. If you're
michael@0 1108 // tempted to do that anywhere else, talk to said module owner first.
michael@0 1109 JSAutoCompartment ac2(cx, xpc::GetJunkScope());
michael@0 1110
michael@0 1111 JS::Rooted<JS::Value> eventValue(cx);
michael@0 1112 if (!event.ToObject(cx, &eventValue)) {
michael@0 1113 return;
michael@0 1114 }
michael@0 1115
michael@0 1116 JS::Rooted<JSObject*> eventObj(cx, &eventValue.toObject());
michael@0 1117 MOZ_ASSERT(eventObj);
michael@0 1118
michael@0 1119 if (!JS_DefineProperty(cx, eventObj, "wrappedJSObject", eventValue, JSPROP_ENUMERATE)) {
michael@0 1120 return;
michael@0 1121 }
michael@0 1122
michael@0 1123 if (ShouldIncludeStackrace(aData->mMethodName)) {
michael@0 1124 // Now define the "stacktrace" property on eventObj. There are two cases
michael@0 1125 // here. Either we came from a worker and have a reified stack, or we want
michael@0 1126 // to define a getter that will lazily reify the stack.
michael@0 1127 if (!aData->mReifiedStack.empty()) {
michael@0 1128 JS::Rooted<JS::Value> stacktrace(cx);
michael@0 1129 if (!ToJSValue(cx, aData->mReifiedStack.ref(), &stacktrace) ||
michael@0 1130 !JS_DefineProperty(cx, eventObj, "stacktrace", stacktrace,
michael@0 1131 JSPROP_ENUMERATE)) {
michael@0 1132 return;
michael@0 1133 }
michael@0 1134 } else {
michael@0 1135 JSFunction* fun = js::NewFunctionWithReserved(cx, LazyStackGetter, 0, 0,
michael@0 1136 eventObj, "stacktrace");
michael@0 1137 if (!fun) {
michael@0 1138 return;
michael@0 1139 }
michael@0 1140
michael@0 1141 JS::Rooted<JSObject*> funObj(cx, JS_GetFunctionObject(fun));
michael@0 1142
michael@0 1143 // We want to store our stack in the function and have it stay alive. But
michael@0 1144 // we also need sane access to the C++ nsIStackFrame. So store both a JS
michael@0 1145 // wrapper and the raw pointer: the former will keep the latter alive.
michael@0 1146 JS::Rooted<JS::Value> stackVal(cx);
michael@0 1147 nsresult rv = nsContentUtils::WrapNative(cx, aData->mStack,
michael@0 1148 &stackVal);
michael@0 1149 if (NS_FAILED(rv)) {
michael@0 1150 return;
michael@0 1151 }
michael@0 1152
michael@0 1153 js::SetFunctionNativeReserved(funObj, SLOT_STACKOBJ, stackVal);
michael@0 1154 js::SetFunctionNativeReserved(funObj, SLOT_RAW_STACK,
michael@0 1155 JS::PrivateValue(aData->mStack.get()));
michael@0 1156
michael@0 1157 if (!JS_DefineProperty(cx, eventObj, "stacktrace",
michael@0 1158 JS::UndefinedHandleValue,
michael@0 1159 JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER |
michael@0 1160 JSPROP_SETTER,
michael@0 1161 JS_DATA_TO_FUNC_PTR(JSPropertyOp, funObj.get()),
michael@0 1162 nullptr)) {
michael@0 1163 return;
michael@0 1164 }
michael@0 1165 }
michael@0 1166 }
michael@0 1167
michael@0 1168 if (!mStorage) {
michael@0 1169 mStorage = do_GetService("@mozilla.org/consoleAPI-storage;1");
michael@0 1170 }
michael@0 1171
michael@0 1172 if (!mStorage) {
michael@0 1173 NS_WARNING("Failed to get the ConsoleAPIStorage service.");
michael@0 1174 return;
michael@0 1175 }
michael@0 1176
michael@0 1177 nsAutoString innerID;
michael@0 1178 innerID.AppendInt(mInnerID);
michael@0 1179
michael@0 1180 if (NS_FAILED(mStorage->RecordEvent(innerID, eventValue))) {
michael@0 1181 NS_WARNING("Failed to record a console event.");
michael@0 1182 }
michael@0 1183
michael@0 1184 nsXPConnect* xpc = nsXPConnect::XPConnect();
michael@0 1185 nsCOMPtr<nsISupports> wrapper;
michael@0 1186 const nsIID& iid = NS_GET_IID(nsISupports);
michael@0 1187
michael@0 1188 if (NS_FAILED(xpc->WrapJS(cx, eventObj, iid, getter_AddRefs(wrapper)))) {
michael@0 1189 return;
michael@0 1190 }
michael@0 1191
michael@0 1192 nsCOMPtr<nsIObserverService> obs =
michael@0 1193 do_GetService("@mozilla.org/observer-service;1");
michael@0 1194 if (obs) {
michael@0 1195 nsAutoString outerID;
michael@0 1196 outerID.AppendInt(mOuterID);
michael@0 1197
michael@0 1198 obs->NotifyObservers(wrapper, "console-api-log-event", outerID.get());
michael@0 1199 }
michael@0 1200 }
michael@0 1201
michael@0 1202 void
michael@0 1203 Console::ProcessArguments(JSContext* aCx,
michael@0 1204 const nsTArray<JS::Heap<JS::Value>>& aData,
michael@0 1205 Sequence<JS::Value>& aSequence,
michael@0 1206 Sequence<JS::Value>& aStyles)
michael@0 1207 {
michael@0 1208 if (aData.IsEmpty()) {
michael@0 1209 return;
michael@0 1210 }
michael@0 1211
michael@0 1212 if (aData.Length() == 1 || !aData[0].isString()) {
michael@0 1213 ArgumentsToValueList(aData, aSequence);
michael@0 1214 return;
michael@0 1215 }
michael@0 1216
michael@0 1217 JS::Rooted<JS::Value> format(aCx, aData[0]);
michael@0 1218 JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, format));
michael@0 1219 if (!jsString) {
michael@0 1220 return;
michael@0 1221 }
michael@0 1222
michael@0 1223 nsDependentJSString string;
michael@0 1224 if (!string.init(aCx, jsString)) {
michael@0 1225 return;
michael@0 1226 }
michael@0 1227
michael@0 1228 nsString::const_iterator start, end;
michael@0 1229 string.BeginReading(start);
michael@0 1230 string.EndReading(end);
michael@0 1231
michael@0 1232 nsString output;
michael@0 1233 uint32_t index = 1;
michael@0 1234
michael@0 1235 while (start != end) {
michael@0 1236 if (*start != '%') {
michael@0 1237 output.Append(*start);
michael@0 1238 ++start;
michael@0 1239 continue;
michael@0 1240 }
michael@0 1241
michael@0 1242 ++start;
michael@0 1243 if (start == end) {
michael@0 1244 output.Append('%');
michael@0 1245 break;
michael@0 1246 }
michael@0 1247
michael@0 1248 if (*start == '%') {
michael@0 1249 output.Append(*start);
michael@0 1250 ++start;
michael@0 1251 continue;
michael@0 1252 }
michael@0 1253
michael@0 1254 nsAutoString tmp;
michael@0 1255 tmp.Append('%');
michael@0 1256
michael@0 1257 int32_t integer = -1;
michael@0 1258 int32_t mantissa = -1;
michael@0 1259
michael@0 1260 // Let's parse %<number>.<number> for %d and %f
michael@0 1261 if (*start >= '0' && *start <= '9') {
michael@0 1262 integer = 0;
michael@0 1263
michael@0 1264 do {
michael@0 1265 integer = integer * 10 + *start - '0';
michael@0 1266 tmp.Append(*start);
michael@0 1267 ++start;
michael@0 1268 } while (*start >= '0' && *start <= '9' && start != end);
michael@0 1269 }
michael@0 1270
michael@0 1271 if (start == end) {
michael@0 1272 output.Append(tmp);
michael@0 1273 break;
michael@0 1274 }
michael@0 1275
michael@0 1276 if (*start == '.') {
michael@0 1277 tmp.Append(*start);
michael@0 1278 ++start;
michael@0 1279
michael@0 1280 if (start == end) {
michael@0 1281 output.Append(tmp);
michael@0 1282 break;
michael@0 1283 }
michael@0 1284
michael@0 1285 // '.' must be followed by a number.
michael@0 1286 if (*start < '0' || *start > '9') {
michael@0 1287 output.Append(tmp);
michael@0 1288 continue;
michael@0 1289 }
michael@0 1290
michael@0 1291 mantissa = 0;
michael@0 1292
michael@0 1293 do {
michael@0 1294 mantissa = mantissa * 10 + *start - '0';
michael@0 1295 tmp.Append(*start);
michael@0 1296 ++start;
michael@0 1297 } while (*start >= '0' && *start <= '9' && start != end);
michael@0 1298
michael@0 1299 if (start == end) {
michael@0 1300 output.Append(tmp);
michael@0 1301 break;
michael@0 1302 }
michael@0 1303 }
michael@0 1304
michael@0 1305 char ch = *start;
michael@0 1306 tmp.Append(ch);
michael@0 1307 ++start;
michael@0 1308
michael@0 1309 switch (ch) {
michael@0 1310 case 'o':
michael@0 1311 case 'O':
michael@0 1312 {
michael@0 1313 if (!output.IsEmpty()) {
michael@0 1314 JS::Rooted<JSString*> str(aCx, JS_NewUCStringCopyN(aCx,
michael@0 1315 output.get(),
michael@0 1316 output.Length()));
michael@0 1317 if (!str) {
michael@0 1318 return;
michael@0 1319 }
michael@0 1320
michael@0 1321 aSequence.AppendElement(JS::StringValue(str));
michael@0 1322 output.Truncate();
michael@0 1323 }
michael@0 1324
michael@0 1325 JS::Rooted<JS::Value> v(aCx);
michael@0 1326 if (index < aData.Length()) {
michael@0 1327 v = aData[index++];
michael@0 1328 }
michael@0 1329
michael@0 1330 aSequence.AppendElement(v);
michael@0 1331 break;
michael@0 1332 }
michael@0 1333
michael@0 1334 case 'c':
michael@0 1335 {
michael@0 1336 if (!output.IsEmpty()) {
michael@0 1337 JS::Rooted<JSString*> str(aCx, JS_NewUCStringCopyN(aCx,
michael@0 1338 output.get(),
michael@0 1339 output.Length()));
michael@0 1340 if (!str) {
michael@0 1341 return;
michael@0 1342 }
michael@0 1343
michael@0 1344 aSequence.AppendElement(JS::StringValue(str));
michael@0 1345 output.Truncate();
michael@0 1346 }
michael@0 1347
michael@0 1348 if (index < aData.Length()) {
michael@0 1349 JS::Rooted<JS::Value> v(aCx, aData[index++]);
michael@0 1350 JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, v));
michael@0 1351 if (!jsString) {
michael@0 1352 return;
michael@0 1353 }
michael@0 1354
michael@0 1355 int32_t diff = aSequence.Length() - aStyles.Length();
michael@0 1356 if (diff > 0) {
michael@0 1357 for (int32_t i = 0; i < diff; i++) {
michael@0 1358 aStyles.AppendElement(JS::NullValue());
michael@0 1359 }
michael@0 1360 }
michael@0 1361 aStyles.AppendElement(JS::StringValue(jsString));
michael@0 1362 }
michael@0 1363 break;
michael@0 1364 }
michael@0 1365
michael@0 1366 case 's':
michael@0 1367 if (index < aData.Length()) {
michael@0 1368 JS::Rooted<JS::Value> value(aCx, aData[index++]);
michael@0 1369 JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
michael@0 1370 if (!jsString) {
michael@0 1371 return;
michael@0 1372 }
michael@0 1373
michael@0 1374 nsDependentJSString v;
michael@0 1375 if (!v.init(aCx, jsString)) {
michael@0 1376 return;
michael@0 1377 }
michael@0 1378
michael@0 1379 output.Append(v);
michael@0 1380 }
michael@0 1381 break;
michael@0 1382
michael@0 1383 case 'd':
michael@0 1384 case 'i':
michael@0 1385 if (index < aData.Length()) {
michael@0 1386 JS::Rooted<JS::Value> value(aCx, aData[index++]);
michael@0 1387
michael@0 1388 int32_t v;
michael@0 1389 if (!JS::ToInt32(aCx, value, &v)) {
michael@0 1390 return;
michael@0 1391 }
michael@0 1392
michael@0 1393 nsCString format;
michael@0 1394 MakeFormatString(format, integer, mantissa, 'd');
michael@0 1395 output.AppendPrintf(format.get(), v);
michael@0 1396 }
michael@0 1397 break;
michael@0 1398
michael@0 1399 case 'f':
michael@0 1400 if (index < aData.Length()) {
michael@0 1401 JS::Rooted<JS::Value> value(aCx, aData[index++]);
michael@0 1402
michael@0 1403 double v;
michael@0 1404 if (!JS::ToNumber(aCx, value, &v)) {
michael@0 1405 return;
michael@0 1406 }
michael@0 1407
michael@0 1408 nsCString format;
michael@0 1409 MakeFormatString(format, integer, mantissa, 'f');
michael@0 1410 output.AppendPrintf(format.get(), v);
michael@0 1411 }
michael@0 1412 break;
michael@0 1413
michael@0 1414 default:
michael@0 1415 output.Append(tmp);
michael@0 1416 break;
michael@0 1417 }
michael@0 1418 }
michael@0 1419
michael@0 1420 if (!output.IsEmpty()) {
michael@0 1421 JS::Rooted<JSString*> str(aCx, JS_NewUCStringCopyN(aCx, output.get(),
michael@0 1422 output.Length()));
michael@0 1423 if (!str) {
michael@0 1424 return;
michael@0 1425 }
michael@0 1426
michael@0 1427 aSequence.AppendElement(JS::StringValue(str));
michael@0 1428 }
michael@0 1429
michael@0 1430 // The rest of the array, if unused by the format string.
michael@0 1431 for (; index < aData.Length(); ++index) {
michael@0 1432 aSequence.AppendElement(aData[index]);
michael@0 1433 }
michael@0 1434 }
michael@0 1435
michael@0 1436 void
michael@0 1437 Console::MakeFormatString(nsCString& aFormat, int32_t aInteger,
michael@0 1438 int32_t aMantissa, char aCh)
michael@0 1439 {
michael@0 1440 aFormat.Append("%");
michael@0 1441 if (aInteger >= 0) {
michael@0 1442 aFormat.AppendInt(aInteger);
michael@0 1443 }
michael@0 1444
michael@0 1445 if (aMantissa >= 0) {
michael@0 1446 aFormat.Append(".");
michael@0 1447 aFormat.AppendInt(aMantissa);
michael@0 1448 }
michael@0 1449
michael@0 1450 aFormat.Append(aCh);
michael@0 1451 }
michael@0 1452
michael@0 1453 void
michael@0 1454 Console::ComposeGroupName(JSContext* aCx,
michael@0 1455 const nsTArray<JS::Heap<JS::Value>>& aData,
michael@0 1456 nsAString& aName)
michael@0 1457 {
michael@0 1458 for (uint32_t i = 0; i < aData.Length(); ++i) {
michael@0 1459 if (i != 0) {
michael@0 1460 aName.AppendASCII(" ");
michael@0 1461 }
michael@0 1462
michael@0 1463 JS::Rooted<JS::Value> value(aCx, aData[i]);
michael@0 1464 JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
michael@0 1465 if (!jsString) {
michael@0 1466 return;
michael@0 1467 }
michael@0 1468
michael@0 1469 nsDependentJSString string;
michael@0 1470 if (!string.init(aCx, jsString)) {
michael@0 1471 return;
michael@0 1472 }
michael@0 1473
michael@0 1474 aName.Append(string);
michael@0 1475 }
michael@0 1476 }
michael@0 1477
michael@0 1478 JS::Value
michael@0 1479 Console::StartTimer(JSContext* aCx, const JS::Value& aName,
michael@0 1480 DOMHighResTimeStamp aTimestamp)
michael@0 1481 {
michael@0 1482 if (mTimerRegistry.Count() >= MAX_PAGE_TIMERS) {
michael@0 1483 RootedDictionary<ConsoleTimerError> error(aCx);
michael@0 1484
michael@0 1485 JS::Rooted<JS::Value> value(aCx);
michael@0 1486 if (!error.ToObject(aCx, &value)) {
michael@0 1487 return JS::UndefinedValue();
michael@0 1488 }
michael@0 1489
michael@0 1490 return value;
michael@0 1491 }
michael@0 1492
michael@0 1493 RootedDictionary<ConsoleTimerStart> timer(aCx);
michael@0 1494
michael@0 1495 JS::Rooted<JS::Value> name(aCx, aName);
michael@0 1496 JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, name));
michael@0 1497 if (!jsString) {
michael@0 1498 return JS::UndefinedValue();
michael@0 1499 }
michael@0 1500
michael@0 1501 nsDependentJSString key;
michael@0 1502 if (!key.init(aCx, jsString)) {
michael@0 1503 return JS::UndefinedValue();
michael@0 1504 }
michael@0 1505
michael@0 1506 timer.mName = key;
michael@0 1507
michael@0 1508 DOMHighResTimeStamp entry;
michael@0 1509 if (!mTimerRegistry.Get(key, &entry)) {
michael@0 1510 mTimerRegistry.Put(key, aTimestamp);
michael@0 1511 } else {
michael@0 1512 aTimestamp = entry;
michael@0 1513 }
michael@0 1514
michael@0 1515 timer.mStarted = aTimestamp;
michael@0 1516
michael@0 1517 JS::Rooted<JS::Value> value(aCx);
michael@0 1518 if (!timer.ToObject(aCx, &value)) {
michael@0 1519 return JS::UndefinedValue();
michael@0 1520 }
michael@0 1521
michael@0 1522 return value;
michael@0 1523 }
michael@0 1524
michael@0 1525 JS::Value
michael@0 1526 Console::StopTimer(JSContext* aCx, const JS::Value& aName,
michael@0 1527 DOMHighResTimeStamp aTimestamp)
michael@0 1528 {
michael@0 1529 JS::Rooted<JS::Value> name(aCx, aName);
michael@0 1530 JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, name));
michael@0 1531 if (!jsString) {
michael@0 1532 return JS::UndefinedValue();
michael@0 1533 }
michael@0 1534
michael@0 1535 nsDependentJSString key;
michael@0 1536 if (!key.init(aCx, jsString)) {
michael@0 1537 return JS::UndefinedValue();
michael@0 1538 }
michael@0 1539
michael@0 1540 DOMHighResTimeStamp entry;
michael@0 1541 if (!mTimerRegistry.Get(key, &entry)) {
michael@0 1542 return JS::UndefinedValue();
michael@0 1543 }
michael@0 1544
michael@0 1545 mTimerRegistry.Remove(key);
michael@0 1546
michael@0 1547 RootedDictionary<ConsoleTimerEnd> timer(aCx);
michael@0 1548 timer.mName = key;
michael@0 1549 timer.mDuration = aTimestamp - entry;
michael@0 1550
michael@0 1551 JS::Rooted<JS::Value> value(aCx);
michael@0 1552 if (!timer.ToObject(aCx, &value)) {
michael@0 1553 return JS::UndefinedValue();
michael@0 1554 }
michael@0 1555
michael@0 1556 return value;
michael@0 1557 }
michael@0 1558
michael@0 1559 void
michael@0 1560 Console::ArgumentsToValueList(const nsTArray<JS::Heap<JS::Value>>& aData,
michael@0 1561 Sequence<JS::Value>& aSequence)
michael@0 1562 {
michael@0 1563 for (uint32_t i = 0; i < aData.Length(); ++i) {
michael@0 1564 aSequence.AppendElement(aData[i]);
michael@0 1565 }
michael@0 1566 }
michael@0 1567
michael@0 1568 JS::Value
michael@0 1569 Console::IncreaseCounter(JSContext* aCx, const ConsoleStackEntry& aFrame,
michael@0 1570 const nsTArray<JS::Heap<JS::Value>>& aArguments)
michael@0 1571 {
michael@0 1572 ClearException ce(aCx);
michael@0 1573
michael@0 1574 nsAutoString key;
michael@0 1575 nsAutoString label;
michael@0 1576
michael@0 1577 if (!aArguments.IsEmpty()) {
michael@0 1578 JS::Rooted<JS::Value> labelValue(aCx, aArguments[0]);
michael@0 1579 JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, labelValue));
michael@0 1580
michael@0 1581 nsDependentJSString string;
michael@0 1582 if (jsString && string.init(aCx, jsString)) {
michael@0 1583 label = string;
michael@0 1584 key = string;
michael@0 1585 }
michael@0 1586 }
michael@0 1587
michael@0 1588 if (key.IsEmpty()) {
michael@0 1589 key.Append(aFrame.mFilename);
michael@0 1590 key.Append(NS_LITERAL_STRING(":"));
michael@0 1591 key.AppendInt(aFrame.mLineNumber);
michael@0 1592 }
michael@0 1593
michael@0 1594 uint32_t count = 0;
michael@0 1595 if (!mCounterRegistry.Get(key, &count)) {
michael@0 1596 if (mCounterRegistry.Count() >= MAX_PAGE_COUNTERS) {
michael@0 1597 RootedDictionary<ConsoleCounterError> error(aCx);
michael@0 1598
michael@0 1599 JS::Rooted<JS::Value> value(aCx);
michael@0 1600 if (!error.ToObject(aCx, &value)) {
michael@0 1601 return JS::UndefinedValue();
michael@0 1602 }
michael@0 1603
michael@0 1604 return value;
michael@0 1605 }
michael@0 1606 }
michael@0 1607
michael@0 1608 ++count;
michael@0 1609 mCounterRegistry.Put(key, count);
michael@0 1610
michael@0 1611 RootedDictionary<ConsoleCounter> data(aCx);
michael@0 1612 data.mLabel = label;
michael@0 1613 data.mCount = count;
michael@0 1614
michael@0 1615 JS::Rooted<JS::Value> value(aCx);
michael@0 1616 if (!data.ToObject(aCx, &value)) {
michael@0 1617 return JS::UndefinedValue();
michael@0 1618 }
michael@0 1619
michael@0 1620 return value;
michael@0 1621 }
michael@0 1622
michael@0 1623 void
michael@0 1624 Console::ClearConsoleData()
michael@0 1625 {
michael@0 1626 while (ConsoleCallData* data = mQueuedCalls.popFirst()) {
michael@0 1627 delete data;
michael@0 1628 }
michael@0 1629 }
michael@0 1630
michael@0 1631 bool
michael@0 1632 Console::ShouldIncludeStackrace(MethodName aMethodName)
michael@0 1633 {
michael@0 1634 switch (aMethodName) {
michael@0 1635 case MethodError:
michael@0 1636 case MethodException:
michael@0 1637 case MethodAssert:
michael@0 1638 case MethodTrace:
michael@0 1639 return true;
michael@0 1640 default:
michael@0 1641 return false;
michael@0 1642 }
michael@0 1643 }
michael@0 1644
michael@0 1645 } // namespace dom
michael@0 1646 } // namespace mozilla

mercurial