【油猴脚本 Greasemonkey】GM_xmlhttpRequest内部实现原理

2023-05-16

好久没在CSDN发文章了,自从有了自己的网站,自己的想怎么折腾就怎么折腾,逐步的完善。

另一方面,技术上一直在吃老本,没什么进步,也没什么可发的。

最近在写一个Chrome浏览器扩展,了解到一个扩展和其它网站(准确的说是API)交互的功能,也就是跨域访问。

基于 Manifest V2 的content js中能不能使用 xmlhttpRequest  我没有尝试,至少在 Manifest V3 中是没办法跨域的,而在 background js 中 xmlhttpRequest 是不能使用的。

Chrome官方的 扩展开发文档中明确指出 在 Manifest V3 中,后台页面不支持 XMLHttpRequest(由 Service Workers 提供)。请考虑使用其现代替代品 fetch()。

回归正题

多数扩展脚本都会用到网络请求功能,对应的【油猴脚本 Greasemonkey】就是调用 GM_xmlhttpRequest 函数,这个函数内部是怎么实现的呢?拿到一段油猴脚本想用在自己开发的Chrome扩展中,但是用到了 GM_xmlhttpRequest 函数怎么办?

本文来讲一下实现原理,附代码。

缘起

网页上的即时翻译功能,大家用的较多的就是 划词翻译 了吧,但是今年以来互联网开始由开放转为封闭,很多原来免费的功能现在转为收费了,很多免费使用的翻译接口,现在虽然还没有完全收费,但是也需要自己去申请之后才能使用,这就是在收集用户信息了,只是第一步,接下来估计收费是一个趋势。

相关事件:搜狗翻译的官方平台搜狗深智引擎开放平台发出公告,将于 2022 年 2 月 28 日停止服务,公告具体内容可前往搜狗深智引擎开放平台官网查看。

百度翻译2022年7月14日发送了邮件通知,将从 8 月 1 日起下调每月的免费额度。

调整前免费额度调整后免费额度
标准版无限量5 万字符 / 月
高级版200 万字符 / 月100 万字符 / 月

刚刚看到新闻:腾讯开始对会议收费

刚好自己最近也在研究Chrome扩展的开发,基本算了入门了一点点,所以就考虑自己实现一个划词翻译的功能。

于是网上找划词翻译的源码参考,就找到了 有道划词翻译 的一段源码分享,是基于油猴脚本的,

有道划词翻译源码


// ==UserScript==
// @name 有道划词翻译
// @version 0.111
// @author Jim Lin
// @originalAuthor Liu Yuyang(sa@linuxer.me)
// @match http://*/*
// @description 极简
// @grant GM_xmlhttpRequest
// @namespace https://greasyfork.org/users/25855
// ==/UserScript==
 
window.document.body.addEventListener('mouseup', translate, false);
var context = new AudioContext();
 
