How to update the DOM using chrome.runtime.onMessageExternal function callback - javascript

I'm working on a Chrome app that received an address from an extension and is supposed to open that URL in the app window, using the webview tag and Chrome runtime API message sending.
I'm trying to get the chrome.window.create callback function to update the index.html page the was created.
It's not working as I planned though.
Here is the code:
chrome.runtime.onMessageExternal.addListener(
function (request, sender, sendResponse) {
chrome.app.window.create(
'index.html',
{PARAMETERS},
function () {
//get
var thisWindow = document.querySelector("webview");
thisWindow.setAttribute("src", request.url);
}
);
}
The index.html file is just a webview tag and some styling.
This opens an empty window once the message is received. However, it opens the page when I send again while the app is open, meaning that the callback probably tried to access the index.html file before it was created?
Thanks for reading!

The window.create callback function is called prior to the execution of the created windows onload event (see here). So presumably the DOM is not yet available at this stage. What you can do is, bind your modifications to the created windows onload event, thus ensuring the DOM is available.
chrome.app.window.create(
'index.html',
{
PARAMETERS
},
function (createdWindow) {
var contentWindow = createdWindow.contentWindow;
contentWindow.onload = function() {
var thisWindow = contentWindow.document.querySelector("webview");
thisWindow.setAttribute("src", request.url);
}
}
);

Related

Porting Chrome extension to Edge

I have created a chrome extension to enable clipboard data access. The solution is explained in details here Implementing 'Paste' in custom context menu. Now the problem is how to port this extension to Edge. There is a tool for that I know I used it, and maybe it is working, but my problem is how to "consume" this extension, what is equivalent to chrome.runtime.sendMessage in Edge? In Chrome I used this https://developer.chrome.com/apps/messaging#external-webpage - the part 'Sending messages from webpages', but in Edge I just can't find anything similar. Thanks for your time and help.
There is runtime.sendMessage() in Edge too.
https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/sendMessage
The thing to keep in mind is that the runtime object is defined on the browser object, not chrome.
Sends a single message to event listeners within your extension or a different extension.
If sending to your extension, omit the extensionId argument. The runtime.onMessage event will be fired in each page in your extension, except for the frame that called runtime.sendMessage.
If sending to a different extension, include the extensionId argument set to the other extension's ID. runtime.onMessageExternal will be fired in the other extension.
Extensions cannot send messages to content scripts using this method. To send messages to content scripts, use tabs.sendMessage.
This is an asynchronous function that returns a Promise.
I managed to solve this. There is no way (at least I couldn't find it) to communicate from web page with extension background script (and only the background script can get data from the clipboard and has the 'browser' object defined). So what I did, I communicated with content script and content script communicated with background script. Here is the code.
PAGE CODE:
contextMenuPaste: function () {
if (getBrowserName() == 'EDGE') {
window.postMessage({
direction: "from-page-script"
}, "*");
}
},
window.addEventListener("message", function (event) {
if (event.source == window &&
event.data.direction &&
event.data.direction == "from-content-script") {
console.log('Data in page script', event.data.message);
}
});
CONTENT SCRIPT CODE
window.addEventListener("message", (event) => {
// If message came from page-script send request to background script to get clipboard data
if (event.source == window &&
event.data &&
event.data.direction == "from-page-script") {
browser.runtime.sendMessage({
message: "getClipboardData"
},
function(clipboardData) {
messagePageScript(clipboardData);
}
);
}
});
// Send clipboard data to page script
function messagePageScript(clipboardData) {
window.postMessage({
direction: "from-content-script",
message: clipboardData
}, "*");
}
BACKGROUND SCRIPT CODE
browser.runtime.onMessage.addListener(
function(req, sender, callback) {
if (req) {
if (req.message) {
if (req.message == "installed") {
console.log('Checking is extension is installed!');
callback(true);
}
else if(req.message = "getClipboardData") {
console.log('Get clipboard data');
callback(getDataFromClipboard());
}
}
}
return true;
}
);
function getDataFromClipboard() {
var bg = browser.extension.getBackgroundPage();
var helperTextArea = bg.document.getElementById('sandbox');
if (helperTextArea == null) {
helperTextArea = bg.document.createElement("textarea");
document.body.appendChild(helperTextArea);
}
helperTextArea.value = '';
helperTextArea.select();
// Clipboard data
var clipboardData = '';
bg.document.execCommand("Paste");
clipboardData = helperTextArea.value;
helperTextArea.value = '';
return clipboardData;
}
But there is one tiny issue. This code works if I have a break-point set on line
bg.document.execCommand("Paste");
and it doesn't if I don't have that break-point. I thought it is a trimming issue, added pauses, delayed executions but nothing helped. I will start a new question for that issues and will copy solution here (if I find one).

Unable to access Global variables in chrome.tabs.executescript in Chrome version 55

I recently updated Chrome to version 55.0.2883.75.
I am using a self developed Chrome Plugin to parse my HTML files wherein I use chrome.tabs.executescript to get data from background HTML page.
So when I execute chrome.extension.onRequest, I save the background page's parsed data to a global variable and access it in the callback function of chrome.tabs.executescript and process it.
This was working fine till I update to Version 55.0.2883.75.
How can I access the global variables in the new version ??
My Code Below :
Step 1 :
chrome.extension.onRequest.addListener(
function (request, sender, sendResponse) {
parser = new DOMParser();
htmlDoc = parser.parseFromString(request.content, "text/html");
//outputJson is a global variable which is Populated here
outputJson = parseMyPage(outputJson, htmlDoc);
});
Step 2:
chrome.tabs.getSelected(null, function (tab) {
// Now inject a script onto the page
chrome.tabs.executeScript(tab.id,{
code: "chrome.extension.sendRequest({content: document.body.innerHTML}, function(response) { console.log('success'); });"
}, function () {
//my code to access global variables
if (outputJson && null != outputJson) {
// other stuff
}
});
});
The way your code is designed, you are relying on the order in which two asynchronous blocks of code are executed: the extension.onRequest1 event and the callback for tabs.executeScript(). Your code requires that the extension.onRequest1 event fires before the tabs.executeScript() callback is executed. There is no guarantee that this will be the order in which these occur. If this is a released extension, it is quite possible that this was failing on users' machines, depending on their configuration. It is also possible that the code in Chrome, prior to Chrome 55, resulted in the event and callback always happening in the order you required.
The solution is to to rewrite this to not require any particular order for the execution of these asynchronous code blocks. Fortunately, there is a way to do that and reduce complexity at the same time.
You can transfer the information you desire from the content script to your background script directly into the callback of the tabs.executeScript(), without the need to explicitly pass a message. The value of the executed script is passed to the callback in an array containing one entry per frame in which the script was injected. This can very conveniently be used to pass data from a content script to the tabs.executeScript() callback. Obviously, you can only send back a single value per frame this way.
The following code should do what you desire. I hand edited this code from your code in this Question and my answer here. While the code in that answer is fully tested, the fact that I edited this only within this answer means that some errors may have crept in:
chrome.tabs.getSelected(null, function (tab) {
// Now inject a script onto the page
chrome.tabs.executeScript(tab.id,{
code: "document.body.innerHTML;"
}, function (results) {
parser = new DOMParser();
htmlDoc = parser.parseFromString(results[0], "text/html");
//outputJson is a global variable which is Populated here
outputJson = parseMyPage(outputJson, htmlDoc);
//my code to access global variables
if (outputJson && null != outputJson) {
// other stuff
}
});
});
extension.sendRequest() and extension.onRequest have been deprecated since Chrome 33. You should replace these anywhere you are using them with runtime.sendmessage() and runtime.onMessage.

Run Javascript in iFrame context

I am attempting to run a script in a webpage, that should be executed in an <iframe>. Right now I can call a function that is set within the <iframe>.. I'm just having issues running a script to the <iframe>'s context.
Here's how I run a function set in the <iframe>
$('#iframe').get(0).contentWindow.performSearch();
Now instead of calling the preformSearch function, I wish to run a script - for this example, this is the script...
console.log('worked!');
My actual script is a big one, so I won't put it here - for the sake of this question.
So, is there any way to run that script through the <iframe>'s context? For example, my first guess would be/was..
$('#iframe').get(0).contentWindow.function(){
console.log('worked!');
}
I've never messed with running functions through something like an <iframe> before though, so I'm stumped.
Thanks for any help in advance!
NOTE I am using NW.js (Node-Webkit) to remove all <iframe> restrictions.
NOTE v2 The preformSearch() function was just a reforence on how I call functions in the frame.
You could try and use the messaging mechanizm
which means that on the parent frame you could send the message with
var win =$("#iframe").contentWindow;
win.postMessage({ messageType: "predefinedMessage", features: data }, '*');
and than in the iframe you could get the message
function FrameCommunicator(attachManager) {
var mfilesFrame;
function _actOnMessage(data) {
if (data.messageType === "predefinedMessage") {
//perform here what you need
}
}
window.addEventListener("message", function (e) {
var data = e.data;
mfilesFrame = e.source;
_actOnMessage(data);
}, false);
}
now the thing is that the iframe and the parent fram can have the same code if they reference to the remotely.
Another way would be just to send the content of the message as JS and run it with eval , but that is risky , bad practice and lotsa other things :)

Injected HTML accessing the Chrome API and global variables

I'm working on a Chrome Extension, and I'm new to the process. The extension I'm working on injects an HTML sidebar into the page, injects java-script functions to the header, then let's the user press the buttons on the sidebar to create/save my extension's data.
However, when I want to save the information, I use localStorage, however, the localStorage always saves with respect to the current website. How can I use localStorage to save with respect to our extension?
Furthermore, I would like to use some global javascript variables in the chrome-extension. Where do these belong? I know I can't currently access them from the injected Javascript.
I've looked into message passing, and I've had some trouble with it. I'm not sure how it works in the scope of injected javascript into a page's header. I've tried working with this example, but my extension doesn't seem to catch the message.
// This function is called in the injected header javascript.
function sendToExtension() {
setTimeout(function() {
console.log('page javascript sending message');
window.postMessage({ type: 'page_js_type',
text: "Hello from the page's javascript!"},
'*' /* targetOrigin: any */);
}, 10);
}
// This is installed in a background script.
window.addEventListener('message', function(event) {
console.log('content_script.js got message:', event);
});
You have to use Chrome's sendMessage function and onMessage listener. See below:
function sendToExtension() {
console.log('Sending message');
chrome.runtime.sendMessage({ext: "myExtension"}, function(response) {
console.log(response.ack);
});
}
// Listener - Put this in the background script to listen to all the events.
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.ext) {
console.log('Message received from ' + request.ext);
sendResponse({ack:'received'}); // This send a response message to the requestor
}
});

Chrome extension development: Call a method in popup from background_html

I have a popup named mf_popup.html and my background page is named mf_background.html.
As I start the browser my background page fires (as is my understanding, I am new to Chrome development), and I would like to call a function in my mf_popup.html page from my background page.
For example:
Start chrome
background page fires
background page calls some function in popup page (which initiates some stuff)
How do I do the above?
if you place the needed code in both html-files in mf_javascript.js and includes the script in both with this line:
<script type="text/javascript" src="mf_javascript.js">
mf_popup.html
//sendRequest([any type] request, [function] responseCallback)
chrome.extension.sendRequest({
function: "foo",
params: [myParam1, myParam2],
function(response) {
alert("foo returns:"+response.result+");
}
});
mf_background.html
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
if(request.function == "foo")
var bar = foo(request.params[0], request.params[1]);
sendResponse({result: bar});
}
);
You also could just use sendRequest("foo") if you don't want send any params and/or use an callback function.

Categories

Resources