I've been trying to use observers for a while now, but it seems like I cannot get them to work. I put the observe function in different modules of my extension, and also different parts of my code. Registering it from everywhere but it seems like every way that I tried was just a deadened. I can't get it to work. My goal is to listen for when a user exits from Firefox so I can clear preferences objects of my addon. As of now my observer is in bootstrap.js, and this is how I implemented it. I implemented my observe function this way because it was mentioned in this stackoverflow post that this is the right way to observe for quit-application notification. On a side note observers registered gets printed in console log but "oh observing" doesn't.
function myObserver()
{
this.register();
}
myObserver.prototype = {
observe: function(subject, topic, data) {
console.log("oh observing!!");
if (topic == "app-startup" || topic == "profile-after-change") {
var observerService = Components.classes["#mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, "quit-application", false);
}
else if (topic == "quit-application-requested" || topic == "quit-application")
{
console.log("browser closing");
alert('hello');
myextension.Utils.prefService.clearUserPref("questionType");
myextension.Utils.prefService.clearUserPref("clickThrough");
this.unregister();
}
},
register: function() {
var observerService = Components.classes["#mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, "quit-application-requested",false);
observerService.addObserver(this, "quit-application",false);
observerService.addObserver(this, "app-startup", false);
observerService.addObserver(this, "profile-after-change", false);
console.log("observers registered");
},
unregister: function() {
var observerService = Components.classes["#mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.removeObserver(this, "quit-application-requested");
console.log("unregistering obs");
}
}
This is my startup function:
function startup(data, reason) {
Components.utils.import("chrome://ext/content/commons.jsm");
let wm = Cc["#mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator);
let windows = wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
WindowListener.setupBrowserUI(domWindow);
}
// Wait for any new browser windows to open
wm.addListener(WindowListener);
}
and this is my setupBrowserUI function in which I register the observer:
setupBrowserUI: function(domWindow) {
extension.onLoad(domWindow.gBrowser);
observer = new myObserver();
}
The easy way:
https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Services.jsm
let observer = {
observe: function() {
dump("oh observing")
}
};
function startup(data, reason)
{
Services.obs.addObserver(observer, "*", false);
observer.observe();
}
My suggest is create a log file with the preferences; to output dump calls instead to a file, set browser.dom.window.dump.file to the file destination where the log should be created and restart the application.
https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/Preference_reference/browser.dom.window.dump.file
Related
Is there a way to check if the service worker found an update before loading custom functions?
i have this function which is working, but it runs the custom functions twice, and seems very untidy..
I'm looking for a way to only run the custom functions once, and not when an update was found and installed. When an update is found, the user || the page will reload automatically and then the custom functions can run normally..
I added the reg.events in this function to determine where to place my custom functions. I hope this question is understandable..
function installApp(path, scope) {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(path, {
scope: scope
}).then((reg) => {
// event listener to catch the prompt if any and store in
// an instance for later use with add to homescreen() function.
getPrompt();
// this is a custom alert type notification
makeProgress('System','is ok');
/* THIS IS THE UPDATE FOUND FUNCTION */
reg.onupdatefound = function() {
var installingWorker = reg.installing;
installingWorker.onstatechange = function() {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
// the _clear() function removes items from the locaforage db to
// force the app to not auto login, but let the user
// login again to refresh any data when the page reloads
_clear('uuid');
_clear('user');
_clear('token');
makeProgress('new version','reload app');
} else {
// removes any custom notifications
clearProgress();
//just go into the app because everything is loaded.
//We dont need to reinstall the
//homescreen or listen for the homescreen because this
//is an update and the homescreen should already be installed?
enterApp();
}
break;
case 'redundant':
// removes any custom notifications cause
//the install is complete
clearProgress();
enterApp();
console.log('The installing service worker became redundant.');
break;
}
};
return;
};
/** Here is the events that fire during the install
// process and where i am currently stuck **/
if (reg.installing) {
makeProgress('updating','files');
/* THE SERVICE WORKER IS DOWNLOADING THE CACHE FROM THE SERVER */
} else if (reg.waiting) {
/* what message here ?*/
/* as far as i can tell, THE SERVICE WORKER IS WAITING FOR
*//*PREVIOUS SERVICE WORKER TO BEREFRESHED SO A RELOAD */
/*UI SHOULD COME HERE??*/
} else if (reg.active) {
/* what message here ?*/
/* IS THIS THE BEST PLACE TO RUN THE BELOW CUSTOM
*//*FUNCTIONS?? WILL //THEY ALWAYS FIRE */
}
/** AT WHICH OF THE EVENTS ABOVE WILL I ADD THE FUNCTIONS FROM HERE **/
requestWakeLock();
const browserFeatures = detectFeatures(reg);
setCompatibilityArray(browserFeatures);
localforage.ready().then(function() {
localforage.getItem('homescreen').then(function (value) {
if(value != 1){
if (platform == 'iPhone' || platform == 'iPad') {
installHome();
} else {
makeProgress('waiting', 'prompt');
waitPrompt();
}
return;
} else {
enterApp();
return;
}
}).catch(function (err) {
alertIt('something went wrong. Please refresh the page to try again. If the problem persists, try another browser.</br>', 'warning', 0);
return;
});
}).catch(function (err) {
alertIt('Something went wrong.<br>Please refresh the page to restart the installation process.<br>'+err, 'danger', 0);
return;
});
/** TO HERE, WITHOUT RUNNING THESE FUNCTION DURING*/
/*THE ONUPDATEFOUND EVENT AS THEN THEY WILL RUN TWICE**/
}, (err) => {
alertIt('Something went wrong.<br>Please refresh the page to restart the installation process.<br>', 'danger', 0);
})
} else {
alertIt('This browser is not compatible with this app.<br>Please try to use a different browser to install this application.<br>', 'danger', 0);
return;
}
}
I initialize this script like so:
window.addEventListener("load", () => {
makeProgress('Checking','system');
installApp(appsPath, appScope);
})
basically they must not be invoked if a new update is found..
I discovered that the onupdate function runs when old service worker is active..
If the onupdate function fires it changes a variable to a true value
I then used a time out function in the active event to see if a variable had changed... if it did change then i return false, and let the onupdate functions continue their course.. otherwise i continue to load my custom functions...Its working, but it doesn't seem like the best way.
Do you have a better method?
so like this:
function installApp(path, scope) {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(path, {
scope: scope
}).then((reg) => {
getPrompt();
makeProgress('refreshing','files');
var entApp = true;
reg.onupdatefound = function() {
entApp = false;
var installingWorker = reg.installing;
installingWorker.onstatechange = function() {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
_clear('uuid');
_clear('user');
_clear('token');
makeProgress('new version','reloading app');
setTimeout(function(){
location.reload();
}, 2500);
return;
} else {
/*NOT SURE WHAT IS SUPPOSED TO GO HERE, SO I JUST RELOADED THE PAGE*/
makeProgress('new version','reloading app');
setTimeout(function(){
location.reload();
}, 2500);
return;
}
break;
case 'redundant':
/*NOT SURE WHAT IS SUPPOSED TO GO HERE, SO I JUST RELOADED THE PAGE*/
makeProgress('new version','reloading app');
setTimeout(function(){
location.reload();
}, 2500);
return;
break;
}
};
return;
};
if (reg.active) {
/** RIGHT HERE IS WHERE THE ONUPDATE FIRES. I GAVE IT A
2.5 SECONDS TO DO ITS THING, THEN CHECKED TO SEE IF THERE WAS
AN UPDATE, IF NO UPDATE THEN I RUN MY CUSTOM FUNCTIONS, OTHERWISE
THE ONUPDATE FUNCTION RELOADS THE PAGE AND THE UPDATED SW.JS FILE
WILL THEN RUN THESE FUNCTIONS WHEN ITS ACTIVE.. IS THERE A BETTER
IN-BUILT METHOD TO DO THIS?**/
setTimeout(function(){
if(entApp === true){
requestWakeLock();
const browserFeatures = detectFeatures(reg);
setCompatibilityArray(browserFeatures);
localforage.ready().then(function() {
localforage.getItem('homescreen').then(function (value) {
if(value != 1){
if (platform == 'iPhone' || platform == 'iPad') {
installHome();
} else {
makeProgress('waiting', 'prompt');
waitPrompt();
}
return;
} else {
enterApp();
return;
}
}).catch(function (err) {
alertIt('something went wrong. Please refresh the page to try again. If the problem persists, try another browser.</br>', 'warning', 0);
return;
});
}).catch(function (err) {
alertIt('Something went wrong.<br>Please refresh the page to restart the installation process.<br>'+err, 'danger', 0);
return;
});
}
}, 2500);
}
i'm having difficulties trying to open up links in phonegap's (cordova's) inAppBrowser.
I need to be able to make it so the IAB can open both local phonegap links (file:///android_assets/www/example.html) and online links (https://www.google.com for instance)
I have achieved this so far by creating an object in index.js which creates an initiate IAB object, and then for every call from there onwards I was thinking that I could just open that link.
I've found a number of problems, for example when I try to use window.location = some_url it will change the index.html page to that some_url and hence my attached JS no longer works.
I've tried injecting a window.location from my index.html's JS into the IAB document however that breaks down when trying to fetch a local file
My current method is causing a memory leak because it's continuously opening IAB objects which I'm then having difficulties closing because ...
Using the .close() method on an IAB object shows (according to Chrome's Remote Devices view) that it doesn't actually delete the IAB window but rather turns it into an about:new tab
Heres my current code for the JS which is called in index.js (im sorry i know there's a lot of redundant code!)
var interceptor = {
// library dictionary object of <K,V> pairs in the form of <current page, desired page>
// insert your k,v pairs here:
library: {
'login/signup.php': 'example.html', //test data
'calendar/view.php': 'example.html',
'course/view.php?id=10': 'example.html'
},
// origin dictionary
// dictionary for redirecting from the phonegap page to the moodle page
// must be explicit in your page names and avoid duplication
// e.g. use full path notation -> full/path/to/file.html not file.html
origin: {
'current/attempt': ['example1.html', 'example2.html', 'course/index.php'] //test data
},
history_stack: [],
// stack of all current windows
browser_windows: [],
is_back: 'false',
windows: 0,
redirect_flag: false,
first: true,
currentWindow: null,
currentLocalFile: null,
get_origin: function() {
return this.origin;
},
get_library: function() {
return this.library;
},
// interceptor constructor
initialize: function(old_ref = null, default_url = config.moodleURL, android = true) {
// interval for checking our back flag
// the scope changes when you enter the anonymous closure and this changes. this is a hack for that
var self = this;
var ref;
console.log('default url: ' + default_url);
// check for android
if ((android) && (self.windows < 1)) {
// self.browser_windows[0] = cordova.InAppBrowser.open(default_url, '_blank', 'location=no,zoom=no');
ref = cordova.InAppBrowser.open(default_url, '_blank', 'location=yes,zoom=no');
self.first = false;
}
// otherwise iOS
else if ((!android) && (self.windows < 1)) {
// self.browser_windows[0] = cordova.InAppBrowser.open(default_url, '_blank', 'location=no,zoom=no,suppressesIncrementalRendering');
ref = cordova.InAppBrowser.open(default_url, '_blank', 'location=no,zoom=no,suppressesIncrementalRendering');
// self.browser_windows.push(ref);
self.first = false;
} else {
old_ref =
// old_ref = cordova.InAppBrowser.open(default_url, '_blank', 'location=yes,zoom=no');
ref = old_ref;
}
self.windows++;
var library_dictionary = this.get_library();
var origin_dictionary = this.get_origin();
var redirect_URL;
ref.addEventListener('loadstart', function(event) {
// push current page to histoy stack
self.history_stack.push(event.url);
// check to see if an element of one of our origin arrays is in the URL
for (var origin_list in origin_dictionary) {
if (event.url.includes(origin_list)) self.redirect_flag = true;
for (var elements in origin_dictionary[origin_list]) {
// if it is raise a flag and store the key that its array maps to
if ((event.url.includes(origin_dictionary[origin_list][elements])) && (self.redirect_flag)) {
redirect_URL = origin_list;
self.redirect_flag = false;
var temp_previous_pages = self.history_stack;
var temp_element;
// pop elements of stack until empty
while (temp_previous_pages.length !== 0) {
temp_element = temp_previous_pages.pop();
// if we find an element in the stack (our URL history) that matches the key that our array mapped to
if (temp_element.indexOf(redirect_URL) !== -1) {
// redirect and break from loop
self.initialize(ref, temp_element, android);
break;
}
}
}
}
}
for (var key in library_dictionary) {
if (event.url.includes(key)) {
self.initialize(ref, library_dictionary[key], android);
break;
}
}
});
ref.addEventListener('loadstop', function() {
// when we've finished loading our page set up an interval every 2.5 seconds to check
// if we've been signalled to change pages
console.log('here');
var is_back_interval = setInterval(function() {
ref.executeScript({
code: "localStorage.getItem('is_back')"
}, function(values) {
if (values[0] === 'true') {
// if we have been signalled then remove that signal from our html or moodle page
ref.executeScript({
code: "localStorage.setItem('is_back', '')"
});
// get 3rd last since the last will be the current page as will the 2nd last
prev_page = self.history_stack[self.history_stack.length - 3];
self.initialize(ref, prev_page, android);
}
});
}, 2500);
});
}
};
and heres index.js
var app = {
// Application Constructor
initialize: function() {
this.bindEvents();
},
// Bind Event Listeners
//
// Bind any events that are required on startup. Common events are:
// 'load', 'deviceready', 'offline', and 'online'.
bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false);
},
// deviceready Event Handler
//
// The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicitly call 'app.receivedEvent(...);'
onDeviceReady: function() {
app.receivedEvent('deviceready');
},
// Update DOM on a Received Event
receivedEvent: function(id) {
if (window.device.platform === "iOS"){
interceptor.initialize(config.moodleURL, false);
} else {
interceptor.initialize();
}
}
};
example.html: https://gist.github.com/anonymous/28a78ab0879878d7a9dac8eb89544cda
I have the following code in javascript:
function ConnectWebSocket() {
if ("WebSocket" in window) {
myWebsocket = new WebSocket("wss://myserver/mychannel");
myWebsocket.onmessage = function(evt) {
alert("onmessage");
}
myWebsocket.onopen = function() {
alert("onopen");
myWebsocket.send("msg0");
myWebsocket.send("msg1");
myWebsocket.send("msg2");
}
myWebsocket.onclose = function() {
alert("onclose");
ConnectWebSocket();
}
} else {
// Do something if there is no websockets support
}
}
ConnectWebSocket();
The problem is that in Firefox, the connection is closed after sending the messages, and reopened due to the command on the onclose event. If I try to send only one message on onopen, the connection keeps opened, but if I try to send more than one message, the connection shut down. This issue appears only in Firefox, not in Chrome, not in IE, not in Safari.
Can someone help me? In other browsers like IE or Chrome, once the connection is created, it keep opened until I leave the page. I have the 40.0.3v of Firefox
Try this example:
var url = "ws://echo.websocket.org";
if (!window.WebSocket) alert("WebSocket not supported by this browser");
var myWebSocket = {
connect: function () {
var location = url
this._ws = new WebSocket(location);
this._ws.onopen = this._onopen;
this._ws.onmessage = this._onmessage;
this._ws.onclose = this._onclose;
this._ws.onerror = this._onerror;
},
_onopen: function () {
console.debug("WebSocket Connected");
},
_onmessage: function (message) {
console.debug("Message Recieved: " + message.data);
},
_onclose: function () {
console.debug("WebSocket Closed");
kiosk.connect();
},
_onerror: function (e) {
console.debug("Error occured: " + e);
},
_send: function (message) {
console.debug("Message Send: " + message);
if (this._ws) this._ws.send(message);
}
};
myWebSocket.connect();
setInterval(function() {
myWebSocket._send('msg1');
}, 5000);
Here is a JSFidlle
It may be that your support var is not behaving as you expect. The following code works in FireFox without closing the connection:
function ConnectWebSocket() {
if ("WebSocket" in window) {
myWebsocket = new WebSocket("ws://echo.websocket.org/");
myWebsocket.onmessage = function (evt) {
alert("onmessage");
}
myWebsocket.onopen = function () {
alert("onopen");
myWebsocket.send("a test message");
}
myWebsocket.onclose = function () {
alert("onclose");
ConnectWebSocket();
}
} else {
// Do something if there is no websockets support
}
}
ConnectWebSocket();
Example Fiddle
You can use the tool on Websocket.org to make sure websockets are
working correctly in your browser.
Or (although your issue is with FF) you can use the steps listed
here to debug websockets.
Try it.
var WS = window.WebSocket || window.MozWebSocket;
if (WS){
var websocket = new WS("wss://myserver/mychannel");
}
I'm writing a firefox plugin and keeping track of each page's workers in an array. Apart from a bit of fancy footwork required to manage this array (as described here https://bugzilla.mozilla.org/show_bug.cgi?id=686035 and here Addon SDK - context-menu and page-mod workers) everything is working properly. One issue I'm having is that when listening to the tabs pageshow event (or the worker's own pageshow event for that matter), the callback seems to fire before the worker is actually ready. When retrieving the page's corresponding worker in the callback and using it to try to send a message to the content script, I'm receiving the error The page is currently hidden and can no longer be used until it is visible again. Normally, I'd just use a setTimeout and grit my teeth, but this isn't available for add-ons. What's a suitable workaround? The code for the main part of the add-on is below:
var { ToggleButton } = require('sdk/ui/button/toggle');
var panels = require('sdk/panel');
var tabs = require('sdk/tabs');
var self = require('sdk/self');
var pageMods = require('sdk/page-mod');
var ss = require('sdk/simple-storage');
var workers = [];
ss.storage.isPluginActive = ss.storage.isPluginActive || false;
var button = ToggleButton({
id: 'tomorrowww',
label: 'Tomorowww',
icon: {
'16': './icon-16.png',
'32': './icon-32.png',
'64': './icon-64.png'
},
onChange: handleButtonChange
});
var panel = panels.Panel({
contentURL: self.data.url('panel.html'),
contentScriptFile: self.data.url('panel-script.js'),
onHide: handlePanelHide,
width: 342,
height: 270
});
panel.port.on('panel-ready', handlePanelReady);
panel.port.on('plugin-toggled', handlePluginToggled);
panel.port.on('link-clicked', handleLinkClicked);
pageMods.PageMod({
include: ['*'],
contentScriptFile: [self.data.url('CancerDOMManager.js'), self.data.url('content-script.js')],
contentStyleFile: self.data.url('content-style.css'),
onAttach: function (worker) {
addWorker(worker);
sendActiveState(ss.storage.isPluginActive);
}
});
// move between tabs
tabs.on('activate', function () {
sendActiveState();
});
// this actually fires before the worker's pageshow event so isn't useful as the workers array will be out of sync
//tabs.on('pageshow', function () {
// sendActiveState();
//});
function addWorker (worker) {
if(workers.indexOf(worker) > -1) {
return;
}
worker.on('detach', handleWorkerDetach);
worker.on('pageshow', handleWorkerShown);
worker.on('pagehide', handleWorkerHidden);
workers.push(worker);
}
function handleWorkerDetach () {
removeWorker(this, true);
}
function handleWorkerShown () {
addWorker(this);
// back / forward page history
// trying to send the state here will trigger the page hidden error
sendActiveState();
}
function handleWorkerHidden () {
removeWorker(this);
}
function removeWorker (worker, removeEvents) {
var index = workers.indexOf(worker);
removeEvents = removeEvents || false;
if(index > -1) {
if(removeEvents) {
worker.removeListener('detach', handleWorkerDetach);
worker.removeListener('pageshow', handleWorkerShown);
worker.removeListener('pagehide', handleWorkerHidden);
}
workers.splice(index, 1);
}
}
function getWorkersForCurrentTab () {
var i;
var tabWorkers = [];
i = workers.length;
while(--i > -1) {
if(workers[i].tab.id === tabs.activeTab.id) {
tabWorkers.push(workers[i]);
}
}
return tabWorkers;
}
function handlePanelReady () {
setActive(ss.storage.isPluginActive);
}
function setActive (bool) {
ss.storage.isPluginActive = bool;
panel.port.emit('active-changed', bool);
sendActiveState();
}
function sendActiveState () {
var tabWorkers = getWorkersForCurrentTab();
var i = tabWorkers.length;
while(--i > -1) {
tabWorkers[i].port.emit('toggle-plugin', ss.storage.isPluginActive);
}
}
function handleButtonChange (state) {
if(state.checked) {
panel.show({
position: button
});
}
}
function handlePanelHide () {
button.state('window', {checked: false});
}
function handleLinkClicked (url) {
if(panel.isShowing) {
panel.hide();
}
tabs.open(url);
}
function handlePluginToggled (bool) {
if(panel.isShowing) {
panel.hide();
}
setActive(bool);
}
try using contentScriptWhen: "start" in the page-mod
I was dealing with a similar problem. I think I have it working the way I want now by putting the listener in the content script instead of the addon script. I listen for the event on the window, I then emit a message from my content script to my addon script, my addon script then sends a message back to the content script with the information needed from the addon script.
In my code, I am working on update the preferences in the content script to ensure that the tab always has the most up to date settings when they are changed, only the addon script can listen to the prefs change event.
This particular snippet will listen for when the page is navigated to from history (i.e., back or forward button), will inform the addon script, the addon script will get the most up to date preferences, and then send them back to a port listening in the content script.
Content script:
window.onpageshow = function(){
console.log("onpageshow event fired (content script)");
self.port.emit("triggerPrefChange", '');
};
Addon Script (e.g., main.js:
worker.port.on("triggerPrefChange", function() {
console.log("Received request to triggerPrefChange in the addon script");
worker.port.emit("setPrefs", prefSet.prefs);
});
Since the event is being fired from the DOM event, the page must be shown. I am not sure if listening to the pageshow event in the addon script is doing what we think.
I have an issue with capturing a newly created tab in chrome.
I create the new tab with chrome.tabs.create and pass the tabid to my callback function which captures it.
function createtab(url) {
chrome.tabs.create({'url': url,'active':false}, function(tab) {
captureWindowTab((tab.id);
});
}
function captureWindowTab(tabid) {
chrome.tabs.update(tabid, {}, function() {
chrome.tabs.captureVisibleTab(27, {format:"png"}, function(dataUrl) {
capturecallback(dataUrl);
});
});
}
function capturecallback(dataurl) {
console.log(dataurl);
}
It works ONLY when i do it on current existing tabs. i cannot get it to work on newly created tabs. always returns undefined.
I dont understand whats the issue.
Based on the documentation it seems you need the host permission (or all hosts permission) to be able to capture it. See the docuemtation at:
https://developer.chrome.com/extensions/tabs.html#method-captureVisibleTab
Do you have the host permission or all hosts permission set?
I think you might also need the "tabs" permission, if you don't already have it.
Resolved it like this:
chrome.tabs.onUpdated.addListener(function(tabid , info) {
//console.log('loading tab'+tabid);
if(info.status == "complete") {
chrome.tabs.get(tabid,function(tab) {
chrome.topSites.get(function(sites){
tab.url = NewTab.getHostFromUrl(tab.url);
console.log(tab.url);
//console.log('loaded '+tab.url);
for (var i = 8 - 1; i >= 0; i--) {
sites[i].url = NewTab.getHostFromUrl(sites[i].url);
//console.log('checking '+sites[i].url);
if(tab.url == sites[i].url && tab.url != 'newtab') {
chrome.tabs.update(tabid, {'highlighted':true,'active':true}, function(tab){
chrome.tabs.captureVisibleTab(chrome.windows.WINDOW_ID_CURRENT, {format:"jpeg","quality":30}, function(dataUrl) {
// add data URL to array
if(dataUrl) {
console.log('its a winner!');
window.localStorage['topsite_'+encodeURI(NewTab.getHostFromUrl(tab.url))+'_thumbnail'] = dataUrl;
NewTab.getTopSites();
}
});
});
}
};
});
});
}
});