I am writing an page action extension for Google Chrome. The extension injects the following script into a search page after it loads. After the script finds all the occurrences of class "f_foto" (typically 10 items), it finds the first link in each of them, puts these hrefs in an array and then iterates thru the array opening a new window for each link and examining the result. That's what it is supposed to do.
Everything works ok in this code except the last part. The new window opens in a new tab (I have tabs permission) but it only finishes loading after the script finishes. Each new window overwrites the previous one in the same tab which would be ok if I had a chance to examine the contents first. So if I run it without using the debugger when the script finishes the new tab contains the last item in the array and focus is on the new tab. As far as I can see, handleResponse is never called.
If I run it in the DOM inspector and stop it at window.open, I can see that the new tab opens with "About Blank" in the title and the tab shows a spinning thingy showing that it is loading. Stepping thru the code, detailWin remains undefined even after detailWin=window.open(profileLinks[i], "Detail Window"); is executed. I've tried replacing window.onload = handleResponse; with detailWin.onload =handleResponse; but in this case detailWin is undefined.
It seems to me I need to add an event listener that fires when the new window is loaded and executes handleResponse. Yes? No?
//PEEK.JS//
var req;
var detailWin;
var profileLinks = new Array();
function handleResponse()
{
// var contentDetail = document.getElementsByClassName("content");
alert("Examine Detail Page Here");
};
//drag off the f_foto class
var searchResult = document.getElementsByClassName("f_foto");
alert("Found Class f_foto "+searchResult.length+" times.");
//collect profile links
for (var i = 0; i<searchResult.length; ++i)
{
var profileLink=searchResult[i].getElementsByTagName("a");
profileLinks[i]=profileLink[0].href;
// alert(i+1+" of "+searchResult.length+" "+profileLinks[i]+" length of "+profileLinks[i].length);
}
for (var i = 0; i<searchResult.length; ++i)
{
//DYSFUNCTIONAL CODE: New window finishes loading only after script completes, how to execute handleResponse?
detailWin=window.open(profileLinks[i], "Detail Window");
window.onload = handleResponse;
}
Option #1: make two separated content scripts - one for the search page only, one for the profile page only. Search script would only open profile link, profile script would only process it (contains code inside your handleResponse())
Option #2 If for some reasons you don't want to inject profile script to all profile pages, only to those you opened yourself from the search page, then instead of opening windows from a content script you should send a message to a background page asking it to open a profile link in a new tab and inject your profile script.
You still will have two content scripts.
search.js (injected to search pages only):
//PEEK.JS//
var req;
var detailWin;
//drag off the f_foto class
var searchResult = document.getElementsByClassName("f_foto");
alert("Found Class f_foto "+searchResult.length+" times.");
//collect profile links
for (var i = 0; i<searchResult.length; ++i)
{
var profileLink=searchResult[i].getElementsByTagName("a");
profileLinks[i]=profileLink[0].href;
// alert(i+1+" of "+searchResult.length+" "+profileLinks[i]+" length of "+profileLinks[i].length);
}
for (var i = 0; i<searchResult.length; ++i)
{
//tell bkgd page to open link
chrome.extension.sendRequest({cmd: "openProfile", url: profileLinks[i]});
}
profile.js (will be injected to profile pages you opened)
var contentDetail = document.getElementsByClassName("content");
alert("Examine Detail Page Here");
background.html:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if(request.cmd == "openProfile") {
chrome.tabs.create({url: request.url}, function(tab){
//profile tab is created, inject profile script
chrome.tabs.executeScript(tab.id, {file: "profile.js"});
});
}
});
Option #3: Maybe you don't need to create profile window at all? If all you need is to find something in the page source, then you can just load that page through ajax and parse it (you would need to do it in a background page).
Related
I have one page with list of reports and after clicking on report it redirects me to internal report page in new tab with:
window.open(reportIdUrl,reportId);
So when i am back on report list and i want to open same report i will use
window.open("",reportId);
if(redirect.location.href === "about:blank" || redirect.location.href !== '<internalreportpage>') {
redirect = window.open(reportUrl,reportId);
redirect.focus();
} else {
redirect.focus();
}
But when someone from new tab with internal report page navigates somehow to report list page (within this tab) and then tries to open internal report page it will open it in same tab as it is tab reference not content reference.
Does anybody know some way to drop reference when i access if condition?
Something like:
window.open("",reportId);
if(redirect.location.href === "about:blank" || redirect.location.href !== '<internalreportpage>') {
window.dropReference(reportId) //change_me
redirect = window.open(reportUrl,reportId);
redirect.focus();
} else {
redirect.focus();
}
So it will create new tab with new reportId reference?
Thanks
EXAMPLE
--reportlistpage
linkToReport1 (window.open(report1Url,1))
linkToReport2 (window.open(report2Url,2))
.
.
.
You click on linkToReport1 - 2 tabs are opened. One with reportlistpage and one with internal-report/report1.
You go back to parent tab and click to linkToReport1. Tab is opened with reference to "1" and will focus to it and no new tab is opened (this is ok).
You are on linkToReport1 and you redirect with some menu hyperlink to reportlistpage (within linkToReport1 tab). Url changed to /report-list
You click to linkToReport1. Nothing happen (this is not ok) because you are on the tab with reference to 1 (content and url changed), now you want to open a new tab with /internal-report/report1 url and store it as 1 with window references.
I figured it out. It is bit simple when you realize you can set or reset window.name. So there is nothing like references from parent.
Based on content you can do something following. In the page you want to keep track in tab,
at the beginning (in my case internalReport id):
var currentWindow = window.self;
currentWindow.name = reportId; //this is in case someone manually opened it without window.open(internalReportUrl, reportId)
then bind resetting of window.name on beforeunload event (e.g.):
var resetWindowName = function() {
var currentWindow = window.self;
currentWindow.name = ""
}
window.addEventListener('beforeunload', resetWindowName);
So when you redirect from internalreportpage somewhere, name is cleared and you can repeadetly call
window.open(internalReportUrl,reportId)
which will open new tab for you.
I am currently writing a Firefox extension that opens a new window in my background script. When a user clicks a button on a page the background script executes and opens the window.
So far I have:
// content script
downloadMod(linkToOpen); //button clicked
function downloadMod(url) {
// var test = window.open(url, '_blank');
var myPort = browser.runtime.connect({ name: "cac410c4672fff93bf0d3186636d8876de3dfeb6#temporary-addon"});
myPort.postMessage({ greeting: url });
}
In my background script (the script the above code connects too) I have:
//background script
portFromCS.onMessage.addListener(function (m) {
var test = browser.windows.create({
url: m.greeting,
allowScriptsToClose: true,
});
NOTE: all the code works as expected. However, the trouble comes when closing the window.
I have tried variations of self.close(), window.close(). I even created a function to wait 5 seconds to load the newly created window and then close the page to no avail. All of my attempts come back with the error:
Scripts may not close windows that were not opened by script.
I thought this error was supposed to be removed with the allowScriptsToClose flag?
To close a window you created with browser.windows.create, you need to use browser.windows.remove.
So:
let windowId;
browser.windows.create({url: "http://google.be"}).then((data) => {
windowId = data.id;
});
// Sometime later, use windowId to close the window
setTimeout(function(){
browser.windows.remove(windowId);
}, 5000);
I have an extension that injects content scripts when the tab is updated.
Main script:
chrome.browserAction.onClicked.addListener(function(tab) {
"use strict";
const sendScriptToPage = function(tabId, changeInfo, tab) {
if (changeInfo.status === "complete" && tab && tab.url && tab.url.indexOf("http") === 0) {
console.log ("executeScript ");
chrome.tabs.executeScript(tabId, {
file: "content.js", allFrames: true
});
}
};
chrome.tabs.onUpdated.addListener(sendScriptToPage);
});
Content script:
const Content = (function() {
"use strict";
console.log("Content");
const makeRandomColor = function(){
let c = '';
while (c.length < 6) {
c += (Math.random()).toString(16).substr(-6).substr(-1)
}
return '#'+c;
};
document.body.style.backgroundColor = makeRandomColor();
}
)();
It works fine when I reload a tab. However, when a tab is reloaded dynamically, the content script gets reloaded although it is already loaded in the tab. This shows in the log since const can't be re-declared.
Should not content scripts be unloaded when an update occurs? How can I know if a content script is already loaded or not?
A URL that shows this behaviour:
http://www.prisjakt.nu/produkt.php?p=391945#rparams=ss=android
Typing something in the search field triggers the onUpdated event handler, but the content script is already in the page.
A test extension:
https://github.com/hawk-lord/chrome-test
A navigation would wipe the content scripts, but onUpdated can be triggered by many things.
The first that comes to mind is an iframe being loaded. The main page doesn't navigate but you do inject indiscriminately.
Many approaches are possible:
Make the content script more robust by checking for Content being defined before you do anything.
Listen to a different event, for example, various webNavigation API events. They will uniquely identify the frame they refer to.
Don't listen to events - always inject, and change your triggering logic (for instance, keep a flag in chrome.storage).
I am trying to develop a simple add-on for Firefox which should work something like this:
User clicks item in context menu.
New tab is opened.
Content (innerHTML) of the new tab is overridden using content script.
Also, the content script should only be executed once, so that if the user would enter a website in the new tab the script should not be executed.
I've got it working with editing the new tabs content, but my only problem is to have the content script run only once when the tab is opened. In the code I have at the moment the script run every time a page has been loaded in the tab:
var contextMenu = require("sdk/context-menu");
var tabs = require("sdk/tabs");
var menuItem = contextMenu.Item({
label: "Test",
contentScript: 'self.on("click", function () { self.postMessage(); })',
onMessage: function (data) {
newTab();
}
});
function newTab () {
tabs.open("about:blank");
tabs.activeTab.on('ready', function (tab) {
tab.attach({
contentScript: 'document.body.innerHTML = "testing";'
});
});
}
I'm guessing there's a way to have this run only the first time the tab is "ready". Seems like a simple task but I can't figure out how to do this. Anyone got any tips?
Use tabs.activeTab.once(); instead of tabs.activeTab.on();.
This way the listener gets detached once it intercepts the first message.
I have a link on a page. When the user clicks the link a static HTML page loads. I would like to prevent the user from launching a second instance of the same static HTML document.
I'm thinking some javascript up front in the static HTML page might do. Basically if the script detects that there is already an instance of the static HTML document loaded then a Javascript pop-up would indicate to the user that 'document is already loaded" (something like that).
Anyhow, Java script is not my strong point so wondering if someone can please shed some light on this.
I'm not sure if this is cross-browser, but works in Chrome:
var wins = {};
var anchors = document.getElementsByTagName('a');
for(var i=0, len=anchors.length; i<len; i++) {
anchors[i].onclick = function(e) {
e.preventDefault();
var href = this.href;
if(wins[href]) {
wins[href].focus();
} else {
wins[href] = window.open(href);
}
}
}
http://jsbin.com/ivabax/1/edit
You could implement a logging feature that tracks each link that was clicked and maintains a session for the current user. If the link has already been clicked then you could present your firewall window that says "Sorry, this link has already been accessed".
So the hyperlink the user would click would first hit a tracking mechanism before being allowed (or not allowed) to continue to the URL in question.