diff --git a/browser/base/content/test/siteIdentity/browser.ini b/browser/base/content/test/siteIdentity/browser.ini
index 8b7b6de3bddce..da2c04e1b2993 100644
--- a/browser/base/content/test/siteIdentity/browser.ini
+++ b/browser/base/content/test/siteIdentity/browser.ini
@@ -42,6 +42,10 @@ tags = mcb
support-files =
file_csp_block_all_mixedcontent.html
file_csp_block_all_mixedcontent.js
+[browser_deprecatedTLSVersions.js]
+[browser_getSecurityInfo.js]
+support-files =
+ dummy_iframe_page.html
[browser_identity_UI.js]
[browser_identityBlock_focus.js]
support-files = ../permissions/permissions.html
@@ -115,4 +119,3 @@ support-files =
support-files =
file_mixedPassiveContent.html
file_bug1045809_1.html
-[browser_deprecatedTLSVersions.js]
diff --git a/browser/base/content/test/siteIdentity/browser_getSecurityInfo.js b/browser/base/content/test/siteIdentity/browser_getSecurityInfo.js
new file mode 100644
index 0000000000000..5be82b22893cd
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_getSecurityInfo.js
@@ -0,0 +1,57 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
+const MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT = MOZILLA_PKIX_ERROR_BASE + 14;
+
+const IFRAME_PAGE = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com") + "dummy_iframe_page.html";
+
+// Tests the getSecurityInfo() function exposed on WindowGlobalParent.
+add_task(async function test() {
+ await BrowserTestUtils.withNewTab("about:blank", async function(browser) {
+ let loaded = BrowserTestUtils.waitForErrorPage(browser);
+ await BrowserTestUtils.loadURI(browser, "https://self-signed.example.com");
+ await loaded;
+
+ let securityInfo = await browser.browsingContext.currentWindowGlobal.getSecurityInfo();
+ securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
+ ok(securityInfo, "Found some security info");
+ ok(securityInfo.failedCertChain, "Has a failed cert chain");
+ is(securityInfo.errorCode, MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT, "Has the correct error code");
+ is(securityInfo.serverCert.commonName, "self-signed.example.com", "Has the correct certificate");
+
+ loaded = BrowserTestUtils.browserLoaded(browser);
+ await BrowserTestUtils.loadURI(browser, "http://example.com");
+ await loaded;
+
+ securityInfo = await browser.browsingContext.currentWindowGlobal.getSecurityInfo();
+ is(securityInfo, null, "Found no security info");
+
+ loaded = BrowserTestUtils.browserLoaded(browser);
+ await BrowserTestUtils.loadURI(browser, "https://example.com");
+ await loaded;
+
+ securityInfo = await browser.browsingContext.currentWindowGlobal.getSecurityInfo();
+ securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
+ ok(securityInfo, "Found some security info");
+ ok(securityInfo.succeededCertChain, "Has a succeeded cert chain");
+ is(securityInfo.errorCode, 0, "Has no error code");
+ is(securityInfo.serverCert.commonName, "example.com", "Has the correct certificate");
+
+ loaded = BrowserTestUtils.browserLoaded(browser);
+ await BrowserTestUtils.loadURI(browser, IFRAME_PAGE);
+ await loaded;
+
+ // Get the info of the parent, which is HTTP.
+ securityInfo = await browser.browsingContext.currentWindowGlobal.getSecurityInfo();
+ is(securityInfo, null, "Found no security info");
+
+ // Get the info of the frame, which is HTTPS.
+ securityInfo = await browser.browsingContext.getChildren()[0].currentWindowGlobal.getSecurityInfo();
+ securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
+ ok(securityInfo, "Found some security info");
+ ok(securityInfo.succeededCertChain, "Has a succeeded cert chain");
+ is(securityInfo.errorCode, 0, "Has no error code");
+ is(securityInfo.serverCert.commonName, "example.com", "Has the correct certificate");
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/dummy_iframe_page.html b/browser/base/content/test/siteIdentity/dummy_iframe_page.html
new file mode 100644
index 0000000000000..ea80367aa5f54
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/dummy_iframe_page.html
@@ -0,0 +1,10 @@
+
+
+Dummy iframe test page
+
+
+
+
+Dummy test page
+
+
diff --git a/dom/chrome-webidl/WindowGlobalActors.webidl b/dom/chrome-webidl/WindowGlobalActors.webidl
index 2dd779e058d6e..f60b2d63b78e6 100644
--- a/dom/chrome-webidl/WindowGlobalActors.webidl
+++ b/dom/chrome-webidl/WindowGlobalActors.webidl
@@ -7,6 +7,7 @@ interface Principal;
interface URI;
interface nsIDocShell;
interface RemoteTab;
+interface nsITransportSecurityInfo;
[Exposed=Window, ChromeOnly]
interface WindowGlobalParent {
@@ -54,6 +55,18 @@ interface WindowGlobalParent {
Promise changeFrameRemoteness(
BrowsingContext? bc, DOMString remoteType,
unsigned long long pendingSwitchId);
+
+ /**
+ * Fetches the securityInfo object for this window. This function will
+ * look for failed and successful channels to find the security info,
+ * thus it will work on regular HTTPS pages as well as certificate
+ * error pages.
+ *
+ * This returns a Promise which resolves to an nsITransportSecurity
+ * object with certificate data or undefined if no security info is available.
+ */
+ [Throws]
+ Promise getSecurityInfo();
};
[Exposed=Window, ChromeOnly]
diff --git a/dom/ipc/PWindowGlobal.ipdl b/dom/ipc/PWindowGlobal.ipdl
index 7c72a13b83ab3..934bed20094d0 100644
--- a/dom/ipc/PWindowGlobal.ipdl
+++ b/dom/ipc/PWindowGlobal.ipdl
@@ -40,6 +40,11 @@ child:
uint64_t aSwitchId)
returns (nsresult rv, nullable PBrowserBridge bridge);
+ /**
+ * Returns the serialized security info associated with this window.
+ */
+ async GetSecurityInfo() returns(nsCString? serializedSecInfo);
+
both:
async RawMessage(JSWindowActorMessageMeta aMetadata, ClonedMessageData aData);
diff --git a/dom/ipc/WindowGlobalChild.cpp b/dom/ipc/WindowGlobalChild.cpp
index 96f0b532c205d..2bf2555ad0905 100644
--- a/dom/ipc/WindowGlobalChild.cpp
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -23,6 +23,7 @@
#include "nsGlobalWindowInner.h"
#include "nsFrameLoaderOwner.h"
#include "nsQueryObject.h"
+#include "nsSerializationHelper.h"
#include "mozilla/dom/JSWindowActorBinding.h"
#include "mozilla/dom/JSWindowActorChild.h"
@@ -253,6 +254,36 @@ IPCResult WindowGlobalChild::RecvChangeFrameRemoteness(
return IPC_OK();
}
+mozilla::ipc::IPCResult WindowGlobalChild::RecvGetSecurityInfo(
+ GetSecurityInfoResolver&& aResolve) {
+ Maybe result;
+
+ if (nsCOMPtr doc = mWindowGlobal->GetDoc()) {
+ nsCOMPtr secInfo;
+ nsresult rv = NS_OK;
+
+ // First check if there's a failed channel, in case of a certificate
+ // error.
+ if (nsIChannel* failedChannel = doc->GetFailedChannel()) {
+ rv = failedChannel->GetSecurityInfo(getter_AddRefs(secInfo));
+ } else {
+ // When there's no failed channel we should have a regular
+ // security info on the document. In some cases there's no
+ // security info at all, i.e. on HTTP sites.
+ secInfo = doc->GetSecurityInfo();
+ }
+
+ if (NS_SUCCEEDED(rv) && secInfo) {
+ nsCOMPtr secInfoSer = do_QueryInterface(secInfo);
+ result.emplace();
+ NS_SerializeToString(secInfoSer, result.ref());
+ }
+ }
+
+ aResolve(result);
+ return IPC_OK();
+}
+
IPCResult WindowGlobalChild::RecvRawMessage(
const JSWindowActorMessageMeta& aMeta, const ClonedMessageData& aData) {
StructuredCloneData data;
diff --git a/dom/ipc/WindowGlobalChild.h b/dom/ipc/WindowGlobalChild.h
index 46aeea5a14673..70e764c9927b2 100644
--- a/dom/ipc/WindowGlobalChild.h
+++ b/dom/ipc/WindowGlobalChild.h
@@ -103,6 +103,9 @@ class WindowGlobalChild final : public WindowGlobalActor,
dom::BrowsingContext* aBc, const nsString& aRemoteType,
uint64_t aPendingSwitchId, ChangeFrameRemotenessResolver&& aResolver);
+ mozilla::ipc::IPCResult RecvGetSecurityInfo(
+ GetSecurityInfoResolver&& aResolve);
+
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
private:
diff --git a/dom/ipc/WindowGlobalParent.cpp b/dom/ipc/WindowGlobalParent.cpp
index 00f972bff9c5b..6c92ef8b0c553 100644
--- a/dom/ipc/WindowGlobalParent.cpp
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -27,6 +27,8 @@
#include "nsGlobalWindowInner.h"
#include "nsQueryObject.h"
#include "nsFrameLoaderOwner.h"
+#include "nsSerializationHelper.h"
+#include "nsITransportSecurityInfo.h"
#include "mozilla/dom/JSWindowActorBinding.h"
#include "mozilla/dom/JSWindowActorParent.h"
@@ -319,6 +321,45 @@ already_AddRefed WindowGlobalParent::ChangeFrameRemoteness(
return promise.forget();
}
+already_AddRefed WindowGlobalParent::GetSecurityInfo(
+ ErrorResult& aRv) {
+ RefPtr browserParent = GetBrowserParent();
+ if (NS_WARN_IF(!browserParent)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ nsIGlobalObject* global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
+ RefPtr promise = Promise::Create(global, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ SendGetSecurityInfo(
+ [promise](Maybe&& aResult) {
+ if (aResult) {
+ nsCOMPtr infoObj;
+ nsresult rv =
+ NS_DeserializeObject(aResult.value(), getter_AddRefs(infoObj));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ promise->MaybeReject(NS_ERROR_FAILURE);
+ }
+ nsCOMPtr info = do_QueryInterface(infoObj);
+ if (!info) {
+ promise->MaybeReject(NS_ERROR_FAILURE);
+ }
+ promise->MaybeResolve(info);
+ } else {
+ promise->MaybeResolveWithUndefined();
+ }
+ },
+ [promise](ResponseRejectReason&& aReason) {
+ promise->MaybeReject(NS_ERROR_FAILURE);
+ });
+
+ return promise.forget();
+}
+
void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
mIPCClosed = true;
gWindowGlobalParentsById->Remove(mInnerWindowId);
diff --git a/dom/ipc/WindowGlobalParent.h b/dom/ipc/WindowGlobalParent.h
index fc3a33099f80d..9d8f6b3ef2a6f 100644
--- a/dom/ipc/WindowGlobalParent.h
+++ b/dom/ipc/WindowGlobalParent.h
@@ -107,6 +107,8 @@ class WindowGlobalParent final : public WindowGlobalActor,
uint64_t aPendingSwitchId,
ErrorResult& aRv);
+ already_AddRefed GetSecurityInfo(ErrorResult& aRv);
+
// Create a WindowGlobalParent from over IPC. This method should not be called
// from outside of the IPC constructors.
WindowGlobalParent(const WindowGlobalInit& aInit, bool aInProcess);