How to keep popup open when launchWebAuthFlow starts? - javascript

I am running launchWebAuthflow in background.js and successfully retrieving the Oauth2 token in the call back. However, the extension pop closes during this flow and thus it doesn't receive my response. When I have dev-tools open on the pop-up, it stays open and everything works as expected. It seems that somehow launchWebAuthFlow is taking focus from the popup and subsequently closing it even though it should be running in the background. Any advice would be much appreciated. See code in background.js below
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.message === "getAuth") {
chrome.identity.launchWebAuthFlow({
url: authUrl.href,
interactive: true,
},
(redirectUrl) => {
if (redirectUrl) {
// The ID token is in the URL hash
const urlHash = redirectUrl.split("#")[1];
const params = new URLSearchParams(urlHash);
const jwt = params.get("id_token");
console.log(jwt);
sendResponse({
token: jwt
});
}
}
}
return true;
}

Related

upgrading chrome extension from manifest version2 to v3, need to get clipboard text in background.js

Hi I am converting Google chrome extension from manifest version-2 to version-3
facing 2 issues those are mentioned below, but before that I will explain what extension in expected to do.
On click specific button on webpage I am calling console application that is copying JSON string in clipboard, then in chrome extension background.js I am getting clipboard data and passing it to content.js which is showing it in web page.
Errors / challenges:
1- Need to get clipboard text into a variable in background.js. I am able to get it in content.js but I need it to get it in background.js
2- I am getting these 2 error in background.js console, but extension is working
Unchecked runtime.lastError: Native host has exited.
Unchecked runtime.lastError: The message port closed before a response was received.
My Background.js looks like this
var port = null;
var tabId = null;
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
tabId=sender.tab.id;
var hostName = "my.console.app";
port = chrome.runtime.connectNative(hostName);
port.onDisconnect.addListener(onDisconnected);
sendResponse({status: 'ok'});
return true;
});
function onDisconnected() {
port = null;
SendResponse();
}
//this funciton need to upgrade to manifest version 3 because in v3 `chrome.extension.getBackgroundPage` is not compatible
function SendResponse() {
bg = chrome.extension.getBackgroundPage();
bg.document.body.innerHTML = ""; // clear the background page
var helper = null;
if (helper == null) {
helper = bg.document.createElement("textarea");
helper.style.position = "absolute";
helper.style.border = "none";
document.body.appendChild(helper);
}
//Focus the textarea
helper.select();
// perform a Paste in the selected control, here the textarea
bg.document.execCommand("Paste");
// Send data back to content_script
chrome.tabs.sendMessage(tabId, { action: "MY_CUSTOM_EVENT", response: helper.value });
}
content.js
document.addEventListener("MY_CUSTOM_EVENT", function (data) {
chrome.runtime.sendMessage({ runConsoleApp: true }, response => {
});
});
async function copyToTheClipboard(textToCopy){
navigator.clipboard.readText()
.then(text => {
//console.log('Pasted content: ', text);
$('.simulateEidResponse').html(text).trigger('click');
})
.catch(err => {
console.error('Failed to read clipboard contents: ', err);
});
}
Currently there's no way to do it in the background script due to crbug.com/1404835.
In the future the workaround will be execCommand + offscreen API.
The only reliable solution that works regardless of whether the tab is focused or not is to use document.execCommand("Paste") in the content script.
// background.js
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.runConsoleApp) {
chrome.runtime.connectNative('my.console.app')
.onDisconnect.addListener(() => sendResponse(true));
return true;
}
});
// content.js
document.addEventListener('MY_CUSTOM_EVENT', async e => {
await chrome.runtime.sendMessage({ runConsoleApp: true });
document.querySelector('.simulateEidResponse').focus();
document.execCommand('selectAll');
document.execCommand('paste');
});

Value appears in console.log but is undefined when assigning via innerHTML

I'm trying to get the timestamp of a YouTube video when a button is clicked on my extension. Right now, my code is setup like so:
contentScript.js
var time;
function get_timestamp() {
time = document.getElementsByClassName('video-stream')[0].currentTime
console.log(time);
return time;
};
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.message === "pause_video") {
pause_video();
}
if (request.message === "get_time") {
sendResponse({timestamp: get_timestamp()})
}
}
);
The pause function is also defined in the same file and works as expected.
popup.js
function get_time() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {message: "get_time"}, function(response) {
console.log(response.timestamp.toString());
return response.timestamp.toString();
});
})
};
popup2.js
document.getElementById("time").innerHTML = get_time();
The line in popup2.js is in a function that is called when the button is pressed. The script is split into 2 files as part of the testing, but they could be combined if necessary. The rest of the program works as expected, with functions to pause the video and get the videoID from the url working fine. All of the console.log commands show the current timestamp of the video, but I always get undefined when I use innerHTML. I feel like this ought to be simple but I can't figure out where the problem seems to be.

How to run callback function from content script after loading page ? chrome extension

I want to run a callback function from content script after tab loading new page .
Here is my code :
content_script.js
chrome.runtime.onMessage.addListener(function(request, sender, callback) {
if (request.id == "content_script") {
// open google.com
chrome.runtime.sendMessage({
"id" : "openUrl",
"url" : "https://google.com"
}, function(response) {
});
// call background script
// go to the claim code page
chrome.runtime.sendMessage({
"id" : "background"
}, function() {
alert("test");
});
}
});
background.js
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg.id == "openUrl") {
var tabId = sender.tab.id;
var url = msg.url;
openUrl(tabId, url);
} else if (msg.id == "background") {
setTimeout(function() {
sendResponse();
}, 5000);
}
});
function openUrl(tabId, url) {
chrome.tabs.update(tabId, {
url : url,
active : false
}, function() {
console.log("open tab url callback");
});
};
I also uploaded the source code to google drive, you can download it using the bellow link :
https://drive.google.com/open?id=15zSn40z4zYkvCZ8B-gclzixvy6b0C8Zr
as you can see the alert test don't show !
However if I remove the code which open new url , then alert ("test") appear !
I am not sure why ! but it looks like javascript lost the reference to the call back function when I open new url .
How can I solve the problem ? what's the correct way ?
Thank you
The sendResponse function becomes invalid after the message callback returns, unless you return true from the event listener to indicate you wish to send a response asynchronously. (https://developer.chrome.com/extensions/runtime#event-onMessage)
Add return true; in background.js to make this handler asynchronous.
Now you get an error Attempting to use a disconnected port object in the sendResponse(); call of background.js, and yes, that's a result of the page navigating away and losing the context that the content script was running in.
There's no way to make this work: The context in which you wanted to call alert() simply doesn't exist anymore.
Try using chrome.tabs.sendMessage instead. But this means you have to set up the listener at the top level, and not inside of any callback. Message passing is hard.

Open chrome extension page programmatically in same tab as website

I am making an chrome-extension-based editor, and I want the user to be able to open a file within the extension when they receive a link like this: http://myeditor.com/link.html?file=xxxx
When going to that website I would like that:
if the person opening the url has the extension, open chrome-extension://blahblah/?file=xxx
else show a read-only version of the file and a link to install the extension.
Now to do that, I have in the link.html file a script that looks like this:
if (window.chrome) {
if (chrome.app.getDetails() === null) {
chrome.runtime.sendMessage(chromeId, { message: "isInstalled" }, function (reply) {
if (reply) {
chrome.runtime.sendMessage(chromeId, { gotoUrl: document.location.search });
}
});
}
}
and in the extension's background.html:
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (request) {
if (request.message) {
if (request.message == "isInstalled") {
sendResponse(true);
}
}
if (request.gotoUrl) {
var url = chrome.extension.getURL(request.gotoUrl);
chrome.tabs.create({url: url}); // how do I open this in the same tab rather than create a new tab?
}
}
return true;
});
The issue is that the code creates a new tab, is there a way I can open the extension page in the same tab as the page I called it from?
Actually chrome.tabs.update instead of chrome.tabs.create works fine...

OAuth Flow not working when using Promise

I am building a simple chrome extension which integrates with Twitter using OAuth. I have slightly modified the Chrome OAuth Tutorial to integrate with Twitter. The extension is build in Reactjs+Flux.
When the user clicks on "Sign in with Twitter" button, an Action signin is triggered, which is declared as follows:
signin: function(){
ChromeUtils.connecttotwitter().then(alert("Step After Then"));
AppDispatcher.dispatch({actionType:TweetSmartActions.SIGN_IN,signedInTwitterUserId: response.user_id});
},
The ChromeUtils.connecttotwitter() is defined as follows:
var ChromeUtils = {
connecttotwitter: function () {
return new Promise(function(fulfill,reject){
var request = {
type : "background.twitterRequestToken",
};
alert("Before sendMessage");
chrome.runtime.sendMessage(request, function(response) {
if (response)
{
fulfill(response);
}
else
{
reject(response);
}
});
});
},
And the event listener onMessage is defined in the background.js as:
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.log("background.js: " + JSON.stringify(request));
var type = request.type;
if (type == "background.twitterRequestToken")
{
oauth.authorize(function(token,secret,userId,screenname){
sendResponse({success:true,userId:userId,screenName:screenname});
});
alert("Alerting before returning true");
return true;
}
When I click on the "Sign In With Twitter" button, the authentication flow does start and a new page opens. However, after I introduced the Promise, the new page does not redirect to the twitter oauth page. In fact, to debug that I have put the following alert statements in chrome_ex_oauth.js:
ChromeExOAuth.prototype.initOAuthFlow = function(callback) {
if (!this.hasToken()) {
var params = ChromeExOAuth.getQueryStringParams();
if (params['chromeexoauthcallback'] == 'true') {
var oauth_token = params['oauth_token'];
var oauth_verifier = params['oauth_verifier']
this.getAccessToken(oauth_token, oauth_verifier, callback);
} else {
var request_params = {
'url_callback_param' : 'chromeexoauthcallback'
}
this.getRequestToken(function(url) {
alert("Url after get request token " + url);
window.location.href = url;
alert(window.location.href);
}, request_params);
}
Here, the url in the first alert is the twitter oauth url but the second alert gives the Chrome extension Url -
chrome-extension://kiekipigbdldhggmlohbnhofnjhcbmem/chrome_ex_oauth.html
Why did the url not get assigned to window.location.href?
Any ideas on what might be happening?
The issue was not because I was using a Promise, but because when using Flux, the Action was being Dispatched before the response from the Promise was received and this was causing the app to hang somehow
signin: function(){
ChromeUtils.connecttotwitter().then(alert("Step After Then"));
AppDispatcher.dispatch({actionType:TweetSmartActions.SIGN_IN,signedInTwitterUserId: response.user_id});
},
In the above, the AppDispatcher.dispatch should be called in the function which is invoked on then.

Categories