Firefox extension: Open window and write dynamic content - javascript

I have developed a Chrome Extension and it's mostly compatible to firefox web-extensions API. Just one problem:
In Chrome Extension i have popup.js and background.js. User click's a button, popup.js does chrome.sendMessage to background.js where data is received and afterwards (popup.html may be closed meanwhile) i just call in background.js:
newWin = window.open("about:blank", "Document Query", "width=800,height=500");
newWin.document.open();
newWin.document.write('<html><body><pre>' + documentJson + '</pre></body></html>');
// newWin.document.close();
so that works fine in Chrome extension but not in firefox. I read here (https://javascript.info/popup-windows) that for safety reasons firefox will only open with a "button click event". And if i move above code to popup.js, inside button-click-evenListener, it will open this way (but i dont have the data prepared yet, thats really not what i want)
So i tried everything i found but i dont get the chrome.tabs.executeScript running. Here is my code with comments:
popup.js
// working in firefox and chrome (popup.js)
const newWin = window.open("about:blank", "hello", "width=200,height=200");
newWin.document.write("Hello, world!");
// not working firefox: id's match, he enters function (newWindow) but document.write doing nothing (but no error in log)
// not working chrome: doesnt even enter "function (newWindow)""
chrome.windows.create({
type: 'popup',
url: "output.html"
}, function (newWindow) {
console.log(newWindow);
console.log(newWindow.id);
chrome.tabs.executeScript(newWindow.tabs[0].id, {
code: 'document.write("hello world");'
});
});
background.js
(created local output.html and gave several permissions in Manifest.json - tabs, activeTab, output.html, , about:blank)
// opening but executeScript not working in firefox: Unchecked lastError value: Error: The operation is insecure.
// opening but executeScript not working in chrome: Unchecked runtime.lastError: Cannot access contents of url "chrome-extension://plhphckppghaijagdmghdnjpilpdidkh/output.html". Extension manifest must request permission to access this host
chrome.tabs.create({
// type: 'popup',
url: "output.html"
}, function (newWindow) {
console.log(newWindow);
console.log(newWindow.id);
chrome.tabs.executeScript(newWindow.id, {
code: 'document.write("hello world");'
});
});
How can I get the data into the new window/popup from background.js - i can open an empty page from there, so it's only about getting executeScript() running

Thanks to #wOxxOm for pointing me to a data URI to transport the json document into the browser from background.js.
While searching for a javascript method to build a data URI i found this thread, with the suggestion to create a Blob :
https://stackoverflow.com/a/57243399/13292573
So my solution is this:
background.js
var documentJson = JSON.stringify(documents, null, 2)
let a = URL.createObjectURL(new Blob([documentJson]))
chrome.windows.create({
type: 'popup',
url: a
});

Related

Google chrome extension race condition

I have a google chrome extension. In the background.js file, it contains this listener:
chrome.webRequest.onBeforeRequest.addListener(function(info)
{
console.log("On Before request info", info);
},
{
urls:
[
"<all_urls>"
]
},
[
"blocking"
]
);
I tell google chrome to open using the "--app" command line parameter and the html page it opens contains this:
<script type="text/javascript">
let url = new URL(window.location.href);
let targetUrl = url.searchParams.get("targetUrl");
if(targetUrl != null)
{
targetUrl = unescape(targetUrl);
console.log("The target url is:", targetUrl);
window.location.href = targetUrl;
}
</script>
It gets a url from the from the params and then navigates to it.
The issue is that my google chrome extension's listener is not being fired.
I found that if I set it to do the redirect with a 1 second timeout.
Then it fires as its suppose to.
This makes me believe that the background.js file of my extension is not executed before the first page is loaded.
How can I make google chrome assure that all the extensions are loaded and initialized before the first page is ever loaded?

Debugging source files using chrome extension

I am trying to control the debugger using Chrome Extension.
I am using devtools-protocol and chrome extension documentation, but I have no idea how to implement them as I have not seen any samples of the methods in use. I used the sample extension from here which shows how to pause and resume the debugger only, but that's absolutely no use to me. I tried to implement some methods in the sample, but nothing happens.
function onDebuggerEnabled(debuggeeId) {
chrome.debugger.sendCommand(debuggeeId, "Debugger.setBreakpointByUrl", {
lineNumber: 45825,
url: 'full https link to the js file from source tab'
});
}
The problem is that the js file I am trying to debug is loaded from the website inside the sources tab and it's huge, we talking 150k+ lines after its been formatted and it takes some time to load.
Now can anyone tell me how to simply add a break point inside the js file from the sources (USING CHROME EXTENSION) so it could be triggered on action which will then stops the debugger so I could change values etc?
Maybe you are placing wrong breakpoint location (formatted source), try working with original source and add columnNumber: integer
and here working version JavaScript pause/resume -> background.js:
install this extension
open https://ewwink.github.io/demo/Debugger.setBreakpointByUrl.html
click debugger pause button
click button "Debug Me!"
it will hit breakpoint on https://ewwink.github.io/demo/script.js at line 10
click debugger continue button, you will see message variable "hijacked..."
the code:
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// mod by ewwink
var attachedTabs = {};
var version = "1.1";
chrome.debugger.onEvent.addListener(onEvent);
chrome.debugger.onDetach.addListener(onDetach);
chrome.browserAction.onClicked.addListener(function(tab) {
var tabId = tab.id;
var debuggeeId = {
tabId: tabId
};
if (attachedTabs[tabId] == "pausing")
return;
if (!attachedTabs[tabId])
chrome.debugger.attach(debuggeeId, version, onAttach.bind(null, debuggeeId));
else if (attachedTabs[tabId])
chrome.debugger.detach(debuggeeId, onDetach.bind(null, debuggeeId));
});
function onAttach(debuggeeId) {
if (chrome.runtime.lastError) {
alert(chrome.runtime.lastError.message);
return;
}
var tabId = debuggeeId.tabId;
chrome.browserAction.setIcon({
tabId: tabId,
path: "debuggerPausing.png"
});
chrome.browserAction.setTitle({
tabId: tabId,
title: "Pausing JavaScript"
});
attachedTabs[tabId] = "pausing";
chrome.debugger.sendCommand(
debuggeeId, "Debugger.enable", {},
onDebuggerEnabled.bind(null, debuggeeId));
}
function onDebuggerEnabled(debuggeeId) {
chrome.debugger.sendCommand(debuggeeId, "Debugger.setBreakpointByUrl", {
lineNumber: 10,
url: 'https://ewwink.github.io/demo/script.js'
});
}
function onEvent(debuggeeId, method, params) {
var tabId = debuggeeId.tabId;
if (method == "Debugger.paused") {
attachedTabs[tabId] = "paused";
var frameId = params.callFrames[0].callFrameId;
chrome.browserAction.setIcon({
tabId: tabId,
path: "debuggerContinue.png"
});
chrome.browserAction.setTitle({
tabId: tabId,
title: "Resume JavaScript"
});
chrome.debugger.sendCommand(debuggeeId, "Debugger.setVariableValue", {
scopeNumber: 0,
variableName: "changeMe",
newValue: {
value: 'hijacked by Extension'
},
callFrameId: frameId
});
}
}
function onDetach(debuggeeId) {
var tabId = debuggeeId.tabId;
delete attachedTabs[tabId];
chrome.browserAction.setIcon({
tabId: tabId,
path: "debuggerPause.png"
});
chrome.browserAction.setTitle({
tabId: tabId,
title: "Pause JavaScript"
});
}
demo
EDIT: Just saw your comment about this being for a custom extension you're writing. My answer won't help you (sorry!), but it might help people that come here looking for a way of setting normal breakpoints in Chrome.
Maybe you already did, but... Have you tried just clicking the line number of the line you want to set the breakpoint in?
Like this:
You can even enable or disable breakpoints in several different calls in the same line.
When the page is loaded, open Dev Tools with F12, then navigate to the JS file in the Sources panel, and add the breakpoints you want. Then, refresh the page to load it again -- Chrome will remember where you set the breakpoints and stop at them.
If you can modify the source code of the file that you want to debug, I would look use the debugger statement.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger
function potentiallyBuggyCode() {
debugger; //application will break here as long as chrome developer tools are open
}
function breakhere() {
debugger;
}
ok, for start you have to sendCommand Debugger.enable .. something like this:
var tabId = parseInt(window.location.search.substring(1));
window.addEventListener("load", function() {
chrome.debugger.sendCommand({tabId:tabId}, "Debugger.enable");
chrome.debugger.attach( tabId, "0.1" );
chrome.debugger.onEvent.addListener(onEvent);
});
then inside you onEvent you can set breaking points
function onEvent(debuggeeId, message, params) {
if (tabId != debuggeeId.tabId) return;
var res = Debugger.setBreakpointByUrl( 2, 'url-of-the-script-file' );
}
I would strongly suggest to check the sniffing section on this page: https://chromedevtools.github.io/devtools-protocol/ because I was able to see the json that get returned via WS protocol and that will help you to do pretty much anything you want.. I can't build you full extension, you are on your own man,,
I guess that you need to add somekind of DOM elemnet with list of scripts that you'll parse from Network.getResponseBody and then somekind of UX tool to pick that scripts and let users to debugging,, that process could take you some time :(
hope I have steered you in the right direction, good luck!

Firefox Extension: Error calling executeScript on file but not code

I'm trying to execute a script in a new tab when that tab is opened.
In my background script I have:
var listener = function (tab) {
browser.tabs.executeScript(null, { file: "content_scripts/contentScript.js" });
}
browser.tabs.onCreated.addListener(listener);
In contentScript.js:
function foo() {
console.log("Executed");
}
foo();
From this I get the following error:
Error: Permission denied to access property "chrome"
If I simply execute code rather than call a js script, ex:
browser.tabs.executeScript(null, { code: "console.log("Executed") });
This does not cause the error. Any idea what is causing this?
It doesn't work properly because apparently this bug has not been fixed and still exists in Firefox 59.
You can work around the issue by letting the tab load for some milliseconds before running executeScript on it.
I have tested this an it works for me:
(Remember that this does not work for new blank tabs as in "about:newtab". These can not be accessed for security reasons.)
background.js
var listener = function (tab) {
setTimeout(()=>{
browser.tabs.executeScript(tab.id, {file: "/content_scripts/contentScript.js"}).then(()=>{
console.log("Executed!")
}).catch(err=>{
console.error("Not executed:")
console.error(err)
})
},1000) // Add a delay because of this bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1254003
}
browser.tabs.onCreated.addListener(listener);
content_scripts/contentScript.js
function foo() {
console.log("Executed");
alert("Executed!");
}
foo();
manifest.json
{
"description": "Test executeScript api",
"manifest_version": 2,
"name": "ExecuteScript-Test",
"version": "1.0",
"permissions": [
"activeTab",
"tabs",
"<all_urls>"
],
"background": {
"scripts": ["background.js"]
}
}
If FF developers docs it's said:
In Firefox, relative URLs passed into tabs.executeScript() or
tabs.insertCSS() are resolved relative to the current page URL. In
Chrome, these URLs are resolved relative to the add-on's base URL. To
work cross-browser, you can specify the path as an absolute URL,
starting at the add-on's root, like this:
/path/to/script.js
so this should do the trick, but in fact FF48 due to unknown reasons both insertCSS() and executeScript() don't want to execute. At the same time I've found if to execute code with these functions calls in debugger in step-by-step mode, they triggered normally. So it looks like FF bug.
Can't comment, so sorry this isn't a real answer ...
I'm not seeing the same results. Provided details in case anything jumps out at you as being different from how I ran it and how you are running it.
In background script (located in the extension root directory), on extension startup, both:
var temp = function (tab) {
browser.tabs.executeScript(null, { file: "src/js/asdf.js" });
};
browser.tabs.onCreated.addListener(temp);
and
browser.tabs.onCreated.addListener(function (tab) {
browser.tabs.executeScript(null, { file: "src/js/asdf.js" });
});
register correctly (no errors on startup).
The file src/js/asdf.js exists, is the correct relative path from background.js, and contains your foo method and call.
When I create a new blank tab (which by definition cannot have content scripts attached), I see the expected error in the console (albeit from undefined instead from background.js):
Error: Missing host permission for the tab
When I create a new blank tab with content from the beginning (i.e., context click to open a link in a new tab), I see the expected result (Executed in the console log).
When I create a new tab from within the extension, I also see the expected result (Executed in the console log.
Potentially relevant manifest info:
asdf.js is not web accessible
permissions include tabs and <all_urls>
there are no content scripts defined in manifest.json
Running Firefox 59.0.2 (64-bit) on Mac 10.13.4

Google chrome extension error when debugger open only

I am trying to send a ajax request to a server inside a google chrome extension. I am using angular with it too and the code is inside the controller. The request works fine generally, but when I have the the DevTools open it throws an error. This is the relevant part of the code:
$scope.sendLink = function (){
chrome.tabs.query({'active': true, 'lastFocusedWindow': true}, function (tabs) {
var url = tabs[0].url;
var date = new Date();
var xPost = new XMLHttpRequest();
xPost.open("POST", **URL HERE*",true);
xPost.setRequestHeader("Content-type", "application/json");
xPost.send(JSON.stringify({"name":$scope.name,"url":url,"date":date}));
});
}
When I hit inspect element on the extension, and then do the request, the tabs array come back as empty. It works fine if the devTools window is closed. I can't figure out why that is the case. Any explanation would be appreciated!
You could have changed the javascript engine behavior via chrome-dev-tools. This would get activated only when the dev-tools are open.
For instance, once I found that a mate of mine had turned-off javascript using the chrome-dev-tools. His app wasn't executing when the chrome-dev-tools were open...

Unknown error on chrome.tabs.executeScript

I need to run a script on an external page.
I'm trying to consume the Dropbox API (JavaScript and HTML only).
I'm using JsOAuth to work with OAuth.
Code
This application is a pair of type Packaged Apps to Google Chrome.
Authorise
//Request token
chrome.windows.create({url: url, type:"popup"}, function(win){
chrome.tabs.executeScript(win.id, { file: "contentScript.js" }, function(){
console.log("Callback executeScript!!");
});
});
url = Request token url
contentScript.js
$(document).ready(function() {
console.log("Script injected!!!");
})
Error in console
Error during tabs.executeScript: Unknown error.
chromeHidden.handleResponseextensions/schema_generated_bindings.js:94
openAuthoriseWindowscripts.js:297
g.fetchRequestTokenjsOAuth-1.3.3.min.js:1
g.init.request.q.onreadystatechange
Attempts
As the external page can not jQuery, an effort was to remove the reference to jQuery
contentScript.js
console.log("Script injected!!!");
Error in console
Error during tabs.executeScript: Unknown error.
chromeHidden.handleResponse
Another attempt was to inject the script via code:
//Request token
chrome.windows.create({url: url, type:"popup"}, function(win){
chrome.tabs.executeScript(win.id, { code: "console.log('Script injected!!')" }, function(){
console.log("Callback executeScript!!");
});
});
But the error was the same as above
I'm not sure whether you are wanting to inject the script into the tab opening the window, or the new tab you just opened. In any event, I made an effort to answer both questions below. First, please note that you should not attempt to load the script into the window object. The window can contain multiple tabs, and each tab has their own scripting environment. Inject your script into a tab of the newly opened window.
Outcome 1: Injecting the Script into the tab you Just Opened
The code below should load the script into all of the tabs of a window since win.tabs gives an array of tabs. For a newly opened window, there is usually only one tab.
chrome.windows.create({url: "https://google.com", type:"popup"}, function(win){
chrome.tabs.executeScript(win.id.tabs,
{ code: "console.log('new tab context');" });
});
Outcome 2: Injecting the Script into the tab opening the window
Record the id of the tab opening the new window, then inject the script on the callback
var openingTabId = ASSIGN_THE_TAB_ID;
chrome.windows.create({url: "https://google.com", type:"popup"}, function(win){
chrome.tabs.executeScript(openingTabId,
{ code: "console.log('opening tab context');" });
});
Notice that I used the code object to pass code without using a script file.

Categories

Resources