| Left: | ||
| Right: |
| 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-2015 Eyeo GmbH | 3 * Copyright (C) 2006-2015 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 // Click-to-hide stuff | 18 // Click-to-hide stuff |
| 19 var clickHide_activated = false; | 19 var clickHide_activated = false; |
| 20 var clickHide_filters = null; | 20 var clickHide_filters = null; |
| 21 var currentElement = null; | 21 var currentElement = null; |
| 22 var highlightedElementsSelector = null; | 22 var highlightedElementsSelector = null; |
| 23 var clickHideFiltersDialog = null; | 23 var clickHideFiltersDialog = null; |
| 24 var lastRightClickEvent = null; | 24 var lastRightClickEvent = null; |
| 25 var lastRightClickEventValid = false; | 25 var lastRightClickEventValid = false; |
| 26 var lastMouseOverEvent = null; | |
| 26 | 27 |
| 27 function highlightElement(element, shadowColor, backgroundColor) | 28 function highlightElement(element, shadowColor, backgroundColor) |
| 28 { | 29 { |
| 29 unhighlightElement(element); | 30 unhighlightElement(element); |
| 30 | 31 |
| 31 var highlightWithOverlay = function() | 32 var highlightWithOverlay = function() |
| 32 { | 33 { |
| 33 var overlay = addElementOverlay(element); | 34 var overlay = addElementOverlay(element); |
| 34 | 35 |
| 35 // If the element isn't displayed no overlay will be added. | 36 // If the element isn't displayed no overlay will be added. |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 189 case "object": | 190 case "object": |
| 190 return getURLsFromObjectElement(element); | 191 return getURLsFromObjectElement(element); |
| 191 | 192 |
| 192 case "video": | 193 case "video": |
| 193 case "audio": | 194 case "audio": |
| 194 case "picture": | 195 case "picture": |
| 195 return getURLsFromMediaElement(element); | 196 return getURLsFromMediaElement(element); |
| 196 } | 197 } |
| 197 | 198 |
| 198 return getURLsFromAttributes(element); | 199 return getURLsFromAttributes(element); |
| 199 } | |
| 200 | |
| 201 function isBlockable(element) | |
| 202 { | |
| 203 if (element.id) | |
| 204 return true; | |
| 205 if (element.classList.length > 0) | |
| 206 return true; | |
| 207 if (getURLsFromElement(element).length > 0) | |
| 208 return true; | |
| 209 | |
| 210 // We only generate filters based on the "style" attribute, | |
| 211 // if this is the only way we can generate a filter, and | |
| 212 // only if there are at least two CSS properties defined. | |
| 213 if (/:.+:/.test(element.getAttribute("style"))) | |
| 214 return true; | |
| 215 | |
| 216 return false; | |
| 217 } | 200 } |
| 218 | 201 |
| 219 // Adds an overlay to an element, which is probably a Flash object | 202 // Adds an overlay to an element, which is probably a Flash object |
| 220 function addElementOverlay(elt) { | 203 function addElementOverlay(elt) { |
| 221 var zIndex = "auto"; | 204 var zIndex = "auto"; |
| 222 var position = "absolute"; | 205 var position = "absolute"; |
| 223 | 206 |
| 224 for (var e = elt; e; e = e.parentElement) | 207 for (var e = elt; e; e = e.parentElement) |
| 225 { | 208 { |
| 226 var style = getComputedStyle(e); | 209 var style = getComputedStyle(e); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 263 // elt.parentNode.appendChild(overlay, elt); | 246 // elt.parentNode.appendChild(overlay, elt); |
| 264 document.documentElement.appendChild(overlay); | 247 document.documentElement.appendChild(overlay); |
| 265 return overlay; | 248 return overlay; |
| 266 } | 249 } |
| 267 | 250 |
| 268 // Show dialog asking user whether she wants to add the proposed filters derived | 251 // Show dialog asking user whether she wants to add the proposed filters derived |
| 269 // from selected page element | 252 // from selected page element |
| 270 function clickHide_showDialog(left, top, filters) | 253 function clickHide_showDialog(left, top, filters) |
| 271 { | 254 { |
| 272 // If we are already selecting, abort now | 255 // If we are already selecting, abort now |
| 273 if (clickHide_activated || clickHideFiltersDialog) | 256 if (clickHide_activated) |
| 274 clickHide_rulesPending(); | 257 clickHide_rulesPending(); |
|
kzar
2015/03/04 10:20:21
Nit: Seems like weird mix of camel case with snake
Sebastian Noack
2015/03/04 10:47:09
Yes, function names should be properly camelcase'd
| |
| 275 | 258 |
| 276 clickHide_filters = filters; | 259 clickHide_filters = filters; |
| 277 | 260 |
| 278 clickHideFiltersDialog = document.createElement("iframe"); | 261 clickHideFiltersDialog = document.createElement("iframe"); |
| 279 clickHideFiltersDialog.src = ext.getURL("block.html"); | 262 clickHideFiltersDialog.src = ext.getURL("block.html"); |
| 280 clickHideFiltersDialog.setAttribute("style", "position: fixed !important; visi bility: hidden; display: block !important; border: 0px !important;"); | 263 clickHideFiltersDialog.setAttribute("style", "position: fixed !important; visi bility: hidden; display: block !important; border: 0px !important;"); |
| 281 clickHideFiltersDialog.style.WebkitBoxShadow = "5px 5px 20px rgba(0,0,0,0.5)"; | 264 clickHideFiltersDialog.style.WebkitBoxShadow = "5px 5px 20px rgba(0,0,0,0.5)"; |
| 282 clickHideFiltersDialog.style.zIndex = 0x7FFFFFFF; | 265 clickHideFiltersDialog.style.zIndex = 0x7FFFFFFF; |
| 283 | 266 |
| 284 // Position in upper-left all the time | 267 // Position in upper-left all the time |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 305 function clickHide_activate() { | 288 function clickHide_activate() { |
| 306 if(document == null) | 289 if(document == null) |
| 307 return; | 290 return; |
| 308 | 291 |
| 309 // If we are already selecting, abort now | 292 // If we are already selecting, abort now |
| 310 if (clickHide_activated || clickHideFiltersDialog) | 293 if (clickHide_activated || clickHideFiltersDialog) |
| 311 clickHide_deactivate(); | 294 clickHide_deactivate(); |
| 312 | 295 |
| 313 // Add overlays for blockable elements that don't emit mouse events, | 296 // Add overlays for blockable elements that don't emit mouse events, |
| 314 // so that they can still be selected. | 297 // so that they can still be selected. |
| 315 var elts = document.querySelectorAll('object,embed,iframe,frame'); | 298 [].forEach.call( |
| 316 for(var i=0; i<elts.length; i++) | 299 document.querySelectorAll('object,embed,iframe,frame'), |
| 317 { | 300 function(element) |
| 318 var element = elts[i]; | 301 { |
| 319 if (isBlockable(element)) | 302 getFiltersForElement(element, function(filters) |
| 320 addElementOverlay(element); | 303 { |
| 321 } | 304 if (filters.length > 0) |
| 305 addElementOverlay(element); | |
| 306 }); | |
| 307 } | |
| 308 ); | |
| 322 | 309 |
| 323 clickHide_activated = true; | 310 clickHide_activated = true; |
| 324 document.addEventListener("mousedown", clickHide_stopPropagation, true); | 311 document.addEventListener("mousedown", clickHide_stopPropagation, true); |
| 325 document.addEventListener("mouseup", clickHide_stopPropagation, true); | 312 document.addEventListener("mouseup", clickHide_stopPropagation, true); |
| 326 document.addEventListener("mouseenter", clickHide_stopPropagation, true); | 313 document.addEventListener("mouseenter", clickHide_stopPropagation, true); |
| 327 document.addEventListener("mouseleave", clickHide_stopPropagation, true); | 314 document.addEventListener("mouseleave", clickHide_stopPropagation, true); |
| 328 document.addEventListener("mouseover", clickHide_mouseOver, true); | 315 document.addEventListener("mouseover", clickHide_mouseOver, true); |
| 329 document.addEventListener("mouseout", clickHide_mouseOut, true); | 316 document.addEventListener("mouseout", clickHide_mouseOut, true); |
| 330 document.addEventListener("click", clickHide_mouseClick, true); | 317 document.addEventListener("click", clickHide_mouseClick, true); |
| 331 document.addEventListener("keydown", clickHide_keyDown, true); | 318 document.addEventListener("keydown", clickHide_keyDown, true); |
| 332 | 319 |
| 333 ext.onExtensionUnloaded.addListener(clickHide_deactivate); | 320 ext.onExtensionUnloaded.addListener(clickHide_deactivate); |
| 334 } | 321 } |
| 335 | 322 |
| 336 // Called when user has clicked on something and we are waiting for confirmation | 323 // Called when user has clicked on something and we are waiting for confirmation |
| 337 // on whether the user actually wants these filters | 324 // on whether the user actually wants these filters |
| 338 function clickHide_rulesPending() { | 325 function clickHide_rulesPending() { |
| 339 clickHide_activated = false; | 326 clickHide_activated = false; |
| 340 document.removeEventListener("mousedown", clickHide_stopPropagation, true); | 327 document.removeEventListener("mousedown", clickHide_stopPropagation, true); |
| 341 document.removeEventListener("mouseup", clickHide_stopPropagation, true); | 328 document.removeEventListener("mouseup", clickHide_stopPropagation, true); |
| 342 document.removeEventListener("mouseenter", clickHide_stopPropagation, true); | 329 document.removeEventListener("mouseenter", clickHide_stopPropagation, true); |
| 343 document.removeEventListener("mouseleave", clickHide_stopPropagation, true); | 330 document.removeEventListener("mouseleave", clickHide_stopPropagation, true); |
| 344 document.removeEventListener("mouseover", clickHide_mouseOver, true); | 331 document.removeEventListener("mouseover", clickHide_mouseOver, true); |
| 345 document.removeEventListener("mouseout", clickHide_mouseOut, true); | 332 document.removeEventListener("mouseout", clickHide_mouseOut, true); |
| 346 document.removeEventListener("click", clickHide_mouseClick, true); | 333 document.removeEventListener("click", clickHide_mouseClick, true); |
| 347 document.removeEventListener("keydown", clickHide_keyDown, true); | 334 document.removeEventListener("keydown", clickHide_keyDown, true); |
| 348 } | 335 } |
| 349 | 336 |
| 350 // Turn off click-to-hide | |
|
kzar
2015/03/04 10:20:21
I don't think this comment really adds anything.
Sebastian Noack
2015/03/04 10:47:09
Also not really matter of this change, but fair en
| |
| 351 function clickHide_deactivate() | 337 function clickHide_deactivate() |
| 352 { | 338 { |
| 353 clickHide_rulesPending(); | 339 clickHide_rulesPending(); |
| 354 | 340 |
| 355 if (clickHideFiltersDialog) | 341 if (clickHideFiltersDialog) |
| 356 { | 342 { |
| 357 document.documentElement.removeChild(clickHideFiltersDialog); | 343 document.documentElement.removeChild(clickHideFiltersDialog); |
| 358 clickHideFiltersDialog = null; | 344 clickHideFiltersDialog = null; |
| 359 } | 345 } |
| 360 | 346 |
| 361 clickHide_filters = null; | 347 clickHide_filters = null; |
| 362 lastRightClickEvent = null; | 348 lastRightClickEvent = null; |
| 363 | 349 |
| 364 if (currentElement) { | 350 if (currentElement) |
| 351 { | |
| 365 currentElement.removeEventListener("contextmenu", clickHide_elementClickHan dler, true); | 352 currentElement.removeEventListener("contextmenu", clickHide_elementClickHan dler, true); |
| 366 unhighlightElements(); | 353 unhighlightElements(); |
| 367 unhighlightElement(currentElement); | 354 unhighlightElement(currentElement); |
| 368 currentElement = null; | 355 currentElement = null; |
| 369 } | 356 } |
| 370 unhighlightElements(); | 357 unhighlightElements(); |
| 371 | 358 |
| 372 var overlays = document.getElementsByClassName("__adblockplus__overlay"); | 359 var overlays = document.getElementsByClassName("__adblockplus__overlay"); |
| 373 while (overlays.length > 0) | 360 while (overlays.length > 0) |
| 374 overlays[0].parentNode.removeChild(overlays[0]); | 361 overlays[0].parentNode.removeChild(overlays[0]); |
| 375 | 362 |
| 376 ext.onExtensionUnloaded.removeListener(clickHide_deactivate); | 363 ext.onExtensionUnloaded.removeListener(clickHide_deactivate); |
| 377 } | 364 } |
| 378 | 365 |
| 379 function clickHide_stopPropagation(e) | 366 function clickHide_stopPropagation(e) |
| 380 { | 367 { |
| 381 e.stopPropagation(); | 368 e.stopPropagation(); |
| 382 } | 369 } |
| 383 | 370 |
| 384 function clickHide_elementClickHandler(e) { | 371 function clickHide_elementClickHandler(e) { |
| 385 e.preventDefault(); | 372 e.preventDefault(); |
| 386 e.stopPropagation(); | 373 e.stopPropagation(); |
| 387 clickHide_mouseClick(e); | 374 clickHide_mouseClick(e); |
| 388 } | 375 } |
| 389 | 376 |
| 390 function getBlockableElementOrAncestor(element) | 377 function getBlockableElementOrAncestor(element, callback) |
| 391 { | 378 { |
| 379 // We assume that the user doesn't want to block the whole page. | |
| 380 // So we never consider the <html> or <body> element. | |
| 392 while (element && element != document.documentElement | 381 while (element && element != document.documentElement |
| 393 && element != document.body) | 382 && element != document.body) |
| 394 { | 383 { |
| 395 if (element instanceof HTMLElement && element.localName != "area") | 384 // We can't handle non-HTML (like SVG) elements, as well as |
| 396 { | 385 // <area> elements (see below). So fall back to the parent element. |
| 397 // Handle <area> and their <map> elements specially, | 386 if (!(element instanceof HTMLElement) || element.localName == "area") |
| 398 // blocking the image they are associated with | 387 element = element.parentElement; |
| 399 if (element.localName == "map") | 388 |
| 389 // If image maps are used mouse events occur for the <area> element. | |
| 390 // But we have to block the image associated with the <map> element. | |
| 391 else if (element.localName == "map") | |
| 392 { | |
| 393 var images = document.querySelectorAll("img[usemap]"); | |
| 394 var image = null; | |
| 395 | |
| 396 for (var i = 0; i < images.length; i++) | |
| 400 { | 397 { |
| 401 var images = document.querySelectorAll("img[usemap]"); | 398 var usemap = image.getAttribute("usemap"); |
| 402 for (var i = 0; i < images.length; i++) | 399 var index = usemap.indexOf("#"); |
| 400 | |
| 401 if (index != -1 && usemap.substr(index + 1) == element.name) | |
| 403 { | 402 { |
| 404 var image = images[i]; | 403 image = images[i]; |
| 405 var usemap = image.getAttribute("usemap"); | 404 break; |
| 406 var index = usemap.indexOf("#"); | |
| 407 | |
| 408 if (index != -1 && usemap.substr(index + 1) == element.name) | |
| 409 return getBlockableElementOrAncestor(image); | |
| 410 } | 405 } |
| 411 | |
| 412 return null; | |
| 413 } | 406 } |
| 414 | 407 |
| 415 if (isBlockable(element)) | 408 element = image; |
| 416 return element; | 409 } |
| 417 } | 410 |
| 418 | 411 // Finally, if none of the above is true, check whether we can generate |
| 419 element = element.parentElement; | 412 // any filters for this element. Otherwise fall back to its parent element. |
| 420 } | 413 else |
| 421 | 414 { |
| 422 return null; | 415 getFiltersForElement(element, function(filters) |
| 416 { | |
| 417 if (filters.length > 0) | |
| 418 callback(element); | |
| 419 else | |
| 420 getBlockableElementOrAncestor(element.parentElement, callback); | |
| 421 }); | |
| 422 | |
| 423 return; | |
| 424 } | |
| 425 } | |
| 426 | |
| 427 // We reached the document root without finding a blockable element. | |
| 428 callback(null); | |
| 423 } | 429 } |
| 424 | 430 |
| 425 // Hovering over an element so highlight it | 431 // Hovering over an element so highlight it |
| 426 function clickHide_mouseOver(e) | 432 function clickHide_mouseOver(e) |
| 427 { | 433 { |
| 428 if (clickHide_activated == false) | 434 lastMouseOverEvent = e; |
| 429 return; | 435 |
| 430 | 436 getBlockableElementOrAncestor(e.target, function(element) |
| 431 var target = getBlockableElementOrAncestor(e.target); | 437 { |
| 432 | 438 if (e == lastMouseOverEvent) |
| 433 if (target) | 439 { |
| 434 { | 440 lastMouseOverEvent = null; |
| 435 currentElement = target; | 441 |
| 436 | 442 if (clickHide_activated) |
| 437 highlightElement(target, "#d6d84b", "#f8fa47"); | 443 { |
| 438 target.addEventListener("contextmenu", clickHide_elementClickHandler, true); | 444 if (currentElement) |
| 439 } | 445 unhighlightElement(currentElement); |
| 446 | |
| 447 if (element) | |
| 448 { | |
| 449 highlightElement(element, "#d6d84b", "#f8fa47"); | |
| 450 element.addEventListener("contextmenu", clickHide_elementClickHandler, true); | |
| 451 } | |
| 452 | |
| 453 currentElement = element; | |
| 454 } | |
| 455 } | |
| 456 }); | |
| 457 | |
| 440 e.stopPropagation(); | 458 e.stopPropagation(); |
| 441 } | 459 } |
| 442 | 460 |
| 443 // No longer hovering over this element so unhighlight it | 461 // No longer hovering over this element so unhighlight it |
| 444 function clickHide_mouseOut(e) | 462 function clickHide_mouseOut(e) |
| 445 { | 463 { |
| 446 if (!clickHide_activated || !currentElement) | 464 if (!clickHide_activated || currentElement != e.target) |
| 447 return; | 465 return; |
| 448 | 466 |
| 449 unhighlightElement(currentElement); | 467 unhighlightElement(currentElement); |
| 450 currentElement.removeEventListener("contextmenu", clickHide_elementClickHandle r, true); | 468 currentElement.removeEventListener("contextmenu", clickHide_elementClickHandle r, true); |
| 451 e.stopPropagation(); | 469 e.stopPropagation(); |
| 452 } | 470 } |
| 453 | 471 |
| 454 // Selects the currently hovered-over filter or cancels selection | 472 // Selects the currently hovered-over filter or cancels selection |
| 455 function clickHide_keyDown(e) | 473 function clickHide_keyDown(e) |
| 456 { | 474 { |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 475 { | 493 { |
| 476 ext.backgroundPage.sendMessage( | 494 ext.backgroundPage.sendMessage( |
| 477 { | 495 { |
| 478 type: "compose-filters", | 496 type: "compose-filters", |
| 479 tagName: element.localName, | 497 tagName: element.localName, |
| 480 id: element.id, | 498 id: element.id, |
| 481 src: element.getAttribute("src"), | 499 src: element.getAttribute("src"), |
| 482 style: element.getAttribute("style"), | 500 style: element.getAttribute("style"), |
| 483 classes: [].slice.call(element.classList), | 501 classes: [].slice.call(element.classList), |
| 484 urls: getURLsFromElement(element), | 502 urls: getURLsFromElement(element), |
| 503 mediatype: typeMap[element.localName], | |
| 485 baseURL: document.location.href | 504 baseURL: document.location.href |
| 486 }, | 505 }, |
| 487 function(response) | 506 function(response) |
| 488 { | 507 { |
| 489 callback(response.filters, response.selectors); | 508 callback(response.filters, response.selectors); |
| 490 } | 509 } |
| 491 ); | 510 ); |
| 492 } | 511 } |
| 493 | 512 |
| 494 // When the user clicks, the currentElement is the one we want. | 513 // When the user clicks, the currentElement is the one we want. |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 653 break; | 672 break; |
| 654 case "clickhide-activate": | 673 case "clickhide-activate": |
| 655 clickHide_activate(); | 674 clickHide_activate(); |
| 656 break; | 675 break; |
| 657 case "clickhide-deactivate": | 676 case "clickhide-deactivate": |
| 658 clickHide_deactivate(); | 677 clickHide_deactivate(); |
| 659 break; | 678 break; |
| 660 case "clickhide-new-filter": | 679 case "clickhide-new-filter": |
| 661 if(lastRightClickEvent) | 680 if(lastRightClickEvent) |
| 662 { | 681 { |
| 663 clickHide_activated = true; | 682 getBlockableElementOrAncestor(lastRightClickEvent.target, function(ele ment) |
| 664 currentElement = getBlockableElementOrAncestor(lastRightClickEvent.tar get); | 683 { |
| 665 clickHide_mouseClick(lastRightClickEvent); | 684 clickHide_activated = true; |
| 685 currentElement = element; | |
| 686 clickHide_mouseClick(lastRightClickEvent); | |
| 687 }); | |
| 666 } | 688 } |
| 667 break; | 689 break; |
| 668 case "clickhide-init": | 690 case "clickhide-init": |
| 669 if (clickHideFiltersDialog) | 691 if (clickHideFiltersDialog) |
| 670 { | 692 { |
| 671 sendResponse({filters: clickHide_filters}); | 693 sendResponse({filters: clickHide_filters}); |
| 672 | 694 |
| 673 clickHideFiltersDialog.style.width = msg.width + "px"; | 695 clickHideFiltersDialog.style.width = msg.width + "px"; |
| 674 clickHideFiltersDialog.style.height = msg.height + "px"; | 696 clickHideFiltersDialog.style.height = msg.height + "px"; |
| 675 clickHideFiltersDialog.style.visibility = "visible"; | 697 clickHideFiltersDialog.style.visibility = "visible"; |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 703 lastRightClickEventValid = false; | 725 lastRightClickEventValid = false; |
| 704 else | 726 else |
| 705 lastRightClickEvent = null; | 727 lastRightClickEvent = null; |
| 706 break; | 728 break; |
| 707 } | 729 } |
| 708 }); | 730 }); |
| 709 | 731 |
| 710 if (window == window.top) | 732 if (window == window.top) |
| 711 ext.backgroundPage.sendMessage({type: "report-html-page"}); | 733 ext.backgroundPage.sendMessage({type: "report-html-page"}); |
| 712 } | 734 } |
| LEFT | RIGHT |