Chrome Extension content script don't load Custom Element via Range.insertNode - google-chrome-extension

I'm writing a Chrome Extension that injects my tiny app into the webpage. My small app was built with Vue as a Custom Element.
I'm bundling the app into one file called custom-elements.js, and injecting it alongside my content.js:
# manifest.json
"content_scripts": [
{
"matches": ["*"],
"js": [
"custom-elements.js",
"content.js"
]
}
],
Now, in my extension I'm using the Range API to hold a reference to a text over elements. My technique is to surround the original text with my custom element.
const originalContents = range.extractContents();
const indicator = document.createElement('overlay-indicator');
indicator.appendChild(originalContents);
range.insertNode(indicator);
This code produces an empty string. Looking inside the DevTools, I see the shadow-root is empty (so the original text is there, but it doesn't render because there is no shadow DOM there).
I know the custom element is working, because if instead of range.insertNode I'm using just appendChild it renders the custom element:
const testIndicator = indicator.cloneNode()
testIndicator.appendChild(originalContents)
const container = range.startContainer;
container.parentElement.insertBefore(testIndicator, container);
Also, I tried to create an HTML webpage with the custom-elements.js imported into it as a script, and there it is working.
In summary
It seems using Custom Element is working in Chrome Extension content script, except when using Range.insertNode.

Related

Is it possible to access the contents of iframe via chrome extension?

I want to ask is there ANY way or extension that can pre-highlight text within the iframe whenever a new window is opened containing iframe? I have tried many extension but none of them works.
I need to filter out content based on certain keywords and the content is within iframe. I can do it with CTRL+F but there are many keywords like 10-15 within each article to be found. So it makes my job very tough and time consuming. Few extensions that I have tried from chrome are multi highlighter, pearls, FF but none of them seems to work.
I also know the reason why these extension can't access content within the iframe i.e. due to cross origin policies.
But I also remember around an year ago I worked with chrome extension named 'Autofill' that could pre-select form elements whenever I opened new chrome window containing iframe.
So is there any work around?
You can set your extension permission to run content scripts in all frames as document at http://developer.chrome.com/extensions/content_scripts.html#registration by setting all_frames to true in the content scripts section of your manifest file. Adding to Google's example from that page, part of your manifest file might look like
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"js": ["jquery.js", "myscript.js"],
"all_frames": true
}
],
...
}
You'll need to be careful since your content scripts are going to be inject into the page once for the parent page and one for each iFrame on the page. Once your content script is injected into all frames on the page you can work your magic with finding and highlighting text.
if (window === top) {
console.log('Running inside the main document', location.href);
} else {
console.log('Running inside the frame document', location.href,
[...document.querySelectorAll('*')]);
}

Is this a Google Chrome extensions bug?

I wanted to make a Google Chrome extension that needed to list the <script> tags on a web page. So I made a manifest.json with a script that would "run_at": "document_start" only on that particular web page. When I loaded the page, I noticed that the value of document.getElementsByTagName('script') was that of an array with the proper <script> element in it, but its length was 0 and I couldn't access the elements.
var scripts = document.getElementsByTagName('script');
console.log(scripts.length); // 0
console.log(scripts); // [<script type=​"text/​javascript">​...</script>​] with expected JavaScript in it
Something somewhere is definitely wrong. The scripts variable has length 0 but the console shows it contains an element which cannot be accessed.
From the documentation:
In the case of "document_start", the files are injected after any files from css, but before any other DOM is constructed or any other script is run.
If you need access to the DOM, change run_at to document_end or leave it as the default document_idle.

append html elements to DOM by background script

I want to append the same html elements to the current TAB's document DOM.
I want these html extended elements to live "forever", so I want to built them once, and append them on every page load by the content script.
I was thinking to place the createElement commends at the background page.
Then for every web page load, I want the content script to communicate with the background script, so it will append them to the current TAB document DOM.
Is that good practice?
My problem is that I don’t know how to append html elements to the DOM from the background page.
Can you please help?
This cannot work. The background page lives in one process, the tab in the other - you cannot send objects from one to the other, only text data (objects sent are automatically converted to JSON). So your best chance is: build everything together in the background page, then get innerHTML of your element and send this text to the content script whenever necessary. The content script should then create a new element and set its innerHTML to the value it received.
You need to do it in content script because "content scripts" are scripts which run in an environment between a page and the Chrome extension. see this. remember these scripts will loaded on every page load and they won't affect already opened tabs.
as an example you can add external scripts like this
var theScript = document.createElement('script');
theScript.src = "http://mybwesite.com/static/js/myscript.js"
document.body.appendChild(theScript);
as long as you've added it's permissions in manifist:
"permissions": [
"activeTab",
"tabs",
"http://*/*", "https://*/*"
],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/contentscript.js"]
}
]

