How to intercept cross-extension messaging using Chrome DevTools? - google-chrome-extension

I am using selenium webdriver to automate chrome extension testing. I try to use driver.execute_cdp_cmd(cmd, cmd_args) to intercept the cross-extension messaging via message passing API. However, I have no idea which method in Chrome DevTools can be used to do so. Any hints are welcome!
Extension A
let extPort = chrome.runtime.connect(ExtB);
extPort.postMessage({from: "ExtA", fn: "greeting"});
extPort.onMessage.addListener(function(message, sender) {
if ((message.from == "ExtB") && (message.fn == "greeting")) {
console.log("Ext B is enabled");
}
});
Extension B
chrome.runtime.onConnectExternal.addListener(function(port) {
port.onMessage.addListener(function(message, sender) {
if ((message.from == "ExtA") && (message.fn == "greeting")) {
port.postMessage({from: "ExtB", fn: "greeting"});
}
});
});

Related

Chrome extension background script sometimes does not run after install or update

I have had recent reports of a chrome extension that I develop that stops working after an update or a fresh install. The background script seems to not start at all.
There is no response to messages sent to it from the content scripts.
There is no process for it in the task manager.
Opening background page from chrome://extensions does not show any activity in the console, or show any source files.
Profiling, memory snapshot buttons are disabled.
Once this issue appears, it persists for the chrome profile even after reloading or uninstalling/reinstalling the extension.
Restarting chrome resolves the problem.
The issue has been seen on chrome v79. But I cannot say for sure that it is exclusive to this version, as the issue is difficult to reproduce and seemingly random.
Has anyone seen such an issue, or has any ideas what to look for? I am happy to update my question with any new info I have or with any info you need.
Edit:
Here is my webNavigation listener, which is used to inject content scripts. This handler is wired up in the 'root' context of the background script (not asynchronously inside an event handler)
chrome.webNavigation.onCompleted.addListener((details) ⇒ {
if(details.frameId === 0) {
injectScript(
'js/contentScript.js',
details.tabId,
details.frameId,
details.url
).catch((e) ⇒ {});
}
}
The injectScript function is as follows
export const injectScript = ƒ (scriptPath,tab,frame,tabUrl) {
return new Promise((res,rej) ⇒ {
let options = {
file : scriptPath,
allFrames : false,
frameId : frame,
matchAboutBlank: false,
runAt : 'document_idle',
};
const cb = ƒ () {
if (chrome.runtime.lastError) {
let err = new Error('Could not inject script');
capture(err,{
...options,
tabUrl,
lastError : chrome.runtime.lastError.message,
});
rej(err);
}else{
res();
}
};
if (tabUrl.indexOf('.salesforce.com') !== -1) {
window.setTimeout(() => {
chrome.tabs.executeScript(tab,options,cb);
},500);
}else{
chrome.tabs.executeScript(tab,options,cb);
}
});
};
Note above, the capture function reports the error to a backend and I cannot see it being reported there as well. Cannot add a breakpoint in code because no source appears in the background page, as noted above.
A background service worker is loaded when it is needed, and unloaded when it goes idle.
https://developer.chrome.com/docs/extensions/mv3/service_workers/
You can use the following methods:
// Keep heartbeat
let heartTimer;
const keepAlive = () => {
heartTimer && clearTimeout(heartTimer);
heartTimer = setTimeout(() => {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
console.info('[heartbeat]')
tabs.length && chrome.tabs.sendMessage(
tabs[0].id,
{ action: "heartbeat" }
);
});
keepAlive();
}, 10000);
};
keepAlive();

Catch 'open link from external link-call' event

I'm searching for an event which triggers when an external Desktop-Software like Outlook, Thunderbird, Messenger App etc. want the default browser (firefox) to open a new link. I could manage with a workaround to catch this event if firefox was closed before:
// catch page when triggered by link and browser was closed
chrome.runtime.onStartup.addListener(function () {
// search for initial tab
chrome.tabs.query({
active: true,
currentWindow: true,
}, function (tabs) {
var initialTab = tabs[0];
// catch open of files and links when browser was closed
if (initialTab.url != 'chrome://newtab/') {
handleNewUrlRequest(initialTab.id,initialTab.url);
}
});
});
But how to catch the opening of an external link when firefox is already running?
Thank you for your help and greetings!
Thanks to #wOxxOm ill made it in this way:
// catch page when triggered by link and browser is already open (firefox) step 1
var lastRequest = null;
chrome.webNavigation.onCommitted.addListener(function (details) {
if(details.url == 'about:blank' && details.frameId == 0 && details.parentFrameId == -1 && details.transitionType == 'link') {
lastRequest = details;
}
});
// catch page when triggered by link and browser is already open (firefox) step 2
chrome.webNavigation.onBeforeNavigate.addListener(function (details) {
if(lastRequest && details.frameId == 0 && details.parentFrameId == -1 && details.tabId == lastRequest.tabId && details.windowId == lastRequest.windowId) {
console.log('New Request detected: open new Link in Firefox when Firefox is already open');
lastRequest = null;
}
});

