From d8202594032466059ae94fe0492db15a1ede0bc2 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 Jan 2018 19:19:43 +0100 Subject: [PATCH] Bug 1425574 - Fill the feature gap between Console.jsm and Console API - part 1 - Console.createInstance(), r=smaug --- dom/bindings/Bindings.conf | 4 + dom/console/Console.cpp | 49 ++++++++---- dom/console/Console.h | 11 +++ dom/console/ConsoleInstance.cpp | 134 ++++++++++++++++++++++++++++++++ dom/console/ConsoleInstance.h | 106 +++++++++++++++++++++++++ dom/console/moz.build | 2 + dom/console/tests/console.jsm | 1 + dom/console/tests/test_jsm.xul | 10 ++- dom/webidl/Console.webidl | 47 +++++++++++ 9 files changed, 349 insertions(+), 15 deletions(-) create mode 100644 dom/console/ConsoleInstance.cpp create mode 100644 dom/console/ConsoleInstance.h diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index ac04d370a1e5..c18b627793fc 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -153,6 +153,10 @@ DOMInterfaces = { 'nativeType': 'mozilla::dom::Console', }, +'ConsoleInstance': { + 'implicitJSContext': ['clear', 'count', 'groupEnd', 'time', 'timeEnd'], +}, + 'ConvolverNode': { 'implicitJSContext': [ 'buffer' ], }, diff --git a/dom/console/Console.cpp b/dom/console/Console.cpp index cd6480e66864..3c9279a571a3 100644 --- a/dom/console/Console.cpp +++ b/dom/console/Console.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/Console.h" +#include "mozilla/dom/ConsoleInstance.h" #include "mozilla/dom/ConsoleBinding.h" #include "mozilla/dom/BlobBinding.h" @@ -950,13 +951,6 @@ METHOD(Debug, "debug") METHOD(Table, "table") METHOD(Trace, "trace") -/* static */ void -Console::Clear(const GlobalObject& aGlobal) -{ - const Sequence data; - Method(aGlobal, MethodClear, NS_LITERAL_STRING("clear"), data); -} - // Displays an interactive listing of all the properties of an object. METHOD(Dir, "dir"); METHOD(Dirxml, "dirxml"); @@ -964,6 +958,15 @@ METHOD(Dirxml, "dirxml"); METHOD(Group, "group") METHOD(GroupCollapsed, "groupCollapsed") +#undef METHOD + +/* static */ void +Console::Clear(const GlobalObject& aGlobal) +{ + const Sequence data; + Method(aGlobal, MethodClear, NS_LITERAL_STRING("clear"), data); +} + /* static */ void Console::GroupEnd(const GlobalObject& aGlobal) { @@ -987,15 +990,27 @@ Console::TimeEnd(const GlobalObject& aGlobal, const nsAString& aLabel) Console::StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel, MethodName aMethodName, const nsAString& aMethodString) { - JSContext* cx = aGlobal.Context(); + RefPtr console = GetConsole(aGlobal); + if (!console) { + return; + } - ClearException ce(cx); + console->StringMethodInternal(aGlobal.Context(), aLabel, aMethodName, + aMethodString); +} + +void +Console::StringMethodInternal(JSContext* aCx, const nsAString& aLabel, + MethodName aMethodName, + const nsAString& aMethodString) +{ + ClearException ce(aCx); Sequence data; - SequenceRooter rooter(cx, &data); + SequenceRooter rooter(aCx, &data); - JS::Rooted value(cx); - if (!dom::ToJSValue(cx, aLabel, &value)) { + JS::Rooted value(aCx); + if (!dom::ToJSValue(aCx, aLabel, &value)) { return; } @@ -1003,7 +1018,7 @@ Console::StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel, return; } - Method(aGlobal, aMethodName, aMethodString, data); + MethodInternal(aCx, aMethodName, aMethodString, data); } /* static */ void @@ -2537,5 +2552,13 @@ Console::MonotonicTimer(JSContext* aCx, MethodName aMethodName, return true; } +/* static */ already_AddRefed +Console::CreateInstance(const GlobalObject& aGlobal, + const ConsoleInstanceOptions& aOptions) +{ + RefPtr console = new ConsoleInstance(aOptions); + return console.forget(); +} + } // namespace dom } // namespace mozilla diff --git a/dom/console/Console.h b/dom/console/Console.h index daca99c30978..7b221f8ae199 100644 --- a/dom/console/Console.h +++ b/dom/console/Console.h @@ -27,9 +27,11 @@ namespace dom { class AnyCallback; class ConsoleCallData; +class ConsoleInstance; class ConsoleRunnable; class ConsoleCallDataRunnable; class ConsoleProfileRunnable; +struct ConsoleInstanceOptions; struct ConsoleTimerError; struct ConsoleStackEntry; @@ -114,6 +116,10 @@ public: static void Clear(const GlobalObject& aGlobal); + static already_AddRefed + CreateInstance(const GlobalObject& aGlobal, + const ConsoleInstanceOptions& aOptions); + void ClearStorage(); @@ -183,6 +189,10 @@ private: StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel, MethodName aMethodName, const nsAString& aMethodString); + void + StringMethodInternal(JSContext* aCx, const nsAString& aLabel, + MethodName aMethodName, const nsAString& aMethodString); + // This method must receive aCx and aArguments in the same JSCompartment. void ProcessCallData(JSContext* aCx, @@ -417,6 +427,7 @@ private: mozilla::TimeStamp mCreationTimeStamp; friend class ConsoleCallData; + friend class ConsoleInstance; friend class ConsoleRunnable; friend class ConsoleCallDataRunnable; friend class ConsoleProfileRunnable; diff --git a/dom/console/ConsoleInstance.cpp b/dom/console/ConsoleInstance.cpp new file mode 100644 index 000000000000..0c12f78da115 --- /dev/null +++ b/dom/console/ConsoleInstance.cpp @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/ConsoleInstance.h" +#include "mozilla/dom/ConsoleBinding.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ConsoleInstance, mConsole) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(ConsoleInstance) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ConsoleInstance) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ConsoleInstance) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY +NS_INTERFACE_MAP_END + +ConsoleInstance::ConsoleInstance(const ConsoleInstanceOptions& aOptions) + : mConsole(new Console(nullptr)) +{} + +ConsoleInstance::~ConsoleInstance() +{} + +JSObject* +ConsoleInstance::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return ConsoleInstanceBinding::Wrap(aCx, this, aGivenProto); +} + +#define METHOD(name, string) \ + void \ + ConsoleInstance::name(JSContext* aCx, const Sequence& aData) \ + { \ + mConsole->MethodInternal(aCx, Console::Method##name, \ + NS_LITERAL_STRING(string), aData); \ + } + +METHOD(Log, "log") +METHOD(Info, "info") +METHOD(Warn, "warn") +METHOD(Error, "error") +METHOD(Exception, "exception") +METHOD(Debug, "debug") +METHOD(Table, "table") +METHOD(Trace, "trace") +METHOD(Dir, "dir"); +METHOD(Dirxml, "dirxml"); +METHOD(Group, "group") +METHOD(GroupCollapsed, "groupCollapsed") + +#undef METHOD + +void +ConsoleInstance::GroupEnd(JSContext* aCx) +{ + const Sequence data; + mConsole->MethodInternal(aCx, Console::MethodGroupEnd, + NS_LITERAL_STRING("groupEnd"), data); +} + +void +ConsoleInstance::Time(JSContext* aCx, const nsAString& aLabel) +{ + mConsole->StringMethodInternal(aCx, aLabel, Console::MethodTime, + NS_LITERAL_STRING("time")); +} + +void +ConsoleInstance::TimeEnd(JSContext* aCx, const nsAString& aLabel) +{ + mConsole->StringMethodInternal(aCx, aLabel, Console::MethodTimeEnd, + NS_LITERAL_STRING("timeEnd")); +} + +void +ConsoleInstance::TimeStamp(JSContext* aCx, const JS::Handle aData) +{ + ClearException ce(aCx); + + Sequence data; + SequenceRooter rooter(aCx, &data); + + if (aData.isString() && !data.AppendElement(aData, fallible)) { + return; + } + + mConsole->MethodInternal(aCx, Console::MethodTimeStamp, + NS_LITERAL_STRING("timeStamp"), data); +} + +void +ConsoleInstance::Profile(JSContext* aCx, const Sequence& aData) +{ + mConsole->ProfileMethodInternal(aCx, NS_LITERAL_STRING("profile"), aData); +} + +void +ConsoleInstance::ProfileEnd(JSContext* aCx, const Sequence& aData) +{ + mConsole->ProfileMethodInternal(aCx, NS_LITERAL_STRING("profileEnd"), aData); +} + +void +ConsoleInstance::Assert(JSContext* aCx, bool aCondition, + const Sequence& aData) +{ + if (!aCondition) { + mConsole->MethodInternal(aCx, Console::MethodAssert, + NS_LITERAL_STRING("assert"), aData); + } +} + +void +ConsoleInstance::Count(JSContext* aCx, const nsAString& aLabel) +{ + mConsole->StringMethodInternal(aCx, aLabel, Console::MethodCount, + NS_LITERAL_STRING("count")); +} + +void +ConsoleInstance::Clear(JSContext* aCx) +{ + const Sequence data; + mConsole->MethodInternal(aCx, Console::MethodClear, + NS_LITERAL_STRING("clear"), data); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/console/ConsoleInstance.h b/dom/console/ConsoleInstance.h new file mode 100644 index 000000000000..03ad6ad6507c --- /dev/null +++ b/dom/console/ConsoleInstance.h @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_ConsoleInstance_h +#define mozilla_dom_ConsoleInstance_h + +#include "mozilla/dom/Console.h" + + +namespace mozilla { +namespace dom { + +class ConsoleInstance final : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ConsoleInstance) + + explicit ConsoleInstance(const ConsoleInstanceOptions& aOptions); + + // WebIDL methods + JSObject* + WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + nsPIDOMWindowInner* GetParentObject() const + { + return nullptr; + } + + void + Log(JSContext* aCx, const Sequence& aData); + + void + Info(JSContext* aCx, const Sequence& aData); + + void + Warn(JSContext* aCx, const Sequence& aData); + + void + Error(JSContext* aCx, const Sequence& aData); + + void + Exception(JSContext* aCx, const Sequence& aData); + + void + Debug(JSContext* aCx, const Sequence& aData); + + void + Table(JSContext* aCx, const Sequence& aData); + + void + Trace(JSContext* aCx, const Sequence& aData); + + void + Dir(JSContext* aCx, const Sequence& aData); + + void + Dirxml(JSContext* aCx, const Sequence& aData); + + void + Group(JSContext* aCx, const Sequence& aData); + + void + GroupCollapsed(JSContext* aCx, const Sequence& aData); + + void + GroupEnd(JSContext* aCx); + + void + Time(JSContext* aCx, const nsAString& aLabel); + + void + TimeEnd(JSContext* aCx, const nsAString& aLabel); + + void + TimeStamp(JSContext* aCx, const JS::Handle aData); + + void + Profile(JSContext* aCx, const Sequence& aData); + + void + ProfileEnd(JSContext* aCx, const Sequence& aData); + + void + Assert(JSContext* aCx, bool aCondition, const Sequence& aData); + + void + Count(JSContext* aCx, const nsAString& aLabel); + + void + Clear(JSContext* aCx); + +private: + ~ConsoleInstance(); + + RefPtr mConsole; +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_ConsoleInstance_h diff --git a/dom/console/moz.build b/dom/console/moz.build index 4612c20c2a44..ed3b0e23a086 100644 --- a/dom/console/moz.build +++ b/dom/console/moz.build @@ -23,10 +23,12 @@ EXPORTS.mozilla += [ EXPORTS.mozilla.dom += [ 'Console.h', + 'ConsoleInstance.h', ] UNIFIED_SOURCES += [ 'Console.cpp', + 'ConsoleInstance.cpp', 'ConsoleReportCollector.cpp', ] diff --git a/dom/console/tests/console.jsm b/dom/console/tests/console.jsm index b4b6fdd164ef..3f521e69a541 100644 --- a/dom/console/tests/console.jsm +++ b/dom/console/tests/console.jsm @@ -7,5 +7,6 @@ this.EXPORTED_SYMBOLS = [ "ConsoleTest" ]; this.ConsoleTest = { go: function() { console.log("Hello world!"); + console.createInstance().log("Hello world!"); } }; diff --git a/dom/console/tests/test_jsm.xul b/dom/console/tests/test_jsm.xul index ff00848e7b81..5c072edd414a 100644 --- a/dom/console/tests/test_jsm.xul +++ b/dom/console/tests/test_jsm.xul @@ -22,6 +22,8 @@ function consoleListener() { } consoleListener.prototype = { + count: 0, + observe: function(aSubject, aTopic, aData) { if (aTopic == "console-api-log-event") { var obj = aSubject.wrappedJSObject; @@ -29,8 +31,12 @@ consoleListener.prototype = { is(obj.ID, "jsm", "ID and InnerID are correctly set."); is (obj.arguments[0], "Hello world!", "Message matches"); - SpecialPowers.removeObserver(this, "console-api-log-event"); - SimpleTest.finish(); + // We want to see 2 messages, the first is generated by console.log, + // the second one from createInstance().log(); + if (++this.count == 2) { + SpecialPowers.removeObserver(this, "console-api-log-event"); + SimpleTest.finish(); + } } } } diff --git a/dom/webidl/Console.webidl b/dom/webidl/Console.webidl index 1ca53cd18387..a48a28d228e7 100644 --- a/dom/webidl/Console.webidl +++ b/dom/webidl/Console.webidl @@ -12,6 +12,10 @@ ClassString="Console", ProtoObjectHack] namespace console { + + // NOTE: if you touch this namespace, remember to update the ConsoleInstance + // interface as well! + // Logging void assert(optional boolean condition = false, any... data); void clear(); @@ -45,6 +49,9 @@ namespace console { [ChromeOnly] const boolean IS_NATIVE_CONSOLE = true; + + [ChromeOnly, NewObject] + ConsoleInstance createInstance(optional ConsoleInstanceOptions options); }; // This is used to propagate console events to the observers. @@ -109,3 +116,43 @@ dictionary ConsoleCounter { dictionary ConsoleCounterError { DOMString error = "maxCountersExceeded"; }; + +[ChromeOnly, + Exposed=(Window,Worker,WorkerDebugger,Worklet,System)] +// This is basically a copy of the console namespace. +interface ConsoleInstance { + // Logging + void assert(optional boolean condition = false, any... data); + void clear(); + void count(optional DOMString label = "default"); + void debug(any... data); + void error(any... data); + void info(any... data); + void log(any... data); + void table(any... data); // FIXME: The spec is still unclear about this. + void trace(any... data); + void warn(any... data); + void dir(any... data); // FIXME: This doesn't follow the spec yet. + void dirxml(any... data); + + // Grouping + void group(any... data); + void groupCollapsed(any... data); + void groupEnd(); + + // Timing + void time(optional DOMString label = "default"); + void timeEnd(optional DOMString label = "default"); + + // Mozilla only or Webcompat methods + + void _exception(any... data); + void timeStamp(optional any data); + + void profile(any... data); + void profileEnd(any... data); +}; + +dictionary ConsoleInstanceOptions { + // TODO +};