Loading an image from a chrome extension on a page

I've got some images in my chrome extension that I want the user to be able to inject into their page when they are using the extension.
The images will show up in the extension pop-up window, but when the user clicks the button to inject them into the page, the page can't access them/see them for some reason. I know there are specific ways of injecting JS and CSS into the page (already doing that) but I don't see any way to do the same thing with images.
I've got the following permissions set in my manifest (added the chrome-extensions:// one hoping that would do it):
"permissions" : [ "tabs", "http://*/*", "https://*/*", "chrome-extension://*/*" ]
Specifically, I'm trying to change the favicon, kind of like this (I've also tried without the leading /, and with chrome.extension.getURL("favicons/example.png")):
iconURL = "/favicons/example.png";
var link = document.createElement("link");
link.type = "image/x-icon";
link.rel = "shortcut icon";
link.href = iconURL;
this.removeLinkIfExists();
this.docHead.appendChild(link);
This code works perfectly if the iconURL is a fully qualified http:// address...
You can see the actual code at my github repo here (favicon.js line 54, called by tabdisplay.js line 260).
In case people are having this problem on Chrome 17 or later, it's because the manifest must include the web_accessible_resources section to allow an image packed within the extension to be injected into a web page.
web accessible resources
Instead of:
iconURL = "/favicons/example.png";
It should be:
iconURL = chrome.extension.getURL("/favicons/example.png");
which returns absolute URL to a file inside extension folder.
Also remove chrome-extension://*/* from manifest as it doesn't do anything.
You can use from CSS as well, make sure to add this image into manifest as well.
"web_accessible_resources": [
inside CSS:
background-image: url("chrome-extension://__MSG_##extension_id__/image.png");
Since you are trying to add an image from the extension into a web page, there are security measures that do not allow directly using a chrome:// url to load things like images.
A solution I can think of is to encode the image into a data uri and sending it as text then using that as the src of the img.
iconURL = "/favicons/example.png";
var link = document.createElement("link");
link.type = "image/x-icon";
link.rel = "shortcut icon";
//convert iconURL into data uri (data:image/png;base64,...)
link.href = iconURL;
this.removeLinkIfExists();
this.docHead.appendChild(link);
However, the only way I can think of to do that conversion is using a canvas element.
The STEEL answer is Ok but limited, if you want to introduce an image in the document, not as background of a div, you can do as follow.
<img id="divId" src="chrome-extension://__MSG_##some_Chrome_Extension_Id/path/image.png">
in the manifest file you does need include.
"web_accessible_resources": ["image.png","anotherImage.png"]

Function not defined creating a Chrome Extension Content Script

I'm writing a Chrome content script and when I inject this in the DOM:
"<a href='#' onclick='alert(\"Hi!\");return false;'>Hi</a>"
it works fine (the alert pops up when I click it), however if I create a function for the alert, it will say function undefined. Ex:
"<a href='#' onclick='alertPlease(\"Hi!\");return false;'>Hi</a>"
function alertPlease(x){
alert(x);
}
All my code is the same content script js file.
Do I have to place whatever code that can be used after loading in another js file in the background? I tried adding a background page with the 'alertPlease();' function, but that didn't work either.
Any hint will be greatly appreciated!
Thanks!
Content scripts run in an "isolated world." The webpage and content scripts can't see each other's JavaScript variables (although they can see the same DOM). When you add an onclick attribute within the DOM, the handler is created on the webpage's world, not your extension's world, so that code can't access the other function you defined. Instead of using an onclick handler, you should define your event listener in pure Javascript and then use addEventListener to attach it.
Isolated worlds are a feature to improve security and stability. Things are similar if you're developing within the Greasemonkey sandbox in Firefox.
Doing one of these today with manifest version 3, I learned:
I can't append a button to the page and use
<button type="button" onclick="myContentScriptFunction();">Call CS Function</div>
But, in the content script, I can do vanilla
let loadButton = document.createElement('button');
loadButton.innerText = 'Call CS Function';
loadButton.addEventListener('click', myContentScriptFunction);
document.querySelector('body').append(loadButton);
or jQuery
let loadButton = $("<button type="button">Call CS Function</button>")
loadButton.on("click", myContentScriptFunction)
$("body").append(loadButton);
And both worked as expected with all code in the content script
Info Source: Chrome Extension Samples Example
Specify any scripts in the manifest.json file under "content_scripts"
{
"name": "My Extension",
"content_scripts": [{
"matches": ["<all_urls>"],
"css": ["style.css"],
"js": ["jquery-1.5.js", "script.js"]
}]
}

Resources