import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

2440
dom/console/Console.cpp Normal file

File diff suppressed because it is too large Load diff

404
dom/console/Console.h Normal file
View file

@ -0,0 +1,404 @@
/* -*- 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_Console_h
#define mozilla_dom_Console_h
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/JSObjectHolder.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "nsDOMNavigationTiming.h"
#include "nsPIDOMWindow.h"
class nsIConsoleAPIStorage;
class nsIPrincipal;
namespace mozilla {
namespace dom {
class AnyCallback;
class ConsoleCallData;
class ConsoleRunnable;
class ConsoleCallDataRunnable;
class ConsoleProfileRunnable;
struct ConsoleStackEntry;
class Console final : public nsIObserver
, public nsSupportsWeakReference
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Console, nsIObserver)
NS_DECL_NSIOBSERVER
static already_AddRefed<Console>
Create(nsPIDOMWindowInner* aWindow, ErrorResult& aRv);
// WebIDL methods
nsPIDOMWindowInner* GetParentObject() const
{
return mWindow;
}
static void
Log(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
Info(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
Warn(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
Error(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
Exception(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
Debug(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
Table(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
Trace(const GlobalObject& aGlobal);
static void
Dir(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
Dirxml(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
Group(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
GroupCollapsed(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
GroupEnd(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
Time(const GlobalObject& aGlobal, const JS::Handle<JS::Value> aTime);
static void
TimeEnd(const GlobalObject& aGlobal, const JS::Handle<JS::Value> aTime);
static void
TimeStamp(const GlobalObject& aGlobal, const JS::Handle<JS::Value> aData);
static void
Profile(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
ProfileEnd(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
Assert(const GlobalObject& aGlobal, bool aCondition,
const Sequence<JS::Value>& aData);
static void
Count(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
Clear(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
static void
NoopMethod(const GlobalObject& aGlobal);
void
ClearStorage();
void
RetrieveConsoleEvents(JSContext* aCx, nsTArray<JS::Value>& aEvents,
ErrorResult& aRv);
void
SetConsoleEventHandler(AnyCallback* aHandler);
private:
explicit Console(nsPIDOMWindowInner* aWindow);
~Console();
void
Initialize(ErrorResult& aRv);
void
Shutdown();
enum MethodName
{
MethodLog,
MethodInfo,
MethodWarn,
MethodError,
MethodException,
MethodDebug,
MethodTable,
MethodTrace,
MethodDir,
MethodDirxml,
MethodGroup,
MethodGroupCollapsed,
MethodGroupEnd,
MethodTime,
MethodTimeEnd,
MethodTimeStamp,
MethodAssert,
MethodCount,
MethodClear
};
static already_AddRefed<Console>
GetConsole(const GlobalObject& aGlobal);
static Console*
GetConsoleInternal(const GlobalObject& aGlobal, ErrorResult &aRv);
static void
ProfileMethod(const GlobalObject& aGlobal, const nsAString& aAction,
const Sequence<JS::Value>& aData);
void
ProfileMethodInternal(JSContext* aCx, const nsAString& aAction,
const Sequence<JS::Value>& aData);
static void
Method(const GlobalObject& aGlobal, MethodName aName,
const nsAString& aString, const Sequence<JS::Value>& aData);
void
MethodInternal(JSContext* aCx, MethodName aName,
const nsAString& aString, const Sequence<JS::Value>& aData);
// This method must receive aCx and aArguments in the same JSCompartment.
void
ProcessCallData(JSContext* aCx,
ConsoleCallData* aData,
const Sequence<JS::Value>& aArguments);
void
StoreCallData(ConsoleCallData* aData);
void
UnstoreCallData(ConsoleCallData* aData);
// Read in Console.cpp how this method is used.
void
ReleaseCallData(ConsoleCallData* aCallData);
// aCx and aArguments must be in the same JS compartment.
void
NotifyHandler(JSContext* aCx,
const Sequence<JS::Value>& aArguments,
ConsoleCallData* aData) const;
// PopulateConsoleNotificationInTheTargetScope receives aCx and aArguments in
// the same JS compartment and populates the ConsoleEvent object (aValue) in
// the aTargetScope.
// aTargetScope can be:
// - the system-principal scope when we want to dispatch the ConsoleEvent to
// nsIConsoleAPIStorage (See the comment in Console.cpp about the use of
// xpc::PrivilegedJunkScope()
// - the mConsoleEventNotifier->Callable() scope when we want to notify this
// handler about a new ConsoleEvent.
// - It can be the global from the JSContext when RetrieveConsoleEvents is
// called.
bool
PopulateConsoleNotificationInTheTargetScope(JSContext* aCx,
const Sequence<JS::Value>& aArguments,
JSObject* aTargetScope,
JS::MutableHandle<JS::Value> aValue,
ConsoleCallData* aData) const;
// If the first JS::Value of the array is a string, this method uses it to
// format a string. The supported sequences are:
// %s - string
// %d,%i - integer
// %f - double
// %o,%O - a JS object.
// %c - style string.
// The output is an array where any object is a separated item, the rest is
// unified in a format string.
// Example if the input is:
// "string: %s, integer: %d, object: %o, double: %d", 's', 1, window, 0.9
// The output will be:
// [ "string: s, integer: 1, object: ", window, ", double: 0.9" ]
//
// The aStyles array is populated with the style strings that the function
// finds based the format string. The index of the styles matches the indexes
// of elements that need the custom styling from aSequence. For elements with
// no custom styling the array is padded with null elements.
bool
ProcessArguments(JSContext* aCx, const Sequence<JS::Value>& aData,
Sequence<JS::Value>& aSequence,
Sequence<nsString>& aStyles) const;
void
MakeFormatString(nsCString& aFormat, int32_t aInteger, int32_t aMantissa,
char aCh) const;
// Stringify and Concat all the JS::Value in a single string using ' ' as
// separator.
void
ComposeGroupName(JSContext* aCx, const Sequence<JS::Value>& aData,
nsAString& aName) const;
// StartTimer is called on the owning thread and populates aTimerLabel and
// aTimerValue. It returns false if a JS exception is thrown or if
// the max number of timers is reached.
// * aCx - the JSContext rooting aName.
// * aName - this is (should be) the name of the timer as JS::Value.
// * aTimestamp - the monotonicTimer for this context (taken from
// window->performance.now() or from Now() -
// workerPrivate->NowBaseTimeStamp() in workers.
// * aTimerLabel - This label will be populated with the aName converted to a
// string.
// * aTimerValue - the StartTimer value stored into (or taken from)
// mTimerRegistry.
bool
StartTimer(JSContext* aCx, const JS::Value& aName,
DOMHighResTimeStamp aTimestamp,
nsAString& aTimerLabel,
DOMHighResTimeStamp* aTimerValue);
// CreateStartTimerValue generates a ConsoleTimerStart dictionary exposed as
// JS::Value. If aTimerStatus is false, it generates a ConsoleTimerError
// instead. It's called only after the execution StartTimer on the owning
// thread.
// * aCx - this is the context that will root the returned value.
// * aTimerLabel - this label must be what StartTimer received as aTimerLabel.
// * aTimerValue - this is what StartTimer received as aTimerValue
// * aTimerStatus - the return value of StartTimer.
JS::Value
CreateStartTimerValue(JSContext* aCx, const nsAString& aTimerLabel,
DOMHighResTimeStamp aTimerValue,
bool aTimerStatus) const;
// StopTimer follows the same pattern as StartTimer: it runs on the
// owning thread and populates aTimerLabel and aTimerDuration, used by
// CreateStopTimerValue. It returns false if a JS exception is thrown or if
// the aName timer doesn't exist in the mTimerRegistry.
// * aCx - the JSContext rooting aName.
// * aName - this is (should be) the name of the timer as JS::Value.
// * aTimestamp - the monotonicTimer for this context (taken from
// window->performance.now() or from Now() -
// workerPrivate->NowBaseTimeStamp() in workers.
// * aTimerLabel - This label will be populated with the aName converted to a
// string.
// * aTimerDuration - the difference between aTimestamp and when the timer
// started (see StartTimer).
bool
StopTimer(JSContext* aCx, const JS::Value& aName,
DOMHighResTimeStamp aTimestamp,
nsAString& aTimerLabel,
double* aTimerDuration);
// This method generates a ConsoleTimerEnd dictionary exposed as JS::Value, or
// a ConsoleTimerError dictionary if aTimerStatus is false. See StopTimer.
// * aCx - this is the context that will root the returned value.
// * aTimerLabel - this label must be what StopTimer received as aTimerLabel.
// * aTimerDuration - this is what StopTimer received as aTimerDuration
// * aTimerStatus - the return value of StopTimer.
JS::Value
CreateStopTimerValue(JSContext* aCx, const nsAString& aTimerLabel,
double aTimerDuration,
bool aTimerStatus) const;
// The method populates a Sequence from an array of JS::Value.
bool
ArgumentsToValueList(const Sequence<JS::Value>& aData,
Sequence<JS::Value>& aSequence) const;
// This method follows the same pattern as StartTimer: its runs on the owning
// thread and populate aCountLabel, used by CreateCounterValue. Returns
// MAX_PAGE_COUNTERS in case of error, otherwise the incremented counter
// value.
// * aCx - the JSContext rooting aData.
// * aFrame - the first frame of ConsoleCallData.
// * aData - the arguments received by the console.count() method.
// * aCountLabel - the label that will be populated by this method.
uint32_t
IncreaseCounter(JSContext* aCx, const ConsoleStackEntry& aFrame,
const Sequence<JS::Value>& aData,
nsAString& aCountLabel);
// This method generates a ConsoleCounter dictionary as JS::Value. If
// aCountValue is == MAX_PAGE_COUNTERS it generates a ConsoleCounterError
// instead. See IncreaseCounter.
// * aCx - this is the context that will root the returned value.
// * aCountLabel - this label must be what IncreaseCounter received as
// aTimerLabel.
// * aCountValue - the return value of IncreaseCounter.
JS::Value
CreateCounterValue(JSContext* aCx, const nsAString& aCountLabel,
uint32_t aCountValue) const;
bool
ShouldIncludeStackTrace(MethodName aMethodName) const;
JSObject*
GetOrCreateSandbox(JSContext* aCx, nsIPrincipal* aPrincipal);
void
AssertIsOnOwningThread() const;
bool
IsShuttingDown() const;
// All these nsCOMPtr are touched on main thread only.
nsCOMPtr<nsPIDOMWindowInner> mWindow;
nsCOMPtr<nsIConsoleAPIStorage> mStorage;
RefPtr<JSObjectHolder> mSandbox;
// Touched on the owner thread.
nsDataHashtable<nsStringHashKey, DOMHighResTimeStamp> mTimerRegistry;
nsDataHashtable<nsStringHashKey, uint32_t> mCounterRegistry;
nsTArray<RefPtr<ConsoleCallData>> mCallDataStorage;
// This array is used in a particular corner-case where:
// 1. we are in a worker thread
// 2. we have more than STORAGE_MAX_EVENTS
// 3. but the main-thread ConsoleCallDataRunnable of the first one is still
// running (this means that something very bad is happening on the
// main-thread).
// When this happens we want to keep the ConsoleCallData alive for traceing
// its JSValues also if 'officially' this ConsoleCallData must be removed from
// the storage.
nsTArray<RefPtr<ConsoleCallData>> mCallDataStoragePending;
RefPtr<AnyCallback> mConsoleEventNotifier;
#ifdef DEBUG
PRThread* mOwningThread;
#endif
uint64_t mOuterID;
uint64_t mInnerID;
enum {
eUnknown,
eInitialized,
eShuttingDown
} mStatus;
friend class ConsoleCallData;
friend class ConsoleRunnable;
friend class ConsoleCallDataRunnable;
friend class ConsoleProfileRunnable;
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_Console_h */

