Localhost SSE from extension broken in Chrome 38?

345 views
Skip to first unread message

Caleb Spare

unread,
Sep 17, 2014, 10:13:54 PM9/17/14
to chromium-...@chromium.org
As of Chrome 38, my extension[0][1] has stopped working.

It allows for controlling a web page (Google Play Music) externally by running a server on localhost and then using Server-Send Events to communicate with the extension.

The JS code runs 'new EventSource("http://localhost:49133")'. As of Chrome 38, this is blocked with the following message:

[blocked] The page at 'https://play.google.com/music/listen#/album/Bifepvyysncjxjwnrmcfh2td6ku/Explosions+in+the+Sky/All+of+a+Sudden+I+Miss+Everyone' was loaded over HTTPS, but ran insecure content from 'http://localhost:49133/': this content should also be loaded over HTTPS.

How can I fix this? I've been reading about CSP[2] and I would have thought that the same policy that applies to XHRs would apply to SSEs:

"The restriction against resources loaded over HTTP applies only to those resources which are directly executed. You're still free, for example, to make XMLHTTPRequest connections to any origin you like; the default policy doesn't restrict connect-src or any of the other CSP directives in any way."

But I guess that doesn't cover SSEs? I tried using various content_security_policy strings in my manifest but couldn't get any effect. I do see

"You may whitelist script and object sources on any port of either http://127.0.0.1 or http://localhost."

but I guess that SSE is different than a script or object source.

Any ideas?

Thanks!
Caleb Spare

Caleb Spare

unread,
Sep 20, 2014, 12:45:52 PM9/20/14
to chromium-...@chromium.org
Anyone know about this? Is there a better place to try asking?
> --
> You received this message because you are subscribed to the Google Groups
> "Chromium-extensions" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to chromium-extens...@chromium.org.
> To post to this group, send email to chromium-...@chromium.org.
> Visit this group at
> http://groups.google.com/a/chromium.org/group/chromium-extensions/.
> To view this discussion on the web visit
> https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/1bba738f-7dbc-4258-8f50-093314aadd7a%40chromium.org.
> For more options, visit https://groups.google.com/a/chromium.org/d/optout.

PhistucK

unread,
Sep 20, 2014, 4:53:34 PM9/20/14
to Caleb Spare, Chromium-extensions
I guess Server Sent Events are like WebSockets. Insecure WebSocket connections are also not allowed in secure web pages.
Perhaps connect-src is the right token (rather than script-src) in the Content Security Policy. I hope I remember the name correctly.
If it still does not work, you can use a background page for the Server Sent Events and pass a message to the page.


PhistucK

--

Caleb Spare

unread,
Sep 22, 2014, 7:01:35 PM9/22/14
to PhistucK, Chromium-extensions
Thanks for the help.

I tried using connect-src, but I get the same error message.
Specifically, I tried both of these:

"content_security_policy": "connect-src 'self' http://localhost;
script-src 'self'; object-src 'self'",

"content_security_policy": "connect-src 'self' http://localhost:49133;
script-src 'self'; object-src 'self'",

I'll try using an event page or background page next.

-Caleb

Caleb Spare

unread,
Sep 22, 2014, 8:19:05 PM9/22/14
to PhistucK, Chromium-extensions
Okay, I almost have it working now. A background page was indeed able
to receive the SSE messages without issue. Now I have another problem:
how can I get the messages from the background page into JS running in
the web page's DOM context? Previously, I was injecting a script onto
the page using "Method 1" from this stack overflow answer:

http://stackoverflow.com/questions/9515704/building-a-chrome-extension-inject-code-in-a-page-using-a-content-script/9517879#9517879

But this injected code cannot receive the message from the background
script; running 'chrome.runtime.onMessage.addListener(...)' gets the
error message:

Uncaught TypeError: Cannot read property 'addListener' of undefined

So how can I pass the message through?

To be clear, I have three different JS files running in three contexts:

1. background.js is running as a background page, and is able to
receive my SSE events and dispatch messages using code like this:

chrome.tabs.query({url: "https://play.google.com/music/*"}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, msg, function() {});
});

2. playctrl.js runs in the DOM context of the target page itself and
knows how to dispatch DOM events that interact with the page. This
script is *not* able to receive the events from the background page.

3. contentscript.js injects playctrl.js onto the page, using code like this:

s = document.createElement("script");
s.src = chrome.extension.getURL("playctrl.js");
s.onload = function() { return this.parentNode.removeChild(this); };
(document.head || document.documentElement).appendChild(s);

This script *is* able to receive events from the background page (but
it cannot dispatch DOM events onto the page itself, hence the need for
injecting playctrl.js onto the page).

Anyone have more advice?

Thanks!
Caleb

Caleb Spare

unread,
Sep 22, 2014, 8:38:51 PM9/22/14
to PhistucK, Chromium-extensions
I've pushed my current non-working attempt up to the chrome-38-fix
branch on my repo; see the extension code here:

https://github.com/cespare/playctrl/tree/chrome-38-fix/extension

-Caleb

PhistucK

unread,
Sep 23, 2014, 1:55:27 AM9/23/14
to Caleb Spare, Chromium-extensions
You can use the regular window.postMessage(messageString, originString) to pass messages from the content script to the page (origin can be "*").


PhistucK

Caleb Spare

unread,
Sep 23, 2014, 2:18:04 AM9/23/14
to PhistucK, Chromium-extensions
Excellent, thanks for your help.

By the way, is there any way to do this in two hops, rather than
three? That is, is there any way that the JS receiving the SSE could
directly talk to JS that can manipulate the DOM on the page?

-Caleb

PhistucK

unread,
Sep 23, 2014, 2:37:45 AM9/23/14
to Caleb Spare, Chromium-extensions

Caleb Spare

unread,
Sep 23, 2014, 2:39:52 AM9/23/14
to PhistucK, Chromium-extensions
Oh, that looks perfect. I'll try it out tomorrow.

Thanks for all your help.

-Caleb

Caleb Spare

unread,
Sep 23, 2014, 6:59:12 PM9/23/14
to PhistucK, Chromium-extensions
Okay, everything works in Chrome 38 now and I've published a new
version of my extension.

https://chrome.google.com/webstore/detail/playctrl/loakeafbjkkagnmmlpadfmknpeedckjg
(version 0.2.1)

I had one point of difficulty: the injected script needs to know the
extension ID in order to get messages from the background page. I
ended up using postMessage to hand over the extension ID.

The working version of my code works like this:

1. When you load Play Music in Chrome, the extension script running on
the page injects a handler script onto the page.
2. After the handler script loads the extension uses postMessage to
tell the handler script the extension ID.
3. The handler script uses chrome.runtime.connect to open a channel to
the background page
4. The background page waits for the handler script to connect using
chrome.runtime.onExternalConnect and then sends any message it
receives via SSE to the handler script with port.postMessage
5. The handler script knows the keyboard shortcuts for the various
controls (such as space key for play/pause) and emits a keydown event
with the correct keycode.

See code here: https://github.com/cespare/playctrl/tree/master/extension

This seems surprisingly complex for what it accomplishes. I also saw
documentation for Native Messaging:

https://developer.chrome.com/extensions/messaging#native-messaging

But I don't think this helps much (it could replace SSE but I don't
think it ultimately solves the difficulty of getting messages from the
background page onto the Play Music page).

Is there something else that could simplify this IPC rube goldberg
machine? Is my use case particularly weird?

Thanks for your help! I'm glad I could get it working.

-Caleb
Reply all
Reply to author
Forward
0 new messages