在谷歌浏览器扩展中触发按键按下事件

4
我正在开发一款与Gmail聊天互动并能够将同样的消息发送给所有打开聊天窗口的Google Chrome扩展程序。功能部分已经完成,但我无法发送 keydown 事件到文本框中。
我的做法是使用 page_action 在用户访问Gmail时显示扩展图标。当用户点击扩展图标时,它会弹出一个带有文本区域的弹出窗口,在用户打开聊天并在文本区域中写入内容并按下 Enter 键后,每个打开的聊天窗口的文本区域都会填充相同的消息,并且会触发 keydown 事件。
以下是关键代码:

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);
    }
  }
}

代码没有问题,可以填充文本区域,因此所有的文本区域都被识别了,但是在脚本填充文本区域后,keydown事件并没有触发。奇怪的是,当我将 send.js 代码放入 Google Chrome 控制台时,keydown 事件就会正常触发,并且消息也能发送,但是我不能用插件做同样的事情。
那么,如何在插件中触发 keydown 事件呢?

你能提供一个 JSFiddle 的演示吗? - shareef
你是在扩展程序中使用 Chrome Inspector 还是在网页中使用的? - Nathan Campos
@NathanCampos 我使用开发者工具来检查弹出窗口和网页。 - Fong-Wan Chau
@shareef 我想我不能,因为这是一个针对 Gmail 的 Google Chrome 扩展程序,所以需要用户先访问 Gmail 页面。 - Fong-Wan Chau
1个回答

2

Gmail由多个框架组成。
默认情况下,chrome.tabs.executeScript仅向主框架注入代码。为了在子框架中也注入代码,请将allFrames: true添加到您的详细信息中。

此外,不要使用'send("' + USER_INPUT_HERE + '");'来触发您的函数。这会使您的应用程序容易受到脚本注入攻击。请使用JSON.stringify(USER_INPUT_HERE)正确转义字符串。

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

漏洞示例:
用户输入: " + (function(){while(1)chrome.tabs.create({url:"http://evil.com"})}()) + "


扩展程序创建的对象永远不会传递到页面。当事件被传递到页面时,添加到事件中的属性和属性描述符会丢失。因此,建议使用注入脚本 注入脚本、内容脚本和扩展代码有什么区别?来达到所需的效果:

// 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*/
});

为了澄清,前面的代码片段会导致以下代码被注入到页面中:

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

事件对象是在 Gmail 页面内构建和传递的,因此所有属性都得以保留,就像您使用控制台执行脚本一样。 它不再在您的扩展程序上下文中运行


感谢您指出漏洞。我知道Gmail由框架组成,但是Gmail中的聊天在框架之外,所以这不是问题所在。我没有修改文本区域值的问题,因此问题不在于脚本无法找到文本区域,而在于脚本不能正确触发“keydown”事件。 - Fong-Wan Chau
@Fong-WanChau 编辑了答案以解决另一个问题。 - Rob W
已经阅读了您在其他帖子中的答案并尝试了您的方法,但是它没有起作用。 - Fong-Wan Chau
好的,我在本地进行了测试,并发现KeyboardEvent已经被触发,但keyCode: 0,这是由于一个旧的未修复的错误。在这种情况下,Object.defineProperty方法不起作用(我不知道为什么),所以这就是问题所在,我只需要等待补丁发布即可。 - Fong-Wan Chau
1
@Fong-WanChau 如果正确实施,它必须正常工作,因为你说过“我将send.js代码尝试放入Google Chrome控制台时,keydown事件正常触发并且消息被发送”。但是,我没有正确实施它,这仍然是一个内容脚本 :p 更新的答案(参见此答案 查看其他方法如何使用Chrome扩展来注入脚本) - Rob W
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接