| LEFT | RIGHT | 
|---|
| 1 /* | 1 /* | 
| 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
| 3  * Copyright (C) 2006-present eyeo GmbH | 3  * Copyright (C) 2006-present eyeo GmbH | 
| 4  * | 4  * | 
| 5  * Adblock Plus is free software: you can redistribute it and/or modify | 5  * Adblock Plus is free software: you can redistribute it and/or modify | 
| 6  * it under the terms of the GNU General Public License version 3 as | 6  * it under the terms of the GNU General Public License version 3 as | 
| 7  * published by the Free Software Foundation. | 7  * published by the Free Software Foundation. | 
| 8  * | 8  * | 
| 9  * Adblock Plus is distributed in the hope that it will be useful, | 9  * Adblock Plus is distributed in the hope that it will be useful, | 
| 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| 12  * GNU General Public License for more details. | 12  * GNU General Public License for more details. | 
| 13  * | 13  * | 
| 14  * You should have received a copy of the GNU General Public License | 14  * You should have received a copy of the GNU General Public License | 
| 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
| 16  */ | 16  */ | 
| 17 | 17 | 
| 18 "use strict"; | 18 "use strict"; | 
| 19 | 19 | 
| 20 const {require} = ext.backgroundPage.getWindow(); | 20 let tab = null; | 
| 21 |  | 
| 22 const {Filter} = require("filterClasses"); |  | 
| 23 const {FilterStorage} = require("filterStorage"); |  | 
| 24 const {checkWhitelisted} = require("whitelisting"); |  | 
| 25 const {getDecodedHostname} = require("url"); |  | 
| 26 |  | 
| 27 let page = null; |  | 
| 28 |  | 
| 29 function createPageObject(tab) |  | 
| 30 { |  | 
| 31   if (!tab) |  | 
| 32     return null; |  | 
| 33 |  | 
| 34   // Create a lightweight page object that resembles ext.Page, but with only |  | 
| 35   // an id and an optional url property. |  | 
| 36   let page = {id: tab.id}; |  | 
| 37 |  | 
| 38   if (tab.url) |  | 
| 39     page.url = new URL(tab.url); |  | 
| 40 |  | 
| 41   return page; |  | 
| 42 } |  | 
| 43 | 21 | 
| 44 function getPref(key, callback) | 22 function getPref(key, callback) | 
| 45 { | 23 { | 
| 46   chrome.runtime.sendMessage({type: "prefs.get", key}, callback); | 24   chrome.runtime.sendMessage({type: "prefs.get", key}, callback); | 
| 47 } | 25 } | 
| 48 | 26 | 
| 49 function togglePref(key, callback) | 27 function togglePref(key, callback) | 
| 50 { | 28 { | 
| 51   chrome.runtime.sendMessage({type: "prefs.toggle", key}, callback); | 29   chrome.runtime.sendMessage({type: "prefs.toggle", key}, callback); | 
| 52 } | 30 } | 
| 53 | 31 | 
| 54 function checkPageReady(pageId, state) | 32 function isPageWhitelisted(callback) | 
|  | 33 { | 
|  | 34   chrome.runtime.sendMessage({type: "filters.isWhitelisted", tab}, callback); | 
|  | 35 } | 
|  | 36 | 
|  | 37 function whenPageReady() | 
| 55 { | 38 { | 
| 56   return new Promise(resolve => | 39   return new Promise(resolve => | 
| 57   { | 40   { | 
| 58     chrome.runtime.sendMessage({type: "composer.isPageReady", pageId}, ready => | 41     function onMessage(message, sender) | 
| 59     { | 42     { | 
| 60       if (ready == state) | 43       if (message.type == "composer.ready" && sender.page && | 
|  | 44           sender.page.id == tab.id) | 
|  | 45       { | 
|  | 46         ext.onMessage.removeListener(onMessage); | 
| 61         resolve(); | 47         resolve(); | 
|  | 48       } | 
|  | 49     } | 
|  | 50 | 
|  | 51     ext.onMessage.addListener(onMessage); | 
|  | 52 | 
|  | 53     chrome.runtime.sendMessage({ | 
|  | 54       type: "composer.isPageReady", | 
|  | 55       pageId: tab.id | 
|  | 56     }, | 
|  | 57     ready => | 
|  | 58     { | 
|  | 59       if (ready) | 
|  | 60       { | 
|  | 61         ext.onMessage.removeListener(onMessage); | 
|  | 62         resolve(); | 
|  | 63       } | 
| 62     }); | 64     }); | 
| 63   }); | 65   }); | 
| 64 } |  | 
| 65 |  | 
| 66 function waitForPageReady(pageId) |  | 
| 67 { |  | 
| 68   return new Promise(resolve => |  | 
| 69   { |  | 
| 70     let listener = (message, sender) => |  | 
| 71     { |  | 
| 72       if (message.type == "composer.ready" && sender.page.id == pageId) |  | 
| 73       { |  | 
| 74         ext.onMessage.removeListener(listener); |  | 
| 75         resolve(); |  | 
| 76       } |  | 
| 77     }; |  | 
| 78 |  | 
| 79     ext.onMessage.addListener(listener); |  | 
| 80   }); |  | 
| 81 } |  | 
| 82 |  | 
| 83 function whenPageReady(pageId) |  | 
| 84 { |  | 
| 85   return Promise.race([ |  | 
| 86     waitForPageReady(pageId), |  | 
| 87     // Check if the page is ready after adding the listener just in case we've |  | 
| 88     // already missed it. |  | 
| 89     checkPageReady(pageId, true) |  | 
| 90   ]); |  | 
| 91 } | 66 } | 
| 92 | 67 | 
| 93 function onLoad() | 68 function onLoad() | 
| 94 { | 69 { | 
| 95   chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs => | 70   chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs => | 
| 96   { | 71   { | 
| 97     page = createPageObject(tabs[0]); | 72     if (tabs.length > 0) | 
|  | 73       tab = {id: tabs[0].id, url: tabs[0].url}; | 
| 98 | 74 | 
| 99     // Mark page as 'local' or 'nohtml' to hide non-relevant elements | 75     let urlProtocol = tab && tab.url && new URL(tab.url).protocol; | 
| 100     if (!page || (page.url.protocol != "http:" && | 76 | 
| 101                   page.url.protocol != "https:")) | 77     // Mark page as 'local' to hide non-relevant elements | 
|  | 78     if (urlProtocol != "http:" && urlProtocol != "https:") | 
| 102     { | 79     { | 
| 103       document.body.classList.add("local"); | 80       document.body.classList.add("local"); | 
|  | 81       document.body.classList.remove("nohtml"); | 
| 104     } | 82     } | 
| 105     else | 83     else | 
| 106     { | 84     { | 
| 107       checkPageReady(page.id, false).then(() => | 85       whenPageReady().then(() => | 
| 108       { | 86       { | 
| 109         document.body.classList.add("nohtml"); | 87         document.body.classList.remove("nohtml"); | 
| 110         whenPageReady(page.id).then(() => |  | 
| 111         { |  | 
| 112           document.body.classList.remove("nohtml"); |  | 
| 113         }); |  | 
| 114       }); | 88       }); | 
| 115     } | 89     } | 
| 116 | 90 | 
| 117     // Ask content script whether clickhide is active. If so, show | 91     // Ask content script whether clickhide is active. If so, show | 
| 118     // cancel button.  If that isn't the case, ask background.html | 92     // cancel button.  If that isn't the case, ask background.html | 
| 119     // whether it has cached filters. If so, ask the user whether she | 93     // whether it has cached filters. If so, ask the user whether she | 
| 120     // wants those filters. Otherwise, we are in default state. | 94     // wants those filters. Otherwise, we are in default state. | 
| 121     if (page) | 95     if (tab) | 
| 122     { | 96     { | 
| 123       if (checkWhitelisted(page)) | 97       isPageWhitelisted(whitelisted => | 
| 124         document.body.classList.add("disabled"); | 98       { | 
|  | 99         if (whitelisted) | 
|  | 100           document.body.classList.add("disabled"); | 
|  | 101       }); | 
| 125 | 102 | 
| 126       chrome.tabs.sendMessage(page.id, { | 103       chrome.tabs.sendMessage(tab.id, { | 
| 127         type: "composer.content.getState" | 104         type: "composer.content.getState" | 
| 128       }, | 105       }, | 
| 129       response => | 106       response => | 
| 130       { | 107       { | 
| 131         if (response && response.active) | 108         if (response && response.active) | 
| 132           document.body.classList.add("clickhide-active"); | 109           document.body.classList.add("clickhide-active"); | 
| 133       }); | 110       }); | 
| 134     } | 111     } | 
| 135   }); | 112   }); | 
| 136 | 113 | 
| 137   document.getElementById("enabled").addEventListener( | 114   document.getElementById("enabled").addEventListener( | 
| 138     "click", toggleEnabled, false | 115     "click", toggleEnabled, false | 
| 139   ); | 116   ); | 
| 140   document.getElementById("clickhide").addEventListener( | 117   document.getElementById("clickhide").addEventListener( | 
| 141     "click", activateClickHide, false | 118     "click", activateClickHide, false | 
| 142   ); | 119   ); | 
| 143   document.getElementById("clickhide-cancel").addEventListener( | 120   document.getElementById("clickhide-cancel").addEventListener( | 
| 144     "click", cancelClickHide, false | 121     "click", cancelClickHide, false | 
| 145   ); | 122   ); | 
| 146   document.getElementById("options").addEventListener("click", () => | 123   document.getElementById("options").addEventListener("click", () => | 
| 147   { | 124   { | 
| 148     ext.showOptions(); | 125     ext.showOptions(window.close); | 
| 149   }, false); | 126   }, false); | 
| 150 | 127 | 
| 151   // Set up collapsing of menu items | 128   // Set up collapsing of menu items | 
| 152   for (let collapser of document.getElementsByClassName("collapse")) | 129   for (let collapser of document.getElementsByClassName("collapse")) | 
| 153   { | 130   { | 
| 154     collapser.addEventListener("click", toggleCollapse, false); | 131     collapser.addEventListener("click", toggleCollapse, false); | 
| 155     getPref(collapser.dataset.option, value => | 132     getPref(collapser.dataset.option, value => | 
| 156     { | 133     { | 
| 157       if (value) | 134       if (value) | 
| 158       { | 135       { | 
| 159         document.getElementById( | 136         document.getElementById( | 
| 160           collapser.dataset.collapsible | 137           collapser.dataset.collapsible | 
| 161         ).classList.remove("collapsed"); | 138         ).classList.remove("collapsed"); | 
| 162       } | 139       } | 
| 163     }); | 140     }); | 
| 164   } | 141   } | 
| 165 } | 142 } | 
| 166 | 143 | 
| 167 function toggleEnabled() | 144 function toggleEnabled() | 
| 168 { | 145 { | 
| 169   let disabled = document.body.classList.toggle("disabled"); | 146   let disabled = document.body.classList.toggle("disabled"); | 
| 170   if (disabled) | 147   chrome.runtime.sendMessage({ | 
| 171   { | 148     type: disabled ? "filters.whitelist" : "filters.unwhitelist", | 
| 172     let host = getDecodedHostname(page.url).replace(/^www\./, ""); | 149     tab | 
| 173     let filter = Filter.fromText("@@||" + host + "^$document"); | 150   }); | 
| 174     if (filter.subscriptions.length && filter.disabled) |  | 
| 175       filter.disabled = false; |  | 
| 176     else |  | 
| 177     { |  | 
| 178       filter.disabled = false; |  | 
| 179       FilterStorage.addFilter(filter); |  | 
| 180     } |  | 
| 181   } |  | 
| 182   else |  | 
| 183   { |  | 
| 184     // Remove any exception rules applying to this URL |  | 
| 185     let filter = checkWhitelisted(page); |  | 
| 186     while (filter) |  | 
| 187     { |  | 
| 188       FilterStorage.removeFilter(filter); |  | 
| 189       if (filter.subscriptions.length) |  | 
| 190         filter.disabled = true; |  | 
| 191       filter = checkWhitelisted(page); |  | 
| 192     } |  | 
| 193   } |  | 
| 194 } | 151 } | 
| 195 | 152 | 
| 196 function activateClickHide() | 153 function activateClickHide() | 
| 197 { | 154 { | 
| 198   document.body.classList.add("clickhide-active"); | 155   document.body.classList.add("clickhide-active"); | 
| 199   page.sendMessage({type: "composer.content.startPickingElement"}); | 156   chrome.tabs.sendMessage(tab.id, { | 
|  | 157     type: "composer.content.startPickingElement" | 
|  | 158   }); | 
| 200 | 159 | 
| 201   // Close the popup after a few seconds, so user doesn't have to | 160   // Close the popup after a few seconds, so user doesn't have to | 
| 202   activateClickHide.timeout = window.setTimeout(ext.closePopup, 5000); | 161   activateClickHide.timeout = window.setTimeout(window.close, 5000); | 
| 203 } | 162 } | 
| 204 | 163 | 
| 205 function cancelClickHide() | 164 function cancelClickHide() | 
| 206 { | 165 { | 
| 207   if (activateClickHide.timeout) | 166   if (activateClickHide.timeout) | 
| 208   { | 167   { | 
| 209     window.clearTimeout(activateClickHide.timeout); | 168     window.clearTimeout(activateClickHide.timeout); | 
| 210     activateClickHide.timeout = null; | 169     activateClickHide.timeout = null; | 
| 211   } | 170   } | 
| 212   document.body.classList.remove("clickhide-active"); | 171   document.body.classList.remove("clickhide-active"); | 
| 213   page.sendMessage({type: "composer.content.finished"}); | 172   chrome.tabs.sendMessage(tab.id, {type: "composer.content.finished"}); | 
| 214 } | 173 } | 
| 215 | 174 | 
| 216 function toggleCollapse(event) | 175 function toggleCollapse(event) | 
| 217 { | 176 { | 
| 218   let collapser = event.currentTarget; | 177   let collapser = event.currentTarget; | 
| 219   let collapsible = document.getElementById(collapser.dataset.collapsible); | 178   let collapsible = document.getElementById(collapser.dataset.collapsible); | 
| 220   collapsible.classList.toggle("collapsed"); | 179   collapsible.classList.toggle("collapsed"); | 
| 221   togglePref(collapser.dataset.option); | 180   togglePref(collapser.dataset.option); | 
| 222 } | 181 } | 
| 223 | 182 | 
| 224 document.addEventListener("DOMContentLoaded", onLoad, false); | 183 document.addEventListener("DOMContentLoaded", onLoad, false); | 
| LEFT | RIGHT | 
|---|