I'm developing a react app based chrome extension which uses Google's material design and has a couple of pages with navigation.
I want to inject the extension inside the browser tab when the extension is launched from the browser address toolbar. I've seen multiple extensions do so by injecting a div(inside the body of webpage) containing an iframe with src equal to the extension's pop-up HTML page.
I execute the following function when the extension is launched. Which basically injects the extension into the target webpage body but it appears multiple times inside the target web page.
function main() {
const extensionOrigin = "chrome-extension://" + chrome.runtime.id;
if (!location.ancestorOrigins.contains(extensionOrigin)) {
// Fetch the local React index.html page
fetch(chrome.runtime.getURL("index.html") /*, options */)
.then((response) => response.text())
.then((html) => {
const styleStashHTML = html.replace(
/\/static\//g,
`${extensionOrigin}/static/`
);
const body = document.getElementsByTagName("body")[0];
$(styleStashHTML).appendTo(body);
})
.catch((error) => {
console.warn(error);
});
}
}
See Image of Incorrect Injection
Any help or guidance would be very appreciated. Thanks!
Related
I have a Chrome extension (https://chrome.google.com/webstore/detail/apsic-xbench-extension-fo/nhadbgflnognogbicnbeepnbpehlocgc) that suddenly stopped working right after the Chrome 73 update.
The symptom is that if I go to the page where the extension is designed to work (https://translate.google.com/toolkit) and I click on the extension icon, instead of running the background page code, the pop-up menu for the extension appears (as if I had right-clicked the icon).
However, if I load the exact same code locally (not from the store), the Chrome extension runs fine.
The background page console for the extension loaded from the store does not seem to issue any error. If I place a breakpoint for the first line in the onClicked listener for the page action, it does not stop there for the Chrome store extension (and the breakpoint works fine for the extension loaded locally).
Why do I get different behaviors if I load the extension from the Chrome store or I load it locally?
In Chrome 72, the extension worked fine.
Answering own question: I tracked down the issue. It turned out that if the Chrome extension was installed from the Chrome store using Chrome 72, then it did not work right after the update to Chrome 73.
However, if after Chrome 73 is updated, you remove the extension and add it again from the Chrome store, then the Chrome extension works again. Strange but true.
Chrome 73 inject some new security. Just try to move your xHTTP requests to your background script with chrome.runtime.sendMessage and get response with SendResponse callback.
In content or popup script replace ajax with :
chrome.runtime.sendMessage(
{ action: "check", data: {/* params for url */}},
// callback with url response
function(response) {
if( response.success ) {
var myDataFromUrl = response.data;
...
} else {
console.log('Error with `check`,', response.data);
}
}
);
From background script:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
var url = 'https://mysyte.com/';
if(request.action === 'check' ) {
url = url + 'check'
ajax( url, request.data,
success: function( d ) {
sendResponse({success: true, data: d});
},
error : function( d ) {
sendResponse({success: false, data: d});
}
);
}
});
function ajax( url, params, cbSuccess, cbError ) { ... }
I am trying to make an onClick button to download a file from S3 bucket using pre-signet url. The problem comes when I received my url. I want an automatic redirect or kind of. In other words, how can I lunch the download file after getting back my signed url?
this is my document list
The onClick event is on the Download button.
redux action
Redux action call my nodejs route
api route nodejs
Ask for pre-signed url then send it to my redux reducer.
Now in my front-end page, I got my link but I want an automatic redirect to start the file download.
Part of Component
Hope my first post isn't too messy.
I resolved my problem with a redux action. With one click I call my action, who return my pre-signed URL, then automatically click the link. This trigger download event with the original file name when I upload it to S3.
export const downDoc = (docId) => async dispatch => {
const res = await axios({ url: 'myApiCall', method: 'GET', responseType: 'blob' })
.then((response) => {
console.log(response)
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `${docId.originalName}`);
document.body.appendChild(link);
link.click();
});
The other answer does direct DOM manipulation, creates a blob, which looks as though it buffers the whole file in memory before sending it to the user and also creates a new link each time you download. A react-y of doing is:
const downloadFileRef = useRef<HTMLAnchorElement | null>(null);
const [downloadFileUrl, setDownloadFileUrl] = useState<string>();
const [downloadFileName, setDownloadFileName] = useState<string>();
const onLinkClick = (filename: string) => {
axios.get("/presigned-url")
.then((response: { url: string }) => {
setDownloadFileUrl(response.url);
setDownloadFileName(filename);
downloadFileRef.current?.click();
})
.catch((err) => {
console.log(err);
});
};
return (
<>
<a onClick={() => onLinkClick("document.pdf")} aria-label="Download link">
Download
</a>
<a
href={downloadFileUrl}
download={downloadFileName}
className="hidden"
ref={downloadFileRef}
/>
</>)
See here for more info https://levelup.gitconnected.com/react-custom-hook-typescript-to-download-a-file-through-api-b766046db18a
The way I did it was different and has the advantage of being able to see the progress of the download as the file is being downloaded. If you're downloading a large file then it makes a difference UX wise as you see feedback immediately.
What I did was:
When creating the S3 presigned URL I set the content-disposition to `attachment
I used an anchor element to download the actual item <a url='https://presigned-url' download>Download me</a>
Others have mentioned simulating a click within the DOM or React, but another option is to use window.open(). You can set the target attribute to _blank to open a tab, but you do need window.open() inside the click event to prevent popup blockers from stopping the functionality. There's some good discussion on the subject here. I found this to be a better solution than simulating a click event.
Here's an example (though there may be more needed depending on how you fetch the signed_url).
function downloadDocument() {
const signedurlPromise = fetch("/signed_url")
signedurlPromise.then((response) => {
window.open(response.signed_url, "_blank");
})
}
My requirement is to generate pdf view of UI(angular 4 app) using Nodejs api. For that i fetched the entire content from UI and did some pre-processing which will be send to nodejs api. In node api i used html-pdfpackage to generate pdf from the html received. Am able to generate pdf with proper styling as it appears in UI. Below are my questions
What is the safe way to pass the entire UI content(html, styles, bootstrap css) to Node api. (Currently passing as normal string for POC purpose)
How will i return the pdf stream generated by html-pdfpackage back to UI to show it on new tab
html-pdf package
Node api call from angular:
this.http.post('http://localhost:3000/api/createpdf', { 'arraybuff': data }, options)
.catch(this.handleError)
.subscribe(res => {
})
Currently data is normal html string, which i am retrieving by setting body-parser in node.
I don't think there is any better way to pass HTML to server or may be I am not aware of it, but to open the link in new tab use below code
this.http.post('http://localhost:3000/api/createpdf', { 'arraybuff': data }, options)
.catch(this.handleError)
.subscribe(res => {
var blob = new Blob([(<any>res)], { type: 'application/pdf' });
var url = window.URL.createObjectURL(blob);
window.open(url);
})
I am learning to make a chrome extension and deveoped one with a popup browser action and have the following js file (called in popup.html via )
chrome.tabs.executeScript({
code:
"var frameworks = [];" +
"if(!!window.Vue) frameworks.push('Vue.js');" +
"if(!!window.jQuery) frameworks.push('jQuery.js');" +
"frameworks;"
}, (frameworks) => {
document.body.innerHTML = frameworks[0].length
})
But when I am testing it on my website made using both vue and jquery it returns 0, I also checked it on other websites but all behaved same.
What am I doing wrong here?
In Chrome Extension Development we have Background Page Concepts. Is any thing similar available in Firefox Extension Development also. While Developing Chrome Extensions I have seen methods like
window.Bkg = chrome.extension.getBackgroundPage().Bkg;
$(function () {
var view = null;
if (Bkg.account.isLoggedIn()) {
view = new Views.Popup();
$("#content").append(view.render().el);
} else {
$("#content").append(Template('logged_out')());
Bkg.refresh();
}
}...........
Where the main logic are written in Background Page(like isLoggedIn etc) and from the Extension Popup page we are calling Background page. Here for instance the background page is always loaded which manages the session. How can we have similar functionality in Firefox Extension Development.
All communication between the background page (main.js) and content scripts (your popup script) occurs via events. You cannot call functions immediately, so you won't receive any return values, but you can send an event from a content script to the background script that sends an event back to the content script and calls a new function, like so:
main.js partial
// See docs below on how to create a panel/popup
panel.port.on('loggedIn', function(message) {
panel.port.emit('isLoggedIn', someBoolean);
});
panel.port.on('refresh', function() {
// do something to refresh the view
});
popup.js
var view = null;
addon.port.on('isLoggedIn', function(someBool) {
if (someBool) {
// Obviously not code that's going to work in FF, just want you to know how to structure
view = new Views.Popup();
$("#content").append(view.render().el);
} else {
$("#content").append(Template('logged_out')());
addon.port.emit('refresh');
}
});
addon.port.emit('loggedIn', 'This is a message. I can pass any var along with the event, but I don't have to');
You should read this stuff:
Panel
Communicating between scripts