title: Dynamic extension context menu that depends on selected text
body: <p>I am trying to create entries on the Chrome context menu based on what is selected.
  I found several questions about this on Stackoverflow, and for all of them the answer is: use a content script with a "mousedown" listener that looks at the current selection and creates the Context Menu.</p>
  
  <p>I implemented this, but it does not always work. Sometimes all the log messages say that the context menu was modified as I wanted, but the context menu that appears is not updated.</p>
  
  <p>Based on this I suspected it was a race condition: sometimes chrome starts rendering the context menu before the code ran completely.</p>
  
  <p>I tried adding a eventListener to "contextmenu" and "mouseup". The later triggers when the user selects the text with the mouse, so it changes the contextmenu much before it appears (even seconds). Even with this technique, I still see the same error happening!</p>
  
  <p>This happens very often in Chrome 22.0.1229.94 (Mac), occasionally in Chromium 20.0.1132.47 (linux) and it did not happen in 2 minutes trying on Windows (Chrome 22.0.1229.94).</p>
  
  <p>What is happening exactly? How can I fix that? Is there any other workaround?</p>
  
  <hr>
  
  <p>Here is a simplified version of my code (not so simple because I am keeping the log messages):</p>
  
  <h1>manifest.json:</h1>
  
  <pre><code>{
    "name": "Test",
    "version": "0.1",
    "permissions": ["contextMenus"],
    "content_scripts": [{
      "matches": ["http://*/*", "https://*/*"],
      "js": ["content_script.js"]
    }],
    "background": {
      "scripts": ["background.js"]
    },
    "manifest_version": 2
  }
  </code></pre>
  
  <h1>content_script.js</h1>
  
  <pre><code>function loadContextMenu() {
    var selection = window.getSelection().toString().trim();
    chrome.extension.sendMessage({request: 'loadContextMenu', selection: selection}, function (response) {
      console.log('sendMessage callback');
    });
  }
  
  document.addEventListener('mousedown', function(event){
    if (event.button == 2) {
      loadContextMenu();
    }
  }, true);
  </code></pre>
  
  <h1>background.js</h1>
  
  <pre><code>function SelectionType(str) {
    if (str.match("^[0-9]+$"))
      return "number";
    else if (str.match("^[a-z]+$"))
      return "lowercase string";
    else
      return "other";
  }
  
  chrome.extension.onMessage.addListener(function(msg, sender, sendResponse) {
    console.log("msg.request = " + msg.request);
    if (msg.request == "loadContextMenu") {
      var type = SelectionType(msg.selection);
      console.log("selection = " + msg.selection + ", type = " + type);
      if (type == "number" || type == "lowercase string") {
        console.log("Creating context menu with title = " + type);
        chrome.contextMenus.removeAll(function() {
          console.log("contextMenus.removeAll callback");
          chrome.contextMenus.create(
              {"title": type,
               "contexts": ["selection"],
               "onclick": function(info, tab) {alert(1);}},
              function() {
                  console.log("ContextMenu.create callback! Error? " + chrome.extension.lastError);});
        });
      } else {
        console.log("Removing context menu")
        chrome.contextMenus.removeAll(function() {
            console.log("contextMenus.removeAll callback");
        });
      }
      console.log("handling message 'loadContextMenu' done.");
    }
    sendResponse({});
  });
  </code></pre>