Configuring a sidebar_action to be only on for certain sites? - javascript

I want to create a firefox extension that allows a sidebar to automatically show when a user navigates to certain web pages.
For example, let's say I configure it so if a user navigates to google.com they will be presented a sidebar that lets them see some "previous searches" they had done.
I do not want them to have to click a menu action / keyboard shortcut to display it. And I do not want to display it indefinitely.
I've been looking at these links to learn how to use sidebars:
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/user_interface/Sidebars
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/sidebar_action
https://github.com/mdn/webextensions-examples/tree/master/annotate-page
But they do not seem to cover how to hide/show sidebars conditionally. They are just kinda, always shown. Not really what I want.
Is there a way to use sidebars in this way or should I use another method?

You can control when your extension is inserted into the currently visited page (thus, causing your sidebar to appear) or not based on the permission settings in the manifest file
manifest.json
...
"permissions": [
...
"http://*.google.com/",
"http://*event*/",
...
],
In the above example, the extension will work only on google.com as well as any domain containing the word event. If you want to target domains where the word event appear as part of the path, then you would use something like
"http://*/*event*/*",
You get the idea.
For more information read Google's Extension Development - Declare Permissions

This can be achieved by specifying hosts which can be either one of a list of known strings (such as "geolocation") or a match pattern that gives access to one or more hosts.
Here's an example of the permissions part of a manifest file:
"permissions": [
"tabs",
"bookmarks",
"http://www.blogger.com/",
"http://*.google.com/",
"unlimitedStorage"
],

Related

Google Chrome extension - Restart eventlisteners

First of all, I am a newbie in chrome extension development. I am developing a chrome extension to collect user inputs from a form on a specific webpage.
In my manifest.json, I have added javascripts to be used for this purpose inside "content_scripts" and they are invoked when the matched URL loads. In my javsacript, I have added eventlisteners for triggering functions when a button is clicked.
The problem I am facing is that, evenlisteners work fine and the required functions get executed when the button is clicked for first time.
But when the button is clicked for second time, it does not trigger the listener.
Can anyone please recommend me a way to overcome this issue?
Content script part of my manifest.json:
"content_scripts": [
{
"matches": ["*://domain/pathtofile*"],
"all_frames": true,
"js": ["myscript.js"],
"run_at": "document_end"
}
]
And in "myscript.js", I use this:
document.getElementById("elementid").addEventListener("click", functiontocall);
Thank you
Sujith
------ Edit ------
Extension I am developing is for a specific client and this is an unlisted extension in chrome store. So I am not able to provide the URL due to NDA with client.
The URL (example: "http://www.domain.com/urlpath/form.php") has a DIV inside it, which contains an HTML form. So when this URL is loaded, it matches with the manifest and loads a javascript "myscript.js". In "myscript.js", I have written a code:
document.getElementById("formbuttonelement").addEventListener("click", getFormData);
The function "getFormData()" just contains basic javascripts to take values from each form element and prepare a query string and send to another PHP script ("collectForm.php") using AJAX.
After the form button is clicked, the DIV element will close. In the web page, there is a button to open this DIV (HTML form) again, and user can input data in form and click "formbuttonelement" again to submit form.
So, my problem is that, when I open the URL "http://www.domain.com/urlpath/form.php" for the first time, everything works as expected, including the addEventListener part and I can get the form data in "collectForm.php". And, as mentioned earlier, the DIV element will close.
But when the DIV is opened again (which does not load the URL again), addEventListener is not working. I presume that it is because the URL is not reloading, thereby not invoking "myscript.js" again.
I am looking for an option to restart event listener even if the web page is not reloaded.
I hope I have explained the situation.
Thanks & Regards
Sujith

Is it possible to search the DOM for a keyword occurring within an iframe and reload page until it's found?

