Chrome Extension Manifest V3 inject\execute local script - chrome-extension-manifest-v3

I am trying to figure out how to add a script to a page via my simple chrome extension. Currently I have a script, mycoolscript.js which is a local file to my extension and I am using the following code (snippets including manifest) to add it to the current page.
I feel like I am close, the script tag is added to the head tag but when the onload fires I am seeing stating that coolObject is not defined. coolObject is returned from mycoolscript to the window. If I build a page manually or use tampermonkey this works perfectly. Any suggestions on what I might be doing wrong here?
manifest.json
"content_scripts": [
{
"matches": ["<all_urls>"],
"css": ["contentScript.css"],
"js": ["contentScript.js"]
}],
"web_accessible_resources": [
{
"resources": ["mycoolscript.js"],
"matches": ["<all_urls>"]
}
]
contentScript.js
function addMyCoolScript(){
var s = document.createElement('script');
s.src = chrome.runtime.getURL('mycoolscript.js');
(document.head || document.documentElement).appendChild(s);
s.onload = function () {
s.parentNode.removeChild(s);
onScriptLoaded();
};
}
function onScriptLoaded() {
let apikey = "ABC123"
let baseUrl = "example.com"
coolObject.initialize(apikey, {
baseUrl: baseUrl,
});
coolObject.runStartup();
coolObject.openSession();
}

Related

electron app update now crashing on http require statement

I made a electron app a couple years ago. now like to update for a little bit diiferint usage.
When I try to run this after updating electron and libraries and import it is failing on the http requirement.
The long ago compiled app still works, but even the code for that if i copy it to new project also failed on this require line.
Error in the console
ReferenceError: Can't find variable: require
renderer.js:62
And below are the codes
browser setup in index.js
const path = require('path');
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) { // eslint-disable-line global-require
app.quit();
}
const createWindow = () => {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 200,
height: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
}
});
// and load the index.html of the app.
mainWindow.loadFile(path.join(__dirname, 'index.html'));
// Open the DevTools.
//mainWindow.webContents.openDevTools();
};
failing request in renderer.js
/* placeCall
* Grab the SIP uri from the input box and send it to the codec.
* Requires an account on the codec that can make calls.
* Send the authorization in the http header.
*/
function placeCall(){
var uri = document.getElementById("sipuri").value;
var xmldoc = '<Command><Dial command="true"><Number>' + uri + '</Number></Dial></Command>';
var xmlLength = xmldoc.length;
var sysIp = document.getElementById("sysip").value;
// console.log("XMLDOC: " + xmldoc);
var http = require("https"); <- CRASHES HERE ON LINE 62!
var options = {
"method": "POST",
"hostname": sysIp,
"port": null,
"path": "/putxml",
"rejectUnauthorized": false,
"headers": {
"content-type": "text/xml",
"content-length": xmlLength,
"authorization": "Basic dkeicjsmcielwoslkdddddd"
}
};
var req = http.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function () {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
});
req.write(xmldoc);
req.end();
}
the error is in the browser. seems like it has to run as electron app.
That must be that require is really a node.js operation.
Maybe this question is repeated elsewhere and this one can be deleted.

Firefox addon button (browser_action) not calling js on click

Trying to create a simple addon to retry a duckduckgo search in google. It parses the current duckduckgo url search params, appends them to a google url, and opens that url in a new tab.
The searchGoogle.js works great when pasted directly into the console context of a duckduckgo search. And the addon loads correctly in about:debugging... But I am unable to get the "searchGoogle.js" to fire on button (browser_action) click. What am I missing here?
manifest.json
{
"manifest_version": 2,
"name": "SearchGoogle",
"version": "1.0",
"description": "Repeats a duckduckgo search in a new tab using google.",
"icons": {
"48": "icons/48search-pointer-icon.png",
"32": "icons/32search-pointer-icon.png",
"16": "icons/16search-pointer-icon.png"
},
"permissions": [
"tabs",
"activeTab",
"webRequest",
"webNavigation"
],
"browser_action": {
"default_icon": "icons/48search-pointer-icon.png",
"default_title": "SearchGoogle"
},
"background": {
"scripts": ["searchGoogle.js"]
}
}
searchGoogle.js
var myStr = window.location.href;
var googurl = 'https://www.google.com/search?q=';
var params = getmyuri('q', myStr);
window.open(googurl+params, '_blank');
window.focus();
window.alert(googurl+params);
function getmyuri(n,s){
n = n.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");
var p = (new RegExp("[\\?&]"+n+"=([^&#]*)")).exec(s);
return (p===null) ? "" : p[1];
}
What you're currently doing is merely declaring a background script. A background script is loaded when the addon is loaded by the browser, but you need some code to react to other events.
Here's what I found in the MDN pages of browser_action :
If you don't supply a popup, then a click event is dispatched to your extension's background scripts when the user clicks the button.
(...)
The extension's background scripts can receive click events when the user clicks the icon using code like this:
browser.browserAction.onClicked.addListener(function(){})
So in your case, you need to wrap your "on click" code into this browserAction.onClicked event handler, something like this:
// searchGoogle.js
browser.browserAction.onClicked.addListener(function(){
var myStr = window.location.href;
var googurl = 'https://www.google.com/search?q=';
var params = getmyuri('q', myStr);
window.open(googurl+params, '_blank');
window.focus();
window.alert(googurl+params);
});
And voilĂ , your code should be executed everytime you click the browserAction icon.
For anyone else who comes here with the same question. Here are lessons observed:
window.alert is not a thing in a background script. Instead, wrap things in console.log(). Logging to console from Firefox extension?
Event listeners can pass tab information already, see tabs.getCurrent() result is undefined?.
To fire on button click, DON'T supply a popup in the manifest, and DO wrap the code to be fired in browser.browserAction.onClicked.addListener(function(){}).
Here's the final working script.
Main stays the same.
JS is:
browser.browserAction.onClicked.addListener(function(tab){
function getmyuri(n,s){
n = n.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");
var p = (new RegExp("[\\?&]"+n+"=([^&#]*)")).exec(s);
return (p===null) ? "" : p[1];
}
console.log(tab)
var googurl = 'https://www.google.com/search?q=';
var params = getmyuri('q', tab.url);
browser.tabs.create({url: googurl+params});
});

