Force JavaScript to load additional resources over https - javascript

I am using the Virtual Keyboard (Deprecated) from Google in my current project. Unfortunately it loads some additionale js-resources from an insecure source. Is there a way to force the script to use https instead of http?
The problem is the language file which is used to display the correct letters. The stylesheet e.g. is loaded over https.
Here is my script:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Virtual Keyboard | Google APIs</title>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
</head>
<body>
<input id="language">
<script>
google.load("elements", "1", {
packages: "keyboard"
});
function onLoad() {
var kbd1 = new google.elements.keyboard.Keyboard(
[google.elements.keyboard.LayoutCode.SWEDISH],
['language']);
}
google.setOnLoadCallback(onLoad);
</script>
</body>
</html>
Update:
The only resource, which is loaded over normal http is the language-file, in this case the swedish version.
The language-file is loaded in the function onLoad during the var kb1 = new google.....

Based on the answer to another question
it seems possible to redefine the src property of a <script> element, which is used to load
the javascript code for the swedisch keyboard. If you make sure the following code is executed
before the new google.elements.keyboard.Keyboard call, the http will be replaced by https.
From the network info in the chrome debug console, this indeed seems to load the keyboard
settings over https.
Object.defineProperty(HTMLScriptElement.prototype, 'src', {
get: function() {
return this.getAttribute('src')
},
set: function(url) {
var prefix = "http://";
if (url.startsWith(prefix))
url = "https://" + url.substr(prefix.length);
console.log('being set: ' + url);
this.setAttribute('src', url);
}
});

Just setup your script url like this and it will work!
<script src="//www.google.com/jsapi"></script>
This is the url relative protocol
The type"javascript" part is not anymore necessary as it is taken as javascript if nothing is specified, so save space! MDN element script

Related

How to open url from script html file and run query in new tab

I am trying to open a url from script html file and run the query there. I am able to open the page but my query doesn work in there. Can someone have any idea ?
<html>
<head>
<title></title>
</head>
<body>
<script type="text/javascript">
window.location = "https://www.google.com/"
window.onload = function ()
{
alert("Hello World!");
}
</script>
</body>
</html>
Basicly you can't do this for security reasons. Here is some info about this. Tabs must have the same origin (port, protocol, host) to be able to interact with each other.
In your example problem is that u use onload in your CURRENT window, not in a new one. Here is correct code for https://www.google.com origin:
let windowProxy = window.open('https://www.google.com/');
windowProxy.addEventListener('load', () => {
windowProxy.alert('Hello');
});
But if you want to make interaction between two tabs with same origin there are better descisions: Broadcast API for example.

PouchDB IE11 using file://

