油猴脚本 GreasemonkeyGM_xmlhttpRequest内部实现原理

Posted 微wx笑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了油猴脚本 GreasemonkeyGM_xmlhttpRequest内部实现原理相关的知识,希望对你有一定的参考价值。

好久没在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 实现跨域请求

以上是关于油猴脚本 GreasemonkeyGM_xmlhttpRequest内部实现原理的主要内容,如果未能解决你的问题,请参考以下文章

有哪些超神的油猴脚本?

浏览器神器“油猴”使用教程

手机新版火狐浏览器如何安装油猴脚本管理器

油猴脚本:关闭知乎自动登录弹框及外链自动跳转

油猴怎么用

油猴怎么用