I was a bad boy: Firefox addon to edit body of post request (which I am doing manually atm)

I need to intercept POST request, change a parameter and let it continue in Mozilla addon.
Background:
I am part of the comment section of example.org. That site had an option in profile to allow to show 10, 25, 50, 100 and 500 comments per page. A week ago, in order to save bandwidth, they removed that option and locked the users to 50 comments per page or less.
I was a naughty boy and researched a bit.
There is https://example.org/api.html?GET_COMMENTS post method.
As part of a JSON object in the body of request, it takes preferred_count parameter. The object looks something like this
{ userid: 1234567,
postId: 56789,
preferred_count: 50,
location_parameters:[] etc}
My research lead me to a discovery that if I change preferred_count to 500 in the request body in Firefox developer tools then resend the request I will get full 500 comments on that post as a response! Neat. But it sucks to be doing it constantly. So I tried to develop a firefox addon to do it for me.
My manifest.json looks like this:
{
"description": "Makes example comments.cool.",
"manifest_version": 2,
"name": "examplecomments",
"version": "1.0",
"permissions": [
"webRequest",
"webRequestBlocking",
"storage",
"https://example.com/*",
"https://example.org/api.html?GET_COMMENTS"
],
"icons": {
"48": "border.png",
"96": "border.png"
},
"applications": {
"gecko": {
"id": "borderify#example.com"
}
},
"background": {
"scripts": [ "badComments.js" ]
} }
My badComments.js looks like this:
var lookUrl = "https://example.com/api.phtml?GET_COMMENTS";
function moreComments(requestResp) {
var dec = new TextDecoder();
var enc = new TextEncoder();
if (requestResp.url.toLowerCase() == lookUrl.toLowerCase()) {
console.log(requestResp);
var rawBytes = requestResp.requestBody.raw[0].bytes;
var decoded = dec.decode(rawBytes)
console.log(decoded);
var requestedObject = JSON.parse(decoded);
var numberOnPage = requestedObject.body[1].server_get_comments.preferred_count;
console.log(numberOnPage);
numberOnPage = 500;
console.log(numberOnPage);
requestedObject.body[1].server_get_comments.preferred_count = numberOnPage;
console.log( requestedObject.body[1].server_get_comments.preferred_count);
var requestedObjString = JSON.stringify(requestedObject);
console.log(requestedObjString);
var encodedObject = enc.encode(requestedObjString);
console.log(encodedObject);
requestResp.requestBody.raw[0].bytes = encodedObject.buffer;
console.log(requestResp);
}
return {requestBody:requestResp.requestBody};
// return {requestBody:requestResp};
}
browser.webRequest.onBeforeRequest.addListener(
moreComments,
{ urls: [lookUrl] },
["blocking", "requestBody"]
);
Console shows that preferred_count gets replaced with the correct number. But after scanning it in firefox console doesn't show it, it remains the old one! And I never get my 500 comments. What am I doing wrong? How can I change request, or, failing that, stop the existing one, copy all the needed data for request and send my own?
thank you.

How to use CustomElement v1 polyfill in a webpack/babel build?