I'm trying to get PouchDB's Getting Started Guide working using IE11 from a local file (file://). Is it possible?
It works great using a local http server by adding the following scripts to the header in the index.html file:
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill#8/dist/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/whatwg-fetch#3.0.0/dist/fetch.umd.min.js"></script>
I think my issue is both indexedDB and localStorage seem to be restricted by IE when served using the file:// protocol, however I was able to get localStorage working on it's own using the code below from this post:
!localStorage && (l = location, p = l.pathname.replace(/(^..)(:)/, "$1$$"), (l.href = l.protocol + "//127.0.0.1" + p));
So I thought that would get it working, but even then when i add the pouchdb localstorage adapter I get this error: "AssertionError: .status required, old abstract-leveldown".
Even if that did work, the solution isn't ideal because I need to add file://127.0.0.1 to the trusted sites list.
That's about as far as I've gotten, any help would be appreciated!
Thanks to Zhi Lv - MSFT comment I was able to get the demo working in IE11, however it requires the user to add 'file://127.0.0.1' to the trusted sites list in IE.
After completing the 'Getting Started' guide you'll need to make the following changes.
Update the head element in index.html file:
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>VanillaJS • TodoMVC</title>
<link rel="stylesheet" href="style/base.css">
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill#8/dist/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/whatwg-fetch#3.0.0/dist/fetch.umd.min.js"></script>
<script src="pouchdb/object-assign.js"></script>
<script src="pouchdb/pouchdb-7.2.1.js"></script>
<script src="pouchdb/pouchdb.localstorage.js"></script>
<script src="pouchdb/pouchdb.memory.js"></script>
<!--[if IE]>
<script src="style/ie.js"></script>
<![endif]-->
</head>
You will need to download any missing 7.2.1 pouch-db files and put into a pouchdb directory. object-assign.js can be found here.
Modify the app.js, replace the db variable with these two lines:
!localStorage && (l = location, p = l.pathname.replace(/(^..)(:)/, "$1$$"), (l.href = l.protocol + "//127.0.0.1" + p));
var db = new PouchDB('todos', {adapter: 'localstorage'});
Goto line 8796 of pouchdb.localstorage.js, edit it to set the db.status like this:
function LevelUP (db, options, callback) {
db.status = 'unknown';
if (!(this instanceof LevelUP)) {
return new LevelUP(db, options, callback)
}
Bit of a muck around, but worked for me. Any improvement please let me know.

How to load third-party javascript tag asynchronously which has document.write

We give out a piece of javascript tags such as <script src="http://ours.com/some.js"></script> which site owners put on their site like http://example.com and in this javascript tag we want to dynamically include a third-party js such as which can have document.write in it, but of course if we try to include it by conventional method,
var script_tag = document.createElement('script');
script_tag.type = 'text/javascript';
script_tag.src="http://third-party.com/some.js";
document.getElementById('target').appendChild(script_tag);
we get a warning from browser,
Warning: A call to document.write() from an asynchronously-loaded
external script was ignored.
How do we get around this? Keep in mind, we don't really have control over third-party scripts so we can't change the logic in it. We are looking for some solution which can work across all browsers.
The problem with loading a script on a already loaded document (instead of having the browser ignore the document.write()) is that you would delete all existent HTML. See this example so you can understand exactly what's happening, or for more details look at a documentation page for the document.write() method.
While I know this might not be what you're expecting to get as an answer, I believe you are out of luck since rewriting the script is not an option.
This appears to be a similar question with similar replies.
You can support script injection the correct way by intercepting calls to document.write in this way:
document.writeText = document.write;
document.write = function(parameter) {
if (!parameter) return;
var scriptPattern = /<script.*?src=['|"](.*?)['|"]/;
if (scriptPattern.test(parameter)) {
var srcAttribute = scriptPattern.exec(parameter)[1];
var script = document.createElement('script');
script.src = srcAttribute;
document.head.appendChild(script);
}
else {
document.writeText(parameter);
}
};
Obviously this can be condensed down a bit further, but the variable names are included for clarity.
Source
How about instead of loading the script by appending a script element, you load the contents of the script URL with an AJAX call and then use eval() to run it in the global scope? Here's an example and I did test it to verify that it works:
<!DOCTYPE html>
<html>
<head>
<script>
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
}else{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
window.eval(xmlhttp.responseText); //Indirect call to eval to execute in global scope (http://perfectionkills.com/global-eval-what-are-the-options/)
}
}
xmlhttp.open("GET", "https://third-party.com/test.js", false); //This is synchronous so that any document.write calls don't overwrite the entire page when they get called after the document is finished rendering. For all intents and purposes, this just loads the script like any other script, synchronously.
xmlhttp.send();
</script>
</head>
<body>
<div><h2>Hello World</h2></div>
</body>
</html>
And here are the contents I had in the test.js file:
document.write("This is a test...");
alert("...This is a test alert...");
console.log("...And a console message.");
I made the AJAX request for the script synchronous so that it would be loaded exactly as if it were a regular embedded script tag. If you run it asynchronously, and the script uses document.write after the page has been fully rendered, it clears the DOM and then writes to it... Kind of annoying actually. Lemme know if this works for you. :)
Document.write will not work from async script because document is already loaded when script starts working.
But you can do this:
document.body.innerHTML = document.body.innerHTML + '<h1>Some HTML</h1>';
Another procedure is to change the behavior of document.write() function.
Assume you have the main index.php file:
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
Hello<br>
<div id="target"></div>
<script>
document.write = function(input) {
document.body.innerHTML += input;
}
var doit = function() {
var script_tag = document.createElement('script');
script_tag.type = 'text/javascript';
script_tag.src="http://127.0.0.1:8080/testPlace/jsfile.js";
document.getElementById('target').appendChild(script_tag);
}
</script>
</body>
</html>
and the jsfile.js is like this:
document.write("OK MAN!");
now if you type doit() in the js browser console to execute that function (and the script do what you wrote) then the result would be:
Hello
OK MAN!
In which the html is like this:
<html><head>
<meta charset="utf-8">
</head>
<body>
Hello<br>
<div id="target"><script src="http://127.0.0.1:8080/testPlace/jsfile.js" type="text/javascript"></script></div>
<script>
//That Script which here I removed it to take less space in answer
</script>
OK MAN!</body>
</html>
What is the 3rd party javascript file?
If it's Google Maps JavaScript API v3 then make sure you include "&callback=your_init_funct" in the script URL. Then it will call 'your_init_funct' once the maps library is loaded so that you can begin displaying the map.
Another solution would be bezen.domwrite.js which is available here: http://bezen.org/javascript/index.html
Demo: http://bezen.org/javascript/test/test-domwrite.html
Yes, document.write can't be called from an asynchronously loaded script, because it's detached from the document, so it can't write to it.
You can see the approach used here for the google maps api to get around this problem. So, it is possible some of your 3rd party scripts that you haven't named, could have the similar callback pattern implemented.
https://developers.google.com/maps/documentation/javascript/examples/map-simple?hl=EN
<!DOCTYPE html>
<html>
<head>
<title>Simple Map</title>
<meta name="viewport" content="initial-scale=1.0">
<meta charset="utf-8">
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#map {
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
var map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?callback=initMap"
async defer></script>
</body>
</html>

