Bug 1211665 - Save originAttributes in the console event messages. r?baku draft
authorLuca Greco <luca.greco@alcacoop.it>
Fri, 01 Apr 2016 13:55:40 +0200
changeset 346625 2ca1d48a4570e6478a2cdaa8eeb3ea0e84786bff
parent 346007 e14db462d31d566570e3bece66d5380f7b1ad400
child 346626 93586efd2dbfaee6cfa0f5c7534793d004ea7728
push id14421
push userluca.greco@alcacoop.it
push dateFri, 01 Apr 2016 13:26:10 +0000
reviewersbaku
bugs1211665
milestone48.0a1
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
dom/base/Console.cpp
dom/tests/browser/browser.ini
dom/tests/browser/browser_ConsoleAPI_originAttributes.js
dom/webidl/Console.webidl
--- 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;