I'm trying to create a simple website blocker in chrome, but after blocking a URL (for example https://example.com), I'd like to render my own custom HTML file that tells the user that the page has been blocked. It's important to me that when the custom blocked page is shown, the URL is still example.com.
I figured out how to block the URL using the webRequest API, but the only way I can see to show a custom page saying that the website has been blocked is to redirect the user, which obviously won't work for my goals.
Instead of redirecting/blocking try stopping the page and show your UI as standard DOM.
background script:
chrome.webRequest.onHeadersReceived.addListener(info => {
chrome.tabs.executeScript(info.tabId, {
file: 'stop.js',
runAt: 'document_start',
});
}, {
urls: ['*://foo/*'],
types: ['main_frame'],
});
stop.js - running as a content script
window.stop();
// reset DOM
document.write('<!DOCTYPE html><html>');
// create your UI
document.body.textContent = 'blocked';
You can load your UI in an iframe pointing to an html file declared in web_accessible_resources.
I had the same issue and I was able to fix it by specifying my custom html page in "web_accessible_resources" in the manifest.
For further reading, check here
Related
I am trying to open my web-extension options page from an injected button via a content-script onto a page. Here is the setup:
manifest setting:
"options_ui": {
"page": "options/options.html",
"open_in_tab":true
},
"web_accessible_resources": ["icons/icon.png", "icons/icon64.png","options/options.html"]
content-script.js:
Settings
What am I missing here? Also, I know moz-extension: might not be the best option for cross-browser operation but not sure what should be the correct namesapce?
EDIT:
I am using a fixed id in manifest as :
"applications": {
"gecko": {
"id": "{adacfr40-acra-e2e1-8ccb-e01fd0e08bde}"
}
},
Your content script, as shown, is actually a chunk of HTML and not JavaScript, as expected. So it wouldn't work. Perhaps that's not your actual code, just what you create?
But suppose you do adjust the content script to add a button and have a listener attached (out of scope for this question, I think). How to open the options page?
The canonical way is to call browser.runtime.openOptionsPage(), but that API can't be called from a content script.
Two options:
Stick with openOptionsPage(). In that case, you need a background (event) page that listens to Messaging, and then signal from the content script that you want the options page open.
The advantage of this approach is that you don't need to make the options page web-accessible.
If you insist on directly opening the page from the content script / a tag, you can, but you'll need to get the dynamically-allocated UUID for your extension instance.
The URL for your options page is not fixed, but should be obtained with browser.runtime.getURL("options.html"), and that URL should be used in the link creation.
This method requires declaring it as web-accessible.
I'm making a Chrome App, and i'm using the web view tag, which is similar to an iframe.
Is there a way to load halfway down the webpage that's inside the web view?
I have tried:
<webview onLoad="window.scroll(0, 150)" class="test" src="http://example.co.uk/test.aspx"></webview>
But it seems that would be far to easy. I'm not convinced its even possible.
Thanks
Assuming for a moment that code would execute (it won't because of the CSP), window would refer to the app's page, not the embedded page.
You need to execute the code in the context of the embedded page.
If you worked with Chrome extensions before, you would know that the part interacting with web content is called a Content Script.
Apps have a similar concept for <webview> elements. You can either declare in advance which pages should get which content script while they load, or you can explicitly load a content script.
So, suppose you have a file content.js with the following contents (excuse the pun):
window.scroll(0, 150);
Also, assume you have a script app.js running inside your app page, and a variable webview is a reference to the <webview> in there.
Then, you can make the webview execute it:
Declaratively, by calling webview.addContentScripts before loading the page (e.g. before setting src from app.js code):
// Assuming <webview id="wv"></webview>
var webview = document.getElementById("wv");
webview.addContentScripts([{
name: "ExampleRule",
matches: ["http://example.co.uk/*"], // can be as narrow as you wish
js: ["content.js"]
}]);
webview.src = "http://example.co.uk/test.aspx";
Explicitly, when the page is already loaded:
// Assuming <webview id="wv" src="http://example.co.uk/test.aspx"></webview>
var webview = document.getElementById("wv");
webview.executeScript({file: "content.js"});
It is, of course, possible to make content.js much more sophisticated (for example, receive commands from the app) and/or more precisely control the timing of your operation - but those are at this point generic content script questions for which you can find solutions elsewhere or ask a new question.
I currently have a PDF embedded into an webpage. The PDF has several hyperlinks within it, but the links open in the parent frame when clicked. This takes the user to a new page with no option to return to the original PDF (navigation turned off). I can't seem to figure out how to get the links to open in a new window.
Sample PDF
<embed src="https://www.antennahouse.com/XSLsample/pdf/sample-link_1.pdf" type="application/pdf" />
Problem
Clicking second(External) link on this PDF will navigate to
another website within the same tab.
Working Plunkr
The PDF documents were originally created in PowerPoint, which prevents me from adding the proper href attribute. Is there a way to modify links within a PDF to include target="_blank"?
If not, I'm wondering if there is a something I can include within the html code that would universally control how links open.
Any suggestions are welcome.
Thanks.
Just to quickly qualify this answer, this should work for modern browsers, and only if the PDF and PDFJS are hosted on the same domain that you are embedding it in.
The trick here is to force the use of PDF.js and override Chrome's default behavior of rendering it like an extension. That way you get an iframe with html elements you can manipulate if you don't try and do it CORS. If this is for a CORS related use case, you are pretty much out of luck as editing a CORS pdf is kind of a security risk and rightfully disallowed.
You are going to want to start by getting a site set up following the example of "forced" usage. Resources here:
https://github.com/mozilla/pdf.js/wiki/Setup-PDF.js-in-a-website
https://pdfobject.com/examples/pdfjs-forced.html
You'll need to run it on a webserver as well, because it won't server correctly off the filesystem alone.. Hooray more CORS issues.
Then, you'll set up your page and call it like this (based on #Paco's gist)
<html>
<head>
<title>test pdf</title>
</head>
<div id="pdf"
style="width:900px; height:500px"></div>
<script src="https://pdfobject.com/js/pdfobject.min.js"></script>
<script>
var options = {
pdfOpenParams: {
page: 1,
view: "Fit",
toolbar: 0
},
forcePDFJS: true, //*** Forces the use of PDF.js instead of default behavior
PDFJS_URL: "web/viewer.html" //*** Required to use PDF.js
};
PDFObject.embed("../pdf-test.pdf", "#pdf", options);
document.querySelector('#pdf iframe').onload = function () {
//can try and hook the PDF.js event for rendering completed and call it then instead.
//https://stackoverflow.com/questions/12693207/how-to-know-if-pdf-js-has-finished-rendering
setTimeout(function () {
document.querySelector('#pdf iframe').contentDocument.querySelectorAll('a:not(.bookmark)').forEach(function (a, i) {
a.setAttribute('target', '_blank');
})
}, 5000)
}
</script>
</body>
</html>
Using Adobe Acrobat right click on the link properties. Click the Actions tab. If there are any actions listed delete them and then select Run a Javascript. Click add. A box will appear for you to add the following javascript. app.launchURL("http://www.yourlink.com", true);
The crossrider sidepanel is simply an iframe (you can use js-injected html, but I'm interested in using an iframe to reduce interference with the rest of the page). I'm having trouble getting any interaction between my browser extension and the iframe at all.
I see no point at all in adding a sidepanel with an extension unless you can do some basic JS communication. In this case I want a few options, checkboxes etc, in the iframe which control the extension. Since this plugin exists, I'm assuming there must be a way.
Ideally I'd like to have some basic input handling js in the child iframe and have it send back the odd save/load command. Is the answer really some form of message passing? If so which API should I be using here?
I believe this is related: Accessing iframe from chrome extension
[EDIT]
OK, so I've tried a few things...
It seems the expected usage is to host the iframe's html content somewhere. A bit strange considering it should be local and part of the extension. What happens if you want to view some pages offline?? This is just silly and I'm dismissing it as an option. Why waste resources hosting something that should just be available locally.
The alternative is to provide the HTML that goes in the sidebar. Note that this HTML doesn't get put in an iframe. I like the idea of an iframe because it keeps the CSS and JS very separate, so there's minimal interference between a page and your extension.
So I tried creating an iframe via the html sidebar attribute with and ID and injected the content after a 100ms delay using myiframe.contentWindow.document.open/writeln/close(). This works fine on chrome but fails in firefox with a security error (The operation is insecure on open()).
Another way is to provide the iframe content via the src url (for the sidebar I use a data address for the url attribute): Html code as IFRAME source rather than a URL. This works in firefox but results in a CORS error in chrome: The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "data". Protocols must match. and Warning: Blocked a frame with origin "http://localhost" from accessing a cross-origin frame. Function-name: appAPI.message.addListener
These CORS issues strike me as really daft. It's all my code coming from the same extension, injected into the same page. There's no cross origin happening, I created the damn thing. If I have the power to change the origin, then it's not secure in the first place so why bother.
Assuming you are using the url sidebar property to load your sidebar's HTML (i.e. a hosted web page), you can use the extension's Run in Iframe feature to communicate between the iframe extension and the parent window's extension.
To achieve this, first enable the extension to run in iframes (Settings > Run in Iframes) and then you can use the extension.js to load your sidebar and handle messaging. For example, the following code loads a page that has a button with identification btnSave:
Hosted web page file:
<html>
<head>
</head>
<body>
<div id="mySidebar">
My sidebar form
<br />
<button id="btnSave">Save</button>
</div>
</body>
</html>
extension.js file:
appAPI.ready(function($) {
// Check if running in iframe and the sidebar page loaded
if (appAPI.dom.isIframe() && $('#mySidebar').length) {
// Set click handler for button to send message to parent window
$('#btnSave').click(function() {
appAPI.message.toCurrentTabWindow({
type:'save',
data:'My save data'
});
});
// End of Iframe code ... exit
return;
}
// Parent window message listener
appAPI.message.addListener(function(msg) {
if (msg.type === 'save') {
console.log('Extn:: Parent received data: ' +
appAPI.JSON.stringify(msg.data));
}
});
// Create the sidebar
var sidebar = new appAPI.sidebar({
position:'right',
url: 'http://yourdomain.com/sidebar_page.html',
title:{
content:'Sidebar Title',
close:true
},
opacity:1.0,
width:'300px',
height:'650px',
preloader:true,
sticky:true,
slide:150,
openAction:['click', 'mouseover'],
closeAction:'click',
theme:'default',
scrollbars:false,
openOnInstall:true,
events:{
onShow:function () {
console.log("Extn:: Show sidebar event triggered");
},
onHide:function () {
console.log("Extn:: Hide sidebar event triggered");
}
}
});
});
However, if you are using the html sidebar property to load your sidebar's HTML, then this solution will not work since the extension does not run in this context. However, you may be able to utilize the methods described in the StackOverflow thread you quoted to communicate with the parent window (this would be browser specific) that in turn can communicate with the extension using our CrossriderAPI event.
[Disclaimer: I am a Crossrider employee]
I am using the following code to implement a hotkey for my Chrome extension:
// content script:
window.addEventListener("keydown", function(event) {
if (event.ctrlKey && event.keyCode == 81) {alert('Ctrl+Q Pressed!');}
}, false);
Since inserting the following line in the manifest file it has worked in most situations, even when iFrames are selected:
...
"content_scripts": [
{
"all_frames": true
...
For example with http://danish.typeit.org, the hotkey even now works when typing with that and facebook personal messages too. Everywhere it seems except when composing emails using gmail, yahoo mail or gmx. While the composition box is selected, the hotkey doesn't work. This is a disappointment as I was hoping people would use my extension to aid with writing emails. And Twitter, it does not work when typing on Twitter, either.
I think the reason that the content script is not being loaded for the editor iframe even when you have all_frames specified is that the content scripts only get applied to iframes that are present in the markup when the containing page is loaded.
In the case of the gmail page, there are multiple iframes on the page, some that are present in the markup of the containing page (which the content script gets applied to) and then the one for the editor, which is created by JavaScript after the page is loaded.
Even if you were to try and wait for the JavaScript on the page to load the iframe for the editor the JavaScript in the content script would not be able to access it because accessing the contentWindow object of an iframe is not allowed in content scripts.
A long shot might be to inject a JavaScript file into the DOM that then executes the logic you have in the content script.
The content script might be something like:
document.body.appendChild(document.createElement("script")).src = "http://external/file/javascript.js";
Then the contents of the remote JavaScript file could try and get access to the editor iframe, you might need to use a setInterval until the element is created.
// These are the IDs gmail uses, each mail app would be different
document.getElementById("canvas_frame").contentWindow.document.getElementById(":nt")