Problems with SkyScanner Widget in webpage

I am following this example to include a SkyScanner widget on my website:
http://business.skyscanner.net/portal/en-GB/Documentation/Widgets
For some reason I just get an empty div - could this be something to do with the key? When I clicked on the activation link I got from SkyScanner for the widget key, I got a page saying the following:
Description: An application error occurred on the server. The current custom error settings for this application prevent the details of the application error from being viewed remotely (for security reasons). It could, however, be viewed by browsers running on the local server machine.
Details: To enable the details of this specific error message to be
viewable on remote machines, please create a tag within
a "web.config" configuration file located in the root directory of the
current web application. This tag should then have its
"mode" attribute set to "Off".
I have created a web.config file with the following code:
<!-- Web.Config Configuration File -->
<configuration>
<system.web>
<customErrors mode="Off"/>
</system.web>
</configuration>
I've had a look at 'inspect element' on Chrome and get the error
Uncaught TypeError: Object # has no method 'write'
api.ashx?key=[KEY]:1
This is the JS:
var API_KEY = 'b7290ac3-1f9f-4575-88a7-89fed0c61f7f',
MAIN_URL = 'http://api.skyscanner.net/api.ashx?key=' + API_KEY;
function main(){
console.log('loaded module');
var snippet = new skyscanner.snippets.SearchPanelControl();
snippet.setCurrency('GBP');
snippet.setDeparture('uk');
snippet.draw(document.getElementById('snippet_searchpanel'));
};
function newWrite(str) {
$(str).appendTo('body');
}
var oldWrite = document.write;
document.write = newWrite;
function onMainSkyscannerScriptLoad(e) {
console.log('loaded main script');
skyscanner.loadAndWait('snippets', '1', {'nocss' : true}, main);
}
$('button').click(function() {
console.log('getting main script');
$.getScript(MAIN_URL, onMainSkyscannerScriptLoad);
});
I also used this to personalise the widget:
skyscanner.load("snippets","2");
function main(){
var snippet = new skyscanner.snippets.SearchPanelControl();
snippet.setShape("box300x250");
snippet.setCulture("en-GB");
snippet.setCurrency("USD");
snippet.setColourScheme("classicbluelight");
snippet.setProduct("flights","1");
snippet.setProduct("hotels","2");
snippet.setProduct("carhire","3");
snippet.draw(document.getElementById("snippet_searchpanel"));
}
skyscanner.setOnLoadCallback(main);
Skyscanner B2B support engineer here. Are you still having trouble with this?
For the quickest response, please contact us here: http://business.skyscanner.net/portal/en-GB/Home/Contact
The first thing to check is that the file is on a web server (localhost is fine), and not loaded from the desktop (or local disk in general). This is because some of the Skyscanner JS code looks up other files using URLs like //api.skyscanner.net. In other words, saving the file to the desktop and opening it from there will not work (will show as an empty div). Here's the most basic example of drawing a widget. Can you try putting this in a file and accessing it through a server? If it works we can build on it :)
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" >
<head>
<script type="text/javascript" src="//api.skyscanner.net/api.ashx?key=b7290ac3-1f9f-4575-88a7-89fed0c61f7f"></script>
<script type="text/javascript">
skyscanner.load("snippets","2");
function main(){
var snippet = new skyscanner.snippets.SearchPanelControl();
snippet.setShape("box300x250");
snippet.setCulture("en-GB");
snippet.setCurrency("USD");
snippet.setProduct("flights","1");
snippet.setProduct("hotels","2");
snippet.setProduct("carhire","3");
snippet.draw(document.getElementById("snippet_searchpanel"));
}
skyscanner.setOnLoadCallback(main);
</script>
</head>
<body>
<div id="snippet_searchpanel" style="width: auto; height:auto;"></div>
</body>
</html>

