 Issue 5468762555809792:
  Issue 1601 - Generate blocking filters for all URLs associated with the selected element  (Closed)
    
  
    Issue 5468762555809792:
  Issue 1601 - Generate blocking filters for all URLs associated with the selected element  (Closed) 
  | Left: | ||
| Right: | 
| LEFT | RIGHT | 
|---|---|
| 1 /* | 1 /* | 
| 2 * This file is part of Adblock Plus <http://adblockplus.org/>, | 2 * This file is part of Adblock Plus <http://adblockplus.org/>, | 
| 3 * Copyright (C) 2006-2014 Eyeo GmbH | 3 * Copyright (C) 2006-2014 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 | 
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 69 // author created shadow roots, but ignore insertion points. | 69 // author created shadow roots, but ignore insertion points. | 
| 70 var child = document.createTextNode(""); | 70 var child = document.createTextNode(""); | 
| 71 clone.appendChild(child); | 71 clone.appendChild(child); | 
| 72 | 72 | 
| 73 var shadow = document.createElement("shadow"); | 73 var shadow = document.createElement("shadow"); | 
| 74 clone.shadowRoot.appendChild(shadow); | 74 clone.shadowRoot.appendChild(shadow); | 
| 75 | 75 | 
| 76 return shadow.getDistributedNodes()[0] == child; | 76 return shadow.getDistributedNodes()[0] == child; | 
| 77 } | 77 } | 
| 78 | 78 | 
| 79 function getOriginalStyle(element) | |
| 80 { | |
| 81 if ("_originalStyle" in element) | |
| 82 return element._originalStyle; | |
| 83 | |
| 84 return element.getAttribute("style"); | |
| 85 } | |
| 86 | |
| 79 function highlightElement(element, shadowColor, backgroundColor) | 87 function highlightElement(element, shadowColor, backgroundColor) | 
| 80 { | 88 { | 
| 81 unhighlightElement(element); | 89 unhighlightElement(element); | 
| 82 | 90 | 
| 83 var originalBoxShadowPriority = element.style.getPropertyPriority("box-shadow" ); | 91 var originalBoxShadowPriority = element.style.getPropertyPriority("box-shadow" ); | 
| 84 var originalBackgroundColorPriority = element.style.getPropertyPriority("backg round-color"); | 92 var originalBackgroundColorPriority = element.style.getPropertyPriority("backg round-color"); | 
| 85 | 93 | 
| 86 var boxShadow = "inset 0px 0px 5px " + shadowColor; | 94 var boxShadow = "inset 0px 0px 5px " + shadowColor; | 
| 87 | 95 | 
| 88 var highlightWithShadowDOM = function() | 96 var highlightWithShadowDOM = function() | 
| (...skipping 11 matching lines...) Expand all Loading... | |
| 100 element._unhighlight = function() | 108 element._unhighlight = function() | 
| 101 { | 109 { | 
| 102 root.removeChild(style); | 110 root.removeChild(style); | 
| 103 }; | 111 }; | 
| 104 }; | 112 }; | 
| 105 | 113 | 
| 106 var highlightWithStyleAttribute = function() | 114 var highlightWithStyleAttribute = function() | 
| 107 { | 115 { | 
| 108 var originalBoxShadow = element.style.getPropertyValue("box-shadow"); | 116 var originalBoxShadow = element.style.getPropertyValue("box-shadow"); | 
| 109 var originalBackgroundColor = element.style.getPropertyValue("background-col or"); | 117 var originalBackgroundColor = element.style.getPropertyValue("background-col or"); | 
| 118 | |
| 119 element._originalStyle = getOriginalStyle(element); | |
| 110 | 120 | 
| 111 element.style.setProperty("box-shadow", boxShadow, "important"); | 121 element.style.setProperty("box-shadow", boxShadow, "important"); | 
| 112 element.style.setProperty("background-color", backgroundColor, "important"); | 122 element.style.setProperty("background-color", backgroundColor, "important"); | 
| 113 | 123 | 
| 114 element._unhighlight = function() | 124 element._unhighlight = function() | 
| 115 { | 125 { | 
| 116 this.style.removeProperty("box-shadow"); | 126 this.style.removeProperty("box-shadow"); | 
| 117 this.style.setProperty( | 127 this.style.setProperty( | 
| 118 "box-shadow", | 128 "box-shadow", | 
| 119 originalBoxShadow, | 129 originalBoxShadow, | 
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 166 function unhighlightElements() { | 176 function unhighlightElements() { | 
| 167 if (highlightedElementsSelector) | 177 if (highlightedElementsSelector) | 
| 168 { | 178 { | 
| 169 Array.prototype.forEach.call( | 179 Array.prototype.forEach.call( | 
| 170 document.querySelectorAll(highlightedElementsSelector), | 180 document.querySelectorAll(highlightedElementsSelector), | 
| 171 unhighlightElement | 181 unhighlightElement | 
| 172 ); | 182 ); | 
| 173 | 183 | 
| 174 highlightedElementsSelector = null; | 184 highlightedElementsSelector = null; | 
| 175 } | 185 } | 
| 186 } | |
| 187 | |
| 188 function getURLsFromObjectElement(element) | |
| 189 { | |
| 190 var url = element.getAttribute("data"); | |
| 191 if (url) | |
| 192 return [resolveURL(url)]; | |
| 193 | |
| 194 for (var i = 0; i < element.children.length; i++) | |
| 195 { | |
| 196 var child = element.children[i]; | |
| 197 if (child.localName != "param") | |
| 198 continue; | |
| 199 | |
| 200 var name = child.getAttribute("name"); | |
| 201 if (name != "movie" && // Adobe Flash | |
| 202 name != "source" && // Silverlight | |
| 203 name != "src" && // Real Media + Quicktime | |
| 204 name != "FileName") // Windows Media | |
| 205 continue; | |
| 206 | |
| 207 var value = child.getAttribute("value"); | |
| 208 if (!value) | |
| 209 continue; | |
| 210 | |
| 211 return [resolveURL(value)]; | |
| 212 } | |
| 213 | |
| 214 return []; | |
| 215 } | |
| 216 | |
| 217 function getURLsFromAttributes(element) | |
| 218 { | |
| 219 var urls = []; | |
| 220 | |
| 221 if (element.src) | |
| 222 urls.push(element.src); | |
| 223 | |
| 224 if (element.srcset) | |
| 225 { | |
| 226 var candidates = element.srcset.split(","); | |
| 227 for (var i = 0; i < candidates.length; i++) | |
| 228 { | |
| 229 var url = candidates[i].trim().replace(/\s+\S+$/, ""); | |
| 230 if (url) | |
| 231 urls.push(resolveURL(url)); | |
| 232 } | |
| 233 } | |
| 234 | |
| 235 return urls; | |
| 236 } | |
| 237 | |
| 238 function getURLsFromMediaElement(element) | |
| 239 { | |
| 240 var urls = getURLsFromAttributes(element); | |
| 241 | |
| 242 for (var i = 0; i < element.children.length; i++) | |
| 243 { | |
| 244 var child = element.children[i]; | |
| 245 if (child.localName == "source" || child.localName == "track") | |
| 246 urls.push.apply(urls, getURLsFromAttributes(child)); | |
| 247 } | |
| 248 | |
| 249 if (element.poster) | |
| 250 urls.push(element.poster); | |
| 251 | |
| 252 return urls; | |
| 253 } | |
| 254 | |
| 255 function getURLsFromElement(element) { | |
| 256 switch (element.localName) | |
| 257 { | |
| 258 case "object": | |
| 259 return getURLsFromObjectElement(element); | |
| 260 | |
| 261 case "video": | |
| 262 case "audio": | |
| 263 case "picture": | |
| 264 return getURLsFromMediaElement(element); | |
| 265 } | |
| 266 | |
| 267 return getURLsFromAttributes(element); | |
| 268 } | |
| 269 | |
| 270 function isBlockable(element) | |
| 271 { | |
| 272 if (element.id) | |
| 273 return true; | |
| 274 if (element.classList.length > 0) | |
| 275 return true; | |
| 276 if (getURLsFromElement(element).length > 0) | |
| 277 return true; | |
| 278 | |
| 279 // We only generate filters based on the "style" attribute, | |
| 280 // if this is the only way we can generate a filter, and | |
| 281 // only if there are at least two CSS properties defined. | |
| 282 if (/:.+:/.test(getOriginalStyle(element))) | |
| 283 return true; | |
| 284 | |
| 285 return false; | |
| 176 } | 286 } | 
| 177 | 287 | 
| 178 // Gets the absolute position of an element by walking up the DOM tree, | 288 // Gets the absolute position of an element by walking up the DOM tree, | 
| 179 // adding up offsets. | 289 // adding up offsets. | 
| 180 // I hope there's a better way because it just seems absolutely stupid | 290 // I hope there's a better way because it just seems absolutely stupid | 
| 181 // that the DOM wouldn't have a direct way to get this, given that it | 291 // that the DOM wouldn't have a direct way to get this, given that it | 
| 182 // has hundreds and hundreds of other methods that do random junk. | 292 // has hundreds and hundreds of other methods that do random junk. | 
| 183 function getAbsolutePosition(elt) { | 293 function getAbsolutePosition(elt) { | 
| 184 var l = 0; | 294 var l = 0; | 
| 185 var t = 0; | 295 var t = 0; | 
| 186 for(; elt; elt = elt.offsetParent) { | 296 for(; elt; elt = elt.offsetParent) { | 
| 187 l += elt.offsetLeft; | 297 l += elt.offsetLeft; | 
| 188 t += elt.offsetTop; | 298 t += elt.offsetTop; | 
| 189 } | 299 } | 
| 190 return [l, t]; | 300 return [l, t]; | 
| 191 } | 301 } | 
| 192 | 302 | 
| 193 // Adds an overlay to an element, which is probably a Flash object | 303 // Adds an overlay to an element, which is probably a Flash object | 
| 194 function addElementOverlay(elt) { | 304 function addElementOverlay(elt) { | 
| 195 // If this element is enclosed in an object tag, we prefer to block that inste ad | 305 // If this element is enclosed in an object tag, we prefer to block that inste ad | 
| 196 if(!elt) | 306 if(!elt) | 
| 197 return null; | 307 return null; | 
| 198 | 308 | 
| 199 // If element doesn't have at least one of class name, ID or URL, give up | 309 // If element doesn't have at least one of class name, ID or URL, give up | 
| 200 // because we don't know how to construct a filter rule for it | 310 // because we don't know how to construct a filter rule for it | 
| 201 if(!hasFilters(elt)) | 311 if(!isBlockable(elt)) | 
| 202 return; | 312 return; | 
| 313 | |
| 314 // If the element isn't rendered (since its or one of its ancestor's | |
| 315 // "display" property is "none"), the overlay wouldn't match the element. | |
| 316 if (!elt.offsetParent) | |
| 317 return; | |
| 318 | |
| 203 var thisStyle = getComputedStyle(elt, null); | 319 var thisStyle = getComputedStyle(elt, null); | 
| 204 var overlay = document.createElement('div'); | 320 var overlay = document.createElement('div'); | 
| 205 overlay.prisoner = elt; | 321 overlay.prisoner = elt; | 
| 206 overlay.className = "__adblockplus__overlay"; | 322 overlay.className = "__adblockplus__overlay"; | 
| 207 overlay.setAttribute('style', 'opacity:0.4; background-color:#ffffff; display: inline-box; ' + 'width:' + thisStyle.width + '; height:' + thisStyle.height + '; position:absolute; overflow:hidden; -webkit-box-sizing:border-box; z-index: 999 99'); | 323 overlay.setAttribute('style', 'opacity:0.4; background-color:#ffffff; display: inline-box; ' + 'width:' + thisStyle.width + '; height:' + thisStyle.height + '; position:absolute; overflow:hidden; -webkit-box-sizing:border-box;'); | 
| 208 var pos = getAbsolutePosition(elt); | 324 var pos = getAbsolutePosition(elt); | 
| 209 overlay.style.left = pos[0] + "px"; | 325 overlay.style.left = pos[0] + "px"; | 
| 210 overlay.style.top = pos[1] + "px"; | 326 overlay.style.top = pos[1] + "px"; | 
| 327 | |
| 328 if (thisStyle.position != "static") | |
| 329 overlay.style.zIndex = thisStyle.zIndex; | |
| 330 else | |
| 331 overlay.style.zIndex = getComputedStyle(elt.offsetParent).zIndex; | |
| 332 | |
| 211 // elt.parentNode.appendChild(overlay, elt); | 333 // elt.parentNode.appendChild(overlay, elt); | 
| 212 document.body.appendChild(overlay); | 334 document.body.appendChild(overlay); | 
| 213 return overlay; | 335 return overlay; | 
| 214 } | 336 } | 
| 215 | 337 | 
| 216 // Show dialog asking user whether she wants to add the proposed filters derived | 338 // Show dialog asking user whether she wants to add the proposed filters derived | 
| 217 // from selected page element | 339 // from selected page element | 
| 218 function clickHide_showDialog(left, top, filters) | 340 function clickHide_showDialog(left, top, filters) | 
| 219 { | 341 { | 
| 220 // If we are already selecting, abort now | 342 // If we are already selecting, abort now | 
| 221 if (clickHide_activated || clickHideFiltersDialog) | 343 if (clickHide_activated || clickHideFiltersDialog) | 
| 222 { | 344 clickHide_deactivate(true); | 
| 223 var savedElement = (currentElement.prisoner ? currentElement.prisoner : curr entElement); | |
| 224 clickHide_deactivate(); | |
| 225 currentElement = savedElement; | |
| 226 } | |
| 227 | 345 | 
| 228 clickHide_filters = filters; | 346 clickHide_filters = filters; | 
| 229 | 347 | 
| 230 clickHideFiltersDialog = document.createElement("iframe"); | 348 clickHideFiltersDialog = document.createElement("iframe"); | 
| 231 clickHideFiltersDialog.src = ext.getURL("block.html"); | 349 clickHideFiltersDialog.src = ext.getURL("block.html"); | 
| 232 clickHideFiltersDialog.setAttribute("style", "position: fixed !important; visi bility: hidden; display: block !important; border: 0px !important;"); | 350 clickHideFiltersDialog.setAttribute("style", "position: fixed !important; visi bility: hidden; display: block !important; border: 0px !important;"); | 
| 233 clickHideFiltersDialog.style.WebkitBoxShadow = "5px 5px 20px rgba(0,0,0,0.5)"; | 351 clickHideFiltersDialog.style.WebkitBoxShadow = "5px 5px 20px rgba(0,0,0,0.5)"; | 
| 234 clickHideFiltersDialog.style.zIndex = 0x7FFFFFFF; | 352 clickHideFiltersDialog.style.zIndex = 0x7FFFFFFF; | 
| 235 | 353 | 
| 236 // Position in upper-left all the time | 354 // Position in upper-left all the time | 
| (...skipping 24 matching lines...) Expand all Loading... | |
| 261 // If we are already selecting, abort now | 379 // If we are already selecting, abort now | 
| 262 if (clickHide_activated || clickHideFiltersDialog) | 380 if (clickHide_activated || clickHideFiltersDialog) | 
| 263 clickHide_deactivate(); | 381 clickHide_deactivate(); | 
| 264 | 382 | 
| 265 // Add overlays for elements with URLs so user can easily click them | 383 // Add overlays for elements with URLs so user can easily click them | 
| 266 var elts = document.querySelectorAll('object,embed,img,iframe,video,audio,pict ure'); | 384 var elts = document.querySelectorAll('object,embed,img,iframe,video,audio,pict ure'); | 
| 267 for(var i=0; i<elts.length; i++) | 385 for(var i=0; i<elts.length; i++) | 
| 268 addElementOverlay(elts[i]); | 386 addElementOverlay(elts[i]); | 
| 269 | 387 | 
| 270 clickHide_activated = true; | 388 clickHide_activated = true; | 
| 271 document.addEventListener("mouseover", clickHide_mouseOver, false); | 389 document.addEventListener("mouseover", clickHide_mouseOver, true); | 
| 272 document.addEventListener("mouseout", clickHide_mouseOut, false); | 390 document.addEventListener("mouseout", clickHide_mouseOut, true); | 
| 273 document.addEventListener("click", clickHide_mouseClick, false); | 391 document.addEventListener("click", clickHide_mouseClick, true); | 
| 274 document.addEventListener("keydown", clickHide_keyDown, false); | 392 document.addEventListener("keydown", clickHide_keyDown, true); | 
| 275 } | 393 } | 
| 276 | 394 | 
| 277 // Called when user has clicked on something and we are waiting for confirmation | 395 // Called when user has clicked on something and we are waiting for confirmation | 
| 278 // on whether the user actually wants these filters | 396 // on whether the user actually wants these filters | 
| 279 function clickHide_rulesPending() { | 397 function clickHide_rulesPending() { | 
| 280 clickHide_activated = false; | 398 clickHide_activated = false; | 
| 281 document.removeEventListener("mouseover", clickHide_mouseOver, false); | 399 document.removeEventListener("mouseover", clickHide_mouseOver, true); | 
| 282 document.removeEventListener("mouseout", clickHide_mouseOut, false); | 400 document.removeEventListener("mouseout", clickHide_mouseOut, true); | 
| 283 document.removeEventListener("click", clickHide_mouseClick, false); | 401 document.removeEventListener("click", clickHide_mouseClick, true); | 
| 284 document.removeEventListener("keydown", clickHide_keyDown, false); | 402 document.removeEventListener("keydown", clickHide_keyDown, true); | 
| 285 } | 403 } | 
| 286 | 404 | 
| 287 // Turn off click-to-hide | 405 // Turn off click-to-hide | 
| 288 function clickHide_deactivate() | 406 function clickHide_deactivate(keepOverlays) | 
| 289 { | 407 { | 
| 290 if (clickHideFiltersDialog) | 408 if (clickHideFiltersDialog) | 
| 291 { | 409 { | 
| 292 document.body.removeChild(clickHideFiltersDialog); | 410 document.body.removeChild(clickHideFiltersDialog); | 
| 293 clickHideFiltersDialog = null; | 411 clickHideFiltersDialog = null; | 
| 294 } | 412 } | 
| 295 | |
| 296 if(currentElement) { | |
| 297 currentElement.removeEventListener("contextmenu", clickHide_elementClickHand ler, false); | |
| 298 unhighlightElements(); | |
| 299 unhighlightElement(currentElement); | |
| 300 currentElement = null; | |
| 301 clickHideFilters = null; | |
| 302 } | |
| 303 unhighlightElements(); | |
| 304 | 413 | 
| 305 clickHide_activated = false; | 414 clickHide_activated = false; | 
| 306 clickHide_filters = null; | 415 clickHide_filters = null; | 
| 307 if(!document) | 416 if(!document) | 
| 308 return; // This can happen inside a nuked iframe...I think | 417 return; // This can happen inside a nuked iframe...I think | 
| 309 document.removeEventListener("mouseover", clickHide_mouseOver, false); | 418 document.removeEventListener("mouseover", clickHide_mouseOver, true); | 
| 310 document.removeEventListener("mouseout", clickHide_mouseOut, false); | 419 document.removeEventListener("mouseout", clickHide_mouseOut, true); | 
| 311 document.removeEventListener("click", clickHide_mouseClick, false); | 420 document.removeEventListener("click", clickHide_mouseClick, true); | 
| 312 document.removeEventListener("keydown", clickHide_keyDown, false); | 421 document.removeEventListener("keydown", clickHide_keyDown, true); | 
| 313 | 422 | 
| 314 // Remove overlays | 423 if (!keepOverlays) | 
| 315 // For some reason iterating over the array returend by getElementsByClassName () doesn't work | 424 { | 
| 316 var elt; | 425 if (currentElement) { | 
| 317 while(elt = document.querySelector('.__adblockplus__overlay')) | 426 currentElement.removeEventListener("contextmenu", clickHide_elementClickH andler, true); | 
| 318 elt.parentNode.removeChild(elt); | 427 unhighlightElements(); | 
| 428 unhighlightElement(currentElement); | |
| 429 currentElement = null; | |
| 430 clickHideFilters = null; | |
| 431 } | |
| 432 unhighlightElements(); | |
| 433 | |
| 434 var overlays = document.getElementsByClassName("__adblockplus__overlay"); | |
| 435 while (overlays.length > 0) | |
| 436 overlays[0].parentNode.removeChild(overlays[0]); | |
| 437 } | |
| 319 } | 438 } | 
| 320 | 439 | 
| 321 function clickHide_elementClickHandler(ev) { | 440 function clickHide_elementClickHandler(ev) { | 
| 322 ev.preventDefault(); | 441 ev.preventDefault(); | 
| 323 ev.stopPropagation(); | 442 ev.stopPropagation(); | 
| 324 clickHide_mouseClick(ev); | 443 clickHide_mouseClick(ev); | 
| 325 } | 444 } | 
| 326 | 445 | 
| 327 // Hovering over an element so highlight it | 446 // Hovering over an element so highlight it | 
| 328 function clickHide_mouseOver(e) | 447 function clickHide_mouseOver(e) | 
| 329 { | 448 { | 
| 330 if (clickHide_activated == false) | 449 if (clickHide_activated == false) | 
| 331 return; | 450 return; | 
| 332 | 451 | 
| 333 var target = e.target; | 452 var target = e.target; | 
| 334 while (target.parentNode && !hasFilters(target)) | 453 while (target.parentNode && !isBlockable(target)) | 
| 335 target = target.parentNode; | 454 target = target.parentNode; | 
| 336 if (target == document.documentElement || target == document.body) | 455 if (target == document.documentElement || target == document.body) | 
| 337 target = null; | 456 target = null; | 
| 338 | 457 | 
| 339 if (target && target instanceof HTMLElement) | 458 if (target && target instanceof HTMLElement) | 
| 340 { | 459 { | 
| 341 currentElement = target; | 460 currentElement = target; | 
| 342 | 461 | 
| 343 highlightElement(target, "#d6d84b", "#f8fa47"); | 462 highlightElement(target, "#d6d84b", "#f8fa47"); | 
| 344 target.addEventListener("contextmenu", clickHide_elementClickHandler, false) ; | 463 target.addEventListener("contextmenu", clickHide_elementClickHandler, true); | 
| 345 } | 464 } | 
| 346 } | 465 } | 
| 347 | 466 | 
| 348 // No longer hovering over this element so unhighlight it | 467 // No longer hovering over this element so unhighlight it | 
| 349 function clickHide_mouseOut(e) | 468 function clickHide_mouseOut(e) | 
| 350 { | 469 { | 
| 351 if (!clickHide_activated || !currentElement) | 470 if (!clickHide_activated || !currentElement) | 
| 352 return; | 471 return; | 
| 353 | 472 | 
| 354 unhighlightElement(currentElement); | 473 unhighlightElement(currentElement); | 
| 355 currentElement.removeEventListener("contextmenu", clickHide_elementClickHandle r, false); | 474 currentElement.removeEventListener("contextmenu", clickHide_elementClickHandle r, true); | 
| 356 } | 475 } | 
| 357 | 476 | 
| 358 // Selects the currently hovered-over filter or cancels selection | 477 // Selects the currently hovered-over filter or cancels selection | 
| 359 function clickHide_keyDown(e) | 478 function clickHide_keyDown(e) | 
| 360 { | 479 { | 
| 361 if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 13 /*DOM_VK_RETURN* /) | 480 if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 13 /*DOM_VK_RETURN* /) | 
| 362 clickHide_mouseClick(e); | 481 clickHide_mouseClick(e); | 
| 363 else if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 27 /*DOM_VK_ES CAPE*/) | 482 else if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 27 /*DOM_VK_ES CAPE*/) | 
| 364 { | 483 { | 
| 365 ext.backgroundPage.sendMessage( | 484 ext.backgroundPage.sendMessage( | 
| 366 { | 485 { | 
| 367 type: "forward", | 486 type: "forward", | 
| 368 payload: | 487 payload: | 
| 369 { | 488 { | 
| 370 type: "clickhide-deactivate" | 489 type: "clickhide-deactivate" | 
| 371 } | 490 } | 
| 372 }); | 491 }); | 
| 373 e.preventDefault(); | 492 e.preventDefault(); | 
| 374 e.stopPropagation(); | 493 e.stopPropagation(); | 
| 375 } | 494 } | 
| 376 } | 495 } | 
| 377 | 496 | 
| 378 | |
| 379 | |
| 380 // When the user clicks, the currentElement is the one we want. | 497 // When the user clicks, the currentElement is the one we want. | 
| 381 // We should have ABP rules ready for when the | 498 // We should have ABP rules ready for when the | 
| 382 // popup asks for them. | 499 // popup asks for them. | 
| 383 function clickHide_mouseClick(e) | 500 function clickHide_mouseClick(e) | 
| 384 { | 501 { | 
| 385 if (!currentElement || !clickHide_activated) | 502 if (!currentElement || !clickHide_activated) | 
| 386 return; | 503 return; | 
| 387 | 504 | 
| 388 var elt = currentElement; | 505 var elt = currentElement; | 
| 389 if (currentElement.classList.contains("__adblockplus__overlay")) | 506 if (currentElement.classList.contains("__adblockplus__overlay")) | 
| 390 elt = currentElement.prisoner; | 507 elt = currentElement.prisoner; | 
| 391 | 508 | 
| 392 clickHideFilters = new Array(); | 509 clickHideFilters = new Array(); | 
| 393 selectorList = new Array(); | 510 selectorList = new Array(); | 
| 394 | 511 | 
| 395 var addSelector = function(selector) | 512 var addSelector = function(selector) | 
| 
kzar
2014/11/25 18:10:11
Why we don't just do "function addSelector(selecto
 
Sebastian Noack
2014/11/25 19:01:15
The only reason that comes to my mind would be tha
 | |
| 396 { | 513 { | 
| 397 if (selectorList.indexOf(selector) != -1) | 514 if (selectorList.indexOf(selector) != -1) | 
| 398 return; | 515 return; | 
| 399 | 516 | 
| 400 clickHideFilters.push(document.domain + "##" + selector); | 517 clickHideFilters.push(document.domain + "##" + selector); | 
| 401 selectorList.push(selector); | 518 selectorList.push(selector); | 
| 402 }; | 519 }; | 
| 403 | 520 | 
| 404 if (elt.id) | 521 if (elt.id) | 
| 405 addSelector("#" + escapeCSS(elt.id)); | 522 addSelector("#" + escapeCSS(elt.id)); | 
| 406 | 523 | 
| 407 if (elt.classList.length > 0) | 524 if (elt.classList.length > 0) | 
| 408 { | 525 { | 
| 409 var selector = ""; | 526 var selector = ""; | 
| 410 | 527 | 
| 411 for (var i = 0; i < elt.classList.length; i++) | 528 for (var i = 0; i < elt.classList.length; i++) | 
| 412 selector += "." + escapeCSS(elt.classList[i]); | 529 selector += "." + escapeCSS(elt.classList[i]); | 
| 413 | 530 | 
| 414 addSelector(selector); | 531 addSelector(selector); | 
| 415 } | 532 } | 
| 416 | 533 | 
| 417 var urls = getElementURLs(elt); | 534 var urls = getURLsFromElement(elt); | 
| 418 for (var i = 0; i < urls.length; i++) | 535 for (var i = 0; i < urls.length; i++) | 
| 419 { | 536 { | 
| 420 var url = urls[i]; | 537 var url = urls[i]; | 
| 421 var isHTTP = /^https?:/i.test(url); | 538 | 
| 422 | 539 if (/^https?:/i.test(url)) | 
| 423 if (isHTTP) | |
| 
kzar
2014/11/25 18:10:11
I found this line a little bit confusing to start
 
Sebastian Noack
2014/11/25 19:01:15
What I actually want, is excluding URLs that can't
 | |
| 424 { | 540 { | 
| 425 var filter = url.replace(/^[\w\-]+:\/+(?:www\.)?/, "||"); | 541 var filter = url.replace(/^[\w\-]+:\/+(?:www\.)?/, "||"); | 
| 426 | 542 | 
| 427 if (clickHideFilters.indexOf(filter) != -1) | 543 if (clickHideFilters.indexOf(filter) == -1) | 
| 428 continue; | 544 clickHideFilters.push(filter); | 
| 429 | 545 | 
| 430 clickHideFilters.push(filter); | 546 continue; | 
| 431 } | 547 } | 
| 432 | 548 | 
| 433 if (url == elt.src) | 549 if (url == elt.src) | 
| 434 { | 550 addSelector(escapeCSS(elt.localName) + '[src=' + quote(elt.getAttribute("s rc")) + ']'); | 
| 435 var selector = escapeCSS(elt.localName) + '[src=' + quote(elt.getAttribute ("src")) + ']'; | 551 } | 
| 436 | |
| 437 if (isHTTP) | |
| 438 selectorList.push(selector); | |
| 439 else | |
| 440 addSelector(selector); | |
| 441 } | |
| 442 } | |
| 443 | |
| 444 // restore the original style, before generating the fallback filter that | |
| 445 // will include the style, and to prevent highlightElements from saving those | |
| 446 unhighlightElement(currentElement); | |
| 447 | 552 | 
| 448 // as last resort, create a filter based on inline styles | 553 // as last resort, create a filter based on inline styles | 
| 449 if (clickHideFilters.length == 0) | 554 if (clickHideFilters.length == 0) | 
| 450 { | 555 { | 
| 451 var style = elt.getAttribute("style"); | 556 var style = getOriginalStyle(elt); | 
| 452 if (style) | 557 if (style) | 
| 453 addSelector(escapeCSS(elt.localName) + '[style=' + quote(style) + ']'); | 558 addSelector(escapeCSS(elt.localName) + '[style=' + quote(style) + ']'); | 
| 454 } | 559 } | 
| 455 | 560 | 
| 456 // Show popup | 561 // Show popup | 
| 457 clickHide_showDialog(e.clientX, e.clientY, clickHideFilters); | 562 clickHide_showDialog(e.clientX, e.clientY, clickHideFilters); | 
| 458 | 563 | 
| 459 // Highlight the elements specified by selector in yellow | 564 // Highlight the elements specified by selector in yellow | 
| 460 highlightElements(selectorList.join(",")); | 565 if (selectorList.length > 0) | 
| 566 highlightElements(selectorList.join(",")); | |
| 461 // Now, actually highlight the element the user clicked on in red | 567 // Now, actually highlight the element the user clicked on in red | 
| 462 highlightElement(currentElement, "#fd1708", "#f6a1b5"); | 568 highlightElement(currentElement, "#fd1708", "#f6a1b5"); | 
| 463 | 569 | 
| 464 // Make sure the browser doesn't handle this click | 570 // Make sure the browser doesn't handle this click | 
| 465 e.preventDefault(); | 571 e.preventDefault(); | 
| 466 e.stopPropagation(); | 572 e.stopPropagation(); | 
| 467 } | |
| 468 | |
| 469 function parseSrcSet(element) | |
| 470 { | |
| 471 if (!element.srcset) | |
| 472 return []; | |
| 473 | |
| 474 var urls = element.srcset.split(","); | |
| 475 for (var i = 0; i < urls.length; i++) | |
| 476 { | |
| 477 var url = urls[i].replace(/^\s+/, "").replace(/(\s+\S+)?\s*$/, ""); | |
| 478 if (url) | |
| 479 urls[i] = resolveURL(url); | |
| 480 else | |
| 481 urls.splice(i--, 1); | |
| 
kzar
2014/11/25 18:10:11
Does removing an element from the array you're ite
 
Sebastian Noack
2014/11/25 19:01:15
Not as long as you decrease the iteration counter
 
Sebastian Noack
2014/11/25 19:03:11
s/is problamatic/isn't problamatic/
 | |
| 482 } | |
| 483 | |
| 484 return urls; | |
| 485 } | |
| 486 | |
| 487 function getElementURLs(elt) { | |
| 488 var urls = []; | |
| 489 | |
| 490 if (elt.src) | |
| 491 urls.push(elt.src); | |
| 492 | |
| 493 switch (elt.localName) | |
| 494 { | |
| 495 case "object": | |
| 496 var url = elt.getAttribute("data"); | |
| 497 if (url) | |
| 498 return [resolveURL(url)]; | |
| 499 | |
| 500 for (var i = 0; i < elt.children.length; i++) | |
| 501 { | |
| 502 var child = elt.children[i]; | |
| 503 if (child.localName != "param") | |
| 504 continue; | |
| 505 | |
| 506 var name = child.getAttribute("name"); | |
| 507 if (name != "movie" && name != "src") | |
| 508 continue; | |
| 509 | |
| 510 var value = child.getAttribute("value"); | |
| 511 if (!value) | |
| 512 continue; | |
| 513 | |
| 514 return [resolveURL(value)]; | |
| 515 } | |
| 516 | |
| 517 return []; | |
| 518 | |
| 519 case "video": | |
| 520 case "audio": | |
| 521 case "picture": | |
| 522 for (var i = 0; i < elt.children.length; i++) | |
| 523 { | |
| 524 var child = elt.children[i]; | |
| 525 | |
| 526 if (child.localName != "source") | |
| 527 continue; | |
| 528 | |
| 529 if (child.src) | |
| 530 urls.push(child.src); | |
| 531 | |
| 532 urls = urls.concat(parseSrcSet(child)); | |
| 533 } | |
| 534 | |
| 535 break; | |
| 536 | |
| 537 case "img": | |
| 538 urls = urls.concat(parseSrcSet(elt)); | |
| 539 } | |
| 540 | |
| 541 return urls; | |
| 542 } | |
| 543 | |
| 544 function hasFilters(element) | |
| 
kzar
2014/11/26 10:51:22
Maybe a name like "isFilterable" would be better t
 
kzar
2014/11/26 10:51:22
How come you don't put this function above where i
 
Sebastian Noack
2014/11/26 11:08:14
As discussed on IRC, it's called "isBlockable" now
 
Sebastian Noack
2014/11/26 11:08:14
Since it relies on "getElementURLs()", I just put
 | |
| 545 { | |
| 546 if (element.id) | |
| 547 return true; | |
| 548 if (element.classList.length > 0) | |
| 549 return true; | |
| 550 if (getElementURLs(element).length > 0) | |
| 551 return true; | |
| 552 if (/:.+:/.test(element.getAttribute("style"))) | |
| 
kzar
2014/11/26 10:51:22
Maybe comment this line to explain what you told m
 
Sebastian Noack
2014/11/26 11:08:14
Well, this logic isn't new. But I guess I can add
 | |
| 553 return true; | |
| 554 | |
| 555 return false; | |
| 556 } | 573 } | 
| 557 | 574 | 
| 558 // This function Copyright (c) 2008 Jeni Tennison, from jquery.uri.js | 575 // This function Copyright (c) 2008 Jeni Tennison, from jquery.uri.js | 
| 559 // and licensed under the MIT license. See jquery-*.min.js for details. | 576 // and licensed under the MIT license. See jquery-*.min.js for details. | 
| 560 function removeDotSegments(u) { | 577 function removeDotSegments(u) { | 
| 561 var r = '', m = []; | 578 var r = '', m = []; | 
| 562 if (/\./.test(u)) { | 579 if (/\./.test(u)) { | 
| 563 while (u !== undefined && u !== '') { | 580 while (u !== undefined && u !== '') { | 
| 564 if (u === '.' || u === '..') { | 581 if (u === '.' || u === '..') { | 
| 565 u = ''; | 582 u = ''; | 
| (...skipping 23 matching lines...) Expand all Loading... | |
| 589 // exists before continuing to avoid "Uncaught ReferenceError: ext is not define d". | 606 // exists before continuing to avoid "Uncaught ReferenceError: ext is not define d". | 
| 590 // See https://crbug.com/416907 | 607 // See https://crbug.com/416907 | 
| 591 if ("ext" in window && document instanceof HTMLDocument) | 608 if ("ext" in window && document instanceof HTMLDocument) | 
| 592 { | 609 { | 
| 593 // Use a contextmenu handler to save the last element the user right-clicked o n. | 610 // Use a contextmenu handler to save the last element the user right-clicked o n. | 
| 594 // To make things easier, we actually save the DOM event. | 611 // To make things easier, we actually save the DOM event. | 
| 595 // We have to do this because the contextMenu API only provides a URL, not the actual | 612 // We have to do this because the contextMenu API only provides a URL, not the actual | 
| 596 // DOM element. | 613 // DOM element. | 
| 597 document.addEventListener('contextmenu', function(e) { | 614 document.addEventListener('contextmenu', function(e) { | 
| 598 lastRightClickEvent = e; | 615 lastRightClickEvent = e; | 
| 599 }, false); | 616 }, true); | 
| 600 | 617 | 
| 601 document.addEventListener("click", function(event) | 618 document.addEventListener("click", function(event) | 
| 602 { | 619 { | 
| 603 // Ignore right-clicks | 620 // Ignore right-clicks | 
| 604 if (event.button == 2) | 621 if (event.button == 2) | 
| 605 return; | 622 return; | 
| 606 | 623 | 
| 607 // Search the link associated with the click | 624 // Search the link associated with the click | 
| 608 var link = event.target; | 625 var link = event.target; | 
| 609 while (link && !(link instanceof HTMLAnchorElement)) | 626 while (link && !(link instanceof HTMLAnchorElement)) | 
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 729 } | 746 } | 
| 730 break; | 747 break; | 
| 731 case "clickhide-move": | 748 case "clickhide-move": | 
| 732 if (clickHideFiltersDialog) | 749 if (clickHideFiltersDialog) | 
| 733 { | 750 { | 
| 734 clickHideFiltersDialog.style.left = (parseInt(clickHideFiltersDialog.s tyle.left, 10) + msg.x) + "px"; | 751 clickHideFiltersDialog.style.left = (parseInt(clickHideFiltersDialog.s tyle.left, 10) + msg.x) + "px"; | 
| 735 clickHideFiltersDialog.style.top = (parseInt(clickHideFiltersDialog.st yle.top, 10) + msg.y) + "px"; | 752 clickHideFiltersDialog.style.top = (parseInt(clickHideFiltersDialog.st yle.top, 10) + msg.y) + "px"; | 
| 736 } | 753 } | 
| 737 break; | 754 break; | 
| 738 case "clickhide-close": | 755 case "clickhide-close": | 
| 739 if (clickHideFiltersDialog) | 756 if (clickHideFiltersDialog && msg.remove) | 
| 740 { | 757 { | 
| 741 // Explicitly get rid of currentElement | 758 // Explicitly get rid of currentElement | 
| 742 if (msg.remove && currentElement && currentElement.parentNode) | 759 var element = currentElement.prisoner || currentElement; | 
| 743 currentElement.parentNode.removeChild(currentElement); | 760 if (element && element.parentNode) | 
| 761 element.parentNode.removeChild(element); | |
| 744 } | 762 } | 
| 745 clickHide_deactivate(); | 763 clickHide_deactivate(); | 
| 746 break; | 764 break; | 
| 747 default: | 765 default: | 
| 748 sendResponse({}); | 766 sendResponse({}); | 
| 749 break; | 767 break; | 
| 750 } | 768 } | 
| 751 }); | 769 }); | 
| 752 | 770 | 
| 753 if (window == window.top) | 771 if (window == window.top) | 
| 754 ext.backgroundPage.sendMessage({type: "report-html-page"}); | 772 ext.backgroundPage.sendMessage({type: "report-html-page"}); | 
| 755 } | 773 } | 
| LEFT | RIGHT |