View file

@ -0,0 +1,2 @@
component {96cf7855-dfa9-4c6d-8276-f9705b4890f2} ConsoleAPIStorage.js
contract @mozilla.org/consoleAPI-storage;1 {96cf7855-dfa9-4c6d-8276-f9705b4890f2}

View file

@ -0,0 +1,161 @@
/* 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/. */
"use strict";
var Cu = Components.utils;
var Ci = Components.interfaces;
var Cc = Components.classes;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
// This constant tells how many messages to process in a single timer execution.
const MESSAGES_IN_INTERVAL = 1500
const STORAGE_MAX_EVENTS = 1000;
var _consoleStorage = new Map();
const CONSOLEAPISTORAGE_CID = Components.ID('{96cf7855-dfa9-4c6d-8276-f9705b4890f2}');
/**
* The ConsoleAPIStorage is meant to cache window.console API calls for later
* reuse by other components when needed. For example, the Web Console code can
* display the cached messages when it opens for the active tab.
*
* ConsoleAPI messages are stored as they come from the ConsoleAPI code, with
* all their properties. They are kept around until the inner window object that
* created the messages is destroyed. Messages are indexed by the inner window
* ID.
*
* Usage:
* Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm");
*
* // Get the cached events array for the window you want (use the inner
* // window ID).
* let events = ConsoleAPIStorage.getEvents(innerWindowID);
* events.forEach(function(event) { ... });
*
* // Clear the events for the given inner window ID.
* ConsoleAPIStorage.clearEvents(innerWindowID);
*/
function ConsoleAPIStorageService() {
this.init();
}
ConsoleAPIStorageService.prototype = {
classID : CONSOLEAPISTORAGE_CID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleAPIStorage,
Ci.nsIObserver]),
classInfo: XPCOMUtils.generateCI({
classID: CONSOLEAPISTORAGE_CID,
contractID: '@mozilla.org/consoleAPI-storage;1',
interfaces: [Ci.nsIConsoleAPIStorage, Ci.nsIObserver],
flags: Ci.nsIClassInfo.SINGLETON
}),
observe: function CS_observe(aSubject, aTopic, aData)
{
if (aTopic == "xpcom-shutdown") {
Services.obs.removeObserver(this, "xpcom-shutdown");
Services.obs.removeObserver(this, "inner-window-destroyed");
Services.obs.removeObserver(this, "memory-pressure");
}
else if (aTopic == "inner-window-destroyed") {
let innerWindowID = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
this.clearEvents(innerWindowID + "");
}
else if (aTopic == "memory-pressure") {
this.clearEvents();
}
},
/** @private */
init: function CS_init()
{
Services.obs.addObserver(this, "xpcom-shutdown", false);
Services.obs.addObserver(this, "inner-window-destroyed", false);
Services.obs.addObserver(this, "memory-pressure", false);
},
/**
* Get the events array by inner window ID or all events from all windows.
*
* @param string [aId]
* Optional, the inner window ID for which you want to get the array of
* cached events.
* @returns array
* The array of cached events for the given window. If no |aId| is
* given this function returns all of the cached events, from any
* window.
*/
getEvents: function CS_getEvents(aId)
{
if (aId != null) {
return (_consoleStorage.get(aId) || []).slice(0);
}
let result = [];
for (let [id, events] of _consoleStorage) {
result.push.apply(result, events);
}
return result.sort(function(a, b) {
return a.timeStamp - b.timeStamp;
});
},
/**
* Record an event associated with the given window ID.
*
* @param string aId
* The ID of the inner window for which the event occurred or "jsm" for
* messages logged from JavaScript modules..
* @param string aOuterId
* This ID is used as 3rd parameters for the console-api-log-event
* notification.
* @param object aEvent
* A JavaScript object you want to store.
*/
recordEvent: function CS_recordEvent(aId, aOuterId, aEvent)
{
if (!_consoleStorage.has(aId)) {
_consoleStorage.set(aId, []);
}
let storage = _consoleStorage.get(aId);
storage.push(aEvent);
// truncate
if (storage.length > STORAGE_MAX_EVENTS) {
storage.shift();
}
Services.obs.notifyObservers(aEvent, "console-api-log-event", aOuterId);
Services.obs.notifyObservers(aEvent, "console-storage-cache-event", aId);
},
/**
* Clear storage data for the given window.
*
* @param string [aId]
* Optional, the inner window ID for which you want to clear the
* messages. If this is not specified all of the cached messages are
* cleared, from all window objects.
*/
clearEvents: function CS_clearEvents(aId)
{
if (aId != null) {
_consoleStorage.delete(aId);
}
else {
_consoleStorage.clear();
Services.obs.notifyObservers(null, "console-storage-reset", null);
}
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ConsoleAPIStorageService]);