Adding complex HTML using a Chrome content script

I am working with Chrome extension's content script to create a complex display that is added on web pages.
I have first tested it directly integrated on a website, but now I need to put it in an extension.
The thing is that the content script API for Chrome only allows to inject javascript. That means that, to inject complex HTML layouts I would need to write it entirely with JS objects, which is long to write, hard to maintain and absolutely not designer-friendly.
I'm wondering if anyone know or can think of a clever way to get a better workflow on this.
It's relatively easy to add whole web pages by having your content script inject them in an iframe. Just follow these guidelines:
Place the *.htm or *.html files in your extension's source folder(s).
Place any *.css and *.js files, that the HTML uses, in the extension folder(s) too.
Declare the HTML file(s) as resources. EG:
"web_accessible_resources": ["Embedded_Hello_world.htm"]
Do not use any inline, or external server, javascript in your HTML files. This avoids problems with the Content Security Policy (CSP).
This question doesn't cover communicating with the page/iframe, but if you want to do that, it is a bit more involved. Search here on SO; it's been covered many times.
Example:
You can see this in action by:
Creating a new extension folder.
Download jQuery into it.
Create the 5 files as specified below.
Load the unpacked extension (You can see similar steps in this answer.)
Reload this page in Chrome; you'll see the "Hello World" page, embedded at the top.
Create these files in the extension folder:
manifest.json:
{
"manifest_version": 2,
"content_scripts": [ {
"js": [ "iframeInjector.js" ],
"matches": [ "https://stackoverflow.com/questions/*"
]
} ],
"description": "Inject a complete, premade web page",
"name": "Inject whole web page",
"version": "1",
"web_accessible_resources": ["Embedded_Hello_world.htm"]
}
iframeInjector.js:
var iFrame = document.createElement ("iframe");
iFrame.src = chrome.extension.getURL ("Embedded_Hello_world.htm");
document.body.insertBefore (iFrame, document.body.firstChild);
Embedded_Hello_world.htm:
<!DOCTYPE html>
<html><head>
<title>Embedded Hello World</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link href="HelloWorld.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="HelloWorld.js"></script>
</head><body>
<p>Hello World!</p>
</body></html>
HelloWorld.css:
body {
color: red;
background-color: lightgreen;
}
HelloWorld.js:
$(document).ready (jQueryMain);
function jQueryMain () {
$("body").append ('<p>Added by jQuery</p>');
}
This may be better, no external library and no iframe. Is nearly the same as iautomation solution.
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var div = document.createElement('div');
div.innerHTML = this.responseText;
document.body.insertBefore(div, document.body.firstChild);
} else {
console.log('files not found');
}
};
xhttp.open("GET", chrome.extension.getURL("/content.htm"), true);
xhttp.send();
I had the same issue, that my extension heavily relies on script templates
Here's what I did:
create templates.html to store script templates in
add templates.html to the web_accessible_resources as in the the above answer^^
access templates.html from content.js with xhr and parse with jQuery
manifest.json
"web_accessible_resources": ["templates.html"]
templates.html
<script id="template1" type="text/template">
<div class="template1">template1</div>
</script>
<script id="template2" type="text/template">
<div class="template2">template2</div>
</script>
content.js
function getTemplates(){
return new Promise(function(resolve){
$.ajax({
url: chrome.extension.getURL('/templates.html'),
success: function(data) {
var $templates = $('<div></div>').append($.parseHTML(data)).find('script'),
templates = {};
$templates.each(function(){
templates[this.id] = this.innerHTML;
});
return resolve(templates);
}
});
});
}
getTemplates().then(function(templates){
console.log(templates.template1); //<div class="template1">template1</div>
});

Categories