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

Unified Diff: chrome/ext/background.js

Issue 5088751004942336: Issue 370 - Right-clicked element is removed independent of created filter (Closed)
Patch Set: Rebase to rev 3c9cea80c481 Created July 18, 2014, 8:54 a.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « block.js ('k') | chrome/ext/common.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/ext/background.js
===================================================================
rename from chrome/background.js
rename to chrome/ext/background.js
--- a/chrome/background.js
+++ b/chrome/ext/background.js
@@ -1,6 +1,6 @@
/*
* This file is part of Adblock Plus <http://adblockplus.org/>,
- * Copyright (C) 2006-2013 Eyeo GmbH
+ * Copyright (C) 2006-2014 Eyeo GmbH
*
* Adblock Plus is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
@@ -17,184 +17,110 @@
(function()
{
- /* Events */
+ /* Pages */
- var TabEventTarget = function()
+ var Page = ext.Page = function(tab)
{
- WrappedEventTarget.apply(this, arguments);
+ this._id = tab.id;
+ this._url = tab.url;
- this._tabs = {};
+ this.browserAction = new BrowserAction(tab.id);
+ this.contextMenus = new ContextMenus(this);
+ };
+ Page.prototype = {
+ get url()
+ {
+ // usually our Page objects are created from Chrome's Tab objects, which
+ // provide the url. So we can return the url given in the constructor.
+ if (this._url != null)
+ return this._url;
- this._sharedListener = this._sharedListener.bind(this);
- this._removeTab = this._removeTab.bind(this);
- };
- TabEventTarget.prototype = {
- __proto__: WrappedEventTarget.prototype,
- _bindToTab: function(tab)
+ // but sometimes we only have the tab id when we create a Page object.
+ // In that case we get the url from top frame of the tab, recorded by
+ // the onBeforeRequest handler.
+ var frames = framesOfTabs[this._id];
+ if (frames)
+ {
+ var frame = frames[0];
+ if (frame)
+ return frame.url;
+ }
+ },
+ activate: function()
{
- return {
- addListener: function(listener)
- {
- var listeners = this._tabs[tab._id];
-
- if (!listeners)
- {
- this._tabs[tab._id] = listeners = [];
-
- if (Object.keys(this._tabs).length == 1)
- this.addListener(this._sharedListener);
-
- tab.onRemoved.addListener(this._removeTab);
- }
-
- listeners.push(listener);
- }.bind(this),
- removeListener: function(listener)
- {
- var listeners = this._tabs[tab._id];
- if (!listeners)
- return;
-
- var idx = listeners.indexOf(listener);
- if (idx == -1)
- return;
-
- listeners.splice(idx, 1);
-
- if (listeners.length == 0)
- tab.onRemoved.removeListener(this._removeTab);
- else
- {
- if (listeners.length > 1)
- return;
- if (listeners[0] != this._removeTab)
- return;
- }
-
- this._removeTab(tab);
- }.bind(this)
- };
+ chrome.tabs.update(this._id, {selected: true});
},
- _sharedListener: function(tab)
+ sendMessage: function(message, responseCallback)
{
- var listeners = this._tabs[tab._id];
-
- if (!listeners)
- return;
-
- // copy listeners before calling them, because they might
- // add or remove other listeners, which must not be taken
- // into account before the next occurrence of the event
- listeners = listeners.slice(0);
-
- for (var i = 0; i < listeners.length; i++)
- listeners[i](tab);
- },
- _removeTab: function(tab)
- {
- delete this._tabs[tab._id];
-
- if (Object.keys(this._tabs).length == 0)
- this.removeListener(this._sharedListener);
+ chrome.tabs.sendMessage(this._id, message, responseCallback);
}
};
- var LoadingTabEventTarget = function()
- {
- TabEventTarget.call(this, chrome.tabs.onUpdated);
- };
- LoadingTabEventTarget.prototype = {
- __proto__: TabEventTarget.prototype,
- _wrapListener: function(listener)
+ ext.pages = {
+ open: function(url, callback)
{
- return function(id, info, tab)
+ if (callback)
{
- if (info.status == "loading")
- listener(new Tab(tab));
- };
- }
+ chrome.tabs.create({url: url}, function(openedTab)
+ {
+ var onUpdated = function(tabId, changeInfo, tab)
+ {
+ if (tabId == openedTab.id && changeInfo.status == "complete")
+ {
+ chrome.tabs.onUpdated.removeListener(onUpdated);
+ callback(new Page(tab));
+ }
+ };
+ chrome.tabs.onUpdated.addListener(onUpdated);
+ });
+ }
+ else
+ chrome.tabs.create({url: url});
+ },
+ query: function(info, callback)
+ {
+ var rawInfo = {};
+ for (var property in info)
+ {
+ switch (property)
+ {
+ case "active":
+ case "lastFocusedWindow":
+ rawInfo[property] = info[property];
+ }
+ }
+
+ chrome.tabs.query(rawInfo, function(tabs)
+ {
+ callback(tabs.map(function(tab)
+ {
+ return new Page(tab);
+ }));
+ });
+ },
+ onLoading: new ext._EventTarget()
};
- var CompletedTabEventTarget = function()
+ chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab)
{
- TabEventTarget.call(this, chrome.tabs.onUpdated);
- };
- CompletedTabEventTarget.prototype = {
- __proto__: TabEventTarget.prototype,
- _wrapListener: function(listener)
- {
- return function(id, info, tab)
- {
- if (info.status == "complete")
- listener(new Tab(tab));
- };
- }
- };
+ if (changeInfo.status == "loading")
+ ext.pages.onLoading._dispatch(new Page(tab));
+ });
- var ActivatedTabEventTarget = function()
+ chrome.webNavigation.onBeforeNavigate.addListener(function(details)
{
- TabEventTarget.call(this, chrome.tabs.onActivated);
- };
- ActivatedTabEventTarget.prototype = {
- __proto__: TabEventTarget.prototype,
- _wrapListener: function(listener)
- {
- return function(info)
- {
- chrome.tabs.get(info.tabId, function(tab)
- {
- listener(new Tab(tab));
- });
- };
- }
- }
+ if (details.frameId == 0)
+ ext._removeFromAllPageMaps(details.tabId);
+ });
- var RemovedTabEventTarget = function()
+ chrome.tabs.onRemoved.addListener(function(tabId)
{
- TabEventTarget.call(this, chrome.tabs.onRemoved);
- };
- RemovedTabEventTarget.prototype = {
- __proto__: TabEventTarget.prototype,
- _wrapListener: function(listener)
- {
- return function(id) { listener(new Tab({id: id})); };
- }
- };
+ ext._removeFromAllPageMaps(tabId);
+ delete framesOfTabs[tabId];
+ });
- var BeforeRequestEventTarget = function()
- {
- WrappedEventTarget.call(this, chrome.webRequest.onBeforeRequest);
- };
- BeforeRequestEventTarget.prototype = {
- __proto__: WrappedEventTarget.prototype,
- _wrapListener: function(listener)
- {
- return function(details)
- {
- var tab = null;
- if (details.tabId != -1)
- tab = new Tab({id: details.tabId});
-
- return {cancel: listener(
- details.url,
- details.type,
- tab,
- details.frameId,
- details.parentFrameId
- ) === false};
- };
- },
- _prepareExtraArguments: function(urls)
- {
- return [urls ? {urls: urls} : {}, ["blocking"]];
- }
- };
-
-
- /* Tabs */
-
- var sendMessage = chrome.tabs.sendMessage || chrome.tabs.sendRequest;
+ /* Browser actions */
var BrowserAction = function(tabId)
{
@@ -203,11 +129,14 @@
BrowserAction.prototype = {
setIcon: function(path)
{
- chrome.browserAction.setIcon({tabId: this._tabId, path: path});
- },
- setTitle: function(title)
- {
- chrome.browserAction.setTitle({tabId: this._tabId, title: title});
+ var paths = {};
+ for (var i = 1; i <= 2; i++)
+ {
+ var size = i * 19;
+ paths[size] = path.replace("$size", size);
+ }
+
+ chrome.browserAction.setIcon({tabId: this._tabId, path: paths});
},
setBadge: function(badge)
{
@@ -238,153 +167,202 @@
}
};
- Tab = function(tab)
+
+ /* Context menus */
+
+ var contextMenuItems = new ext.PageMap();
+ var contextMenuUpdating = false;
+
+ var updateContextMenu = function()
{
- this._id = tab.id;
+ if (contextMenuUpdating)
+ return;
- this.url = tab.url;
- this.browserAction = new BrowserAction(tab.id);
+ contextMenuUpdating = true;
- this.onLoading = ext.tabs.onLoading._bindToTab(this);
- this.onCompleted = ext.tabs.onCompleted._bindToTab(this);
- this.onActivated = ext.tabs.onActivated._bindToTab(this);
- this.onRemoved = ext.tabs.onRemoved._bindToTab(this);
+ chrome.tabs.query({active: true, lastFocusedWindow: true}, function(tabs)
+ {
+ chrome.contextMenus.removeAll(function()
+ {
+ contextMenuUpdating = false;
+
+ if (tabs.length == 0)
+ return;
+
+ var items = contextMenuItems.get({_id: tabs[0].id});
+
+ if (!items)
+ return;
+
+ items.forEach(function(item)
+ {
+ chrome.contextMenus.create({
+ title: item.title,
+ contexts: item.contexts,
+ onclick: function(info, tab)
+ {
+ item.onclick(info.srcUrl, new Page(tab));
+ }
+ });
+ });
+ });
+ });
};
- Tab.prototype = {
- close: function()
+
+ var ContextMenus = function(page)
+ {
+ this._page = page;
+ };
+ ContextMenus.prototype = {
+ create: function(item)
{
- chrome.tabs.remove(this._id);
+ var items = contextMenuItems.get(this._page);
+ if (!items)
+ contextMenuItems.set(this._page, items = []);
+
+ items.push(item);
+ updateContextMenu();
},
- activate: function()
+ removeAll: function()
{
- chrome.tabs.update(this._id, {selected: true});
- },
- sendMessage: function(message, responseCallback)
- {
- sendMessage(this._id, message, responseCallback);
+ contextMenuItems.delete(this._page);
+ updateContextMenu();
}
};
- TabMap = function()
+ chrome.tabs.onActivated.addListener(updateContextMenu);
+
+ chrome.windows.onFocusChanged.addListener(function(windowId)
{
- this._map = {};
- this.delete = this.delete.bind(this);
- };
- TabMap.prototype = {
- get: function(tab)
- {
- return (this._map[tab._id] || {}).value;
- },
- set: function(tab, value)
- {
- if (!(tab._id in this._map))
- tab.onRemoved.addListener(this.delete);
+ if (windowId != chrome.windows.WINDOW_ID_NONE)
+ updateContextMenu();
+ });
- this._map[tab._id] = {tab: tab, value: value};
- },
- has: function(tab)
- {
- return tab._id in this._map;
- },
- clear: function()
- {
- for (var id in this._map)
- this.delete(this._map[id].tab);
- }
- };
- TabMap.prototype["delete"] = function(tab)
+
+ /* Web requests */
+
+ var framesOfTabs = {__proto__: null};
+
+ ext.getFrame = function(tabId, frameId)
{
- delete this._map[tab._id];
- tab.onRemoved.removeListener(this.delete);
- };
-
-
- /* Windows */
-
- Window = function(win)
- {
- this._id = win.id;
- this.visible = win.status != "minimized";
- };
- Window.prototype = {
- getAllTabs: function(callback)
- {
- chrome.tabs.query({windowId: this._id}, function(tabs)
- {
- callback(tabs.map(function(tab) { return new Tab(tab); }));
- });
- },
- getActiveTab: function(callback)
- {
- chrome.tabs.query({windowId: this._id, active: true}, function(tabs)
- {
- callback(new Tab(tabs[0]));
- });
- },
- openTab: function(url, callback)
- {
- var props = {windowId: this._id, url: url};
-
- if (!callback)
- chrome.tabs.create(props);
- else
- chrome.tabs.create(props, function(tab)
- {
- callback(new Tab(tab));
- });
- }
- };
-
-
- /* API */
-
- ext.windows = {
- getAll: function(callback)
- {
- chrome.windows.getAll(function(windows)
- {
- callback(windows.map(function(win)
- {
- return new Window(win);
- }));
- });
- },
- getLastFocused: function(callback)
- {
- chrome.windows.getLastFocused(function(win)
- {
- callback(new Window(win));
- });
- }
- };
-
- ext.tabs = {
- onLoading: new LoadingTabEventTarget(),
- onCompleted: new CompletedTabEventTarget(),
- onActivated: new ActivatedTabEventTarget(),
- onRemoved: new RemovedTabEventTarget()
+ return (framesOfTabs[tabId] || {})[frameId];
};
ext.webRequest = {
- onBeforeRequest: new BeforeRequestEventTarget(),
+ onBeforeRequest: new ext._EventTarget(true),
handlerBehaviorChanged: chrome.webRequest.handlerBehaviorChanged
};
- ext.contextMenus = {
- create: function(title, contexts, onclick)
+ chrome.tabs.query({}, function(tabs)
+ {
+ tabs.forEach(function(tab)
{
- chrome.contextMenus.create({
- title: title,
- contexts: contexts,
- onclick: function(info, tab)
+ chrome.webNavigation.getAllFrames({tabId: tab.id}, function(details)
+ {
+ if (details && details.length > 0)
{
- onclick(info.srcUrl, new Tab(tab));
+ var frames = framesOfTabs[tab.id] = {__proto__: null};
+
+ for (var i = 0; i < details.length; i++)
+ frames[details[i].frameId] = {url: details[i].url, parent: null};
+
+ for (var i = 0; i < details.length; i++)
+ {
+ var parentFrameId = details[i].parentFrameId;
+
+ if (parentFrameId != -1)
+ frames[details[i].frameId].parent = frames[parentFrameId];
+ }
}
});
- },
- removeAll: function(callback)
+ });
+ });
+
+ chrome.webRequest.onBeforeRequest.addListener(function(details)
+ {
+ try
{
- chrome.contextMenus.removeAll(callback);
+ // the high-level code isn't interested in requests that aren't related
+ // to a tab and since those can only be handled in Chrome, we ignore
+ // them here instead of in the browser independent high-level code.
+ if (details.tabId == -1)
+ return;
+
+ var isMainFrame = details.type == "main_frame" || (
+ // assume that the first request belongs to the top frame. Chrome
+ // may give the top frame the type "object" instead of "main_frame".
+ // https://code.google.com/p/chromium/issues/detail?id=281711
+ details.frameId == 0 && !(details.tabId in framesOfTabs)
+ );
+
+ var frames = null;
+ if (!isMainFrame)
+ frames = framesOfTabs[details.tabId];
+ if (!frames)
+ frames = framesOfTabs[details.tabId] = {__proto__: null};
+
+ var frame = null;
+ if (!isMainFrame)
+ {
+ // we are looking for the frame that contains the element that
+ // is about to load, however if a frame is loading the surrounding
+ // frame is indicated by parentFrameId instead of frameId
+ var frameId;
+ if (details.type == "sub_frame")
+ frameId = details.parentFrameId;
+ else
+ frameId = details.frameId;
+
+ frame = frames[frameId] || frames[Object.keys(frames)[0]];
+
+ if (frame && !ext.webRequest.onBeforeRequest._dispatch(details.url, details.type, new Page({id: details.tabId}), frame))
+ return {cancel: true};
+ }
+
+ if (isMainFrame || details.type == "sub_frame")
+ frames[details.frameId] = {url: details.url, parent: frame};
}
- };
+ catch (e)
+ {
+ // recent versions of Chrome cancel the request when an error occurs in
+ // the onBeforeRequest listener. However in our case it is preferred, to
+ // let potentially some ads through, rather than blocking legit requests.
+ console.error(e);
+ }
+ }, {urls: ["<all_urls>"]}, ["blocking"]);
+
+
+ /* Message passing */
+
+ chrome.runtime.onMessage.addListener(function(message, rawSender, sendResponse)
+ {
+ var sender = {
+ page: new Page(rawSender.tab),
+ frame: {
+ url: rawSender.url,
+ get parent()
+ {
+ var frames = framesOfTabs[rawSender.tab.id];
+
+ if (!frames)
+ return null;
+
+ for (var frameId in frames)
+ {
+ if (frames[frameId].url == rawSender.url)
+ return frames[frameId].parent;
+ }
+
+ return frames[0];
+ }
+ }
+ };
+
+ return ext.onMessage._dispatch(message, sender, sendResponse);
+ });
+
+
+ /* Storage */
+
+ ext.storage = localStorage;
})();
« no previous file with comments | « block.js ('k') | chrome/ext/common.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld