Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: include.postload.js

Issue 29336084: Issue 2426 - Open block.html as a popup window (Closed)
Patch Set: Reduce callback checking boilerplate Created Feb. 15, 2016, 3:45 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ext/background.js ('k') | lib/devtools.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2016 Eyeo GmbH
4 *
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
7 * published by the Free Software Foundation.
8 *
9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
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/>.
16 */
17
18 // Click-to-hide stuff
19 var clickHide_activated = false;
20 var clickHide_filters = null;
21 var currentElement = null;
22 var highlightedElementsSelector = null;
23 var highlightedElementsInterval = null;
24 var clickHideFiltersDialog = null;
25 var lastRightClickEvent = null;
26 var lastRightClickEventValid = false;
27 var lastMouseOverEvent = null;
28
29 function highlightElement(element, shadowColor, backgroundColor)
30 {
31 unhighlightElement(element);
32
33 var highlightWithOverlay = function()
34 {
35 var overlay = addElementOverlay(element);
36
37 // If the element isn't displayed no overlay will be added.
38 // Moreover, we don't need to highlight anything then.
39 if (!overlay)
40 return;
41
42 highlightElement(overlay, shadowColor, backgroundColor);
43 overlay.style.pointerEvents = "none";
44
45 element._unhighlight = function()
46 {
47 overlay.parentNode.removeChild(overlay);
48 };
49 };
50
51 var highlightWithStyleAttribute = function()
52 {
53 var originalBoxShadow = element.style.getPropertyValue("box-shadow");
54 var originalBoxShadowPriority = element.style.getPropertyPriority("box-shado w");
55 var originalBackgroundColor = element.style.getPropertyValue("background-col or");
56 var originalBackgroundColorPriority = element.style.getPropertyPriority("bac kground-color");
57
58 element.style.setProperty("box-shadow", "inset 0px 0px 5px " + shadowColor, "important");
59 element.style.setProperty("background-color", backgroundColor, "important");
60
61 element._unhighlight = function()
62 {
63 this.style.removeProperty("box-shadow");
64 this.style.setProperty(
65 "box-shadow",
66 originalBoxShadow,
67 originalBoxShadowPriority
68 );
69
70 this.style.removeProperty("background-color");
71 this.style.setProperty(
72 "background-color",
73 originalBackgroundColor,
74 originalBackgroundColorPriority
75 );
76 };
77 };
78
79 if ("prisoner" in element)
80 highlightWithStyleAttribute();
81 else
82 highlightWithOverlay();
83 }
84
85
86 function unhighlightElement(element)
87 {
88 if ("_unhighlight" in element)
89 {
90 element._unhighlight();
91 delete element._unhighlight;
92 }
93 }
94
95 // Highlight elements according to selector string. This would include
96 // all elements that would be affected by proposed filters.
97 function highlightElements(selectorString) {
98 unhighlightElements();
99
100 var elements = Array.prototype.slice.call(document.querySelectorAll(selectorSt ring));
101 highlightedElementsSelector = selectorString;
102
103 // Highlight elements progressively. Otherwise the page freezes
104 // when a lot of elements get highlighted at the same time.
105 highlightedElementsInterval = setInterval(function()
106 {
107 if (elements.length > 0)
108 {
109 var element = elements.shift();
110 if (element != currentElement)
111 highlightElement(element, "#fd6738", "#f6e1e5");
112 }
113 else
114 {
115 clearInterval(highlightedElementsInterval);
116 highlightedElementsInterval = null;
117 }
118 }, 0);
119 }
120
121 // Unhighlight all elements, including those that would be affected by
122 // the proposed filters
123 function unhighlightElements() {
124 if (highlightedElementsInterval)
125 {
126 clearInterval(highlightedElementsInterval)
127 highlightedElementsInterval = null;
128 }
129
130 if (highlightedElementsSelector)
131 {
132 Array.prototype.forEach.call(
133 document.querySelectorAll(highlightedElementsSelector),
134 unhighlightElement
135 );
136
137 highlightedElementsSelector = null;
138 }
139 }
140
141 // Adds an overlay to an element, which is probably a Flash object
142 function addElementOverlay(elt) {
143 var position = "absolute";
144 var offsetX = window.scrollX;
145 var offsetY = window.scrollY;
146
147 for (var e = elt; e; e = e.parentElement)
148 {
149 var style = getComputedStyle(e);
150
151 // If the element isn't rendered (since its or one of its ancestor's
152 // "display" property is "none"), the overlay wouldn't match the element.
153 if (style.display == "none")
154 return null;
155
156 // If the element or one of its ancestors uses fixed postioning, the overlay
157 // has to use fixed postioning too. Otherwise it might not match the element .
158 if (style.position == "fixed")
159 {
160 position = "fixed";
161 offsetX = offsetY = 0;
162 }
163 }
164
165 var overlay = document.createElement('div');
166 overlay.prisoner = elt;
167 overlay.className = "__adblockplus__overlay";
168 overlay.setAttribute('style', 'opacity:0.4; display:inline-box; overflow:hidde n; box-sizing:border-box;');
169 var rect = elt.getBoundingClientRect();
170 overlay.style.width = rect.width + "px";
171 overlay.style.height = rect.height + "px";
172 overlay.style.left = (rect.left + offsetX) + "px";
173 overlay.style.top = (rect.top + offsetY) + "px";
174 overlay.style.position = position;
175 overlay.style.zIndex = 0x7FFFFFFE;
176
177 // elt.parentNode.appendChild(overlay, elt);
178 document.documentElement.appendChild(overlay);
179 return overlay;
180 }
181
182 // Show dialog asking user whether she wants to add the proposed filters derived
183 // from selected page element
184 function clickHide_showDialog(filters)
185 {
186 clickHide_filters = filters;
187
188 clickHideFiltersDialog = document.createElement("iframe");
189 clickHideFiltersDialog.src = ext.getURL("block.html");
190 clickHideFiltersDialog.setAttribute("style", "position: fixed !important; visi bility: hidden; display: block !important; border: 0px !important;");
191 clickHideFiltersDialog.style.WebkitBoxShadow = "5px 5px 20px rgba(0,0,0,0.5)";
192 clickHideFiltersDialog.style.zIndex = 0x7FFFFFFF;
193
194 // Position in upper-left all the time
195 clickHideFiltersDialog.style.left = "50px";
196 clickHideFiltersDialog.style.top = "50px";
197
198 // Make dialog partly transparent when mouse isn't over it so user has a bette r
199 // view of what's going to be blocked
200 clickHideFiltersDialog.onmouseout = function()
201 {
202 if (clickHideFiltersDialog)
203 clickHideFiltersDialog.style.setProperty("opacity", "0.7");
204 };
205 clickHideFiltersDialog.onmouseover = function()
206 {
207 if (clickHideFiltersDialog)
208 clickHideFiltersDialog.style.setProperty("opacity", "1.0");
209 };
210
211 document.documentElement.appendChild(clickHideFiltersDialog);
212 }
213
214 // Turn on the choose element to create filter thing
215 function clickHide_activate() {
216 if(document == null)
217 return;
218
219 // If we are already selecting, abort now
220 if (clickHide_activated || clickHideFiltersDialog)
221 clickHide_deactivate();
222
223 // Add overlays for blockable elements that don't emit mouse events,
224 // so that they can still be selected.
225 [].forEach.call(
226 document.querySelectorAll('object,embed,iframe,frame'),
227 function(element)
228 {
229 getFiltersForElement(element, function(filters)
230 {
231 if (filters.length > 0)
232 addElementOverlay(element);
233 });
234 }
235 );
236
237 clickHide_activated = true;
238 document.addEventListener("mousedown", clickHide_stopPropagation, true);
239 document.addEventListener("mouseup", clickHide_stopPropagation, true);
240 document.addEventListener("mouseenter", clickHide_stopPropagation, true);
241 document.addEventListener("mouseleave", clickHide_stopPropagation, true);
242 document.addEventListener("mouseover", clickHide_mouseOver, true);
243 document.addEventListener("mouseout", clickHide_mouseOut, true);
244 document.addEventListener("click", clickHide_mouseClick, true);
245 document.addEventListener("keydown", clickHide_keyDown, true);
246
247 ext.onExtensionUnloaded.addListener(clickHide_deactivate);
248 }
249
250 // Called when user has clicked on something and we are waiting for confirmation
251 // on whether the user actually wants these filters
252 function clickHide_rulesPending() {
253 clickHide_activated = false;
254
255 if (clickHideFiltersDialog)
256 {
257 document.documentElement.removeChild(clickHideFiltersDialog);
258 clickHideFiltersDialog = null;
259 }
260
261 document.removeEventListener("mousedown", clickHide_stopPropagation, true);
262 document.removeEventListener("mouseup", clickHide_stopPropagation, true);
263 document.removeEventListener("mouseenter", clickHide_stopPropagation, true);
264 document.removeEventListener("mouseleave", clickHide_stopPropagation, true);
265 document.removeEventListener("mouseover", clickHide_mouseOver, true);
266 document.removeEventListener("mouseout", clickHide_mouseOut, true);
267 document.removeEventListener("click", clickHide_mouseClick, true);
268 document.removeEventListener("keydown", clickHide_keyDown, true);
269 }
270
271 function clickHide_deactivate()
272 {
273 clickHide_rulesPending();
274
275 clickHide_filters = null;
276 lastRightClickEvent = null;
277
278 if (currentElement)
279 {
280 currentElement.removeEventListener("contextmenu", clickHide_elementClickHan dler, true);
281 unhighlightElement(currentElement);
282 currentElement = null;
283 }
284 unhighlightElements();
285
286 var overlays = document.getElementsByClassName("__adblockplus__overlay");
287 while (overlays.length > 0)
288 overlays[0].parentNode.removeChild(overlays[0]);
289
290 ext.onExtensionUnloaded.removeListener(clickHide_deactivate);
291 }
292
293 function clickHide_stopPropagation(e)
294 {
295 e.stopPropagation();
296 }
297
298 function clickHide_elementClickHandler(e) {
299 e.preventDefault();
300 e.stopPropagation();
301 clickHide_mouseClick(e);
302 }
303
304 function getBlockableElementOrAncestor(element, callback)
305 {
306 // We assume that the user doesn't want to block the whole page.
307 // So we never consider the <html> or <body> element.
308 while (element && element != document.documentElement
309 && element != document.body)
310 {
311 // We can't handle non-HTML (like SVG) elements, as well as
312 // <area> elements (see below). So fall back to the parent element.
313 if (!(element instanceof HTMLElement) || element.localName == "area")
314 element = element.parentElement;
315
316 // If image maps are used mouse events occur for the <area> element.
317 // But we have to block the image associated with the <map> element.
318 else if (element.localName == "map")
319 {
320 var images = document.querySelectorAll("img[usemap]");
321 var image = null;
322
323 for (var i = 0; i < images.length; i++)
324 {
325 var usemap = images[i].getAttribute("usemap");
326 var index = usemap.indexOf("#");
327
328 if (index != -1 && usemap.substr(index + 1) == element.name)
329 {
330 image = images[i];
331 break;
332 }
333 }
334
335 element = image;
336 }
337
338 // Finally, if none of the above is true, check whether we can generate
339 // any filters for this element. Otherwise fall back to its parent element.
340 else
341 {
342 getFiltersForElement(element, function(filters)
343 {
344 if (filters.length > 0)
345 callback(element);
346 else
347 getBlockableElementOrAncestor(element.parentElement, callback);
348 });
349
350 return;
351 }
352 }
353
354 // We reached the document root without finding a blockable element.
355 callback(null);
356 }
357
358 // Hovering over an element so highlight it
359 function clickHide_mouseOver(e)
360 {
361 lastMouseOverEvent = e;
362
363 getBlockableElementOrAncestor(e.target, function(element)
364 {
365 if (e == lastMouseOverEvent)
366 {
367 lastMouseOverEvent = null;
368
369 if (clickHide_activated)
370 {
371 if (currentElement)
372 unhighlightElement(currentElement);
373
374 if (element)
375 {
376 highlightElement(element, "#d6d84b", "#f8fa47");
377 element.addEventListener("contextmenu", clickHide_elementClickHandler, true);
378 }
379
380 currentElement = element;
381 }
382 }
383 });
384
385 e.stopPropagation();
386 }
387
388 // No longer hovering over this element so unhighlight it
389 function clickHide_mouseOut(e)
390 {
391 if (!clickHide_activated || currentElement != e.target)
392 return;
393
394 unhighlightElement(currentElement);
395 currentElement.removeEventListener("contextmenu", clickHide_elementClickHandle r, true);
396 e.stopPropagation();
397 }
398
399 // Selects the currently hovered-over filter or cancels selection
400 function clickHide_keyDown(e)
401 {
402 if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 13 /*DOM_VK_RETURN* /)
403 clickHide_mouseClick(e);
404 else if (!e.ctrlKey && !e.altKey && !e.shiftKey && e.keyCode == 27 /*DOM_VK_ES CAPE*/)
405 {
406 ext.backgroundPage.sendMessage(
407 {
408 type: "forward",
409 payload:
410 {
411 type: "clickhide-deactivate"
412 }
413 });
414 e.preventDefault();
415 e.stopPropagation();
416 }
417 }
418
419 function getFiltersForElement(element, callback)
420 {
421 ext.backgroundPage.sendMessage(
422 {
423 type: "compose-filters",
424 tagName: element.localName,
425 id: element.id,
426 src: element.getAttribute("src"),
427 style: element.getAttribute("style"),
428 classes: [].slice.call(element.classList),
429 urls: getURLsFromElement(element),
430 mediatype: typeMap[element.localName],
431 baseURL: document.location.href
432 },
433 function(response)
434 {
435 callback(response.filters, response.selectors);
436 }
437 );
438 }
439
440 // When the user clicks, the currentElement is the one we want.
441 // We should have ABP rules ready for when the
442 // popup asks for them.
443 function clickHide_mouseClick(e)
444 {
445 if (!currentElement || !clickHide_activated)
446 return;
447
448 var elt = currentElement;
449 if (currentElement.classList.contains("__adblockplus__overlay"))
450 elt = currentElement.prisoner;
451
452 getFiltersForElement(elt, function(filters, selectors)
453 {
454 ext.backgroundPage.sendMessage(
455 {
456 type: "forward",
457 payload:
458 {
459 type: "clickhide-show-dialog",
460 clickHideFilters: filters
461 }
462 });
463
464 if (selectors.length > 0)
465 highlightElements(selectors.join(","));
466
467 highlightElement(currentElement, "#fd1708", "#f6a1b5");
468 });
469
470 // Make sure the browser doesn't handle this click
471 e.preventDefault();
472 e.stopPropagation();
473 }
474
475 // This function Copyright (c) 2008 Jeni Tennison, from jquery.uri.js
476 // and licensed under the MIT license. See jquery-*.min.js for details.
477 function removeDotSegments(u) {
478 var r = '', m = [];
479 if (/\./.test(u)) {
480 while (u !== undefined && u !== '') {
481 if (u === '.' || u === '..') {
482 u = '';
483 } else if (/^\.\.\//.test(u)) { // starts with ../
484 u = u.substring(3);
485 } else if (/^\.\//.test(u)) { // starts with ./
486 u = u.substring(2);
487 } else if (/^\/\.(\/|$)/.test(u)) { // starts with /./ or consists of /.
488 u = '/' + u.substring(3);
489 } else if (/^\/\.\.(\/|$)/.test(u)) { // starts with /../ or consists of / ..
490 u = '/' + u.substring(4);
491 r = r.replace(/\/?[^\/]+$/, '');
492 } else {
493 m = u.match(/^(\/?[^\/]*)(\/.*)?$/);
494 u = m[2];
495 r = r + m[1];
496 }
497 }
498 return r;
499 } else {
500 return u;
501 }
502 }
503
504 // In Chrome 37-40, the document_end content script (this one) runs properly, wh ile the
505 // document_start content scripts (that defines ext) might not. Check whether va riable ext
506 // exists before continuing to avoid "Uncaught ReferenceError: ext is not define d".
507 // See https://crbug.com/416907
508 if ("ext" in window && document instanceof HTMLDocument)
509 {
510 // Use a contextmenu handler to save the last element the user right-clicked o n.
511 // To make things easier, we actually save the DOM event.
512 // We have to do this because the contextMenu API only provides a URL, not the actual
513 // DOM element.
514 document.addEventListener('contextmenu', function(e)
515 {
516 lastRightClickEvent = e;
517 // We also need to ensure any old lastRightClickEvent variables in other
518 // frames are cleared.
519 lastRightClickEventValid = true;
520 ext.backgroundPage.sendMessage(
521 {
522 type: "forward",
523 payload:
524 {
525 type: "clickhide-clear-last-right-click-event"
526 }
527 });
528 }, true);
529
530 document.addEventListener("click", function(event)
531 {
532 // Ignore right-clicks
533 if (event.button == 2)
534 return;
535
536 // Search the link associated with the click
537 var link = event.target;
538 while (!(link instanceof HTMLAnchorElement))
539 {
540 link = link.parentNode;
541
542 if (!link)
543 return;
544 }
545
546 if (link.protocol == "http:" || link.protocol == "https:")
547 {
548 if (link.host != "subscribe.adblockplus.org" || link.pathname != "/")
549 return;
550 }
551 else if (!/^abp:\/*subscribe\/*\?/i.test(link.href))
552 return;
553
554 // This is our link - make sure the browser doesn't handle it
555 event.preventDefault();
556 event.stopPropagation();
557
558 // Decode URL parameters
559 var params = link.search.substr(1).split("&");
560 var title = null;
561 var url = null;
562 for (var i = 0; i < params.length; i++)
563 {
564 var parts = params[i].split("=", 2);
565 if (parts.length != 2 || !/\S/.test(parts[1]))
566 continue;
567 switch (parts[0])
568 {
569 case "title":
570 title = decodeURIComponent(parts[1]);
571 break;
572 case "location":
573 url = decodeURIComponent(parts[1]);
574 break;
575 }
576 }
577 if (!url)
578 return;
579
580 // Default title to the URL
581 if (!title)
582 title = url;
583
584 // Trim spaces in title and URL
585 title = title.trim();
586 url = url.trim();
587 if (!/^(https?|ftp):/.test(url))
588 return;
589
590 ext.backgroundPage.sendMessage({
591 type: "add-subscription",
592 title: title,
593 url: url
594 });
595 }, true);
596
597 ext.onMessage.addListener(function(msg, sender, sendResponse)
598 {
599 switch (msg.type)
600 {
601 case "get-clickhide-state":
602 sendResponse({active: clickHide_activated});
603 break;
604 case "clickhide-activate":
605 clickHide_activate();
606 break;
607 case "clickhide-deactivate":
608 clickHide_deactivate();
609 break;
610 case "clickhide-new-filter":
611 if(lastRightClickEvent)
612 {
613 var event = lastRightClickEvent;
614 getBlockableElementOrAncestor(event.target, function(element)
615 {
616 clickHide_activate();
617 currentElement = element;
618 clickHide_mouseClick(event);
619 });
620 }
621 break;
622 case "clickhide-init":
623 if (clickHideFiltersDialog)
624 {
625 sendResponse({filters: clickHide_filters});
626
627 clickHideFiltersDialog.style.width = msg.width + "px";
628 clickHideFiltersDialog.style.height = msg.height + "px";
629 clickHideFiltersDialog.style.visibility = "visible";
630 }
631 break;
632 case "clickhide-move":
633 if (clickHideFiltersDialog)
634 {
635 var rect = clickHideFiltersDialog.getBoundingClientRect();
636 var x = Math.max(0, Math.min(rect.left + msg.x, window.innerWidth - re ct.width));
637 var y = Math.max(0, Math.min(rect.top + msg.y, window.innerHeight - re ct.height));
638
639 clickHideFiltersDialog.style.left = x + "px";
640 clickHideFiltersDialog.style.top = y + "px";
641 }
642 break;
643 case "clickhide-close":
644 if (currentElement && msg.remove)
645 {
646 // Hide the selected element itself if an added blocking
647 // filter is causing it to collapse. Note that this
648 // behavior is incomplete, but the best we can do here,
649 // e.g. if an added blocking filter matches other elements,
650 // the effect won't be visible until the page is is reloaded.
651 checkCollapse(currentElement.prisoner || currentElement);
652
653 // Apply added element hiding filters.
654 updateStylesheet();
655 }
656 clickHide_deactivate();
657 break;
658 case "clickhide-show-dialog":
659 clickHide_rulesPending();
660 if (window.self == window.top)
661 clickHide_showDialog(msg.clickHideFilters);
662 break;
663 case "clickhide-clear-last-right-click-event":
664 if (lastRightClickEventValid)
665 lastRightClickEventValid = false;
666 else
667 lastRightClickEvent = null;
668 break;
669 }
670 });
671
672 if (window == window.top)
673 ext.backgroundPage.sendMessage({type: "report-html-page"});
674 }
OLDNEW
« no previous file with comments | « ext/background.js ('k') | lib/devtools.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld