1
/* ***** BEGIN LICENSE BLOCK *****
4
* The contents of this file are subject to the Mozilla Public License Version
5
* 1.1 (the "License"); you may not use this file except in compliance with
6
* the License. You may obtain a copy of the License at
7
* http://www.mozilla.org/MPL/
9
* Software distributed under the License is distributed on an "AS IS" basis,
10
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11
* for the specific language governing rights and limitations under the
14
* The Original Code is Adblock Plus.
16
* The Initial Developer of the Original Code is
18
* Portions created by the Initial Developer are Copyright (C) 2006-2010
19
* the Initial Developer. All Rights Reserved.
23
* ***** END LICENSE BLOCK ***** */
26
* @fileOverview Application integration module, will keep track of application
27
* windows and handle the necessary events.
30
var EXPORTED_SYMBOLS = ["AppIntegration"];
32
const Cc = Components.classes;
33
const Ci = Components.interfaces;
34
const Cr = Components.results;
35
const Cu = Components.utils;
37
let baseURL = Cc["@adblockplus.org/abp/private;1"].getService(Ci.nsIURI);
39
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
40
Cu.import(baseURL.spec + "Utils.jsm");
41
Cu.import(baseURL.spec + "Prefs.jsm");
42
Cu.import(baseURL.spec + "ContentPolicy.jsm");
43
Cu.import(baseURL.spec + "FilterStorage.jsm");
44
Cu.import(baseURL.spec + "FilterClasses.jsm");
45
Cu.import(baseURL.spec + "SubscriptionClasses.jsm");
46
Cu.import(baseURL.spec + "RequestNotifier.jsm");
49
* Flag used to trigger special behavior for Fennec.
52
let isFennec = (Utils.appID == "{a23983c0-fd0e-11dc-95ff-0800200c9a66}");
54
Cu.import(baseURL.spec + "AppIntegrationFennec.jsm");
57
* Wrappers for tracked application windows.
58
* @type Array of WindowWrapper
63
* Stores the current value of showintoolbar preference (to detect changes).
65
let currentlyShowingInToolbar = Prefs.showintoolbar;
68
* Initializes app integration module
72
// Process preferences
75
// Listen for pref and filters changes
76
Prefs.addListener(function(name)
78
if (name == "enabled" || name == "showintoolbar" || name == "showinstatusbar" || name == "defaulttoolbaraction" || name == "defaultstatusbaraction")
81
FilterStorage.addFilterObserver(reloadPrefs);
82
FilterStorage.addSubscriptionObserver(reloadPrefs);
86
* Exported app integration functions.
92
* Adds an application window to the tracked list.
94
addWindow: function(/**Window*/ window)
96
let hooks = window.document.getElementById("abp-hooks");
100
// Execute first-run actions
101
if (!("lastVersion" in Prefs))
103
Prefs.lastVersion = Prefs.currentVersion;
105
// Show subscriptions dialog if the user doesn't have any subscriptions yet
106
if (Prefs.currentVersion != Utils.addonVersion)
108
Prefs.currentVersion = Utils.addonVersion;
110
if ("nsISessionStore" in Ci)
112
// Have to wait for session to be restored
115
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
116
observe: function(subject, topic, data)
118
observerService.removeObserver(observer, "sessionstore-windows-restored");
125
let observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
126
observerService.addObserver(observer, "sessionstore-windows-restored", false);
128
// Just in case, don't wait more than a second
129
let timer = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer);
130
timer.init(observer, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
133
Utils.runAsync(showSubscriptions);
137
let wrapper = new WindowWrapper(window, hooks);
138
wrappers.push(wrapper);
142
* Retrieves the wrapper object corresponding to a particular application window.
144
getWrapperForWindow: function(/**Window*/ wnd) /**WindowWrapper*/
146
for each (let wrapper in wrappers)
147
if (wrapper.window == wnd)
154
* Toggles the value of a boolean preference.
156
togglePref: function(/**String*/ pref)
158
Prefs[pref] = !Prefs[pref];
162
* If the given filter is already in user's list, removes it from the list. Otherwise adds it.
164
toggleFilter: function(/**Filter*/ filter)
166
if (filter.subscriptions.length)
168
if (filter.disabled || filter.subscriptions.some(function(subscription) !(subscription instanceof SpecialSubscription)))
170
filter.disabled = !filter.disabled;
171
FilterStorage.triggerFilterObservers(filter.disabled ? "disable" : "enable", [filter]);
174
FilterStorage.removeFilter(filter);
177
FilterStorage.addFilter(filter);
178
FilterStorage.saveToDisk();
183
* Removes an application window from the tracked list.
185
function removeWindow()
189
for (let i = 0; i < wrappers.length; i++)
190
if (wrappers[i].window == wnd)
191
wrappers.splice(i--, 1);
195
* Class providing various functions related to application windows.
198
function WindowWrapper(window, hooks)
200
this.window = window;
202
this.initializeHooks(hooks);
206
this.configureKeys();
207
this.initContextMenu();
210
// Some people actually switch off browser.frames.enabled and are surprised
211
// that things stop working...
212
window.QueryInterface(Ci.nsIInterfaceRequestor)
213
.getInterface(Ci.nsIWebNavigation)
214
.QueryInterface(Ci.nsIDocShell)
215
.allowSubframes = true;
217
this.registerEventListeners(!isFennec);
218
this.executeFirstRunActions();
220
// Custom initialization for Fennec
222
AppIntegrationFennec.initWindow(this);
224
WindowWrapper.prototype =
227
* Application window this object belongs to.
233
* Current state as displayed for this window.
239
* Methods that can be defined at attributes of the hooks element.
240
* @type Array of String
242
customMethods: ["getBrowser", "addTab", "getContextMenu", "getToolbox", "getDefaultToolbar", "toolbarInsertBefore", "unhideToolbar"],
245
* Progress listener used to watch for location changes, if any.
246
* @type nsIProgressListener
248
progressListener: null,
251
* Filter corresponding with "disable on site" menu item (set in fillPopup()).
256
* Filter corresponding with "disable on site" menu item (set in fillPopup()).
262
* Data associated with the node currently under mouse pointer (set in updateContextMenu()).
267
* The document node that nodeData belongs to.
271
* Data associated with the background image currently under mouse pointer (set in updateContextMenu()).
274
backgroundData: null,
276
* Data associated with the frame currently under mouse pointer (set in updateContextMenu()).
281
* The frame that frameData belongs to.
286
* Window of the detached list of blockable items (might be null or closed).
289
detachedSidebar: null,
292
* Binds a function to the object, ensuring that "this" pointer is always set
295
_bindMethod: function(/**Function*/ method) /**Function*/
298
return function() method.apply(me, arguments);
302
* Retrieves an element by its ID.
304
E: function(/**String*/ id)
306
let doc = this.window.document;
307
this.E = function(id) doc.getElementById(id);
312
* Initializes abp-hooks element, converts any function attributes to actual
315
initializeHooks: function(hooks)
317
for each (let hook in this.customMethods)
319
let handler = hooks.getAttribute(hook);
320
this[hook] = hooks[hook] = (handler ? this._bindMethod(new Function(handler)) : null);
325
* Makes a copy of the ABP icon's context menu for the toolbar button.
327
fixupMenus: function()
331
if (node.nodeType == node.ELEMENT_NODE)
333
if (node.hasAttribute("id"))
334
node.setAttribute("id", node.getAttribute("id").replace(/abp-status/, "abp-toolbar"));
336
for (let i = 0, len = node.childNodes.length; i < len; i++)
337
fixId(node.childNodes[i]);
342
let menuSource = this.E("abp-status-popup");
343
let paletteButton = this.getPaletteButton();
344
let toolbarButton = this.E("abp-toolbarbutton");
346
toolbarButton.appendChild(fixId(menuSource.cloneNode(true)));
347
if (paletteButton && paletteButton != toolbarButton)
348
paletteButton.appendChild(fixId(menuSource.cloneNode(true)));
352
* Attaches event listeners to a window represented by hooks element
354
registerEventListeners: function(/**Boolean*/ addProgressListener)
356
// Palette button elements aren't reachable by ID, create a lookup table
357
let paletteButtonIDs = {};
358
let paletteButton = this.getPaletteButton();
361
function getElementIds(element)
363
if (element.hasAttribute("id"))
364
paletteButtonIDs[element.getAttribute("id")] = element;
366
for (let child = element.firstChild; child; child = child.nextSibling)
367
if (child.nodeType == Ci.nsIDOMNode.ELEMENT_NODE)
368
getElementIds(child);
370
getElementIds(paletteButton);
373
// Go on and register listeners
374
this.window.addEventListener("unload", removeWindow, false);
375
for each (let [id, event, handler] in this.eventHandlers)
377
handler = this._bindMethod(handler);
379
let element = this.E(id);
381
element.addEventListener(event, handler, false);
383
if (id in paletteButtonIDs)
384
paletteButtonIDs[id].addEventListener(event, handler, false);
387
let browser = this.getBrowser();
388
browser.addEventListener("click", this._bindMethod(this.handleLinkClick), true);
390
// Register progress listener as well if requested
391
if (addProgressListener)
393
let dummy = function() {};
394
this.progressListener =
396
onLocationChange: this._bindMethod(this.updateState),
397
onProgressChange: dummy,
398
onSecurityChange: dummy,
399
onStateChange: dummy,
400
onStatusChange: dummy,
401
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference])
403
browser.addProgressListener(this.progressListener);
408
* Retrieves the current location of the browser (might return null on failure).
410
getCurrentLocation: function() /**nsIURI*/
412
if ("currentHeaderData" in this.window && "content-base" in this.window.currentHeaderData)
414
// Thunderbird blog entry
415
return Utils.unwrapURL(this.window.currentHeaderData["content-base"].headerValue);
417
else if ("currentHeaderData" in this.window && "from" in this.window.currentHeaderData)
419
// Thunderbird mail/newsgroup entry
422
let headerParser = Cc["@mozilla.org/messenger/headerparser;1"].getService(Ci.nsIMsgHeaderParser);
423
let emailAddress = headerParser.extractHeaderAddressMailboxes(this.window.currentHeaderData.from.headerValue);
424
return Utils.makeURI("mailto:" + emailAddress.replace(/^[\s"]+/, "").replace(/[\s"]+$/, "").replace(/\s/g, "%20"));
434
return Utils.unwrapURL(this.getBrowser().contentWindow.location.href);
439
* Executes window-specific first-run actions if necessary.
441
executeFirstRunActions: function()
443
// Only execute first-run actions for this window once
444
if ("doneFirstRunActions " + this.window.location.href in Prefs)
446
Prefs["doneFirstRunActions " + this.window.location.href] = true;
448
// Check version we previously executed first-run actions for;
449
let hooks = this.E("abp-hooks");
450
let lastVersion = hooks.getAttribute("currentVersion") || "0.0";
451
if (lastVersion != Prefs.currentVersion)
453
hooks.setAttribute("currentVersion", Prefs.currentVersion);
454
this.window.document.persist("abp-hooks", "currentVersion");
456
let needInstall = (Utils.versionComparator.compare(lastVersion, "0.0") <= 0);
459
// Before version 1.1 we didn't add toolbar icon in SeaMonkey, do it now
460
needInstall = Utils.appID == "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}" &&
461
Utils.versionComparator.compare(lastVersion, "1.1") < 0;
464
// Add ABP icon to toolbar if necessary
466
Utils.runAsync(this.installToolbarIcon, this);
471
* Finds the toolbar button in the toolbar palette.
473
getPaletteButton: function()
475
let toolbox = (this.getToolbox ? this.getToolbox() : null);
476
if (!toolbox || !("palette" in toolbox) || !toolbox.palette)
479
for (var child = toolbox.palette.firstChild; child; child = child.nextSibling)
480
if (child.id == "abp-toolbarbutton")
487
* Updates displayed state for an application window.
489
updateState: function()
491
let state = (Prefs.enabled ? "active" : "disabled");
493
if (state == "active")
495
let location = this.getCurrentLocation();
496
if (location && Policy.isWhitelisted(location.spec))
497
state = "whitelisted";
501
function updateElement(element)
506
if (element.tagName == "statusbarpanel")
507
element.hidden = !Prefs.showinstatusbar;
509
element.hidden = !Prefs.showintoolbar;
511
// HACKHACK: Show status bar icon instead of toolbar icon if the application doesn't have a toolbar icon
512
if (element.hidden && element.tagName == "statusbarpanel" && !this.getDefaultToolbar)
513
element.hidden = !Prefs.showintoolbar;
515
element.setAttribute("abpstate", state);
518
let status = this.E("abp-status");
521
updateElement.call(this, status);
522
if (Prefs.defaultstatusbaraction == 0)
523
status.setAttribute("popup", status.getAttribute("context"));
525
status.removeAttribute("popup");
528
let button = this.E("abp-toolbarbutton");
531
updateElement.call(this, button);
532
if (button.hasAttribute("context") && Prefs.defaulttoolbaraction == 0)
533
button.setAttribute("type", "menu");
535
button.setAttribute("type", "menu-button");
538
updateElement.call(this, this.getPaletteButton());
542
* Sets up hotkeys for the window.
544
configureKeys: function()
546
for (let pref in Prefs)
548
if (pref.match(/_key$/))
552
this.configureKey(RegExp.leftContext, Prefs[pref]);
563
* Sets a hotkey to the value defined in preferences.
565
configureKey: function(/**String*/ id, /**String*/ value)
577
let command = this.E("abp-command-" + id);
584
for each (let part in value.split(/\s+/))
586
if (part.toLowerCase() in validModifiers)
587
modifiers.push(validModifiers[part.toLowerCase()]);
588
else if (part.length == 1)
590
else if ("DOM_VK_" + part.toUpperCase() in Ci.nsIDOMKeyEvent)
591
keycode = "VK_" + part.toUpperCase();
594
if (keychar || keycode)
596
let element = this.window.document.createElement("key");
597
element.setAttribute("id", "abp-key-" + id);
598
element.setAttribute("command", "abp-command-" + id);
600
element.setAttribute("key", keychar);
602
element.setAttribute("keycode", keycode);
603
element.setAttribute("modifiers", modifiers.join(","));
605
this.E("abp-keyset").appendChild(element);
610
* Initializes window's context menu.
612
initContextMenu: function()
614
let contextMenu = this.getContextMenu();
617
contextMenu.addEventListener("popupshowing", this._bindMethod(this.updateContextMenu), false);
619
// Make sure our context menu items are at the bottom
620
contextMenu.appendChild(this.E("abp-removeWhitelist-menuitem"));
621
contextMenu.appendChild(this.E("abp-frame-menuitem"));
622
contextMenu.appendChild(this.E("abp-object-menuitem"));
623
contextMenu.appendChild(this.E("abp-media-menuitem"));
624
contextMenu.appendChild(this.E("abp-image-menuitem"));
629
* Makes sure the toolbar button is displayed.
631
installToolbarIcon: function()
633
let tb = this.E("abp-toolbarbutton");
634
if (tb && tb.parentNode.localName != "toolbarpalette")
637
let toolbar = (this.getDefaultToolbar ? this.getDefaultToolbar() : null);
638
if (!toolbar || typeof toolbar.insertItem != "function")
641
let insertBefore = (this.toolbarInsertBefore ? this.toolbarInsertBefore() : null);
642
if (insertBefore && insertBefore.parentNode != toolbar)
645
toolbar.insertItem("abp-toolbarbutton", insertBefore, null, false);
647
toolbar.setAttribute("currentset", toolbar.currentSet);
648
this.window.document.persist(toolbar.id, "currentset");
650
if (this.unhideToolbar && this.unhideToolbar())
652
toolbar.setAttribute("collapsed", "false");
653
this.window.document.persist(toolbar.id, "collapsed");
658
* Handles browser clicks to intercept clicks on abp: links.
660
handleLinkClick: function (/**Event*/ event)
662
// Ignore right-clicks
663
if (event.button == 2)
666
// Search the link associated with the click
667
let link = event.target;
668
while (link && !(link instanceof Ci.nsIDOMHTMLAnchorElement))
669
link = link.parentNode;
671
if (!link || !/^abp:\/*subscribe\/*\?(.*)/i.test(link.href)) /**/
674
// This is our link - make sure the browser doesn't handle it
675
event.preventDefault();
676
event.stopPropagation();
678
// Decode URL parameters
681
let mainSubscriptionTitle = null;
682
let mainSubscriptionURL = null;
683
for each (let param in RegExp.$1.split('&'))
685
let parts = param.split("=", 2);
686
if (parts.length != 2 || !/\S/.test(parts[1]))
691
title = decodeURIComponent(parts[1]);
694
url = decodeURIComponent(parts[1]);
696
case "requiresTitle":
697
mainSubscriptionTitle = decodeURIComponent(parts[1]);
699
case "requiresLocation":
700
mainSubscriptionURL = decodeURIComponent(parts[1]);
707
// Default title to the URL
711
// Main subscription needs both title and URL
712
if (mainSubscriptionTitle && !mainSubscriptionURL)
713
mainSubscriptionTitle = null;
714
if (mainSubscriptionURL && !mainSubscriptionTitle)
715
mainSubscriptionURL = null;
717
// Trim spaces in title and URL
718
title = title.replace(/^\s+/, "").replace(/\s+$/, "");
719
url = url.replace(/^\s+/, "").replace(/\s+$/, "");
720
if (mainSubscriptionURL)
722
mainSubscriptionTitle = mainSubscriptionTitle.replace(/^\s+/, "").replace(/\s+$/, "");
723
mainSubscriptionURL = mainSubscriptionURL.replace(/^\s+/, "").replace(/\s+$/, "");
726
// Verify that the URL is valid
727
url = Utils.makeURI(url);
728
if (!url || (url.scheme != "http" && url.scheme != "https" && url.scheme != "ftp"))
732
if (mainSubscriptionURL)
734
mainSubscriptionURL = Utils.makeURI(mainSubscriptionURL);
735
if (!mainSubscriptionURL || (mainSubscriptionURL.scheme != "http" && mainSubscriptionURL.scheme != "https" && mainSubscriptionURL.scheme != "ftp"))
736
mainSubscriptionURL = mainSubscriptionTitle = null;
738
mainSubscriptionURL = mainSubscriptionURL.spec;
744
let subscription = {url: url, title: title, disabled: false, external: false, autoDownload: true,
745
mainSubscriptionTitle: mainSubscriptionTitle, mainSubscriptionURL: mainSubscriptionURL};
746
this.window.openDialog("chrome://adblockplus/content/ui/subscriptionSelection.xul", "_blank",
747
"chrome,centerscreen,resizable,dialog=no", subscription, null);
751
// Special handling for Fennec
752
AppIntegrationFennec.openFennecSubscriptionDialog(this, url, title);
757
* Updates state of the icon tooltip.
759
fillTooltip: function(/**Event*/ event)
761
let node = this.window.document.tooltipNode;
762
if (!node || !node.hasAttribute("tooltip"))
764
event.preventDefault();
768
let type = (node.id == "abp-toolbarbutton" ? "toolbar" : "statusbar");
769
let action = parseInt(Prefs["default" + type + "action"]);
773
let actionDescr = this.E("abp-tooltip-action");
774
actionDescr.hidden = (action < 0 || action > 3);
775
if (!actionDescr.hidden)
776
actionDescr.setAttribute("value", Utils.getString("action" + action + "_tooltip"));
778
let statusDescr = this.E("abp-tooltip-status");
779
let statusStr = Utils.getString(this.state + "_tooltip");
780
if (this.state == "active")
782
let [activeSubscriptions, activeFilters] = FilterStorage.subscriptions.reduce(function([subscriptions, filters], current)
784
if (current instanceof SpecialSubscription)
785
return [subscriptions, filters + current.filters.filter(function(filter) !filter.disabled).length];
786
else if (!current.disabled)
787
return [subscriptions + 1, filters];
789
return [subscriptions, filters]
792
statusStr = statusStr.replace(/\?1\?/, activeSubscriptions).replace(/\?2\?/, activeFilters);
794
statusDescr.setAttribute("value", statusStr);
796
let activeFilters = [];
797
this.E("abp-tooltip-blocked-label").hidden = (this.state != "active");
798
this.E("abp-tooltip-blocked").hidden = (this.state != "active");
799
if (this.state == "active")
801
let stats = RequestNotifier.getWindowStatistics(this.getBrowser().contentWindow);
803
let blockedStr = Utils.getString("blocked_count_tooltip");
804
blockedStr = blockedStr.replace(/\?1\?/, stats ? stats.blocked : 0).replace(/\?2\?/, stats ? stats.items : 0);
806
if (stats && stats.whitelisted + stats.hidden)
808
blockedStr += " " + Utils.getString("blocked_count_addendum");
809
blockedStr = blockedStr.replace(/\?1\?/, stats.whitelisted).replace(/\?2\?/, stats.hidden);
812
this.E("abp-tooltip-blocked").setAttribute("value", blockedStr);
816
let filterSort = function(a, b)
818
return stats.filters[b] - stats.filters[a];
820
for (let filter in stats.filters)
821
activeFilters.push(filter);
822
activeFilters = activeFilters.sort(filterSort);
825
if (activeFilters.length > 0)
827
let filtersContainer = this.E("abp-tooltip-filters");
828
while (filtersContainer.firstChild)
829
filtersContainer.removeChild(filtersContainer.firstChild);
831
for (let i = 0; i < activeFilters.length && i < 3; i++)
833
let descr = filtersContainer.ownerDocument.createElement("description");
834
descr.setAttribute("value", activeFilters[i] + " (" + stats.filters[activeFilters[i]] + ")");
835
filtersContainer.appendChild(descr);
840
this.E("abp-tooltip-filters-label").hidden = (activeFilters.length == 0);
841
this.E("abp-tooltip-filters").hidden = (activeFilters.length == 0);
842
this.E("abp-tooltip-more-filters").hidden = (activeFilters.length <= 3);
846
* Updates state of the icon context menu.
848
fillPopup: function(/**Event*/ event)
850
let popup = event.target;
852
// Submenu being opened - ignore
853
if (!/^(abp-(?:toolbar|status)-)popup$/.test(popup.getAttribute("id")))
855
let prefix = RegExp.$1;
857
let sidebarOpen = this.isSidebarOpen();
858
this.E(prefix + "opensidebar").hidden = sidebarOpen;
859
this.E(prefix + "closesidebar").hidden = !sidebarOpen;
861
let whitelistItemSite = this.E(prefix + "whitelistsite");
862
let whitelistItemPage = this.E(prefix + "whitelistpage");
863
whitelistItemSite.hidden = whitelistItemPage.hidden = true;
865
let whitelistSeparator = whitelistItemPage.nextSibling;
866
while (whitelistSeparator.nodeType != whitelistSeparator.ELEMENT_NODE)
867
whitelistSeparator = whitelistSeparator.nextSibling;
869
let location = this.getCurrentLocation();
870
if (location && Policy.isBlockableScheme(location))
875
host = location.host.replace(/^www\./, "");
881
if (location instanceof Ci.nsIURL && location.ref)
883
if (location instanceof Ci.nsIURL && location.query)
889
this.siteWhitelist = Filter.fromText("@@||" + host + "^$document");
890
whitelistItemSite.setAttribute("checked", this.siteWhitelist.subscriptions.length && !this.siteWhitelist.disabled);
891
whitelistItemSite.setAttribute("label", whitelistItemSite.getAttribute("labeltempl").replace(/\?1\?/, host));
892
whitelistItemSite.hidden = false;
894
this.pageWhitelist = Filter.fromText("@@|" + location.spec + ending + "$document");
895
whitelistItemPage.setAttribute("checked", this.pageWhitelist.subscriptions.length && !this.pageWhitelist.disabled);
896
whitelistItemPage.hidden = false;
900
this.siteWhitelist = Filter.fromText("@@|" + location.spec + "|");
901
whitelistItemSite.setAttribute("checked", this.siteWhitelist.subscriptions.length && !this.siteWhitelist.disabled);
902
whitelistItemSite.setAttribute("label", whitelistItemSite.getAttribute("labeltempl").replace(/\?1\?/, location.spec.replace(/^mailto:/, "")));
903
whitelistItemSite.hidden = false;
906
whitelistSeparator.hidden = whitelistItemSite.hidden && whitelistItemPage.hidden;
908
this.E(prefix + "enabled").setAttribute("checked", Prefs.enabled);
909
this.E(prefix + "frameobjects").setAttribute("checked", Prefs.frameobjects);
910
this.E(prefix + "slowcollapse").setAttribute("checked", !Prefs.fastcollapse);
911
this.E(prefix + "showintoolbar").setAttribute("checked", Prefs.showintoolbar);
912
this.E(prefix + "showinstatusbar").setAttribute("checked", Prefs.showinstatusbar);
914
let defAction = (prefix == "abp-toolbar-" || this.window.document.popupNode.id == "abp-toolbarbutton" ?
915
Prefs.defaulttoolbaraction :
916
Prefs.defaultstatusbaraction);
917
this.E(prefix + "opensidebar").setAttribute("default", defAction == 1);
918
this.E(prefix + "closesidebar").setAttribute("default", defAction == 1);
919
this.E(prefix + "settings").setAttribute("default", defAction == 2);
920
this.E(prefix + "enabled").setAttribute("default", defAction == 3);
924
* Opens report wizard for the current page.
926
openReportDialog: function()
928
let wnd = Utils.windowMediator.getMostRecentWindow("abp:sendReport");
932
this.window.openDialog("chrome://adblockplus/content/ui/sendReport.xul", "_blank", "chrome,centerscreen,resizable=no", this.window.content);
936
* Tests whether blockable items list is currently open.
938
isSidebarOpen: function() /**Boolean*/
940
if (this.detachedSidebar && !this.detachedSidebar.closed)
943
let sidebar = this.E("abp-sidebar");
944
return (sidebar ? !sidebar.hidden : false);
948
* Toggles open/closed state of the blockable items list.
950
toggleSidebar: function()
952
if (this.detachedSidebar && !this.detachedSidebar.closed)
954
this.detachedSidebar.close();
955
this.detachedSidebar = null;
959
let sidebar = this.E("abp-sidebar");
960
if (sidebar && (!Prefs.detachsidebar || !sidebar.hidden))
962
this.E("abp-sidebar-splitter").hidden = !sidebar.hidden;
963
this.E("abp-sidebar-browser").setAttribute("src", sidebar.hidden ? "chrome://adblockplus/content/ui/sidebar.xul" : "about:blank");
964
sidebar.hidden = !sidebar.hidden;
966
this.getBrowser().contentWindow.focus();
969
this.detachedSidebar = this.window.openDialog("chrome://adblockplus/content/ui/sidebarDetached.xul", "_blank", "chrome,resizable,dependent,dialog=no");
972
let menuItem = this.E("abp-blockableitems");
974
menuItem.setAttribute("checked", this.isSidebarOpen());
978
* Removes/disables the exception rule applying for the current page.
980
removeWhitelist: function()
982
let location = this.getCurrentLocation();
985
filter = Policy.isWhitelisted(location.spec);
986
if (filter && filter.subscriptions.length && !filter.disabled)
987
AppIntegration.toggleFilter(filter);
991
* Handles command events on toolbar icon.
993
handleToolbarCommand: function(event)
995
if (event.eventPhase != event.AT_TARGET)
998
if (Prefs.defaulttoolbaraction == 0)
999
event.target.open = true;
1001
this.executeAction(Prefs.defaulttoolbaraction);
1005
* Handles click events on toolbar icon.
1007
handleToolbarClick: function(/**Event*/ event)
1009
if (event.eventPhase != event.AT_TARGET)
1012
if (event.button == 1)
1013
AppIntegration.togglePref("enabled");
1017
* Handles click events on status bar icon.
1019
handleStatusClick: function(/**Event*/ event)
1021
if (event.eventPhase != event.AT_TARGET)
1024
if (event.button == 0)
1025
this.executeAction(Prefs.defaultstatusbaraction);
1026
else if (event.button == 1)
1027
AppIntegration.togglePref("enabled");
1030
// Executes default action for statusbar/toolbar by its number
1031
executeAction: function (action)
1034
this.toggleSidebar();
1035
else if (action == 2)
1036
Utils.openSettingsDialog();
1037
else if (action == 3)
1038
AppIntegration.togglePref("enabled");
1042
* Updates context menu, in particularly controls the visibility of context
1043
* menu items like "Block image".
1045
updateContextMenu: function(event)
1047
if (event.eventPhase != event.AT_TARGET)
1050
let contextMenu = this.getContextMenu();
1051
let target = this.window.document.popupNode;
1052
if (target instanceof Ci.nsIDOMHTMLMapElement || target instanceof Ci.nsIDOMHTMLAreaElement)
1054
// HTML image maps will usually receive events when the mouse pointer is
1055
// over a different element, get the real event target.
1056
let rect = target.getClientRects()[0];
1057
target = target.ownerDocument.elementFromPoint(Math.max(rect.left, 0), Math.max(rect.top, 0));
1060
let nodeType = null;
1061
this.nodeData = null;
1062
this.currentNode = null;
1063
this.backgroundData = null;
1064
this.frameData = null;
1065
this.currentFrame = null;
1068
// Lookup the node in our stored data
1069
let data = RequestNotifier.getDataForNode(target);
1070
if (data && !data[1].filter)
1072
[this.currentNode, this.nodeData] = data;
1073
nodeType = this.nodeData.typeDescr;
1076
let wnd = Utils.getWindow(target);
1078
if (wnd.frameElement)
1080
let data = RequestNotifier.getDataForNode(wnd.frameElement, true);
1081
if (data && !data[1].filter)
1082
[this.currentFrame, this.frameData] = data;
1085
if (nodeType != "IMAGE")
1087
// Look for a background image
1088
let imageNode = target;
1091
if (imageNode.nodeType == imageNode.ELEMENT_NODE)
1093
let style = wnd.getComputedStyle(imageNode, "");
1094
let bgImage = extractImageURL(style, "background-image") || extractImageURL(style, "list-style-image");
1097
let data = RequestNotifier.getDataForNode(wnd.document, true, Policy.type.IMAGE, bgImage);
1098
if (data && !data[1].filter)
1100
this.backgroundData = data[1];
1106
imageNode = imageNode.parentNode;
1110
// Hide "Block Images from ..." if hideimagemanager pref is true and the image manager isn't already blocking something
1111
let imgManagerContext = this.E("context-blockimage");
1112
if (imgManagerContext && shouldHideImageManager())
1114
// Don't use "hidden" attribute - it might be overridden by the default popupshowing handler
1115
imgManagerContext.collapsed = true;
1119
this.E("abp-image-menuitem").hidden = (nodeType != "IMAGE" && this.backgroundData == null);
1120
this.E("abp-object-menuitem").hidden = (nodeType != "OBJECT");
1121
this.E("abp-media-menuitem").hidden = (nodeType != "MEDIA");
1122
this.E("abp-frame-menuitem").hidden = (this.frameData == null);
1124
let location = this.getCurrentLocation();
1125
this.E("abp-removeWhitelist-menuitem").hidden = (!location || !Policy.isWhitelisted(location.spec));
1129
* Brings up the filter composer dialog to block an item.
1131
blockItem: function(/**Node*/ node, /**RequestEntry*/ item)
1136
this.window.openDialog("chrome://adblockplus/content/ui/composer.xul", "_blank", "chrome,centerscreen,resizable,dialog=no,dependent", [node], item);
1141
* List of event handers to be registered. For each event handler the element ID,
1142
* event and the actual event handler are listed.
1145
WindowWrapper.prototype.eventHandlers = [
1146
["abp-tooltip", "popupshowing", WindowWrapper.prototype.fillTooltip],
1147
["abp-status-popup", "popupshowing", WindowWrapper.prototype.fillPopup],
1148
["abp-toolbar-popup", "popupshowing", WindowWrapper.prototype.fillPopup],
1149
["abp-command-sendReport", "command", WindowWrapper.prototype.openReportDialog],
1150
["abp-command-settings", "command", function() {Utils.openSettingsDialog();}],
1151
["abp-command-sidebar", "command", WindowWrapper.prototype.toggleSidebar],
1152
["abp-command-togglesitewhitelist", "command", function() { AppIntegration.toggleFilter(this.siteWhitelist); }],
1153
["abp-command-togglepagewhitelist", "command", function() { AppIntegration.toggleFilter(this.pageWhitelist); }],
1154
["abp-command-toggleobjtabs", "command", function() { AppIntegration.togglePref("frameobjects"); }],
1155
["abp-command-togglecollapse", "command", function() { AppIntegration.togglePref("fastcollapse"); }],
1156
["abp-command-toggleshowintoolbar", "command", function() { AppIntegration.togglePref("showintoolbar"); }],
1157
["abp-command-toggleshowinstatusbar", "command", function() { AppIntegration.togglePref("showinstatusbar"); }],
1158
["abp-command-enable", "command", function() { AppIntegration.togglePref("enabled"); }],
1159
["abp-toolbarbutton", "command", WindowWrapper.prototype.handleToolbarCommand],
1160
["abp-toolbarbutton", "click", WindowWrapper.prototype.handleToolbarClick],
1161
["abp-status", "click", WindowWrapper.prototype.handleStatusClick],
1162
["abp-image-menuitem", "command", function() { this.backgroundData ? this.blockItem(null, this.backgroundData) : this.blockItem(this.currentNode, this.nodeData); }],
1163
["abp-object-menuitem", "command", function() { this.blockItem(this.currentNode, this.nodeData); }],
1164
["abp-media-menuitem", "command", function() { this.blockItem(this.currentNode, this.nodeData); }],
1165
["abp-frame-menuitem", "command", function() { this.blockItem(this.currentFrame, this.frameData); }],
1166
["abp-removeWhitelist-menuitem", "command", WindowWrapper.prototype.removeWhitelist]
1170
* Updates displayed status for all application windows (on prefs or filters
1173
function reloadPrefs()
1175
if (currentlyShowingInToolbar != Prefs.showintoolbar)
1177
currentlyShowingInToolbar = Prefs.showintoolbar;
1178
if (Prefs.showintoolbar)
1179
for each (let wrapper in wrappers)
1180
wrapper.installToolbarIcon();
1183
for each (let wrapper in wrappers)
1184
wrapper.updateState();
1188
* Tests whether image manager context menu entry should be hidden with user's current preferences.
1191
function shouldHideImageManager()
1194
if (Prefs.hideimagemanager && "@mozilla.org/permissionmanager;1" in Cc)
1199
let enumerator = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager).enumerator;
1200
while (enumerator.hasMoreElements())
1202
let item = enumerator.getNext().QueryInterface(Ci.nsIPermission);
1203
if (item.type == "image" && item.capability == Ci.nsIPermissionManager.DENY_ACTION)
1216
shouldHideImageManager = function() result;
1221
* Executed on first run, presents the user with a list of filter subscriptions
1222
* and allows choosing one.
1224
function showSubscriptions()
1226
let wrapper = (wrappers.length ? wrappers[0] : null);
1228
// Don't annoy the user if he has a subscription already
1229
let hasSubscriptions = FilterStorage.subscriptions.some(function(subscription) subscription instanceof DownloadableSubscription);
1230
if (hasSubscriptions)
1233
// Only show the list if this is the first run or the user has no filters
1234
let hasFilters = FilterStorage.subscriptions.some(function(subscription) subscription.filters.length);
1235
if (hasFilters && Utils.versionComparator.compare(Prefs.lastVersion, "0.0") > 0)
1238
if (wrapper && wrapper.addTab)
1240
wrapper.addTab("chrome://adblockplus/content/ui/subscriptionSelection.xul");
1244
Utils.windowWatcher.openWindow(wrapper ? wrapper.window : null,
1245
"chrome://adblockplus/content/ui/subscriptionSelection.xul",
1246
"_blank", "chrome,centerscreen,resizable,dialog=no", null);
1251
* Extracts the URL of the image from a CSS property.
1253
function extractImageURL(/**CSSStyleDeclaration*/ computedStyle, /**String*/ property)
1255
let value = computedStyle.getPropertyCSSValue(property);
1256
if (value instanceof Ci.nsIDOMCSSValueList && value.length >= 1)
1258
if (value instanceof Ci.nsIDOMCSSPrimitiveValue && value.primitiveType == Ci.nsIDOMCSSPrimitiveValue.CSS_URI)
1259
return Utils.unwrapURL(value.getStringValue()).spec;