I've built an extension that loads a React-application via an iframe.
I'm passing a message from the application and listening for that message in content script to ensure application has loaded.
Here's a simplified version of what I have so far:
App.js
componentDidMount() {
window.top.postMessage({isAppLoaded: true}, '*'};
}
Content.js
chrome.runtime.onMessage.addListener(function (msg, sender) {
if (msg.isPageLoaded) {
console.log('Current page has loaded!');
}
}
window.addEventListener('message', function(event) {
if (event.data.isAppLoaded) {
console.log('React app successfully loaded!');
}
}
Background.js
chrome.tabs.onUpdated.addListener(function (tabId, info) {
if (info.status === 'complete') {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {isPageLoaded: true});
})
}
})
On some websites the page loads first then the React-app and vice-versa on some other websites. This becomes more complicated with websites that have lots of embedded iframes such as LinkedIn.
What is the correct/better way to guarantee the React-app AND the page has finished loading?
Please ignore the wildcard message passing. I'm aware of its security vulnerabilities. Above code snippets are simplified version of what I have.
Related
I'm writing chrome extension and I need to catch all requests from the beginning of downloading page.
I'm using chrome.webRequest.onBeforeRequest in background.js, and send them to content.js for logging.
But I don't see all requests. It looks like background.js start working with delay (and I missing important requests). How can I avoid it?
Here is my background.js:
chrome.webRequest.onBeforeRequest.addListener(logURL, { urls: ["<all_urls>"] });
function logURL(requestDetails) {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
chrome.tabs.sendMessage(
tabs[0].id,
{ message: requestDetails.url },
function(response) {}
);
});
}
and content.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.message == "reload") {
location.reload();
console.log("reload");
} else {
console.log(request.message);
}
});
How can I catch all request or run my background.js previously? Maybe I have made mistakes and can't find them?
Thanks!
If somebody will have the same mistake:
chrome.webRequest catch all requests, but content.js file inject later (after few requests).
So you should collect requests in background.js and send them after content.js injection (for example you can send a message to background.js).
Porting extension from Chrome into FF
Followed this tutorial (which works fine in Chrome): http://www.codingscripts.com/check-whether-user-has-a-chrome-extension-installed/
Sending message from webpage to extension:
In (web)pagescript.js this has:
function IsExist(extensionId,callback){
chrome.runtime.sendMessage(extensionId, { message: "installed" },
function (reply) {
if (reply) {
callback(true);
}else{
callback(false);
}
});
}
IsExist("Your extension id",function(installed){
if(!installed){
alert("Please install extension ");
}
});
Receiving message from webpage in extension:
chrome.runtime.onMessageExternal.addListener(
function(req, sender, callback) {
if (req) {
if (req.message) {
if (req.message == "installed") {
callback(true);
}
}
}
return true;
});
What I'm trying to achieve
A couple of html pages on my website need to redirect back to the homepage when the extension is NOT installed. So those pages need to be able to figure out (on their own) if the extension is installed or not.
Error I'm getting when I open webpage
ReferenceError : chrome is not defined. (I also tried with browser.runtime.onMessageExternal but then it throws "browser" is not defined).
Is there no way to do this similar to what can be done in Chrome ?
Thanks to all the comments this is what I came up with. (I had to go for document_end (altho comments advise document_start) cause I had other things going on in content_script.js
In my add-on's content_script.js
document.body.classList.add("plugininstalledblabla");
In my add-on's manifest.json
"content_scripts":
[
{
"matches": ["*://*/*"],
"all_frames": true,
"js": ["content_script.js"],
"run_at": "document_end"
}
]
in my website's main.js script
$(window).on("load", function() { // !! Window onLoad cause : document_end -> DOM should be loaded here
// Set
$body = $('body');
if(document.body.classList.contains("plugininstalledblabla")){
console.log('addon is installed');
}
});
I would like to link to chrome://history page in a button of an extension I'm working, but with href="chrome://history" the console says Not allowed to load local resource: chrome://history/
What can I do?
Thanks and greetings
Edited:
I'm trying with this:
The button I want to trigger with the event has .btn-history class.
In content.js:
function messaging(){
chrome.runtime.sendMessage({command: "openHistory"});
}
document.addEventListener('DOMContentLoaded', function () {
document.getElementsByClassName('btn-history')[0].addEventListener('click', messaging);
});
In background.js:
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if(command == "openHistory") {
chrome.tabs.create({'url': 'chrome://history'});
}
});
But doesn't work.
If you are using jquery, you can have a button in your html, like so:
<button id="historyBtn">History</button>
And in your javascript you can use define an event handler for the button using jquery:
$(function(){
$("#historyBtn").click(function(){
chrome.tabs.create({url: "chrome://history"});
});
});
If you would like to change the currently active tab to open history, you can replace the chrome.tabs.create line in the above snippet with something like:
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.update(tabs[0].id, { url: "chrome://history" });
});
Hope this helps :)
extension script:
$(document).ready(function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id,{"askDomain":true},function(response) {
if(response.domain) {
console.log(response.domain);
populateExtension(response.domain);
}
});
});
});
content script:
chrome.runtime.onMessage.addListener(["askDomain"],function(request,sender,sendResponse) {
console.log("asking domain");
if(request.askDomain) {
// sendResponse({"domain":window.location.href});
}
});
I am sending message from extension scrip to content script. When the page loads I receive this error on the content page console:
extensions::event_bindings:266Uncaught Error: This event does not support filters.
What am i doing wrong?
I've read through all of the related errors, and I think this case is a bit different. I'm trying to send a message from the background context to a content script. E.g.
chrome.tabs.query({
currentWindow: true,
active: true
}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id, {
name: name,
args: args
}, function(response){
if (!response) return callback('You tried to message a tab that does not exist');
});
});
This will throw a Port error if the open tab was loaded before the extension was installed. To recreate:
Open a new tab and load a web page
Navigate to the extensions tab and reload the local unpacked extension
Navigate back to the web tab and invoke the extension via a Browser Action--it will throw the Port error unless the web page is manually reloaded.
Is there a workaround for this?
Instead of sending a message, programatically insert a content script and use the callback's results:
chrome.tabs.query({
currentWindow: true,
active: true
}, function(tabs) {
chrome.tabs.executeScript(tabs[0].id, {
code: 'location.href',
runAt: 'document_start',
allFrames: false // Run at the top-level frame only to get
// just one result
}, function(results) {
var result = results[0];
console.log(result); // Example
});
});
Instead of specifiying the code in a string, you can also run a file by using file: 'code.js' instead of code: '...'.