Hatch declarative message extension style
authorBill Erickson <berickxx@gmail.com>
Wed, 23 Nov 2016 21:38:30 +0000 (16:38 -0500)
committerBill Erickson <berickxx@gmail.com>
Wed, 23 Nov 2016 21:40:16 +0000 (16:40 -0500)
Avoid the requirement to hard-code the allowed externally_connectable
hosts by letting the extension ask for permissions instead.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
INSTALL.adoc
extension/app/content.js [new file with mode: 0644]
extension/app/extension.js [new file with mode: 0644]
extension/app/main.js [deleted file]
extension/app/manifest.json

index 6aa36a1..d971cc0 100644 (file)
@@ -77,20 +77,6 @@ $ ./hatch.sh test
 
 === Setup Chrome Extension ===
 
-==== Tweak Extension Values ====
-
-Edit extension/app/manifest.json and change the "evergreen.example.org" value 
-found here to the hostname of your Evergreen server.
-
-[source,js]
--------------------------------------------------------------------------
-...
-"externally_connectable": {
-  "matches": ["*://evergreen.example.org/*"]
-}
-...
--------------------------------------------------------------------------
-
 NOTE: At time of writing, the Evergreen server used must have the
 patches included in the http://git.evergreen-ils.org/?p=working/Evergreen.git;a=shortlog;h=refs/heads/user/berick/lp1640255-hatch-native-messaging[Hatch Native Messaging working branch].
 
diff --git a/extension/app/content.js b/extension/app/content.js
new file mode 100644 (file)
index 0000000..07ce8b7
--- /dev/null
@@ -0,0 +1,72 @@
+/* -----------------------------------------------------------------------
+ * Copyright 2016 King County Library System
+ * Bill Erickson <berickxx@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * -----------------------------------------------------------------------
+ *
+ * Hatch Content Script.
+ *
+ * Relays messages between the browser tab and the Hatch extension.js
+ * script.
+ */
+
+console.debug('Loading Hatch relay content script');
+
+// Tell the page DOM we're here.
+document.body.setAttribute('hatch-is-open', '4-8-15-16-23-42');
+
+/**
+ * Open a port to our extension.
+ */
+var port = chrome.runtime.connect();
+
+/**
+ * Relay all messages received from the extension back to the tab
+ */
+port.onMessage.addListener(function(message) {
+
+    /*
+    console.debug(
+        "Content script received from extension: "+ JSON.stringify(message));
+    */
+
+    window.postMessage(message, location.origin);
+});
+
+
+/**
+ * Receive messages from the browser tab and relay them to the
+ * Hatch extension script.
+ */
+window.addEventListener("message", function(event) {
+
+    // We only accept messages from ourselves
+    if (event.source != window) return;
+
+    var message = event.data;
+
+    // Ignore broadcast messages.  We only care about messages
+    // received from our browser tab/page.
+    if (message.from != 'page') return;
+
+    /* 
+    console.debug(
+        "Content script received from page: " + JSON.stringify(message));
+    */
+
+    // standard Hatch-bound message; relay to extension.
+    port.postMessage(message);
+
+}, false);
+
+
+
diff --git a/extension/app/extension.js b/extension/app/extension.js
new file mode 100644 (file)
index 0000000..a8a72ff
--- /dev/null
@@ -0,0 +1,147 @@
+/* -----------------------------------------------------------------------
+ * Copyright 2016 King County Library System
+ * Bill Erickson <berickxx@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * -----------------------------------------------------------------------
+ */
+
+// Singleton connection to Hatch
+var hatchPort = null;
+
+// Map of tab identifers to tab-specific connection ports.
+var browserPorts = {};
+
+
+/**
+ * Handle response messages received from Hatch.
+ */
+function onNativeMessage(message) {
+    var tabId = message.clientid;
+
+    if (tabId) {
+        if (browserPorts[tabId]) {
+            message.from = 'extension';
+            browserPorts[tabId].postMessage(message);
+
+        } else {
+            console.warn(
+                "Hatch message contains port ID " + tabId +
+                " which was not found in the browser tab map. " +
+                "Unable to deliver response to browser");
+        }
+
+    } else {
+        console.warn("Hatch response does not contain a 'clientid' value. " +
+            "Unable to deliver response to browser");
+    }
+}
+
+/**
+ * Called when the connection to Hatch goes away.
+ */
+function onDisconnected() {
+  console.warn("Hatch connection failed: " + chrome.runtime.lastError.message);
+  hatchPort = null;
+  browserPorts = {};
+}
+
+
+/**
+ * Called when our content script opens connection to this extension.
+ */
+chrome.runtime.onConnect.addListener(function(port) {
+    var tabId = port.sender.tab.id;
+
+    browserPorts[tabId] = port;
+    console.debug('new port connected with id ' + tabId);
+
+    port.onMessage.addListener(function(msg) {
+        console.debug("Received message from browser on port " + tabId);
+
+        if (!msg) { // belt+suspenders
+            console.warn("Received NULL message");
+            return;
+        }
+
+        // tag the message with the browser tab ID for response routing.
+        msg.clientid = tabId;
+
+        if (msg.action == 'init') {
+            // "init" messages require origin info.
+            // Extract just the protocol + host
+            msg.origin = port.sender.url.match(/https?:\/\/[^\/]+/)[0];
+            console.debug("Init'ing message with origin: " + msg.origin);
+        }
+
+        hatchPort.postMessage(msg);
+    });
+
+    port.onDisconnect.addListener(function() {
+        console.log("Removing port " + tabId + " on tab disconnect");
+        delete browserPorts[tabId];
+    });
+});
+
+
+function setPageActionRules() {
+    // Replace all rules on extension reload
+    chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
+        chrome.declarativeContent.onPageChanged.addRules([
+            {
+                conditions: [
+                    new chrome.declarativeContent.PageStateMatcher({
+                        pageUrl : {
+                            pathPrefix : '/eg/staff/',
+                            schemes : ['https']
+                        },
+                        css: ["eg-navbar"] // match on <eg-navbar/>
+                    })
+                ],
+                actions: [ 
+                    new chrome.declarativeContent.RequestContentScript({
+                        'js': ['content.js']
+                    })
+                ]
+            }
+        ]);
+    });
+}
+
+chrome.browserAction.onClicked.addListener(function (tab) {
+    chrome.permissions.request({
+        origins: ['https://*/eg/staff/*']
+    }, function (ok) {
+        if (ok) {
+            console.log('access granted');
+        } else if (chrome.runtime.lastError) {
+            alert('Permission Error: ' + chrome.runtime.lastError.message);
+        } else {
+            alert('Optional permission denied.');
+        }
+    });
+});
+
+
+/**
+ * Link the page action icon to loading the content script
+ */
+chrome.runtime.onInstalled.addListener(setPageActionRules);
+
+/**
+ * Connect to Hatch on startup.
+ */
+var hostName = "org.evergreen_ils.hatch";
+console.debug("Connecting to native messaging host: " + hostName);
+hatchPort = chrome.runtime.connectNative(hostName);
+hatchPort.onMessage.addListener(onNativeMessage);
+hatchPort.onDisconnect.addListener(onDisconnected);
+
diff --git a/extension/app/main.js b/extension/app/main.js
deleted file mode 100644 (file)
index 64b49c4..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/* -----------------------------------------------------------------------     
- * Copyright 2016 King County Library System                                   
- * Bill Erickson <berickxx@gmail.com>                                          
- *                                                                             
- * This program is free software; you can redistribute it and/or               
- * modify it under the terms of the GNU General Public License                 
- * as published by the Free Software Foundation; either version 2              
- * of the License, or (at your option) any later version.                      
- *                                                                             
- * This program is distributed in the hope that it will be useful,             
- * but WITHOUT ANY WARRANTY; without even the implied warranty of              
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               
- * GNU General Public License for more details.                                
- * -----------------------------------------------------------------------     
- */ 
-
-// Singleton connection to Hatch
-var hatchPort = null;
-
-// Map of tab identifers to tab-specific connection ports.
-var browserPorts = {};
-
-
-/**
- * Handle response messages received from Hatch.
- */
-function onNativeMessage(message) {
-    var tabId = message.clientid;
-
-    if (tabId) {
-        if (browserPorts[tabId]) {
-            browserPorts[tabId].postMessage(message);
-
-        } else {
-            console.warn(
-                "Hatch message contains port ID " + tabId +
-                " which was not found in the browser tab map. " +
-                "Unable to deliver response to browser");
-        }
-
-    } else {
-        console.warn("Hatch response does not contain a 'clientid' value. " +
-            "Unable to deliver response to browser");
-    }
-}
-
-/**
- * Called when the connection to Hatch goes away.
- */
-function onDisconnected() {
-  console.warn("Failed to connect: " + chrome.runtime.lastError.message);
-  hatchPort = null;
-  browserPorts = {};
-}
-
-/**
- * Respond to 'ping' requests to let the browser know we exist.
- */
-chrome.runtime.onMessageExternal.addListener(
-    function(request, sender, sendResponse) {
-        if (request && request.ping) {
-            console.debug("Got 'ping' request from tab: " + sender.tab.id);
-            sendResponse({pong : true});
-        }
-        return true;
-    }
-);
-
-/**
- * Called when a browser tab opens a connection to this extension.
- */
-chrome.runtime.onConnectExternal.addListener(function(port) {
-    var tabId = port.sender.tab.id;
-
-    browserPorts[tabId] = port;
-    console.debug('new port connected with id ' + tabId);
-
-    port.onMessage.addListener(function(msg) {
-        console.debug("Received message from browser on port " + tabId);
-
-        if (!msg) { // belt+suspenders
-            console.warn("Received NULL message");
-            return;
-        }
-
-        // tag the message with the browser tab ID for response routing.
-        msg.clientid = tabId; 
-
-        if (msg.action == 'init') {
-            // "init" messages require origin info.
-            // Extract just the protocol + host
-            msg.origin = port.sender.url.match(/https?:\/\/[^\/]+/)[0];
-            console.debug("Init'ing message with origin: " + msg.origin);
-        }
-
-        hatchPort.postMessage(msg);
-    });
-
-    port.onDisconnect.addListener(function() {
-        console.log("Removing port " + tabId + " on tab disconnect");
-        delete browserPorts[tabId];
-    });
-});
-    
-
-/**
- * Connect to Hatch on startup.
- */
-var hostName = "org.evergreen_ils.hatch";
-console.debug("Connecting to native messaging host: " + hostName);
-hatchPort = chrome.runtime.connectNative(hostName);
-hatchPort.onMessage.addListener(onNativeMessage);
-hatchPort.onDisconnect.addListener(onDisconnected);
-
index 902cb48..ec39606 100644 (file)
@@ -1,17 +1,22 @@
 {
   // Extension ID: knldjmfmopnpolahpmmgbagdohdnhkik
   "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB",
-  "name": "Hatch Native Messaging Extension",
+  "name": "Hatch Native Messenger",
   "version": "1.0",
   "manifest_version": 2,
   "description": "Relays messages to/from Hatch.",
   "background" : {
-    "scripts" : ["main.js"]
+    "scripts" : ["extension.js"]
+  },
+  "browser_action": {
+    "default_title": "Hatch"
   },
   "permissions": [
-    "nativeMessaging"
+    "nativeMessaging",
+    "declarativeContent"
+  ],
+  "optional_permissions": [
+    "https://*/eg/staff/*"
   ],
-  "externally_connectable": {
-    "matches": ["*://evergreen.example.org/*"]
-  }
+  "minimum_chrome_version": "38"
 }