| OLD | NEW | 
|    1 /* |    1 /* | 
|    2  * This file is part of Adblock Plus <http://adblockplus.org/>, |    2  * This file is part of Adblock Plus <http://adblockplus.org/>, | 
|    3  * Copyright (C) 2006-2014 Eyeo GmbH |    3  * Copyright (C) 2006-2014 Eyeo GmbH | 
|    4  * |    4  * | 
|    5  * Adblock Plus is free software: you can redistribute it and/or modify |    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 |    6  * it under the terms of the GNU General Public License version 3 as | 
|    7  * published by the Free Software Foundation. |    7  * published by the Free Software Foundation. | 
|    8  * |    8  * | 
|    9  * Adblock Plus is distributed in the hope that it will be useful, |    9  * Adblock Plus is distributed in the hope that it will be useful, | 
|   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of |   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|   12  * GNU General Public License for more details. |   12  * GNU General Public License for more details. | 
|   13  * |   13  * | 
|   14  * You should have received a copy of the GNU General Public License |   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/>. |   15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|   16  */ |   16  */ | 
|   17  |   17  | 
|   18 (function() |   18 (function() | 
|   19 { |   19 { | 
|   20   /* Events */ |   20   /* Pages */ | 
|   21  |   21  | 
|   22   var TabEventTarget = function() |   22   var sendMessage = chrome.tabs.sendMessage || chrome.tabs.sendRequest; | 
 |   23  | 
 |   24   var Page = ext.Page = function(tab) | 
|   23   { |   25   { | 
|   24     WrappedEventTarget.apply(this, arguments); |   26     this._id = tab.id; | 
 |   27     this._url = tab.url; | 
|   25  |   28  | 
|   26     this._tabs = {}; |   29     this.browserAction = new BrowserAction(tab.id); | 
 |   30   }; | 
 |   31   Page.prototype = { | 
 |   32     get url() | 
 |   33     { | 
 |   34       // usually our Page objects are created from Chrome's Tab objects, which | 
 |   35       // provide the url. So we can return the url given in the constructor. | 
 |   36       if (this._url != null) | 
 |   37         return this._url; | 
|   27  |   38  | 
|   28     this._sharedListener = this._sharedListener.bind(this); |   39       // but sometimes we only have the tab id when we create a Page object. | 
|   29     this._removeTab = this._removeTab.bind(this); |   40       // In that case we get the url from top frame of the tab, recorded by | 
|   30   }; |   41       // the onBeforeRequest handler. | 
|   31   TabEventTarget.prototype = { |   42       var frames = framesOfTabs[this._id]; | 
|   32     __proto__: WrappedEventTarget.prototype, |   43       if (frames) | 
|   33     _bindToTab: function(tab) |   44       { | 
 |   45         var frame = frames[0]; | 
 |   46         if (frame) | 
 |   47           return frame.url; | 
 |   48       } | 
 |   49     }, | 
 |   50     activate: function() | 
|   34     { |   51     { | 
|   35       return { |   52       chrome.tabs.update(this._id, {selected: true}); | 
|   36         addListener: function(listener) |  | 
|   37         { |  | 
|   38           var listeners = this._tabs[tab._id]; |  | 
|   39  |  | 
|   40           if (!listeners) |  | 
|   41           { |  | 
|   42             this._tabs[tab._id] = listeners = []; |  | 
|   43  |  | 
|   44             if (Object.keys(this._tabs).length == 1) |  | 
|   45               this.addListener(this._sharedListener); |  | 
|   46  |  | 
|   47             tab.onRemoved.addListener(this._removeTab); |  | 
|   48           } |  | 
|   49  |  | 
|   50           listeners.push(listener); |  | 
|   51         }.bind(this), |  | 
|   52         removeListener: function(listener) |  | 
|   53         { |  | 
|   54           var listeners = this._tabs[tab._id]; |  | 
|   55           if (!listeners) |  | 
|   56             return; |  | 
|   57  |  | 
|   58           var idx = listeners.indexOf(listener); |  | 
|   59           if (idx == -1) |  | 
|   60             return; |  | 
|   61  |  | 
|   62           listeners.splice(idx, 1); |  | 
|   63  |  | 
|   64           if (listeners.length == 0) |  | 
|   65             tab.onRemoved.removeListener(this._removeTab); |  | 
|   66           else |  | 
|   67           { |  | 
|   68             if (listeners.length > 1) |  | 
|   69               return; |  | 
|   70             if (listeners[0] != this._removeTab) |  | 
|   71               return; |  | 
|   72           } |  | 
|   73  |  | 
|   74           this._removeTab(tab); |  | 
|   75         }.bind(this) |  | 
|   76       }; |  | 
|   77     }, |   53     }, | 
|   78     _sharedListener: function(tab) |   54     sendMessage: function(message, responseCallback) | 
|   79     { |   55     { | 
|   80       var listeners = this._tabs[tab._id]; |   56       sendMessage(this._id, message, responseCallback); | 
|   81  |  | 
|   82       if (!listeners) |  | 
|   83         return; |  | 
|   84  |  | 
|   85       // copy listeners before calling them, because they might |  | 
|   86       // add or remove other listeners, which must not be taken |  | 
|   87       // into account before the next occurrence of the event |  | 
|   88       listeners = listeners.slice(0); |  | 
|   89  |  | 
|   90       for (var i = 0; i < listeners.length; i++) |  | 
|   91         listeners[i](tab); |  | 
|   92     }, |  | 
|   93     _removeTab: function(tab) |  | 
|   94     { |  | 
|   95       delete this._tabs[tab._id]; |  | 
|   96  |  | 
|   97       if (Object.keys(this._tabs).length == 0) |  | 
|   98         this.removeListener(this._sharedListener); |  | 
|   99     } |   57     } | 
|  100   }; |   58   }; | 
|  101  |   59  | 
|  102   var BeforeNavigateTabEventTarget = function() |   60   ext.pages = { | 
|  103   { |   61     open: function(url, callback) | 
|  104     TabEventTarget.call(this, chrome.webNavigation.onBeforeNavigate); |  | 
|  105   }; |  | 
|  106   BeforeNavigateTabEventTarget.prototype = { |  | 
|  107     __proto__: TabEventTarget.prototype, |  | 
|  108     _wrapListener: function(listener) |  | 
|  109     { |   62     { | 
|  110       return function(details) |   63       if (callback) | 
|  111       { |   64       { | 
|  112         if (details.frameId == 0) |   65         chrome.tabs.create({url: url}, function(openedTab) | 
|  113           listener(new Tab({id: details.tabId, url: details.url})); |   66         { | 
|  114       }; |   67           var onUpdated = function(tabId, changeInfo, tab) | 
|  115     } |   68           { | 
 |   69             if (tabId == openedTab.id && changeInfo.status == "complete") | 
 |   70             { | 
 |   71               chrome.tabs.onUpdated.removeListener(onUpdated); | 
 |   72               callback(new Page(tab)); | 
 |   73             } | 
 |   74           }; | 
 |   75           chrome.tabs.onUpdated.addListener(onUpdated); | 
 |   76         }); | 
 |   77       } | 
 |   78       else | 
 |   79         chrome.tabs.create({url: url}); | 
 |   80     }, | 
 |   81     query: function(info, callback) | 
 |   82     { | 
 |   83       var rawInfo = {}; | 
 |   84       var visibleWindow = null; | 
 |   85  | 
 |   86       for (var property in info) | 
 |   87       { | 
 |   88         switch (property) | 
 |   89         { | 
 |   90           case "active": | 
 |   91           case "lastFocusedWindow": | 
 |   92             rawInfo[property] = info[property]; | 
 |   93             break; | 
 |   94           case "visibleWindow": | 
 |   95             visibleWindow = info[property]; | 
 |   96             break; | 
 |   97         } | 
 |   98       } | 
 |   99  | 
 |  100       chrome.tabs.query(rawInfo, function(tabs) | 
 |  101       { | 
 |  102         if (visibleWindow != null && tabs.length > 0) | 
 |  103         { | 
 |  104           var windows = {__proto__: null}; | 
 |  105           var pending = 0; | 
 |  106  | 
 |  107           for (var i = 0; i < tabs.length; i++) | 
 |  108           { | 
 |  109             var windowId = tabs[i].windowId; | 
 |  110             if (!(windowId in windows)) | 
 |  111             { | 
 |  112               windows[windowId] = null; | 
 |  113               pending++; | 
 |  114  | 
 |  115               chrome.windows.get(windowId, null, function(win) | 
 |  116               { | 
 |  117                 if (visibleWindow != (win.state != "minimized")) | 
 |  118                 { | 
 |  119                   for (var j = 0; j < tabs.length; j++) | 
 |  120                   { | 
 |  121                     if (tabs[j].windowId == win.id) | 
 |  122                       tabs.splice(j--, 1); | 
 |  123                   } | 
 |  124                 } | 
 |  125  | 
 |  126                 if (--pending == 0) | 
 |  127                   callback(tabs.map(function(tab) | 
 |  128                   { | 
 |  129                     return new Page(tab); | 
 |  130                   })); | 
 |  131               }); | 
 |  132             } | 
 |  133           } | 
 |  134         } | 
 |  135         else | 
 |  136         { | 
 |  137           callback(tabs.map(function(tab) | 
 |  138           { | 
 |  139             return new Page(tab); | 
 |  140           })); | 
 |  141         } | 
 |  142       }); | 
 |  143     }, | 
 |  144     onLoading: new ext._EventTarget() | 
|  116   }; |  145   }; | 
|  117  |  146  | 
|  118   var LoadingTabEventTarget = function() |  147   chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) | 
|  119   { |  148   { | 
|  120     TabEventTarget.call(this, chrome.tabs.onUpdated); |  149     if (changeInfo.status == "loading") | 
|  121   }; |  150       ext.pages.onLoading._dispatch(new Page(tab)); | 
|  122   LoadingTabEventTarget.prototype = { |  151   }); | 
|  123     __proto__: TabEventTarget.prototype, |  | 
|  124     _wrapListener: function(listener) |  | 
|  125     { |  | 
|  126       return function(id, info, tab) |  | 
|  127       { |  | 
|  128         if (info.status == "loading") |  | 
|  129           listener(new Tab(tab)); |  | 
|  130       }; |  | 
|  131     } |  | 
|  132   }; |  | 
|  133  |  152  | 
|  134   var CompletedTabEventTarget = function() |  153   chrome.webNavigation.onBeforeNavigate.addListener(function(details) | 
|  135   { |  154   { | 
|  136     TabEventTarget.call(this, chrome.tabs.onUpdated); |  155     if (details.frameId == 0) | 
|  137   }; |  156       ext._removeFromAllPageMaps(details.tabId); | 
|  138   CompletedTabEventTarget.prototype = { |  157   }); | 
|  139     __proto__: TabEventTarget.prototype, |  | 
|  140     _wrapListener: function(listener) |  | 
|  141     { |  | 
|  142       return function(id, info, tab) |  | 
|  143       { |  | 
|  144         if (info.status == "complete") |  | 
|  145           listener(new Tab(tab)); |  | 
|  146       }; |  | 
|  147     } |  | 
|  148   }; |  | 
|  149  |  158  | 
|  150   var ActivatedTabEventTarget = function() |  159   chrome.tabs.onRemoved.addListener(function(tabId) | 
|  151   { |  160   { | 
|  152     TabEventTarget.call(this, chrome.tabs.onActivated); |  161     ext._removeFromAllPageMaps(tabId); | 
|  153   }; |  162     delete framesOfTabs[tabId]; | 
|  154   ActivatedTabEventTarget.prototype = { |  163   }); | 
|  155     __proto__: TabEventTarget.prototype, |  | 
|  156     _wrapListener: function(listener) |  | 
|  157     { |  | 
|  158       return function(info) |  | 
|  159       { |  | 
|  160         chrome.tabs.get(info.tabId, function(tab) |  | 
|  161         { |  | 
|  162           listener(new Tab(tab)); |  | 
|  163         }); |  | 
|  164       }; |  | 
|  165     } |  | 
|  166   } |  | 
|  167  |  | 
|  168   var RemovedTabEventTarget = function() |  | 
|  169   { |  | 
|  170     TabEventTarget.call(this, chrome.tabs.onRemoved); |  | 
|  171   }; |  | 
|  172   RemovedTabEventTarget.prototype = { |  | 
|  173     __proto__: TabEventTarget.prototype, |  | 
|  174     _wrapListener: function(listener) |  | 
|  175     { |  | 
|  176       return function(id) { listener(new Tab({id: id})); }; |  | 
|  177     } |  | 
|  178   }; |  | 
|  179  |  | 
|  180   var BackgroundMessageEventTarget = function() |  | 
|  181   { |  | 
|  182     MessageEventTarget.call(this); |  | 
|  183   } |  | 
|  184   BackgroundMessageEventTarget.prototype = { |  | 
|  185     __proto__: MessageEventTarget.prototype, |  | 
|  186     _wrapSender: function(sender) |  | 
|  187     { |  | 
|  188       var tab = new Tab(sender.tab); |  | 
|  189        |  | 
|  190       //url parameter is missing in sender object (Chrome v28 and below) |  | 
|  191       if (!("url" in sender)) |  | 
|  192         sender.url = tab.url; |  | 
|  193       return {tab: tab, frame: new Frame({url: sender.url, tab: tab})}; |  | 
|  194     } |  | 
|  195   }; |  | 
|  196  |  164  | 
|  197  |  165  | 
|  198   /* Tabs */ |  166   /* Browser actions */ | 
|  199  |  | 
|  200   var sendMessage = chrome.tabs.sendMessage || chrome.tabs.sendRequest; |  | 
|  201  |  167  | 
|  202   var BrowserAction = function(tabId) |  168   var BrowserAction = function(tabId) | 
|  203   { |  169   { | 
|  204     this._tabId = tabId; |  170     this._tabId = tabId; | 
|  205   }; |  171   }; | 
|  206   BrowserAction.prototype = { |  172   BrowserAction.prototype = { | 
|  207     setIcon: function(path) |  173     setIcon: function(path) | 
|  208     { |  174     { | 
|  209       var paths = {}; |  175       var paths = {}; | 
|  210       for (var i = 1; i <= 2; i++) |  176       for (var i = 1; i <= 2; i++) | 
| (...skipping 26 matching lines...) Expand all  Loading... | 
|  237       if ("number" in badge) |  203       if ("number" in badge) | 
|  238       { |  204       { | 
|  239         chrome.browserAction.setBadgeText({ |  205         chrome.browserAction.setBadgeText({ | 
|  240           tabId: this._tabId, |  206           tabId: this._tabId, | 
|  241           text: badge.number.toString() |  207           text: badge.number.toString() | 
|  242         }); |  208         }); | 
|  243       } |  209       } | 
|  244     } |  210     } | 
|  245   }; |  211   }; | 
|  246  |  212  | 
|  247   Tab = function(tab) |  | 
|  248   { |  | 
|  249     this._id = tab.id; |  | 
|  250     this._url = tab.url; |  | 
|  251  |  | 
|  252     this.browserAction = new BrowserAction(tab.id); |  | 
|  253  |  | 
|  254     this.onLoading = ext.tabs.onLoading._bindToTab(this); |  | 
|  255     this.onCompleted = ext.tabs.onCompleted._bindToTab(this); |  | 
|  256     this.onActivated = ext.tabs.onActivated._bindToTab(this); |  | 
|  257     this.onRemoved = ext.tabs.onRemoved._bindToTab(this); |  | 
|  258  |  | 
|  259     // the "beforeNavigate" event in Safari isn't dispatched when a new URL |  | 
|  260     // was entered into the address bar. So we can only use it only on Chrome, |  | 
|  261     // but we have to hide it from the browser-independent high level code. |  | 
|  262     this._onBeforeNavigate = ext.tabs._onBeforeNavigate._bindToTab(this); |  | 
|  263   }; |  | 
|  264   Tab.prototype = { |  | 
|  265     get url() |  | 
|  266     { |  | 
|  267       // usually our Tab objects are created from chrome Tab objects, which |  | 
|  268       // provide the url. So we can return the url given in the constructor. |  | 
|  269       if (this._url != null) |  | 
|  270         return this._url; |  | 
|  271  |  | 
|  272       // but sometimes we only have the id when we create a Tab object. |  | 
|  273       // In that case we get the url from top frame of the tab, recorded by |  | 
|  274       // the onBeforeRequest handler. |  | 
|  275       var frames = framesOfTabs.get(this); |  | 
|  276       if (frames) |  | 
|  277       { |  | 
|  278         var frame = frames[0]; |  | 
|  279         if (frame) |  | 
|  280           return frame.url; |  | 
|  281       } |  | 
|  282     }, |  | 
|  283     close: function() |  | 
|  284     { |  | 
|  285       chrome.tabs.remove(this._id); |  | 
|  286     }, |  | 
|  287     activate: function() |  | 
|  288     { |  | 
|  289       chrome.tabs.update(this._id, {selected: true}); |  | 
|  290     }, |  | 
|  291     sendMessage: function(message, responseCallback) |  | 
|  292     { |  | 
|  293       sendMessage(this._id, message, responseCallback); |  | 
|  294     } |  | 
|  295   }; |  | 
|  296  |  | 
|  297   TabMap = function(deleteOnPageUnload) |  | 
|  298   { |  | 
|  299     this._map = {}; |  | 
|  300  |  | 
|  301     this._delete = this._delete.bind(this); |  | 
|  302     this._deleteOnPageUnload = deleteOnPageUnload; |  | 
|  303   }; |  | 
|  304   TabMap.prototype = { |  | 
|  305     get: function(tab) |  | 
|  306     { |  | 
|  307       return (this._map[tab._id] || {}).value; |  | 
|  308     }, |  | 
|  309     set: function(tab, value) |  | 
|  310     { |  | 
|  311       if (!(tab._id in this._map)) |  | 
|  312       { |  | 
|  313         tab.onRemoved.addListener(this._delete); |  | 
|  314         if (this._deleteOnPageUnload) |  | 
|  315           tab._onBeforeNavigate.addListener(this._delete); |  | 
|  316       } |  | 
|  317  |  | 
|  318       this._map[tab._id] = {tab: tab, value: value}; |  | 
|  319     }, |  | 
|  320     has: function(tab) |  | 
|  321     { |  | 
|  322       return tab._id in this._map; |  | 
|  323     }, |  | 
|  324     clear: function() |  | 
|  325     { |  | 
|  326       for (var id in this._map) |  | 
|  327         this.delete(this._map[id].tab); |  | 
|  328     }, |  | 
|  329     _delete: function(tab) |  | 
|  330     { |  | 
|  331       // delay so that other event handlers can still lookup this tab |  | 
|  332       setTimeout(this.delete.bind(this, tab), 0); |  | 
|  333     }, |  | 
|  334     delete: function(tab) |  | 
|  335     { |  | 
|  336       delete this._map[tab._id]; |  | 
|  337  |  | 
|  338       tab.onRemoved.removeListener(this._delete); |  | 
|  339       tab._onBeforeNavigate.removeListener(this._delete); |  | 
|  340     } |  | 
|  341   }; |  | 
|  342  |  | 
|  343  |  | 
|  344   /* Windows */ |  | 
|  345  |  | 
|  346   Window = function(win) |  | 
|  347   { |  | 
|  348     this._id = win.id; |  | 
|  349     this.visible = win.status != "minimized"; |  | 
|  350   }; |  | 
|  351   Window.prototype = { |  | 
|  352     getAllTabs: function(callback) |  | 
|  353     { |  | 
|  354       chrome.tabs.query({windowId: this._id}, function(tabs) |  | 
|  355       { |  | 
|  356         callback(tabs.map(function(tab) { return new Tab(tab); })); |  | 
|  357       }); |  | 
|  358     }, |  | 
|  359     getActiveTab: function(callback) |  | 
|  360     { |  | 
|  361       chrome.tabs.query({windowId: this._id, active: true}, function(tabs) |  | 
|  362       { |  | 
|  363         callback(new Tab(tabs[0])); |  | 
|  364       }); |  | 
|  365     }, |  | 
|  366     openTab: function(url, callback) |  | 
|  367     { |  | 
|  368       var props = {windowId: this._id, url: url}; |  | 
|  369  |  | 
|  370       if (!callback) |  | 
|  371         chrome.tabs.create(props); |  | 
|  372       else |  | 
|  373         chrome.tabs.create(props, function(tab) |  | 
|  374         { |  | 
|  375           callback(new Tab(tab)); |  | 
|  376         }); |  | 
|  377     } |  | 
|  378   }; |  | 
|  379  |  | 
|  380  |  213  | 
|  381   /* Frames */ |  214   /* Frames */ | 
|  382  |  215  | 
|  383   var framesOfTabs = new TabMap(); |  216   var framesOfTabs = {__proto__: null}; | 
|  384  |  217  | 
|  385   Frame = function(params) |  218   var Frame = ext.Frame = function(params) | 
|  386   { |  219   { | 
|  387     this._tab = params.tab; |  220     this._frameId = params.frameId; | 
|  388     this._id = params.id; |  221     this._tabId = params.tabId; | 
|  389     this._url = params.url; |  222     this._url = params.url; | 
|  390   }; |  223   }; | 
|  391   Frame.prototype = { |  224   Frame.prototype = { | 
|  392     get url() |  225     get url() | 
|  393     { |  226     { | 
|  394       if (this._url != null) |  227       if (this._url != null) | 
|  395         return this._url; |  228         return this._url; | 
|  396  |  229  | 
|  397       var frames = framesOfTabs.get(this._tab); |  230       var frames = framesOfTabs[this._tabId]; | 
|  398       if (frames) |  231       if (frames) | 
|  399       { |  232       { | 
|  400         var frame = frames[this._id]; |  233         var frame = frames[this._frameId]; | 
|  401         if (frame) |  234         if (frame) | 
|  402           return frame.url; |  235           return frame.url; | 
|  403       } |  236       } | 
|  404     }, |  237     }, | 
|  405     get parent() |  238     get parent() | 
|  406     { |  239     { | 
|  407       var frames = framesOfTabs.get(this._tab); |  240       var frames = framesOfTabs[this._tabId]; | 
|  408       if (frames) |  241       if (frames) | 
|  409       { |  242       { | 
|  410         var frame; |  243         var frame; | 
|  411         if (this._id != null) |  244         if (this._frameId != null) | 
|  412           frame = frames[this._id]; |  245           frame = frames[this._frameId]; | 
|  413         else |  246         else | 
|  414         { |  247         { | 
|  415           // the frame ID wasn't available when we created |  248           // the frame ID wasn't available when we created | 
|  416           // the Frame object (e.g. for the onMessage event), |  249           // the Frame object (e.g. for the onMessage event), | 
|  417           // so we have to find the frame details by their URL. |  250           // so we have to find the frame details by their URL. | 
|  418           for (var frameId in frames) |  251           for (var frameId in frames) | 
|  419           { |  252           { | 
|  420             if (frames[frameId].url == this._url) |  253             if (frames[frameId].url == this._url) | 
|  421             { |  254             { | 
|  422               frame = frames[frameId]; |  255               frame = frames[frameId]; | 
|  423               break; |  256               break; | 
|  424             } |  257             } | 
|  425           } |  258           } | 
|  426         } |  259         } | 
|  427  |  260  | 
|  428         if (!frame || frame.parent == -1) |  261         if (!frame || frame.parent == -1) | 
|  429           return null; |  262           return null; | 
|  430  |  263  | 
|  431         return new Frame({id: frame.parent, tab: this._tab}); |  264         return new Frame({frameId: frame.parent, tabId: this._tabId}); | 
|  432       } |  265       } | 
|  433     } |  266     } | 
|  434   }; |  267   }; | 
|  435  |  268  | 
|  436  |  269  | 
|  437   /* Web request blocking */ |  270   /* Web requests */ | 
 |  271  | 
 |  272   ext.webRequest = { | 
 |  273     onBeforeRequest: new ext._EventTarget(true), | 
 |  274     handlerBehaviorChanged: chrome.webRequest.handlerBehaviorChanged | 
 |  275   }; | 
|  438  |  276  | 
|  439   chrome.webRequest.onBeforeRequest.addListener(function(details) |  277   chrome.webRequest.onBeforeRequest.addListener(function(details) | 
|  440   { |  278   { | 
|  441     try |  279     try | 
|  442     { |  280     { | 
|  443       // the high-level code isn't interested in requests that aren't related |  281       // the high-level code isn't interested in requests that aren't related | 
|  444       // to a tab and since those can only be handled in Chrome, we ignore |  282       // to a tab and since those can only be handled in Chrome, we ignore | 
|  445       // them here instead of in the browser independent high-level code. |  283       // them here instead of in the browser independent high-level code. | 
|  446       if (details.tabId == -1) |  284       if (details.tabId == -1) | 
|  447         return; |  285         return; | 
|  448  |  286  | 
|  449       var tab = new Tab({id: details.tabId}); |  287       var page = new Tab({id: details.tabId}); | 
|  450       var frames = framesOfTabs.get(tab); |  288       var frames = framesOfTabs[details.tabId]; | 
|  451  |  289  | 
|  452       if (!frames) |  290       if (!frames) | 
|  453       { |  291       { | 
|  454         frames = []; |  292         frames = framesOfTabs[details.tabId] = []; | 
|  455         framesOfTabs.set(tab, frames); |  | 
|  456  |  293  | 
|  457         // assume that the first request belongs to the top frame. Chrome |  294         // assume that the first request belongs to the top frame. Chrome | 
|  458         // may give the top frame the type "object" instead of "main_frame". |  295         // may give the top frame the type "object" instead of "main_frame". | 
|  459         // https://code.google.com/p/chromium/issues/detail?id=281711 |  296         // https://code.google.com/p/chromium/issues/detail?id=281711 | 
|  460         if (frameId == 0) |  297         if (frameId == 0) | 
|  461           details.type = "main_frame"; |  298           details.type = "main_frame"; | 
|  462       } |  299       } | 
|  463  |  300  | 
|  464       var frameId; |  301       var frameId; | 
|  465       if (details.type == "main_frame" || details.type == "sub_frame") |  302       if (details.type == "main_frame" || details.type == "sub_frame") | 
| (...skipping 20 matching lines...) Expand all  Loading... | 
|  486  |  323  | 
|  487         // however when the src of the frame is a javascript: or data: URL, we |  324         // however when the src of the frame is a javascript: or data: URL, we | 
|  488         // don't know the frame either. But since we know the top-level frame we |  325         // don't know the frame either. But since we know the top-level frame we | 
|  489         // can just pretend that we are in the top-level frame, in order to have |  326         // can just pretend that we are in the top-level frame, in order to have | 
|  490         // at least most domain-based filter rules working. |  327         // at least most domain-based filter rules working. | 
|  491         frameId = 0; |  328         frameId = 0; | 
|  492         if (details.type == "sub_frame") |  329         if (details.type == "sub_frame") | 
|  493           frames[details.frameId].parent = frameId; |  330           frames[details.frameId].parent = frameId; | 
|  494       } |  331       } | 
|  495  |  332  | 
|  496       var frame = new Frame({id: frameId, tab: tab}); |  333       var frame = new Frame({id: frameId, tabId: details.tabId}); | 
|  497  |  334  | 
|  498       for (var i = 0; i < ext.webRequest.onBeforeRequest._listeners.length; i++) |  335       if (!ext.webRequest.onBeforeRequest._dispatch(details.url, details.type, p
     age, frame)) | 
|  499       { |  336         return {cancel: true}; | 
|  500         if (ext.webRequest.onBeforeRequest._listeners[i](details.url, details.ty
     pe, tab, frame) === false) |  | 
|  501           return {cancel: true}; |  | 
|  502       } |  | 
|  503     } |  337     } | 
|  504     catch (e) |  338     catch (e) | 
|  505     { |  339     { | 
|  506       // recent versions of Chrome cancel the request when an error occurs in |  340       // recent versions of Chrome cancel the request when an error occurs in | 
|  507       // the onBeforeRequest listener. However in our case it is preferred, to |  341       // the onBeforeRequest listener. However in our case it is preferred, to | 
|  508       // let potentially some ads through, rather than blocking legit requests. |  342       // let potentially some ads through, rather than blocking legit requests. | 
|  509       console.error(e); |  343       console.error(e); | 
|  510     } |  344     } | 
|  511   }, {urls: ["<all_urls>"]}, ["blocking"]); |  345   }, {urls: ["<all_urls>"]}, ["blocking"]); | 
|  512  |  346  | 
|  513  |  347  | 
|  514   /* API */ |  348   /* Context menus */ | 
|  515  |  | 
|  516   ext.windows = { |  | 
|  517     getAll: function(callback) |  | 
|  518     { |  | 
|  519       chrome.windows.getAll(function(windows) |  | 
|  520       { |  | 
|  521         callback(windows.map(function(win) |  | 
|  522         { |  | 
|  523           return new Window(win); |  | 
|  524         })); |  | 
|  525       }); |  | 
|  526     }, |  | 
|  527     getLastFocused: function(callback) |  | 
|  528     { |  | 
|  529       chrome.windows.getLastFocused(function(win) |  | 
|  530       { |  | 
|  531         callback(new Window(win)); |  | 
|  532       }); |  | 
|  533     } |  | 
|  534   }; |  | 
|  535  |  | 
|  536   ext.tabs = { |  | 
|  537     onLoading: new LoadingTabEventTarget(), |  | 
|  538     onCompleted: new CompletedTabEventTarget(), |  | 
|  539     onActivated: new ActivatedTabEventTarget(), |  | 
|  540     onRemoved: new RemovedTabEventTarget(), |  | 
|  541  |  | 
|  542     // the "beforeNavigate" event in Safari isn't dispatched when a new URL |  | 
|  543     // was entered into the address bar. So we can only use it only on Chrome, |  | 
|  544     // but we have to hide it from the browser-independent high level code. |  | 
|  545     _onBeforeNavigate: new BeforeNavigateTabEventTarget() |  | 
|  546   }; |  | 
|  547  |  | 
|  548   ext.webRequest = { |  | 
|  549     onBeforeRequest: new SimpleEventTarget(), |  | 
|  550     handlerBehaviorChanged: chrome.webRequest.handlerBehaviorChanged |  | 
|  551   }; |  | 
|  552  |  | 
|  553   ext.storage = localStorage; |  | 
|  554  |  349  | 
|  555   var contextMenuItems = []; |  350   var contextMenuItems = []; | 
|  556   var isContextMenuHidden = true; |  351   var isContextMenuHidden = true; | 
 |  352  | 
