- 
                Notifications
    
You must be signed in to change notification settings  - Fork 60
 
Create right-click menu #284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
0d52930
              8874b79
              646c7e5
              3a7f61d
              025c98e
              fcc3d39
              7aaf9c4
              48a3d35
              434affd
              f880d43
              f9b23ee
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| 
          
            
          
           | 
    @@ -41,6 +41,8 @@ chrome.browserAction.setBadgeBackgroundColor({ | |
| chrome.tabs.onUpdated.addListener((tabId, info) => { | ||
| // unregister any auth listeners for this tab | ||
| if (info.status === "complete") { | ||
| createContextMenu(); | ||
| 
     | 
||
| if (authListeners[tabId]) { | ||
| chrome.webRequest.onAuthRequired.removeListener(authListeners[tabId]); | ||
| delete authListeners[tabId]; | ||
| 
        
          
        
         | 
    @@ -51,6 +53,10 @@ chrome.tabs.onUpdated.addListener((tabId, info) => { | |
| updateMatchingPasswordsCount(tabId); | ||
| }); | ||
| 
     | 
||
| chrome.tabs.onActivated.addListener(() => { | ||
| createContextMenu(); | ||
| }); | ||
| 
     | 
||
| // handle incoming messages | ||
| chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) { | ||
| receiveMessage(message, sender, sendResponse); | ||
| 
          
            
          
           | 
    @@ -1156,3 +1162,61 @@ function onExtensionInstalled(details) { | |
| }); | ||
| } | ||
| } | ||
| 
     | 
||
| /** | ||
| * Create a context menu, also called right-click menu | ||
| * | ||
| * @since 3.8.0 | ||
| * | ||
| * @return void | ||
| */ | ||
| async function createContextMenu() { | ||
| await chrome.contextMenus.removeAll(); | ||
| 
     | 
||
| const menuEntryProps = { | ||
| contexts: ["all"], | ||
| type: "normal", | ||
| }; | ||
| const menuEntryId = "menuEntry"; | ||
| 
     | 
||
| const settings = await getFullSettings(); | ||
| const response = await hostAction(settings, "list"); | ||
| 
     | 
||
| if (response.status != "ok") { | ||
| throw new Error(JSON.stringify(response)); | ||
| } | ||
| const files = helpers.ignoreFiles(response.data.files, settings); | ||
| const logins = helpers.prepareLogins(files, settings); | ||
| const loginsForThisHost = helpers.filterSortLogins(logins, "", true); | ||
                
      
                  max-baz marked this conversation as resolved.
               
              
                Outdated
          
            Show resolved
            Hide resolved
         | 
||
| const numberOfLoginsForThisHost = loginsForThisHost.length; | ||
| const singularOrPlural = numberOfLoginsForThisHost === 1 ? 'entry' : 'entries' | ||
| 
     | 
||
| await chrome.contextMenus.create({ | ||
| ...menuEntryProps, | ||
| title: `Browserpass - ${numberOfLoginsForThisHost} ${singularOrPlural}`, | ||
                
       | 
||
| id: menuEntryId, | ||
| }); | ||
| 
     | 
||
| for (let i = 0; i < numberOfLoginsForThisHost; i++) { | ||
| await chrome.contextMenus.create({ | ||
| ...menuEntryProps, | ||
| parentId: menuEntryId, | ||
| id: "login" + i, | ||
| title: loginsForThisHost[i].login, | ||
| onclick: () => clickMenuEntry(settings, loginsForThisHost[i]), | ||
| }); | ||
| } | ||
| } | ||
| 
     | 
||
| /** | ||
| * Handle the click of a context menu item | ||
| * | ||
| * @since 3.8.0 | ||
| * | ||
| * @param object settings Full settings object | ||
| * @param array login Filtered and sorted list of logins | ||
                
       | 
||
| * @return void | ||
| */ | ||
| async function clickMenuEntry(settings, login) { | ||
| await handleMessage(settings, { action: "fill", login }, () => {}); | ||
                
       | 
||
| } | ||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While testing I managed to get some errors in the console when I was opening a new tab, which is because of concurrent attempts to recreate context menu. Here's what happens:
createContextMenuisasync, so by calling it here we don't really block browser, there's no way of knowing when the function completes. In the beginning ofcreateContextMenuwe first remove the context menu and then try to recreate it. At the same time,chrome.tabs.onActivatedwill get called, in turn callingcreateContextMenuin parallel. The previous run has just created the parent menu, this run removes it, and then the previous run tries to create children to a now gone parent, and crashes.Of course one way to reduce the chances of a crash is to move
chrome.contextMenus.removeAll()from the beginning of the function to as much later as possible - but this will not entirely eliminate the problem, but rather just reduce its chances.Another problem is that here you actually call
createContextMenuonly oncompleteevent - I understand why you did it, but it presents a problem on slow websites. I open github.com and my context menu is filled with entries. I then go in this tab to some very slow website, which loads a ton of images, and socompleteevent will only fire in say 5 seconds. During the first 5 seconds if I right click, I will still see entries for github.I propose to repeat what
updateMatchingPasswordsCountfunction does:completeisRefreshingvariable to prevent concurrent runschrome.tabs.onActivatedbelow, because badge seems to not need it...Let me know if it makes sense and if you have any questions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the thorough review, I can reproduce but haven't figured out how to solve it completely yet. I'll keep you posted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I figured it out 🎉. I call
createContextMenunow on every status, but I needchrome.tabs.onActivatedto "kill" callbacks that are being called from an "old" tab.It's a bit hard to formulate, so here's an example: I'm in
tab Awith a slow page, I switch totab Bbeforetab Afinished loading, whentab Afinally finished loading I don't want it to mess up the context menu.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fkneist Would simply including the origin in the callback be sufficient? That way you can just discard any calls that have a different origin than the currently active one.