diff --git a/PROTOCOL.md b/PROTOCOL.md index 2512564..810388b 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -27,7 +27,8 @@ GET /ws/?as=[&injector=] (WebSocket upgrade) A session has at most one host and one observer; a newer connection replaces the older one, which is closed with code `4001`. Any number of remotes can join. `injector` is free text describing how the host got injected -(`bookmark`, `extension_chrome`) and is shown in the connection info. +(`bookmark`, `extension_chrome`, `extension_firefox`) and is shown in the +connection info. The first frame the server sends is always `init`: diff --git a/static/js/indexController.js b/static/js/indexController.js index 35f3b39..b4cb441 100644 --- a/static/js/indexController.js +++ b/static/js/indexController.js @@ -26,17 +26,18 @@ slideApp.controller("indexController", ["$scope", "$http", "$cookies", "$timeout console.log(data.info) $scope.statusIcon.showMessage("check", "lime", [200], "Connected", 2000); - // Notify extension - try { - chrome.runtime.sendMessage($scope.extension.id.chrome, {session: $scope.session}, function (msg) { - }); + // Notify extension. If it opened this tab just to get a session, close + // the tab once the extension has it - or after a second at the latest, + // as extension versions up to 1.2.0 never answer this message. + var closeSessionOnlyTab = function () { if ($state.params.sessionOnly) { $timeout(function () { window.close(); }, 100); } - } catch (ignoerd) { - } + }; + $scope.extension.send({session: $scope.session}, closeSessionOnlyTab); + $timeout(closeSessionOnlyTab, 900); }); } else if (data.state == "not_found") {// This should be impossible here, since the observer generates the new session ID console.warn("Session not found"); diff --git a/static/js/mainController.js b/static/js/mainController.js index 265307f..41915f2 100644 --- a/static/js/mainController.js +++ b/static/js/mainController.js @@ -94,17 +94,41 @@ slideApp.controller("slideController", ["$scope", "$cookies", "$location", "$htt // chrome: "enmfbjneielhjagdgebkmcdhgbbionek" chrome: "ojmlekgoiolmbfddhkdbnlpndbcipfkp" }, - installed: false - }; - try { - chrome.runtime.sendMessage($scope.extension.id.chrome, {ping: "hello"}, function (msg) { - console.log(msg) - if (msg && msg.pong && msg.pong == 'hello') { - $scope.extension.installed = true; + installed: false, + // Chrome exposes chrome.runtime to this site once the extension is + // installed (externally_connectable). Firefox doesn't let pages message an + // extension at all, so there the extension's siteBridge.js content script + // relays what is posted to this window, and posts the answer back. + send: function (message, callback) { + if (window.chrome && chrome.runtime && chrome.runtime.sendMessage) { + try { + chrome.runtime.sendMessage($scope.extension.id.chrome, message, function (response) { + void chrome.runtime.lastError; + if (callback) callback(response); + }); + } catch (ignored) { + } + return; } - }); - } catch (ignoerd) { - } + var id = ++extensionBridge.lastId; + if (callback) extensionBridge.callbacks[id] = callback; + window.postMessage({channel: "remoteslide", direction: "to-extension", id: id, message: message}, window.location.origin); + } + }; + var extensionBridge = {lastId: 0, callbacks: {}}; + window.addEventListener("message", function (event) { + var data = event.data; + if (event.source !== window || !data || data.channel !== "remoteslide" || data.direction !== "to-page") return; + var callback = extensionBridge.callbacks[data.id]; + delete extensionBridge.callbacks[data.id]; + if (callback) callback(data.response); + }); + $scope.extension.send({ping: "hello"}, function (msg) { + console.log(msg) + if (msg && msg.pong && msg.pong == 'hello') { + $scope.extension.installed = true; + } + }); $scope.isMobile = function () { return window.mobilecheck();