View file

@ -0,0 +1,190 @@
/* -*- 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/ConsoleReportCollector.h"
#include "nsIConsoleService.h"
#include "nsIScriptError.h"
#include "nsNetUtil.h"
namespace mozilla {
NS_IMPL_ISUPPORTS(ConsoleReportCollector, nsIConsoleReportCollector)
ConsoleReportCollector::ConsoleReportCollector()
: mMutex("mozilla::ConsoleReportCollector")
{
}
void
ConsoleReportCollector::AddConsoleReport(uint32_t aErrorFlags,
const nsACString& aCategory,
nsContentUtils::PropertiesFile aPropertiesFile,
const nsACString& aSourceFileURI,
uint32_t aLineNumber,
uint32_t aColumnNumber,
const nsACString& aMessageName,
const nsTArray<nsString>& aStringParams)
{
// any thread
MutexAutoLock lock(mMutex);
mPendingReports.AppendElement(PendingReport(aErrorFlags, aCategory,
aPropertiesFile, aSourceFileURI,
aLineNumber, aColumnNumber,
aMessageName, aStringParams));
}
void
ConsoleReportCollector::FlushConsoleReports(nsIDocument* aDocument,
ReportAction aAction)
{
MOZ_ASSERT(NS_IsMainThread());
nsTArray<PendingReport> reports;
{
MutexAutoLock lock(mMutex);
if (aAction == ReportAction::Forget) {
mPendingReports.SwapElements(reports);
} else {
reports = mPendingReports;
}
}
for (uint32_t i = 0; i < reports.Length(); ++i) {
PendingReport& report = reports[i];
// It would be nice if we did not have to do this since ReportToConsole()
// just turns around and converts it back to a spec.
nsCOMPtr<nsIURI> uri;
if (!report.mSourceFileURI.IsEmpty()) {
nsresult rv = NS_NewURI(getter_AddRefs(uri), report.mSourceFileURI);
MOZ_ALWAYS_SUCCEEDS(rv);
if (NS_FAILED(rv)) {
continue;
}
}
// Convert back from nsTArray<nsString> to the char16_t** format required
// by our l10n libraries and ReportToConsole. (bug 1219762)
UniquePtr<const char16_t*[]> params;
uint32_t paramsLength = report.mStringParams.Length();
if (paramsLength > 0) {
params = MakeUnique<const char16_t*[]>(paramsLength);
for (uint32_t j = 0; j < paramsLength; ++j) {
params[j] = report.mStringParams[j].get();
}
}
nsContentUtils::ReportToConsole(report.mErrorFlags, report.mCategory,
aDocument, report.mPropertiesFile,
report.mMessageName.get(),
params.get(),
paramsLength, uri, EmptyString(),
report.mLineNumber, report.mColumnNumber);
}
}
void
ConsoleReportCollector::FlushConsoleReports(nsIConsoleReportCollector* aCollector)
{
MOZ_ASSERT(aCollector);
nsTArray<PendingReport> reports;
{
MutexAutoLock lock(mMutex);
mPendingReports.SwapElements(reports);
}
for (uint32_t i = 0; i < reports.Length(); ++i) {
PendingReport& report = reports[i];
aCollector->AddConsoleReport(report.mErrorFlags, report.mCategory,
report.mPropertiesFile, report.mSourceFileURI,
report.mLineNumber, report.mColumnNumber,
report.mMessageName, report.mStringParams);
}
}
void
ConsoleReportCollector::FlushReportsByWindowId(uint64_t aWindowId,
ReportAction aAction)
{
MOZ_ASSERT(NS_IsMainThread());
nsTArray<PendingReport> reports;
{
MutexAutoLock lock(mMutex);
if (aAction == ReportAction::Forget) {
mPendingReports.SwapElements(reports);
} else {
reports = mPendingReports;
}
}
nsCOMPtr<nsIConsoleService> consoleService =
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
if (!consoleService) {
NS_WARNING("GetConsoleService failed");
return;
}
nsresult rv;
for (uint32_t i = 0; i < reports.Length(); ++i) {
PendingReport& report = reports[i];
nsXPIDLString errorText;
if (!report.mStringParams.IsEmpty()) {
rv = nsContentUtils::FormatLocalizedString(report.mPropertiesFile,
report.mMessageName.get(),
report.mStringParams,
errorText);
} else {
rv = nsContentUtils::GetLocalizedString(report.mPropertiesFile,
report.mMessageName.get(),
errorText);
}
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
nsCOMPtr<nsIScriptError> errorObject =
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
rv = errorObject->InitWithWindowID(errorText,
NS_ConvertUTF8toUTF16(report.mSourceFileURI),
EmptyString(),
report.mLineNumber,
report.mColumnNumber,
report.mErrorFlags,
report.mCategory,
aWindowId);
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
consoleService->LogMessage(errorObject);
}
}
void
ConsoleReportCollector::ClearConsoleReports()
{
MutexAutoLock lock(mMutex);
mPendingReports.Clear();
}
ConsoleReportCollector::~ConsoleReportCollector()
{
}
} // namespace mozilla

View file

@ -0,0 +1,84 @@
/* -*- 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_ConsoleReportCollector_h
#define mozilla_ConsoleReportCollector_h
#include "mozilla/Mutex.h"
#include "nsIConsoleReportCollector.h"
#include "nsTArray.h"
namespace mozilla {
class ConsoleReportCollector final : public nsIConsoleReportCollector
{
public:
ConsoleReportCollector();
void
AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory,
nsContentUtils::PropertiesFile aPropertiesFile,
const nsACString& aSourceFileURI,
uint32_t aLineNumber, uint32_t aColumnNumber,
const nsACString& aMessageName,
const nsTArray<nsString>& aStringParams) override;
void
FlushConsoleReports(nsIDocument* aDocument,
ReportAction aAction = ReportAction::Forget) override;
void
FlushConsoleReports(nsIConsoleReportCollector* aCollector) override;
void
FlushReportsByWindowId(uint64_t aWindowId,
ReportAction aAction = ReportAction::Forget) override;
void
ClearConsoleReports() override;
private:
~ConsoleReportCollector();
struct PendingReport
{
PendingReport(uint32_t aErrorFlags, const nsACString& aCategory,
nsContentUtils::PropertiesFile aPropertiesFile,
const nsACString& aSourceFileURI, uint32_t aLineNumber,
uint32_t aColumnNumber, const nsACString& aMessageName,
const nsTArray<nsString>& aStringParams)
: mErrorFlags(aErrorFlags)
, mCategory(aCategory)
, mPropertiesFile(aPropertiesFile)
, mSourceFileURI(aSourceFileURI)
, mLineNumber(aLineNumber)
, mColumnNumber(aColumnNumber)
, mMessageName(aMessageName)
, mStringParams(aStringParams)
{ }
const uint32_t mErrorFlags;
const nsCString mCategory;
const nsContentUtils::PropertiesFile mPropertiesFile;
const nsCString mSourceFileURI;
const uint32_t mLineNumber;
const uint32_t mColumnNumber;
const nsCString mMessageName;
const nsTArray<nsString> mStringParams;
};
Mutex mMutex;
// protected by mMutex
nsTArray<PendingReport> mPendingReports;
public:
NS_DECL_THREADSAFE_ISUPPORTS
};
} // namespace mozilla
#endif // mozilla_ConsoleReportCollector_h

45
dom/console/moz.build Normal file
View file

@ -0,0 +1,45 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
XPIDL_SOURCES += [
'nsIConsoleAPIStorage.idl',
]
XPIDL_MODULE = 'dom'
EXPORTS += [
'nsIConsoleReportCollector.h',
]
EXPORTS.mozilla += [
'ConsoleReportCollector.h',
]
EXPORTS.mozilla.dom += [
'Console.h',
]
UNIFIED_SOURCES += [
'Console.cpp',
'ConsoleReportCollector.cpp',
]
EXTRA_COMPONENTS += [
'ConsoleAPI.manifest',
'ConsoleAPIStorage.js',
]
LOCAL_INCLUDES += [
'/docshell/base',
'/dom/base',
'/dom/workers',
'/js/xpconnect/src',
]
MOCHITEST_MANIFESTS += [ 'tests/mochitest.ini' ]
MOCHITEST_CHROME_MANIFESTS += [ 'tests/chrome.ini' ]
FINAL_LIBRARY = 'xul'

View file

@ -0,0 +1,47 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "nsISupports.idl"
[scriptable, uuid(9e32a7b6-c4d1-4d9a-87b9-1ef6b75c27a9)]
interface nsIConsoleAPIStorage : nsISupports
{
/**
* Get the events array by inner window ID or all events from all windows.
*
* @param string [aId]
* Optional, the inner window ID for which you want to get the array of
* cached events.
* @returns array
* The array of cached events for the given window. If no |aId| is
* given this function returns all of the cached events, from any
* window.
*/
jsval getEvents([optional] in DOMString aId);
/**
* Record an event associated with the given window ID.
*
* @param string aId
* The ID of the inner window for which the event occurred or "jsm" for
* messages logged from JavaScript modules..
* @param string aOuterId
* This ID is used as 3rd parameters for the console-api-log-event
* notification.
* @param object aEvent
* A JavaScript object you want to store.
*/
void recordEvent(in DOMString aId, in DOMString aOuterId, in jsval aEvent);
/**
* Clear storage data for the given window.
*
* @param string [aId]
* Optional, the inner window ID for which you want to clear the
* messages. If this is not specified all of the cached messages are
* cleared, from all window objects.
*/
void clearEvents([optional] in DOMString aId);
};

