From a4ce5ec6afc1f9d17bdcb792f61ded3313a8d5b4 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 19 Oct 2016 10:16:50 -0400 Subject: [PATCH] for now, make webext version based on chromium implementation as much as possible --- .../webext/img/browsericons/icon19-off.png | Bin 954 -> 0 bytes platform/webext/img/browsericons/icon19.png | Bin 995 -> 0 bytes .../webext/img/browsericons/icon38-off.png | Bin 1726 -> 0 bytes platform/webext/img/browsericons/icon38.png | Bin 1841 -> 0 bytes platform/webext/is-webrtc-supported.html | 8 - platform/webext/is-webrtc-supported.js | 52 - platform/webext/managed_storage.json | 11 - platform/webext/options_ui.html | 11 - platform/webext/options_ui.js | 47 - platform/webext/vapi-background.js | 1424 ----------------- platform/webext/vapi-client.js | 525 ------ platform/webext/vapi-common.js | 101 -- 12 files changed, 2179 deletions(-) delete mode 100644 platform/webext/img/browsericons/icon19-off.png delete mode 100644 platform/webext/img/browsericons/icon19.png delete mode 100644 platform/webext/img/browsericons/icon38-off.png delete mode 100644 platform/webext/img/browsericons/icon38.png delete mode 100644 platform/webext/is-webrtc-supported.html delete mode 100644 platform/webext/is-webrtc-supported.js delete mode 100644 platform/webext/managed_storage.json delete mode 100644 platform/webext/options_ui.html delete mode 100644 platform/webext/options_ui.js delete mode 100644 platform/webext/vapi-background.js delete mode 100644 platform/webext/vapi-client.js delete mode 100644 platform/webext/vapi-common.js diff --git a/platform/webext/img/browsericons/icon19-off.png b/platform/webext/img/browsericons/icon19-off.png deleted file mode 100644 index 79fb3b823faa18dbaf2b264651ecaf32d9f872cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 954 zcmV;r14aCaP)-8BDqt~2zdhrOi2qlmqLy-Zt7YQS`dOl zi4-aUx2r=PI(A40n?Jxp9Xg4F#i4_PLDaFB8&tyeHgAY{$!Xq-&6#xR9jD&)$FsaV z@AG^=@ArLpd6~({Nd(2p%F4r&latq$Wjzx`@rxu$AC{Juz6bR)ez0=6oH{-}Rt&>< z(Q35@o&CPPzUJ`o@aNId(Xyf_x-V>DVd3M&#l<54gWYadq?8g$DIerO5QHCsAk-O# zu>dp&1_rd%)zw!V0GOuv zwx@vJ-d?vc6bgakH~?_hyW!}$-$~mkjEsz6c6Jt}QV9SsGc$uurvp(GK?s4Ws@U7x zL!;5~!rESEf!7R&!;mBinx_4T#)%%U1VNfP$=_ff4@Z;c`zj~}|QbUIy8RrTuf@-hMdwOS3eTJ4r#YirAQrv3f> zSLt-R;?As4C^QHm+rHg@2|~zrp-^bJFaXHqa`TBq;^2QkiA3Tcm&?sNxeF^6i_L5{ znmm))LZlY z&j4U~dHJcX>+kCIy6kcDEtN{WEtN`N{WO6v09apN5AW{ozBxNPdl`$xK8=r$zt89M cKZE*z0ZZKN!SC2YssI2007*qoM6N<$f?W@!EC2ui diff --git a/platform/webext/img/browsericons/icon19.png b/platform/webext/img/browsericons/icon19.png deleted file mode 100644 index e6d57bdda9a31de7e0b1663691bfa6e4831c49a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 995 zcmV<9104K`P)CylGzOO%@tMMOH{+5h79-(uE)4lqlh(sOa1QxB0P+xy^Nv=Q!m| z^tpS_|Ns2n=l{MhhtO)Zh^5zh{jEP^V~=;&*DDj!)5o$ZD=klkhyTXvC%9nU27~hV z=xFQK>}=hE)0xP8kC(|jH;ao0vvs=eR;zV|6V@aazuVi`$V4QP7y%NzPN&pM)1p`j z#8RnGER)d$unU6lTu)1*-g>WEXWknOH0wZw#GKz8ybRc za1c#1GkCVMgS&lwNKz`#!h$ETbH=<9ACGLE4%Ea1KGxJAB_{`WdV5d9j{W@TnYomh zkpcJWD!#R~0RUVSg_flyBxh#AMNx?MlgAzpV-EU!AO!+|AOHZ7qa*gr06-*>Abfa; zz}{Z;o*p)tNFrGTPz3;6^YaM#{kYxUjsu4S;*=B!L?YCTk7H|Y4h4;k_+_)fOViOC zEFuv2pG>A!FEE8jIE+sX4Y-k?k6gVT-#a?+rmzqP4hOQTsxaNvg)c2Fr;dX06I!hn z*3QnP57pJ1`sk0Hk;gFHoHjxFqqA5sY*3@Sr7n7RjNsY+3W^RE&v*n zsa~$p{J0EAuF?F^m`wG|nuRr6ES@5xv0AE9*)N#LUSu>@H(M<1OW-LK3btXwZr^`U zTl+ca^Cf$o&fKF=h&&773x(mdk`ilPU0u`Dk&(^l8t4BE0G_MW6)RIyujr+v0ya%< z?oVZT`HQZFg|A$mQ^}$=y}eDn92j`-ce#QDkEh5eE91lY>-@hDd^nZjc_pY@| RvpN6(002ovPDHLkV1jZ&xE%lh diff --git a/platform/webext/img/browsericons/icon38-off.png b/platform/webext/img/browsericons/icon38-off.png deleted file mode 100644 index a1b5c00bf32b759fa2b5505e4b569ea1bb3b08c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1726 zcmV;v20{6WP)U|WLpzT zO*fZzA2hR*W}<0h>;7S2<~!&6&Tqasw>iYVefvUf!4=Oy~@hUg2#^^zYGA1tmD(CPd~eJ=g!9~YPATZ zQYk!q_;53T&4ub%ES5h2Tm}GH)<_UUX0@P-m6$;$vVU+eh!_MgnM{`cFf9K`%<%uu z7(oypJY!;pqQJ_^%0SaJ)~{cmSEf`dLDMv7n$EGS)#{SK6e8n7A%{JC_Mok;4e@v! zJv}`+W!tuGLwkEW0KnyPVgLU9Fc=J=C<@7B5|Ky*BO@bt_Uu_PU;=}%GMNmoUcG|H<3ThUg+`-6b8|Cjn#S(k zyD>F21+Ujz&|l0T1}nMq$K!GI_xEFNZceayJRTf9dK7xS9yXf|JkKMUOcnym@BAyB z$I8pgh5MT~Z{|P&Kr)%c@bEAIK&4Vaub146(;GG1O9O8|gEp}@wC8*|DwZQ3N<@7=o> zEX(GUn@lFyY&HQZ8jVV}L6RgYD=R0n&%&tG>-Bdv8cp)Wix8^lPgwv;8FCF&U0pp_S64S6w&j31olYOaFrKx= z0vLwzIGs+P*d|+AslLActWKwktre6`r;F9s*PmT#&jai1?7VHUSo#aiY41}cNy1{W z^mlf4-hL+-0I)23%4)UV%FfoSA%Y-awOVhnEPG0-ToR1qxJ+wntHWlq4XsAzNRouj zW*cg4ZFO)QC;1a33HAmq(bCd#$Zog0)M|CfyW6bPYIVwPx4T+eS`Kj>S8zoy>QLqU z`Sab69zFVUe0zbULWTfhB*|NoNHkw2xMlz7lyuZNI5=3t^ZfVs@8AC< z9*OV07*qoM6N<$g4QV?KL7v# diff --git a/platform/webext/img/browsericons/icon38.png b/platform/webext/img/browsericons/icon38.png deleted file mode 100644 index 09a57d2ffb6e32604912d9aa260487a9e0cc204b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1841 zcmV-12hRA3P)CBp zmo%4@9Qi#zKVq=E+X4Wrot$0dgMl%k*E94lUI=x4eNG0g)=Agj@6=LS z>SEAp&AO3^;s^xr3=i+%+1PY*1cGj!t!+2g%1XoHaz|Ml&M1q^9kmn+JHO1%J_7*k zvA}*&7#*Dv7&T+B=mJ)zq##+XhPz*M+DI)EYCZBTuDWH8t>9w+=j8TL1vp+8VAQA=rN7 z2G-~1nnCu4f%+(EGiI)sY&M3TKS%BH<9OE81Y37^1SBLtC>CQyQWBmvG@!n)ke-Xe zU{;NptAX}*@W^F7)Yj*%ox|&y4_A9d1fkTxW6BzNl7pc4})!KiA5_{Lb7QS3Ds3ogHyr5 z!P8wVDnP_(#8rM2%D3viW6Yhx6O7OIUo(P?pL=wSzc zf6e=Xa`5-pEDjAlJkdT0EJ3L(Tdq*#)Ah7>DGr~HK%bRxkkYlgkfCGc#{7 zSgbjNVz5|{W@g^vM8W1|=s)69l27^E2zC%6D;biGN+Xl+s*M z;yD{Auc)Y&rP0yZ$%Ta{-sZ~^Q_fR#`nA6P-ZupWMS_J37YuZEdg^+6Ev5rxCy@-U zPD?u7^6Bhj zR>r8ha_3Il5tZskYmrE&*J?dAYPHampWE8C?ZIhjN2M7V@tfo0)uw!=(yTsps`By{ zHL6rc8&048bfBYy2LSV3Ty(xWcmC`h6O$gJP;|X*jX41u(}_KMA|777dJq7XY}|Mx fF*o - - - - - - - diff --git a/platform/webext/is-webrtc-supported.js b/platform/webext/is-webrtc-supported.js deleted file mode 100644 index 5a3eea1e7..000000000 --- a/platform/webext/is-webrtc-supported.js +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - - uBlock Origin - a browser extension to block requests. - Copyright (C) 2015 Raymond Hill - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see {http://www.gnu.org/licenses/}. - - Home: https://github.com/gorhill/uBlock -*/ - -// https://github.com/gorhill/uBlock/issues/533#issuecomment-164292868 -// If WebRTC is supported, there won't be an exception if we -// try to instanciate a peer connection object. - -// https://github.com/gorhill/uBlock/issues/533#issuecomment-168097594 -// Because Chromium leaks WebRTC connections after they have been closed -// and forgotten, we need to test for WebRTC support inside an iframe, this -// way the closed and forgottetn WebRTC connections are properly garbage -// collected. - -(function() { - 'use strict'; - - var pc = null; - try { - var PC = self.RTCPeerConnection || self.webkitRTCPeerConnection; - if ( PC ) { - pc = new PC(null); - } - } catch (ex) { - console.error(ex); - } - if ( pc !== null ) { - pc.close(); - } - - window.top.postMessage( - pc !== null ? 'webRTCSupported' : 'webRTCNotSupported', - window.location.origin - ); -})(); diff --git a/platform/webext/managed_storage.json b/platform/webext/managed_storage.json deleted file mode 100644 index d3f2c864f..000000000 --- a/platform/webext/managed_storage.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-03/schema#", - "type": "object", - "properties": { - "adminSettings": { - "title": "A valid JSON string compliant with uBO's backup format.", - "description": "All entries present will overwrite local settings.", - "type": "string" - } - } -} diff --git a/platform/webext/options_ui.html b/platform/webext/options_ui.html deleted file mode 100644 index d94c001ef..000000000 --- a/platform/webext/options_ui.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - -Dashboard - - diff --git a/platform/webext/options_ui.js b/platform/webext/options_ui.js deleted file mode 100644 index 1e7f552b1..000000000 --- a/platform/webext/options_ui.js +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************* - - µBlock - a browser extension to block requests. - Copyright (C) 2015 The µBlock authors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see {http://www.gnu.org/licenses/}. - - Home: https://github.com/gorhill/uBlock -*/ - -/******************************************************************************/ - -(function() { - -/******************************************************************************/ - -'use strict'; - -vAPI.messaging.send( - 'default', - { - what: 'gotoURL', - details: { - url: 'dashboard.html', - select: true, - index: -1 - } - } -); -window.close(); - -/******************************************************************************/ - -})(); - -/******************************************************************************/ diff --git a/platform/webext/vapi-background.js b/platform/webext/vapi-background.js deleted file mode 100644 index 5e95622d6..000000000 --- a/platform/webext/vapi-background.js +++ /dev/null @@ -1,1424 +0,0 @@ -/******************************************************************************* - - uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2016 The uBlock Origin authors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see {http://www.gnu.org/licenses/}. - - Home: https://github.com/gorhill/uBlock -*/ - -// For background page - -/******************************************************************************/ - -(function() { - -'use strict'; - -/******************************************************************************/ - -var vAPI = self.vAPI = self.vAPI || {}; - -var chrome = self.chrome; -var manifest = chrome.runtime.getManifest(); - -vAPI.chrome = true; -vAPI.cantWebsocket = true; - -var noopFunc = function(){}; - -/******************************************************************************/ - -vAPI.app = { - name: manifest.name, - version: manifest.version -}; - -/******************************************************************************/ - -vAPI.app.restart = function() { - chrome.runtime.reload(); -}; - -/******************************************************************************/ -/******************************************************************************/ - -// chrome.storage.local.get(null, function(bin){ console.debug('%o', bin); }); - -vAPI.storage = chrome.storage.local; - -/******************************************************************************/ -/******************************************************************************/ - -// https://github.com/gorhill/uMatrix/issues/234 -// https://developer.chrome.com/extensions/privacy#property-network - -// 2015-08-12: Wrapped Chrome API in try-catch statements. I had a fluke -// event in which it appeared Chrome 46 decided to restart uBlock (for -// unknown reasons) and again for unknown reasons the browser acted as if -// uBlock did not declare the `privacy` permission in its manifest, putting -// uBlock in a bad, non-functional state -- because call to `chrome.privacy` -// API threw an exception. - -// https://github.com/gorhill/uBlock/issues/2048 -// Do not mess up with existing settings if not assigning them stricter -// values. - -vAPI.browserSettings = { - webRTCSupported: undefined, - - // https://github.com/gorhill/uBlock/issues/875 - // Must not leave `lastError` unchecked. - noopCallback: function() { - void chrome.runtime.lastError; - }, - - // Calling with `true` means IP address leak is not prevented. - // https://github.com/gorhill/uBlock/issues/533 - // We must first check wether this Chromium-based browser was compiled - // with WebRTC support. To do this, we use an iframe, this way the - // empty RTCPeerConnection object we create to test for support will - // be properly garbage collected. This prevents issues such as - // a computer unable to enter into sleep mode, as reported in the - // Chrome store: - // https://github.com/gorhill/uBlock/issues/533#issuecomment-167931681 - setWebrtcIPAddress: function(setting) { - // We don't know yet whether this browser supports WebRTC: find out. - if ( this.webRTCSupported === undefined ) { - this.webRTCSupported = { setting: setting }; - var iframe = document.createElement('iframe'); - var me = this; - var messageHandler = function(ev) { - if ( ev.origin !== self.location.origin ) { - return; - } - window.removeEventListener('message', messageHandler); - var setting = me.webRTCSupported.setting; - me.webRTCSupported = ev.data === 'webRTCSupported'; - me.setWebrtcIPAddress(setting); - iframe.parentNode.removeChild(iframe); - iframe = null; - }; - window.addEventListener('message', messageHandler); - iframe.src = 'is-webrtc-supported.html'; - document.body.appendChild(iframe); - return; - } - - // We are waiting for a response from our iframe. This makes the code - // safe to re-entrancy. - if ( typeof this.webRTCSupported === 'object' ) { - this.webRTCSupported.setting = setting; - return; - } - - // https://github.com/gorhill/uBlock/issues/533 - // WebRTC not supported: `webRTCMultipleRoutesEnabled` can NOT be - // safely accessed. Accessing the property will cause full browser - // crash. - if ( this.webRTCSupported !== true ) { - return; - } - - var cp = chrome.privacy, - cpn = cp.network; - - // Older version of Chromium do not support this setting, and is - // marked as "deprecated" since Chromium 48. - if ( typeof cpn.webRTCMultipleRoutesEnabled === 'object' ) { - try { - if ( setting ) { - cpn.webRTCMultipleRoutesEnabled.clear({ - scope: 'regular' - }, this.noopCallback); - } else { - cpn.webRTCMultipleRoutesEnabled.set({ - value: false, - scope: 'regular' - }, this.noopCallback); - } - } catch(ex) { - console.error(ex); - } - } - - // This setting became available in Chromium 48. - if ( typeof cpn.webRTCIPHandlingPolicy === 'object' ) { - try { - if ( setting ) { - cpn.webRTCIPHandlingPolicy.clear({ - scope: 'regular' - }, this.noopCallback); - } else { - // Respect current stricter setting if any. - cpn.webRTCIPHandlingPolicy.get({}, function(details) { - var value = details.value === 'disable_non_proxied_udp' ? - 'disable_non_proxied_udp' : - 'default_public_interface_only'; - cpn.webRTCIPHandlingPolicy.set({ - value: value, - scope: 'regular' - }, this.noopCallback); - }.bind(this)); - } - } catch(ex) { - console.error(ex); - } - } - }, - - set: function(details) { - for ( var setting in details ) { - if ( details.hasOwnProperty(setting) === false ) { - continue; - } - switch ( setting ) { - case 'prefetching': - try { - if ( !!details[setting] ) { - chrome.privacy.network.networkPredictionEnabled.clear({ - scope: 'regular' - }, this.noopCallback); - } else { - chrome.privacy.network.networkPredictionEnabled.set({ - value: false, - scope: 'regular' - }, this.noopCallback); - } - } catch(ex) { - console.error(ex); - } - break; - - case 'hyperlinkAuditing': - try { - if ( !!details[setting] ) { - chrome.privacy.websites.hyperlinkAuditingEnabled.clear({ - scope: 'regular' - }, this.noopCallback); - } else { - chrome.privacy.websites.hyperlinkAuditingEnabled.set({ - value: false, - scope: 'regular' - }, this.noopCallback); - } - } catch(ex) { - console.error(ex); - } - break; - - case 'webrtcIPAddress': - this.setWebrtcIPAddress(!!details[setting]); - break; - - default: - break; - } - } - } -}; - -/******************************************************************************/ -/******************************************************************************/ - -vAPI.tabs = {}; - -/******************************************************************************/ - -vAPI.isBehindTheSceneTabId = function(tabId) { - return tabId.toString() === '-1'; -}; - -vAPI.noTabId = '-1'; - -/******************************************************************************/ - -var toChromiumTabId = function(tabId) { - if ( typeof tabId === 'string' ) { - tabId = parseInt(tabId, 10); - } - if ( typeof tabId !== 'number' || isNaN(tabId) || tabId === -1 ) { - return 0; - } - return tabId; -}; - -/******************************************************************************/ - -vAPI.tabs.registerListeners = function() { - var onNavigationClient = this.onNavigation || noopFunc; - var onUpdatedClient = this.onUpdated || noopFunc; - - // https://developer.chrome.com/extensions/webNavigation - // [onCreatedNavigationTarget ->] - // onBeforeNavigate -> - // onCommitted -> - // onDOMContentLoaded -> - // onCompleted - - // The chrome.webRequest.onBeforeRequest() won't be called for everything - // else than `http`/`https`. Thus, in such case, we will bind the tab as - // early as possible in order to increase the likelihood of a context - // properly setup if network requests are fired from within the tab. - // Example: Chromium + case #6 at - // http://raymondhill.net/ublock/popup.html - var reGoodForWebRequestAPI = /^https?:\/\//; - - // https://forums.lanik.us/viewtopic.php?f=62&t=32826 - // Chromium-based browsers: sanitize target URL. I've seen data: URI with - // newline characters in standard fields, possibly as a way of evading - // filters. As per spec, there should be no whitespaces in a data: URI's - // standard fields. - var sanitizeURL = function(url) { - if ( url.startsWith('data:') === false ) { return url; } - var pos = url.indexOf(','); - if ( pos === -1 ) { return url; } - var s = url.slice(0, pos); - if ( s.search(/\s/) === -1 ) { return url; } - return s.replace(/\s+/, '') + url.slice(pos); - }; - - var onCreatedNavigationTarget = function(details) { - //console.debug('onCreatedNavigationTarget: popup candidate tab id %d = "%s"', details.tabId, details.url); - if ( reGoodForWebRequestAPI.test(details.url) === false ) { - details.frameId = 0; - details.url = sanitizeURL(details.url); - onNavigationClient(details); - } - if ( typeof vAPI.tabs.onPopupCreated === 'function' ) { - vAPI.tabs.onPopupCreated(details.tabId.toString(), details.sourceTabId.toString()); - } - }; - - var onBeforeNavigate = function(details) { - if ( details.frameId !== 0 ) { - return; - } - }; - - var onCommitted = function(details) { - if ( details.frameId !== 0 ) { - return; - } - details.url = sanitizeURL(details.url); - onNavigationClient(details); - }; - - var onActivated = function(details) { - vAPI.contextMenu.onMustUpdate(details.tabId); - }; - - var onUpdated = function(tabId, changeInfo, tab) { - if ( changeInfo.url ) { - changeInfo.url = sanitizeURL(changeInfo.url); - } - onUpdatedClient(tabId, changeInfo, tab); - }; - - chrome.webNavigation.onBeforeNavigate.addListener(onBeforeNavigate); - chrome.webNavigation.onCommitted.addListener(onCommitted); - chrome.webNavigation.onCreatedNavigationTarget.addListener(onCreatedNavigationTarget); - chrome.tabs.onActivated.addListener(onActivated); - chrome.tabs.onUpdated.addListener(onUpdated); - - if ( typeof this.onClosed === 'function' ) { - chrome.tabs.onRemoved.addListener(this.onClosed); - } - -}; - -/******************************************************************************/ - -vAPI.tabs.get = function(tabId, callback) { - var onTabReady = function(tab) { - // https://code.google.com/p/chromium/issues/detail?id=410868#c8 - if ( chrome.runtime.lastError ) { - /* noop */ - } - // Caller must be prepared to deal with nil tab value - callback(tab); - }; - - if ( tabId !== null ) { - tabId = toChromiumTabId(tabId); - if ( tabId === 0 ) { - onTabReady(null); - } else { - chrome.tabs.get(tabId, onTabReady); - } - return; - } - - var onTabReceived = function(tabs) { - // https://code.google.com/p/chromium/issues/detail?id=410868#c8 - void chrome.runtime.lastError; - callback(tabs[0]); - }; - chrome.tabs.query({ active: true, currentWindow: true }, onTabReceived); -}; - -/******************************************************************************/ - -// properties of the details object: -// url: 'URL', // the address that will be opened -// tabId: 1, // the tab is used if set, instead of creating a new one -// index: -1, // undefined: end of the list, -1: following tab, or after index -// active: false, // opens the tab in background - true and undefined: foreground -// select: true, // if a tab is already opened with that url, then select it instead of opening a new one -// popup: true // open in a new window - -vAPI.tabs.open = function(details) { - var targetURL = details.url; - if ( typeof targetURL !== 'string' || targetURL === '' ) { - return null; - } - - // extension pages - if ( /^[\w-]{2,}:/.test(targetURL) !== true ) { - targetURL = vAPI.getURL(targetURL); - } - - // dealing with Chrome's asynchronous API - var wrapper = function() { - if ( details.active === undefined ) { - details.active = true; - } - - var subWrapper = function() { - var _details = { - url: targetURL, - active: !!details.active - }; - - // Opening a tab from incognito window won't focus the window - // in which the tab was opened - var focusWindow = function(tab) { - if ( tab.active ) { - chrome.windows.update(tab.windowId, { focused: true }); - } - }; - - if ( !details.tabId ) { - if ( details.index !== undefined ) { - _details.index = details.index; - } - - chrome.tabs.create(_details, focusWindow); - return; - } - - // update doesn't accept index, must use move - chrome.tabs.update(toChromiumTabId(details.tabId), _details, function(tab) { - // if the tab doesn't exist - if ( vAPI.lastError() ) { - chrome.tabs.create(_details, focusWindow); - } else if ( details.index !== undefined ) { - chrome.tabs.move(tab.id, {index: details.index}); - } - }); - }; - - // Open in a standalone window - if ( details.popup === true ) { - chrome.windows.create({ - url: details.url, - focused: details.active, - type: 'popup' - }); - return; - } - - if ( details.index !== -1 ) { - subWrapper(); - return; - } - - vAPI.tabs.get(null, function(tab) { - if ( tab ) { - details.index = tab.index + 1; - } else { - delete details.index; - } - - subWrapper(); - }); - }; - - if ( !details.select ) { - wrapper(); - return; - } - - // https://developer.chrome.com/extensions/tabs#method-query - // "Note that fragment identifiers are not matched." - // It's a lie, fragment identifiers ARE matched. So we need to remove the - // fragment. - var pos = targetURL.indexOf('#'); - var targetURLWithoutHash = pos === -1 ? targetURL : targetURL.slice(0, pos); - - chrome.tabs.query({ url: targetURLWithoutHash }, function(tabs) { - var tab = tabs[0]; - if ( !tab ) { - wrapper(); - return; - } - - var _details = { - active: true, - url: undefined - }; - if ( targetURL !== tab.url ) { - _details.url = targetURL; - } - chrome.tabs.update(tab.id, _details, function(tab) { - chrome.windows.update(tab.windowId, { focused: true }); - }); - }); -}; - -/******************************************************************************/ - -// Replace the URL of a tab. Noop if the tab does not exist. - -vAPI.tabs.replace = function(tabId, url) { - tabId = toChromiumTabId(tabId); - if ( tabId === 0 ) { - return; - } - - var targetURL = url; - - // extension pages - if ( /^[\w-]{2,}:/.test(targetURL) !== true ) { - targetURL = vAPI.getURL(targetURL); - } - - chrome.tabs.update(tabId, { url: targetURL }, function() { - // https://code.google.com/p/chromium/issues/detail?id=410868#c8 - if ( chrome.runtime.lastError ) { - /* noop */ - } - }); -}; - -/******************************************************************************/ - -vAPI.tabs.remove = function(tabId) { - tabId = toChromiumTabId(tabId); - if ( tabId === 0 ) { - return; - } - - var onTabRemoved = function() { - // https://code.google.com/p/chromium/issues/detail?id=410868#c8 - if ( chrome.runtime.lastError ) { - /* noop */ - } - }; - - chrome.tabs.remove(tabId, onTabRemoved); -}; - -/******************************************************************************/ - -vAPI.tabs.reload = function(tabId /*, flags*/) { - tabId = toChromiumTabId(tabId); - if ( tabId === 0 ) { - return; - } - - var onReloaded = function() { - // https://code.google.com/p/chromium/issues/detail?id=410868#c8 - if ( chrome.runtime.lastError ) { - /* noop */ - } - }; - - chrome.tabs.reload(tabId, onReloaded); -}; - -/******************************************************************************/ - -// Select a specific tab. - -vAPI.tabs.select = function(tabId) { - tabId = toChromiumTabId(tabId); - if ( tabId === 0 ) { - return; - } - - chrome.tabs.update(tabId, { active: true }, function(tab) { - if ( chrome.runtime.lastError ) { - /* noop */ - } - if ( !tab ) { - return; - } - chrome.windows.update(tab.windowId, { focused: true }); - }); -}; - -/******************************************************************************/ - -vAPI.tabs.injectScript = function(tabId, details, callback) { - var onScriptExecuted = function() { - // https://code.google.com/p/chromium/issues/detail?id=410868#c8 - if ( chrome.runtime.lastError ) { - /* noop */ - } - if ( typeof callback === 'function' ) { - callback(); - } - }; - if ( tabId ) { - chrome.tabs.executeScript(toChromiumTabId(tabId), details, onScriptExecuted); - } else { - chrome.tabs.executeScript(details, onScriptExecuted); - } -}; - -/******************************************************************************/ -/******************************************************************************/ - -// Must read: https://code.google.com/p/chromium/issues/detail?id=410868#c8 - -// https://github.com/chrisaljoudi/uBlock/issues/19 -// https://github.com/chrisaljoudi/uBlock/issues/207 -// Since we may be called asynchronously, the tab id may not exist -// anymore, so this ensures it does still exist. - -vAPI.setIcon = function(tabId, iconStatus, badge) { - tabId = toChromiumTabId(tabId); - if ( tabId === 0 ) { - return; - } - - var onIconReady = function() { - if ( vAPI.lastError() ) { - return; - } - chrome.browserAction.setBadgeText({ tabId: tabId, text: badge }); - if ( badge !== '' ) { - chrome.browserAction.setBadgeBackgroundColor({ - tabId: tabId, - color: '#666' - }); - } - }; - - var iconPaths = iconStatus === 'on' ? - { '19': 'img/browsericons/icon19.png', '38': 'img/browsericons/icon38.png' } : - { '19': 'img/browsericons/icon19-off.png', '38': 'img/browsericons/icon38-off.png' }; - - chrome.browserAction.setIcon({ tabId: tabId, path: iconPaths }, onIconReady); - vAPI.contextMenu.onMustUpdate(tabId); -}; - -/******************************************************************************/ -/******************************************************************************/ - -vAPI.messaging = { - ports: {}, - listeners: {}, - defaultHandler: null, - NOOPFUNC: noopFunc, - UNHANDLED: 'vAPI.messaging.notHandled' -}; - -/******************************************************************************/ - -vAPI.messaging.listen = function(listenerName, callback) { - this.listeners[listenerName] = callback; -}; - -/******************************************************************************/ - -vAPI.messaging.onPortMessage = (function() { - var messaging = vAPI.messaging; - var toAuxPending = {}; - - // Use a wrapper to avoid closure and to allow reuse. - var CallbackWrapper = function(port, request, timeout) { - this.callback = this.proxy.bind(this); // bind once - this.init(port, request, timeout); - }; - - CallbackWrapper.prototype.init = function(port, request, timeout) { - this.port = port; - this.request = request; - this.timerId = timeout !== undefined ? - vAPI.setTimeout(this.callback, timeout) : - null; - return this; - }; - - CallbackWrapper.prototype.proxy = function(response) { - if ( this.timerId !== null ) { - clearTimeout(this.timerId); - delete toAuxPending[this.timerId]; - this.timerId = null; - } - // https://github.com/chrisaljoudi/uBlock/issues/383 - if ( messaging.ports.hasOwnProperty(this.port.name) ) { - this.port.postMessage({ - auxProcessId: this.request.auxProcessId, - channelName: this.request.channelName, - msg: response !== undefined ? response : null - }); - } - // Mark for reuse - this.port = this.request = null; - callbackWrapperJunkyard.push(this); - }; - - var callbackWrapperJunkyard = []; - - var callbackWrapperFactory = function(port, request, timeout) { - var wrapper = callbackWrapperJunkyard.pop(); - if ( wrapper ) { - return wrapper.init(port, request, timeout); - } - return new CallbackWrapper(port, request, timeout); - }; - - var toAux = function(details, portFrom) { - var port, portTo; - var chromiumTabId = toChromiumTabId(details.toTabId); - - // TODO: This could be an issue with a lot of tabs: easy to address - // with a port name to tab id map. - for ( var portName in messaging.ports ) { - if ( messaging.ports.hasOwnProperty(portName) === false ) { - continue; - } - // When sending to an auxiliary process, the target is always the - // port associated with the root frame. - port = messaging.ports[portName]; - if ( port.sender.frameId === 0 && port.sender.tab.id === chromiumTabId ) { - portTo = port; - break; - } - } - - var wrapper; - if ( details.auxProcessId !== undefined ) { - wrapper = callbackWrapperFactory(portFrom, details, 1023); - } - - // Destination not found: - if ( portTo === undefined ) { - if ( wrapper !== undefined ) { - wrapper.callback(); - } - return; - } - - // As per HTML5, timer id is always an integer, thus suitable to be - // used as a key, and which value is safe to use across process - // boundaries. - if ( wrapper !== undefined ) { - toAuxPending[wrapper.timerId] = wrapper; - } - - portTo.postMessage({ - mainProcessId: wrapper && wrapper.timerId, - channelName: details.toChannel, - msg: details.msg - }); - }; - - var toAuxResponse = function(details) { - var mainProcessId = details.mainProcessId; - if ( mainProcessId === undefined ) { - return; - } - if ( toAuxPending.hasOwnProperty(mainProcessId) === false ) { - return; - } - var wrapper = toAuxPending[mainProcessId]; - delete toAuxPending[mainProcessId]; - wrapper.callback(details.msg); - }; - - return function(request, port) { - // Auxiliary process to auxiliary process - if ( request.toTabId !== undefined ) { - toAux(request, port); - return; - } - - // Auxiliary process to auxiliary process: response - if ( request.mainProcessId !== undefined ) { - toAuxResponse(request); - return; - } - - // Auxiliary process to main process: prepare response - var callback = messaging.NOOPFUNC; - if ( request.auxProcessId !== undefined ) { - callback = callbackWrapperFactory(port, request).callback; - } - - // Auxiliary process to main process: specific handler - var r = messaging.UNHANDLED; - var listener = messaging.listeners[request.channelName]; - if ( typeof listener === 'function' ) { - r = listener(request.msg, port.sender, callback); - } - if ( r !== messaging.UNHANDLED ) { - return; - } - - // Auxiliary process to main process: default handler - r = messaging.defaultHandler(request.msg, port.sender, callback); - if ( r !== messaging.UNHANDLED ) { - return; - } - - // Auxiliary process to main process: no handler - console.error('uBlock> messaging > unknown request: %o', request); - - // Need to callback anyways in case caller expected an answer, or - // else there is a memory leak on caller's side - callback(); - }; -})(); - -/******************************************************************************/ - -vAPI.messaging.onPortDisconnect = function(port) { - port.onDisconnect.removeListener(vAPI.messaging.onPortDisconnect); - port.onMessage.removeListener(vAPI.messaging.onPortMessage); - delete vAPI.messaging.ports[port.name]; -}; - -/******************************************************************************/ - -vAPI.messaging.onPortConnect = function(port) { - port.onDisconnect.addListener(vAPI.messaging.onPortDisconnect); - port.onMessage.addListener(vAPI.messaging.onPortMessage); - vAPI.messaging.ports[port.name] = port; -}; - -/******************************************************************************/ - -vAPI.messaging.setup = function(defaultHandler) { - // Already setup? - if ( this.defaultHandler !== null ) { - return; - } - - if ( typeof defaultHandler !== 'function' ) { - defaultHandler = function(){ return vAPI.messaging.UNHANDLED; }; - } - this.defaultHandler = defaultHandler; - - chrome.runtime.onConnect.addListener(this.onPortConnect); -}; - -/******************************************************************************/ - -vAPI.messaging.broadcast = function(message) { - var messageWrapper = { - broadcast: true, - msg: message - }; - - for ( var portName in this.ports ) { - if ( this.ports.hasOwnProperty(portName) === false ) { - continue; - } - this.ports[portName].postMessage(messageWrapper); - } -}; - -/******************************************************************************/ -/******************************************************************************/ - -vAPI.net = {}; - -/******************************************************************************/ - -vAPI.net.registerListeners = function() { - var µb = µBlock; - var µburi = µb.URI; - - // https://bugs.chromium.org/p/chromium/issues/detail?id=410382 - // Between Chromium 38-48, plug-ins' network requests were reported as - // type "other" instead of "object". - var is_v38_48 = /\bChrom[a-z]+\/(?:3[89]|4[0-8])\.[\d.]+\b/.test(navigator.userAgent), - is_v49_55 = /\bChrom[a-z]+\/(?:49|5[012345])\b/.test(navigator.userAgent); - - // Chromium-based browsers understand only these network request types. - var validTypes = { - 'main_frame': true, - 'sub_frame': true, - 'stylesheet': true, - 'script': true, - 'image': true, - 'object': true, - 'xmlhttprequest': true, - 'other': true - }; - - var denormalizeTypes = function(aa) { - if ( aa.length === 0 ) { - return Object.keys(validTypes); - } - var out = []; - var i = aa.length, - type, - needOther = true; - while ( i-- ) { - type = aa[i]; - if ( validTypes.hasOwnProperty(type) ) { - out.push(type); - } - if ( type === 'other' ) { - needOther = false; - } - } - if ( needOther ) { - out.push('other'); - } - return out; - }; - - var headerValue = function(headers, name) { - var i = headers.length; - while ( i-- ) { - if ( headers[i].name.toLowerCase() === name ) { - return headers[i].value.trim(); - } - } - return ''; - }; - - var normalizeRequestDetails = function(details) { - details.tabId = details.tabId.toString(); - - // https://github.com/gorhill/uBlock/issues/1493 - // Chromium 49+ support a new request type: `ping`, which is fired as - // a result of using `navigator.sendBeacon`. - if ( details.type === 'ping' ) { - details.type = 'beacon'; - return; - } - - // The rest of the function code is to normalize type - if ( details.type !== 'other' ) { - return; - } - - var path = µburi.pathFromURI(details.url); - var pos = path.indexOf('.', path.length - 6); - - // https://github.com/chrisaljoudi/uBlock/issues/862 - // If no transposition possible, transpose to `object` as per - // Chromium bug 410382 (see below) - if ( pos !== -1 ) { - var needle = path.slice(pos) + '.'; - if ( '.eot.ttf.otf.svg.woff.woff2.'.indexOf(needle) !== -1 ) { - details.type = 'font'; - return; - } - - if ( '.mp3.mp4.webm.'.indexOf(needle) !== -1 ) { - details.type = 'media'; - return; - } - - // Still need this because often behind-the-scene requests are wrongly - // categorized as 'other' - if ( '.ico.png.gif.jpg.jpeg.webp.'.indexOf(needle) !== -1 ) { - details.type = 'image'; - return; - } - } - - // Try to extract type from response headers if present. - if ( details.responseHeaders ) { - var contentType = headerValue(details.responseHeaders, 'content-type'); - if ( contentType.startsWith('font/') ) { - details.type = 'font'; - return; - } - if ( contentType.startsWith('image/') ) { - details.type = 'image'; - return; - } - if ( contentType.startsWith('audio/') || contentType.startsWith('video/') ) { - details.type = 'media'; - return; - } - } - - // https://code.google.com/p/chromium/issues/detail?id=410382 - if ( is_v38_48 ) { - details.type = 'object'; - } - }; - - // https://bugs.chromium.org/p/chromium/issues/detail?id=129353 - // https://github.com/gorhill/uBlock/issues/1497 - // Expose websocket-based network requests to uBO's filtering engine, - // logger, etc. - // Counterpart of following block of code is found in "vapi-client.js" -- - // search for "https://github.com/gorhill/uBlock/issues/1497". - var onBeforeWebsocketRequest = function(details) { - details.type = 'websocket'; - var matches = /url=([^&]+)/.exec(details.url); - details.url = decodeURIComponent(matches[1]); - var r = onBeforeRequestClient(details); - // Blocked? - if ( r && r.cancel ) { - return r; - } - // Returning a 1x1 transparent pixel means "not blocked". - return { redirectUrl: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' }; - }; - - var onBeforeRequestClient = this.onBeforeRequest.callback; - var onBeforeRequest = function(details) { - // https://github.com/gorhill/uBlock/issues/1497 - if ( - details.type === 'image' && - details.url.endsWith('ubofix=f41665f3028c7fd10eecf573336216d3') - ) { - return onBeforeWebsocketRequest(details); - } - - normalizeRequestDetails(details); - return onBeforeRequestClient(details); - }; - - // This is needed for Chromium 49-55. - var onBeforeSendHeaders = function(details) { - if ( details.type !== 'ping' || details.method !== 'POST' ) { return; } - var type = headerValue(details.requestHeaders, 'content-type'); - if ( type === '' ) { return; } - if ( type.endsWith('/csp-report') ) { - details.type = 'csp_report'; - return onBeforeRequestClient(details); - } - }; - - var onHeadersReceivedClient = this.onHeadersReceived.callback, - onHeadersReceivedClientTypes = this.onHeadersReceived.types.slice(0), - onHeadersReceivedTypes = denormalizeTypes(onHeadersReceivedClientTypes); - var onHeadersReceived = function(details) { - normalizeRequestDetails(details); - // Hack to work around Chromium API limitations, where requests of - // type `font` are returned as `other`. For example, our normalization - // fail at transposing `other` into `font` for URLs which are outside - // what is expected. At least when headers are received we can check - // for content type `font/*`. Blocking at onHeadersReceived time is - // less worse than not blocking at all. Also, due to Chromium bug, - // `other` always becomes `object` when it can't be normalized into - // something else. Test case for "unfriendly" font URLs: - // https://www.google.com/fonts - if ( details.type === 'font' ) { - var r = onBeforeRequestClient(details); - if ( typeof r === 'object' && r.cancel === true ) { - return { cancel: true }; - } - } - if ( - onHeadersReceivedClientTypes.length !== 0 && - onHeadersReceivedClientTypes.indexOf(details.type) === -1 - ) { - return; - } - return onHeadersReceivedClient(details); - }; - - var installListeners = (function() { - var crapi = chrome.webRequest; - - //listener = function(details) { - // quickProfiler.start('onBeforeRequest'); - // var r = onBeforeRequest(details); - // quickProfiler.stop(); - // return r; - //}; - if ( crapi.onBeforeRequest.hasListener(onBeforeRequest) === false ) { - crapi.onBeforeRequest.addListener( - onBeforeRequest, - { - 'urls': this.onBeforeRequest.urls || [''], - 'types': this.onBeforeRequest.types || undefined - }, - this.onBeforeRequest.extra - ); - } - - // Chromium 48 and lower does not support `ping` type. - // Chromium 56 and higher does support `csp_report` stype. - if ( is_v49_55 && crapi.onBeforeSendHeaders.hasListener(onBeforeSendHeaders) === false ) { - crapi.onBeforeSendHeaders.addListener( - onBeforeSendHeaders, - { - 'urls': [ '' ], - 'types': [ 'ping' ] - }, - [ 'blocking', 'requestHeaders' ] - ); - } - - if ( crapi.onHeadersReceived.hasListener(onHeadersReceived) === false ) { - crapi.onHeadersReceived.addListener( - onHeadersReceived, - { - 'urls': this.onHeadersReceived.urls || [''], - 'types': onHeadersReceivedTypes - }, - this.onHeadersReceived.extra - ); - } - - // https://github.com/gorhill/uBlock/issues/675 - // Experimental: keep polling to be sure our listeners are still installed. - //setTimeout(installListeners, 20000); - }).bind(this); - - installListeners(); -}; - -/******************************************************************************/ -/******************************************************************************/ - -vAPI.contextMenu = { - _callback: null, - _entries: [], - _createEntry: function(entry) { - chrome.contextMenus.create(JSON.parse(JSON.stringify(entry)), function() { - void chrome.runtime.lastError; - }); - }, - onMustUpdate: function() {}, - setEntries: function(entries, callback) { - entries = entries || []; - var n = Math.max(this._entries.length, entries.length), - oldEntryId, newEntry; - for ( var i = 0; i < n; i++ ) { - oldEntryId = this._entries[i]; - newEntry = entries[i]; - if ( oldEntryId && newEntry ) { - if ( newEntry.id !== oldEntryId ) { - chrome.contextMenus.remove(oldEntryId); - this._createEntry(newEntry); - this._entries[i] = newEntry.id; - } - } else if ( oldEntryId && !newEntry ) { - chrome.contextMenus.remove(oldEntryId); - } else if ( !oldEntryId && newEntry ) { - this._createEntry(newEntry); - this._entries[i] = newEntry.id; - } - } - n = this._entries.length = entries.length; - callback = callback || null; - if ( callback === this._callback ) { - return; - } - if ( n !== 0 && callback !== null ) { - chrome.contextMenus.onClicked.addListener(callback); - this._callback = callback; - } else if ( n === 0 && this._callback !== null ) { - chrome.contextMenus.onClicked.removeListener(this._callback); - this._callback = null; - } - } -}; - -/******************************************************************************/ -/******************************************************************************/ - -vAPI.lastError = function() { - return chrome.runtime.lastError; -}; - -/******************************************************************************/ -/******************************************************************************/ - -// This is called only once, when everything has been loaded in memory after -// the extension was launched. It can be used to inject content scripts -// in already opened web pages, to remove whatever nuisance could make it to -// the web pages before uBlock was ready. - -vAPI.onLoadAllCompleted = function() { - // http://code.google.com/p/chromium/issues/detail?id=410868#c11 - // Need to be sure to access `vAPI.lastError()` to prevent - // spurious warnings in the console. - var scriptDone = function() { - vAPI.lastError(); - }; - var scriptStart = function(tabId) { - vAPI.tabs.injectScript(tabId, { - file: 'js/vapi-client.js', - allFrames: true, - runAt: 'document_idle' - }, function(){ }); - vAPI.tabs.injectScript(tabId, { - file: 'js/contentscript.js', - allFrames: true, - runAt: 'document_idle' - }, scriptDone); - }; - var bindToTabs = function(tabs) { - var µb = µBlock; - var i = tabs.length, tab; - while ( i-- ) { - tab = tabs[i]; - µb.tabContextManager.commit(tab.id, tab.url); - µb.bindTabToPageStats(tab.id); - // https://github.com/chrisaljoudi/uBlock/issues/129 - scriptStart(tab.id); - } - }; - - chrome.tabs.query({ url: '' }, bindToTabs); -}; - -/******************************************************************************/ -/******************************************************************************/ - -vAPI.punycodeHostname = function(hostname) { - return hostname; -}; - -vAPI.punycodeURL = function(url) { - return url; -}; - -/******************************************************************************/ -/******************************************************************************/ - -// https://github.com/gorhill/uBlock/issues/531 -// Storage area dedicated to admin settings. Read-only. - -// https://github.com/gorhill/uBlock/commit/43a5ed735b95a575a9339b6e71a1fcb27a99663b#commitcomment-13965030 -// Not all Chromium-based browsers support managed storage. Merely testing or -// exception handling in this case does NOT work: I don't know why. The -// extension on Opera ends up in a non-sensical state, whereas vAPI become -// undefined out of nowhere. So only solution left is to test explicitly for -// Opera. -// https://github.com/gorhill/uBlock/issues/900 -// Also, UC Browser: http://www.upsieutoc.com/image/WXuH - -vAPI.adminStorage = { - getItem: function(key, callback) { - var onRead = function(store) { - var data; - if ( - !chrome.runtime.lastError && - typeof store === 'object' && - store !== null - ) { - data = store[key]; - } - callback(data); - }; - try { - chrome.storage.managed.get(key, onRead); - } catch (ex) { - callback(); - } - } -}; - -/******************************************************************************/ -/******************************************************************************/ - -vAPI.cloud = (function() { - var chunkCountPerFetch = 16; // Must be a power of 2 - - // Mind chrome.storage.sync.MAX_ITEMS (512 at time of writing) - var maxChunkCountPerItem = Math.floor(512 * 0.75) & ~(chunkCountPerFetch - 1); - - // Mind chrome.storage.sync.QUOTA_BYTES_PER_ITEM (8192 at time of writing) - var maxChunkSize = Math.floor(chrome.storage.sync.QUOTA_BYTES_PER_ITEM * 0.75); - - // Mind chrome.storage.sync.QUOTA_BYTES (128 kB at time of writing) - var maxStorageSize = chrome.storage.sync.QUOTA_BYTES; - - var options = { - defaultDeviceName: window.navigator.platform, - deviceName: window.localStorage.getItem('deviceName') || '' - }; - - // This is used to find out a rough count of how many chunks exists: - // We "poll" at specific index in order to get a rough idea of how - // large is the stored string. - // This allows reading a single item with only 2 sync operations -- a - // good thing given chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_MINUTE - // and chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_HOUR. - - var getCoarseChunkCount = function(dataKey, callback) { - var bin = {}; - for ( var i = 0; i < maxChunkCountPerItem; i += 16 ) { - bin[dataKey + i.toString()] = ''; - } - - chrome.storage.sync.get(bin, function(bin) { - if ( chrome.runtime.lastError ) { - callback(0, chrome.runtime.lastError.message); - return; - } - - var chunkCount = 0; - for ( var i = 0; i < maxChunkCountPerItem; i += 16 ) { - if ( bin[dataKey + i.toString()] === '' ) { - break; - } - chunkCount = i + 16; - } - - callback(chunkCount); - }); - }; - - var deleteChunks = function(dataKey, start) { - var keys = []; - - // No point in deleting more than: - // - The max number of chunks per item - // - The max number of chunks per storage limit - var n = Math.min( - maxChunkCountPerItem, - Math.ceil(maxStorageSize / maxChunkSize) - ); - for ( var i = start; i < n; i++ ) { - keys.push(dataKey + i.toString()); - } - chrome.storage.sync.remove(keys); - }; - - var start = function(/* dataKeys */) { - }; - - var push = function(dataKey, data, callback) { - var bin = { - 'source': options.deviceName || options.defaultDeviceName, - 'tstamp': Date.now(), - 'data': data, - 'size': 0 - }; - bin.size = JSON.stringify(bin).length; - var item = JSON.stringify(bin); - - // Chunkify taking into account QUOTA_BYTES_PER_ITEM: - // https://developer.chrome.com/extensions/storage#property-sync - // "The maximum size (in bytes) of each individual item in sync - // "storage, as measured by the JSON stringification of its value - // "plus its key length." - bin = {}; - var chunkCount = Math.ceil(item.length / maxChunkSize); - for ( var i = 0; i < chunkCount; i++ ) { - bin[dataKey + i.toString()] = item.substr(i * maxChunkSize, maxChunkSize); - } - bin[dataKey + i.toString()] = ''; // Sentinel - - chrome.storage.sync.set(bin, function() { - var errorStr; - if ( chrome.runtime.lastError ) { - errorStr = chrome.runtime.lastError.message; - } - callback(errorStr); - - // Remove potentially unused trailing chunks - deleteChunks(dataKey, chunkCount); - }); - }; - - var pull = function(dataKey, callback) { - var assembleChunks = function(bin) { - if ( chrome.runtime.lastError ) { - callback(null, chrome.runtime.lastError.message); - return; - } - - // Assemble chunks into a single string. - var json = [], jsonSlice; - var i = 0; - for (;;) { - jsonSlice = bin[dataKey + i.toString()]; - if ( jsonSlice === '' ) { - break; - } - json.push(jsonSlice); - i += 1; - } - - var entry = null; - try { - entry = JSON.parse(json.join('')); - } catch(ex) { - } - callback(entry); - }; - - var fetchChunks = function(coarseCount, errorStr) { - if ( coarseCount === 0 || typeof errorStr === 'string' ) { - callback(null, errorStr); - return; - } - - var bin = {}; - for ( var i = 0; i < coarseCount; i++ ) { - bin[dataKey + i.toString()] = ''; - } - - chrome.storage.sync.get(bin, assembleChunks); - }; - - getCoarseChunkCount(dataKey, fetchChunks); - }; - - var getOptions = function(callback) { - if ( typeof callback !== 'function' ) { - return; - } - callback(options); - }; - - var setOptions = function(details, callback) { - if ( typeof details !== 'object' || details === null ) { - return; - } - - if ( typeof details.deviceName === 'string' ) { - window.localStorage.setItem('deviceName', details.deviceName); - options.deviceName = details.deviceName; - } - - getOptions(callback); - }; - - return { - start: start, - push: push, - pull: pull, - getOptions: getOptions, - setOptions: setOptions - }; -})(); - -/******************************************************************************/ -/******************************************************************************/ - -})(); - -/******************************************************************************/ diff --git a/platform/webext/vapi-client.js b/platform/webext/vapi-client.js deleted file mode 100644 index 0dd976fff..000000000 --- a/platform/webext/vapi-client.js +++ /dev/null @@ -1,525 +0,0 @@ -/******************************************************************************* - - uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2016 The uBlock Origin authors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see {http://www.gnu.org/licenses/}. - - Home: https://github.com/gorhill/uBlock -*/ - -/* global HTMLDocument, XMLDocument */ - -'use strict'; - -// For non background pages - -/******************************************************************************/ - -(function(self) { - -/******************************************************************************/ -/******************************************************************************/ - -// https://github.com/chrisaljoudi/uBlock/issues/464 -if ( document instanceof HTMLDocument === false ) { - // https://github.com/chrisaljoudi/uBlock/issues/1528 - // A XMLDocument can be a valid HTML document. - if ( - document instanceof XMLDocument === false || - document.createElement('div') instanceof HTMLDivElement === false - ) { - return; - } -} - -// https://github.com/gorhill/uBlock/issues/1124 -// Looks like `contentType` is on track to be standardized: -// https://dom.spec.whatwg.org/#concept-document-content-type -// https://forums.lanik.us/viewtopic.php?f=64&t=31522 -// Skip text/plain documents. -var contentType = document.contentType || ''; -if ( /^image\/|^text\/plain/.test(contentType) ) { - return; -} - -/******************************************************************************/ - -var vAPI = self.vAPI = self.vAPI || {}; -var chrome = self.chrome; - -// https://github.com/chrisaljoudi/uBlock/issues/456 -// Already injected? -if ( vAPI.sessionId ) { - return; -} - -/******************************************************************************/ - -// Support minimally working Set() for legacy Chromium. - -if ( self.Set instanceof Function ) { - self.createSet = function() { - return new Set(); - }; -} else { - self.createSet = (function() { - //console.log('Polyfilling for ES6-like Set().'); - var PrimitiveSet = function() { - this.clear(); - }; - PrimitiveSet.prototype = { - add: function(k) { - if ( this._set[k] === undefined ) { - this._set[k] = true; - this.size += 1; - } - return this; - }, - clear: function() { - this._set = Object.create(null); - this.size = 0; - this._values = undefined; - this._i = undefined; - this.value = undefined; - this.done = true; - }, - delete: function(k) { - if ( this._set[k] === undefined ) { return false; } - delete this._set[k]; - this.size -= 1; - return true; - }, - has: function(k) { - return this._set[k] !== undefined; - }, - next: function() { - if ( this._i < this.size ) { - this.value = this._values[this._i++]; - } else { - this._values = undefined; - this.value = undefined; - this.done = true; - } - return this; - }, - polyfill: true, - values: function() { - this._values = Object.keys(this._set); - this._i = 0; - this.value = undefined; - this.done = false; - return this; - } - }; - var ReferenceSet = function() { - this.clear(); - }; - ReferenceSet.prototype = { - add: function(k) { - if ( this._set.indexOf(k) === -1 ) { - this._set.push(k); - } - }, - clear: function() { - this._set = []; - this._i = 0; - this.value = undefined; - this.done = true; - }, - delete: function(k) { - var pos = this._set.indexOf(k); - if ( pos === -1 ) { return false; } - this._set.splice(pos, 1); - return true; - }, - has: function(k) { - return this._set.indexOf(k) !== -1; - }, - next: function() { - if ( this._i === this._set.length ) { - this.value = undefined; - this.done = true; - } else { - this.value = this._set[this._i]; - this._i += 1; - } - return this; - }, - polyfill: true, - values: function() { - this._i = 0; - this.done = false; - return this; - } - }; - Object.defineProperty(ReferenceSet.prototype, 'size', { - get: function() { return this._set.length; } - }); - return function(type) { - return type === 'object' ? new ReferenceSet() : new PrimitiveSet(); - }; - })(); -} - -/******************************************************************************/ - -var referenceCounter = 0; - -vAPI.lock = function() { - referenceCounter += 1; -}; - -vAPI.unlock = function() { - referenceCounter -= 1; - if ( referenceCounter === 0 ) { - // Eventually there will be code here to flush the javascript code - // from this file out of memory when it ends up unused. - - } -}; - -/******************************************************************************/ - -vAPI.executionCost = { - start: function(){}, - stop: function(){} -}; -/* -vAPI.executionCost = { - tcost: 0, - tstart: 0, - nstart: 0, - level: 1, - start: function() { - if ( this.nstart === 0 ) { - this.tstart = window.performance.now(); - } - this.nstart += 1; - }, - stop: function(mark) { - this.nstart -= 1; - if ( this.nstart !== 0 ) { - return; - } - var tcost = window.performance.now() - this.tstart; - this.tcost += tcost; - if ( mark === undefined ) { - return; - } - var top = window === window.top; - if ( !top && this.level < 2 ) { - return; - } - var context = window === window.top ? ' top' : 'frame'; - var percent = this.tcost / window.performance.now() * 100; - console.log( - 'uBO cost (%s): %sms/%s%% (%s: %sms)', - context, - this.tcost.toFixed(1), - percent.toFixed(1), - mark, - tcost.toFixed(2) - ); - } -}; -*/ -vAPI.executionCost.start(); - -/******************************************************************************/ - -vAPI.randomToken = function() { - return String.fromCharCode(Date.now() % 26 + 97) + - Math.floor(Math.random() * 982451653 + 982451653).toString(36); -}; - -vAPI.sessionId = vAPI.randomToken(); -vAPI.chrome = true; -vAPI.setTimeout = vAPI.setTimeout || self.setTimeout.bind(self); - -/******************************************************************************/ - -vAPI.shutdown = { - jobs: [], - add: function(job) { - this.jobs.push(job); - }, - exec: function() { - var job; - while ( (job = this.jobs.pop()) ) { - job(); - } - }, - remove: function(job) { - var pos; - while ( (pos = this.jobs.indexOf(job)) !== -1 ) { - this.jobs.splice(pos, 1); - } - } -}; - -/******************************************************************************/ -/******************************************************************************/ - -vAPI.messaging = { - port: null, - portTimer: null, - portTimerDelay: 10000, - channels: Object.create(null), - channelCount: 0, - pending: Object.create(null), - pendingCount: 0, - auxProcessId: 1, - shuttingDown: false, - - shutdown: function() { - this.shuttingDown = true; - this.destroyPort(); - }, - - disconnectListener: function() { - this.port = null; - vAPI.shutdown.exec(); - }, - disconnectListenerCallback: null, - - messageListener: function(details) { - if ( !details ) { - return; - } - - // Sent to all channels - if ( details.broadcast === true && !details.channelName ) { - for ( var channelName in this.channels ) { - this.sendToChannelListeners(channelName, details.msg); - } - return; - } - - // Response to specific message previously sent - if ( details.auxProcessId ) { - var listener = this.pending[details.auxProcessId]; - delete this.pending[details.auxProcessId]; - delete details.auxProcessId; // TODO: why? - if ( listener ) { - this.pendingCount -= 1; - listener(details.msg); - return; - } - } - - // Sent to a specific channel - var response = this.sendToChannelListeners(details.channelName, details.msg); - - // Respond back if required - if ( details.mainProcessId === undefined ) { - return; - } - var port = this.connect(); - if ( port !== null ) { - port.postMessage({ - mainProcessId: details.mainProcessId, - msg: response - }); - } - }, - messageListenerCallback: null, - - portPoller: function() { - this.portTimer = null; - if ( this.port !== null ) { - if ( this.channelCount !== 0 || this.pendingCount !== 0 ) { - this.portTimer = vAPI.setTimeout(this.portPollerCallback, this.portTimerDelay); - this.portTimerDelay = Math.min(this.portTimerDelay * 2, 60 * 60 * 1000); - return; - } - } - this.destroyPort(); - }, - portPollerCallback: null, - - destroyPort: function() { - if ( this.portTimer !== null ) { - clearTimeout(this.portTimer); - this.portTimer = null; - } - var port = this.port; - if ( port !== null ) { - port.disconnect(); - port.onMessage.removeListener(this.messageListenerCallback); - port.onDisconnect.removeListener(this.disconnectListenerCallback); - this.port = null; - } - if ( this.channelCount !== 0 ) { - this.channels = Object.create(null); - this.channelCount = 0; - } - // service pending callbacks - if ( this.pendingCount !== 0 ) { - var pending = this.pending, callback; - this.pending = Object.create(null); - this.pendingCount = 0; - for ( var auxId in pending ) { - callback = pending[auxId]; - if ( typeof callback === 'function' ) { - callback(null); - } - } - } - }, - - createPort: function() { - if ( this.shuttingDown ) { - return null; - } - if ( this.messageListenerCallback === null ) { - this.messageListenerCallback = this.messageListener.bind(this); - this.disconnectListenerCallback = this.disconnectListener.bind(this); - this.portPollerCallback = this.portPoller.bind(this); - } - try { - this.port = chrome.runtime.connect({name: vAPI.sessionId}) || null; - } catch (ex) { - this.port = null; - } - if ( this.port !== null ) { - this.port.onMessage.addListener(this.messageListenerCallback); - this.port.onDisconnect.addListener(this.disconnectListenerCallback); - } - this.portTimerDelay = 10000; - if ( this.portTimer === null ) { - this.portTimer = vAPI.setTimeout(this.portPollerCallback, this.portTimerDelay); - } - return this.port; - }, - - connect: function() { - return this.port !== null ? this.port : this.createPort(); - }, - - send: function(channelName, message, callback) { - this.sendTo(channelName, message, undefined, undefined, callback); - }, - - sendTo: function(channelName, message, toTabId, toChannel, callback) { - // Too large a gap between the last request and the last response means - // the main process is no longer reachable: memory leaks and bad - // performance become a risk -- especially for long-lived, dynamic - // pages. Guard against this. - if ( this.pendingCount > 25 ) { - vAPI.shutdown.exec(); - } - var port = this.connect(); - if ( port === null ) { - if ( typeof callback === 'function' ) { - callback(); - } - return; - } - var auxProcessId; - if ( callback ) { - auxProcessId = this.auxProcessId++; - this.pending[auxProcessId] = callback; - this.pendingCount += 1; - } - port.postMessage({ - channelName: channelName, - auxProcessId: auxProcessId, - toTabId: toTabId, - toChannel: toChannel, - msg: message - }); - }, - - addChannelListener: function(channelName, callback) { - if ( typeof callback !== 'function' ) { - return; - } - var listeners = this.channels[channelName]; - if ( listeners !== undefined && listeners.indexOf(callback) !== -1 ) { - console.error('Duplicate listener on channel "%s"', channelName); - return; - } - if ( listeners === undefined ) { - this.channels[channelName] = [callback]; - this.channelCount += 1; - } else { - listeners.push(callback); - } - this.connect(); - }, - - removeChannelListener: function(channelName, callback) { - if ( typeof callback !== 'function' ) { - return; - } - var listeners = this.channels[channelName]; - if ( listeners === undefined ) { - return; - } - var pos = this.listeners.indexOf(callback); - if ( pos === -1 ) { - console.error('Listener not found on channel "%s"', channelName); - return; - } - listeners.splice(pos, 1); - if ( listeners.length === 0 ) { - delete this.channels[channelName]; - this.channelCount -= 1; - } - }, - - removeAllChannelListeners: function(channelName) { - var listeners = this.channels[channelName]; - if ( listeners === undefined ) { - return; - } - delete this.channels[channelName]; - this.channelCount -= 1; - }, - - sendToChannelListeners: function(channelName, msg) { - var listeners = this.channels[channelName]; - if ( listeners === undefined ) { - return; - } - var response; - for ( var i = 0, n = listeners.length; i < n; i++ ) { - response = listeners[i](msg); - if ( response !== undefined ) { - break; - } - } - return response; - } -}; - -/******************************************************************************/ - -vAPI.shutdown.add(function() { - vAPI.messaging.shutdown(); - delete window.vAPI; -}); - -// https://www.youtube.com/watch?v=rT5zCHn0tsg -// https://www.youtube.com/watch?v=E-jS4e3zacI - -vAPI.executionCost.stop('vapi-client.js'); - -/******************************************************************************/ -/******************************************************************************/ - -})(this); - -/******************************************************************************/ diff --git a/platform/webext/vapi-common.js b/platform/webext/vapi-common.js deleted file mode 100644 index 6020d6665..000000000 --- a/platform/webext/vapi-common.js +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* - - uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2016 The uBlock Origin authors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see {http://www.gnu.org/licenses/}. - - Home: https://github.com/gorhill/uBlock -*/ - -// For background page or non-background pages - -'use strict'; - -/******************************************************************************/ -/******************************************************************************/ - -(function() { - -var vAPI = self.vAPI = self.vAPI || {}; -var chrome = self.chrome; - -/******************************************************************************/ - -vAPI.setTimeout = vAPI.setTimeout || self.setTimeout.bind(self); - -/******************************************************************************/ - -// http://www.w3.org/International/questions/qa-scripts#directions - -var setScriptDirection = function(language) { - document.body.setAttribute( - 'dir', - ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(language) !== -1 ? 'rtl' : 'ltr' - ); -}; - -/******************************************************************************/ - -vAPI.download = function(details) { - if ( !details.url ) { - return; - } - - var a = document.createElement('a'); - a.href = details.url; - a.setAttribute('download', details.filename || ''); - a.dispatchEvent(new MouseEvent('click')); -}; - -/******************************************************************************/ - -vAPI.insertHTML = function(node, html) { - node.innerHTML = html; -}; - -/******************************************************************************/ - -vAPI.getURL = chrome.runtime.getURL; - -/******************************************************************************/ - -vAPI.i18n = chrome.i18n.getMessage; - -setScriptDirection(vAPI.i18n('@@ui_locale')); - -/******************************************************************************/ - -vAPI.closePopup = function() { - window.open('','_self').close(); -}; - -/******************************************************************************/ - -// A localStorage-like object which should be accessible from the -// background page or auxiliary pages. -// This storage is optional, but it is nice to have, for a more polished user -// experience. - -// This can throw in some contexts (like in devtool). -try { - vAPI.localStorage = window.localStorage; -} catch (ex) { -} - -/******************************************************************************/ - -})(); - -/******************************************************************************/