I'm having some trouble getting this WebComponents polyfill + native-shim to work right across all devices, though webpack.
Some background on my setup:
* Webpack2 + babel-6
* app is written in ES6, transpiling to ES5
* imports a node_module package written in ES6, which defines/registers a CustomElement used in the app
So the relevant webpack dev config looks something like this:
const config = webpackMerge(baseConfig, {
entry: [
'webpack/hot/only-dev-server',
'#webcomponents/custom-elements/src/native-shim',
'#webcomponents/custom-elements',
'<module that uses CustomElements>/dist/src/main',
'./src/client',
],
output: {
path: path.resolve(__dirname, './../dist/assets/'),
filename: 'app.js',
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
include: [
path.join(NODE_MODULES_DIR, '<module that uses CustomElements>'),
path.join(__dirname, '../src'),
],
},
],
},
...
key take aways:
* I need CustomElement poly loaded before <module that uses CustomElements>
* I need <module that uses CustomElements> loaded before my app soure
* <module that uses CustomElements> is ES6 so we're transpiling it ( thus the include in the babel-loader).
The above works as-expected in modern ES6 browsers ( IE desktop Chrome ), HOWEVER
it does not work in older browsers. I get the following error in older browsers, for example iOS 8:
SyntaxError: Unexpected token ')'
pointing to the opening anonymous function in the native-shim pollyfill:
(() => {
'use strict';
// Do nothing if `customElements` does not exist.
if (!window.customElements) return;
const NativeHTMLElement = window.HTMLElement;
const nativeDefine = window.customElements.define;
const nativeGet = window.customElements.get;
So it seems to me like the native-shim would need to be transpiled to ES5:
include: [
+ path.join(NODE_MODULES_DIR, '#webcomponents/custom-elements/src/native-shim'),
path.join(NODE_MODULES_DIR, '<module that uses CustomElements>'),
path.join(__dirname, '../src'),
],
...but doing so now breaks both Chrome and iOS 8 with the following error:
app.js:1 Uncaught TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.
at new StandInElement (native-shim.js:122)
at HTMLDocument.createElement (<anonymous>:1:1545)
at ReactDOMComponent.mountComponent (ReactDOMComponent.js:504)
at Object.mountComponent (ReactReconciler.js:46)
at ReactCompositeComponentWrapper.performInitialMount (ReactCompositeComponent.js:371)
at ReactCompositeComponentWrapper.mountComponent (ReactCompositeComponent.js:258)
at Object.mountComponent (ReactReconciler.js:46)
at Object.updateChildren (ReactChildReconciler.js:121)
at ReactDOMComponent._reconcilerUpdateChildren (ReactMultiChild.js:208)
at ReactDOMComponent._updateChildren (ReactMultiChild.js:312)
.. which takes me to this constructor() line in the native-shim:
window.customElements.define = (tagname, elementClass) => {
const elementProto = elementClass.prototype;
const StandInElement = class extends NativeHTMLElement {
constructor() {
Phew. So it's very unclear to me how we actually include this in a webpack based build, where the dependency using CustomElements is ES6 ( and needs transpiling).
Transpiling the native-shim to es5 doesn't work
using the native-shim as-is at the top of the bundle entry point doesn't work for iOS 8, but does for Chrome
not including the native-shim breaks both Chrome and iOS
I'm really quite frustrated with web components at this point. I just want to use this one dependency that happens to be built with web components. How can I get it to work properly in a webpack build, and work across all devices? Am I missing something obvious here?
My .babelrc config for posterity sake (dev config most relevant):
{
"presets": [
["es2015", { "modules": false }],
"react"
],
"plugins": [
"transform-custom-element-classes",
"transform-object-rest-spread",
"transform-object-assign",
"transform-exponentiation-operator"
],
"env": {
"test": {
"plugins": [
[ "babel-plugin-webpack-alias", { "config": "./cfg/test.js" } ]
]
},
"dev": {
"plugins": [
"react-hot-loader/babel",
[ "babel-plugin-webpack-alias", { "config": "./cfg/dev.js" } ]
]
},
"dist": {
"plugins": [
[ "babel-plugin-webpack-alias", { "config": "./cfg/dist.js" } ],
"transform-react-constant-elements",
"transform-react-remove-prop-types",
"minify-dead-code-elimination",
"minify-constant-folding"
]
},
"production": {
"plugins": [
[ "babel-plugin-webpack-alias", { "config": "./cfg/server.js" } ],
"transform-react-constant-elements",
"transform-react-remove-prop-types",
"minify-dead-code-elimination",
"minify-constant-folding"
]
}
}
}
I was able to achieve something similar with the .babelrc plugin pipeline below. It looks like the only differences are https://babeljs.io/docs/plugins/transform-es2015-classes/ and https://babeljs.io/docs/plugins/transform-es2015-classes/, but I honestly can't remember what problems those were solving specifically:
{
"plugins": [
"transform-runtime",
["babel-plugin-transform-builtin-extend", {
"globals": ["Error", "Array"]
}],
"syntax-async-functions",
"transform-async-to-generator",
"transform-custom-element-classes",
"transform-es2015-classes"
]
}

Accessing local resources from a content script

Is there a way i can access local resources inside a script?
I need to access data.url('layout.html'), data.url('icon.png') and data.url('style.css') inside the contentScript handler.
exports.main = function() {
require("widget").Widget({
onClick: function() {
tabs.activeTab.attach({
contentScriptFile: [ data.url('jquery.js')],
contentScript:
"setTimeout(function(){ alert('asd');}, 100);",
});
}
});
}
I've ended up using cssUrl = data.url('alert.css'). In main.js i set this up, and in the script running client-side add a script having href=cssUrl.
Use contentScriptOptions to pass it to the content script, like this:
//main.js
contentScriptFile: [ data.url('jquery.js')],
contentScriptOptions: {
cssUrl: data.url('alert.css')
}
// jquery.js
console.log(self.options.cssUrl)

Resources