Chrome extension detect Google search refresh - google-chrome-extension

How can my content script detect a refresh of Google's search?
I believe it is an AJAX reload of the page and not a "real" refresh, so my events won't detect the refresh.
Is it possible to detect it somehow in both a Google Chrome extension and a Firefox WebExtensions add-on?

Google search is a dynamically updated page. Several well-known methods exist to detect an update: MutationObserver, timer-based approach (see waitForKeyElements wrapper), and an event used by the site like pjax:end on GitHub.
Luckily, Google Search in Chrome browser uses message event, so here's our content script:
window.addEventListener('message', function onMessage(e) {
// console.log(e);
if (typeof e.data === 'object' && e.data.type === 'sr') {
onSearchUpdated();
}
});
function onSearchUpdated() {
document.getElementById('resultStats').style.backgroundColor = 'yellow';
}
This method relies on an undocumented site feature which doesn't work in Firefox, for example.
A more reliable crossbrowser method available to Chrome extensions and WebExtensions is to monitor page url changes because Google Search results page always updates its URL hash part. We'll need a background/event page, chrome.tabs.onUpdated listener, and messaging:
background.js
var rxGoogleSearch = /^https?:\/\/(www\.)?google\.(com|\w\w(\.\w\w)?)\/.*?[?#&]q=/;
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (rxGoogleSearch.test(changeInfo.url)) {
chrome.tabs.sendMessage(tabId, 'url-update');
}
});
content.js
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg === 'url-update') {
onSearchUpdated();
}
});
function onSearchUpdated() {
document.getElementById('resultStats').style.backgroundColor = 'yellow';
}
manifest.json: background/event page and content script declarations, "tabs" permission.

Related

chrome.tabs.onupdated.addlistener doesn't work on cached pages

chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (tab.url.indexOf('http') == '0' && changeInfo.status == 'complete') {
alert(JSON.stringify(changeInfo));
}
});
i have such background.js it nearly works always %99 but not %100
whenever i restart my browser and enter an already accessed web page when browser loads all contents from cache it doesn't fire this chrome.tabs event
but the interesting part is when i click same website from dial speed it doesn't load from cache and calls the "tabs." action
According to the docs https://developer.chrome.com/extensions/webNavigation
Not all navigating tabs correspond to actual tabs in Chrome's UI,
e.g., a tab that is being pre-rendered. Such tabs are not accessible
via the tabs API nor can you request information about them via
webNavigation.getFrame or webNavigation.getAllFrames. Once such a tab
is swapped in, an onTabReplaced event is fired and they become
accessible via these APIs.
and
If a navigation was triggered via Chrome Instant or Instant Pages, a
completely loaded page is swapped into the current tab. In that case,
an onTabReplaced event is fired.
Try using the chrome.webNavigation.onTabReplaced event to catch this:
chrome.webNavigation.onTabReplaced.addListener(function (details) {
chrome.tabs.get(details.tabId, function(tab) {
console.log(tab.url);
});
});
Note: You'll need the "webNavigation" permission in the chrome extension manifest:
{
"name": "My extension",
...
"permissions": [
"webNavigation"
],
...
}
I tested this with 25 open tabs using:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){
console.log("update: "+JSON.stringify(changeInfo));
});
On restarting my browser I received 25 "loading" events, but only 23 "completes".
On restarting my browser a second time I received 25 "loading" events, but only 22 "completes".
I don't think changeInfo.status == 'complete' will always follow a pageload, especially after restarting.
consider using chrome.tabs.query() right after your extension loads instead.
chrome.tabs.query({}, function(tabs) {
for(var i=0;i<tabs.length;i++){
if (tabs[i].url.indexOf('http') == '0') {
dostuff();
}
}
});

Firefox Extension Development

In Chrome Extension Development we have Background Page Concepts. Is any thing similar available in Firefox Extension Development also. While Developing Chrome Extensions I have seen methods like
window.Bkg = chrome.extension.getBackgroundPage().Bkg;
$(function () {
var view = null;
if (Bkg.account.isLoggedIn()) {
view = new Views.Popup();
$("#content").append(view.render().el);
} else {
$("#content").append(Template('logged_out')());
Bkg.refresh();
}
}...........
Where the main logic are written in Background Page(like isLoggedIn etc) and from the Extension Popup page we are calling Background page. Here for instance the background page is always loaded which manages the session. How can we have similar functionality in Firefox Extension Development.
All communication between the background page (main.js) and content scripts (your popup script) occurs via events. You cannot call functions immediately, so you won't receive any return values, but you can send an event from a content script to the background script that sends an event back to the content script and calls a new function, like so:
main.js partial
// See docs below on how to create a panel/popup
panel.port.on('loggedIn', function(message) {
panel.port.emit('isLoggedIn', someBoolean);
});
panel.port.on('refresh', function() {
// do something to refresh the view
});
popup.js
var view = null;
addon.port.on('isLoggedIn', function(someBool) {
if (someBool) {
// Obviously not code that's going to work in FF, just want you to know how to structure
view = new Views.Popup();
$("#content").append(view.render().el);
} else {
$("#content").append(Template('logged_out')());
addon.port.emit('refresh');
}
});
addon.port.emit('loggedIn', 'This is a message. I can pass any var along with the event, but I don't have to');
You should read this stuff:
Panel
Communicating between scripts

Content scripts not trapping chrome.tabs.onUpdate

I am developing a Google Chrome Extension. I want to get the contents(text only) of the active tab. I am sure that Content scripts will help me find what I am looking for.
But I am stuck in the below scenario:
If I run my chrome.tabs.onUpdated.addListener(function(){ in my background.js, I am able to achieve other tasks, say ajax post etc. But if I include document.body.innerHTML in that chrome.tabs.onUpdated.addListener(function(){, to get the contents of the opened tab, it is alerting the contents of my background.html and NOT the contents of the tab which is opened in the browser.
If I paste the chrome.tabs.onUpdated.addListener(function(){ in my content scripts, I am not able to trap the tab update.
Can I know where I am going wrong?
Try like this:
// In background.js
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (changeInfo.status === 'complete') {
chrome.tabs.sendMessage(tabId, {type: 'getDoc'}, function (doc) {
console.log(doc);
});
}
});
// In content_scripts.js
chrome.extension.onMessage.addListener(function(request, sender, response) {
if (request.type === 'getDoc') {
response(document.body.innerHTML);
}
return true;
});

How to implement Chrome extension 's chrome.tabs.sendMessage API in Firefox addon

I'm working on a Firefox addon development with Addon-Builder. I have no idea about how to implement Chrome extension 's chrome.tabs.sendMessage API in Firefox addon. The code is like this (the code is in the background.js, something like main.js in the Firefox addon):
function sendMessageToTabs(message, callbackFunc){
chrome.tabs.query({}, function(tabsArray){
for(var i=0; i<tabsArray.length; i++){
//console.log("Tab id: "+tabsArray[i].id);
chrome.tabs.sendMessage(tabsArray[i].id,message,callbackFunc);
}
});
}
So, How can I achieve this?
In add-ons build using the Add-on SDK, content scripts are managed by main.js. There's no built-in way to access all of your add-on's content scripts. To send a message to all tabs, you need to manually keep track of the content scripts.
One-way messages are easily implemented by the existing APIs. Callbacks are not built-in, though.
My browser-action SDK library contains a module called "messaging", which implements the Chrome messaging API. In the following example, the content script and the main script use an object called "extension". This object exposes the onMessage and sendMessage methods, modelled after the Chrome extension messaging APIs.
The following example adds a content script to every page on Stack Overflow, and upon click, the titles of the tabs are logged to the console (the one opened using Ctrl + Shift + J).
lib/main.js
// https://github.com/Rob--W/browser-action-jplib/blob/master/lib/messaging.js
const { createMessageChannel, messageContentScriptFile } = require('messaging');
const { PageMod } = require('sdk/page-mod');
const { data } = require('sdk/self');
// Adds the message API to every page within the add-on
var ports = [];
var pagemod = PageMod({
include: ['http://stackoverflow.com/*'],
contentScriptWhen: 'start',
contentScriptFile: [messageContentScriptFile, data.url('contentscript.js')],
contentScriptOptions: {
channelName: 'whatever you want',
endAtPage: false
},
onAttach: function(worker) {
var extension = createMessageChannel(pagemod.contentScriptOptions, worker.port);
ports.push(extension);
worker.on('detach', function() {
// Remove port from list of workers when the content script is deactivated.
var index = ports.indexOf(extension);
if (index !== -1) ports.splice(index, 1);
});
}
});
function sendMessageToTabs(message, callbackFunc) {
for (var i=0; i<ports.length; i++) {
ports[i].sendMessage(message, callbackFunc);
}
}
// Since we've included the browser-action module, we can use it in the demo
var badge = require('browserAction').BrowserAction({
default_title: 'Click to send a message to all tabs on Stack Overflow'
});
badge.onClicked.addListener(function() {
sendMessageToTabs('gimme title', function(response) {
// Use console.error to make sure that the log is visible in the console.
console.error(response);
});
});
For the record, the interesting part of main.js is inside the onAttach event.
data/contentscript.js
extension.onMessage.addListener(function(message, sender, sendResponse) {
if (message === 'gimme title') {
sendResponse(document.title);
}
});

Message isn't passed between background.html and popup.html

I'm trying to pass data that is saved in sessionStorage from background.html to popup.html
background.html:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
data = sessionStorage.getItem(request.tabId);
alert(data);
sendResponse({ data: data });
});
and in popup.html:
chrome.tabs.getSelected(null, function(tab) {
chrome.extension.sendRequest({ tabId: tab.id }, function(response) {
alert(response.data);
});
});
The popup is opened by a pageAction button, when I click the button I get an alert box with "null" on the popup and then an alert box with the data that I stored in sessionStorage on the background!
Any ideas how to fix this?
You don't need to use message/request APIs. I think this response may help you.
You also don't need sessionStorage, just store your data in a global variable of the background page. It will persist until the browser is closed or until the extension is restarted.
So, here is how I would rewrite your code:
background.html:
var data = {}; // Object storing data indexed by tab id
and in popup.html:
chrome.tabs.getSelected(null, function(tab) {
alert(chrome.extension.getBackgroundPage().data[tab.id]);
});
Note that chrome.tabs.getSelected is deprecated since Chrome 16, so popup code should be:
chrome.windows.getCurrent(function(win) {
chrome.tabs.query({'windowId': win.id, 'active': true}, function(tabArray) {
alert(chrome.extension.getBackgroundPage().data[tabArray[0].id]);
});
});
Well, I've done something dumb.
I inspected the background page by opening chrome-extension://[extension-id]/background.html in a tab instead of clicking on "inspect active views: background.html" in the extensions management page. This caused the tab to catch the request and call sendResponse, but the popup expected the REAL background page to call sendResponse (and if I understand Google's documentation regarding message passing, the fact that sendResponse was called twice is root of the problem, because the first call clears the request object)

Resources