Appearance
Chrome extension 修改 window.navigator.userAgent
网页开发有一些场景会做一些ua的判断,特别是在手机端展示的页面,非手机就不让看了,这还是挺让人觉得不方便的,毕竟在电脑上各种屏蔽广告的插件还是很强大的。
那么有没有办法假装电脑版的网页是手机设备呢。毕竟之前做过的项目一般来说是判断一下 window.navigator.userAgent 来判定当前是否是手机设备。然后再判断是否要显示,或者判断是否要支持一些特殊的功能。
正常情况下以本机为例,获取到的 window.navigator.userAgent 如下: 但是当我直接设置他的时候,他好像没反应,没有设置上去。
一个比较正式的文档https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgent上说。
Navigator: userAgent property The Navigator.userAgent read-only property returns the user agent string for the current browser.
但是还是想改一下啊,这样就能在电脑上调试一些东西了,就比较方便。所以不管他,继续做。
找到一个比较好的的做法是,可以给navigator在设置一个get方法,然后用户再次获取的时候就是调用的自定义的了。方法如下:
javascript
function setUserAgent(window, userAgent) {
if (window.navigator.userAgent != userAgent) {
var userAgentProp = {
get: function() {
return userAgent;
}
};
try {
Object.defineProperty(window.navigator, 'userAgent', userAgentProp);
console.log('change ua success ', window, window.navigator.userAgent)
} catch (e) {
console.log('change ua fail ', e)
window.navigator = Object.create(navigator, {
userAgent: userAgentProp
});
}
}
}
这个方法可以直接设置指定window的useragent,当有iframe修改需求的时候也是可以用的,还是比较贴心的。
使用方式如下:
javascript
setUserAgent(
window,
'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/15.0.0 Mobile/15A5370a Safari/602.1'
);
下面在开发者工具上试一下:
看样子是可以了,那么下边就是怎么把这个结论应用到我们自己的插件当中了。
下边有两种方式可以实现这个效果,都是同 ExecutionWorld 这个关键的东西相关。默认的脚本都是在隔离环境中运行的,运行的局部变量同网页中的变量隔离,如果在之前的脚本中设置不会生效。尝试了很久都是血和泪。
content_scripts 方式
在 mainfest 中增加如下配置:
json
"content_scripts": [
{
"matches": [ "http://*/*", "https://*/*"],
"run_at": "document_start",
"js": ["js/content_scripts.js"],
"css": ["js/style.css"],
"world": "MAIN"
}
]
这个 world: "MAIN" 很关键。文档地址https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts#isolated_world
然后在 content_script 文件中增加如下代码:
javascript
function setUserAgent(window, userAgent) {
if (window.navigator.userAgent != userAgent) {
var userAgentProp = {
get: function() {
return userAgent;
}
};
try {
Object.defineProperty(window.navigator, 'userAgent', userAgentProp);
} catch (e) {
window.navigator = Object.create(navigator, {
userAgent: userAgentProp
});
}
}
}
setUserAgent(
window,
'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/15.0.0 Mobile/15A5370a Safari/602.1'
);
但是这种实现方式有个缺点就是在 content_script 文件中不能添加 chrome.runtime.onMessage.addListener 了,这样就不好控制脚本什么时候执行什么时候不知行了,会导致污染所有的网页。下边就是第二种方法。
scripting
还有个神奇的方法,可以动态的在可控的tab里边执行一些代码。 文档https://developer.chrome.com/docs/extensions/reference/api/scripting
能够根据tabid控制影响范围,还是非常好的。 实现方式如下: 增加权限
json
"background": {
"service_worker": "js/background.js"
},
"permissions": [
"scripting",
"activeTab"
],
在background.js 文件中的合适位置上增加如下代码:
javascript
chrome.scripting.executeScript({
world: 'MAIN',
target: {tabId: tabId},
func: () => {
function setUserAgent(window, userAgent) {
if (window.navigator.userAgent != userAgent) {
var userAgentProp = {
get: function() {
return userAgent;
}
};
try {
Object.defineProperty(window.navigator, 'userAgent', userAgentProp);
} catch (e) {
window.navigator = Object.create(navigator, {
userAgent: userAgentProp
});
}
}
}
setUserAgent(
window,
'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/15.0.0 Mobile/15A5370a Safari/602.1'
);
}
})
tabId 是指定的一个活动的tab或者特定的tab。 另外这个 world: 'MAIN', 一定要添加上,要不然不会生效。
如上例子就是监听插件按钮被点击的时候,background 的 service_worker 修改当前活动tab的 user agent。