function translate(e) {
    var previous = document.querySelector('.youdaoPopup');
    if (previous) {
        document.body.removeChild(previous);
    }
    var selectObj = document.getSelection();
 
    if (selectObj.anchorNode.nodeType == 3) {
        var word = selectObj.toString();
        if (word == '') {
            return;
        }
 
        word = word.replace('-\n', '');
        word = word.replace('\n', ' ');
 
        var ts = new Date().getTime();
        var x = e.clientX;
        var y = e.clientY;
        translate(word, ts);
    }
 
    function popup(x, y, result) {
        var youdaoWindow = document.createElement('div');
        youdaoWindow.classList.toggle('youdaoPopup');
 
        var dict = JSON.parse(result);
        var query = dict['query'];
        var errorCode = dict['errorCode'];
        if (dict['basic']) {
            word();
        } else {
            sentence();
        }
 
        youdaoWindow.style.zIndex = '1024';
        youdaoWindow.style.display = 'block';
        youdaoWindow.style.position = 'fixed';
 
        youdaoWindow.style.color = 'black';
        youdaoWindow.style.textAlign = 'left';
        youdaoWindow.style.wordWrap = 'break-word';
 
        youdaoWindow.style.background = 'lightBlue';
        youdaoWindow.style.borderRadius = '5px';
        youdaoWindow.style.boxShadow = '0 0 5px 0';
        youdaoWindow.style.opacity = '1';
 
        youdaoWindow.style.width = '200px';
        youdaoWindow.style.left = x + 10 + 'px';
        youdaoWindow.style.padding = '5px';
 
        if (x + 200 + 10 >= window.innerWidth) {
            youdaoWindow.style.left = parseInt(youdaoWindow.style.left) - 200 + 'px';
        }
        if (y + youdaoWindow.offsetHeight + 10 >= window.innerHeight) {
            youdaoWindow.style.bottom = '20px';
        } else {
            youdaoWindow.style.top = y + 10 + 'px';
        }
        document.body.appendChild(youdaoWindow);
 
        function word() {
            var basic = dict['basic'];
            var header = document.createElement('p');
            var span = document.createElement('span');
            span.innerHTML = query;
            header.appendChild(span);
 
            var phonetic = basic['phonetic'];
            if (phonetic) {
                var phoneticNode = document.createElement('span');
                phoneticNode.innerHTML = '[' + phonetic + ']';
                phoneticNode.style.cursor = 'pointer';
                header.appendChild(phoneticNode);
 
                phoneticNode.addEventListener('mouseup', function (e) {
                    e.stopPropagation()
                }, false);
 
 
                var soundUrl = 'https://dict.youdao.com/dictvoice?type=2&audio={}'.replace('{}', query);
                var promise = new Promise(function () {
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: soundUrl,
                        responseType: 'arraybuffer',
                        onload: function (res) {
                            try {
                                context.decodeAudioData(res.response, function (buffer) {
                                    phoneticNode.addEventListener('mouseup', function () {
                                        var source = context.createBufferSource();
                                        source.buffer = buffer;
                                        source.connect(context.destination);
                                        source.start(0);
                                    }, false);
                                    header.appendChild(document.createTextNode('✓'));
                                })
                            } catch (e) {
                            }
                        }
                    });
                });
                promise.then();
            }
 
            header.style.color = 'darkBlue';
            header.style.margin = '0';
            header.style.padding = '0';
 
            span.style.color = 'black';
            youdaoWindow.appendChild(header);
 
            var hr = document.createElement('hr');
            hr.style.margin = '0';
            hr.style.padding = '0';
            youdaoWindow.appendChild(hr);
 
            var ul = document.createElement('ul');
            ul.style.margin = '0';
            ul.style.padding = '0';
 
            basic['explains'].map(function (trans) {
                var li = document.createElement('li');
                li.style.listStyle = 'none';
                li.style.margin = '0';
                li.style.padding = '0';
                li.appendChild(document.createTextNode(trans));
                ul.appendChild(li);
            });
            youdaoWindow.appendChild(ul);
        }
 
        function sentence() {
            var ul = document.createElement('ul');
            ul.style.margin = '0';
            ul.style.padding = '0';
            dict['translation'].map(function (trans) {
                var li = document.createElement('li');
                li.style.listStyle = 'none';
                li.style.margin = '0';
                li.style.padding = '0';
                li.appendChild(document.createTextNode(trans));
                ul.appendChild(li);
            });
            youdaoWindow.appendChild(ul);
        }
    }
 
 
    function translate(word, ts) {
        var reqUrl = 'http://fanyi.youdao.com/openapi.do?type=data&doctype=json&version=1.1&relatedUrl=' +
            escape('http://fanyi.youdao.com/#') +
            '&keyfrom=fanyiweb&key=null&translate=on' +
            '&q={}'.replace('{}', word) +
            '&ts={}'.replace('{}', ts);
 
        GM_xmlhttpRequest({
            method: 'GET',
            url: reqUrl,
            onload: function (res) {
                popup(x, y, res.response);
            }
        });
    }
}  

实现原理

上面代码中就用到了 GM_xmlhttpRequest 函数,自己如何实现这个函数呢?

用过 ajax,xmlhttpRequest 的小伙伴看到这个函数的调用方法应该感觉非常熟悉,和 ajax 的调用基本差不多,无非是把调用成功的响应事件由 success 改成了 onload

所以自己定义一个函数,内部封装 ajax 的调用就可以了。

但是需要注意,你的扩展程序运行在别人的网站上,发出的请求基本都是跨域的请求,仅仅封装 ajax 是不行的,这就需要根据Chrome扩展提供的能力、方法来实现跨域请求;

具体实现代码参考:

【Chrome扩展程序】利用 background 实现跨域请求

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【油猴脚本 Greasemonkey】GM_xmlhttpRequest内部实现原理 的相关文章