|  557   ext.contextMenus = { |  353   ext.contextMenus = { | 
|  558     addMenuItem: function(title, contexts, onclick) |  354     addMenuItem: function(title, contexts, onclick) | 
|  559     { |  355     { | 
|  560       contextMenuItems.push({ |  356       contextMenuItems.push({ | 
|  561         title: title, |  357         title: title, | 
|  562         contexts: contexts, |  358         contexts: contexts, | 
|  563         onclick: function(info, tab) |  359         onclick: function(info, tab) | 
|  564         { |  360         { | 
|  565           onclick(info.srcUrl, new Tab(tab)); |  361           onclick(info.srcUrl, new Page(tab)); | 
|  566         } |  362         } | 
|  567       }); |  363       }); | 
|  568       this.showMenuItems(); |  364       this.showMenuItems(); | 
|  569     }, |  365     }, | 
|  570     removeMenuItems: function() |  366     removeMenuItems: function() | 
|  571     { |  367     { | 
|  572       contextMenuItems = []; |  368       contextMenuItems = []; | 
|  573       this.hideMenuItems(); |  369       this.hideMenuItems(); | 
|  574     }, |  370     }, | 
|  575     showMenuItems: function() |  371     showMenuItems: function() | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
|  594     hideMenuItems: function() |  390     hideMenuItems: function() | 
|  595     { |  391     { | 
|  596       if (isContextMenuHidden) |  392       if (isContextMenuHidden) | 
|  597         return; |  393         return; | 
|  598  |  394  | 
|  599       chrome.contextMenus.removeAll(); |  395       chrome.contextMenus.removeAll(); | 
|  600       isContextMenuHidden = true; |  396       isContextMenuHidden = true; | 
|  601     } |  397     } | 
|  602   }; |  398   }; | 
|  603  |  399  | 
|  604   ext.onMessage = new BackgroundMessageEventTarget(); |  400  | 
 |  401   /* Message passing */ | 
 |  402  | 
 |  403   ext._setupMessageListener(function(sender) | 
 |  404   { | 
 |  405     return { | 
 |  406       page: new Page(sender.tab), | 
 |  407       frame: new Frame({url: sender.url, tabId: sender.tab.id}) | 
 |  408     }; | 
 |  409   }); | 
 |  410  | 
 |  411  | 
 |  412   /* Storage */ | 
 |  413  | 
 |  414   ext.storage = localStorage; | 
|  605 })(); |  415 })(); | 
| OLD | NEW |