diff --git a/background_scripts/bootstrap.js b/background_scripts/bootstrap.js index aa7e29d..ae840ca 100644 --- a/background_scripts/bootstrap.js +++ b/background_scripts/bootstrap.js @@ -4,6 +4,7 @@ const {packingLib, standards, storageLib} = window.WEB_API_MANAGER; const rootObject = window.browser || window.chrome; + const defaultKey = "(default)"; // Once loaded from storage, will be a mapping from regular expressions // (or the default option, "(default)"), to an array of standards @@ -14,14 +15,42 @@ domainRules = loadedDomainRules; }); - rootObject.runtime.onMessage.addListener(function (request, sender, tab) { - const [label, data] = request; - // Listen for updates to the domain rules from the config page. - if (label === "rulesUpdate") { - domainRules = data; - } - }); + // Manage the state of the browser activity, by displaying the number + // of origins / frames + const updateBrowserActionBadge = function (activeInfo) { + const {tabId, windowId} = activeInfo; + rootObject.tabs.executeScript( + tabId, + { + allFrames: true, + code: "window.location.host" + }, + function (allHosts) { + if (rootObject.runtime.lastError) { + rootObject.browserAction.disable(tabId); + return; + } + + rootObject.browserAction.enable(tabId); + + const numFrames = allHosts + ? Array.from(new Set(allHosts)).length.toString() + : ""; + + rootObject.browserAction.setBadgeText({ + text: numFrames, + tabId: tabId + }); + } + ); + }; + + rootObject.tabs.onUpdated.addListener(updateBrowserActionBadge); + rootObject.tabs.onActivated.addListener(updateBrowserActionBadge); + rootObject.windows.onFocusChanged.addListener(updateBrowserActionBadge); + + // Inject the blocking settings for each visited domain / frame. const extractHostFromUrl = function (url) { const uri = URI(url); return uri.hostname(); @@ -40,6 +69,37 @@ return prev; }; + const whichDomainRuleMatches = function (hostName) { + // of the URL being requested. + const matchingUrlReduceFunctionBound = matchingUrlReduceFunction.bind(undefined, hostName); + const matchingPattern = Object + .keys(domainRules) + .filter((aRule) => aRule !== defaultKey) + .sort() + .reduce(matchingUrlReduceFunctionBound, undefined); + + return matchingPattern || defaultKey; + }; + + // Listen for updates to the domain rules from the config page. + rootObject.runtime.onMessage.addListener(function (request, sender, sendResponse) { + const [label, data] = request; + if (label === "rulesUpdate") { + domainRules = data; + return; + } + + if (label === "rulesForDomains") { + const ruleForDomain = data.map(whichDomainRuleMatches); + const mapping = {}; + for (let i = 0; i < ruleForDomain.length; i += 1) { + mapping[data[i]] = ruleForDomain[i]; + } + sendResponse(mapping); + return; + } + }); + const requestFilter = { urls: [""], types: ["main_frame", "sub_frame"] @@ -50,18 +110,11 @@ const url = details.url; const hostName = extractHostFromUrl(url); - const defaultKey = "(default)"; // Decide which set of blocking rules to use, depending on the host // of the URL being requested. - const matchingUrlReduceFunctionBound = matchingUrlReduceFunction.bind(undefined, hostName); - const matchingPattern = Object - .keys(domainRules) - .filter((aRule) => aRule !== defaultKey) - .sort() - .reduce(matchingUrlReduceFunctionBound, undefined); - - const standardsToBlock = domainRules[matchingPattern || defaultKey]; + const matchingDomainKey = whichDomainRuleMatches(hostName); + const standardsToBlock = domainRules[matchingDomainKey]; const options = Object.keys(standards); const packedValues = packingLib.pack(options, standardsToBlock); diff --git a/config/index.html b/config/index.html index 9a66ca7..fbb10ff 100644 --- a/config/index.html +++ b/config/index.html @@ -2,7 +2,6 @@ - Web API Manager Configuration diff --git a/images/uic-128.png b/images/uic-128.png new file mode 100644 index 0000000..49667fa Binary files /dev/null and b/images/uic-128.png differ diff --git a/images/uic-48.png b/images/uic-48.png new file mode 100644 index 0000000..65d9559 Binary files /dev/null and b/images/uic-48.png differ diff --git a/manifest.json b/manifest.json index 14a39d3..b92f9f7 100644 --- a/manifest.json +++ b/manifest.json @@ -3,14 +3,21 @@ "name": "WebAPI Manager", "version": "0.4", "description": "Improves browser security by restricting page access to parts of the Web API.", + "icons": { + "48": "images/uic-48.png", + "128": "images/uic-128.png" + }, + "browser_action": { + "default_popup": "popup/popup.html" + }, "permissions": [ - "http://*/*", "https://*/*", "", "storage", "tabs", "webRequest", "webRequestBlocking", - "webNavigation" + "webNavigation", + "activeTab" ], "content_scripts": [ { diff --git a/popup/js/example.js b/popup/js/example.js new file mode 100644 index 0000000..6639d6f --- /dev/null +++ b/popup/js/example.js @@ -0,0 +1,37 @@ +(function () { + const rootObject = window.browser || window.chrome; + + rootObject.tabs.executeScript( + { + allFrames: true, + code: "window.location.host" + }, + function (response) { + + const uniqueDomains = Array.from(new Set(response)).sort(); + const message = ["rulesForDomains", uniqueDomains]; + rootObject.runtime.sendMessage(message, function (response) { + + const listGroupElm = document.querySelector("ul.list-group"); + const domainNames = Object.keys(response); + + domainNames.forEach(function (aDomain) { + const domainRule = response[aDomain]; + + const liElm = document.createElement("li"); + liElm.className = "list-group-item"; + + const spanElm = document.createElement("span"); + spanElm.className = "badge"; + const badgeText = document.createTextNode(domainRule); + spanElm.appendChild(badgeText); + liElm.appendChild(spanElm); + + const textElm = document.createTextNode(aDomain); + liElm.appendChild(textElm); + listGroupElm.appendChild(liElm); + }); + }); + } + ); +}()); \ No newline at end of file diff --git a/popup/popup.html b/popup/popup.html new file mode 100644 index 0000000..7818f3e --- /dev/null +++ b/popup/popup.html @@ -0,0 +1,20 @@ + + + + + + + Frame listing popup + + + + +
+ This page has loaded the followng frames… +
    +
+
+ + + +