View file

@ -0,0 +1,115 @@
/* -*- 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 nsIConsoleReportCollector_h
#define nsIConsoleReportCollector_h
#include "nsContentUtils.h"
#include "nsISupports.h"
#include "nsTArrayForwardDeclare.h"
class nsACString;
class nsIDocument;
class nsString;
#define NS_NSICONSOLEREPORTCOLLECTOR_IID \
{0xdd98a481, 0xd2c4, 0x4203, {0x8d, 0xfa, 0x85, 0xbf, 0xd7, 0xdc, 0xd7, 0x05}}
// An interface for saving reports until we can flush them to the correct
// window at a later time.
class NS_NO_VTABLE nsIConsoleReportCollector : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_NSICONSOLEREPORTCOLLECTOR_IID)
// Add a pending report to be later displayed on the console. This may be
// called from any thread.
//
// aErrorFlags A nsIScriptError flags value.
// aCategory Name of module reporting error.
// aPropertiesFile Properties file containing localized message.
// aSourceFileURI The URI of the script generating the error. Must be a URI
// spec.
// aLineNumber The line number where the error was generated. May be 0 if
// the line number is not known.
// aColumnNumber The column number where the error was generated. May be 0
// if the line number is not known.
// aMessageName The name of the localized message contained in the
// properties file.
// aStringParams An array of nsString parameters to use when localizing the
// message.
virtual void
AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory,
nsContentUtils::PropertiesFile aPropertiesFile,
const nsACString& aSourceFileURI, uint32_t aLineNumber,
uint32_t aColumnNumber, const nsACString& aMessageName,
const nsTArray<nsString>& aStringParams) = 0;
// A version of AddConsoleReport() that accepts the message parameters
// as variable nsString arguments (or really, any sort of const nsAString).
// All other args the same as AddConsoleReport().
template<typename... Params>
void
AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory,
nsContentUtils::PropertiesFile aPropertiesFile,
const nsACString& aSourceFileURI, uint32_t aLineNumber,
uint32_t aColumnNumber, const nsACString& aMessageName,
Params&&... aParams)
{
nsTArray<nsString> params;
mozilla::dom::StringArrayAppender::Append(params, sizeof...(Params),
mozilla::Forward<Params>(aParams)...);
AddConsoleReport(aErrorFlags, aCategory, aPropertiesFile, aSourceFileURI,
aLineNumber, aColumnNumber, aMessageName, params);
}
// An enum calss to indicate whether should free the pending reports or not.
// Forget Free the pending reports.
// Save Keep the pending reports.
enum class ReportAction {
Forget,
Save
};
// Flush all pending reports to the console. Main thread only.
//
// aDocument An optional document representing where to flush the
// reports. If provided, then the corresponding window's
// web console will get the reports. Otherwise the reports
// go to the browser console.
// aAction An action to determine whether to reserve the pending
// reports. Defalut action is to forget the report.
virtual void
FlushConsoleReports(nsIDocument* aDocument,
ReportAction aAction = ReportAction::Forget) = 0;
// Flush all pending reports to another collector. May be called from any
// thread.
//
// aCollector A required collector object that will effectively take
// ownership of our currently console reports.
virtual void
FlushConsoleReports(nsIConsoleReportCollector* aCollector) = 0;
// Flush all pending reports to the console accroding to window ID. Main
// thread only.
//
// aWindowId A window ID representing where to flush the reports and it's
// typically the inner window ID.
//
// aAction An action to decide whether free the pending reports or not.
virtual void
FlushReportsByWindowId(uint64_t aWindowId,
ReportAction aAction = ReportAction::Forget) = 0;
// Clear all pending reports.
virtual void
ClearConsoleReports() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIConsoleReportCollector, NS_NSICONSOLEREPORTCOLLECTOR_IID)
#endif // nsIConsoleReportCollector_h

View file

@ -0,0 +1,6 @@
[DEFAULT]
skip-if = os == 'android'
support-files =
file_empty.html
[test_console.xul]

View file

@ -0,0 +1 @@
<!DOCTYPE html><html><body></body></html>

View file

@ -0,0 +1,11 @@
[DEFAULT]
support-files =
file_empty.html
[test_bug659625.html]
[test_bug978522.html]
[test_bug979109.html]
[test_bug989665.html]
[test_consoleEmptyStack.html]
[test_console_binding.html]
[test_console_proto.html]

View file

@ -0,0 +1,92 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=659625
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 659625</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=659625">Mozilla Bug 659625</a>
<script type="application/javascript">
const { Cc, Ci } = SpecialPowers;
let consoleStorage = Cc["@mozilla.org/consoleAPI-storage;1"];
let storage = consoleStorage.getService(Ci.nsIConsoleAPIStorage);
let clearAndCheckStorage = () => {
console.clear();
ok(storage.getEvents().length === 1,
"Only one event remains in consoleAPIStorage");
ok(storage.getEvents()[0].level === "clear",
"Remaining event has level 'clear'");
}
storage.clearEvents();
ok(storage.getEvents().length === 0,
"Console is empty when test is starting");
clearAndCheckStorage();
console.log("log");
console.debug("debug");
console.warn("warn");
console.error("error");
console.exception("exception");
ok(storage.getEvents().length === 6,
"5 new console events have been registered for logging variants");
clearAndCheckStorage();
console.trace();
ok(storage.getEvents().length === 2,
"1 new console event registered for trace");
clearAndCheckStorage();
console.dir({});
ok(storage.getEvents().length === 2,
"1 new console event registered for dir");
clearAndCheckStorage();
console.count("count-label");
console.count("count-label");
ok(storage.getEvents().length === 3,
"2 new console events registered for 2 count calls");
clearAndCheckStorage();
console.group("group-label")
console.log("group-log");
ok(storage.getEvents().length === 3,
"2 new console events registered for group + log");
clearAndCheckStorage();
console.groupCollapsed("group-collapsed")
console.log("group-collapsed-log");
ok(storage.getEvents().length === 3,
"2 new console events registered for groupCollapsed + log");
clearAndCheckStorage();
console.group("closed-group-label")
console.log("group-log");
console.groupEnd()
ok(storage.getEvents().length === 4,
"3 new console events registered for group/groupEnd");
clearAndCheckStorage();
console.time("time-label");
console.timeEnd();
ok(storage.getEvents().length === 3,
"2 new console events registered for time/timeEnd");
clearAndCheckStorage();
console.timeStamp("timestamp-label");
ok(storage.getEvents().length === 2,
"1 new console event registered for timeStamp");
clearAndCheckStorage();
// Check that console.clear() clears previous clear messages
clearAndCheckStorage();
</script>
</body>
</html>

View file

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=978522
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 978522 - basic support</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=978522">Mozilla Bug 978522</a>
<script type="application/javascript">
console.log('%s', {
toString: function() {
console.log('%s', {
toString: function() {
ok(true, "Still alive \\o/");
SimpleTest.finish();
return "hello world";
}
});
}
});
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>

View file

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=979109
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 979109</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=979109">Mozilla Bug 979109</a>
<script type="application/javascript">
console.warn("%", "a");
console.warn("%%", "a");
console.warn("%123", "a");
console.warn("%123.", "a");
console.warn("%123.123", "a");
console.warn("%123.123o", "a");
console.warn("%123.123s", "a");
console.warn("%123.123d", "a");
console.warn("%123.123f", "a");
console.warn("%123.123z", "a");
console.warn("%.", "a");
console.warn("%.123", "a");
ok(true, "Still alive \\o/");
</script>
</body>
</html>

View file

@ -0,0 +1,21 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=989665
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 989665</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=989665">Mozilla Bug 989665</a>
<script type="application/javascript">
w = new Worker("data:text/javascript;charset=UTF-8, console.log('%s', {toString: function() { throw 3 }}); ");
ok(true, "This test should not crash.");
</script>
</body>
</html>

View file

@ -0,0 +1,35 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
<window title="Test for URL API"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<iframe id="iframe" />
</body>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
ok("console" in window, "Console exists");
window.console.log(42);
ok("table" in console, "Console has the 'table' method.");
window.console = 42;
is(window.console, 42, "Console is replacable");
var frame = document.getElementById("iframe");
ok(frame, "Frame must exist");
frame.src="http://mochi.test:8888/tests/dom/console/test/file_empty.html";
frame.onload = function() {
ok("console" in frame.contentWindow, "Console exists in the iframe");
frame.contentWindow.console.log(42);
frame.contentWindow.console = 42;
is(frame.contentWindow.console, 42, "Console is replacable in the iframe");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
]]></script>
</window>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test for empty stack in console</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
window.setTimeout(console.log.bind(console), 0, "xyz");
window.addEventListener("fake", console.log.bind(console, "xyz"));
window.addEventListener("fake", function() {
ok(true, "Still alive");
SimpleTest.finish();
});
window.dispatchEvent(new Event("fake"));
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,42 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test Console binding</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript">
function consoleListener() {
SpecialPowers.addObserver(this, "console-api-log-event", false);
}
var order = 0;
consoleListener.prototype = {
observe: function(aSubject, aTopic, aData) {
if (aTopic == "console-api-log-event") {
var obj = aSubject.wrappedJSObject;
if (order+1 == parseInt(obj.arguments[0])) {
ok(true, "Message received: " + obj.arguments[0]);
order++;
}
if (order == 3) {
SpecialPowers.removeObserver(this, "console-api-log-event");
SimpleTest.finish();
return;
}
}
}
}
var cl = new consoleListener();
SimpleTest.waitForExplicitFinish();
[1,2,3].forEach(console.log);
</script>
</body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test for console.__proto__</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript">
isnot(Object.getPrototypeOf(console), Object.prototype, "Foo");
is(Object.getPrototypeOf(Object.getPrototypeOf(console)), Object.prototype, "Boo");
</script>
</body>
</html>