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

Unified Diff: chrome/ext/background.js

Issue 6346177440120832: Added abstraction for frames, to fix domain-based rules, whitelisting and ad counter on Safari (Closed)
Patch Set: Rebased and addressed comments Created Jan. 19, 2014, 10:40 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 | « background.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
===================================================================
--- a/chrome/ext/background.js
+++ b/chrome/ext/background.js
@@ -99,6 +99,22 @@
}
};
+ var BeforeNavigateTabEventTarget = function()
+ {
+ TabEventTarget.call(this, chrome.webNavigation.onBeforeNavigate);
+ };
+ BeforeNavigateTabEventTarget.prototype = {
+ __proto__: TabEventTarget.prototype,
+ _wrapListener: function(listener)
+ {
+ return function(details)
+ {
+ if (details.frameId == 0)
+ listener(new Tab({id: details.tabId, url: details.url}));
+ };
+ }
+ };
+
var LoadingTabEventTarget = function()
{
TabEventTarget.call(this, chrome.tabs.onUpdated);
@@ -161,33 +177,16 @@
}
};
- var BeforeRequestEventTarget = function()
+ var BackgroundMessageEventTarget = function()
{
- WrappedEventTarget.call(this, chrome.webRequest.onBeforeRequest);
- };
- BeforeRequestEventTarget.prototype = {
- __proto__: WrappedEventTarget.prototype,
- _wrapListener: function(listener)
+ MessageEventTarget.call(this);
+ }
+ BackgroundMessageEventTarget.prototype = {
+ __proto__: MessageEventTarget.prototype,
+ _wrapSender: function(sender)
{
- 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"]];
+ var tab = new Tab(sender.tab);
+ return {tab: tab, frame: new Frame({url: sender.url, tab: tab})};
}
};
@@ -241,16 +240,35 @@
Tab = function(tab)
{
this._id = tab.id;
+ this._url = tab.url;
- this.url = tab.url;
this.browserAction = new BrowserAction(tab.id);
+ this.onBeforeNavigate = ext.tabs.onBeforeNavigate._bindToTab(this);
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);
};
Tab.prototype = {
+ get url()
+ {
+ // usually our Tab objects are created from chrome Tab objects, which
+ // provide the url. So we can return the url given in the constructor.
+ if (this._url != null)
+ return this._url;
+
+ // but sometimes we only have the id when we create a Tab object.
+ // In that case we get the url from top frame of the tab, recorded by
+ // the onBeforeRequest handler.
+ var frames = framesOfTabs.get(this);
+ if (frames)
+ {
+ var frame = frames[0];
+ if (frame)
+ return frame.url;
+ }
+ },
close: function()
{
chrome.tabs.remove(this._id);
@@ -265,10 +283,12 @@
}
};
- TabMap = function()
+ TabMap = function(deleteTabOnBeforeNavigate)
{
this._map = {};
- this.delete = this.delete.bind(this);
+
+ this._delete = this._delete.bind(this);
+ this._deleteTabOnBeforeNavigate = deleteTabOnBeforeNavigate;
};
TabMap.prototype = {
get: function(tab)
@@ -278,7 +298,11 @@
set: function(tab, value)
{
if (!(tab._id in this._map))
- tab.onRemoved.addListener(this.delete);
+ {
+ tab.onRemoved.addListener(this._delete);
+ if (this._deleteTabOnBeforeNavigate)
+ tab.onBeforeNavigate.addListener(this._delete);
+ }
this._map[tab._id] = {tab: tab, value: value};
},
@@ -290,12 +314,19 @@
{
for (var id in this._map)
this.delete(this._map[id].tab);
+ },
+ _delete: function(tab)
+ {
+ // delay so that other event handlers can still lookup this tab
+ setTimeout(this.delete.bind(this, tab), 0);
}
};
TabMap.prototype["delete"] = function(tab)
{
delete this._map[tab._id];
- tab.onRemoved.removeListener(this.delete);
+
+ tab.onRemoved.removeListener(this._delete);
+ tab.onBeforeNavigate.removeListener(this._delete);
};
@@ -336,6 +367,118 @@
};
+ /* Frames */
+
+ var framesOfTabs = new TabMap();
+
+ Frame = function(params)
+ {
+ this._tab = params.tab;
+ this._id = params.id;
+ this._url = params.url;
+ };
+ Frame.prototype = {
+ get url()
+ {
+ if (this._url != null)
+ return this._url;
+
+ var frames = framesOfTabs.get(this._tab);
+ if (frames)
+ {
+ var frame = frames[this._id];
+ if (frame)
+ return frame.url;
+ }
+ },
+ get parent()
+ {
+ var frames = framesOfTabs.get(this._tab);
+ if (frames)
+ {
+ var frame;
+ if (this._id != null)
+ frame = frames[this._id];
+ else
+ {
+ // the frame ID wasn't available when we created
+ // the Frame object (e.g. for the onMessage event),
+ // so we have to find the frame details by their URL.
+ for (var frameId in frames)
+ {
+ if (frames[frameId].url == this._url)
+ {
+ frame = frames[frameId];
+ break;
+ }
+ }
+ }
+
+ if (frame && frame.parent != -1)
+ return new Frame({id: frame.parent, tab: this._tab});
+ else
Felix Dahlke 2014/01/20 00:02:15 No else before return, as described in the Mozilla
+ return null;
+ }
+ }
+ };
+
+
+ /* Web request blocking */
+
+ chrome.webRequest.onBeforeRequest.addListener(function(details)
+ {
+ // 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 tab = new Tab({id: details.tabId});
+ var frames = framesOfTabs.get(tab);
+
+ if (!frames)
+ {
+ frames = [];
+ framesOfTabs.set(tab, frames);
+
+ // 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
+ if (frameId == 0)
+ details.type = "main_frame";
+ }
+
+ var frameId;
+ if (details.type == "main_frame" || details.type == "sub_frame")
+ {
+ frameId = details.parentFrameId;
+ frames[details.frameId] = {url: details.url, parent: frameId};
+
+ // the high-level code isn't interested in top frame requests and
+ // since those can only be handled in Chrome, we ignore them here
+ // instead of in the browser independent high-level code.
+ if (details.type == "main_frame")
+ return;
+ }
+ else
+ frameId = details.frameId;
+
+ // the high-level code relies on the frame. However in case the frame can't
+ // be controlled by the extension or the extension was (re)loaded after the
+ // frame was loaded, the frame is unknown and we have to ignore the request.
+ if (!(frameId in frames))
+ return;
+
+ var frame = new Frame({id: frameId, tab: tab});
+
+ for (var i = 0; i < ext.webRequest.onBeforeRequest._listeners.length; i++)
+ {
+ if (ext.webRequest.onBeforeRequest._listeners[i](details.url, details.type, tab, frame) === false)
+ return {cancel: true};
+ }
+ }, {urls: ["<all_urls>"]}, ["blocking"]);
+
+
/* API */
ext.windows = {
@@ -359,6 +502,7 @@
};
ext.tabs = {
+ onBeforeNavigate: new BeforeNavigateTabEventTarget(),
onLoading: new LoadingTabEventTarget(),
onCompleted: new CompletedTabEventTarget(),
onActivated: new ActivatedTabEventTarget(),
@@ -366,7 +510,7 @@
};
ext.webRequest = {
- onBeforeRequest: new BeforeRequestEventTarget(),
+ onBeforeRequest: new SimpleEventTarget(),
handlerBehaviorChanged: chrome.webRequest.handlerBehaviorChanged
};
@@ -407,4 +551,6 @@
chrome.contextMenus.removeAll();
}
};
+
+ ext.onMessage = new BackgroundMessageEventTarget();
})();
« no previous file with comments | « background.js ('k') | chrome/ext/common.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld