Bug 1211665 - Save originAttributes in the console event messages. r=baku
authorLuca Greco <luca.greco@alcacoop.it>
Fri, 01 Apr 2016 13:55:40 +0200
changeset 291276 44c3d66a71e3f2bf2f5e38ac3a20f6afd506aca1
parent 291275 7fa6eea202cb13eda52766eb0e2c9efa27a21ff6
child 291277 75d0e51772db9f842eb5e1dee98230e774c69b33
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
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;