Skip to content

Commit

Permalink
Keep track of permissions granted via background local storage
Browse files Browse the repository at this point in the history
Settings use cloud sync, so without tracking optional permissions, the
user could reinstall the extension and get stuck in an unrecoverable
error state on startup because the view mode is set to an option we
haven't asked permissions for yet. The chrome.permissions* methods can
only be accessed via user gestures, including
chrome.permissions.contains from my testing. Work around this by
tracking permissions granted, and resetting preferences accordingly when
setPrefs is called.
  • Loading branch information
jaszhix committed Aug 11, 2019
1 parent 2966bef commit d707b91
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 7 deletions.
2 changes: 2 additions & 0 deletions app/scripts/bg/bg.js
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,8 @@ class Bg {
this.undoAction(this.state.windows[refWindow].tabs, this.state.chromeVersion);
} else if (msg.method === 'getActions') {
sendResponse({actions: this.state.actions, windowId: sender.tab.windowId});
} else if (msg.method === 'setPermissions') {
prefsStore.setPermissions(msg.permissions);
}
return true;
});
Expand Down
1 change: 1 addition & 0 deletions app/scripts/components/settings/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ class Preferences extends React.Component {
}, (granted) => {
if (!granted) return;

msgStore.setPermissions({screenshot: true});
this.handleClick(opt);

setTimeout(chrome.runtime.reload, 500);
Expand Down
15 changes: 10 additions & 5 deletions app/scripts/components/stores/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,16 @@ export var utilityStore = {
// Chrome only allows interacting with the chrome.permissions* API with a user gesture,
// including the function that calls back with a boolean if we have a permission.
// This makes it difficult to handle extension reloads, where TM5K likes to load
// a new tab page if one was already open, to restore it. For now we can assume
// the only way a user will get stuck in a mode requiring permissions without the permission,
// is if they are modifying state directly in devtools.
// a new tab page if one was already open, to restore it. For now TM5K tracks permissions
// granted manually, and resets prefs based on those values any time prefs are updated.
if (userGesture) {
chrome.permissions.request({
permissions: ['bookmarks'],
origins: ['<all_urls>']
}, (granted) => {
if (!granted) return;

msgStore.setPermissions({bookmarks: true});
msgStore.queryBookmarks(init);
this._handleMode(mode, stateUpdate, init);
});
Expand All @@ -105,6 +105,7 @@ export var utilityStore = {
}, (granted) => {
if (!granted) return;

msgStore.setPermissions({history: true});
msgStore.queryHistory(init);
this._handleMode(mode, stateUpdate, init);
});
Expand All @@ -123,6 +124,7 @@ export var utilityStore = {
}, (granted) => {
if (!granted) return;

msgStore.setPermissions({management: true});
msgStore.queryExtensions(init);
this._handleMode(mode, stateUpdate, init);
});
Expand Down Expand Up @@ -286,15 +288,15 @@ export var msgStore = {
handleMessage(s, msg, sender, sendResponse);
});
},
setPrefs(obj){
setPrefs(obj) {
state.set({prefs: obj}, true);
chrome.runtime.sendMessage(chrome.runtime.id, {method: 'setPrefs', obj: obj}, (response) => {
if (response && response.prefs) {
console.log('setPrefs: ', obj);
}
});
},
getPrefs(){
getPrefs() {
return new Promise((resolve) => {
chrome.runtime.sendMessage(chrome.runtime.id, {method: 'prefs'}, (response) => {
if (response && response.prefs) {
Expand All @@ -303,6 +305,9 @@ export var msgStore = {
});
});
},
setPermissions(permissions) {
chrome.runtime.sendMessage(chrome.runtime.id, {method: 'setPermissions', permissions});
},
getWindowId() {
return new Promise((resolve) => {
chrome.runtime.sendMessage(chrome.runtime.id, {method: 'getWindowId'}, (windowId) => {
Expand Down
50 changes: 48 additions & 2 deletions app/scripts/components/stores/prefs.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _ from 'lodash';
import initStore from '@jaszhix/state';
import {each} from '@jaszhix/utils';
import {each, tryFn} from '@jaszhix/utils';

let prefsStore = initStore({
prefs: {},
Expand Down Expand Up @@ -39,7 +39,13 @@ let prefsStore = initStore({
allTabs: false,
resetSearchOnClick: true,
tablePadding: 5,
errorTelemetry: false
errorTelemetry: false,
},
permissions: {
screenshot: false,
bookmarks: false,
history: false,
management: false,
},
init: function() {
let getPrefs = new Promise((resolve, reject)=>{
Expand All @@ -52,6 +58,7 @@ let prefsStore = initStore({
reject(chrome.extension.lastError);
} else {
prefsStore.prefs = prefsStore.defaultPrefs;
prefsStore.syncPermissions();
prefsStore.setPrefs(prefsStore.prefs);
console.log('init prefs: ', prefsStore.prefs);
}
Expand All @@ -69,14 +76,53 @@ let prefsStore = initStore({
}
});

prefsStore.syncPermissions();

console.log('load prefs: ', prefs, prefsStore.prefs);
prefsStore.set({prefs: prefsStore.prefs}, true);
}).catch((err)=>{
console.log('chrome.extension.lastError: ', err);
});
},
syncPermissions() {
// With cloud syncing, the extension settings could be restored and the user might get
// stuck in a view mode we haven't asked permission for yet. Resolve this by tracking
// all permissions granted, and resetting prefs accordingly (handled in checkPermissions).
let permissions = localStorage.getItem('tm5kPermissionsTracking');

if (!permissions) {
localStorage.setItem('tm5kPermissionsTracking', JSON.stringify(prefsStore.permissions));
} else {
permissions = tryFn(
() => JSON.parse(permissions),
() => prefsStore.permissions
);

prefsStore.permissions = permissions;
}
},
checkPermissions(prefs) {
const {permissions} = prefsStore;

if (!permissions.screenshot && prefs.screenshot) {
prefs.screenshot = false;
}

if ((!permissions.bookmarks && prefs.mode === 'bookmarks')
|| (!permissions.history && prefs.mode === 'history')
|| (!permissions.management && (prefs.mode === 'apps' || prefs.mode === 'extensions'))) {
prefs.mode = 'tabs';
}
},
setPermissions(obj) {
_.merge(prefsStore.permissions, obj);
localStorage.setItem('tm5kPermissionsTracking', JSON.stringify(prefsStore.permissions));
},
setPrefs(obj) {
prefsStore.checkPermissions(obj);

_.merge(prefsStore.prefs, obj);

prefsStore.set({prefs: prefsStore.prefs}, true);
let themePrefs = {
wallpaper: prefsStore.prefs.wallpaper,
Expand Down

0 comments on commit d707b91

Please sign in to comment.