| Index: lib/ui.js | 
| =================================================================== | 
| --- a/lib/ui.js | 
| +++ b/lib/ui.js | 
| @@ -454,16 +454,32 @@ let UI = exports.UI = | 
| }.bind(UI) | 
| }; | 
| Services.obs.addObserver(documentCreationObserver, "content-document-global-created", false); | 
| onShutdown.add(function() | 
| { | 
| Services.obs.removeObserver(documentCreationObserver, "content-document-global-created", false); | 
| }); | 
|  | 
| +    // Frame script URL has to be randomized due to caching | 
| +    // (see https://bugzilla.mozilla.org/show_bug.cgi?id=1051238) | 
| +    let frameScript = "chrome://adblockplus/content/subscribeLinkHandler.js?" + Math.random(); | 
| + | 
| +    // Initialize subscribe link handling | 
| +    let callback = this.subscribeLinkClicked.bind(this); | 
| +    let messageManager = Cc["@mozilla.org/globalmessagemanager;1"] | 
| +                           .getService(Ci.nsIMessageListenerManager); | 
| +    messageManager.loadFrameScript(frameScript, true); | 
| +    messageManager.addMessageListener("AdblockPlus:SubscribeLink", callback); | 
| +    onShutdown.add(() => { | 
| +      messageManager.broadcastAsyncMessage("AdblockPlus:Shutdown", frameScript); | 
| +      messageManager.removeDelayedFrameScript(frameScript); | 
| +      messageManager.removeMessageListener("AdblockPlus:SubscribeLink", callback); | 
| +    }); | 
| + | 
| // Execute first-run actions if a window is open already, otherwise it | 
| // will happen in applyToWindow() when a window is opened. | 
| this.firstRunActions(this.currentWindow); | 
| }, | 
|  | 
| addToolbarButton: function() | 
| { | 
| let {WindowObserver} = require("windowObserver"); | 
| @@ -537,17 +553,17 @@ let UI = exports.UI = | 
| */ | 
| firstRunDone: false, | 
|  | 
| /** | 
| * Initializes Adblock Plus UI in a window. | 
| */ | 
| applyToWindow: function(/**Window*/ window, /**Boolean*/ noDelay) | 
| { | 
| -    let {delayInitialization, isKnownWindow, getBrowser, addBrowserLocationListener, addBrowserClickListener} = require("appSupport"); | 
| +    let {delayInitialization, isKnownWindow, getBrowser, addBrowserLocationListener} = require("appSupport"); | 
| if (window.document.documentElement.id == "CustomizeToolbarWindow" || isKnownWindow(window)) | 
| { | 
| // Add style processing instruction | 
| let style = window.document.createProcessingInstruction("xml-stylesheet", 'class="adblockplus-node" href="chrome://adblockplus/skin/overlay.css" type="text/css"'); | 
| window.document.insertBefore(style, window.document.firstChild); | 
| } | 
|  | 
| if (!isKnownWindow(window)) | 
| @@ -588,17 +604,16 @@ let UI = exports.UI = | 
| window.addEventListener("popupshowing", this.onPopupShowing, false); | 
| window.addEventListener("keypress", this.onKeyPress, false); | 
|  | 
| addBrowserLocationListener(window, function() | 
| { | 
| this.updateIconState(window, window.document.getElementById("abp-status")); | 
| this.updateIconState(window, window.document.getElementById("abp-toolbarbutton")); | 
| }.bind(this)); | 
| -    addBrowserClickListener(window, this.onBrowserClick.bind(this, window)); | 
|  | 
| let notificationPanel = window.document.getElementById("abp-notification"); | 
| notificationPanel.addEventListener("command", function(event) | 
| { | 
| switch (event.target.id) | 
| { | 
| case "abp-notification-close": | 
| notificationPanel.classList.add("abp-closing"); | 
| @@ -623,17 +638,17 @@ let UI = exports.UI = | 
| .allowSubframes = true; | 
| }, | 
|  | 
| /** | 
| * Removes Adblock Plus UI from a window. | 
| */ | 
| removeFromWindow: function(/**Window*/ window) | 
| { | 
| -    let {isKnownWindow, removeBrowserLocationListeners, removeBrowserClickListeners} = require("appSupport"); | 
| +    let {isKnownWindow, removeBrowserLocationListeners} = require("appSupport"); | 
| if (window.document.documentElement.id == "CustomizeToolbarWindow" || isKnownWindow(window)) | 
| { | 
| // Remove style processing instruction | 
| for (let child = window.document.firstChild; child; child = child.nextSibling) | 
| if (child.nodeType == child.PROCESSING_INSTRUCTION_NODE && child.data.indexOf("adblockplus-node") >= 0) | 
| child.parentNode.removeChild(child); | 
| } | 
|  | 
| @@ -658,17 +673,16 @@ let UI = exports.UI = | 
| if (clone) | 
| clone.parentNode.removeChild(clone); | 
| } | 
| } | 
|  | 
| window.removeEventListener("popupshowing", this.onPopupShowing, false); | 
| window.removeEventListener("keypress", this.onKeyPress, false); | 
| removeBrowserLocationListeners(window); | 
| -    removeBrowserClickListeners(window); | 
| }, | 
|  | 
| /** | 
| * The overlay information to be used when adding elements to the UI. | 
| * @type Object | 
| */ | 
| overlay: null, | 
|  | 
| @@ -918,84 +932,22 @@ let UI = exports.UI = | 
| }, false); | 
| request.send(); | 
| } | 
| else | 
| notifyUser(); | 
| }, | 
|  | 
| /** | 
| -   * Handles clicks inside the browser's content area, will intercept clicks on | 
| -   * abp: links as well as links to subscribe.adblockplus.org. | 
| +   * Called whenever subscribeLinkHandler.js intercepts clicks on abp: links | 
| +   * as well as links to subscribe.adblockplus.org. | 
| */ | 
| -  onBrowserClick: function (/**Window*/ window, /**Event*/ event) | 
| +  subscribeLinkClicked: function(message) | 
| { | 
| -    // Ignore right-clicks | 
| -    if (event.button == 2) | 
| -      return; | 
| - | 
| -    // Search the link associated with the click | 
| -    let link = event.target; | 
| -    while (!(link instanceof Ci.nsIDOMHTMLAnchorElement)) | 
| -    { | 
| -      link = link.parentNode; | 
| - | 
| -      if (!link) | 
| -        return; | 
| -    } | 
| - | 
| -    let queryString = null; | 
| -    if (link.protocol == "http:" || link.protocol == "https:") | 
| -    { | 
| -      if (link.host == "subscribe.adblockplus.org" && link.pathname == "/") | 
| -        queryString = link.search.substr(1); | 
| -    } | 
| -    else | 
| -    { | 
| -      // Firefox doesn't populate the "search" property for links with | 
| -      // non-standard URL schemes so we need to extract the query string | 
| -      // manually | 
| -      let match = /^abp:\/*subscribe\/*\?(.*)/i.exec(link.href); | 
| -      if (match) | 
| -        queryString = match[1]; | 
| -    } | 
| - | 
| -    if (!queryString) | 
| -      return; | 
| - | 
| -    // This is our link - make sure the browser doesn't handle it | 
| -    event.preventDefault(); | 
| -    event.stopPropagation(); | 
| - | 
| -    // Decode URL parameters | 
| -    let title = null; | 
| -    let url = null; | 
| -    let mainSubscriptionTitle = null; | 
| -    let mainSubscriptionURL = null; | 
| -    for (let param of queryString.split("&")) | 
| -    { | 
| -      let parts = param.split("=", 2); | 
| -      if (parts.length != 2 || !/\S/.test(parts[1])) | 
| -        continue; | 
| -      switch (parts[0]) | 
| -      { | 
| -        case "title": | 
| -          title = decodeURIComponent(parts[1]); | 
| -          break; | 
| -        case "location": | 
| -          url = decodeURIComponent(parts[1]); | 
| -          break; | 
| -        case "requiresTitle": | 
| -          mainSubscriptionTitle = decodeURIComponent(parts[1]); | 
| -          break; | 
| -        case "requiresLocation": | 
| -          mainSubscriptionURL = decodeURIComponent(parts[1]); | 
| -          break; | 
| -      } | 
| -    } | 
| +    let {title, url, mainSubscriptionTitle, mainSubscriptionURL} = message.data; | 
| if (!url) | 
| return; | 
|  | 
| // Default title to the URL | 
| if (!title) | 
| title = url; | 
|  | 
| // Main subscription needs both title and URL | 
| @@ -1023,17 +975,17 @@ let UI = exports.UI = | 
| { | 
| mainSubscriptionURL = Utils.makeURI(mainSubscriptionURL); | 
| if (!mainSubscriptionURL || (mainSubscriptionURL.scheme != "http" && mainSubscriptionURL.scheme != "https" && mainSubscriptionURL.scheme != "ftp")) | 
| mainSubscriptionURL = mainSubscriptionTitle = null; | 
| else | 
| mainSubscriptionURL = mainSubscriptionURL.spec; | 
| } | 
|  | 
| -    this.openSubscriptionDialog(window, url, title, mainSubscriptionURL, mainSubscriptionTitle); | 
| +    this.openSubscriptionDialog(this.currentWindow, url, title, mainSubscriptionURL, mainSubscriptionTitle); | 
| }, | 
|  | 
| /** | 
| * Opens a dialog letting the user confirm/adjust a filter subscription to | 
| * be added. | 
| */ | 
| openSubscriptionDialog: function(/**Window*/ window, /**String*/ url, /**String*/ title, /**String*/ mainURL, /**String*/ mainTitle) | 
| { | 
|  |