Mixed content blocking is bypassed for WebSockets in Workers (159726)
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Sep 2019 15:31:55 +0000 (15:31 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Sep 2019 15:31:55 +0000 (15:31 +0000)
https://bugs.webkit.org/show_bug.cgi?id=159726
<rdar://problem/27326438>

Patch by Kate Cheney <katherine_cheney@apple.com> on 2019-09-24
Reviewed by Brady Eidson.

Source/WebCore:

Tests: http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-with-embedded-http.html
       http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https.html
       http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https.html
       http/tests/websocket/tests/hybi/non-document-mixed-content-blocked.https.html

* Modules/websockets/WebSocket.cpp:
(WebCore::WebSocket::connect):
* Modules/websockets/WorkerThreadableWebSocketChannel.cpp:
(WebCore::WorkerThreadableWebSocketChannel::Bridge::connect):
* loader/MixedContentChecker.cpp:
(WebCore::MixedContentChecker::checkForMixedContentInFrameTree):
* loader/MixedContentChecker.h:
Patch to block insecure WebSocket URL connection attempts by Workers
on secure pages. If the URL is ws, and any embedding iframe has protocol
https, and the context is a Worker, the connection should be blocked.

I was unable to write a test case to hit the case where a document
does not have a frame and tries to communicate via a worker to
connect to a WebSocket because after removing the subframe from its
parent, the subframe is unable to perform a postMessage to a worker
even in the same script execution to tell the worker to connect
to a WebSocket.

LayoutTests:

Added 4 test cases and 2 html resources utilized by the tests.
The cases test the following:
1. an https page with a worker trying to connect via ws: url -->
fails.
2. an https page embedded in an http page trying to connect via ws
:url (through a worker) --> fails.
3. an http page embedded in an https page with an http top frame
trying to connect via an insecure ws url fails.
4. an https page embedded in an http page embedded in an https page
trying to connect to a ws :url via a worker --> fails.

* http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-expected.txt: Added.
* http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-with-embedded-http-expected.txt: Added.
* http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-with-embedded-http.html: Added.
* http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https.html: Added.
* http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https-expected.txt: Added.
* http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https.html: Added.
* http/tests/websocket/tests/hybi/non-document-mixed-content-blocked.https-expected.txt: Added.
* http/tests/websocket/tests/hybi/non-document-mixed-content-blocked.https.html: Added.
* http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked-embedded-http.https.html: Added.
* http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked-embedding-https.js: Added.
(handleConnect.self.postMessage):
(handleConnect):
(runTests.ws.onopen):
(runTests.ws.onerror):
(runTests):
* http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked.html: Added.
* http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked.js: Added.
(handleConnect.self.postMessage):
(handleConnect):
(runTests.ws.onopen):
(runTests.ws.onerror):
(runTests):
* http/tests/workers/service/resources/serviceworker-websocket-worker.js:
(async.doTest):
Updated previous test which was hitting the fix to use a wss url

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@250300 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-with-embedded-http-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-with-embedded-http.html [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https.html [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https.html [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked.https.html [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked-embedded-http.https.html [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked.html [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked.js [new file with mode: 0644]
LayoutTests/http/tests/workers/service/resources/serviceworker-websocket-worker.js
Source/WebCore/ChangeLog
Source/WebCore/Modules/websockets/WebSocket.cpp
Source/WebCore/Modules/websockets/WorkerThreadableWebSocketChannel.cpp
Source/WebCore/loader/MixedContentChecker.cpp
Source/WebCore/loader/MixedContentChecker.h

index fb8200a..a1ff8dd 100644 (file)
@@ -1,3 +1,48 @@
+2019-09-24  Kate Cheney  <katherine_cheney@apple.com>
+
+        Mixed content blocking is bypassed for WebSockets in Workers (159726)
+        https://bugs.webkit.org/show_bug.cgi?id=159726
+        <rdar://problem/27326438>
+
+        Reviewed by Brady Eidson.
+
+        Added 4 test cases and 2 html resources utilized by the tests.
+        The cases test the following: 
+        1. an https page with a worker trying to connect via ws: url -->
+        fails.
+        2. an https page embedded in an http page trying to connect via ws
+        :url (through a worker) --> fails.
+        3. an http page embedded in an https page with an http top frame
+        trying to connect via an insecure ws url fails.
+        4. an https page embedded in an http page embedded in an https page
+        trying to connect to a ws :url via a worker --> fails.
+
+        * http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-expected.txt: Added.
+        * http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-with-embedded-http-expected.txt: Added.
+        * http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-with-embedded-http.html: Added.
+        * http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https.html: Added.
+        * http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https-expected.txt: Added.
+        * http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https.html: Added.
+        * http/tests/websocket/tests/hybi/non-document-mixed-content-blocked.https-expected.txt: Added.
+        * http/tests/websocket/tests/hybi/non-document-mixed-content-blocked.https.html: Added.
+        * http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked-embedded-http.https.html: Added.
+        * http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked-embedding-https.js: Added.
+        (handleConnect.self.postMessage):
+        (handleConnect):
+        (runTests.ws.onopen):
+        (runTests.ws.onerror):
+        (runTests):
+        * http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked.html: Added.
+        * http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked.js: Added.
+        (handleConnect.self.postMessage):
+        (handleConnect):
+        (runTests.ws.onopen):
+        (runTests.ws.onerror):
+        (runTests):
+        * http/tests/workers/service/resources/serviceworker-websocket-worker.js:
+        (async.doTest):
+        Updated previous test which was hitting the fix to use a wss url 
+
 2019-09-23  Youenn Fablet  <youenn@apple.com>
 
         Support sync-xhr feature policy
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-expected.txt b/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-expected.txt
new file mode 100644 (file)
index 0000000..751e35d
--- /dev/null
@@ -0,0 +1,5 @@
+CONSOLE MESSAGE: WebSocket connection failed: The page at https://localhost:8443/websocket/tests/hybi/non-document-mixed-content-blocked.https.html was blocked from connecting insecurely to ws://127.0.0.1:8880/websocket/tests/hybi/simple either because the protocol is insecure or the page is embedded from an insecure page.
+
+Test topFrame http with embedded https blocks worker from iframe.
+
+
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-with-embedded-http-expected.txt b/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-with-embedded-http-expected.txt
new file mode 100644 (file)
index 0000000..4acaa5f
--- /dev/null
@@ -0,0 +1,5 @@
+CONSOLE MESSAGE: WebSocket connection failed: The page at https://localhost:8443/websocket/tests/hybi/resources/non-document-mixed-content-blocked-embedded-http.https.html was blocked from connecting insecurely to ws://127.0.0.1:8880/websocket/tests/hybi/simple either because the protocol is insecure or the page is embedded from an insecure page.
+
+Test topFrame http with embedded https with embedded http blocks worker connecting to insecure webSocket.
+
+
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-with-embedded-http.html b/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-with-embedded-http.html
new file mode 100644 (file)
index 0000000..0395359
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<head></head>
+<body>
+<iframe src="https://localhost:8443/websocket/tests/hybi/resources/non-document-mixed-content-blocked-embedded-http.https.html"></iframe>
+<p>Test topFrame http with embedded https with embedded http blocks worker connecting to insecure webSocket.
+</p>
+<p></p>
+<pre id=log>
+</pre>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https.html b/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https.html
new file mode 100644 (file)
index 0000000..b444e71
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<head></head>
+<body>
+<iframe src="https://localhost:8443/websocket/tests/hybi/non-document-mixed-content-blocked.https.html"></iframe>
+<p>Test topFrame http with embedded https blocks worker from iframe.
+</p>
+<p></p>
+<pre id=log>
+</pre>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https-expected.txt b/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https-expected.txt
new file mode 100644 (file)
index 0000000..a7508c4
--- /dev/null
@@ -0,0 +1,7 @@
+CONSOLE MESSAGE: The page at about:blank was allowed to display insecure content from http://localhost:8000/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https.html.
+
+CONSOLE MESSAGE: WebSocket connection failed: The page at https://localhost:8443/websocket/tests/hybi/non-document-mixed-content-blocked.https.html was blocked from connecting insecurely to ws://127.0.0.1:8880/websocket/tests/hybi/simple either because the protocol is insecure or the page is embedded from an insecure page.
+
+Test topFrame https with embedded http with embedded https blocks worker trying to connect with insecure webSocket.
+
+
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https.html b/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https.html
new file mode 100644 (file)
index 0000000..f28cfff
--- /dev/null
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script>
+
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+}
+if (window.internals)
+    internals.settings.setAllowDisplayOfInsecureContent(true);
+
+</script>
+</head>
+<body>
+<iframe src="http://localhost:8000/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https.html"></iframe>
+<p>Test topFrame https with embedded http with embedded https blocks worker trying to connect with insecure webSocket.
+</p>
+<p></p>
+<pre id=log>
+</pre>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked.https-expected.txt b/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked.https-expected.txt
new file mode 100644 (file)
index 0000000..22ab54d
--- /dev/null
@@ -0,0 +1,9 @@
+CONSOLE MESSAGE: WebSocket connection failed: The page at https://127.0.0.1:8443/websocket/tests/hybi/non-document-mixed-content-blocked.https.html was blocked from connecting insecurely to ws://127.0.0.1:8880/websocket/tests/hybi/simple either because the protocol is insecure or the page is embedded from an insecure page.
+Test mixed content from unsecure WebSocket within Worker blocked from secure page.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS worker onerror. readyState 3
+DONE
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked.https.html b/LayoutTests/http/tests/websocket/tests/hybi/non-document-mixed-content-blocked.https.html
new file mode 100644 (file)
index 0000000..d893e1b
--- /dev/null
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head></head>
+<body>
+<p>Test mixed content from unsecure WebSocket within Worker blocked from secure page.</p>
+<p></p>
+<p>On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".</p>
+<pre id=log>
+</pre>
+<script>
+if (location.protocol !== "https:")
+    location = "https://127.0.0.1:8443/wesocket/tests/hybi/non-document-mixed-content-blocked.https.html";
+
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+}
+
+if (window.internals)
+    internals.settings.setAllowDisplayOfInsecureContent(true);
+
+
+function log(message)
+{
+    document.getElementById("log").innerHTML += message + "\n";
+}
+
+function endTest()
+{
+    log("TEST COMPLETE");
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+var worker = new Worker("resources/non-document-mixed-content-blocked.js");
+worker.onmessage = function (event)
+{
+    log(event.data);
+    if (event.data == "DONE")
+        endTest();
+};
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked-embedded-http.https.html b/LayoutTests/http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked-embedded-http.https.html
new file mode 100644 (file)
index 0000000..61277da
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head></head>
+<body>
+<iframe src="http://localhost:8000/websocket/tests/hybi/resources/non-document-mixed-content-blocked.html"></iframe>
+<p>Https page with embedded http</p>
+<pre id=log>
+</pre>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked.html b/LayoutTests/http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked.html
new file mode 100644 (file)
index 0000000..06135f6
--- /dev/null
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head></head>
+<body>
+<p></p>
+<p>Plain http page trying to connect to a worker.</p>
+<pre id=log>
+</pre>
+<script>
+
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+}
+
+function log(message)
+{
+    document.getElementById("log").innerHTML += message + "\n";
+}
+
+function endTest()
+{
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+var worker = new Worker("non-document-mixed-content-blocked.js");
+worker.onmessage = function (event)
+{
+    log(event.data);
+    if (event.data == "DONE")
+        endTest();
+};
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked.js b/LayoutTests/http/tests/websocket/tests/hybi/resources/non-document-mixed-content-blocked.js
new file mode 100644 (file)
index 0000000..b802ba0
--- /dev/null
@@ -0,0 +1,29 @@
+if (self.postMessage)
+    runTests();
+else
+    onconnect = handleConnect;
+
+function handleConnect(event)
+{
+    // For shared workers, create a faux postMessage() API to send message back to the parent page.
+    self.postMessage = function (message) { event.ports[0].postMessage(message); };
+    runTests();
+};
+
+function runTests()
+{
+    var ws = new WebSocket('ws://127.0.0.1:8880/websocket/tests/hybi/simple');
+
+    ws.onopen = function() {
+        postMessage("FAIL: worker: Connected. readyState " + ws.readyState);
+        postMessage("DONE");
+    };
+    ws.onerror = function(error) {
+        postMessage("PASS worker onerror. readyState " + ws.readyState);
+        postMessage("DONE");
+    };
+
+    setTimeout(function() {
+        postMessage("FAIL worker timeout");
+    }, 3000);
+}
index 111b642..d3b4288 100644 (file)
@@ -6,8 +6,7 @@ async function doTest(event)
     }
 
     try {
-        var webSocket = new WebSocket('ws://localhost:8880/websocket/tests/hybi/workers/resources/echo');
-
+        var webSocket = new WebSocket('wss://localhost:9323/websocket/tests/hybi/workers/resources/echo');
         webSocket.onerror = (e) => {
             event.source.postMessage("FAIL: websocket had an error: " + e);
         };
index 52122b7..d23fe98 100644 (file)
@@ -1,3 +1,34 @@
+2019-09-24  Kate Cheney  <katherine_cheney@apple.com>
+
+        Mixed content blocking is bypassed for WebSockets in Workers (159726)
+        https://bugs.webkit.org/show_bug.cgi?id=159726
+        <rdar://problem/27326438>
+
+        Reviewed by Brady Eidson.
+
+        Tests: http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https-with-embedded-http.html
+               http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-http-with-embedded-https.html
+               http/tests/websocket/tests/hybi/non-document-mixed-content-blocked-https-with-embedded-http-with-embedded-https.https.html
+               http/tests/websocket/tests/hybi/non-document-mixed-content-blocked.https.html
+        
+        * Modules/websockets/WebSocket.cpp:
+        (WebCore::WebSocket::connect):
+        * Modules/websockets/WorkerThreadableWebSocketChannel.cpp:
+        (WebCore::WorkerThreadableWebSocketChannel::Bridge::connect):
+        * loader/MixedContentChecker.cpp:
+        (WebCore::MixedContentChecker::checkForMixedContentInFrameTree):
+        * loader/MixedContentChecker.h:
+        Patch to block insecure WebSocket URL connection attempts by Workers
+        on secure pages. If the URL is ws, and any embedding iframe has protocol
+        https, and the context is a Worker, the connection should be blocked.
+
+        I was unable to write a test case to hit the case where a document
+        does not have a frame and tries to communicate via a worker to
+        connect to a WebSocket because after removing the subframe from its
+        parent, the subframe is unable to perform a postMessage to a worker
+        even in the same script execution to tell the worker to connect
+        to a WebSocket.
+
 2019-09-24  Zalan Bujtas  <zalan@apple.com>
 
         [LFC][IFC] Line::Content should use LineBox
index 9108800..3038a97 100644 (file)
@@ -305,6 +305,7 @@ ExceptionOr<void> WebSocket::connect(const String& url, const Vector<String>& pr
     if (is<Document>(context)) {
         Document& document = downcast<Document>(context);
         RefPtr<Frame> frame = document.frame();
+        // FIXME: make the mixed content check equivalent to the non-document mixed content check currently in WorkerThreadableWebSocketChannel::Bridge::connect()
         if (!frame || !frame->loader().mixedContentChecker().canRunInsecureContent(document.securityOrigin(), m_url)) {
             failAsynchronously();
             return { };
index 982e573..050b8cc 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "Blob.h"
 #include "Document.h"
+#include "FrameLoader.h"
 #include "ScriptExecutionContext.h"
 #include "SocketProvider.h"
 #include "ThreadableWebSocketChannelClientWrapper.h"
@@ -402,9 +403,20 @@ void WorkerThreadableWebSocketChannel::Bridge::connect(const URL& url, const Str
 
     m_loaderProxy.postTaskToLoader([peer = m_peer, url = url.isolatedCopy(), protocol = protocol.isolatedCopy()](ScriptExecutionContext& context) {
         ASSERT(isMainThread());
-        ASSERT_UNUSED(context, context.isDocument());
+        ASSERT(context.isDocument());
         ASSERT(peer);
 
+        auto& document = downcast<Document>(context);
+        
+        // FIXME: make this mixed content check equivalent to the document mixed content check currently in WebSocket::connect()
+        if (document.frame()) {
+            Optional<String> errorString = document.frame()->loader().mixedContentChecker().checkForMixedContentInFrameTree(url);
+            if (errorString) {
+                peer->fail(errorString.value());
+                return;
+            }
+        }
+
         if (peer->connect(url, protocol) == ThreadableWebSocketChannel::ConnectStatus::KO)
             peer->didReceiveMessageError();
     });
index fade01b..3461241 100644 (file)
@@ -120,6 +120,28 @@ void MixedContentChecker::checkFormForMixedContent(SecurityOrigin& securityOrigi
     client().didDisplayInsecureContent();
 }
 
+Optional<String> MixedContentChecker::checkForMixedContentInFrameTree(const URL& url)
+{
+    auto* document = m_frame.document();
+
+    while (document) {
+        RELEASE_ASSERT_WITH_MESSAGE(document->frame(), "An unparented document tried to connect to a websocket with url: %s", url.string().utf8().data());
+        
+        auto* frame = document->frame();
+        if (isMixedContent(document->securityOrigin(), url))
+            return makeString("The page at ", document->url().stringCenterEllipsizedToLength(), " was blocked from connecting insecurely to ", url.stringCenterEllipsizedToLength(), " either because the protocol is insecure or the page is embedded from an insecure page.");
+
+        if (frame->isMainFrame())
+            break;
+
+        frame = frame->tree().parent();
+        RELEASE_ASSERT_WITH_MESSAGE(frame, "Should never have a parentless non main frame");
+        document = frame->document();
+    }
+    
+    return WTF::nullopt;
+}
+
 void MixedContentChecker::logWarning(bool allowed, const String& action, const URL& target) const
 {
     const char* errorString = allowed ? " was allowed to " : " was not allowed to ";
index 5fdfff8..6889e5f 100644 (file)
@@ -58,6 +58,7 @@ public:
     bool canRunInsecureContent(SecurityOrigin&, const URL&) const;
     void checkFormForMixedContent(SecurityOrigin&, const URL&) const;
     static bool isMixedContent(SecurityOrigin&, const URL&);
+    Optional<String> checkForMixedContentInFrameTree(const URL&);
 
 private:
     // FIXME: This should probably have a separate client from FrameLoader.