OLD | NEW |
---|---|
1 // Copyright 2016 Google Inc. All rights reserved. | 1 // Copyright 2016 Google Inc. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // Load the options, then register a keyboard listener. We capture the event | 5 // Load the options, then register a keyboard listener. We capture the event |
6 // at the Window to let any handlers or listeners registered on the Document | 6 // at the Window to let any handlers or listeners registered on the Document |
7 // have a chance to handle it first. | 7 // have a chance to handle it first. |
8 var options; | 8 var options; |
9 chrome.storage.sync.get({ | 9 chrome.storage.sync.get({ |
10 blacklist: [], | 10 blacklist: [], |
11 disableInApplets: true, | 11 disableInApplets: true, |
12 whitelist: [] | 12 whitelist: [] |
13 }, function(items) { | 13 }, function(items) { |
14 options = items; | 14 options = items; |
15 window.addEventListener('keydown', function(e) { | 15 window.addEventListener('keydown', handleBackspace); |
16 handleBackspace(e); | |
17 }); | |
18 }); | 16 }); |
19 | 17 |
20 // Update the local options when they're changed externally. | 18 // Update the local options when they're changed externally. |
21 chrome.storage.onChanged.addListener(function(changes, area) { | 19 chrome.storage.onChanged.addListener(function(changes, area) { |
22 if (area === 'sync') { | 20 if (area === 'sync') { |
23 if (changes.blacklist) | 21 if (changes.blacklist) |
24 options.blacklist = changes.blacklist.newValue; | 22 options.blacklist = changes.blacklist.newValue; |
25 if (changes.disableInApplets) | 23 if (changes.disableInApplets) |
26 options.disableInApplets = changes.disableInApplets.newValue; | 24 options.disableInApplets = changes.disableInApplets.newValue; |
27 if (changes.whitelist) | 25 if (changes.whitelist) |
28 options.whitelist = changes.whitelist.newValue; | 26 options.whitelist = changes.whitelist.newValue; |
29 } | 27 } |
30 }); | 28 }); |
31 | 29 |
32 // Check for shift-backspace or unmodified backspace and navigate if | 30 // Check for shift-backspace or unmodified backspace and navigate if |
33 // applicable. | 31 // applicable. |
34 function handleBackspace(e) { | 32 function handleBackspace(e) { |
35 if (e.defaultPrevented || | 33 if (e.defaultPrevented || |
36 e.key !== 'Backspace' || | 34 e.key !== 'Backspace' || |
37 e.altKey || | 35 e.altKey || |
38 e.ctrlKey || | 36 e.ctrlKey || |
39 e.metaKey) | 37 e.metaKey || |
38 window.history.length < 2) // Nowhere to go back or forward to anyway. | |
40 return; | 39 return; |
41 | 40 |
42 // The blacklist overrides everything. | 41 // The blacklist overrides everything. |
43 var url = window.location.href; | 42 var url = window.location.href; |
44 if (options.blacklist.includes(url)) | 43 if (options.blacklist.includes(url)) |
45 return; | 44 return; |
46 | 45 |
47 // The whitelist overrides applet focus. | 46 // The whitelist overrides applet focus. |
48 // Listening on the Window means the event has no path (see | 47 // Listening on the Window means the event has no path (see |
49 // http://crbug.com/645527), so we'll have to look at the focused (active) | 48 // http://crbug.com/645527), so we'll have to look at the focused (active) |
50 // element. This means it will not work properly with shadow DOM. | 49 // element. This means it will not work properly with shadow DOM. |
51 // TODO: Fix behavior with shadow DOM when the above bug is resolved. | 50 // TODO: Fix behavior with shadow DOM when the above bug is resolved. |
52 if (!options.whitelist.includes(url) && | 51 if (!options.whitelist.includes(url) && |
53 disabledInApplet(document.activeElement)) | 52 disabledInApplet(document.activeElement)) |
54 return; | 53 return; |
55 if (isEditable(document.activeElement)) | 54 if (isEditable(document.activeElement)) |
56 return; | 55 return; |
57 | 56 |
58 e.shiftKey ? window.history.forward(): window.history.back(); | 57 // Make sure this extension is still active. |
59 e.preventDefault(); | 58 // sendMessage throws an internal error, not reported in lastError, if the |
59 // other end no longer exists. So we use JS error-catching rather than | |
60 // extension errors. | |
Devlin
2016/09/27 16:08:57
please add a crbug.com/nnn here.
Pam (message me for reviews)
2016/09/27 23:37:32
I'll take that as confirmation that it's not inten
| |
61 try { | |
62 chrome.runtime.sendMessage('', function(response) { | |
Devlin
2016/09/27 16:08:57
shouldn't need ''
Pam (message me for reviews)
2016/09/27 23:37:32
That's the message, which is not optional. If it s
Devlin
2016/09/29 20:55:34
Whoops, you're right. This is fine.
| |
63 // Future-proofing in case sendMessage ever changes to setting lastError | |
64 // instead of throwing a JS error. | |
65 if (chrome.runtime.lastError) { | |
66 window.removeEventListener('keydown', handleBackspace); | |
67 } else { | |
68 e.shiftKey ? window.history.forward(): window.history.back(); | |
69 e.preventDefault(); | |
70 } | |
71 }); | |
72 } catch(error) { | |
73 // If we have no connection to the background page, the extension has | |
74 // been updated, disabled, or uninstalled. Remove our listener and do | |
75 // nothing. | |
76 window.removeEventListener('keydown', handleBackspace); | |
77 } | |
60 } | 78 } |
61 | 79 |
62 // Return true if the option to disable the extension in applets is enabled, | 80 // Return true if the option to disable the extension in applets is enabled, |
63 // and focus is in an embedded Flash or Java applet. | 81 // and focus is in an embedded Flash or Java applet. |
64 function disabledInApplet(target) { | 82 function disabledInApplet(target) { |
65 if (!options.disableInApplets) | 83 if (!options.disableInApplets) |
66 return false; | 84 return false; |
67 | 85 |
68 var nodeName = target.nodeName.toUpperCase(); | 86 var nodeName = target.nodeName.toUpperCase(); |
69 var nodeType = target.type || ''; | 87 var nodeType = target.type || ''; |
70 nodeType = nodeType.toLowerCase(); | 88 nodeType = nodeType.toLowerCase(); |
71 if ((nodeName === 'EMBED' || nodeName === 'OBJECT') && | 89 if ((nodeName === 'EMBED' || nodeName === 'OBJECT') && |
72 (nodeType === 'application/x-shockwave-flash' || | 90 (nodeType === 'application/java' || |
73 nodeType === 'application/java')) { | 91 nodeType === 'application/pdf' || |
92 nodeType === 'application/x-chat' || | |
93 nodeType === 'application/x-google-chrome-pdf' || | |
94 nodeType === 'application/x-shockwave-flash')) { | |
74 return true; | 95 return true; |
75 } | 96 } |
76 return false; | 97 return false; |
77 } | 98 } |
OLD | NEW |