Xamarin iOS Zxing with ZXingScannerView

Need to navigate to other view as soon as Scan completes
Using Zxing with ZXingScannerView
Using this code
scannerView.StartScanning(async (result) =>
{
if (!ContinuousScanning)
{
Console.WriteLine("Stopping scan...");
Console.WriteLine("Result: " + result.Text);
scannerView.StopScanning();
if (result != null)
{
await GetScannedDetails(result.Text);
// here i need to navigate to other screen
}
}
var evt = this.OnScannedResult;
if (evt != null) evt(result);
}, this.ScanningOptions);
When i tried navigating I got this error
Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.
The issue you are having is, you try to run UI related code inside async task. Do the navigation inside main thread
BeginInvokeOnMainThread(
() =>
{
scannerView.StopScanning();
// Navigate code goes here
});

Can I detect fullscreen in a Chrome extension?

I have a Chrome extension (specifically, a "content script") where I'd like to detect whether the page I am monitoring/changing is in fullscreen state. I have tried several APIs, as well as the "screenfull" library, but no luck so far. Any ideas?
Thanks for your help!
If you want to detect whether the page has used the Fullscreen API to enter fullscreen mode, just check document.webkitIsFullscreen.
If you want a general method to reliably detect full screen mode, the chrome.windows API is your only option. Since this API is unavailable to content scripts, you need to use the message passing API to interact with a background or event page.
Example: content script
function isFullScreen(callback) {
chrome.runtime.sendMessage('getScreenState', function(result) {
callback(result === 'fullscreen');
});
}
// Example: Whenever you want to know the state:
isFullScreen(function(isFullScreen) {
alert('Window is ' + (isFullScreen ? '' : 'not ') + 'in full screen mode.');
});
Example: background / event page
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if (message === 'getScreenState') {
chrome.windows.get(sender.tab.windowId, function(chromeWindow) {
// "normal", "minimized", "maximized" or "fullscreen"
sendResponse(chromeWindow.state);
});
return true; // Signifies that we want to use sendResponse asynchronously
}
});
You can try something like this:
var isFullScreen = (screen.width == window.outerWidth) && (screen.height == window.outerHeight);
if(isFullScreen) {
// ...
}
The simplest way is to listen for webkitfullscreenchange event, e.g
$(document).on('webkitfullscreenchange',function(){
if (document.webkitIsFullScreen === true) {
console.log('Full screen mode is on");
} else {
console.log('Full screen mode is off");
}
});

How to tell if a script is run as content script or background script?

In a Chrome extension, a script may be included as a content script or background script.
Most stuff it does is the same, but there are some would vary according to different context.
The question is, how could a script tell which context it is being run at?
Thank you.
I think this is a fairly robust version that worked in my initial tests and does not require a slower try catch, and it identifies at least the three primary contexts of a chrome extension, and should let you know if you are on the base page as well.
av = {};
av.Env = {
isChromeExt: function(){
return !!(window['chrome'] && window['chrome']['extension'])
},
getContext: function(){
var loc = window.location.href;
if(!!(window['chrome'] && window['chrome']['extension'])){
if(/^chrome/.test(loc)){
if(window == chrome.extension.getBackgroundPage()){
return 'background';
}else{
return 'extension';
}
}else if( /^https?/.test(loc) ){
return 'content';
}
}else{
return window.location.protocol.replace(':','');
}
}
};
Well I managed to work out this:
var scriptContext = function() {
try {
if (chrome.bookmarks) {
return "background";
}
else {
return "content";
}
}
catch (e) {
return "content";
}
}
It's because an exception would be thrown if the content script tries to access the chrome.* parts except chrome.extension.
Reference: http://code.google.com/chrome/extensions/content_scripts.html
The best solution I've found to this problem comes from over here.
const isBackground = () => location.protocol === 'chrome-extension:'
The background service worker at Manifest v3 does not contain a window.
I use this as part of my extension error handling which reloads the content scripts, when i receive an Extension context invalidated error:
...
if (!self.window) {
console.warn('Background error: \n', error);
} else {
location.reload();
}
...

Resources