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.

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

mercurial