From c4e524a69f1631e0a32e692190814e2f582e20c2 Mon Sep 17 00:00:00 2001 From: Peter Snyder Date: Fri, 20 Oct 2017 11:09:12 -0400 Subject: [PATCH] large re-org and refactor of code, initial work on dynamically setting the sha256- CSP http policy to fix cookie setting --- background_scripts/background.js | 59 +++- config/index.html | 4 +- content_scripts/dist/README.md | 2 - content_scripts/instrument.js | 29 ++ content_scripts/src/instrument.js | 43 --- content_scripts/src/proxyblock.js | 174 ---------- data/standards.js | 2 + gulpfile.js | 32 +- lib/cookieencoding.js | 82 +++++ {content_scripts/src => lib}/defaults.js | 2 + lib/httpheaders.js | 113 +++++++ lib/init.js | 7 +- lib/proxyblock.js | 236 +++++++++++++ lib/{ => vendor}/URI.js | 0 lib/{ => vendor}/js.cookie.js | 0 lib/vendor/sjcl.js | 60 ++++ manifest.json | 28 +- package-lock.json | 400 ++++++----------------- package.json | 3 +- 19 files changed, 688 insertions(+), 588 deletions(-) delete mode 100644 content_scripts/dist/README.md create mode 100644 content_scripts/instrument.js delete mode 100644 content_scripts/src/instrument.js delete mode 100644 content_scripts/src/proxyblock.js create mode 100644 data/standards.js create mode 100644 lib/cookieencoding.js rename {content_scripts/src => lib}/defaults.js (96%) create mode 100644 lib/httpheaders.js create mode 100644 lib/proxyblock.js rename lib/{ => vendor}/URI.js (100%) rename lib/{ => vendor}/js.cookie.js (100%) create mode 100644 lib/vendor/sjcl.js diff --git a/background_scripts/background.js b/background_scripts/background.js index 10f7984..0022551 100644 --- a/background_scripts/background.js +++ b/background_scripts/background.js @@ -3,7 +3,9 @@ (function () { "use strict"; - const {packingLib, standards, storageLib, domainMatcherLib} = window.WEB_API_MANAGER; + const {storageLib, domainMatcherLib, constants} = window.WEB_API_MANAGER; + const {cookieEncodingLib, proxyBlockLib, httpHeadersLib} = window.WEB_API_MANAGER; + const {standards} = window.WEB_API_MANAGER; const rootObject = window.browser || window.chrome; const defaultKey = "(default)"; @@ -48,7 +50,6 @@ tabId: tabId }); rootObject.browserAction.enable(); - } ); }; @@ -124,22 +125,50 @@ // of the URL being requested. const matchingDomainRule = domainMatcherLib.matchUrl(Object.keys(domainRules), url); const standardsToBlock = domainRules[matchingDomainRule || defaultKey]; - const shouldLogOption = ["shouldLog"]; + const encodedOptions = cookieEncodingLib.toCookieValue(standardsToBlock, shouldLog); - const options = Object.keys(standards).concat(shouldLogOption); - const standardsToBlockWithShouldLogOption = shouldLog - ? standardsToBlock.concat(shouldLogOption) - : standardsToBlock; + // If we're on a site thats sending the "strict-dynamic" + // Content-Security-Policy instruction, then we need to add the + // injected proxy code to the list of scripts that are allowed to + // run in the page. + const cspDynamicPolicyHeaders = details.responseHeaders + .filter(httpHeadersLib.isHeaderCSP) + .filter(httpHeadersLib.isCSPHeaderSettingStrictDynamic); - const packedValues = packingLib.pack( - options, - standardsToBlockWithShouldLogOption - ); + if (cspDynamicPolicyHeaders.length === 1) { + const [ignore, scriptHash] = proxyBlockLib.generateScriptPayload( + standards, + standardsToBlock, + shouldLog + ); + + const newCSPValue = httpHeadersLib.createCSPInstructionWithHashAllowed( + cspDynamicPolicyHeaders[0].value, + "sha256-" + scriptHash + ); + + if (newCSPValue !== false) { + cspDynamicPolicyHeaders[0].value = newCSPValue; + } + } - details.responseHeaders.push({ - name: "Set-Cookie", - value: `wam-temp-cookie=${packedValues}` - }); + // If there is already a set-cookie instruction being issued, + // don't overwrite it, but add our cookie to the end of it. Otherwise, + // create a new set-cookie instruction header. + const webAPIStandardsCookie = `${constants.cookieName}=${encodedOptions}`; + const setCookieHeaders = details.responseHeaders.filter(httpHeadersLib.isSetCookie); + + if (setCookieHeaders.length > 0) { + + setCookieHeaders[0].value += "; " + webAPIStandardsCookie; + + } else { + + details.responseHeaders.push({ + name: "Set-Cookie", + value: webAPIStandardsCookie + }); + } return { responseHeaders: details.responseHeaders diff --git a/config/index.html b/config/index.html index b20c4c0..1c71145 100644 --- a/config/index.html +++ b/config/index.html @@ -37,8 +37,8 @@ - - + + diff --git a/content_scripts/dist/README.md b/content_scripts/dist/README.md deleted file mode 100644 index fea22e3..0000000 --- a/content_scripts/dist/README.md +++ /dev/null @@ -1,2 +0,0 @@ -This directory contains script files that are build from gulp, and which are -injected into visited frames / pages. \ No newline at end of file diff --git a/content_scripts/instrument.js b/content_scripts/instrument.js new file mode 100644 index 0000000..4fe1e75 --- /dev/null +++ b/content_scripts/instrument.js @@ -0,0 +1,29 @@ +/*jslint es6: true, browser: true*/ +/*global window*/ +// This script file runs in the context of the extension, and mainly +// exists to inject the proxy blocking code into content frames. +(function () { + "use strict"; + + const cookies2 = window.Cookies.noConflict(); + const {standards, constants, cookieEncodingLib, proxyBlockLib} = window.WEB_API_MANAGER; + const standardsCookieName = constants.cookieName; + + const doc = window.document; + const script = doc.createElement('script'); + const rootElm = doc.head || doc.documentElement; + + const cookieValue = cookies2.get(standardsCookieName); + const [standardsToBlock, shouldLog] = cookieEncodingLib.fromCookieValue(cookieValue); + cookies2.remove(standardsCookieName); + + const [scriptToInject, scriptHash] = proxyBlockLib.generateScriptPayload( + standards, + standardsToBlock, + shouldLog + ); + + script.appendChild(doc.createTextNode(scriptToInject)); + script.integrity = "sha256-" + scriptHash; + rootElm.appendChild(script); +}()); diff --git a/content_scripts/src/instrument.js b/content_scripts/src/instrument.js deleted file mode 100644 index 2e0d697..0000000 --- a/content_scripts/src/instrument.js +++ /dev/null @@ -1,43 +0,0 @@ -/*jslint es6: true, browser: true*/ -/*global window, Cookies*/ -// This script file runs in the context of the extension, and mainly -// exists to inject the proxy blocking code into content frames. -(function () { - "use strict"; - - const doc = window.document; - const script = doc.createElement('script'); - const rootElm = doc.head || doc.documentElement; - const shouldLogValue = "shouldLog"; - - const standardsCookieKey = "wam-temp-cookie"; - const {packingLib, standards} = window.WEB_API_MANAGER; - const options = Object.keys(standards); - const optionsWithShouldLog = options.concat([shouldLogValue]); - const packedValues = Cookies.get(standardsCookieKey); - const unpackedValues = packingLib.unpack(optionsWithShouldLog, packedValues); - Cookies.remove(standardsCookieKey); - - let shouldLog; - const standardsToBlock = unpackedValues; - const indexOfShouldLog = unpackedValues.indexOf(shouldLogValue); - - if (indexOfShouldLog === -1) { - shouldLog = false; - } else { - shouldLog = true; - standardsToBlock.splice(indexOfShouldLog, 1); - } - - const code = ` - window.WEB_API_MANAGER_PAGE = { - standards: ${JSON.stringify(standards)}, - toBlock: ${JSON.stringify(standardsToBlock)}, - shouldLog: ${shouldLog} - }; - ###-INJECTED-PROXY-BLOCKING-CODE-### - `; - - script.appendChild(doc.createTextNode(code)); - rootElm.appendChild(script); -}()); diff --git a/content_scripts/src/proxyblock.js b/content_scripts/src/proxyblock.js deleted file mode 100644 index 96879be..0000000 --- a/content_scripts/src/proxyblock.js +++ /dev/null @@ -1,174 +0,0 @@ -/*jslint es6: true, browser: true*/ -/*global window*/ -// The contents of this file are programatically injected into all frames. -// It is compiled into the src/instrument.js file to create the -// dist/instrument.js script/ -(function () { - "use strict"; - - const settings = window.WEB_API_MANAGER_PAGE; - const shouldLog = settings.shouldLog; - const standardsToBlock = settings.toBlock; - const standardDefinitions = settings.standards; - const hostName = window.location.hostname; - - // Its possible that the Web API removal code will block direct references - // to the following methods, so grab references to them before the - // DOM is instrumented (and their references are possibly blocked). - const removeChild = window.Element.prototype.removeChild; - const getElementsByTagName = window.document.getElementsByTagName; - - const defaultFunction = function () {}; - const funcPropNames = Object.getOwnPropertyNames(defaultFunction); - const unconfigurablePropNames = funcPropNames.filter(function (propName) { - const possiblePropDesc = Object.getOwnPropertyDescriptor(defaultFunction, propName); - return (possiblePropDesc && !possiblePropDesc.configurable); - }); - - const featuresToBlock = standardsToBlock.reduce(function (prev, cur) { - return prev.concat(standardDefinitions[cur].features); - }, []); - - const toPrimitiveFunc = function (hint) { - if (hint === "number" || hint === "default") { - return 0; - } - if (hint === "string") { - return ""; - } - return undefined; - }; - - const keyPathToRefPath = function (keyPath) { - const keyParts = keyPath.split("."); - return keyParts.reduce(function (prev, cur) { - - if (prev === undefined) { - return undefined; - } - - const numNodes = prev.length; - const currentLeaf = (numNodes === 0) - ? window - : prev[numNodes - 1]; - const nextLeaf = currentLeaf[cur]; - - if (nextLeaf === undefined) { - return undefined; - } - - return prev.concat([nextLeaf]); - }, []); - }; - - const createBlockingProxy = function (keyPath) { - - let hasBeenLogged = false; - - const logKeyPath = function () { - - if (keyPath !== undefined && - hasBeenLogged === false && - shouldLog) { - hasBeenLogged = true; - console.log("Blocked '" + keyPath + "' on '" + hostName + "'"); - } - }; - - let blockingProxy; - blockingProxy = new Proxy(defaultFunction, { - get: function (ignore, property) { - logKeyPath(); - - if (property === Symbol.toPrimitive) { - return toPrimitiveFunc; - } - - if (property === "valueOf") { - return toPrimitiveFunc; - } - - return blockingProxy; - }, - set: function () { - logKeyPath(); - return blockingProxy; - }, - apply: function () { - logKeyPath(); - return blockingProxy; - }, - ownKeys: function (ignore) { - return unconfigurablePropNames; - }, - has: function (ignore, property) { - return (unconfigurablePropNames.indexOf(property) > -1); - }, - getOwnPropertyDescriptor: function (ignore, property) { - if (unconfigurablePropNames.indexOf(property) === -1) { - return undefined; - } - return Object.getOwnPropertyDescriptor(defaultFunction, property); - } - }); - - return blockingProxy; - }; - - const defaultBlockingProxy = createBlockingProxy(); - - const blockFeatureAtKeyPath = function (keyPath) { - const propertyRefs = keyPathToRefPath(keyPath); - - // If we weren't able to turn the key path into an array of references, - // then it means that the property doesn't exist in this DOM / - // environment, so there is nothing to block. - if (propertyRefs === undefined) { - return false; - } - - const keyPathSegments = keyPath.split("."); - const lastPropertyName = keyPathSegments[keyPathSegments.length - 1]; - const leafRef = propertyRefs[propertyRefs.length - 1]; - const parentRef = propertyRefs[propertyRefs.length - 2]; - - // At least for now, only interpose on methods. - if (typeof leafRef !== "function") { - return false; - } - - try { - - if (shouldLog === true) { - parentRef[lastPropertyName] = createBlockingProxy(keyPath); - return true; - } - - parentRef[lastPropertyName] = defaultBlockingProxy; - return true; - - } catch (e) { - - if (shouldLog) { - console.log("Error instrumenting " + keyPath + ": " + e); - } - - return false; - } - }; - - featuresToBlock.forEach(blockFeatureAtKeyPath); - - // Next, delete the WEB_API_MANAGER_PAGE global property. Technically - // this never needed to be global, but doing so allows for easier - // jslinting of the code, makes things easier to understand (for me - // at least) and doesn't have any side effect as long as we delete - // it when we're done, and before the page scripts can start running. - delete window.WEB_API_MANAGER_PAGE; - - // Last, remove the script tag containing this code from the document, - // so that the structure of the page looks like what the page author - // expects / intended. - const scriptTags = getElementsByTagName.call(window.document, "script"); - removeChild.call(scriptTags[0].parentNode, scriptTags[0]); -}()); diff --git a/data/standards.js b/data/standards.js new file mode 100644 index 0000000..a9a4773 --- /dev/null +++ b/data/standards.js @@ -0,0 +1,2 @@ +/** This file is automatically generated. **/ +window.WEB_API_MANAGER.standards = {"XMLHttpRequest":{"info":{"name":"XMLHttpRequest","subsection_number":null,"subsection_name":null,"url":"https://xhr.spec.whatwg.org/","idenitifer":"XMLHttpRequest"},"features":["FormData.prototype.append","FormData.prototype.delete","FormData.prototype.get","FormData.prototype.getAll","FormData.prototype.has","FormData.prototype.set","XMLHttpRequest.prototype.abort","XMLHttpRequest.prototype.getAllResponseHeaders","XMLHttpRequest.prototype.getResponseHeader","XMLHttpRequest.prototype.open","XMLHttpRequest.prototype.overrideMimeType","XMLHttpRequest.prototype.send","XMLHttpRequest.prototype.setRequestHeader"]},"Ambient Light Sensor API":{"info":{"name":"Ambient Light Sensor API","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/ambient-light/","idenitifer":"Ambient Light Sensor API"},"features":["window.ondevicelight"]},"Battery Status API":{"info":{"name":"Battery Status API","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/battery-status/","idenitifer":"Battery Status API"},"features":["navigator.battery","Navigator.prototype.getBattery"]},"Beacon":{"info":{"name":"Beacon","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/beacon/","idenitifer":"Beacon"},"features":["Navigator.prototype.sendBeacon"]},"Console API":{"info":{"name":"Console API","subsection_number":null,"subsection_name":null,"url":"https://github.com/DeveloperToolsWG/console-object","idenitifer":"Console API"},"features":["Console.prototype.assert","Console.prototype.clear","Console.prototype.count","Console.prototype.debug","Console.prototype.dir","Console.prototype.dirxml","Console.prototype.error","Console.prototype.group","Console.prototype.groupCollapsed","Console.prototype.groupEnd","Console.prototype.info","Console.prototype.log","Console.prototype.markTimeline","Console.prototype.profile","Console.prototype.profileEnd","Console.prototype.table","Console.prototype.time","Console.prototype.timeEnd","Console.prototype.timeline","Console.prototype.timelineEnd","Console.prototype.timeStamp","Console.prototype.trace","Console.prototype.warn"]},"CSS Conditional Rules Module Level 3":{"info":{"name":"CSS Conditional Rules Module Level 3","subsection_number":null,"subsection_name":null,"url":"https://drafts.csswg.org/css-conditional-3/","idenitifer":"CSS Conditional Rules Module Level 3"},"features":["CSS.supports"]},"CSS Font Loading Module Level 3":{"info":{"name":"CSS Font Loading Module Level 3","subsection_number":null,"subsection_name":null,"url":"https://drafts.csswg.org/css-font-loading/","idenitifer":"CSS Font Loading Module Level 3"},"features":["document.fonts","FontFace.prototype.load","FontFaceSet.prototype.add","FontFaceSet.prototype.check","FontFaceSet.prototype.clear","FontFaceSet.prototype.delete","FontFaceSet.prototype.entries","FontFaceSet.prototype.forEach","FontFaceSet.prototype.has","FontFaceSet.prototype.keys","FontFaceSet.prototype.load","FontFaceSet.prototype.values"]},"CSS Object Model (CSSOM)":{"info":{"name":"CSS Object Model (CSSOM)","subsection_number":null,"subsection_name":null,"url":"https://drafts.csswg.org/cssom/","idenitifer":"CSS Object Model (CSSOM)"},"features":["CSS.escape","Document.prototype.caretPositionFromPoint","Document.prototype.elementFromPoint","Element.prototype.getBoundingClientRect","Element.prototype.getClientRects","Element.prototype.scroll","Element.prototype.scrollBy","Element.prototype.scrollIntoView","Element.prototype.scrollTo","MediaList.prototype.appendMedium","MediaList.prototype.deleteMedium","MediaList.prototype.item","MediaQueryList.prototype.addListener","MediaQueryList.prototype.removeListener","StyleSheetList.prototype.item"]},"CSSOM View Module":{"info":{"name":"CSSOM View Module","subsection_number":null,"subsection_name":null,"url":"https://drafts.csswg.org/cssom-view/","idenitifer":"CSSOM View Module"},"features":["CaretPosition.prototype.getClientRect","screen.availHeight","screen.availWidth","screen.colorDepth","screen.height","screen.left","screen.pixelDepth","screen.width","window.devicePixelRatio","window.innerHeight","window.innerWidth","window.matchMedia","window.moveBy","window.moveTo","window.outerHeight","window.outerWidth","window.pageXOffset","window.pageYOffset","window.resizeBy","window.resizeTo","window.screen","window.screenX","window.screenY","window.scroll","window.scrollBy","window.scrollTo","window.scrollX","window.scrollY"]},"DeviceOrientation Event Specification":{"info":{"name":"DeviceOrientation Event Specification","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/orientation-event/","idenitifer":"DeviceOrientation Event Specification"},"features":["DeviceMotionEvent.prototype.initDeviceMotionEvent","DeviceOrientationEvent.prototype.initDeviceOrientationEvent"]},"DOM Parsing and Serialization":{"info":{"name":"DOM Parsing and Serialization","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/DOM-Parsing/","idenitifer":"DOM Parsing and Serialization"},"features":["DOMParser.prototype.parseFromString","Element.prototype.insertAdjacentHTML","XMLSerializer.prototype.serializeToString"]},"DOM":{"info":{"name":"DOM","subsection_number":null,"subsection_name":null,"url":"https://dom.spec.whatwg.org/","idenitifer":"DOM"},"features":["CharacterData.prototype.appendData","CharacterData.prototype.deleteData","CharacterData.prototype.insertData","CharacterData.prototype.remove","CharacterData.prototype.replaceData","CharacterData.prototype.substringData","CustomEvent.prototype.initCustomEvent","document.characterSet","document.childElementCount","document.children","document.close","document.compatMode","document.contentType","document.firstElementChild","document.inputEncoding","document.lastElementChild","Document.prototype.createAttribute","Document.prototype.createElement","Document.prototype.createProcessingInstruction","Document.prototype.getElementsByClassName","document.scripts","DocumentFragment.prototype.getElementById","DocumentType.prototype.remove","DOMImplementation.prototype.createHTMLDocument","DOMTokenList.prototype.add","DOMTokenList.prototype.contains","DOMTokenList.prototype.item","DOMTokenList.prototype.remove","DOMTokenList.prototype.toggle","Element.prototype.closest","Element.prototype.getElementsByClassName","Element.prototype.matches","Element.prototype.mozMatchesSelector","Element.prototype.remove","window.clearInterval","window.clearTimeout"]},"Document Object Model (DOM) Level 1 Specification":{"info":{"name":"Document Object Model (DOM) Level 1 Specification","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/REC-DOM-Level-1/","idenitifer":"Document Object Model (DOM) Level 1 Specification"},"features":["Document.prototype.getElementById","Document.prototype.getElementsByTagName","Element.prototype.getAttribute","Element.prototype.getAttributeNode","Element.prototype.getElementsByTagName","Element.prototype.removeAttribute","Element.prototype.removeAttributeNode","Element.prototype.setAttribute","Element.prototype.setAttributeNode","HTMLCollection.prototype.item","HTMLCollection.prototype.namedItem","HTMLDocument.prototype.close","HTMLDocument.prototype.open","HTMLDocument.prototype.write","HTMLDocument.prototype.writeln","HTMLElement.prototype.click","HTMLElement.prototype.focus","HTMLFormElement.prototype.reset","HTMLFormElement.prototype.submit","HTMLInputElement.prototype.select","HTMLSelectElement.prototype.add","HTMLSelectElement.prototype.item","HTMLSelectElement.prototype.namedItem","HTMLSelectElement.prototype.remove","HTMLTableElement.prototype.createCaption","HTMLTableElement.prototype.createTBody","HTMLTableElement.prototype.createTFoot","HTMLTableElement.prototype.createTHead","HTMLTableElement.prototype.deleteCaption","HTMLTableElement.prototype.deleteRow","HTMLTableElement.prototype.deleteTFoot","HTMLTableElement.prototype.deleteTHead","HTMLTableElement.prototype.insertRow","HTMLTableRowElement.prototype.deleteCell","HTMLTableRowElement.prototype.insertCell","HTMLTableSectionElement.prototype.deleteRow","HTMLTableSectionElement.prototype.insertRow","HTMLTextAreaElement.prototype.select","Node.prototype.appendChild","Node.prototype.cloneNode","Node.prototype.hasChildNodes","Node.prototype.insertBefore","Node.prototype.normalize","Node.prototype.removeChild","Node.prototype.replaceChild","NodeList.prototype.item","Text.prototype.splitText"]},"DOM Level 2: Core":{"info":{"name":"DOM Level 2: Core","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/DOM-Level-2-Core/","idenitifer":"DOM Level 2: Core"},"features":["document.doctype","document.documentElement","document.domain","document.implementation","Document.prototype.createAttributeNS","Document.prototype.createCDATASection","Document.prototype.createComment","Document.prototype.createDocumentFragment","Document.prototype.createElementNS","Document.prototype.createTextNode","Document.prototype.getElementsByTagNameNS","Document.prototype.importNode","DOMImplementation.prototype.createDocument","DOMImplementation.prototype.createDocumentType","DOMImplementation.prototype.hasFeature","Element.prototype.getAttributeNodeNS","Element.prototype.getAttributeNS","Element.prototype.getElementsByTagNameNS","Element.prototype.hasAttribute","Element.prototype.hasAttributeNS","Element.prototype.hasAttributes","Element.prototype.removeAttributeNS","Element.prototype.setAttributeNodeNS","Element.prototype.setAttributeNS","NamedNodeMap.prototype.getNamedItem","NamedNodeMap.prototype.getNamedItemNS","NamedNodeMap.prototype.item","NamedNodeMap.prototype.removeNamedItem","NamedNodeMap.prototype.removeNamedItemNS","NamedNodeMap.prototype.setNamedItem","NamedNodeMap.prototype.setNamedItemNS"]},"DOM Level 2: Events":{"info":{"name":"DOM Level 2: Events","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/DOM-Level-2-Events/","idenitifer":"DOM Level 2: Events"},"features":["Document.prototype.createEvent","Event.prototype.initEvent","Event.prototype.preventDefault","Event.prototype.stopPropagation","EventTarget.prototype.addEventListener","EventTarget.prototype.dispatchEvent","EventTarget.prototype.removeEventListener"]},"DOM Level 2: HTML":{"info":{"name":"DOM Level 2: HTML","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/DOM-Level-2-HTML/","idenitifer":"DOM Level 2: HTML"},"features":["document.cookie","document.forms","document.getElementsByName","document.open","document.referrer","document.title","document.URL","document.write","document.writeln","HTMLDocument.prototype.getElementsByName","HTMLElement.prototype.blur"]},"DOM Level 2: Style":{"info":{"name":"DOM Level 2: Style","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/DOM-Level-2-Style/","idenitifer":"DOM Level 2: Style"},"features":["CSSPrimitiveValue.prototype.getCounterValue","CSSPrimitiveValue.prototype.getFloatValue","CSSPrimitiveValue.prototype.getRectValue","CSSPrimitiveValue.prototype.getRGBColorValue","CSSPrimitiveValue.prototype.getStringValue","CSSPrimitiveValue.prototype.setFloatValue","CSSPrimitiveValue.prototype.setStringValue","CSSRuleList.prototype.item","CSSStyleDeclaration.prototype.getPropertyCSSValue","CSSStyleDeclaration.prototype.getPropertyPriority","CSSStyleDeclaration.prototype.getPropertyValue","CSSStyleDeclaration.prototype.item","CSSStyleDeclaration.prototype.removeProperty","CSSStyleDeclaration.prototype.setProperty","CSSStyleSheet.prototype.deleteRule","CSSStyleSheet.prototype.insertRule","CSSValueList.prototype.item","document.styleSheets","window.getComputedStyle"]},"DOM Level 2: Traversal and Range":{"info":{"name":"DOM Level 2: Traversal and Range","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/DOM-Level-2-Traversal-Range/","idenitifer":"DOM Level 2: Traversal and Range"},"features":["Document.prototype.createNodeIterator","Document.prototype.createRange","Document.prototype.createTreeWalker","NodeIterator.prototype.detach","NodeIterator.prototype.nextNode","NodeIterator.prototype.previousNode","Range.prototype.cloneContents","Range.prototype.cloneRange","Range.prototype.collapse","Range.prototype.compareBoundaryPoints","Range.prototype.comparePoint","Range.prototype.createContextualFragment","Range.prototype.deleteContents","Range.prototype.detach","Range.prototype.extractContents","Range.prototype.getBoundingClientRect","Range.prototype.getClientRects","Range.prototype.insertNode","Range.prototype.intersectsNode","Range.prototype.isPointInRange","Range.prototype.selectNode","Range.prototype.selectNodeContents","Range.prototype.setEnd","Range.prototype.setEndAfter","Range.prototype.setEndBefore","Range.prototype.setStart","Range.prototype.setStartAfter","Range.prototype.setStartBefore","Range.prototype.surroundContents","TreeWalker.prototype.firstChild","TreeWalker.prototype.lastChild","TreeWalker.prototype.nextNode","TreeWalker.prototype.nextSibling","TreeWalker.prototype.parentNode","TreeWalker.prototype.previousNode","TreeWalker.prototype.previousSibling"]},"DOM Level 3: Core":{"info":{"name":"DOM Level 3: Core","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/DOM-Level-3-Core/","idenitifer":"DOM Level 3: Core"},"features":["document.documentURI","Document.prototype.adoptNode","DOMStringList.prototype.contains","DOMStringList.prototype.item","Node.prototype.compareDocumentPosition","Node.prototype.contains","Node.prototype.isDefaultNamespace","Node.prototype.isEqualNode","Node.prototype.lookupNamespaceURI","Node.prototype.lookupPrefix"]},"DOM Level 3: XPath":{"info":{"name":"DOM Level 3: XPath","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/DOM-Level-3-XPath/","idenitifer":"DOM Level 3: XPath"},"features":["Document.prototype.createExpression","Document.prototype.createNSResolver","Document.prototype.evaluate","XPathEvaluator.prototype.createExpression","XPathEvaluator.prototype.createNSResolver","XPathEvaluator.prototype.evaluate","XPathExpression.prototype.evaluate","XPathResult.prototype.iterateNext","XPathResult.prototype.snapshotItem"]},"W3C DOM4":{"info":{"name":"W3C DOM4","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/dom/","idenitifer":"W3C DOM4"},"features":["MutationObserver.prototype.disconnect","MutationObserver.prototype.observe","MutationObserver.prototype.takeRecords"]},"Directory Upload":{"info":{"name":"Directory Upload","subsection_number":null,"subsection_name":null,"url":"https://wicg.github.io/directory-upload/proposal.html","idenitifer":"Directory Upload"},"features":["Directory.prototype.getFilesAndDirectories"]},"Encoding":{"info":{"name":"Encoding","subsection_number":null,"subsection_name":null,"url":"https://encoding.spec.whatwg.org/","idenitifer":"Encoding"},"features":["TextDecoder.prototype.decode","TextEncoder.prototype.encode"]},"execCommand":{"info":{"name":"execCommand","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/editing/execCommand.html","idenitifer":"execCommand"},"features":["document.execCommand","document.queryCommandEnabled","document.queryCommandIndeterm","document.queryCommandState","document.queryCommandSupported","document.queryCommandValue","HTMLDocument.prototype.execCommand","HTMLDocument.prototype.queryCommandEnabled","HTMLDocument.prototype.queryCommandIndeterm","HTMLDocument.prototype.queryCommandState","HTMLDocument.prototype.queryCommandSupported","HTMLDocument.prototype.queryCommandValue"]},"Encrypted Media Extensions":{"info":{"name":"Encrypted Media Extensions","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/encrypted-media/","idenitifer":"Encrypted Media Extensions"},"features":["MediaKeys.prototype.createSession","MediaKeys.prototype.setServerCertificate","MediaKeySession.prototype.close","MediaKeySession.prototype.generateRequest","MediaKeySession.prototype.load","MediaKeySession.prototype.remove","MediaKeySession.prototype.update","MediaKeyStatusMap.prototype.entries","MediaKeyStatusMap.prototype.keys","MediaKeyStatusMap.prototype.values","MediaKeySystemAccess.prototype.createMediaKeys","MediaKeySystemAccess.prototype.getConfiguration","Navigator.prototype.requestMediaKeySystemAccess"]},"Fetch":{"info":{"name":"Fetch","subsection_number":null,"subsection_name":null,"url":"https://fetch.spec.whatwg.org/","idenitifer":"Fetch"},"features":["Headers.prototype.append","Headers.prototype.delete","Headers.prototype.get","Headers.prototype.getAll","Headers.prototype.has","Headers.prototype.set","Request.prototype.arrayBuffer","Request.prototype.blob","Request.prototype.clone","Request.prototype.formData","Request.prototype.json","Request.prototype.text","Response.error","Response.prototype.arrayBuffer","Response.prototype.blob","Response.prototype.clone","Response.prototype.formData","Response.prototype.json","Response.prototype.text","Response.redirect","window.fetch"]},"File API":{"info":{"name":"File API","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/FileAPI/","idenitifer":"File API"},"features":["Blob.prototype.slice","FileList.prototype.item","FileReader.prototype.abort","FileReader.prototype.readAsArrayBuffer","FileReader.prototype.readAsBinaryString","FileReader.prototype.readAsDataURL","FileReader.prototype.readAsText","URL.createObjectURL","URL.revokeObjectURL"]},"Fullscreen API":{"info":{"name":"Fullscreen API","subsection_number":null,"subsection_name":null,"url":"https://fullscreen.spec.whatwg.org/","idenitifer":"Fullscreen API"},"features":["document.mozFullScreen","document.mozFullScreenElement","document.mozFullScreenEnabled","document.onmozfullscreenchange","document.onmozfullscreenerror","Document.prototype.mozCancelFullScreen","Element.prototype.mozRequestFullScreen","window.onmozfullscreenchange","window.onmozfullscreenerror"]},"Geolocation API":{"info":{"name":"Geolocation API","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/geolocation-API/","idenitifer":"Geolocation API"},"features":["navigator.geolocation","navigator.geolocation.getCurrentPosition","navigator.geolocation.watchPosition","navigator.geolocation.clearWatch"]},"Geometry Interfaces Module Level 1":{"info":{"name":"Geometry Interfaces Module Level 1","subsection_number":null,"subsection_name":null,"url":"https://drafts.fxtf.org/geometry/","idenitifer":"Geometry Interfaces Module Level 1"},"features":["DOMMatrix.prototype.invertSelf","DOMMatrix.prototype.multiplySelf","DOMMatrix.prototype.preMultiplySelf","DOMMatrix.prototype.rotateAxisAngleSelf","DOMMatrix.prototype.rotateFromVectorSelf","DOMMatrix.prototype.rotateSelf","DOMMatrix.prototype.scale3dSelf","DOMMatrix.prototype.scaleNonUniformSelf","DOMMatrix.prototype.scaleSelf","DOMMatrix.prototype.setMatrixValue","DOMMatrix.prototype.skewXSelf","DOMMatrix.prototype.skewYSelf","DOMMatrix.prototype.translateSelf","DOMMatrixReadOnly.prototype.flipX","DOMMatrixReadOnly.prototype.flipY","DOMMatrixReadOnly.prototype.inverse","DOMMatrixReadOnly.prototype.multiply","DOMMatrixReadOnly.prototype.rotate","DOMMatrixReadOnly.prototype.rotateAxisAngle","DOMMatrixReadOnly.prototype.rotateFromVector","DOMMatrixReadOnly.prototype.scale","DOMMatrixReadOnly.prototype.scale3d","DOMMatrixReadOnly.prototype.scaleNonUniform","DOMMatrixReadOnly.prototype.skewX","DOMMatrixReadOnly.prototype.skewY","DOMMatrixReadOnly.prototype.toFloat32Array","DOMMatrixReadOnly.prototype.toFloat64Array","DOMMatrixReadOnly.prototype.transformPoint","DOMMatrixReadOnly.prototype.translate","DOMRectList.prototype.item"]},"Gamepad":{"info":{"name":"Gamepad","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/gamepad/","idenitifer":"Gamepad"},"features":["Navigator.prototype.getGamepads"]},"HTML: Broadcasting":{"info":{"name":"HTML","subsection_number":"9.6","subsection_name":"Broadcasting","url":"https://html.spec.whatwg.org/multipage/comms.html#dom-broadcastchannel-postmessage","idenitifer":"HTML: Broadcasting"},"features":["BroadcastChannel.prototype.close","BroadcastChannel.prototype.postMessage"]},"HTML: Canvas Element":{"info":{"name":"HTML","subsection_number":"4.12.4","subsection_name":"Canvas Element","url":"https://html.spec.whatwg.org/multipage/scripting.html#the-canvas-element","idenitifer":"HTML: Canvas Element"},"features":["CanvasGradient.prototype.addColorStop","CanvasPattern.prototype.setTransform","CanvasRenderingContext2D.prototype.arc","CanvasRenderingContext2D.prototype.arcTo","CanvasRenderingContext2D.prototype.beginPath","CanvasRenderingContext2D.prototype.bezierCurveTo","CanvasRenderingContext2D.prototype.clearRect","CanvasRenderingContext2D.prototype.clip","CanvasRenderingContext2D.prototype.closePath","CanvasRenderingContext2D.prototype.createImageData","CanvasRenderingContext2D.prototype.createLinearGradient","CanvasRenderingContext2D.prototype.createPattern","CanvasRenderingContext2D.prototype.createRadialGradient","CanvasRenderingContext2D.prototype.drawFocusIfNeeded","CanvasRenderingContext2D.prototype.drawImage","CanvasRenderingContext2D.prototype.fill","CanvasRenderingContext2D.prototype.fillRect","CanvasRenderingContext2D.prototype.fillText","CanvasRenderingContext2D.prototype.getImageData","CanvasRenderingContext2D.prototype.getLineDash","CanvasRenderingContext2D.prototype.isPointInPath","CanvasRenderingContext2D.prototype.isPointInStroke","CanvasRenderingContext2D.prototype.lineTo","CanvasRenderingContext2D.prototype.measureText","CanvasRenderingContext2D.prototype.moveTo","CanvasRenderingContext2D.prototype.putImageData","CanvasRenderingContext2D.prototype.quadraticCurveTo","CanvasRenderingContext2D.prototype.rect","CanvasRenderingContext2D.prototype.resetTransform","CanvasRenderingContext2D.prototype.restore","CanvasRenderingContext2D.prototype.rotate","CanvasRenderingContext2D.prototype.save","CanvasRenderingContext2D.prototype.scale","CanvasRenderingContext2D.prototype.setLineDash","CanvasRenderingContext2D.prototype.setTransform","CanvasRenderingContext2D.prototype.stroke","CanvasRenderingContext2D.prototype.strokeRect","CanvasRenderingContext2D.prototype.strokeText","CanvasRenderingContext2D.prototype.transform","CanvasRenderingContext2D.prototype.translate","HTMLCanvasElement.prototype.getContext","HTMLCanvasElement.prototype.mozGetAsFile","HTMLCanvasElement.prototype.toBlob","HTMLCanvasElement.prototype.toDataURL","Path2D.prototype.addPath","Path2D.prototype.arc","Path2D.prototype.arcTo","Path2D.prototype.bezierCurveTo","Path2D.prototype.closePath","Path2D.prototype.lineTo","Path2D.prototype.moveTo","Path2D.prototype.quadraticCurveTo","Path2D.prototype.rect","window.requestAnimationFrame"]},"HTML: Channel Messaging":{"info":{"name":"HTML","subsection_number":"9.5","subsection_name":"Channel Messaging","url":"https://html.spec.whatwg.org/multipage/comms.html#channel-messaging","idenitifer":"HTML: Channel Messaging"},"features":["MessagePort.prototype.close","MessagePort.prototype.postMessage","MessagePort.prototype.start","window.postMessage"]},"HTML: History Interface":{"info":{"name":"HTML","subsection_number":"7.5.2","subsection_name":"History Interface","url":"https://html.spec.whatwg.org/multipage/browsers.html#the-history-interface","idenitifer":"HTML: History Interface"},"features":["History.prototype.back","History.prototype.forward","History.prototype.go","History.prototype.pushState","History.prototype.replaceState","window.history"]},"HTML: Plugins":{"info":{"name":"HTML","subsection_number":"8.6.1.5","subsection_name":"Plugins","url":"https://html.spec.whatwg.org/multipage/webappapis.html#plugins-2","idenitifer":"HTML: Plugins"},"features":["document.plugins","MimeTypeArray.prototype.item","MimeTypeArray.prototype.namedItem","navigator.mimeTypes","navigator.plugins","Plugin.prototype.item","Plugin.prototype.namedItem","PluginArray.prototype.item","PluginArray.prototype.namedItem","PluginArray.prototype.refresh"]},"HTML: Web Storage":{"info":{"name":"HTML","subsection_number":"11","subsection_name":"Web Storage","url":"https://html.spec.whatwg.org/multipage/webstorage.html","idenitifer":"HTML: Web Storage"},"features":["Storage.prototype.clear","Storage.prototype.getItem","Storage.prototype.key","Storage.prototype.removeItem","Storage.prototype.setItem","StorageEvent.prototype.initStorageEvent","window.localStorage","window.sessionStorage"]},"HTML: Web Sockets":{"info":{"name":"HTML","subsection_number":"9.3","subsection_name":"Web Sockets","url":"https://html.spec.whatwg.org/multipage/comms.html#network","idenitifer":"HTML: Web Sockets"},"features":["WebSocket.prototype.close","WebSocket.prototype.send"]},"HTML: Web Workers":{"info":{"name":"HTML","subsection_number":"10","subsection_name":"Web Workers","url":"https://html.spec.whatwg.org/multipage/workers.html","idenitifer":"HTML: Web Workers"},"features":["Worker.prototype.postMessage","Worker.prototype.terminate"]},"High Resolution Time Level 2":{"info":{"name":"High Resolution Time Level 2","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/hr-time/","idenitifer":"High Resolution Time Level 2"},"features":["Performance.prototype.now"]},"HTML":{"info":{"name":"HTML","subsection_number":null,"subsection_name":null,"url":"https://html.spec.whatwg.org","idenitifer":"HTML"},"features":["DataTransfer.prototype.addElement","DataTransfer.prototype.clearData","DataTransfer.prototype.getData","DataTransfer.prototype.mozClearDataAt","DataTransfer.prototype.mozGetDataAt","DataTransfer.prototype.mozSetDataAt","DataTransfer.prototype.mozTypesAt","DataTransfer.prototype.setData","DataTransfer.prototype.setDragImage","document.activeElement","document.body","document.currentScript","document.designMode","document.dir","document.getItems","document.head","document.images","document.lastModified","document.lastStyleSheetSet","document.links","document.onabort","document.onblur","document.oncanplay","document.oncanplaythrough","document.onchange","document.onclick","document.oncontextmenu","document.oncopy","document.oncut","document.ondblclick","document.ondrag","document.ondragend","document.ondragenter","document.ondragleave","document.ondragover","document.ondragstart","document.ondrop","document.ondurationchange","document.onemptied","document.onended","document.onerror","document.onfocus","document.oninput","document.oninvalid","document.onkeydown","document.onkeypress","document.onkeyup","document.onload","document.onloadeddata","document.onloadedmetadata","document.onloadstart","document.onmousedown","document.onmouseenter","document.onmouseleave","document.onmousemove","document.onmouseout","document.onmouseover","document.onmouseup","document.onpaste","document.onpause","document.onplay","document.onplaying","document.onprogress","document.onratechange","document.onreadystatechange","document.onreset","document.onresize","document.onscroll","document.onseeked","document.onseeking","document.onselect","document.onshow","document.onstalled","document.onsubmit","document.onsuspend","document.ontimeupdate","document.onvolumechange","document.onwaiting","document.onwheel","Document.prototype.enableStyleSheetsForSet","Document.prototype.hasFocus","document.selectedStyleSheetSet","document.styleSheetSets","EventSource.prototype.close","HashChangeEvent.prototype.initHashChangeEvent","HTMLAllCollection.prototype.item","HTMLAllCollection.prototype.namedItem","HTMLDocument.prototype.clear","HTMLDocument.prototype.getItems","navigator.appCodeName","navigator.appName","navigator.appVersion","navigator.cookieEnabled","navigator.onLine","navigator.platform","navigator.product","navigator.productSub","Navigator.prototype.registerContentHandler","Navigator.prototype.registerProtocolHandler","navigator.userAgent","navigator.vendor","navigator.vendorSub","PropertyNodeList.prototype.getValues","TimeRanges.prototype.end","TimeRanges.prototype.start","window.blur","window.close","window.closed","window.console","window.createImageBitmap","window.document","window.focus","window.frameElement","window.frames","window.length","window.location","window.name","window.navigator","window.onabort","window.onafterprint","window.onbeforeprint","window.onbeforeunload","window.onblur","window.oncanplay","window.oncanplaythrough","window.onchange","window.onclick","window.oncontextmenu","window.ondblclick","window.ondrag","window.ondragend","window.ondragenter","window.ondragleave","window.ondragover","window.ondragstart","window.ondurationchange","window.onemptied","window.onended","window.onerror","window.onfocus","window.onhashchange","window.oninput","window.oninvalid","window.onkeydown","window.onkeypress","window.onkeyup","window.onlanguagechange","window.onloadeddata","window.onloadedmetadata","window.onloadstart","window.onmessage","window.onmousedown","window.onmouseenter","window.onmouseleave","window.onmousemove","window.onmouseout","window.onmouseover","window.onmouseup","window.onoffline","window.ononline","window.onpagehide","window.onpageshow","window.onpause","window.onplay","window.onplaying","window.onpopstate","window.onprogress","window.onratechange","window.onreset","window.onresize","window.onscroll","window.onseeked","window.onseeking","window.onselect","window.onshow","window.onstalled","window.onsubmit","window.onsuspend","window.ontimeupdate","window.onunload","window.onvolumechange","window.onwaiting","window.onwheel","window.open","window.opener","window.parent","window.self","window.setInterval","window.setResizable","window.setTimeout","window.showModalDialog","window.stop","window.top","window.window","XMLDocument.prototype.load"]},"HTML 5":{"info":{"name":"HTML 5","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/html5/","idenitifer":"HTML 5"},"features":["document.defaultView","document.location","document.onafterscriptexecute","document.onbeforescriptexecute","document.preferredStyleSheetSet","document.readyState","HTMLButtonElement.prototype.checkValidity","HTMLButtonElement.prototype.setCustomValidity","HTMLFieldSetElement.prototype.checkValidity","HTMLFieldSetElement.prototype.setCustomValidity","HTMLFormControlsCollection.prototype.item","HTMLFormElement.prototype.checkValidity","HTMLInputElement.prototype.checkValidity","HTMLInputElement.prototype.setCustomValidity","HTMLInputElement.prototype.setRangeText","HTMLInputElement.prototype.setSelectionRange","HTMLInputElement.prototype.stepDown","HTMLInputElement.prototype.stepUp","HTMLMediaElement.prototype.addTextTrack","HTMLMediaElement.prototype.canPlayType","HTMLMediaElement.prototype.fastSeek","HTMLMediaElement.prototype.load","HTMLMediaElement.prototype.mozCaptureStream","HTMLMediaElement.prototype.mozCaptureStreamUntilEnded","HTMLMediaElement.prototype.mozGetMetadata","HTMLMediaElement.prototype.pause","HTMLMediaElement.prototype.play","HTMLMediaElement.prototype.setMediaKeys","HTMLObjectElement.prototype.checkValidity","HTMLObjectElement.prototype.setCustomValidity","HTMLOptionsCollection.prototype.add","HTMLOptionsCollection.prototype.remove","HTMLOutputElement.prototype.checkValidity","HTMLOutputElement.prototype.setCustomValidity","HTMLSelectElement.prototype.checkValidity","HTMLSelectElement.prototype.setCustomValidity","HTMLTextAreaElement.prototype.checkValidity","HTMLTextAreaElement.prototype.setCustomValidity","HTMLTextAreaElement.prototype.setRangeText","HTMLTextAreaElement.prototype.setSelectionRange","location.assign","location.reload","location.replace","OfflineResourceList.prototype.mozAdd","OfflineResourceList.prototype.mozHasItem","OfflineResourceList.prototype.mozItem","OfflineResourceList.prototype.mozRemove","OfflineResourceList.prototype.swapCache","OfflineResourceList.prototype.update","TextTrack.prototype.addCue","TextTrack.prototype.removeCue","TextTrackCueList.prototype.getCueById","TextTrackList.prototype.getTrackById","TimeEvent.prototype.initTimeEvent","window.alert","window.applicationCache","window.atob","window.btoa","window.confirm","window.locationbar","window.menubar","window.personalbar","window.print","window.prompt","window.scrollbars","window.status","window.statusbar","window.toolbar","location.href"]},"HTML 5.1":{"info":{"name":"HTML 5.1","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/html51/","idenitifer":"HTML 5.1"},"features":["DragEvent.prototype.initDragEvent","External.prototype.addSearchEngine","External.prototype.AddSearchProvider","External.prototype.IsSearchProviderInstalled","navigator.language","navigator.languages"]},"Indexed Database API":{"info":{"name":"Indexed Database API","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/IndexedDB/","idenitifer":"Indexed Database API"},"features":["IDBCursor.prototype.advance","IDBCursor.prototype.continue","IDBCursor.prototype.delete","IDBCursor.prototype.update","IDBDatabase.prototype.close","IDBDatabase.prototype.createMutableFile","IDBDatabase.prototype.createObjectStore","IDBDatabase.prototype.deleteObjectStore","IDBDatabase.prototype.mozCreateFileHandle","IDBDatabase.prototype.transaction","IDBFactory.prototype.cmp","IDBFactory.prototype.deleteDatabase","IDBFactory.prototype.open","IDBFileHandle.prototype.abort","IDBFileHandle.prototype.append","IDBFileHandle.prototype.flush","IDBFileHandle.prototype.getMetadata","IDBFileHandle.prototype.readAsArrayBuffer","IDBFileHandle.prototype.readAsText","IDBFileHandle.prototype.truncate","IDBFileHandle.prototype.write","IDBIndex.prototype.count","IDBIndex.prototype.get","IDBIndex.prototype.getKey","IDBIndex.prototype.mozGetAll","IDBIndex.prototype.mozGetAllKeys","IDBIndex.prototype.openCursor","IDBIndex.prototype.openKeyCursor","IDBKeyRange.bound","IDBKeyRange.lowerBound","IDBKeyRange.only","IDBKeyRange.upperBound","IDBMutableFile.prototype.getFile","IDBMutableFile.prototype.open","IDBObjectStore.prototype.add","IDBObjectStore.prototype.clear","IDBObjectStore.prototype.count","IDBObjectStore.prototype.createIndex","IDBObjectStore.prototype.delete","IDBObjectStore.prototype.deleteIndex","IDBObjectStore.prototype.get","IDBObjectStore.prototype.index","IDBObjectStore.prototype.mozGetAll","IDBObjectStore.prototype.openCursor","IDBObjectStore.prototype.put","IDBTransaction.prototype.abort","IDBTransaction.prototype.objectStore","window.indexedDB"]},"Media Capture from DOM Elements":{"info":{"name":"Media Capture from DOM Elements","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/mediacapture-fromelement/","idenitifer":"Media Capture from DOM Elements"},"features":["CanvasCaptureMediaStream.prototype.requestFrame","HTMLCanvasElement.prototype.captureStream"]},"Media Capture and Streams":{"info":{"name":"Media Capture and Streams","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/mediacapture-streams/","idenitifer":"Media Capture and Streams"},"features":["LocalMediaStream.prototype.stop","MediaDevices.prototype.enumerateDevices","MediaDevices.prototype.getUserMedia","navigator.mediaDevices"]},"Media Source Extensions":{"info":{"name":"Media Source Extensions","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/media-source/","idenitifer":"Media Source Extensions"},"features":["HTMLVideoElement.prototype.getVideoPlaybackQuality","MediaSource.isTypeSupported","MediaSource.prototype.addSourceBuffer","MediaSource.prototype.endOfStream","MediaSource.prototype.removeSourceBuffer","SourceBuffer.prototype.abort","SourceBuffer.prototype.appendBuffer","SourceBuffer.prototype.remove"]},"MediaStream Recording":{"info":{"name":"MediaStream Recording","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/mediastream-recording/","idenitifer":"MediaStream Recording"},"features":["MediaRecorder.prototype.pause","MediaRecorder.prototype.requestData","MediaRecorder.prototype.resume","MediaRecorder.prototype.start","MediaRecorder.prototype.stop","MediaStream.prototype.getAudioTracks","MediaStream.prototype.getTracks","MediaStream.prototype.getVideoTracks","MediaStreamTrack.prototype.applyConstraints","MediaStreamTrack.prototype.stop"]},"Non-Standard":{"info":{"name":"Non-Standard","subsection_number":null,"subsection_name":null,"url":null,"idenitifer":"Non-Standard"},"features":["AnonymousContent.prototype.getAttributeForElement","AnonymousContent.prototype.getTextContentForElement","AnonymousContent.prototype.removeAttributeForElement","AnonymousContent.prototype.setAttributeForElement","AnonymousContent.prototype.setTextContentForElement","CommandEvent.prototype.initCommandEvent","document.embeds","Document.prototype.mozSetImageElement","Document.prototype.releaseCapture","DOMCursor.prototype.continue","DOMRequest.prototype.then","Element.prototype.releaseCapture","Element.prototype.setCapture","Event.prototype.getPreventDefault","HTMLDocument.prototype.captureEvents","HTMLDocument.prototype.releaseEvents","HTMLInputElement.prototype.mozIsTextField","HTMLPropertiesCollection.prototype.item","HTMLPropertiesCollection.prototype.namedItem","window.captureEvents","MouseEvent.prototype.initNSMouseEvent","MouseScrollEvent.prototype.initMouseScrollEvent","mozContact.prototype.init","MozPowerManager.prototype.addWakeLockListener","MozPowerManager.prototype.factoryReset","MozPowerManager.prototype.getWakeLockState","MozPowerManager.prototype.powerOff","MozPowerManager.prototype.reboot","MozPowerManager.prototype.removeWakeLockListener","navigator.buildID","navigator.oscpu","Navigator.prototype.javaEnabled","PaintRequestList.prototype.item","screen.availLeft","screen.availTop","screen.top","ScrollAreaEvent.prototype.initScrollAreaEvent","SimpleGestureEvent.prototype.initSimpleGestureEvent","window.content","window.controllers","window.dump","window.external","window.find","window.fullScreen","window.getDefaultComputedStyle","window.mozInnerScreenX","window.mozInnerScreenY","window.mozPaintCount","window.ondrop","window.releaseEvents","window.scrollByLines","window.scrollByPages","window.scrollMaxX","window.scrollMaxY","window.sidebar","window.sizeToContent","window.updateCommands","XSLTProcessor.prototype.clearParameters","XSLTProcessor.prototype.getParameter","XSLTProcessor.prototype.importStylesheet","XSLTProcessor.prototype.removeParameter","XSLTProcessor.prototype.reset","XSLTProcessor.prototype.setParameter","XSLTProcessor.prototype.transformToDocument","XSLTProcessor.prototype.transformToFragment"]},"Navigation Timing":{"info":{"name":"Navigation Timing","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/navigation-timing/","idenitifer":"Navigation Timing"},"features":["performance.navigation","performance.navigation.redirectCount","performance.navigation.type","performance.timing","performance.timing.connectEnd","performance.timing.connectStart","performance.timing.domainLookupEnd","performance.timing.domainLookupStart","performance.timing.domComplete","performance.timing.domContentLoadedEventEnd","performance.timing.domContentLoadedEventStart","performance.timing.domInteractive","performance.timing.domLoading","performance.timing.fetchStart","performance.timing.loadEventEnd","performance.timing.loadEventStart","performance.timing.navigationStart","performance.timing.redirectEnd","performance.timing.redirectStart","performance.timing.requestStart","performance.timing.responseEnd","performance.timing.responseStart","performance.timing.unloadEventEnd","performance.timing.unloadEventStart","window.performance"]},"Proximity Events":{"info":{"name":"Proximity Events","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/proximity/","idenitifer":"Proximity Events"},"features":["window.ondeviceproximity","window.onuserproximity"]},"Pointer Lock":{"info":{"name":"Pointer Lock","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/pointerlock/","idenitifer":"Pointer Lock"},"features":["document.mozPointerLockElement","document.onmozpointerlockchange","document.onmozpointerlockerror","Document.prototype.mozExitPointerLock","Element.prototype.mozRequestPointerLock","window.onmozpointerlockchange","window.onmozpointerlockerror"]},"Performance Timeline":{"info":{"name":"Performance Timeline","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/performance-timeline","idenitifer":"Performance Timeline"},"features":["Performance.prototype.getEntriesByName","Performance.prototype.getEntriesByType"]},"Performance Timeline Level 2":{"info":{"name":"Performance Timeline Level 2","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/performance-timeline","idenitifer":"Performance Timeline Level 2"},"features":["Performance.prototype.getEntries"]},"Page Visibility (Second Edition)":{"info":{"name":"Page Visibility (Second Edition)","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/page-visibility/","idenitifer":"Page Visibility (Second Edition)"},"features":["document.hidden","document.mozHidden","document.mozVisibilityState","document.visibilityState"]},"Resource Timing":{"info":{"name":"Resource Timing","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/resource-timing/","idenitifer":"Resource Timing"},"features":["performance.onresourcetimingbufferfull","Performance.prototype.clearResourceTimings","Performance.prototype.setResourceTimingBufferSize"]},"Shadow DOM":{"info":{"name":"Shadow DOM","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/shadow-dom/","idenitifer":"Shadow DOM"},"features":["HTMLContentElement.prototype.getDistributedNodes"]},"Selection API":{"info":{"name":"Selection API","subsection_number":null,"subsection_name":null,"url":"http://w3c.github.io/selection-api/","idenitifer":"Selection API"},"features":["HTMLDocument.prototype.getSelection","Selection.prototype.addRange","Selection.prototype.collapse","Selection.prototype.collapseToEnd","Selection.prototype.collapseToStart","Selection.prototype.containsNode","Selection.prototype.deleteFromDocument","Selection.prototype.extend","Selection.prototype.getRangeAt","Selection.prototype.modify","Selection.prototype.removeAllRanges","Selection.prototype.removeRange","Selection.prototype.selectAllChildren","window.getSelection"]},"Selectors API Level 1":{"info":{"name":"Selectors API Level 1","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/selectors-api","idenitifer":"Selectors API Level 1"},"features":["Document.prototype.querySelector","Document.prototype.querySelectorAll","DocumentFragment.prototype.querySelector","DocumentFragment.prototype.querySelectorAll","Element.prototype.querySelector","Element.prototype.querySelectorAll"]},"Screen Orientation API":{"info":{"name":"Screen Orientation API","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/screen-orientation/","idenitifer":"Screen Orientation API"},"features":["screen.mozOrientation","screen.onmozorientationchange","screen.orientation","Screen.prototype.mozLockOrientation","Screen.prototype.mozUnlockOrientation","ScreenOrientation.prototype.lock","ScreenOrientation.prototype.unlock","window.ondevicemotion","window.ondeviceorientation"]},"Scalable Vector Graphics (SVG) 1.1 (Second Edition)":{"info":{"name":"Scalable Vector Graphics (SVG) 1.1 (Second Edition)","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/SVG/","idenitifer":"Scalable Vector Graphics (SVG) 1.1 (Second Edition)"},"features":["HTMLEmbedElement.prototype.getSVGDocument","HTMLIFrameElement.prototype.getSVGDocument","HTMLObjectElement.prototype.getSVGDocument","SVGAngle.prototype.convertToSpecifiedUnits","SVGAngle.prototype.newValueSpecifiedUnits","SVGAnimationElement.prototype.beginElement","SVGAnimationElement.prototype.beginElementAt","SVGAnimationElement.prototype.endElement","SVGAnimationElement.prototype.endElementAt","SVGAnimationElement.prototype.getCurrentTime","SVGAnimationElement.prototype.getSimpleDuration","SVGAnimationElement.prototype.getStartTime","SVGAnimationElement.prototype.hasExtension","SVGFEDropShadowElement.prototype.setStdDeviation","SVGFEGaussianBlurElement.prototype.setStdDeviation","SVGFilterElement.apply","SVGGraphicsElement.prototype.getBBox","SVGGraphicsElement.prototype.getCTM","SVGGraphicsElement.prototype.getScreenCTM","SVGGraphicsElement.prototype.getTransformToElement","SVGGraphicsElement.prototype.hasExtension","SVGLength.prototype.convertToSpecifiedUnits","SVGLength.prototype.newValueSpecifiedUnits","SVGLengthList.prototype.appendItem","SVGLengthList.prototype.clear","SVGLengthList.prototype.getItem","SVGLengthList.prototype.initialize","SVGLengthList.prototype.insertItemBefore","SVGLengthList.prototype.removeItem","SVGLengthList.prototype.replaceItem","SVGMarkerElement.prototype.setOrientToAngle","SVGMarkerElement.prototype.setOrientToAuto","SVGMatrix.prototype.flipX","SVGMatrix.prototype.flipY","SVGMatrix.prototype.inverse","SVGMatrix.prototype.multiply","SVGMatrix.prototype.rotate","SVGMatrix.prototype.rotateFromVector","SVGMatrix.prototype.scale","SVGMatrix.prototype.scaleNonUniform","SVGMatrix.prototype.skewX","SVGMatrix.prototype.skewY","SVGMatrix.prototype.translate","SVGNumberList.prototype.appendItem","SVGNumberList.prototype.clear","SVGNumberList.prototype.getItem","SVGNumberList.prototype.initialize","SVGNumberList.prototype.insertItemBefore","SVGNumberList.prototype.removeItem","SVGNumberList.prototype.replaceItem","SVGPathElement.prototype.createSVGPathSegArcAbs","SVGPathElement.prototype.createSVGPathSegArcRel","SVGPathElement.prototype.createSVGPathSegClosePath","SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs","SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel","SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs","SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel","SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs","SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel","SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs","SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel","SVGPathElement.prototype.createSVGPathSegLinetoAbs","SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs","SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel","SVGPathElement.prototype.createSVGPathSegLinetoRel","SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs","SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel","SVGPathElement.prototype.createSVGPathSegMovetoAbs","SVGPathElement.prototype.createSVGPathSegMovetoRel","SVGPathElement.prototype.getPathSegAtLength","SVGPathElement.prototype.getPointAtLength","SVGPathElement.prototype.getTotalLength","SVGPathSegList.prototype.appendItem","SVGPathSegList.prototype.clear","SVGPathSegList.prototype.getItem","SVGPathSegList.prototype.initialize","SVGPathSegList.prototype.insertItemBefore","SVGPathSegList.prototype.removeItem","SVGPathSegList.prototype.replaceItem","SVGPoint.prototype.matrixTransform","SVGPointList.prototype.appendItem","SVGPointList.prototype.clear","SVGPointList.prototype.getItem","SVGPointList.prototype.initialize","SVGPointList.prototype.insertItemBefore","SVGPointList.prototype.removeItem","SVGPointList.prototype.replaceItem","SVGStringList.prototype.appendItem","SVGStringList.prototype.clear","SVGStringList.prototype.getItem","SVGStringList.prototype.initialize","SVGStringList.prototype.insertItemBefore","SVGStringList.prototype.removeItem","SVGStringList.prototype.replaceItem","SVGSVGElement.prototype.animationsPaused","SVGSVGElement.prototype.createSVGAngle","SVGSVGElement.prototype.createSVGLength","SVGSVGElement.prototype.createSVGMatrix","SVGSVGElement.prototype.createSVGNumber","SVGSVGElement.prototype.createSVGPoint","SVGSVGElement.prototype.createSVGRect","SVGSVGElement.prototype.createSVGTransform","SVGSVGElement.prototype.createSVGTransformFromMatrix","SVGSVGElement.prototype.deselectAll","SVGSVGElement.prototype.forceRedraw","SVGSVGElement.prototype.getCurrentTime","SVGSVGElement.prototype.getElementById","SVGSVGElement.prototype.pauseAnimations","SVGSVGElement.prototype.setCurrentTime","SVGSVGElement.prototype.suspendRedraw","SVGSVGElement.prototype.unpauseAnimations","SVGSVGElement.prototype.unsuspendRedraw","SVGSVGElement.prototype.unsuspendRedrawAll","SVGSymbolElement.prototype.hasExtension","SVGTextContentElement.prototype.getCharNumAtPosition","SVGTextContentElement.prototype.getComputedTextLength","SVGTextContentElement.prototype.getEndPositionOfChar","SVGTextContentElement.prototype.getExtentOfChar","SVGTextContentElement.prototype.getNumberOfChars","SVGTextContentElement.prototype.getRotationOfChar","SVGTextContentElement.prototype.getStartPositionOfChar","SVGTextContentElement.prototype.getSubStringLength","SVGTextContentElement.prototype.selectSubString","SVGTransform.prototype.setMatrix","SVGTransform.prototype.setRotate","SVGTransform.prototype.setScale","SVGTransform.prototype.setSkewX","SVGTransform.prototype.setSkewY","SVGTransform.prototype.setTranslate","SVGTransformList.prototype.appendItem","SVGTransformList.prototype.clear","SVGTransformList.prototype.consolidate","SVGTransformList.prototype.createSVGTransformFromMatrix","SVGTransformList.prototype.getItem","SVGTransformList.prototype.initialize","SVGTransformList.prototype.insertItemBefore","SVGTransformList.prototype.removeItem","SVGTransformList.prototype.replaceItem"]},"Service Workers":{"info":{"name":"Service Workers","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/service-workers/","idenitifer":"Service Workers"},"features":["Cache.prototype.add","Cache.prototype.addAll","Cache.prototype.delete","Cache.prototype.keys","Cache.prototype.match","Cache.prototype.matchAll","Cache.prototype.put","CacheStorage.prototype.delete","CacheStorage.prototype.has","CacheStorage.prototype.keys","CacheStorage.prototype.match","CacheStorage.prototype.open","window.caches"]},"Timing Control for Script-Based Animations":{"info":{"name":"Timing Control for Script-Based Animations","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/animation-timing","idenitifer":"Timing Control for Script-Based Animations"},"features":["window.cancelAnimationFrame"]},"Tracking Preference Expression (DNT)":{"info":{"name":"Tracking Preference Expression (DNT)","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/tracking-dnt/","idenitifer":"Tracking Preference Expression (DNT)"},"features":["navigator.doNotTrack"]},"UI Events Specification":{"info":{"name":"UI Events Specification","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/uievents/","idenitifer":"UI Events Specification"},"features":["CompositionEvent.prototype.initCompositionEvent","Event.prototype.stopImmediatePropagation","KeyboardEvent.prototype.getModifierState","KeyboardEvent.prototype.initKeyEvent","MouseEvent.prototype.getModifierState","MouseEvent.prototype.initMouseEvent","MutationEvent.prototype.initMutationEvent","UIEvent.prototype.initUIEvent"]},"URL":{"info":{"name":"URL","subsection_number":null,"subsection_name":null,"url":"https://url.spec.whatwg.org/","idenitifer":"URL"},"features":["URLSearchParams.prototype.append","URLSearchParams.prototype.delete","URLSearchParams.prototype.get","URLSearchParams.prototype.getAll","URLSearchParams.prototype.has","URLSearchParams.prototype.set"]},"User Timing Level 2":{"info":{"name":"User Timing Level 2","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/user-timing/","idenitifer":"User Timing Level 2"},"features":["Performance.prototype.clearMarks","Performance.prototype.clearMeasures","Performance.prototype.mark","Performance.prototype.measure"]},"Vibration API":{"info":{"name":"Vibration API","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/vibration/","idenitifer":"Vibration API"},"features":["Navigator.prototype.vibrate"]},"Web Cryptography API":{"info":{"name":"Web Cryptography API","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/WebCryptoAPI/","idenitifer":"Web Cryptography API"},"features":["Crypto.prototype.getRandomValues","SubtleCrypto.prototype.decrypt","SubtleCrypto.prototype.deriveBits","SubtleCrypto.prototype.deriveKey","SubtleCrypto.prototype.digest","SubtleCrypto.prototype.encrypt","SubtleCrypto.prototype.exportKey","SubtleCrypto.prototype.generateKey","SubtleCrypto.prototype.importKey","SubtleCrypto.prototype.sign","SubtleCrypto.prototype.unwrapKey","SubtleCrypto.prototype.verify","SubtleCrypto.prototype.wrapKey","window.crypto"]},"Web Audio API":{"info":{"name":"Web Audio API","subsection_number":null,"subsection_name":null,"url":"https://webaudio.github.io/web-audio-api","idenitifer":"Web Audio API"},"features":["AnalyserNode.prototype.getByteFrequencyData","AnalyserNode.prototype.getByteTimeDomainData","AnalyserNode.prototype.getFloatFrequencyData","AnalyserNode.prototype.getFloatTimeDomainData","AudioBuffer.prototype.copyFromChannel","AudioBuffer.prototype.copyToChannel","AudioBuffer.prototype.getChannelData","AudioBufferSourceNode.prototype.start","AudioBufferSourceNode.prototype.stop","AudioContext.prototype.close","AudioContext.prototype.createAnalyser","AudioContext.prototype.createBiquadFilter","AudioContext.prototype.createBuffer","AudioContext.prototype.createBufferSource","AudioContext.prototype.createChannelMerger","AudioContext.prototype.createChannelSplitter","AudioContext.prototype.createConvolver","AudioContext.prototype.createDelay","AudioContext.prototype.createDynamicsCompressor","AudioContext.prototype.createGain","AudioContext.prototype.createMediaElementSource","AudioContext.prototype.createMediaStreamDestination","AudioContext.prototype.createMediaStreamSource","AudioContext.prototype.createOscillator","AudioContext.prototype.createPanner","AudioContext.prototype.createPeriodicWave","AudioContext.prototype.createScriptProcessor","AudioContext.prototype.createStereoPanner","AudioContext.prototype.createWaveShaper","AudioContext.prototype.decodeAudioData","AudioContext.prototype.resume","AudioContext.prototype.suspend","AudioListener.prototype.setOrientation","AudioListener.prototype.setPosition","AudioListener.prototype.setVelocity","AudioNode.prototype.connect","AudioNode.prototype.disconnect","AudioParam.prototype.cancelScheduledValues","AudioParam.prototype.exponentialRampToValueAtTime","AudioParam.prototype.linearRampToValueAtTime","AudioParam.prototype.setValueAtTime","AudioParam.prototype.setValueCurveAtTime","BiquadFilterNode.prototype.getFrequencyResponse","OfflineAudioContext.prototype.resume","OfflineAudioContext.prototype.startRendering","OfflineAudioContext.prototype.suspend","OscillatorNode.prototype.setPeriodicWave","OscillatorNode.prototype.start","OscillatorNode.prototype.stop","PannerNode.prototype.setOrientation","PannerNode.prototype.setPosition","PannerNode.prototype.setVelocity"]},"WebGL Specification":{"info":{"name":"WebGL Specification","subsection_number":null,"subsection_name":null,"url":"https://www.khronos.org/registry/webgl/specs/latest/1.0/","idenitifer":"WebGL Specification"},"features":["WebGLRenderingContext.prototype.activeTexture","WebGLRenderingContext.prototype.attachShader","WebGLRenderingContext.prototype.bindAttribLocation","WebGLRenderingContext.prototype.bindBuffer","WebGLRenderingContext.prototype.bindFramebuffer","WebGLRenderingContext.prototype.bindRenderbuffer","WebGLRenderingContext.prototype.bindTexture","WebGLRenderingContext.prototype.blendColor","WebGLRenderingContext.prototype.blendEquation","WebGLRenderingContext.prototype.blendEquationSeparate","WebGLRenderingContext.prototype.blendFunc","WebGLRenderingContext.prototype.blendFuncSeparate","WebGLRenderingContext.prototype.bufferData","WebGLRenderingContext.prototype.bufferSubData","WebGLRenderingContext.prototype.checkFramebufferStatus","WebGLRenderingContext.prototype.clear","WebGLRenderingContext.prototype.clearColor","WebGLRenderingContext.prototype.clearDepth","WebGLRenderingContext.prototype.clearStencil","WebGLRenderingContext.prototype.colorMask","WebGLRenderingContext.prototype.compileShader","WebGLRenderingContext.prototype.compressedTexImage2D","WebGLRenderingContext.prototype.compressedTexSubImage2D","WebGLRenderingContext.prototype.copyTexImage2D","WebGLRenderingContext.prototype.copyTexSubImage2D","WebGLRenderingContext.prototype.createBuffer","WebGLRenderingContext.prototype.createFramebuffer","WebGLRenderingContext.prototype.createProgram","WebGLRenderingContext.prototype.createRenderbuffer","WebGLRenderingContext.prototype.createShader","WebGLRenderingContext.prototype.createTexture","WebGLRenderingContext.prototype.cullFace","WebGLRenderingContext.prototype.deleteBuffer","WebGLRenderingContext.prototype.deleteFramebuffer","WebGLRenderingContext.prototype.deleteProgram","WebGLRenderingContext.prototype.deleteRenderbuffer","WebGLRenderingContext.prototype.deleteShader","WebGLRenderingContext.prototype.deleteTexture","WebGLRenderingContext.prototype.depthFunc","WebGLRenderingContext.prototype.depthMask","WebGLRenderingContext.prototype.depthRange","WebGLRenderingContext.prototype.detachShader","WebGLRenderingContext.prototype.disable","WebGLRenderingContext.prototype.disableVertexAttribArray","WebGLRenderingContext.prototype.drawArrays","WebGLRenderingContext.prototype.drawElements","WebGLRenderingContext.prototype.enable","WebGLRenderingContext.prototype.enableVertexAttribArray","WebGLRenderingContext.prototype.finish","WebGLRenderingContext.prototype.flush","WebGLRenderingContext.prototype.framebufferRenderbuffer","WebGLRenderingContext.prototype.framebufferTexture2D","WebGLRenderingContext.prototype.frontFace","WebGLRenderingContext.prototype.generateMipmap","WebGLRenderingContext.prototype.getActiveAttrib","WebGLRenderingContext.prototype.getActiveUniform","WebGLRenderingContext.prototype.getAttachedShaders","WebGLRenderingContext.prototype.getAttribLocation","WebGLRenderingContext.prototype.getBufferParameter","WebGLRenderingContext.prototype.getContextAttributes","WebGLRenderingContext.prototype.getError","WebGLRenderingContext.prototype.getExtension","WebGLRenderingContext.prototype.getFramebufferAttachmentParameter","WebGLRenderingContext.prototype.getParameter","WebGLRenderingContext.prototype.getProgramInfoLog","WebGLRenderingContext.prototype.getProgramParameter","WebGLRenderingContext.prototype.getRenderbufferParameter","WebGLRenderingContext.prototype.getShaderInfoLog","WebGLRenderingContext.prototype.getShaderParameter","WebGLRenderingContext.prototype.getShaderPrecisionFormat","WebGLRenderingContext.prototype.getShaderSource","WebGLRenderingContext.prototype.getSupportedExtensions","WebGLRenderingContext.prototype.getTexParameter","WebGLRenderingContext.prototype.getUniform","WebGLRenderingContext.prototype.getUniformLocation","WebGLRenderingContext.prototype.getVertexAttrib","WebGLRenderingContext.prototype.getVertexAttribOffset","WebGLRenderingContext.prototype.hint","WebGLRenderingContext.prototype.isBuffer","WebGLRenderingContext.prototype.isContextLost","WebGLRenderingContext.prototype.isEnabled","WebGLRenderingContext.prototype.isFramebuffer","WebGLRenderingContext.prototype.isProgram","WebGLRenderingContext.prototype.isRenderbuffer","WebGLRenderingContext.prototype.isShader","WebGLRenderingContext.prototype.isTexture","WebGLRenderingContext.prototype.lineWidth","WebGLRenderingContext.prototype.linkProgram","WebGLRenderingContext.prototype.pixelStorei","WebGLRenderingContext.prototype.polygonOffset","WebGLRenderingContext.prototype.readPixels","WebGLRenderingContext.prototype.renderbufferStorage","WebGLRenderingContext.prototype.sampleCoverage","WebGLRenderingContext.prototype.scissor","WebGLRenderingContext.prototype.shaderSource","WebGLRenderingContext.prototype.stencilFunc","WebGLRenderingContext.prototype.stencilFuncSeparate","WebGLRenderingContext.prototype.stencilMask","WebGLRenderingContext.prototype.stencilMaskSeparate","WebGLRenderingContext.prototype.stencilOp","WebGLRenderingContext.prototype.stencilOpSeparate","WebGLRenderingContext.prototype.texImage2D","WebGLRenderingContext.prototype.texParameterf","WebGLRenderingContext.prototype.texParameteri","WebGLRenderingContext.prototype.texSubImage2D","WebGLRenderingContext.prototype.uniform1f","WebGLRenderingContext.prototype.uniform1fv","WebGLRenderingContext.prototype.uniform1i","WebGLRenderingContext.prototype.uniform1iv","WebGLRenderingContext.prototype.uniform2f","WebGLRenderingContext.prototype.uniform2fv","WebGLRenderingContext.prototype.uniform2i","WebGLRenderingContext.prototype.uniform2iv","WebGLRenderingContext.prototype.uniform3f","WebGLRenderingContext.prototype.uniform3fv","WebGLRenderingContext.prototype.uniform3i","WebGLRenderingContext.prototype.uniform3iv","WebGLRenderingContext.prototype.uniform4f","WebGLRenderingContext.prototype.uniform4fv","WebGLRenderingContext.prototype.uniform4i","WebGLRenderingContext.prototype.uniform4iv","WebGLRenderingContext.prototype.uniformMatrix2fv","WebGLRenderingContext.prototype.uniformMatrix3fv","WebGLRenderingContext.prototype.uniformMatrix4fv","WebGLRenderingContext.prototype.useProgram","WebGLRenderingContext.prototype.validateProgram","WebGLRenderingContext.prototype.vertexAttrib1f","WebGLRenderingContext.prototype.vertexAttrib1fv","WebGLRenderingContext.prototype.vertexAttrib2f","WebGLRenderingContext.prototype.vertexAttrib2fv","WebGLRenderingContext.prototype.vertexAttrib3f","WebGLRenderingContext.prototype.vertexAttrib3fv","WebGLRenderingContext.prototype.vertexAttrib4f","WebGLRenderingContext.prototype.vertexAttrib4fv","WebGLRenderingContext.prototype.vertexAttribPointer","WebGLRenderingContext.prototype.viewport"]},"WebVTT: The Web Video Text Tracks Format":{"info":{"name":"WebVTT: The Web Video Text Tracks Format","subsection_number":null,"subsection_name":null,"url":"https://w3c.github.io/webvtt/","idenitifer":"WebVTT: The Web Video Text Tracks Format"},"features":["VTTCue.prototype.getCueAsHTML"]},"Web Notifications":{"info":{"name":"Web Notifications","subsection_number":null,"subsection_name":null,"url":"https://notifications.spec.whatwg.org/","idenitifer":"Web Notifications"},"features":["DesktopNotification.prototype.show","DesktopNotificationCenter.prototype.createNotification","Notification.get","Notification.prototype.close","Notification.requestPermission"]},"WebRTC 1.0: Real-time Communication Between Browser":{"info":{"name":"WebRTC 1.0: Real-time Communication Between Browser","subsection_number":null,"subsection_name":null,"url":"https://www.w3.org/TR/webrtc/","idenitifer":"WebRTC 1.0: Real-time Communication Between Browser"},"features":["DataChannel.prototype.close","DataChannel.prototype.send","mozRTCPeerConnection.generateCertificate","mozRTCPeerConnection.prototype.addIceCandidate","mozRTCPeerConnection.prototype.addStream","mozRTCPeerConnection.prototype.addTrack","mozRTCPeerConnection.prototype.close","mozRTCPeerConnection.prototype.createAnswer","mozRTCPeerConnection.prototype.createDataChannel","mozRTCPeerConnection.prototype.createOffer","mozRTCPeerConnection.prototype.getConfiguration","mozRTCPeerConnection.prototype.getIdentityAssertion","mozRTCPeerConnection.prototype.getLocalStreams","mozRTCPeerConnection.prototype.getReceivers","mozRTCPeerConnection.prototype.getRemoteStreams","mozRTCPeerConnection.prototype.getSenders","mozRTCPeerConnection.prototype.getStats","mozRTCPeerConnection.prototype.getStreamById","mozRTCPeerConnection.prototype.removeStream","mozRTCPeerConnection.prototype.removeTrack","mozRTCPeerConnection.prototype.setIdentityProvider","mozRTCPeerConnection.prototype.setLocalDescription","mozRTCPeerConnection.prototype.setRemoteDescription","mozRTCPeerConnection.prototype.updateIce","RTCRtpSender.prototype.replaceTrack","RTCStatsReport.prototype.forEach","RTCStatsReport.prototype.get","RTCStatsReport.prototype.has"]}}; \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index cd8d35c..9a421a9 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,6 +1,5 @@ const gulp = require("gulp"); const fs = require("fs"); -const UglifyJS = require("uglify-es"); gulp.task('default', function () { @@ -36,34 +35,5 @@ gulp.task('default', function () { const renderedStandardsModule = builtScriptComment + `window.WEB_API_MANAGER.standards = ${JSON.stringify(combinedStandards)};`; - fs.writeFileSync("content_scripts/dist/standards.js", renderedStandardsModule); - - const proxyBlockSrc = fs.readFileSync("content_scripts/src/proxyblock.js", "utf8"); - const instrumentSrc = fs.readFileSync("content_scripts/src/instrument.js", "utf8"); - - const stripCommentsFromSource = function (source) { - const fileLines = source.split("\n"); - const linesWithoutComments = fileLines.filter(aLine => !isLineAComment(aLine)); - return linesWithoutComments.join("\n"); - }; - - const proxyBlockSrcWOComments = stripCommentsFromSource(proxyBlockSrc); - const instrumentSrcWOComments = stripCommentsFromSource(instrumentSrc); - - const proxyBlockSrcWithHeader = "/** This code is a minified version of content_scripts/src/proxyblock.js **/\n" + proxyBlockSrcWOComments; - const instrumentSrcWithProxyInjected = instrumentSrcWOComments.replace( - "###-INJECTED-PROXY-BLOCKING-CODE-###", - UglifyJS.minify(proxyBlockSrcWithHeader, {mangle: false}).code - ); - - fs.writeFileSync("content_scripts/dist/instrument.js", builtScriptComment + instrumentSrcWithProxyInjected); - - // Last, several content script files are just copied over, unmodified, - // as script files to be injected. - const srcFilesToCopy = ["defaults.js"]; - srcFilesToCopy.forEach(function (aSrcPath) { - const scriptSrc = fs.readFileSync("content_scripts/src/" + aSrcPath, "utf8"); - const scriptSrcWOComments = stripCommentsFromSource(scriptSrc); - fs.writeFileSync("content_scripts/dist/" + aSrcPath, builtScriptComment + scriptSrcWOComments); - }); + fs.writeFileSync("data/standards.js", renderedStandardsModule); }); diff --git a/lib/cookieencoding.js b/lib/cookieencoding.js new file mode 100644 index 0000000..a4c3588 --- /dev/null +++ b/lib/cookieencoding.js @@ -0,0 +1,82 @@ +/*jslint es6: true*/ +/*global window*/ +(function () { + "use strict"; + const {packingLib, standards, constants} = window.WEB_API_MANAGER; + const standardsNames = Object.keys(standards); + const shouldLogKey = constants.shouldLogKey; + const allStandardsWithShouldLogOption = standardsNames.concat([shouldLogKey]); + + /** + * Creates a cookie safe encoding of standards to block, and + * whether logging should be enabled. + * + * This function is the inverse of the `fromCookieValue` function + * in this module. + * + * The `standardsToBlock` array must be a subset of all the standards + * documented in data/standards. + * + * @param array standardsToBlock + * An array of strings, each a standard that should be blocked. + * @param bool shouldLog + * Whether logging should be enabled. + * + * @return string + * A cookie safe string encoding the above values. + */ + const toCookieValue = function (standardsToBlock, shouldLog) { + + const standardsToBlockWithshouldLogKey = shouldLog + ? standardsToBlock.concat(shouldLogKey) + : standardsToBlock; + + const packedValues = packingLib.pack( + allStandardsWithShouldLogOption, + standardsToBlockWithshouldLogKey + ); + + // Last, replace "=" with "-" in the base64 string, to avoid + // silly ambiguities in the cookie value. + return packedValues.replace(/\=/g, "-"); + }; + + /** + * Takes a encoded string (created from the `toCookieValue` function + * in this module) and returns to values, one an array of + * standard names, and two, a boolean flag of whether the logging option + * is enabled. + * + * @param string data + * A string created from `toCookieValue` + * + * @return [array, bool] + * An array of strings of standard names (representing standards to + * block), and a boolean describing whether to log blocking + * behavior. + */ + const fromCookieValue = function (data) { + + const base64Data = data.replace(/-/g, "="); + + const unpackedValues = packingLib.unpack(allStandardsWithShouldLogOption, base64Data); + + let shouldLog; + const standardsToBlock = unpackedValues; + const indexOfShouldLog = unpackedValues.indexOf(shouldLogKey); + + if (indexOfShouldLog === -1) { + shouldLog = false; + } else { + shouldLog = true; + standardsToBlock.splice(indexOfShouldLog, 1); + } + + return [standardsToBlock, shouldLog]; + }; + + window.WEB_API_MANAGER.cookieEncodingLib = { + toCookieValue, + fromCookieValue + }; +}()); \ No newline at end of file diff --git a/content_scripts/src/defaults.js b/lib/defaults.js similarity index 96% rename from content_scripts/src/defaults.js rename to lib/defaults.js index 8d4262f..36a4c17 100644 --- a/content_scripts/src/defaults.js +++ b/lib/defaults.js @@ -1,3 +1,5 @@ +/*jslint es6: true, browser: true*/ +/*global window*/ /** * This file defines default blocking rules for domains that haven't been * overwritten, either by the extension user, or by a subscribed policy diff --git a/lib/httpheaders.js b/lib/httpheaders.js new file mode 100644 index 0000000..db2dbcd --- /dev/null +++ b/lib/httpheaders.js @@ -0,0 +1,113 @@ +/*jslint es6: true*/ +/*global window*/ +(function () { + "use strict"; + + /** + * Returns a boolean description of whether the given header + * (in the structure defined by the WebExtension WebRequest API) + * is describing a Set-Cookie instruction. + * + * @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/HttpHeaders + * + * @param object header + * An object describing a HTTP header + * + * @return boolean + * true if the given object represents a Set-Cookie instruction, and false + * in all other cases. + */ + const isSetCookie = function (header) { + + return ( + header && + header.name && + header.name.toLowerCase().indexOf("set-cookie") !== -1 + ); + }; + + /** + * Returns a boolean description of whether the given header + * (in the structure defined by the WebExtension WebRequest API) + * is describing a Content-Security-Policy for a site. + * + * @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/HttpHeaders + * + * @param object header + * An object describing a HTTP header + * + * @return boolean + * true if the given object represents a HTTP CSP header, and false + * in all other cases. + */ + const isHeaderCSP = function (header) { + + return ( + header && + header.name && + header.name.toLowerCase().indexOf("content-security-policy") !== -1 + ); + }; + + /** + * Returns a boolean description of whether the given header + * (in the structure defined by the WebExtension WebRequest API) + * is describing a strict dynamic Content-Security-Policy for a site. + * + * @see https://w3c.github.io/webappsec-csp/#strict-dynamic-usage + * + * @param object header + * An object describing a HTTP header + * + * @return boolean + * true if the given object is a CSP header that defines a + * "strict-dynamic" policy, and false in all other cases. + */ + const isCSPHeaderSettingStrictDynamic = function (header) { + + return ( + header && + header.name && + header.value.indexOf("'strict-dynamic'") !== -1 + ); + }; + + /** + * Returns a new CSP instruction, with source with the given hash + * whitelisted. + * + * @see https://w3c.github.io/webappsec-csp/#strict-dynamic-usage + * @see https://w3c.github.io/webappsec-csp/#grammardef-hash-source + * + * @param string cspInstruction + * The value of a HTTP header defining a CSP instruction. + * @param string scriptHash + * A hash value, in the form of "sha256-", that is a valid + * hash source description. + * + * @return string|false + * Returns false if the CSP instruction looks malformed (ie we + * couldn't find a "script-src" tag), otherwise, a new valud + * CSP instruction with the given hash allowed. + */ + const createCSPInstructionWithHashAllowed = function (cspInstruction, scriptHash) { + + const indexOfScriptSrc = cspInstruction.indexOf("script-src "); + if (indexOfScriptSrc === -1) { + return false; + } + + const preSrcScript = cspInstruction.substring(0, indexOfScriptSrc); + const postScriptSrc = cspInstruction.substring(indexOfScriptSrc + 10); + const newInstruction = preSrcScript + "script-src '" + scriptHash + "' " + postScriptSrc; + + return newInstruction; + }; + + window.WEB_API_MANAGER.httpHeadersLib = { + isSetCookie, + isHeaderCSP, + isCSPHeaderSettingStrictDynamic, + createCSPInstructionWithHashAllowed + }; +}()); \ No newline at end of file diff --git a/lib/init.js b/lib/init.js index 1e772b3..690840f 100644 --- a/lib/init.js +++ b/lib/init.js @@ -3,5 +3,10 @@ // the "namespace" we'll use for all the content scripts in the extension. (function () { "use strict"; - window.WEB_API_MANAGER = {}; + window.WEB_API_MANAGER = { + constants: { + cookieName: "wam-temp-cookie", + shouldLogKey: "shouldLogKey" + } + }; }()); diff --git a/lib/proxyblock.js b/lib/proxyblock.js new file mode 100644 index 0000000..a04b65a --- /dev/null +++ b/lib/proxyblock.js @@ -0,0 +1,236 @@ +/*jslint es6: true, browser: true*/ +/*global window, sjcl*/ +// This module generates JavaScript code for instrumenting the DOM +// to prevent pages from accessing Web API standards. This code +// is generated programatically so that both the background and content +// scripts can determine the SHA256 hash of the injected code, so that +// we can set the CSP policy as needed. +(function () { + "use strict"; + + // This function is what does the instrumenting of the DOM, + // based on values set in the global window.WEB_API_MANAGER_PAGE + // structure. This function is never called, but is rendered to + // a string (with Function.prototype.toString) and inserted into + // content scripts. Its written here as a proper function + // just to make it easier to write and deploy (ie vim highlights + // it just like any other JS). + const proxyBlockingFunction = function () { + console.log("well it runs"); + const settings = window.WEB_API_MANAGER_PAGE; + const shouldLog = settings.shouldLog; + const standardsToBlock = settings.toBlock; + const standardDefinitions = settings.standards; + const hostName = window.location.hostname; + + // Its possible that the Web API removal code will block direct references + // to the following methods, so grab references to them before the + // DOM is instrumented (and their references are possibly blocked). + const removeChild = window.Element.prototype.removeChild; + const getElementsByTagName = window.document.getElementsByTagName; + + const defaultFunction = function () {}; + const funcPropNames = Object.getOwnPropertyNames(defaultFunction); + const unconfigurablePropNames = funcPropNames.filter(function (propName) { + const possiblePropDesc = Object.getOwnPropertyDescriptor(defaultFunction, propName); + return (possiblePropDesc && !possiblePropDesc.configurable); + }); + + const featuresToBlock = standardsToBlock.reduce(function (prev, cur) { + return prev.concat(standardDefinitions[cur].features); + }, []); + + const toPrimitiveFunc = function (hint) { + if (hint === "number" || hint === "default") { + return 0; + } + if (hint === "string") { + return ""; + } + return undefined; + }; + + const keyPathToRefPath = function (keyPath) { + const keyParts = keyPath.split("."); + return keyParts.reduce(function (prev, cur) { + + if (prev === undefined) { + return undefined; + } + + const numNodes = prev.length; + const currentLeaf = (numNodes === 0) + ? window + : prev[numNodes - 1]; + const nextLeaf = currentLeaf[cur]; + + if (nextLeaf === undefined) { + return undefined; + } + + return prev.concat([nextLeaf]); + }, []); + }; + + const createBlockingProxy = function (keyPath) { + + let hasBeenLogged = false; + + const logKeyPath = function () { + + if (keyPath !== undefined && + hasBeenLogged === false && + shouldLog) { + hasBeenLogged = true; + console.log("Blocked '" + keyPath + "' on '" + hostName + "'"); + } + }; + + let blockingProxy; + blockingProxy = new Proxy(defaultFunction, { + get: function (ignore, property) { + logKeyPath(); + + if (property === Symbol.toPrimitive) { + return toPrimitiveFunc; + } + + if (property === "valueOf") { + return toPrimitiveFunc; + } + + return blockingProxy; + }, + set: function () { + logKeyPath(); + return blockingProxy; + }, + apply: function () { + logKeyPath(); + return blockingProxy; + }, + ownKeys: function (ignore) { + return unconfigurablePropNames; + }, + has: function (ignore, property) { + return (unconfigurablePropNames.indexOf(property) > -1); + }, + getOwnPropertyDescriptor: function (ignore, property) { + if (unconfigurablePropNames.indexOf(property) === -1) { + return undefined; + } + return Object.getOwnPropertyDescriptor(defaultFunction, property); + } + }); + + return blockingProxy; + }; + + const defaultBlockingProxy = createBlockingProxy(); + + const blockFeatureAtKeyPath = function (keyPath) { + const propertyRefs = keyPathToRefPath(keyPath); + + // If we weren't able to turn the key path into an array of references, + // then it means that the property doesn't exist in this DOM / + // environment, so there is nothing to block. + if (propertyRefs === undefined) { + return false; + } + + const keyPathSegments = keyPath.split("."); + const lastPropertyName = keyPathSegments[keyPathSegments.length - 1]; + const leafRef = propertyRefs[propertyRefs.length - 1]; + const parentRef = propertyRefs[propertyRefs.length - 2]; + + // At least for now, only interpose on methods. + if (typeof leafRef !== "function") { + return false; + } + + try { + + if (shouldLog === true) { + parentRef[lastPropertyName] = createBlockingProxy(keyPath); + return true; + } + + parentRef[lastPropertyName] = defaultBlockingProxy; + return true; + + } catch (e) { + + if (shouldLog) { + console.log("Error instrumenting " + keyPath + ": " + e); + } + + return false; + } + }; + + featuresToBlock.forEach(blockFeatureAtKeyPath); + + // Next, delete the WEB_API_MANAGER_PAGE global property. Technically + // this never needed to be global, but doing so allows for easier + // jslinting of the code, makes things easier to understand (for me + // at least) and doesn't have any side effect as long as we delete + // it when we're done, and before the page scripts can start running. + delete window.WEB_API_MANAGER_PAGE; + + console.log("well its done"); + // Last, remove the script tag containing this code from the document, + // so that the structure of the page looks like what the page author + // expects / intended. + const scriptTags = getElementsByTagName.call(window.document, "script"); + removeChild.call(scriptTags[0].parentNode, scriptTags[0]); + }; + + /** + * Generates a script payload, for injecting into content scripts. The + * generated string is 99% the above proxyBlockingFunction function, + * but with the window.WEB_API_MANAGER_PAGE object set up + * correctly to block the desired functions. + * + * @param object standards + * A mapping of standard names to information about those standards. + * The structure of this object should match whats in data/standards.js + * @param array standardNamesToBlock + * An array of strings, which must be a subset of the keys of the + * standards object. + * @param bool shouldLog + * Whether to log the behavior of the blocking proxy. + * + * @return [string, hash] + * Returns an array containing two values. First, JavaScript code + * that instruments the DOM of page's its injected into to render the + * standardNamesToBlock standards un-reachable, and second, a + * base64 encoded sha256 hash of the code. + */ + const generateScriptPayload = function (standards, standardNamesToBlock, shouldLog) { + + const proxyBlockingSettings = ` + window.WEB_API_MANAGER_PAGE = { + standards: ${JSON.stringify(standards)}, + toBlock: ${JSON.stringify(standardNamesToBlock)}, + shouldLog: ${shouldLog ? "true" : "false"} + }; + `; + + const proxyingBlockingSrc = "(" + proxyBlockingFunction.toString() + "())"; + const completeScriptCode = proxyBlockingSettings + "\n" + proxyingBlockingSrc; + + // Use the SJ Crypto library, instead of the WebCrypto library, + // because we need to compute hashes syncronously (so we can + // be sure the hash operation will complete before we let page + // script run). + // https://bitwiseshiftleft.github.io/sjcl/doc/sjcl.hash.sha256.html + const hash = sjcl.hash.sha256.hash(completeScriptCode); + const hashBase64 = sjcl.codec.base64.fromBits(hash); + + return [completeScriptCode, hashBase64]; + }; + + window.WEB_API_MANAGER.proxyBlockLib = { + generateScriptPayload + }; +}()); diff --git a/lib/URI.js b/lib/vendor/URI.js similarity index 100% rename from lib/URI.js rename to lib/vendor/URI.js diff --git a/lib/js.cookie.js b/lib/vendor/js.cookie.js similarity index 100% rename from lib/js.cookie.js rename to lib/vendor/js.cookie.js diff --git a/lib/vendor/sjcl.js b/lib/vendor/sjcl.js new file mode 100644 index 0000000..c3c3ea9 --- /dev/null +++ b/lib/vendor/sjcl.js @@ -0,0 +1,60 @@ +"use strict";var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}}; +sjcl.cipher.aes=function(a){this.s[0][0][0]||this.O();var b,c,d,e,f=this.s[0][4],g=this.s[1];b=a.length;var h=1;if(4!==b&&6!==b&&8!==b)throw new sjcl.exception.invalid("invalid aes key size");this.b=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c& +255]]}; +sjcl.cipher.aes.prototype={encrypt:function(a){return t(this,a,0)},decrypt:function(a){return t(this,a,1)},s:[[[],[],[],[],[]],[[],[],[],[],[]]],O:function(){var a=this.s[0],b=this.s[1],c=a[4],d=b[4],e,f,g,h=[],k=[],l,n,m,p;for(e=0;0x100>e;e++)k[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=l||1,g=k[g]||1)for(m=g^g<<1^g<<2^g<<3^g<<4,m=m>>8^m&255^99,c[f]=m,d[m]=f,n=h[e=h[l=h[f]]],p=0x1010101*n^0x10001*e^0x101*l^0x1010100*f,n=0x101*h[m]^0x1010100*m,e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8;for(e= +0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}}; +function t(a,b,c){if(4!==b.length)throw new sjcl.exception.invalid("invalid aes block size");var d=a.b[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,k,l,n=d.length/4-2,m,p=4,r=[0,0,0,0];h=a.s[c];a=h[0];var q=h[1],v=h[2],w=h[3],x=h[4];for(m=0;m>>24]^q[f>>16&255]^v[g>>8&255]^w[b&255]^d[p],k=a[f>>>24]^q[g>>16&255]^v[b>>8&255]^w[e&255]^d[p+1],l=a[g>>>24]^q[b>>16&255]^v[e>>8&255]^w[f&255]^d[p+2],b=a[b>>>24]^q[e>>16&255]^v[f>>8&255]^w[g&255]^d[p+3],p+=4,e=h,f=k,g=l;for(m= +0;4>m;m++)r[c?3&-m:m]=x[e>>>24]<<24^x[f>>16&255]<<16^x[g>>8&255]<<8^x[b&255]^d[p++],h=e,e=f,f=g,g=b,b=h;return r} +sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.$(a.slice(b/32),32-(b&31)).slice(1);return void 0===c?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return!1;var c=0,d;for(d=0;d>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32>>24|c>>>8&0xff00|(c&0xff00)<<8|c<<24;return a}}; +sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d>>8>>>8>>>8),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c>>g)>>>e),gn){if(!b)try{return sjcl.codec.base32hex.toBits(a)}catch(p){}throw new sjcl.exception.invalid("this isn't "+m+"!");}h>e?(h-=e,f.push(l^n>>>h),l=n<>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.B,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;dh)throw new sjcl.exception.invalid("this isn't base64!");26>>e),g=h<<32-e):(e+=6,g^=h<<32-e)}e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.b[0]||this.O();a?(this.F=a.F.slice(0),this.A=a.A.slice(0),this.l=a.l):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()}; +sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.F=this.Y.slice(0);this.A=[];this.l=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.A=sjcl.bitArray.concat(this.A,a);b=this.l;a=this.l=b+sjcl.bitArray.bitLength(a);if(0x1fffffffffffffb;c++){e=!0;for(d=2;d*d<=c;d++)if(0===c%d){e= +!1;break}e&&(8>b&&(this.Y[b]=a(Math.pow(c,.5))),this.b[b]=a(Math.pow(c,1/3)),b++)}}}; +function u(a,b){var c,d,e,f=a.F,g=a.b,h=f[0],k=f[1],l=f[2],n=f[3],m=f[4],p=f[5],r=f[6],q=f[7];for(c=0;64>c;c++)16>c?d=b[c]:(d=b[c+1&15],e=b[c+14&15],d=b[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+b[c&15]+b[c+9&15]|0),d=d+q+(m>>>6^m>>>11^m>>>25^m<<26^m<<21^m<<7)+(r^m&(p^r))+g[c],q=r,r=p,p=m,m=n+d|0,n=l,l=k,k=h,h=d+(k&l^n&(k^l))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;f[0]=f[0]+h|0;f[1]=f[1]+k|0;f[2]=f[2]+l|0;f[3]=f[3]+n|0;f[4]=f[4]+m|0;f[5]=f[5]+p|0;f[6]=f[6]+r|0;f[7]= +f[7]+q|0} +sjcl.mode.ccm={name:"ccm",G:[],listenProgress:function(a){sjcl.mode.ccm.G.push(a)},unListenProgress:function(a){a=sjcl.mode.ccm.G.indexOf(a);-1k)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(f=2;4>f&&l>>>8*f;f++);f<15-k&&(f=15-k);c=h.clamp(c, +8*(15-f));b=sjcl.mode.ccm.V(a,b,c,d,e,f);g=sjcl.mode.ccm.C(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),k=f.clamp(b,h-e),l=f.bitSlice(b,h-e),h=(h-e)/8;if(7>g)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));k=sjcl.mode.ccm.C(a,k,c,l,e,b);a=sjcl.mode.ccm.V(a,k.data,c,d,e,b);if(!f.equal(k.tag,a))throw new sjcl.exception.corrupt("ccm: tag doesn't match"); +return k.data},na:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,k=h.i;d=[h.partial(8,(b.length?64:0)|d-2<<2|f-1)];d=h.concat(d,c);d[3]|=e;d=a.encrypt(d);if(b.length)for(c=h.bitLength(b)/8,65279>=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c])),g=h.concat(g,b),b=0;be||16n&&(sjcl.mode.ccm.fa(g/ +k),n+=m),c[3]++,e=a.encrypt(c),b[g]^=e[0],b[g+1]^=e[1],b[g+2]^=e[2],b[g+3]^=e[3];return{tag:d,data:h.clamp(b,l)}}}; +sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){if(128!==sjcl.bitArray.bitLength(c))throw new sjcl.exception.invalid("ocb iv must be 128 bits");var g,h=sjcl.mode.ocb2.S,k=sjcl.bitArray,l=k.i,n=[0,0,0,0];c=h(a.encrypt(c));var m,p=[];d=d||[];e=e||64;for(g=0;g+4e.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c); +return a.encrypt(f(d(f(h,d(h))),g))},S:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}}; +sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.C(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.C(!1,a,f,d,c,e);if(!g.equal(a.tag,b))throw new sjcl.exception.corrupt("gcm: tag doesn't match");return a.data},ka:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.i;e=[0,0, +0,0];f=b.slice(0);for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},j:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;de&&(a=b.hash(a));for(d=0;dd||0>c)throw new sjcl.exception.invalid("invalid params to pbkdf2");"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,k,l=[],n=sjcl.bitArray;for(k=1;32*l.length<(d||1);k++){e=f=a.encrypt(n.concat(b,[k]));for(g=1;gg;g++)e.push(0x100000000*Math.random()|0);for(g=0;g=1<this.o&&(this.o= +f);this.P++;this.b=sjcl.hash.sha256.hash(this.b.concat(e));this.L=new sjcl.cipher.aes(this.b);for(d=0;4>d&&(this.h[d]=this.h[d]+1|0,!this.h[d]);d++);}for(d=0;d>>1;this.c[g].update([d,this.N++,2,b,f,a.length].concat(a))}break;case "string":void 0===b&&(b=a.length);this.c[g].update([d,this.N++,3,b,f,a.length]);this.c[g].update(a);break;default:k=1}if(k)throw new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string");this.m[g]+=b;this.f+=b;h===this.u&&(this.isReady()!==this.u&&A("seeded",Math.max(this.o,this.f)),A("progress",this.getProgress()))}, +isReady:function(a){a=this.T[void 0!==a?a:this.M];return this.o&&this.o>=a?this.m[0]>this.ba&&(new Date).valueOf()>this.Z?this.J|this.I:this.I:this.f>=a?this.J|this.u:this.u},getProgress:function(a){a=this.T[a?a:this.M];return this.o>=a?1:this.f>a?1:this.f/a},startCollectors:function(){if(!this.D){this.a={loadTimeCollector:B(this,this.ma),mouseCollector:B(this,this.oa),keyboardCollector:B(this,this.la),accelerometerCollector:B(this,this.ea),touchCollector:B(this,this.qa)};if(window.addEventListener)window.addEventListener("load", +this.a.loadTimeCollector,!1),window.addEventListener("mousemove",this.a.mouseCollector,!1),window.addEventListener("keypress",this.a.keyboardCollector,!1),window.addEventListener("devicemotion",this.a.accelerometerCollector,!1),window.addEventListener("touchmove",this.a.touchCollector,!1);else if(document.attachEvent)document.attachEvent("onload",this.a.loadTimeCollector),document.attachEvent("onmousemove",this.a.mouseCollector),document.attachEvent("keypress",this.a.keyboardCollector);else throw new sjcl.exception.bug("can't attach event"); +this.D=!0}},stopCollectors:function(){this.D&&(window.removeEventListener?(window.removeEventListener("load",this.a.loadTimeCollector,!1),window.removeEventListener("mousemove",this.a.mouseCollector,!1),window.removeEventListener("keypress",this.a.keyboardCollector,!1),window.removeEventListener("devicemotion",this.a.accelerometerCollector,!1),window.removeEventListener("touchmove",this.a.touchCollector,!1)):document.detachEvent&&(document.detachEvent("onload",this.a.loadTimeCollector),document.detachEvent("onmousemove", +this.a.mouseCollector),document.detachEvent("keypress",this.a.keyboardCollector)),this.D=!1)},addEventListener:function(a,b){this.K[a][this.ga++]=b},removeEventListener:function(a,b){var c,d,e=this.K[a],f=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&f.push(d);for(c=0;cb&&(a.h[b]=a.h[b]+1|0,!a.h[b]);b++);return a.L.encrypt(a.h)} +function B(a,b){return function(){b.apply(a,arguments)}}sjcl.random=new sjcl.prng(6); +a:try{var D,E,F,G;if(G="undefined"!==typeof module&&module.exports){var H;try{H=require("crypto")}catch(a){H=null}G=E=H}if(G&&E.randomBytes)D=E.randomBytes(128),D=new Uint32Array((new Uint8Array(D)).buffer),sjcl.random.addEntropy(D,1024,"crypto['randomBytes']");else if("undefined"!==typeof window&&"undefined"!==typeof Uint32Array){F=new Uint32Array(32);if(window.crypto&&window.crypto.getRandomValues)window.crypto.getRandomValues(F);else if(window.msCrypto&&window.msCrypto.getRandomValues)window.msCrypto.getRandomValues(F); +else break a;sjcl.random.addEntropy(F,1024,"crypto['getRandomValues']")}}catch(a){"undefined"!==typeof window&&window.console&&(console.log("There was an error collecting entropy from the browser:"),console.log(a))} +sjcl.json={defaults:{v:1,iter:1E4,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},ja:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.g({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.g(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));if(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length|| +4=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4 (https://www.cs.uic.edu/~psnyder/)", "license": "GPL-3.0", "dependencies": { - "gulp": "^3.9.1", - "uglify-es": "^3.1.3" + "gulp": "^3.9.1" }, "homepage": "https://github.com/snyderp/web-api-manager", "bugs": {