I'm using a bookmarklet to inject javascript into a webpage. I am trying to login into my gmail account(that part works) and in my gmail account automatically click Sent folder as the page loads. This is the starting page:
This is the code I am using in bookmarklet:
javascript:
document.getElementById('Email').value='myEmail#gmail.com';
document.getElementById('next').click();
setTimeout(function(){
document.getElementById('Passwd').value='myPassword';
document.getElementById('signIn').click();},1000);
setTimeout(function(){
document.getElementsByClassName("J-Ke n0 aBU")[0].click();
},6000);
J-Ke n0 aBU is the class of Sent folder. This code logins into my account, but it doesn't click Sent folder.
I noticed similar behavior on other websites; whenever a new page loads or refreshes, the bookmarklet stops working.
Why is that and what is the correct way of using the same bookmarklet on different page than it was originally clicked.
Disclaimer: I don't have gmail, so I didn't test this for gmail specifically.
This answer exists to address your comment:
What about iframes. Is theoretically possible to use gmail login in an iframe and therefore when the iframe changes to another page this doesnt have effect on the bookmarklet?
Yes, it is technically possible to have a persistent bookmarklet using iframes (or, deity forbid, a frameset).
As long as your parent window (and it's containing iframe) remain on the same domain, it should work according to cross-domain spec.
It is however possible (depending on used method) to (un-)intentionally 'counter-act' this (which, depending on used counter-action, can still be circumvented, etc..).
Navigate to website, then execute bookmarklet which:
Creates iframe.
Sets onload-handler to iframe.
Replaces current web-page content with iframe (to window's full width and height).
Set iframe's source to current url (reloading the currently open page in your injected iframe).
Then the iframe's onload-handler's job is to detect (using url/title/page-content) what page is loaded and which (if any) actions should be taken.
Example (minify (strip comments and unneeded whitespace) using Dean Edward's Packer v3):
javascript:(function(P){
var D=document
, B=D.createElement('body')
, F=D.createElement('iframe')
; //end vars
F.onload=function(){
var w=this.contentWindow //frame window
, d=w.document //frame window document
; //end vars
//BONUS: update address-bar and title.
//Use location.href instead of document.URL to include hash in FF, see https://stackoverflow.com/questions/1034621/get-current-url-in-web-browser
history.replaceState({}, D.title=d.title, w.location.href );
P(w, d); //execute handler
};
D.body.parentNode.replaceChild(B, D.body); //replace body with empty body
B.parentNode.style.cssText= B.style.cssText= (
F.style.cssText= 'width:100%;height:100%;margin:0;padding:0;border:0;'
) + 'overflow:hidden;' ; //set styles for html, body and iframe
//B.appendChild(F).src=D.URL; //doesn't work in FF if parent url === iframe url
//B.appendChild(F).setAttribute('src', D.URL); //doesn't work in FF if parent url === iframe url
B.appendChild(F).contentWindow.location.replace(D.URL); //works in FF
}(function(W, D){ //payload function. W=frame window, D=frame window document
alert('loaded');
// perform tests on D.title, W.location.href, page content, etc.
// and perform tasks accordingly
}));
Note: one of the obvious methods to minify further is to utilize bracket-access with string-variables for things like createElement, contentWindow, etc.
Here is an example function-body for the payload-function (from above bookmarklet) to be used on http://www.w3schools.com (sorry, I couldn't quickly think of another target):
var tmp;
if(D.title==='W3Schools Online Web Tutorials'){
//scroll colorpicker into view and click it after 1 sec
tmp=D.getElementById('main').getElementsByTagName('img')[0].parentNode;
tmp.focus();
tmp.scrollIntoView();
W.setTimeout(function(){tmp.click()},1000);
return;
}
if(D.title==='HTML Color Picker'){
//type color in input and click update color button 'ok'
tmp=D.getElementById('entercolorDIV');
tmp.scrollIntoView();
tmp.querySelector('input').value='yellow';
tmp.querySelector('button').click();
//click 5 colors with 3 sec interval
tmp=D.getElementsByTagName('area');
tmp[0].parentNode.parentNode.scrollIntoView();
W.setTimeout(function(){tmp[120].click()},3000);
W.setTimeout(function(){tmp[48].click()},6000);
W.setTimeout(function(){tmp[92].click()},9000);
W.setTimeout(function(){tmp[31].click()},12000);
W.setTimeout(function(){tmp[126].click()},15000);
return;
}
above example (inside bookmarklet) minified:
javascript:(function(P){var D=document,B=D.createElement('body'),F=D.createElement('iframe');F.onload=function(){var w=this.contentWindow,d=w.document;history.replaceState({},D.title=d.title,w.location.href);P(w,d)};D.body.parentNode.replaceChild(B,D.body);B.parentNode.style.cssText=B.style.cssText=(F.style.cssText='width:100%;height:100%;margin:0;padding:0;border:0;')+'overflow:hidden;';B.appendChild(F).contentWindow.location.replace(D.URL)}(function(W,D){var tmp;if(D.title==='W3Schools Online Web Tutorials'){tmp=D.getElementById('main').getElementsByTagName('img')[0].parentNode;tmp.focus();tmp.scrollIntoView();W.setTimeout(function(){tmp.click()},1000);return}if(D.title==='HTML Color Picker'){tmp=D.getElementById('entercolorDIV');tmp.scrollIntoView();tmp.querySelector('input').value='yellow';tmp.querySelector('button').click();tmp=D.getElementsByTagName('area');tmp[0].parentNode.parentNode.scrollIntoView();W.setTimeout(function(){tmp[120].click()},3000);W.setTimeout(function(){tmp[48].click()},6000);W.setTimeout(function(){tmp[92].click()},9000);W.setTimeout(function(){tmp[31].click()},12000);W.setTimeout(function(){tmp[126].click()},15000);return}}));
Hope this helps (you get started)!
As JavaScript is executed in the context of the current page only, it's not possible to execute JavaScript which spans over more than one page. So whenever a second page is loaded, execution of the JavaScript of the first page get's halted.
If it would be possible to execute JavaScript on two pages, an attacker could send you to another page, read your personal information there and send it to another server in his control with AJAX (e.g. your mails).
A solution for your issue would be to use Selenium IDE for Firefox (direct link to the extension). Originally designed for automated testing, it can also be used to automate your browser.
Related
I am trying to create a bookmarklet to open a web page, populate credentials, and click login in one shot. Here is the needed bookmarklets:
For opening a webpage:
javascript:location.href='http://www.unt.edu'
For Credentials & login:
javascript:(function(){var d=document;s=d.querySelector;s.call(d,'input[name*=email]').value='YOUREMAIL#company.com'; s.call(d,'input[name*=pass]').value='SECRETPASSWORDHERE';s.call(d,'button[id*=login],input[type=button][id*=login],.btn-login').click(); }())
Is it possible to combine both of them considering the asynchronous behavior of opening the web page?
You can combine this in one bookmarklet, but you'll have to click it twice. The first click will open a website, and the next one will do the login.
javascript: (() => {
const url = 'https://stackoverflow.com/users/login';
if (location.href !== url) return (location.href = url);
document.querySelector('input#email').value = 'EMAIL';
document.querySelector('input#password').value = 'PASSWORD';
document.querySelector('button#submit-button').click();
})();
Consider using a userscript instead, which will run automatically on pageload. Set the userscript's #includes to run on http://www.unt.edu, then set the userscript's JS to the content of your current bookmarklet:
var d=document;s=d.querySelector;s.call(d,'input[name*=email]').value='YOUREMAIL#company.com'; s.call(d,'input[name*=pass]').value='SECRETPASSWORDHERE';s.call(d,'button[id*=login],input[type=button][id*=login],.btn-login').click(); }()
(though I'd recommend separating it out onto separate lines for readability and maintainability)
And then use a plain bookmark (not a bookmarklet) to http://www.unt.edu. This way, whenever you click on the bookmark, the page will load, and when the page loads, the userscript that logs you in will automatically run.
I have recently converted a GreaseMonkey script of mine into a WebExtension, just to get a first impression of the process. Now I have a reached a point where it would be nice to do some clean-up or simply undo all my changes when said extension is disabled/uninstalled.
From what I've seen on Mozilla's pages, runtime.onSuspend should do the trick. Unfortunately, it looks like that's not yet implemented (I'm on the regular Firefox release channel).
In other words, what I want to do is run code as a result of the user removing/disabling my extension so that I can clean-up listeners and such and generally restore the tabs to their status quo, i. e., undo all the changes the extension made.
The other answer is incorrect. The first part (about the onSuspend event) is factually incorrect. The part about setUninstallURL is relevant, but does not answer the question since it does not allow you to restore tabs to their original state (as you asked in the question).
In this answer I will first clear the misconception about runtime.onSuspend, and then explain how you can run code for a content script when an extension is disabled.
About runtime.onSuspend
The chrome.runtime.onSuspend and chrome.runtime.onSuspendCanceled events have nothing to do with a disabled/uninstalled extension. The events are defined for event pages, which are basically background pages that are suspended (unloaded) after a period of inactivity. When the event page is about to unload due to suspension, runtime.onSuspend is called. If an extension API is called during this event (e.g. sending an extension message), the suspension will be canceled and trigger the onSuspendCanceled event.
When an extension is unloading because of a browser shutdown or an uninstallation, the lifetime of the extension cannot be extended. Thus you cannot rely on these events to run asynchronous tasks (such as cleaning up tabs from the background page).
Furthermore, these events are not available in content scripts (only extension pages such as background pages), so these cannot be used to synchronously clean up content script logic.
From the above it should be obvious that runtime.onSuspend is not remotely relevant for the goal of clean-up upon disable. Not in Chrome, let alone Firefox (Firefox does not support event pages, these events would be meaningless).
Running code in tabs/content scripts upon extension disable/uninstall
A common pattern in Chrome extensions is to use the port.onDisconnect event to detect that the background page has unloaded, and use that to infer that the extension might have unloaded (combined with option 1 of this method for a higher accuracy). Chrome's content scripts are kept around after an extension is disabled, so this can be used to run asynchronous clean-up code.
This is not possible in Firefox, because the execution context of a content script is destroyed when a Firefox extension is disabled, before the port.onDisconnect event has a chance to fire (at least, until bugzil.la/1223425 is fixed).
Despite these constraints, it is still possible to run clean up logic for a content script when an add-on is disabled. This method is based on the fact that in Firefox, style sheets inserted with tabs.insertCSS are removed when an add-on is disabled.
I will discuss two ways to exploit this characteristic. The first method allows execution of arbitrary code. The second method does not provide execution of arbitrary code, but it is simpler and sufficient if you only want to hide some extension-inserted DOM elements.
Method 1: Run code in page when extension is disabled
One of the ways to observe style changes is by declaring CSS transitions and using transition events to detect CSS property changes.
For this to be helpful, you need to construct a style sheet in such a way that it only affects your HTML elements. So you need to generate a unique selector (class name, ID, ...) and use that for your HTML element(s) and style sheet.
This is code that you have to put in your background script:
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if (message !== 'getStyleCanary') return;
// Generate a random class name, insert a style sheet and send
// the class back to the caller if successful.
var CANARY_CLASS = '_' + crypto.getRandomValues(new Uint32Array(2)).join('');
var code = '.' + CANARY_CLASS + ' { opacity: 0 !important; }';
chrome.tabs.insertCSS(sender.tab.id, {
code,
frameId: sender.frameId,
runAt: 'document_start',
}, function() {
if (chrome.runtime.lastError) {
// Failed to inject. Frame unloaded?
sendResponse();
} else {
sendResponse(CANARY_CLASS);
}
});
return true; // We will asynchronously call sendResponse.
});
In the content script:
chrome.runtime.sendMessage('getStyleCanary', function(CANARY_CLASS) {
if (!CANARY_CLASS) {
// Background was unable to insert a style sheet.
// NOTE: Consider retry sending the message in case
// the background page was not ready yet.
return;
}
var s = document.createElement('script');
s.src = chrome.runtime.getURL('canaryscript.js');
s.onload = s.remove;
s.dataset.canaryClass = CANARY_CLASS;
// This function will become available to the page and be used
// by canaryscript.js. NOTE: exportFunction is Firefox-only.
exportFunction(function() {}, s, {defineAs: 'checkCanary'});
(document.body || document.documentElement).appendChild(s);
});
I use a script tag above, because it is the only way to run a script in the page without being blocked by the page's content security policy. Make sure that you add canaryscript.js to web_accessible_resources in manifest.json, or else the script won't load.
If running the cleanup code is not critical (e.g. because you also use method 2 which I explain later), then you should preferably use inline scripts instead of external scripts (i.e. use s.textContent = '<content of canaryscript.js>' instead of s.src = ...). This is because using .src with extension resources introduces a fingerprinting vulnerability to Firefox (bug 1372288).
This is the content of canaryscript.js:
(function() {
// Thes two properties are set in the content script.
var checkCanary = document.currentScript.checkCanary;
var CANARY_CLASS = document.currentScript.dataset.canaryClass;
var canary = document.createElement('span');
canary.className = CANARY_CLASS;
// The inserted style sheet has opacity:0. Upon removal a transition occurs.
canary.style.opacity = '1';
canary.style.transitionProperty = 'opacity';
// Wait a short while to make sure that the content script destruction
// finishes before the style sheet is removed.
canary.style.transitionDelay = '100ms';
canary.style.transitionDuration = '1ms';
canary.addEventListener('transitionstart', function() {
// To avoid inadvertently running clean-up logic when the event
// is triggered by other means, check whether the content script
// was really destroyed.
try {
// checkCanary will throw if the content script was destroyed.
checkCanary();
// If we got here, the content script is still valid.
return;
} catch (e) {
}
canary.remove();
// TODO: Put the rest of your clean up code here.
});
(document.body || document.documentElement).appendChild(canary);
})();
Note: CSS transition events are only fired if the tab is active. If the tab is inactive, the transition event will not fire until the tab is shown.
Note: exportFunction is a Firefox-only extension method to define a function in a different execution context (in the above example, the function was defined in the page's context, available to scripts running in that page).
All other APIs are available in other browsers too (Chrome/Opera/Edge), but the code cannot be used to detect disabled extensions, because style sheets from tabs.insertCSS are not removed upin uninstall (I only tested with Chrome; it might work in Edge).
Method 2: Visual restoration upon uninstallation
Method 1 allows you to run arbitrary code, such as removing all elements that you inserted in the page. As an alternative to removing the elements from the DOM, you can also choose to hide the elements through CSS.
Below I show how you can modify method 1 to hide the elements without running other code (such as canaryscript.js).
When your content script creates an element for insertion in the DOM, you hide it with an inline style:
var someUI = document.createElement('div');
someUI.style.display = 'none'; // <-- Hidden
// CANARY_CLASS is the random class (prefix) from the background page.
someUI.classList.add(CANARY_CLASS + 'block');
// ... other custom logic, and add to document.
In the style sheet that you add with tabs.insertCSS, you then define the desired display value, with the !important flag so that the inline style is overridden:
// Put this snippet after "var code = '.' + CANARY_CLASS, above.
code += '.' + CANARY_CLASS + 'block {display: block !important;}';
The above example is intentionally generic. If you have multiple UI elements with different CSS display values (e.g. block, inline, ...), then you can add multiple of these lines to re-use the framework that I provided.
To show the simplicity of method 2 over method 1: you can use the same background script (with the above modification), and use the following in the content script:
// Example: Some UI in the content script that you want to clean up.
var someUI = document.createElement('div');
someUI.textContent = 'Example: This is a test';
document.body.appendChild(someUI);
// Clean-up is optional and a best-effort attempt.
chrome.runtime.sendMessage('getStyleCanary', function(CANARY_CLASS) {
if (!CANARY_CLASS) {
// Background was unable to insert a style sheet.
// Do not add clean-up classes.
return;
}
someUI.classList.add(CANARY_CLASS + 'block');
someUI.style.display = 'none';
});
If your extension has more than one element, consider caching the value of CANARY_CLASS in a local variable so that you only insert one new style sheet per execution context.
Your initial wording was somewhat unclear as to exactly what you desire. Thus, this answer also contains information on one way you could receive a notification of the uninstall, under some conditions.
Run code in your WebExtension add-on prior to uninstall/disable:
No, even if it was supported, the runtime.onSuspend event would not do what you want. It's used to signal Event Pages that they are about to be unloaded. Even Pages are unloaded routinely when the handling of the events they are listening for has completed. It is not an indication that the extension is being uninstalled.
"Determine" that your "WebExtension was disabled/uninstalled":
If your question is really what you state in the last line of your question: "... is there a way to determine whether a WebExtension was disabled/uninstalled?" Then, it looks like you could use runtime.setUninstallURL(), which was implemented as of Firefox 47. This will allow you to set a URL to visit when the add-on is uninstalled. This could be used on your server to note that the add-on was uninstalled. It does not inform your WebExtension that it was uninstalled, nor permit you to run code in your WebExtension when that happens.
Unfortunately, you can not use detecting, in your WebExtension, that this URL was visited as indicating your WebExtension is being uninstalled/disabled. Based on testing, this URL is visited after the WebExtension has been completely uninstalled. In addition, it is not visited upon the WebExtension being disabled, nor when uninstalled after being disabled. It is only visited when the WebExtension is uninstalled while the add-on is enabled. From the fact that this is a JavaScript call which is only run when the extension is enabled, one would expect that the page would only be opened when leaving the enabled state.
Testing was done by adding the following line to a WebExtension and seeing when the page was opened:
chrome.runtime.setUninstallURL("http://www.google.com");
Given how this actually functions (only visited if the WebExtension is enabled and directly uninstalled), using this as "a way to determine whether a WebExtension was disabled/uninstalled" will only be partially effective. As should be clear, you will not be notified by a visit to this URL if the add-on is disabled prior to being uninstalled.
I have recently converted a GreaseMonkey script of mine into a WebExtension, just to get a first impression of the process. Now I have a reached a point where it would be nice to do some clean-up or simply undo all my changes when said extension is disabled/uninstalled.
From what I've seen on Mozilla's pages, runtime.onSuspend should do the trick. Unfortunately, it looks like that's not yet implemented (I'm on the regular Firefox release channel).
In other words, what I want to do is run code as a result of the user removing/disabling my extension so that I can clean-up listeners and such and generally restore the tabs to their status quo, i. e., undo all the changes the extension made.
The other answer is incorrect. The first part (about the onSuspend event) is factually incorrect. The part about setUninstallURL is relevant, but does not answer the question since it does not allow you to restore tabs to their original state (as you asked in the question).
In this answer I will first clear the misconception about runtime.onSuspend, and then explain how you can run code for a content script when an extension is disabled.
About runtime.onSuspend
The chrome.runtime.onSuspend and chrome.runtime.onSuspendCanceled events have nothing to do with a disabled/uninstalled extension. The events are defined for event pages, which are basically background pages that are suspended (unloaded) after a period of inactivity. When the event page is about to unload due to suspension, runtime.onSuspend is called. If an extension API is called during this event (e.g. sending an extension message), the suspension will be canceled and trigger the onSuspendCanceled event.
When an extension is unloading because of a browser shutdown or an uninstallation, the lifetime of the extension cannot be extended. Thus you cannot rely on these events to run asynchronous tasks (such as cleaning up tabs from the background page).
Furthermore, these events are not available in content scripts (only extension pages such as background pages), so these cannot be used to synchronously clean up content script logic.
From the above it should be obvious that runtime.onSuspend is not remotely relevant for the goal of clean-up upon disable. Not in Chrome, let alone Firefox (Firefox does not support event pages, these events would be meaningless).
Running code in tabs/content scripts upon extension disable/uninstall
A common pattern in Chrome extensions is to use the port.onDisconnect event to detect that the background page has unloaded, and use that to infer that the extension might have unloaded (combined with option 1 of this method for a higher accuracy). Chrome's content scripts are kept around after an extension is disabled, so this can be used to run asynchronous clean-up code.
This is not possible in Firefox, because the execution context of a content script is destroyed when a Firefox extension is disabled, before the port.onDisconnect event has a chance to fire (at least, until bugzil.la/1223425 is fixed).
Despite these constraints, it is still possible to run clean up logic for a content script when an add-on is disabled. This method is based on the fact that in Firefox, style sheets inserted with tabs.insertCSS are removed when an add-on is disabled.
I will discuss two ways to exploit this characteristic. The first method allows execution of arbitrary code. The second method does not provide execution of arbitrary code, but it is simpler and sufficient if you only want to hide some extension-inserted DOM elements.
Method 1: Run code in page when extension is disabled
One of the ways to observe style changes is by declaring CSS transitions and using transition events to detect CSS property changes.
For this to be helpful, you need to construct a style sheet in such a way that it only affects your HTML elements. So you need to generate a unique selector (class name, ID, ...) and use that for your HTML element(s) and style sheet.
This is code that you have to put in your background script:
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if (message !== 'getStyleCanary') return;
// Generate a random class name, insert a style sheet and send
// the class back to the caller if successful.
var CANARY_CLASS = '_' + crypto.getRandomValues(new Uint32Array(2)).join('');
var code = '.' + CANARY_CLASS + ' { opacity: 0 !important; }';
chrome.tabs.insertCSS(sender.tab.id, {
code,
frameId: sender.frameId,
runAt: 'document_start',
}, function() {
if (chrome.runtime.lastError) {
// Failed to inject. Frame unloaded?
sendResponse();
} else {
sendResponse(CANARY_CLASS);
}
});
return true; // We will asynchronously call sendResponse.
});
In the content script:
chrome.runtime.sendMessage('getStyleCanary', function(CANARY_CLASS) {
if (!CANARY_CLASS) {
// Background was unable to insert a style sheet.
// NOTE: Consider retry sending the message in case
// the background page was not ready yet.
return;
}
var s = document.createElement('script');
s.src = chrome.runtime.getURL('canaryscript.js');
s.onload = s.remove;
s.dataset.canaryClass = CANARY_CLASS;
// This function will become available to the page and be used
// by canaryscript.js. NOTE: exportFunction is Firefox-only.
exportFunction(function() {}, s, {defineAs: 'checkCanary'});
(document.body || document.documentElement).appendChild(s);
});
I use a script tag above, because it is the only way to run a script in the page without being blocked by the page's content security policy. Make sure that you add canaryscript.js to web_accessible_resources in manifest.json, or else the script won't load.
If running the cleanup code is not critical (e.g. because you also use method 2 which I explain later), then you should preferably use inline scripts instead of external scripts (i.e. use s.textContent = '<content of canaryscript.js>' instead of s.src = ...). This is because using .src with extension resources introduces a fingerprinting vulnerability to Firefox (bug 1372288).
This is the content of canaryscript.js:
(function() {
// Thes two properties are set in the content script.
var checkCanary = document.currentScript.checkCanary;
var CANARY_CLASS = document.currentScript.dataset.canaryClass;
var canary = document.createElement('span');
canary.className = CANARY_CLASS;
// The inserted style sheet has opacity:0. Upon removal a transition occurs.
canary.style.opacity = '1';
canary.style.transitionProperty = 'opacity';
// Wait a short while to make sure that the content script destruction
// finishes before the style sheet is removed.
canary.style.transitionDelay = '100ms';
canary.style.transitionDuration = '1ms';
canary.addEventListener('transitionstart', function() {
// To avoid inadvertently running clean-up logic when the event
// is triggered by other means, check whether the content script
// was really destroyed.
try {
// checkCanary will throw if the content script was destroyed.
checkCanary();
// If we got here, the content script is still valid.
return;
} catch (e) {
}
canary.remove();
// TODO: Put the rest of your clean up code here.
});
(document.body || document.documentElement).appendChild(canary);
})();
Note: CSS transition events are only fired if the tab is active. If the tab is inactive, the transition event will not fire until the tab is shown.
Note: exportFunction is a Firefox-only extension method to define a function in a different execution context (in the above example, the function was defined in the page's context, available to scripts running in that page).
All other APIs are available in other browsers too (Chrome/Opera/Edge), but the code cannot be used to detect disabled extensions, because style sheets from tabs.insertCSS are not removed upin uninstall (I only tested with Chrome; it might work in Edge).
Method 2: Visual restoration upon uninstallation
Method 1 allows you to run arbitrary code, such as removing all elements that you inserted in the page. As an alternative to removing the elements from the DOM, you can also choose to hide the elements through CSS.
Below I show how you can modify method 1 to hide the elements without running other code (such as canaryscript.js).
When your content script creates an element for insertion in the DOM, you hide it with an inline style:
var someUI = document.createElement('div');
someUI.style.display = 'none'; // <-- Hidden
// CANARY_CLASS is the random class (prefix) from the background page.
someUI.classList.add(CANARY_CLASS + 'block');
// ... other custom logic, and add to document.
In the style sheet that you add with tabs.insertCSS, you then define the desired display value, with the !important flag so that the inline style is overridden:
// Put this snippet after "var code = '.' + CANARY_CLASS, above.
code += '.' + CANARY_CLASS + 'block {display: block !important;}';
The above example is intentionally generic. If you have multiple UI elements with different CSS display values (e.g. block, inline, ...), then you can add multiple of these lines to re-use the framework that I provided.
To show the simplicity of method 2 over method 1: you can use the same background script (with the above modification), and use the following in the content script:
// Example: Some UI in the content script that you want to clean up.
var someUI = document.createElement('div');
someUI.textContent = 'Example: This is a test';
document.body.appendChild(someUI);
// Clean-up is optional and a best-effort attempt.
chrome.runtime.sendMessage('getStyleCanary', function(CANARY_CLASS) {
if (!CANARY_CLASS) {
// Background was unable to insert a style sheet.
// Do not add clean-up classes.
return;
}
someUI.classList.add(CANARY_CLASS + 'block');
someUI.style.display = 'none';
});
If your extension has more than one element, consider caching the value of CANARY_CLASS in a local variable so that you only insert one new style sheet per execution context.
Your initial wording was somewhat unclear as to exactly what you desire. Thus, this answer also contains information on one way you could receive a notification of the uninstall, under some conditions.
Run code in your WebExtension add-on prior to uninstall/disable:
No, even if it was supported, the runtime.onSuspend event would not do what you want. It's used to signal Event Pages that they are about to be unloaded. Even Pages are unloaded routinely when the handling of the events they are listening for has completed. It is not an indication that the extension is being uninstalled.
"Determine" that your "WebExtension was disabled/uninstalled":
If your question is really what you state in the last line of your question: "... is there a way to determine whether a WebExtension was disabled/uninstalled?" Then, it looks like you could use runtime.setUninstallURL(), which was implemented as of Firefox 47. This will allow you to set a URL to visit when the add-on is uninstalled. This could be used on your server to note that the add-on was uninstalled. It does not inform your WebExtension that it was uninstalled, nor permit you to run code in your WebExtension when that happens.
Unfortunately, you can not use detecting, in your WebExtension, that this URL was visited as indicating your WebExtension is being uninstalled/disabled. Based on testing, this URL is visited after the WebExtension has been completely uninstalled. In addition, it is not visited upon the WebExtension being disabled, nor when uninstalled after being disabled. It is only visited when the WebExtension is uninstalled while the add-on is enabled. From the fact that this is a JavaScript call which is only run when the extension is enabled, one would expect that the page would only be opened when leaving the enabled state.
Testing was done by adding the following line to a WebExtension and seeing when the page was opened:
chrome.runtime.setUninstallURL("http://www.google.com");
Given how this actually functions (only visited if the WebExtension is enabled and directly uninstalled), using this as "a way to determine whether a WebExtension was disabled/uninstalled" will only be partially effective. As should be clear, you will not be notified by a visit to this URL if the add-on is disabled prior to being uninstalled.
I have a website which has all pages contents in one page called "pagecontents". When a menu in navbar is clicked the jQuery load function is triggered and the relevant content is loaded into the main page.
It's working fine, however I added an hash to the URL for whenever a particular content is loaded , so that users can go directly into viewing relevant content when they type or paste the url with the hash. This works fine in my local host but not on remote host I wonder What's the problem.
if(location.hash == '#web') {
$('#contentFetch').load('pagecontents.php #webC');
}if(location.hash == '#graphic') {
$('#contentFetch').load('pagecontents.php #graphicC');
}if(location.hash == '#mobile') {
//$('#testLocation').text("mobile Works");
$('#contentFetch').load('pagecontents.php #mobileC');
}if(location.hash == '#contact') {
$('#contentFetch').load('pagecontents.php #contactC');
}else{
$('#contentFetch').load('pagecontents.php #indexC');
}
Edit: About the hash in query string:
the website url ex: 'www.mywebsite.com/'. when i add the '#graphic' at the end: 'www.mywebsite.com/#graphic' it's not loading the content. I noticed that if keep on pressing enter even though is not loading, it then loads!!!? it seems very incosistent as it loads 1 time out of 5. I wonder if using this is reliable in real world, or is there another way of doing it? Thanks, Mike
Make sure to check the runtime versions that your host is using and that they comply with the ones you have locally.
Usually, a host will run a version of a software that is often outdated in order to accommodate older websites. (ex. running PHP 4 instead of PHP 5).
1- OPEN FIREBUG, on the console tab
2- OPEN YOUR GMAIL ACCOUNT,
3- when gmail is loaded, click on one of your label (at the left under the draft box)
4- WITH FIREBUG YOU SEE THAT THE PAGE DOES NOT COMLETLY RELAOD SINCE ALL PREVIOUS ACTION STILL THERE FOR THE CURRENT DOCUMENT, BUT THE BROWSER COMPLETLY ACT LIKE THE PAGE HAVE BEEN RELOADED, stop button browser own loading effect, etc...)
5- !!!!! this is it..!!!!
Does some on have a clue on how site like Gmail can make the browser load on ajax call ( I mean show the loading icon and all, history, etc)
I already know what to check for the history navigation but how in the world they can make the browser to act like this was a simple link that load a complete new page.
from what I see with things like firebug Gmail basically retrieve mail information in JSON and than use some Javascript to render it to the user. But how they make the browser load in the while.
In gmail once it is loaded, obviously they ain't load all the data, from all your folder in background, so when you click on some of your folder and the data is not already loaded they make the browser 'load' like if it were loading a complete new page, while they retrieve the information from their server with some ajax call ( in Firefox you see the browser act like when you click on a normal link, loading icon, stop (x) button activated, and all).
Is it clear?
I came up with some 'ugly' code to achieve my goal that work quite nice in FireFox and IE (sadly it seems to not work in Chrome/WebKit and Opera).
I tell the browser to go to a url that it will not be able to reach before the ajax call end, with window.location=. The browser start to load and than when the ajax call sucess I call window.stop() (window.document.execCommand('Stop') for IE) than innerHTML the ajax data in the document
To me its look ugly and since it not work properly in Chrome/Webkit, this is apparently not the way to go.
There are many ways to utilize AJAX.
Gmail needs to load a lot of files/data before something meaningful can be displayed for the users.
E.g. showing the folder tree first doesn't make sense if it's not clickable or not ready for any interactive use.
Hence, what they do is show something lightweight like a loading graphic/progress bar while asynchronously (behind the scene), pull more data from the server until they can populate the page with a full interface for usage.
I don't know how to explain further. Maybe wiki can help: http://en.wikipedia.org/wiki/Ajax_%28programming%29
http://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/
Use one of the methods shown as triggering a browser busy state in the table on the page above.
document.getElementById('iframe').src = "http://www.exemple.com/browser_load.html";
They are using iFrame. By changing the source of the iFrame.
Sitepoint has a book "Build Your Own AJAX Applications" and they show some content (all?) in this tutorial:
http://articles.sitepoint.com/article/build-your-own-ajax-web-apps
They will guide you with your AJAX coding.
Think this is your answer:
http://www.obviously.com/tech_tips/slow_load_technique
Looks like gmail and facebook method (browser is showing page as "loading" with loading icons etc. - it is just simulating, because there is a background ajax request) :)
$(function($){
$('a').attr('onclick','return false;').click(function(){
var title = $(this).attr('title');
var href = $(this).attr('href');
$('title').html(title);
$('#content').load(href+' #content', function(){
history.pushState(null, null, href);
}, function(responseText) {
var title = responseText.match(/<title>([^<]*)/)[1];
document.title = title;
});
});
});
window.onpopstate = function( e ) {
var returnLocation = history.location || document.location;
var returnTitle = history.propertyName || document.title;
$('title').html(returnLocation.title)
$('#content').load(returnLocation.href+ ' #content', function(){
history.pushState(null, null, href);
}, function(responseText) {
var title = responseText.match(/<title>([^<]*)/)[1];
document.title = title;
});
}