Loading scripts with document.write in an extension's content script - javascript

If a script contains:
document.write("<iframe>ads here</iframe>");
If it's included in the html before the page is requested for load, it might look something like this:
<html>
<!-- stuff !-->
<div><script src="document_write.js" type="text/javascript"></script></div>
<body>
</html>
Loading an html page with the code similar to above will result in the <iframe> being placed in the <div> tag which housed the script. If the document.write() is called after the page load, it will overwrite the whole page.
Chrome extensions' content scripts will also overwrite a page with document.write, or crash it - depending on when in the lifecycle of a page it was called.
Is there a way to insert scripts containing document.write() in Chrome's content scripts?

I had faced the same problem when I was working with some conversion tracking scripts on my ajax site. I ended up overriding document.write, which fixed the problem.
$(document).ready(function() {
document.write = function(str) {
var moz = !window.opera && !/Apple/.test(navigator.vendor);
if (str.match(/^<\//))
return;
if (!window.opera)
str = str.replace(/&(?![#a-z0-9]+;)/g, "&");
str = str.replace(/<([a-z]+)(.*[^\/])>$/, "<$1$2></$1>");
if (!moz)
str = str.replace(/(<[a-z]+)/g, "$1 xmlns='http://www.w3.org/1999/xhtml'");
var div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
div.innerHTML = str;
var pos;
if (!moz) {
pos = document.getElementsByTagName("*");
pos = pos[pos.length - 1];
} else {
pos = document;
while (pos.lastChild && pos.lastChild.nodeType == 1)
pos = pos.lastChild;
}
var nodes = div.childNodes;
while (nodes.length)
pos.parentNode.appendChild(nodes[0]);
};
});

Related

Dynamically add script tag and execute $(document).ready, without using iframe or document.write

I have a string which comes from api response. Now i want to integrate this script and style tag in my application and execute script.
const styleScriptText = '<style type="text/css">#checkoutmodal .checkoutmodal-box{background:#FFF !important}</style><script src="https://someurl/lib/jquery.min.js" type="text/javascript"></script><script type="text/javascript">$(document).ready(function() { console.log("test")});</script>'
I tried to load it using iframe and i could achieve expected result
const iframe = document.createElement('iframe');
const html = `<body>${styleScriptText}</body>`;
iframe.srcdoc = html;
iframe.style.width = "47%";
iframe.style.left = "25%";
iframe.style.height = "100vh";
iframe.style.position = "relative";
document.getElementById("parentId").appendChild(iframe);
But i don't want to use iframe as it has future constraints i have to redirect to bank page and when it comes back whole application is iframed which i don't want
Next i tried it using document.write as below
const html = `<html>
<head>
</head>
<body>
${styleScriptText}
</body>
</html>`;
document.open("text/html", "replace");
document.write(html);
document.close();
But problem with above approach is i am getting below error
A parser-blocking, cross site (i.e. different eTLD+1) script, https:externalscript.js, is invoked via document.write
If i take any other approch $(document).ready function in script doesnot execute.
Tried almost everything but not able to figure out how can i load and run script coming from api response.
Goal here is i need to take a script coming as string and load it in html and execute every script files
this should work, use it at your own risk:
const styleScriptText =
'<style type="text/css">body {background: #000}</style><script type="text/javascript">alert("OK")</' + 'script' + '>';
const insertStyleScriptText = () => {
const div = document.createElement('div');
div.innerHTML = styleScriptText;
div.childNodes.forEach(child => {
document.head.appendChild(child.cloneNode(true));
if (child.tagName === 'SCRIPT')
eval(child.textContent);
});
};
<button onclick="insertStyleScriptText()">insert StyleScriptText</button>

Use inline javascript to set some values [duplicate]

I'm wondering if there is a way to get a handle on the DOM element that contains the script inside it. So if I had:
<script type="text/javascript> var x = ?; </script>
Is there a way that I can assign "x" a reference to the script element that contains "x"?
There isn't a truly safe way.
The closest you can come is to use getElementsByTagName to get all the scripts, get its length, get the last script element, then work from there.
This can fail if the script is deferred or if the script has been dynamically added to the page before another script element.
You could include some marker text in the script element, and then (similar to what David said), you can loop through all the script elements in the DOM (using getElementsByTagName or whatever features your library, if you're using one, may have). That should find the element reliably. That would look something like this (live example):
<body>
<script id='first' type='text/javascript'>
(function() {
var x = "MARKER:first";
})();
</script>
<script id='second' type='text/javascript'>
(function() {
var x = "MARKER:second";
})();
</script>
<script id='third' type='text/javascript'>
(function() {
var x = "MARKER:third";
})();
</script>
<script id='last' type='text/javascript'>
(function() {
var scripts, index, script;
scripts = document.getElementsByTagName("script");
for (index = 0; index < scripts.length; ++index) {
script = scripts[index];
if (script.innerHTML.indexOf("MARKER:second") >= 0
&& script.id !== "last") {
display("Found MARKER:second in script tag #" + script.id);
}
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = msg;
document.body.appendChild(p);
}
})();
</script>
</body>
Note that, like the script above, if you're looking for a script tag marker from within a different script tag, you'll need to handle that. Above it's handled by checking the ID of the script tag, but you can also just break it up in the one you don't want to find, like this (live example):
if (script.innerHTML.indexOf("MARKER:" + "second") >= 0) {
display("Found MARKER:" + "second in script tag #" + script.id);
}

Loading Inline Javascript through an AJAX load through jQuery

I have a similar problem to this question.
Loading Javascript through an AJAX load through jQuery?
I want to load an HTML page into a div container using Ajax and JQuery's .load() . The html page has javascript on it that loads a weather widget from http://www.showmyweather.com/
This is the script:
<script type="text/javascript" src="http://www.showmyweather.com/weather_widget.php? int=0&type=js&country=ca&state=Ontario&city=Hamilton&smallicon=1&current=1&forecast=1&background_color=ffffff&color=000000&width=175&padding=10&border_width=1&border_color=000000&font_size=11&font_family=Verdana&showicons=1&measure=C&d=2013-11-11"></script>
I don't know how to include the widget in the DOM other than placing the script inline the html page. If there is a way to use this script and add it in using $.getscript(); that would be nice, but I can't figure it out.
var element = document.createElement("iframe");
document.body.appendChild(element);
var frame = window.frames[windows.frames.length - 1];
frame.document.write('<scr' + 'ipt type="text/javascript" src="http://www.showmyweather.com/weather_widget.php?int=0&type=js&country=ca&state=Ontario&city=Hamilton&smallicon=1&current=1&forecast=1&background_color=ffffff&color=000000&width=175&padding=10&border_width=1&border_color=000000&font_size=11&font_family=Verdana&showicons=1&measure=C&d=2013-11-11"></sc'+ 'ript>');
This is the way it's done with mootools in Asset.javascript:
var loadScript = function (source, properties) {
properties || (properties = {});
var script = document.createElement('script');
script.async = true;
script.src = source;
script.type = 'text/javascript';
var doc = properties.document || document, load = properties.onload || properties.onLoad;
return delete properties.onload, delete properties.onLoad, delete properties.document,
load && (script.addEventListener ? script.addEventListener("load", load) : script.attachEvent("readystatechange", function() {
[ "loaded", "complete" ].indexOf(this.readyState) >= 0 && load.call(this);
}))
doc.getElementsByClassName("head")[0].appendChild(script);
}
Now you can call loadScript("script url", {document: window.frames[0].document}) and it will load the script in the window. Just need to pass it an external document in options and a script.

How to load iframe content correctly in to <div in main page (same domain iframe)?

I got an ifram that has many links in it and i am trying to copy those link correctly to my main page. My current code copy the links incorectly .For example if the
actual hyper link is like this in iframe:
5
after coping it in to main page the hyper links become like this :
http://ok.mysite24.com/spring/./ok/doit.php
so after clicking to those links from within my main page i go to dead links instead of actual links. is there away to fix this problem by copying iframe content correctly or should i modify my iframe content ?
<script type='text/javascript'>
function getFrameContents(){
var iFrame = document.getElementById('myframe');
var iFrameBody;
if ( iFrame.contentDocument )
{ // FF
iFrameBody = iFrame.contentDocument.getElementsByTagName('body')[0];
}
else if ( iFrame.contentWindow )
{ // IE
iFrameBody = iFrame.contentWindow.document.getElementsByTagName('body')[0];
}
alert(iFrameBody.innerHTML);
document.getElementById('response').innerHTML = iFrameBody.innerHTML
}
</script>
<iframe id ='myframe' src='http://www.mysite.com/ok.php'></iframe>
<div id="response">
<p>getFrameContents! </p>
Before retrieving the innerHTML loop over all links and replace their DOM-href-attribute with their JS-href-property. This will turn the href-attributes into absolute URIs.
//clone the body to keep the original untouched
iFrameBody = iFrameBody.cloneNode(true);
var links = iFrameBody.getElementsByTagName('a');
for (var i = 0; i < iFrameBody.getElementsByTagName('a').length; ++i) {
if (links[i].hasAttribute('href')) {
links[i].setAttribute('href', links[i].href);
}
}
It looks like you want to resolve one relative address to the iframe's source address, which may also be relative. Psuedo code (ish):k
function resolveAddress(source, link) {
if(link.indexOf("../") == 0) {
index--; // go up one directory .//
return resolveAddress(source.substr(0,source.lastIndexOf("/")-1),
link.substr(1, link.length-1));
}
else if(link.indexOf("./") == 0) {
// reduce to current directory ./
return resolveAddress(source.substr(0,source.lastIndexOf("/"),
link.substr(2, link.length-1));
}
return source + link;
}
frame_src = "http://www.mysite.com/ok.php"
link = "./ok/doit.php";
resolveAddress(frame_src, link);
//=> "http://www.mysite.com/ok/doit.php"

Reference script container element?

I'm wondering if there is a way to get a handle on the DOM element that contains the script inside it. So if I had:
<script type="text/javascript> var x = ?; </script>
Is there a way that I can assign "x" a reference to the script element that contains "x"?
There isn't a truly safe way.
The closest you can come is to use getElementsByTagName to get all the scripts, get its length, get the last script element, then work from there.
This can fail if the script is deferred or if the script has been dynamically added to the page before another script element.
You could include some marker text in the script element, and then (similar to what David said), you can loop through all the script elements in the DOM (using getElementsByTagName or whatever features your library, if you're using one, may have). That should find the element reliably. That would look something like this (live example):
<body>
<script id='first' type='text/javascript'>
(function() {
var x = "MARKER:first";
})();
</script>
<script id='second' type='text/javascript'>
(function() {
var x = "MARKER:second";
})();
</script>
<script id='third' type='text/javascript'>
(function() {
var x = "MARKER:third";
})();
</script>
<script id='last' type='text/javascript'>
(function() {
var scripts, index, script;
scripts = document.getElementsByTagName("script");
for (index = 0; index < scripts.length; ++index) {
script = scripts[index];
if (script.innerHTML.indexOf("MARKER:second") >= 0
&& script.id !== "last") {
display("Found MARKER:second in script tag #" + script.id);
}
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = msg;
document.body.appendChild(p);
}
})();
</script>
</body>
Note that, like the script above, if you're looking for a script tag marker from within a different script tag, you'll need to handle that. Above it's handled by checking the ID of the script tag, but you can also just break it up in the one you don't want to find, like this (live example):
if (script.innerHTML.indexOf("MARKER:" + "second") >= 0) {
display("Found MARKER:" + "second in script tag #" + script.id);
}

Categories