Bug 1211665 - Save originAttributes in the console event messages. r?baku
Save the originAttributes of the current principal into the console events,
so that we can filter the console messages by originAttributes (e.g.
WebExtensions console events can be filtered using the addonId that the
ExtensionPages' originAttributes contain.
MozReview-Commit-ID: 5v9BWLbgskd
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -144,16 +144,22 @@ public:
{
MOZ_ASSERT(mIDType == eUnknown);
mOuterIDString = aOuterID;
mInnerIDString = aInnerID;
mIDType = eString;
}
+ void
+ SetOriginAttributes(const PrincipalOriginAttributes& aOriginAttributes)
+ {
+ mOriginAttributes = aOriginAttributes;
+ }
+
bool
PopulateArgumentsSequence(Sequence<JS::Value>& aSequence) const
{
AssertIsOnOwningThread();
for (uint32_t i = 0; i < mCopiedArguments.Length(); ++i) {
if (NS_WARN_IF(!aSequence.AppendElement(mCopiedArguments[i],
fallible))) {
@@ -236,16 +242,18 @@ public:
} mIDType;
uint64_t mOuterIDNumber;
nsString mOuterIDString;
uint64_t mInnerIDNumber;
nsString mInnerIDString;
+ PrincipalOriginAttributes mOriginAttributes;
+
nsString mMethodString;
// Stack management is complicated, because we want to do it as
// lazily as possible. Therefore, we have the following behavior:
// 1) mTopStackFrame is initialized whenever we have any JS on the stack
// 2) mReifiedStack is initialized if we're created in a worker.
// 3) mStack is set (possibly to null if there is no JS on the stack) if
// we're created on main thread.
@@ -609,16 +617,30 @@ private:
{
AssertIsOnMainThread();
// The windows have to run in parallel.
MOZ_ASSERT(!!aOuterWindow == !!aInnerWindow);
if (aOuterWindow) {
mCallData->SetIDs(aOuterWindow->WindowID(), aInnerWindow->WindowID());
+
+ // Save the principal's OriginAttributes in the console event data
+ // so that we will be able to filter messages by origin attributes.
+ nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aInnerWindow);
+ if (NS_WARN_IF(!sop)) {
+ return;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
+ if (NS_WARN_IF(!principal)) {
+ return;
+ }
+
+ mCallData->SetOriginAttributes(BasePrincipal::Cast(principal)->OriginAttributesRef());
} else {
ConsoleStackEntry frame;
if (mCallData->mTopStackFrame) {
frame = *mCallData->mTopStackFrame;
}
nsString id = frame.mFilename;
nsString innerID;
@@ -629,16 +651,25 @@ private:
// Use scope as ID so the webconsole can decide if the message should
// show up per tab
id.AssignWithConversion(mWorkerPrivate->WorkerName());
} else {
innerID = NS_LITERAL_STRING("Worker");
}
mCallData->SetIDs(id, innerID);
+
+ // Save the principal's OriginAttributes in the console event data
+ // so that we will be able to filter messages by origin attributes.
+ nsCOMPtr<nsIPrincipal> principal = mWorkerPrivate->GetPrincipal();
+ if (NS_WARN_IF(!principal)) {
+ return;
+ }
+
+ mCallData->SetOriginAttributes(BasePrincipal::Cast(principal)->OriginAttributesRef());
}
// Now we could have the correct window (if we are not window-less).
mClonedData.mParent = aInnerWindow;
ProcessCallData(aCx);
mClonedData.mParent = nullptr;
@@ -1248,16 +1279,30 @@ Console::Method(JSContext* aCx, MethodNa
if (!webNav) {
return;
}
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
MOZ_ASSERT(loadContext);
loadContext->GetUsePrivateBrowsing(&callData->mPrivate);
+
+ // Save the principal's OriginAttributes in the console event data
+ // so that we will be able to filter messages by origin attributes.
+ nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
+ if (NS_WARN_IF(!sop)) {
+ return;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
+ if (NS_WARN_IF(!principal)) {
+ return;
+ }
+
+ callData->SetOriginAttributes(BasePrincipal::Cast(principal)->OriginAttributesRef());
}
uint32_t maxDepth = ShouldIncludeStackTrace(aMethodName) ?
DEFAULT_MAX_STACKTRACE_DEPTH : 1;
nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, maxDepth);
if (stack) {
callData->mTopStackFrame.emplace();
@@ -1500,16 +1545,23 @@ Console::PopulateConsoleNotificationInTh
ConsoleStackEntry frame;
if (aData->mTopStackFrame) {
frame = *aData->mTopStackFrame;
}
ClearException ce(aCx);
RootedDictionary<ConsoleEvent> event(aCx);
+ // Save the principal's OriginAttributes in the console event data
+ // so that we will be able to filter messages by origin attributes.
+ JS::Rooted<JS::Value> originAttributesValue(aCx);
+ if (ToJSValue(aCx, aData->mOriginAttributes, &originAttributesValue)) {
+ event.mOriginAttributes = originAttributesValue;
+ }
+
event.mID.Construct();
event.mInnerID.Construct();
if (aData->mIDType == ConsoleCallData::eString) {
event.mID.Value().SetAsString() = aData->mOuterIDString;
event.mInnerID.Value().SetAsString() = aData->mInnerIDString;
} else if (aData->mIDType == ConsoleCallData::eNumber) {
event.mID.Value().SetAsUnsignedLongLong() = aData->mOuterIDNumber;
--- a/dom/tests/browser/browser.ini
+++ b/dom/tests/browser/browser.ini
@@ -12,16 +12,17 @@ support-files =
support-files =
test_new_window_from_content_child.html
[browser_bug1008941_dismissGeolocationHanger.js]
skip-if = buildapp == 'mulet'
[browser_test__content.js]
skip-if = e10s
[browser_ConsoleAPITests.js]
skip-if = e10s
+[browser_ConsoleAPI_originAttributes.js]
[browser_ConsoleStorageAPITests.js]
skip-if = e10s
[browser_ConsoleStoragePBTest_perwindowpb.js]
skip-if = e10s
[browser_autofocus_background.js]
skip-if= buildapp == 'mulet'
[browser_autofocus_preference.js]
[browser_bug1238427.js]
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_ConsoleAPI_originAttributes.js
@@ -0,0 +1,80 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
+ .getService(Ci.nsIConsoleAPIStorage);
+
+const FAKE_ADDON_ID = "test-webext-addon@mozilla.org";
+const EXPECTED_CONSOLE_ID = `addon/${FAKE_ADDON_ID}`;
+const EXPECTED_CONSOLE_MESSAGE_CONTENT = "fake-webext-addon-test-log-message";
+const ConsoleObserver = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+ init() {
+ Services.obs.addObserver(this, "console-api-log-event", false);
+ },
+
+ uninit() {
+ Services.obs.removeObserver(this, "console-api-log-event", false);
+ },
+
+ observe(aSubject, aTopic, aData) {
+ if (aTopic == "console-api-log-event") {
+ let consoleAPIMessage = aSubject.wrappedJSObject;
+
+ is(consoleAPIMessage.arguments[0], EXPECTED_CONSOLE_MESSAGE_CONTENT,
+ "the consoleAPIMessage contains the expected message");
+
+ ok(consoleAPIMessage.originAttributes, "the consoleAPImessage contains originattributes");
+ is(consoleAPIMessage.originAttributes.addonId, FAKE_ADDON_ID,
+ "the consoleAPImessage's originAttributes contains the expected addonId");
+
+ let cachedMessages = ConsoleAPIStorage.getEvents();
+
+ is(cachedMessages.length, 1, "found one console api messsage as expected");
+
+ ok(cachedMessages[0].originAttributes, "the consoleAPImessage contains originattributes");
+ is(cachedMessages[0].originAttributes.addonId, FAKE_ADDON_ID,
+ "the consoleAPImessage's originAttributes contains the expected addonId");
+
+ finish();
+ }
+ }
+};
+
+function test()
+{
+ ConsoleObserver.init();
+
+ waitForExplicitFinish();
+
+ let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
+ let uuid = uuidGenerator.generateUUID().number;
+ uuid = uuid.slice(1, -1); // Strip { and } off the UUID.
+ let baseURI = Services.io.newURI("about:blank", null, null);
+ let originAttributes = {addonId: FAKE_ADDON_ID};
+ let principal = Services.scriptSecurityManager
+ .createCodebasePrincipal(baseURI, originAttributes);
+
+ let chromeWebNav = Services.appShell.createWindowlessBrowser(true);
+ let interfaceRequestor = chromeWebNav.QueryInterface(Ci.nsIInterfaceRequestor);
+ let docShell = interfaceRequestor.getInterface(Ci.nsIDocShell);
+ docShell.createAboutBlankContentViewer(principal);
+
+ info("fake webextension docShell created");
+
+ registerCleanupFunction(function() {
+ if (chromeWebNav) {
+ chromeWebNav.close();
+ chromeWebNav = null;
+ }
+ ConsoleObserver.uninit();
+ });
+
+ let window = docShell.contentViewer.DOMDocument.defaultView;
+ window.eval(`console.log("${EXPECTED_CONSOLE_MESSAGE_CONTENT}");`);
+ chromeWebNav.close();
+ chromeWebNav = null;
+
+ info("fake webextension page logged a console api message");
+}
--- a/dom/webidl/Console.webidl
+++ b/dom/webidl/Console.webidl
@@ -40,16 +40,17 @@ interface Console {
[BinaryName="noopMethod"]
void timelineEnd();
};
// This is used to propagate console events to the observers.
dictionary ConsoleEvent {
(unsigned long long or DOMString) ID;
(unsigned long long or DOMString) innerID;
+ any originAttributes = null;
DOMString level = "";
DOMString filename = "";
unsigned long lineNumber = 0;
unsigned long columnNumber = 0;
DOMString functionName = "";
double timeStamp = 0;
sequence<any> arguments;
sequence<DOMString?> styles;