I have an ad that displays within an iframe on a given publisher site around every 1000 loads or so. I have no control over the host site but I need a way to see the ad live as users will see it. I'm trying to figure out a javascript solution that will load the page, search for the name of my company to see if my ad loaded (the company name is the id of a div tag that loads the iframe) and then either stop there if it finds it, or reload the page if not.
I had it sort of working by running a script in the console that got the innerHTML of the document body, searched for a keyword and then reloaded the page if the keyword wasn't found.
Two problems though.
It could only find keywords outside of the iframe.
It didn't search the content of the iframe (where the actual keyword that would identify my particular ad sits) even if I set a delay or did onload.
Secondly, for every page refresh, the script would be cleared from the console.
I know this is beginner stuff but I would love any pointers to the correct way to tackle this problem.
Thanks so much for the help thus far (also, I upvoted everyone but don't think I have the necessary cred for it to show up publicly)
Here's where I got. I created a chrome plugin with the following manifest.json:
{
"manifest_version": 2,
"name": "Ad Refresh",
"version": "1.0",
"permissions": [
"activeTab",
"tabs"
],
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": ["jquery.min.js", "contentscript.js"],
"all_frames": true
}
]
}
I have the content-scripts running on all urls but will restrict it once I get things running properly.
For contentscript.js that gets injected and runs in each frame, I have:
setTimeout(function(){
$("[title='3rd party ad content']").attr("id", "dfp"); // "3rd party ad content" is the title of all iframes that could potentially contain my ad and is the only identifying attribute across all iframe instances. I stick an id on there so it's easier to grab with getElementById. It only gets the first instance though, need to figure out how to loop through all.
var company = document.getElementById('dfp');
if (company == null) {
console.log("no hit");
} else {
console.log(company);
}
}, 5000);
I'm not worried about reloading the page, I'm just stuck on getting access within the iframe.
I am unable to directly grab any element within the actual content of the iframe with jquery $ or getElementById etc. However, if I run getElementId on the iframe itself and console.log it, it includes all the HTML inside the iframe:
http://i.stack.imgur.com/dfuYt.png
I tried getting the innerHTML of the iframe element so that I'd have it as a string and could search for it, but all it returns is the iframe element tags, none of the inner content. I don't know if any of that makes sense but I appear to be over my head at this point.
Ok, last addition. My ad runs a script that I can see under "Sources" in inspector. So I thought "Why not run
var scripts = document.getElementsByTagName('script'); to get an array of all the scripts that were loaded on the page? Then I could just search the array to see if my script and hence my ad had loaded and we'd be golden." Unfortunately though, it doesn't include my script in the array, even when it's loaded and is visible in "Sources" and accurately includes a random Stripe script that's also loading from within an iframe. Bummer...
Use .load event of the jQuery to know whether iFrame is loaded and then read the innerHTML of the iframe body
Try this:
$('#ID_OF_THE_IFRAME').load(function() {
var iFrameContent = $('body', this.contentWindow.document).html();
console.log(iFrameContent);
});
Fiddle
JS:
var company = document.getElementById('myframe1').contentWindow.document.getElementById('company');
if (company == null) {
//reload
console.log("reload");
} else {
//continue
console.log(company);
}
It sounds like the iframe containing the ad is loaded from a different domain than the main page, is that right? That would explain why your JavaScript code running in the main page (or in the console, same thing) can't access DOM elements inside the iframe. Browser cross-domain security prevents that kind of access: the iframe is treated just like a separate browser window or tab.
If the main page and the iframe were both loaded from the same domain, then you could use contentWindow as a couple of answers have described. But that won't work across domains.
So, what can you do?
You're building a tool for your own use or the use of your colleagues - not something you need to publish on a website for the rest of the world to use, right?
This gives you a couple of other options. First, you could simply disable cross-domain browser security. In Chrome, you can do that as described here:
Disable same origin policy in Chrome
Beware: Don't do any "normal" browsing in a Chrome session running in this mode, only your special testing. But if you do run Chrome in this mode, then you'll be able to access iframe DOM elements via contentWindow and contentWindow.document as described in the other answers.
If that doesn't do the trick, or if you don't want to have to start a special Chrome session for this, another approach would be to write a Chrome extension. This would allow you to write code to access DOM elements in both the iframe and the main window using techniques like these:
access iframe content from a chrome's extension content script
Chrome extension to remove DOM elements inside iframes
Or you could write a Firefox extension if you prefer - similar capabilities are available in both.

How to clear the Google Chrome address bar?

