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

Delta Between Two Patch Sets: chrome/ext/background.js

Issue 29372398: Issue 4804 - Avoid trashing pagemaps prematurely (Closed)
Left Patch Set: Created Jan. 18, 2017, 8:08 a.m.
Right Patch Set: Rebased Created Jan. 23, 2017, 10:19 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | lib/whitelisting.js » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2016 Eyeo GmbH 3 * Copyright (C) 2006-2016 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 "use strict";
19
19 { 20 {
20 /* Pages */ 21 /* Pages */
21 22
22 var Page = ext.Page = function(tab) 23 let Page = ext.Page = function(tab)
23 { 24 {
24 this.id = tab.id; 25 this.id = tab.id;
25 this._url = tab.url && new URL(tab.url); 26 this._url = tab.url && new URL(tab.url);
26 27
27 this.browserAction = new BrowserAction(tab.id); 28 this.browserAction = new BrowserAction(tab.id);
28 this.contextMenus = new ContextMenus(this); 29 this.contextMenus = new ContextMenus(this);
29 }; 30 };
30 Page.prototype = { 31 Page.prototype = {
31 get url() 32 get url()
32 { 33 {
33 // usually our Page objects are created from Chrome's Tab objects, which 34 // usually our Page objects are created from Chrome's Tab objects, which
34 // provide the url. So we can return the url given in the constructor. 35 // provide the url. So we can return the url given in the constructor.
35 if (this._url) 36 if (this._url)
36 return this._url; 37 return this._url;
37 38
38 // but sometimes we only have the tab id when we create a Page object. 39 // but sometimes we only have the tab id when we create a Page object.
39 // In that case we get the url from top frame of the tab, recorded by 40 // In that case we get the url from top frame of the tab, recorded by
40 // the onBeforeRequest handler. 41 // the onBeforeRequest handler.
41 var frames = framesOfTabs[this.id]; 42 let frames = framesOfTabs[this.id];
42 if (frames) 43 if (frames)
43 { 44 {
44 var frame = frames[0]; 45 let frame = frames[0];
45 if (frame) 46 if (frame)
46 return frame.url; 47 return frame.url;
47 } 48 }
48 }, 49 },
49 sendMessage: function(message, responseCallback) 50 sendMessage(message, responseCallback)
50 { 51 {
51 chrome.tabs.sendMessage(this.id, message, responseCallback); 52 chrome.tabs.sendMessage(this.id, message, responseCallback);
52 } 53 }
53 }; 54 };
54 55
55 ext.getPage = function(id) 56 ext.getPage = id => new Page({id: parseInt(id, 10)});
56 {
57 return new Page({id: parseInt(id, 10)});
58 };
59 57
60 function afterTabLoaded(callback) 58 function afterTabLoaded(callback)
61 { 59 {
62 return function(openedTab) 60 return openedTab =>
63 { 61 {
64 var onUpdated = function(tabId, changeInfo, tab) 62 let onUpdated = (tabId, changeInfo, tab) =>
65 { 63 {
66 if (tabId == openedTab.id && changeInfo.status == "complete") 64 if (tabId == openedTab.id && changeInfo.status == "complete")
67 { 65 {
68 chrome.tabs.onUpdated.removeListener(onUpdated); 66 chrome.tabs.onUpdated.removeListener(onUpdated);
69 callback(new Page(openedTab)); 67 callback(new Page(openedTab));
70 } 68 }
71 }; 69 };
72 chrome.tabs.onUpdated.addListener(onUpdated); 70 chrome.tabs.onUpdated.addListener(onUpdated);
73 }; 71 };
74 } 72 }
75 73
76 ext.pages = { 74 ext.pages = {
77 open: function(url, callback) 75 open(url, callback)
78 { 76 {
79 chrome.tabs.create({url: url}, callback && afterTabLoaded(callback)); 77 chrome.tabs.create({url: url}, callback && afterTabLoaded(callback));
80 }, 78 },
81 query: function(info, callback) 79 query(info, callback)
82 { 80 {
83 var rawInfo = {}; 81 let rawInfo = {};
84 for (var property in info) 82 for (let property in info)
85 { 83 {
86 switch (property) 84 switch (property)
87 { 85 {
88 case "active": 86 case "active":
89 case "lastFocusedWindow": 87 case "lastFocusedWindow":
90 rawInfo[property] = info[property]; 88 rawInfo[property] = info[property];
91 } 89 }
92 } 90 }
93 91
94 chrome.tabs.query(rawInfo, function(tabs) 92 chrome.tabs.query(rawInfo, tabs =>
95 { 93 {
96 callback(tabs.map(function(tab) 94 callback(tabs.map(tab => new Page(tab)));
97 {
98 return new Page(tab);
99 }));
100 }); 95 });
101 }, 96 },
102 onLoading: new ext._EventTarget(), 97 onLoading: new ext._EventTarget(),
103 onActivated: new ext._EventTarget(), 98 onActivated: new ext._EventTarget(),
104 onRemoved: new ext._EventTarget() 99 onRemoved: new ext._EventTarget()
105 }; 100 };
106 101
107 chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) 102 chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) =>
108 { 103 {
109 if (changeInfo.status == "loading") 104 if (changeInfo.status == "loading")
110 ext.pages.onLoading._dispatch(new Page(tab)); 105 ext.pages.onLoading._dispatch(new Page(tab));
111 }); 106 });
112 107
113 function createFrame(tabId, frameId) 108 function createFrame(tabId, frameId)
114 { 109 {
115 var frames = framesOfTabs[tabId]; 110 let frames = framesOfTabs[tabId];
116 if (!frames) 111 if (!frames)
117 frames = framesOfTabs[tabId] = Object.create(null); 112 frames = framesOfTabs[tabId] = Object.create(null);
118 113
119 var frame = frames[frameId]; 114 let frame = frames[frameId];
120 if (!frame) 115 if (!frame)
121 frame = frames[frameId] = {}; 116 frame = frames[frameId] = {};
122 117
123 return frame; 118 return frame;
124 } 119 }
125 120
126 function updatePageFrameStructure(frameId, tabId, url) 121 function updatePageFrameStructure(frameId, tabId, url)
127 { 122 {
128 if (frameId == 0) 123 if (frameId == 0)
129 { 124 {
130 let page = new Page({id: tabId, url: url}); 125 let page = new Page({id: tabId, url: url});
131 126
132 chrome.tabs.get(tabId, function() 127 chrome.tabs.get(tabId, () =>
133 { 128 {
134 // If the tab is prerendered, chrome.tabs.get() sets 129 // If the tab is prerendered, chrome.tabs.get() sets
135 // chrome.runtime.lastError and we have to dispatch the onLoading event, 130 // chrome.runtime.lastError and we have to dispatch the onLoading event,
136 // since the onUpdated event isn't dispatched for prerendered tabs. 131 // since the onUpdated event isn't dispatched for prerendered tabs.
137 // However, we have to keep relying on the unUpdated event for tabs that 132 // However, we have to keep relying on the unUpdated event for tabs that
138 // are already visible. Otherwise browser action changes get overridden 133 // are already visible. Otherwise browser action changes get overridden
139 // when Chrome automatically resets them on navigation. 134 // when Chrome automatically resets them on navigation.
140 if (chrome.runtime.lastError) 135 if (chrome.runtime.lastError)
141 ext.pages.onLoading._dispatch(page); 136 ext.pages.onLoading._dispatch(page);
142 }); 137 });
143 } 138 }
144 139
145 // Update frame URL in frame structure 140 // Update frame URL in frame structure
146 var frame = createFrame(tabId, frameId); 141 let frame = createFrame(tabId, frameId);
147 frame.url = new URL(url); 142 frame.url = new URL(url);
148 }; 143 };
149 144
150 chrome.webNavigation.onCommitted.addListener(function(details) 145 chrome.webNavigation.onCommitted.addListener(details =>
151 { 146 {
152 updatePageFrameStructure(details.frameId, details.tabId, details.url); 147 updatePageFrameStructure(details.frameId, details.tabId, details.url);
153 }); 148 });
154 149
155 chrome.webRequest.onHeadersReceived.addListener(function(details) 150 chrome.webRequest.onHeadersReceived.addListener(details =>
156 { 151 {
157 // Ideally we would only need the above chrome.webNavigation.onCommitted 152 // Ideally we would only need the above chrome.webNavigation.onCommitted
158 // listener to update the page state but unfortunately pages can make web 153 // listener to update the page state but unfortunately pages can make web
159 // requests before their onCommitted event fires[1]. 154 // requests before their onCommitted event fires[1].
160 // So for HTTP/S requests we can also use onHeadersReceived, being careful 155 // So for HTTP/S requests we can also use onHeadersReceived, being careful
161 // to ignore responses which won't directly result in a navigation, for 156 // to ignore responses which won't directly result in a navigation, for
162 // example redirections. 157 // example redirections.
163 // [1] - https://bugs.chromium.org/p/chromium/issues/detail?id=665843 158 // [1] - https://bugs.chromium.org/p/chromium/issues/detail?id=665843
164 /**** FIXME - Check Chromium code to ensure we got these right! ****/ 159 /**** FIXME - Check Chromium code to ensure we got these right! ****/
165 if (!(details.statusCode > 299 && details.statusCode < 400 && 160 if (!(details.statusCode > 299 && details.statusCode < 400 &&
166 details.statusCode != 304) || details.statusCode == 204) 161 details.statusCode != 304) || details.statusCode == 204)
167 updatePageFrameStructure(details.frameId, details.tabId, details.url); 162 updatePageFrameStructure(details.frameId, details.tabId, details.url);
168 }, {types: ["main_frame", "sub_frame"], urls: ["http://*/*", "https://*/*"]}); 163 }, {types: ["main_frame", "sub_frame"], urls: ["http://*/*", "https://*/*"]});
169 164
170 chrome.webNavigation.onBeforeNavigate.addListener(function(details) 165 chrome.webNavigation.onBeforeNavigate.addListener(details =>
171 { 166 {
172 // Capture parent frame here because onCommitted doesn't get this info. 167 // Capture parent frame here because onCommitted doesn't get this info.
173 var frame = createFrame(details.tabId, details.frameId); 168 let frame = createFrame(details.tabId, details.frameId);
174 frame.parent = framesOfTabs[details.tabId][details.parentFrameId] || null; 169 frame.parent = framesOfTabs[details.tabId][details.parentFrameId] || null;
175 170
176 // Since we can only listen for HTTP/S requests with onHeadersReceived we 171 // Since we can only listen for HTTP/S requests with onHeadersReceived we
177 // must update the page structure here for other navigations which might 172 // must update the page structure here for other navigations which might
178 // result in further requests. 173 // result in further requests.
179 let url = new URL(details.url); 174 let url = new URL(details.url);
180 if (url.protocol == "about:" || url.protocol == "data:") 175 if (url.protocol == "about:" || url.protocol == "data:")
181 updatePageFrameStructure(details.frameId, details.tabId, details.url); 176 updatePageFrameStructure(details.frameId, details.tabId, details.url);
182 }); 177 });
183 178
184 function forgetTab(tabId) 179 function forgetTab(tabId)
185 { 180 {
186 ext.pages.onRemoved._dispatch(tabId); 181 ext.pages.onRemoved._dispatch(tabId);
187 182
188 ext._removeFromAllPageMaps(tabId); 183 ext._removeFromAllPageMaps(tabId);
189 delete framesOfTabs[tabId]; 184 delete framesOfTabs[tabId];
190 } 185 }
191 186
192 chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId) 187 chrome.tabs.onReplaced.addListener((addedTabId, removedTabId) =>
193 { 188 {
194 forgetTab(removedTabId); 189 forgetTab(removedTabId);
195 }); 190 });
196 191
197 chrome.tabs.onRemoved.addListener(forgetTab); 192 chrome.tabs.onRemoved.addListener(forgetTab);
198 193
199 chrome.tabs.onActivated.addListener(function(details) 194 chrome.tabs.onActivated.addListener(details =>
200 { 195 {
201 ext.pages.onActivated._dispatch(new Page({id: details.tabId})); 196 ext.pages.onActivated._dispatch(new Page({id: details.tabId}));
202 }); 197 });
203 198
204 199
205 /* Browser actions */ 200 /* Browser actions */
206 201
207 var BrowserAction = function(tabId) 202 let BrowserAction = function(tabId)
208 { 203 {
209 this._tabId = tabId; 204 this._tabId = tabId;
210 this._changes = null; 205 this._changes = null;
211 }; 206 };
212 BrowserAction.prototype = { 207 BrowserAction.prototype = {
213 _applyChanges: function() 208 _applyChanges()
214 { 209 {
215 if ("iconPath" in this._changes) 210 if ("iconPath" in this._changes)
216 { 211 {
217 chrome.browserAction.setIcon({ 212 chrome.browserAction.setIcon({
218 tabId: this._tabId, 213 tabId: this._tabId,
219 path: { 214 path: {
220 16: this._changes.iconPath.replace("$size", "16"), 215 16: this._changes.iconPath.replace("$size", "16"),
221 19: this._changes.iconPath.replace("$size", "19"), 216 19: this._changes.iconPath.replace("$size", "19"),
222 20: this._changes.iconPath.replace("$size", "20"), 217 20: this._changes.iconPath.replace("$size", "20"),
223 32: this._changes.iconPath.replace("$size", "32"), 218 32: this._changes.iconPath.replace("$size", "32"),
(...skipping 14 matching lines...) Expand all
238 if ("badgeColor" in this._changes) 233 if ("badgeColor" in this._changes)
239 { 234 {
240 chrome.browserAction.setBadgeBackgroundColor({ 235 chrome.browserAction.setBadgeBackgroundColor({
241 tabId: this._tabId, 236 tabId: this._tabId,
242 color: this._changes.badgeColor 237 color: this._changes.badgeColor
243 }); 238 });
244 } 239 }
245 240
246 this._changes = null; 241 this._changes = null;
247 }, 242 },
248 _queueChanges: function() 243 _queueChanges()
249 { 244 {
250 chrome.tabs.get(this._tabId, function() 245 chrome.tabs.get(this._tabId, () =>
251 { 246 {
252 // If the tab is prerendered, chrome.tabs.get() sets 247 // If the tab is prerendered, chrome.tabs.get() sets
253 // chrome.runtime.lastError and we have to delay our changes 248 // chrome.runtime.lastError and we have to delay our changes
254 // until the currently visible tab is replaced with the 249 // until the currently visible tab is replaced with the
255 // prerendered tab. Otherwise chrome.browserAction.set* fails. 250 // prerendered tab. Otherwise chrome.browserAction.set* fails.
256 if (chrome.runtime.lastError) 251 if (chrome.runtime.lastError)
257 { 252 {
258 var onReplaced = function(addedTabId, removedTabId) 253 let onReplaced = (addedTabId, removedTabId) =>
259 { 254 {
260 if (addedTabId == this._tabId) 255 if (addedTabId == this._tabId)
261 { 256 {
262 chrome.tabs.onReplaced.removeListener(onReplaced); 257 chrome.tabs.onReplaced.removeListener(onReplaced);
263 this._applyChanges(); 258 this._applyChanges();
264 } 259 }
265 }.bind(this); 260 };
266 chrome.tabs.onReplaced.addListener(onReplaced); 261 chrome.tabs.onReplaced.addListener(onReplaced);
267 } 262 }
268 else 263 else
269 { 264 {
270 this._applyChanges(); 265 this._applyChanges();
271 } 266 }
272 }.bind(this)); 267 });
273 }, 268 },
274 _addChange: function(name, value) 269 _addChange(name, value)
275 { 270 {
276 if (!this._changes) 271 if (!this._changes)
277 { 272 {
278 this._changes = {}; 273 this._changes = {};
279 this._queueChanges(); 274 this._queueChanges();
280 } 275 }
281 276
282 this._changes[name] = value; 277 this._changes[name] = value;
283 }, 278 },
284 setIcon: function(path) 279 setIcon(path)
285 { 280 {
286 this._addChange("iconPath", path); 281 this._addChange("iconPath", path);
287 }, 282 },
288 setBadge: function(badge) 283 setBadge(badge)
289 { 284 {
290 if (!badge) 285 if (!badge)
291 { 286 {
292 this._addChange("badgeText", ""); 287 this._addChange("badgeText", "");
293 } 288 }
294 else 289 else
295 { 290 {
296 if ("number" in badge) 291 if ("number" in badge)
297 this._addChange("badgeText", badge.number.toString()); 292 this._addChange("badgeText", badge.number.toString());
298 293
299 if ("color" in badge) 294 if ("color" in badge)
300 this._addChange("badgeColor", badge.color); 295 this._addChange("badgeColor", badge.color);
301 } 296 }
302 } 297 }
303 }; 298 };
304 299
305 300
306 /* Context menus */ 301 /* Context menus */
307 302
308 var contextMenuItems = new ext.PageMap(); 303 let contextMenuItems = new ext.PageMap();
309 var contextMenuUpdating = false; 304 let contextMenuUpdating = false;
310 305
311 var updateContextMenu = function() 306 let updateContextMenu = () =>
312 { 307 {
313 if (contextMenuUpdating) 308 if (contextMenuUpdating)
314 return; 309 return;
315 310
316 contextMenuUpdating = true; 311 contextMenuUpdating = true;
317 312
318 chrome.tabs.query({active: true, lastFocusedWindow: true}, function(tabs) 313 chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs =>
319 { 314 {
320 chrome.contextMenus.removeAll(function() 315 chrome.contextMenus.removeAll(() =>
321 { 316 {
322 contextMenuUpdating = false; 317 contextMenuUpdating = false;
323 318
324 if (tabs.length == 0) 319 if (tabs.length == 0)
325 return; 320 return;
326 321
327 var items = contextMenuItems.get({id: tabs[0].id}); 322 let items = contextMenuItems.get({id: tabs[0].id});
328 323
329 if (!items) 324 if (!items)
330 return; 325 return;
331 326
332 items.forEach(function(item) 327 items.forEach(item =>
333 { 328 {
334 chrome.contextMenus.create({ 329 chrome.contextMenus.create({
335 title: item.title, 330 title: item.title,
336 contexts: item.contexts, 331 contexts: item.contexts,
337 onclick: function(info, tab) 332 onclick(info, tab)
338 { 333 {
339 item.onclick(new Page(tab)); 334 item.onclick(new Page(tab));
340 } 335 }
341 }); 336 });
342 }); 337 });
343 }); 338 });
344 }); 339 });
345 }; 340 };
346 341
347 var ContextMenus = function(page) 342 let ContextMenus = function(page)
348 { 343 {
349 this._page = page; 344 this._page = page;
350 }; 345 };
351 ContextMenus.prototype = { 346 ContextMenus.prototype = {
352 create: function(item) 347 create(item)
353 { 348 {
354 var items = contextMenuItems.get(this._page); 349 let items = contextMenuItems.get(this._page);
355 if (!items) 350 if (!items)
356 contextMenuItems.set(this._page, items = []); 351 contextMenuItems.set(this._page, items = []);
357 352
358 items.push(item); 353 items.push(item);
359 updateContextMenu(); 354 updateContextMenu();
360 }, 355 },
361 remove: function(item) 356 remove(item)
362 { 357 {
363 let items = contextMenuItems.get(this._page); 358 let items = contextMenuItems.get(this._page);
364 if (items) 359 if (items)
365 { 360 {
366 let index = items.indexOf(item); 361 let index = items.indexOf(item);
367 if (index != -1) 362 if (index != -1)
368 { 363 {
369 items.splice(index, 1); 364 items.splice(index, 1);
370 updateContextMenu(); 365 updateContextMenu();
371 } 366 }
372 } 367 }
373 } 368 }
374 }; 369 };
375 370
376 chrome.tabs.onActivated.addListener(updateContextMenu); 371 chrome.tabs.onActivated.addListener(updateContextMenu);
377 372
378 chrome.windows.onFocusChanged.addListener(function(windowId) 373 chrome.windows.onFocusChanged.addListener(windowId =>
379 { 374 {
380 if (windowId != chrome.windows.WINDOW_ID_NONE) 375 if (windowId != chrome.windows.WINDOW_ID_NONE)
381 updateContextMenu(); 376 updateContextMenu();
382 }); 377 });
383 378
384 379
385 /* Web requests */ 380 /* Web requests */
386 381
387 var framesOfTabs = Object.create(null); 382 let framesOfTabs = Object.create(null);
388 383
389 ext.getFrame = function(tabId, frameId) 384 ext.getFrame = (tabId, frameId) =>
390 { 385 {
391 return (framesOfTabs[tabId] || {})[frameId]; 386 return (framesOfTabs[tabId] || {})[frameId];
392 }; 387 };
393 388
394 var handlerBehaviorChangedQuota = chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANG ED_CALLS_PER_10_MINUTES; 389 let handlerBehaviorChangedQuota = chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANG ED_CALLS_PER_10_MINUTES;
395 390
396 function propagateHandlerBehaviorChange() 391 function propagateHandlerBehaviorChange()
397 { 392 {
398 // Make sure to not call handlerBehaviorChanged() more often than allowed 393 // Make sure to not call handlerBehaviorChanged() more often than allowed
399 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES. 394 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES.
400 // Otherwise Chrome notifies the user that this extension is causing issues. 395 // Otherwise Chrome notifies the user that this extension is causing issues.
401 if (handlerBehaviorChangedQuota > 0) 396 if (handlerBehaviorChangedQuota > 0)
402 { 397 {
403 chrome.webNavigation.onBeforeNavigate.removeListener(propagateHandlerBehav iorChange); 398 chrome.webNavigation.onBeforeNavigate.removeListener(propagateHandlerBehav iorChange);
404 chrome.webRequest.handlerBehaviorChanged(); 399 chrome.webRequest.handlerBehaviorChanged();
405 400
406 handlerBehaviorChangedQuota--; 401 handlerBehaviorChangedQuota--;
407 setTimeout(function() { handlerBehaviorChangedQuota++; }, 600000); 402 setTimeout(() => { handlerBehaviorChangedQuota++; }, 600000);
408 } 403 }
409 } 404 }
410 405
411 ext.webRequest = { 406 ext.webRequest = {
412 onBeforeRequest: new ext._EventTarget(), 407 onBeforeRequest: new ext._EventTarget(),
413 handlerBehaviorChanged: function() 408 handlerBehaviorChanged()
414 { 409 {
415 // Defer handlerBehaviorChanged() until navigation occurs. 410 // Defer handlerBehaviorChanged() until navigation occurs.
416 // There wouldn't be any visible effect when calling it earlier, 411 // There wouldn't be any visible effect when calling it earlier,
417 // but it's an expensive operation and that way we avoid to call 412 // but it's an expensive operation and that way we avoid to call
418 // it multiple times, if multiple filters are added/removed. 413 // it multiple times, if multiple filters are added/removed.
419 var onBeforeNavigate = chrome.webNavigation.onBeforeNavigate; 414 let onBeforeNavigate = chrome.webNavigation.onBeforeNavigate;
420 if (!onBeforeNavigate.hasListener(propagateHandlerBehaviorChange)) 415 if (!onBeforeNavigate.hasListener(propagateHandlerBehaviorChange))
421 onBeforeNavigate.addListener(propagateHandlerBehaviorChange); 416 onBeforeNavigate.addListener(propagateHandlerBehaviorChange);
422 } 417 }
423 }; 418 };
424 419
425 chrome.tabs.query({}, function(tabs) 420 chrome.tabs.query({}, tabs =>
426 { 421 {
427 tabs.forEach(function(tab) 422 tabs.forEach(tab =>
428 { 423 {
429 chrome.webNavigation.getAllFrames({tabId: tab.id}, function(details) 424 chrome.webNavigation.getAllFrames({tabId: tab.id}, details =>
430 { 425 {
431 if (details && details.length > 0) 426 if (details && details.length > 0)
432 { 427 {
433 var frames = framesOfTabs[tab.id] = Object.create(null); 428 let frames = framesOfTabs[tab.id] = Object.create(null);
434 429
435 for (var i = 0; i < details.length; i++) 430 for (let i = 0; i < details.length; i++)
436 frames[details[i].frameId] = {url: new URL(details[i].url), parent: null}; 431 frames[details[i].frameId] = {url: new URL(details[i].url), parent: null};
437 432
438 for (var i = 0; i < details.length; i++) 433 for (let i = 0; i < details.length; i++)
439 { 434 {
440 var parentFrameId = details[i].parentFrameId; 435 let parentFrameId = details[i].parentFrameId;
441 436
442 if (parentFrameId != -1) 437 if (parentFrameId != -1)
443 frames[details[i].frameId].parent = frames[parentFrameId]; 438 frames[details[i].frameId].parent = frames[parentFrameId];
444 } 439 }
445 } 440 }
446 }); 441 });
447 }); 442 });
448 }); 443 });
449 444
450 chrome.webRequest.onBeforeRequest.addListener(function(details) 445 chrome.webRequest.onBeforeRequest.addListener(details =>
451 { 446 {
452 // The high-level code isn't interested in requests that aren't 447 // The high-level code isn't interested in requests that aren't
453 // related to a tab or requests loading a top-level document, 448 // related to a tab or requests loading a top-level document,
454 // those should never be blocked. 449 // those should never be blocked.
455 if (details.tabId == -1 || details.type == "main_frame") 450 if (details.tabId == -1 || details.type == "main_frame")
456 return; 451 return;
457 452
458 // We are looking for the frame that contains the element which 453 // We are looking for the frame that contains the element which
459 // has triggered this request. For most requests (e.g. images) we 454 // has triggered this request. For most requests (e.g. images) we
460 // can just use the request's frame ID, but for subdocument requests 455 // can just use the request's frame ID, but for subdocument requests
461 // (e.g. iframes) we must instead use the request's parent frame ID. 456 // (e.g. iframes) we must instead use the request's parent frame ID.
462 var frameId; 457 let frameId;
463 var requestType; 458 let requestType;
464 if (details.type == "sub_frame") 459 if (details.type == "sub_frame")
465 { 460 {
466 frameId = details.parentFrameId; 461 frameId = details.parentFrameId;
467 requestType = "SUBDOCUMENT"; 462 requestType = "SUBDOCUMENT";
468 } 463 }
469 else 464 else
470 { 465 {
471 frameId = details.frameId; 466 frameId = details.frameId;
472 requestType = details.type.toUpperCase(); 467 requestType = details.type.toUpperCase();
473 } 468 }
474 469
475 var frame = ext.getFrame(details.tabId, frameId); 470 let frame = ext.getFrame(details.tabId, frameId);
476 if (frame) 471 if (frame)
477 { 472 {
478 var results = ext.webRequest.onBeforeRequest._dispatch( 473 let results = ext.webRequest.onBeforeRequest._dispatch(
479 new URL(details.url), 474 new URL(details.url),
480 requestType, 475 requestType,
481 new Page({id: details.tabId}), 476 new Page({id: details.tabId}),
482 frame 477 frame
483 ); 478 );
484 479
485 if (results.indexOf(false) != -1) 480 if (results.indexOf(false) != -1)
486 return {cancel: true}; 481 return {cancel: true};
487 } 482 }
488 }, {urls: ["http://*/*", "https://*/*"]}, ["blocking"]); 483 }, {urls: ["http://*/*", "https://*/*"]}, ["blocking"]);
489 484
490 485
491 /* Message passing */ 486 /* Message passing */
492 487
493 chrome.runtime.onMessage.addListener(function(message, rawSender, sendResponse ) 488 chrome.runtime.onMessage.addListener((message, rawSender, sendResponse) =>
494 { 489 {
495 var sender = {}; 490 let sender = {};
496 491
497 // Add "page" and "frame" if the message was sent by a content script. 492 // Add "page" and "frame" if the message was sent by a content script.
498 // If sent by popup or the background page itself, there is no "tab". 493 // If sent by popup or the background page itself, there is no "tab".
499 if ("tab" in rawSender) 494 if ("tab" in rawSender)
500 { 495 {
501 sender.page = new Page(rawSender.tab); 496 sender.page = new Page(rawSender.tab);
502 sender.frame = { 497 sender.frame = {
503 url: new URL(rawSender.url), 498 url: new URL(rawSender.url),
504 get parent() 499 get parent()
505 { 500 {
506 var frames = framesOfTabs[rawSender.tab.id]; 501 let frames = framesOfTabs[rawSender.tab.id];
507 502
508 if (!frames) 503 if (!frames)
509 return null; 504 return null;
510 505
511 var frame = frames[rawSender.frameId]; 506 let frame = frames[rawSender.frameId];
512 if (frame) 507 if (frame)
513 return frame.parent; 508 return frame.parent;
514 509
515 return frames[0]; 510 return frames[0];
516 } 511 }
517 }; 512 };
518 } 513 }
519 514
520 return ext.onMessage._dispatch(message, sender, sendResponse).indexOf(true) != -1; 515 return ext.onMessage._dispatch(message, sender, sendResponse).indexOf(true) != -1;
521 }); 516 });
522 517
523 518
524 /* Storage */ 519 /* Storage */
525 520
526 ext.storage = { 521 ext.storage = {
527 get: function(keys, callback) 522 get(keys, callback)
528 { 523 {
529 chrome.storage.local.get(keys, callback); 524 chrome.storage.local.get(keys, callback);
530 }, 525 },
531 set: function(key, value, callback) 526 set(key, value, callback)
532 { 527 {
533 let items = {}; 528 let items = {};
534 items[key] = value; 529 items[key] = value;
535 chrome.storage.local.set(items, callback); 530 chrome.storage.local.set(items, callback);
536 }, 531 },
537 remove: function(key, callback) 532 remove(key, callback)
538 { 533 {
539 chrome.storage.local.remove(key, callback); 534 chrome.storage.local.remove(key, callback);
540 }, 535 },
541 onChanged: chrome.storage.onChanged 536 onChanged: chrome.storage.onChanged
542 }; 537 };
543 538
544 /* Options */ 539 /* Options */
545 540
546 if ("openOptionsPage" in chrome.runtime) 541 if ("openOptionsPage" in chrome.runtime)
547 { 542 {
548 ext.showOptions = chrome.runtime.openOptionsPage; 543 ext.showOptions = callback =>
544 {
545 if (!callback)
546 {
547 chrome.runtime.openOptionsPage();
548 }
549 else
550 {
551 chrome.runtime.openOptionsPage(() =>
552 {
553 if (chrome.runtime.lastError)
554 return;
555
556 chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs =>
557 {
558 if (tabs.length > 0)
559 {
560 window.setTimeout(() =>
561 {
562 callback(new Page(tabs[0]));
563 });
564 }
565 });
566 });
567 }
568 };
549 } 569 }
550 else 570 else
551 { 571 {
552 // Edge does not yet support runtime.openOptionsPage (tested version 38) 572 // Edge does not yet support runtime.openOptionsPage (tested version 38)
553 // and so this workaround needs to stay for now. 573 // and so this workaround needs to stay for now.
554 ext.showOptions = function(callback) 574 ext.showOptions = callback =>
555 { 575 {
556 chrome.windows.getLastFocused(function(win) 576 chrome.windows.getLastFocused(win =>
557 { 577 {
558 var optionsUrl = chrome.extension.getURL("options.html"); 578 let optionsUrl = chrome.extension.getURL("options.html");
559 var queryInfo = {url: optionsUrl}; 579 let queryInfo = {url: optionsUrl};
560 580
561 // extension pages can't be accessed in incognito windows. In order to 581 // extension pages can't be accessed in incognito windows. In order to
562 // correctly mimic the way in which Chrome opens extension options, 582 // correctly mimic the way in which Chrome opens extension options,
563 // we have to focus the options page in any other window. 583 // we have to focus the options page in any other window.
564 if (!win.incognito) 584 if (!win.incognito)
565 queryInfo.windowId = win.id; 585 queryInfo.windowId = win.id;
566 586
567 chrome.tabs.query(queryInfo, function(tabs) 587 chrome.tabs.query(queryInfo, tabs =>
568 { 588 {
569 if (tabs.length > 0) 589 if (tabs.length > 0)
570 { 590 {
571 var tab = tabs[0]; 591 let tab = tabs[0];
572 592
573 chrome.windows.update(tab.windowId, {focused: true}); 593 chrome.windows.update(tab.windowId, {focused: true});
574 chrome.tabs.update(tab.id, {active: true}); 594 chrome.tabs.update(tab.id, {active: true});
575 595
576 if (callback) 596 if (callback)
577 callback(new Page(tab)); 597 callback(new Page(tab));
578 } 598 }
579 else 599 else
580 { 600 {
581 ext.pages.open(optionsUrl, callback); 601 ext.pages.open(optionsUrl, callback);
582 } 602 }
583 }); 603 });
584 }); 604 });
585 }; 605 };
586 } 606 }
587 607
588 /* Windows */ 608 /* Windows */
589 ext.windows = { 609 ext.windows = {
590 create: function(createData, callback) 610 create(createData, callback)
591 { 611 {
592 chrome.windows.create(createData, function(createdWindow) 612 chrome.windows.create(createData, createdWindow =>
593 { 613 {
594 afterTabLoaded(callback)(createdWindow.tabs[0]); 614 afterTabLoaded(callback)(createdWindow.tabs[0]);
595 }); 615 });
596 } 616 }
597 }; 617 };
598 })(); 618 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld