4

I'm doing a Google Chrome extension that interacts with Gmail chat and can send the same message to all open chat, the functional part is done, but I can not send the keydown event to the textareas.

What I am doing is using a page_action to show the extension icon when the user visit Gmail. When the user click the extension icon, it is going to prompt a popup with a textarea, when the user have opened chat and write something to the textarea and then press Enter key, the textarea of each opened chat are going to fill the same message and suppused to fire keydown event.

Here is the essential code:

popup.js

chrome.tabs.executeScript(null, {file: 'send.js'}, function () {
  // 'message' is the textarea inside the popup.html
  var message = document.getElementById('message');

  message.onkeydown = function (e) {
    if (13 == e.keyCode) {
      chrome.tabs.executeScript(null, {code: 'send("' + message.value + '");'}, function () {
        message.value = '';
      });
      return false;
    }
  };
});

send.js

function send(message) {
  if (message) {
    for (var i = 0, textareas = document.getElementsByTagName("TEXTAREA"), length = textareas.length; i < length; ++i) {
      textarea = textareas[i];
      textarea.value = message;

      var evt = document.createEvent('KeyboardEvent');

      evt.initKeyboardEvent('keydown', true, true, null, false, false, false, false, 13, 13);

      Object.defineProperty(evt, 'keyCode', {
        get : function() {
            return 13;
        }
      });

      Object.defineProperty(evt, 'which', {
        get : function() {
            return 13;
        }
      });

      Object.defineProperty(evt, 'keyIdentifier', {
        get : function() {
            return 'Enter';
        }
      });

      Object.defineProperty(evt, 'shiftKey', {
        get : function() {
            return false;
        }
      });

      textarea.dispatchEvent(evt);
    }
  }
}

The code did not have problem filling the textareas, so all textareas is reconized, but the keydown event did not fire after the script filled textareas. The strange part, is when I tried the send.js code into the Google Chrome console, the keydown event fire normally and the messages were send, but I can't do samething with the extension.

So, how can I fire the keydown event into the extension?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Fong-Wan Chau
  • 2,259
  • 4
  • 26
  • 42

1 Answers1

2

Gmail is composed of frames.
By default, chrome.tabs.executeScript only injects code in the main (top-window) frame. To also inject the code in the subframes, add allFrames: true to your details.

Also, do NOT use 'send("' + USER_INPUT_HERE + '");' to trigger your function. This makes your application vulnerable to script injections. Use JSON.stringify(USER_INPUT_HERE) to correctly escape the string.

chrome.tabs.executeScript(null, {
    file: 'send.js',
    allFrames: true
}, function () {
    chrome.tabs.executeScript(null, {
        code: 'send(' + JSON.stringify(message.value) + ');',
        allFrames: true
    });
    message.value = '';
});

Example of the vulnerability:
User input: " + (function(){while(1)chrome.tabs.create({url:"http://evil.com"})}()) + "


Objects created by the extension are never passed to a page. Properties and property descriptors added to an event are lost when passed to the page. Instead of a content script, I advise to use an injected script What is the difference between an injected script, Content script and extension code? to get the desired effect:

// Script to be injected
var code = '(' + function(message) {
    /* .. "function send"'s body from send.js .. */
} + ')(' + JSON.stringify(message.value) + ');';
// Content script which *injects* the script
chrome.tabs.executeScript(null, {
    code: 'var s = document.createElement("script");' +
          's.textContent = ' + JSON.stringify(code) + ';' + 
          '(document.head||document.documentElement).appendChild(s);' + 
          's.parentNode.removeChild(s);' /*<--Clean-up*/
});

For clarification, the previous snippet results in the following code to be injected in the page:

(function(message) { /* logic of send */ })("user input");

The Event object is constructed and passed within Gmail's page, so that all properties are preserved, as if you used the console to execute the script. It does not run in the context of your extension any more.

Community
  • 1
  • 1
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • Thank you for point out the vulnerability. I know that Gmail is composed of frames, but the chat in Gmail is outside the frame so that is not the problem. I don't have problem with modify the value of textareas, so the problem is not that the script cannot found the textarea, the problem is that the script don't fire `keydown` event correctly. – Fong-Wan Chau Jun 23 '12 at 20:25
  • @Fong-WanChau Edited answer to target yet *another* problem. – Rob W Jun 23 '12 at 20:45
  • But the Object is created inside the `send.js` that are injected to the page, so technically the Object are created inside the page. I used `console.log(evt)` and in the page inspector I can see the `evt` are created correctly. – Fong-Wan Chau Jun 23 '12 at 20:48
  • @Fong-WanChau I recommend to read [this answer](http://stackoverflow.com/a/9916089/938089?chrome-extension-auto-run-a-function). – Rob W Jun 23 '12 at 20:49
  • Already read your answer on other post, and tried your methods, but it did not work. – Fong-Wan Chau Jun 23 '12 at 22:02
  • Ok, I made a test locally, and found that `KeyboardEvent` has been fired with `keyCode: 0`, this is the caused by a [old not fixed bug](https://bugs.webkit.org/show_bug.cgi?id=16735).. The `Object.defineProperty` method did not work in this case (I don't know why), so, that is the problem and I just need to wait until the patch is released. – Fong-Wan Chau Jun 23 '12 at 22:32
  • 1
    @Fong-WanChau Properly implemented, it *has to* work, because you said "when I tried the send.js code into the Google Chrome console, the keydown event fire normally and the messages were send". But, I did not implement it correctly, it was still a content script :p Updated answer (see [this answer](http://stackoverflow.com/a/9517879/938089?building-a-chrome-extension-inject-code-in-a-page-using-a-content-script) for other methods to **inject** a script using a Chrome extension). – Rob W Jun 24 '12 at 08:18
  • +1 Wow, it's working now! The problem seem to be caused by inject script directly. (As if we use `executeScript` method, we never can access defined variable in the web...). Thank you! – Fong-Wan Chau Jun 24 '12 at 15:37