I need to clear the Google Chrome address bar when I load my extension page in the tab.
THIS is my extension, and the address that needs to be cleared looks like this:
chrome-extension://<extension-id>/page.html
* Real solution of your question at the bottom.
Clearing browser address bar
This is a pretty old question that came in the mind of almost everyone who ever used JavaScript: can I replace the whole URL shown in the address bar via JavaScript? Well, I'm sorry, but the answer is a huge NOPE!
Why can't you clear/replace the URL in the address bar?
First of all, JavaScript doesn't actually provide any feature for doing it, and neither do the most famous browsers (Chrome, Mozilla, Opera...) with their APIs. Secondly, but not less important, it would be a huge security flaw. Imagine that some malicious individual wants to steal you some passwords (for example, the Facebook one): if changing the browser's address bar was possible, he would easily create a fake log-in page, copying the original one, and then replace the URL with "facebook.com/login" to make it look like you really are on Facebook (it would be pretty easy, huh?). You can obviously see that this could apply to any site and many other services.
JavaScript location and history
In JavaScript, there are two common objects used to manipulate the page address:
The first one is window.location, which provides you useful information about the current page's location, (such as .hostname, .path, .href, etc), and functions to reload the page or navigate to a different URL. By the way, when changing the address using this object, the page will always be reloaded.
location.href = "http://newsite.com"; // will reolad
location.path = "/home.html"; // will reload
location.replace("google.com"); // will also reload
The second, more tricky one, is window.history, which also allows you to manipulate the URL without reloading the page, within some limitations (obviously). With its methods .pushState() and .replaceState() makes you able to change the current path without reloading the page. By the way, you only can navigate through your own site's pages (you cannot change the hostname).
history.back();
// will make your browser go back to the previous page
history.pushState(null, null, "newPage.html");
// will replace the current path with /newPage.html and add it to the history
// http://example.com/page1.html -> becomes -> http://example.com/newPage.html
// now, if you call history.back() the path will be set back to /page1.html
history.replaceState(null, null, "newPage.html");
// will replace the current path with /newPage.html, PLUS it will also replace the current history path
// http://example.com/page1.html -> becomes -> http://example.com/newPage.html
// calling history.back() will not set the path back to /page1.html, but to the previous one (if exists)
This API is pretty useful. YouTube, Twitter and many other famous sites use this feature to change the URL without reloading the page. This practice allows you to change part of the page content, without having to reload those parts that stay unchanged (for example: on Youtube it will only load the new video, info, and related, not the full page).
Chrome extensions: URL overrides
Google chrome offers to its extensions the possibility to override three common default pages using the "chrome_url_overrides" field in your manifest.json:
The New Tab page (chrome://newtab) displayed when a new tab is opened.
The History page (chrome://history) containing the navigation history and used to manage it.
The Bookmarks page (chrome://bookmarks) containing all of your bookmarks and used to manage them.
These are the only three cases which allow you to "replace" the address bar URL in Chrome. Don't misunderstand what I'm saying: Chrome still doesn't let you change the URL if you are in one of these pages, but it automatically changes it. Using this technique, users can enjoy their beautiful extensions on new tabs, history and bookmarks pages, without seeing the ugly "chrome-extension://..." URL.
Using the "history" override will load your custom page instead of the default history one, but will maintain the default chrome://history URL.
Using the "bookmarks" override will load your custom page instead of the default bookmarks one, but will also maintain the default chrome://bookmarks URL.
Using the "newtab" override will load your custom page instead of the new tab one, but will maintain the default (blank) URL.
What if I want to navigate to another page maintaining the default address?
Assuming that you're in one of those three pages and you loaded your custom page instead of the original one, you can use an <iframe> set to full width and height to navigate to other pages via JavaScript, changing its src attribute:
HTML:
<iframe id="my-frame" style="width: 100%; height: 100%; margin: 0; padding: 0", frameborder="0"></iframe>
JavaScript:
var frame = document.getElementById('my-frame');
frame.src = "/path/to/page.html";
// navigate to a local page stored in your extension
// or:
frame.src = "http://www.somesite.com";
// navigate to an external site (not always possible)
NOTE: navigating to an external site may not be always allowed because of the security policy of that site. If the external page you're trying to load has the X-Frame-Options header set to "SAMEORIGIN" (like google.com for example), you will not be able to load it, and an error will occur:
"Refused to display 'http://www.somesite.com/...' in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'."
To answer your question:
After all this long tour, there's a simple problem in your extension: you are using "chrome_url_overrides" to override the New Tab page with dashboard.html, which will execute the script redirect.js creating a new tab with url: "/searchTab.html". That's why you see the "chrome-extension://..." URL. So why are you doing all this stuff? You load a perfectly useless blank page which has the sole purpose of opening a new tab with the searchTab.html page... you don't really need this. Just delete that useless /dashboard.html page and change the overriding page in your manifest.json to /searchTab.html:
...
"chrome_url_overrides": {
"newtab": "/searchTab.html"
},
...

Load 2nd web page in background to get content from this web page

I couldn't find anything about this topic, maybe because I'm not a good english speaker and can't find the right words to search for in google therefore.
I'm currently working on an Chrome browser extension which enables me to search for user accounts on a specified web page by using the context menu. Example: If I select the text name123 on any webpage and click the context menu entry, a new tab http://www.webpage.de/user/name123/ is opened.
Now I want to extend the extensions capabilities: I want to search for email addresses, too. For that I can use http://www.webpage.de/search/name123#mail.com/ which brings up a list of all user with the email address name123#mail.com. From there, I could extract the link to the user account as follows:
document.getElementsByClassName("xyz")[0].href
My question: Can I skip the loading of http://www.webpage.de/search/name123#mail.com/ and the "extraction" of the url to the user account? Respectively, can I hide the procedure of opening this additional web page? Does JavaScript support any kind of "preloading" of a webpages content, without being displayed in the browser?
If you can parse the required link from raw HTML, you can fetch the page via an XMLHttpRequest in the background page, examine the result and then open the real profile page.
If the page is dynamic and you need its scripts to run before you can extract the link, you can load it in an iframe in the background page and examine it.
You can do it.
Add permessions to your manifest.json file: 'http://www.webpage.de/*'
Create neccesary ajax requests from background script.

Overlaying the page contents from Google Chrome extension

I am writing a very simple Google Chrome extension, but have faced such a multitude of issues that it's a bit overwhelming for a project of such a miserable scale.
The extension is a very simple one: upon arrival on any page (say, google.com) the page contents is hidden and the user is faced with a question (s)he has to answer correctly... or be redirected to some other page that provides the correct answer. In other words, the user cannot access pages on the Internet unless (s)he answers the questions correctly.
To hide the page contents I decided to go for a simple overlay using the following approaches:
Approach #1
I tried appending to the current document's body a simple opaque div with position: fixed;, z-index: 2147483647; and width/height at 100%. That worked, but:
The page's CSS kept interfering with the elements inside of my div.
Flash content occasionally appeared on top of it (at least on Windows XP).
Chasing embeds all over the page and setting wmode to
"transparent" didn't help, offsetting to -10000px or setting
display:none; simply alleviated but didn't solve the problem. See also this question.
Approach #2
I tried sandboxing the GUI in an iframe that is created and injected into the page to behave exactly as the div in the above approach. It perfectly solves the issues of the first approach, but:
Apparently there's no way to access the contents of the iframe because of cross-origin policy. And that access – I need it to assign handlers to the input field where the user is typing the answer, I need to remember who's stealing the focus from my answer input field to give it back once the question is answered, etc. etc. etc.
Using Message Passing didn't work for me and I'm not even sure if I should make it work because messaging makes the entire thing overly complex and prohibits me from using the application as a simple webpage (i.e. not as an extension). Why even bother?
So... where am I wrong with my approaches? Is there a third or a fourth one that I'm not aware of?
I appreciate but don't really need code as an answer. A hint or a push to the right direction would be just as good.
P.S. I suppose that at some point somebody will ask if I have code to share. I do, but there's a bunch of it. Which part specifically would you like to see?
Approach #2
Concern #1
Apparently there's no way to access the contents of the iframe because
of cross-origin policy. And that access – I need it to assign handlers
to the input field where the user is typing the answer, I need to
remember who's stealing the focus from my answer input field to give
it back once the question is answered, etc. etc. etc
Yes you access contents of iframe(s) for matter all the code of a Web Page, no CSP etc.
Content script injecting an iframe.
I suggest this is best approach , you can inject script to dynamic generated iframes as shown here and fetch content
Sample Implementation
manifest.json
{
"name": "Iframe",
"description": "",
"version": "1",
"manifest_version": 2,
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"myscript.js"
],
"run_at": "document_end"
},
{
"matches": [
"<all_urls>"
],
"js": [
"anotherscript.js"
],
"all_frames": true
}
],
"permissions": [
"<all_urls>"
]
}
myscript.js
var iframe = document.createElement("iframe");
iframe.setAttribute("src", "https://www.facebook.com/plugins/like.php?href=http://allofrgb.blogspot.in/");
iframe.setAttribute("style", "border:none; width:150px; height:30px");
iframe.setAttribute("scrolling", "no");
iframe.setAttribute("frameborder", "0");
document.body.appendChild(iframe);
anotherscript.js
iframes = document.getElementsByTagName("iframe");
for (iframe in iframes){
console.log(iframes[iframe].src);
}
console.log("In another Script");
If you observe console logged messages you observe messages are logged twice (document log + iframe log + [any number of optional iframes in pages]*)
anotherscript.js which runs during document idle states does execute in dynamic generated iframe, how ever you can re-run content script through chrome.tabs.executeScript() any time.
Concern #2
Using Message Passing didn't work for me and I'm not even sure if I
should make it work because messaging makes the entire thing overly
complex and prohibits me from using the application as a simple
webpage (i.e. not as an extension). Why even bother?
What is you want to achieve?
Totally forgot about this question... In the end I went with approach 2 using message passing to communicate with the iframe and it works pretty fine. Here's a repo of the extension for those who are interested in reading the code: https://github.com/olegskl/invasive-kanji

Categories