ad7fcfaedb62 — Josiah Ulfers 4 years ago
Check tab url against whitelist resolves #1
4 files changed, 42 insertions(+), 20 deletions(-)

M _static/addon/background.js
M _static/addon/manifest.json
M _static/addon/savior.js
M _static/test.html
M _static/addon/background.js +18 -9
@@ 1,15 1,19 @@ 
+;(function () {
 'use strict'
-// es6
 
 /* Manual tests:
 
-- Open 2 windows. Toggle in one window should not affect the other.
+- Open 2 windows. Toggle in one window should affect the other only if they are on same domain.
 - Open preferences, change whitelist. All windows should reflect changes (icons and prefs).
 
 */
 
+function whitelisted(tab) { return settings.whitelist.includes(domain(tab.url)) }
+
 function updateIcon(tab) {
-    const enabled = ! settings.whitelist.includes(domain(tab.url))
+    // I'm guessing tab.url case-folds and normalizes the same way as HTMLAnchorElement.href,
+    // but would be nice if I could find documentation to that effect.
+    const enabled = ! whitelisted(tab)
     browser.browserAction.setIcon({
         // tabId is an unfortunate name. Because there's only one of these buttons browser per
         // window, not per tab, it only comes into play when you have multiple windows

          
@@ 34,14 38,17 @@ function updateIcon(tab) {
 }
 function updateLocation () {
     browser.tabs.query({active: true})
-    // I'm guessing tabs[0].url case-folds and normalizes the same way as HTMLAnchorElement.href,
-    // but would be nice if I could find documentation to that effect.
-    .then(
-        tabs => { for (const k in tabs) { updateIcon(tabs[k]) } },
-        error => { throw error })
+    // Only the focused window receives tab update and activated events, so need to enumerate
+    // active tabs in all open windows so that the icon state reflects the change
+    .then(tabs => {
+        for (let t of tabs) {
+            updateIcon(t)
+            // No need to filter this message to active tabs, but this is simplest 
+            browser.tabs.sendMessage(t.id, whitelisted(t))
+        }})
 }
 updateLocation()
-// Copying the example https://github.com/mdn/webextensions-examples/tree/master/bookmark-it,
+// Copying the example https://github.com/mdn/webextensions-examples/tree/master/bookmark-it
 browser.tabs.onUpdated.addListener(updateLocation) // url changes
 browser.tabs.onActivated.addListener(updateLocation) // tab switching
 onSettingsChange(updateLocation)

          
@@ 64,3 71,5 @@ browser.browserAction.onClicked.addListe
     }
     browser.storage.local.set(settings) // triggers icon state update via prefs listeners
 })
+
+})()

          
M _static/addon/manifest.json +1 -4
@@ 22,10 22,7 @@ 
   "content_scripts": [
     {
       "all_frames": true,
-      "js": [
-        "shared.js",
-        "savior.js"
-      ],
+      "js": ["savior.js"],
       "matches": [
         "<all_urls>"
       ],

          
M _static/addon/savior.js +12 -4
@@ 1,14 1,20 @@ 
+;(function () {
 'use strict'
-// es6
 
 const inputWhitelist = ["text","number","password","search","email","tel","url"]
 
-// todo: don't allow enter key on most textual inputs
+// Maintain a whitelisted toggle in sync from background script since checking
+// `window.top.location.hostname` fails for cross-domain iframes:
+let whitelisted = false
+browser.runtime.onMessage.addListener(w =>
+    // Though I haven't found documentation to this effect, empirically, it seems that messages
+    // don't cross to other addons, so no need to filter.
+    whitelisted = w)
+
 function saveKeys(keyEvent) {
     const _ = keyEvent.target
 
-    // Domains normalized at save time, assume window.location.hostname is normalized same way:
-    const allow = settings.whitelist.includes(window.location.hostname)
+    const allow = whitelisted
     || keyEvent.key === 'Escape'
     || _.isContentEditable
     || document.fullscreen || document.mozFullScreen

          
@@ 23,3 29,5 @@ function saveKeys(keyEvent) {
 window.addEventListener('keydown', saveKeys, true)
 window.addEventListener('keypress', saveKeys, true)
 window.addEventListener('keyup', saveKeys, true)
+
+})()

          
M _static/test.html +11 -3
@@ 171,15 171,23 @@ pointerTrap.onclick = function () {
 
 <h2>Iframe</h2>
 
-<p>To test cross-domain iframes, this page needs to be published somewhere, such as <a href="http://keyboard-savior-xtreme.readthedocs.io/en/latest/_static/test.html">http://keyboard-savior-xtreme.readthedocs.io/en/latest/_static/test.html</a>, then loaded while hosting this test page on <a href="http://localhost:8000/_static/test.html">http://localhost:8000/_static/test.html</a>.
+<p>To test cross-domain iframes, access this page from somewhere like 127.0.0.1 or <a href="http://keyboard-savior-xtreme.readthedocs.io/en/latest/_static/test.html">http://keyboard-savior-xtreme.readthedocs.io/en/latest/_static/test.html</a>, while hosting it on <a href="http://localhost:8000/_static/test.html">http://localhost:8000/_static/test.html</a>.
+
+If set up correctly, the "top location" displayed in the iframe should show a cross-origin security error.
+
+Whitelisting the top-level domain should also allow the embedded page to capture keyboard events.
 </p>
 
 <script>
 'use strict'
 document.write('<p>my location: ' + window.location + '</p>')
-document.write('<p>top location: ' + window.top.location + '</p>')
+try {
+  document.write('<p>top location: ' + window.top.location + '</p>')
+} catch (e) {
+  document.write('<p>top location: ' + e + '</p>')
+}
 if (window.location.hostname !== 'localhost') {
-  document.write('<iframe src="http://localhost:8000/_static/test.html"></iframe>')
+  document.write('<iframe width=800 height=1000 src="http://localhost:8000/_static/test.html"></iframe>')
 }
 </script>