executeScript in background page fails silently only when DevTools undocked - google-chrome-extension

My DevTools extension injects script into the inspected window in order to parse and receive messages from-- normally on page load, but also when DevTools is first opened.
The relevant line in the background page is as follows:
chrome.tabs.executeScript(message.tabId,{ file: 'insert.js', runAt: 'document_idle' },function(results){console.log(results);});
This line normally displays [null] in the background console upon success-- and is successful both when opening DevTools in docked state, and when a user navigates to a new URL with DevTools in any state. It's only not working when DevTools is opened in an undocked state; in that case, the call is failing silently and results is returning undefined.
Permissions are "tabs", "http:///", "https:///"; and again, it's only not working in this one very specific case.
Is this a known issue with a workaround or should I consider this a Chromium bug?

The test case of your bug report contains a typo.
he onMessage event listener in your event page expects the tab ID to be available as message.tabId:
chrome.tabs.executeScript(message.tabId,{ file: 'insert.js', runAt: 'document_idle' });
However, in the devtools page, the message is being sent as "tabID" (note: capital D).
chrome.runtime.sendMessage({type: 'newpageload',tabID: chrome.devtools.inspectedWindow.tabId});
Because message.tabId is undefined in the event page, Chrome defaults to inserting the content script in the active tab of the current window. When the developer tools is undocked, this tab is (coincidentally) identical to the inspected tab, so you did not notice the error.
When the devtools are undocked, the current window is the devtools. Since you cannot script the developer tools, nothing appears to happen. If you read the value of chrome.runtime.lastError in the callback of chrome.tabs.executeScript, you would have noticed that you're trying to insert the script in the wrong window.
After changing the "tabId" to "tabID", the test case works as expected.

Related

Safari Extension: Display a local HTML, instead of the default or blank page, when the user opens a new tab

I'm trying to port a simple Chrome extension to Safari extension. It should display a local HTML when the user opens a new tab, but the path to the file should not appear in the URL-bar.
The code for the Chrome Extension is:
manifest.json
{
...
"chrome_url_overrides": {
"newtab": "main.html"
},
"permissions": [
"tabs"
]
}
main.html
<html>
<head></head>
<body>
Hello World!
</body>
</html>
In Safari, the close I get is to create a button on the toolbar an redirect to an URL.
How can I achieve the same functionality? I've tried to find examples, with no luck.
This may not be possible to do reliably. You can detect when a new tab is opened using the open event that is fired on safari.application or on a SafariBrowserWindow object, but then you have to make sure the new tab is not going to have a page loaded into it right away. That you can do by listening for a beforeNavigate event on the new tab...but then there are complications.
If the tab remains truly blank, no beforeNavigate event will be fired right away, so you can use a timeout to stop listening for the event and then proceed with loading the page of your choice. But new tabs may not remain blank. They can show the Top Sites page (the default behavior), they can show the user-specified home page, or they can show the page that is loaded in the previous tab.
In the home-page and same-page cases, a beforeNavigate event will be fired on the new tab, and its url property's value will be the URL to be loaded. Whether or not to hijack these types of new tab and load your own page into them is up to you.
In the Top Sites case, a beforeNavigate event will be fired on the new tab, but its url property will be null. So you might think you can detect that and then load the page of your choice in the new tab. However, there is a problem, because this same behavior (a beforeNavigate event with a null url) happens when any extension opens one of its own pages in a new tab. Therefore, if you were to hijack all tabs with a null beforeNavigate URL, you would make it impossible for other extensions to open their own pages.

which window object does getBackgroundPage() method really return?

in the documentation, it says, getBackgroundPage()
Returns the JavaScript 'window' object for the background page running
inside the current extension.
which window is it? is it the same window that we get with: "chrome.windows.getCurrent()".
If you specify a background page in your manifest.json, the page is actually 'open' when your extension is running (go to chrome://extensions and look for extensions with a property Inspect views: some_background_page.html). Although you can't actually see the page, it does exist, and as the documentation states, running getBackgroundPage() will return that page's 'window' object, allowing you to perform all of the actions you could on a normal window (see here for a list of some of those properties/actions).
To answer your second question, you can read here that the value of current window falls back to the last active window when being called from a background page. Here is a sample background.js that may help explain - it will show identical IDs for getCurrent and getAllinWindow, both of which will refer to the page from the active/visible page from which you called the extension - not the background page itself:
// This gets all current window tabs
chrome.tabs.getAllInWindow(null, function(tabs){
// This alerts the window ID when getCurrent is called from background.js
chrome.windows.getCurrent(function(window){
alert("Background ID: " + window.id);
});
// Now we loop through the tabs returned by getAllInWindow,
// showing the window ID. The ID is identical to the one above,
// meaning that getCurrent (when called from a background page)
// returns the current active window - NOT the background page
chrome.windows.get(tabs[0].windowId, function(window) {
alert("Active Window: " + window.id);
});
});
Hope that helps!

Why does a browser action's default icon reapper after a custom icon was applied?

I have a strange problem with a browser action icon in Chrome. There is a default icon for browser action defined in manifest. The icon is displayed correctly. Then in a background page, under some conditions, I call:
chrome.browserAction.setIcon({path:"green_32.png", tabId:request.tabId});
This icon blinks for a moment, and then changes back to the default icon. The active tab and its id passed to setIcon remain the same during all the process.
Can someone suggest an idea why this can happen?
The reason why the icon was reset to default state every time is because I called setIcon before the tab finishes loading and obtains "complete" state.
I guess there should be some information about this in documentation on tabs or on browser actions, but I didn't find it: the default icon is actually applied - by-design - to a specific page after it finishes loading. I moved the call setIcon into tabs.onUpdated handler, and now custom icon persists.
This contradicts to my former understanding that the browser action icon is set on a per tab basis, regarless to a page loaded into the tab and its state.
#KonradDzwinel kindly provided a simple extension to test the case (look at the comments). I changed its background.js script to demonstrate this behaviour:
chrome.browserAction.onClicked.addListener(function(tab)
{
chrome.browserAction.setIcon({path: 'gfx/icon2.png', tabId: tab.id});
});
To reproduce this behaviour, on any tab press the browser action icon to get it changed. Then refresh the page. As a result the browser action icon reset back to default.
If this behaviour is explained in some documentation, please, write this in comments, and I'll update the answer. From what I have read so far, I was convinced that default icon is set for new tab at its creation time, and then any changes to it are solely under extension's control.

Open and pass data to a popup (new tab) from content script

I'm writing a chrome extension and have a question.
My extension has some .html page in it, let it be 'popup.html'. I inject a content script into some page and this script opens a 'popup.html' in a new tab with something like 'var p = window.open(chrome.extension.getURL('/popup.html'), "popup")', which works perfectly. Next, I need to pass some data to this window and I can't figure how to do it in a simple way.
For some reason I can't call child window's function from a content script with
var p = window.open(chrome.extension.getURL('/popup.html'), "popup");
p.foo(data);
In the console I see Uncaught TypeError: Cannot call method 'foo' of undefined message.
I can't pass data in a query string, because the data is simply too big.
Is there an elegant and simple way to pass data to such kind of window? I thought about messaging, but how do I effectively get tab ID of a newly opened window w/out using a background page?
Thanks a lot in advance.
UPD:
I tried to inverse the logic and get a data from parent window with 'window.opener.foo()' but in a newly opened tab window.opener returns null.
Ok, I found two solutions to my problem.
1) Add a background page, which opens a popup with chrome.tabs.create(). Then send a message from a content script to a background page, which re-sends it to a corresponding tab via chrome.tabs.sendMessage(). It looks a little ugly, but works.
2) A better one, w/out background page. Extension (popup) page creates a listener for long-lived connection. Then content script sends a message to this connection. A problem here is that a listener is not created right after the page is opened, so there should be a mechanism for a content script to wait until popup is loaded. It can be a simple setTimeout or a notification from popup via same long-lived connection.
If anyone has a better solution I'd gladly check it out as well.

Hidden tabs in google chrome extensions

I've a chrome extension that sends a message from the content script to the background page and logs the tab_id of the content script.
I noticed that on google.com|de|at two messages are logged thus two content scripts are created: one for the actual web page shown in the tab (e.g. https://www.google.com/search?q=python+standard+library ) and another content script for the first item in the google result list ( in the above example http://docs.python.org/library/ )
Even stranger - the tab_id of the second content script (the hidden one) is not valid. I.e. chrome.pageAction.hide(tab_id) causes the following error to appear:
Error during pageAction.hide: No tab with id: 71
Is there a way to figure out if a content script belongs to a "hidden" tab?
thanks,
Peter
First of all, you can use onCreated and/or onUpdated to keep track of tabs and url mappings without the need for a content script.
However, if there's more to your content script than just informing the background page of the tab id, it may mean more checking.
If your content script is run on all_frames, then you will be getting messages from the content script in the top window and all internal frames. Still, when I test a sample implementation, I get the same IDs for all of them. Also, none of them appear to be from entries in the search result list.
If you are running the script in all tabs, you can ensure that only the top window sends the message by wrapping your sendRequest call with an if (window.top === window).
Could it be possible that you have another extension running that previews google results somehow? That may have this effect....

Resources