随机推荐

  • 腾讯自研交换机系统优化之路

    一 Tencent NOS概述 SONiC is an open source network operating system based on Linux that runs on switches from multiple vend
  • vue-cli 插件开发补充

    官网地址 xff1a https cli vuejs org zh api地址 xff1a https cli vuejs org dev guide plugin api html Plugin API api version 64 vu
  • ubuntu 安装开发工具

    因为各种原因 又回到了Ubuntu系统 以后开始会陆续记些笔记 注意要点 选择ubuntu16 能自动识别新机型的物理硬件 集成了较新较全的驱动 比如网卡和声卡驱动都有 最主要是这两个 没有这两个就上不了网 听不到声音 解决很费时间 安装系
  • ubuntu vscode 使用clang-format and editor 插件序列化代码

    在使用vscode时 xff0c 可以加入插件 xff0c 在写代码的时候自动格式化代码 xff0c 对编码风格做一个自动化的处理 xff0c 这样会使同一个部门使用同一种规格编码 xff0c 在review代码时会很轻松 这里使用一键化的
  • 记录生活,记录学习----我的2016

    过着2017年的日子 xff0c 思考着2016年人生的变化 xff0c 或许 xff0c 最大的变化是懂得记录学习 xff0c 记录生活吧 2016年 xff0c 博客进入了我的生活 xff0c 从年初的寥寥数篇博客 xff0c 到现在C
  • 《Learning Deep Features for Discriminative Localization》——CAM热力图翻译及划重点

    摘要 在这项工作中 xff0c 我们重新审视了 Network in network 中提出的全局平均 池化层 xff08 global average pooling xff09 xff0c 并阐明了它是如何通过图片标签就能让卷积神经网络
  • VS2019的C++项目如何查看源文件(.h,.cc.cpp等)所在的工程

    在大型的C 43 43 项目中 xff0c 一个解决方案有多个 vcxproj工程文件 每个工程文件里又有很多源文件 xff0c 比如 xff1a 8071个项目 xff0c 文件数则更多 xff0c 因此 xff0c 当你打开一个源文件时
  • csdn排名出了Bug了?一场因排名引发的血案!

    一石激起千层浪 xff0c 排名让人很彷徨 xff01 很多人已经注意到了 xff0c 博客的排名出现了非常大的变化 xff01 不只是排名 xff0c CSDN最近的动作可是很频繁 xff0c 让我们来细数CSDN那些改变 但是 xff0
  • CSDN博主排名更新,看看原来的前1000名博主现在的排名如何

    CSDN博主排名更新 xff0c 看看新规则 原来的排名参考 xff1a CSDN前1000名博主 博客原排名新排名stpeace 1 25yuanmeng001 2 143yjclsx 3 72phphot 4 427833Augusdi
  • 程序员的求爱日:比1024多一点,1025,要你爱我!

    转眼又要到了1024 xff0c 还记得去年的1024活动 xff0c 我写一篇 xff1a 今天是程序员节 xff0c 明天就是求爱日啦 xff01 今年的活动 xff1a 程序员邂逅鼓励师的正确姿势 xff01 鼓励师 这个名词 xff
  • C# 绘制箭头的方法,仿微信截图的箭头

    C 绘制箭头的方法 xff0c 仿微信截图的箭头 效果见下图 xff0c 实际上还是有区别的 xff0c 箭头的起点处微信的是圆端 xff0c 而我实现的是尖端 说说我的实现吧 xff0c 实现方法其实是划线 xff0c 线的两端都要设置端
  • CSDN最近变化有点快

    文章内容原来在右边 xff0c 现在移到了左边 不过博客设置中的皮肤缩略图还没有变 归档样式变了 系统通知类型越来越多
  • 简单功能强大的jQuery在线图片裁剪插件croppic

    帝国CMS后台文章编辑功能的标题图片 xff0c 也就是文章封面设置功能用起来非常的不习惯不好用 xff0c 所以考虑换一个更好用的 xff0c 在网上找了一下 xff0c 最后觉得croppic最不错 xff0c 这里给大家分享一下 cr
  • 网站安全情况自查表怎么填?

    今天收到相关单位一个文档要填写 xff0c 包括 xff1a 表二 xff1a 信息系统运营使用单位填写 xff0c 表三 xff1a 网站安全情况自查表 xff0c 这个怎么填写呢 xff1f 按我个人理解 xff0c 按网站备案的信息填
  • 又是一年1024,去年的1025你是怎么过的?

    除非你在等车 xff0c 回首看看 xff0c 没有人不会觉得时间过得太快了吧 xff01 又是一年1024 xff0c 去年的1025你是怎么过的 xff1f 去年的 程序员的求爱日 比1024多一点 1025 要你爱我 前年的 今天是程
  • 2020年终总结——走在代码公益的路上

    从2012年加入 CSDN 博客 xff0c 已经过去了8个年头 xff0c 有收获也有感慨 xff1b 目前总排名24 xff0c 虽然排名已经很靠前了 xff0c 也是输出了很多内容 xff0c 但不像一些人输出的都是比较系统性的内容
  • nginx 实现图片防盗链功能

    在搜索浏览网页的时候 xff0c 发现一篇文章是从我的个人网站转载的 xff0c 但是没有注明出处 xff0c 文章中的图片也没有本地化处理 xff0c 还是从我的服务器请求 xff0c 这就无形中增加了我的服务器的开销 xff0c 于是有
  • 使用element ui + vuedraggable 实现页面控件拖拽排序

    项目要实现一些控件的拖拽排序 从而找到了这款vuedraggable控件 如上图要实现这些控件的拖拽排序 这是拖拽后 由于公司网络的原因 xff0c 项目没有使用npm xff0c 都是使用的引入的js lt script type 61
  • Winform界面开发教程 - 如何对应用程序界面的组织布局

    在设计界面的时候 xff0c 不管是在Web端 xff0c 还是在WinForm端 xff0c 或者是WPF或者移动界面等应用上 xff0c 我们对界面的组织布局 xff0c 一直是比较有趣的话题 xff0c 而组织界面的好坏从用户的感受来
  • 【油猴脚本 Greasemonkey】GM_xmlhttpRequest内部实现原理

    好久没在CSDN发文章了 xff0c 自从有了自己的网站 xff0c 自己的想怎么折腾就怎么折腾 xff0c 逐步的完善 另一方面 xff0c 技术上一直在吃老本 xff0c 没什么进步 xff0c 也没什么可发的 最近在写一个Chrome