I have built a mobile webapp in html/js and I am using the 'window.onerror' to track client side js errors and report them to the server.
Recently I noticed that I receive many reports of ScriptError on line 0.
A quick google search brought me to discussions like Cryptic "Script Error." reported in Javascript in Chrome and Firefox and https://ravikiranj.net/posts/2014/code/how-fix-cryptic-script-error-javascript/
For most people such errors seem to be triggered by failures in scripts that are included from a foreign origin. But unfortunately that's not the case for me.
After many hours of debugging I came up with the following minimal example to reproduce the error:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Test</title>
<meta name="google" content="notranslate"/>
<script type="text/javascript">
function init() {
window.onerror = function() {
alert(JSON.stringify(arguments));
}
}
</script>
</head>
<body onload="init()">
<h1>Hello</h1>
<svg width="300" height="300">
<a href="#">
<rect x="50" y="50" width="200" height="200" />
</a>
</svg>
<p>
Link
</p>
</body>
</html>
As we can see it's a simple html page with no javascript at all except for the onerror handler itself.
In Safari (ios 12.4.1) no error is triggered. But in both Google Chrome for iOS and Firefox for iOS a ScriptError on line 0 is reported each time the link inside the <svg> element is tapped - but not when tapping the link outside the <svg>. Note that no js event handler is attached to the <svg>.
A live version of the code is online at https://static.laszlokorte.de/scripterror.php
I know that all third party browsers on iOS actually just use safari under the hood extending in with a few custom features. I suspect that those custom features cause the error but I have no idea how to track it down further.
Why does clicking a link inside a plain svg element causes an error in iOS Chrome and Firefox but not in Safari? Is there a way to connect the Safari Webdev tools to mobile browsers other than native Safari?
Update
After taking a deeper look at both the Chrome(iOS) and Firefox(iOS) source code I now found the cause of the problem. Both browsers inject javascript code that runs on touchstart and processes the event's target node. Both browsers check if an '' or '' element has been touched and try to access their their href or src attribute respectively. They both then try to send the attributes values via webkit.postMessage to the host application.
The error seems to be caused by the fact that an <a> element inside an <svg> is actually an SVGAElement and it's href attribute is not a String but an SVGAnimatedString which (other than String) can not be cloned to be sent to another process.
The corresponding code for Firefox can be found here:
https://github.com/mozilla-mobile/firefox-ios/blob/826cb396a9eb225b7eb667b47b245e2d98d26ed8/Client/Frontend/UserContent/UserScripts/AllFrames/AtDocumentEnd/ContextMenu.js#L14-L35
The code for Chrome is here:
https://github.com/chromium/chromium/blob/master/ios/web/web_state/js/resources/all_frames_context_menu.js#L136-L139
So for Chrome the solution is actually simple because it allows to explicitly opt-out of all this by setting the css property -webkit-touch-callout: none (You can see the check for that in the linked file). For example like this:
#namespace svg url(http://www.w3.org/2000/svg);
svg|a {
-webkit-touch-callout: none;
}
For Firefox I have not found a easy solution yet.
Update 2
For Firefox I now came up with the following monkey patch solution:
(function() {
var origClosest = SVGElement.prototype.closest;
SVGElement.prototype.closest = function (sel) {
var c = origClosest.apply(this, arguments);
if(c && sel === 'a' && c.namespaceURI === this.namespaceURI) {
return c.parentNode && c.parentNode.closest(sel);
} else {
return c;
}
};
})();
I override the closest method (that Firefox itself uses to find the touched <a>) on an SVG elements with my own method that skips all <a> elements that belong to the SVG namespace.
Additionally I submitted an issue in the Firefox repo at Github and an issue for Chromium.
Related
I created a little demo here: https://codepen.io/min-width/pen/LYVVLwK
But just in case that gets lost in time, here is the JavaScript from that page (it's constructing a script tag with a type of "module" and injecting it into the page):
let value = `<script type="module">
let element = document.createElement("p");
element.appendChild(document.createTextNode("The JavaScript executed."));
document.querySelector("body").appendChild(element);
</scr` + `ipt>`;
let compiled =
document.createRange().createContextualFragment(value),
body = document.querySelector('body');
body.appendChild(compiled);
When working, it should say "The JavaScript executed" in the body of the page. This works fine in most browsers (and I accept that it doesn't work in IE since IE doesn't support modules at all).
My issue is that this does not work in Edge, even though Edge does have support for JavaScript modules: https://caniuse.com/#feat=es6-module
Edge just doesn't seem to like when modules are injected dynamically. Is there some client-side workaround for this? I currently have it working with a server-side check for "Edge" in the user agent (in which case I render the module immediately in the HTML rather than injecting it with JavaScript), but that doesn't feel like an optimal solution.
In case you are wondering, my use case is that I preload some JavaScript modules, then I inject them into the page when the user first interacts with the page (a page speed optimization that PageSpeed Insights seems to like).
Edit: Since somebody asked, here is the Edge version number info (from the settings):
It shows Edge 44 and EdgeHTML 18.
I tried to modify your code and it works with MS Edge 44 version.
<!DOCTYPE html>
<html>
<head>
<title>Demo page</title>
</head>
<body>
<script>
let se =document.createElement('script');
se.setAttribute('type', 'module');
se.innerHTML='var para = document.createElement("P"); para.appendChild (document.createTextNode("The JavaScript executed.")); document.querySelector("body").appendChild(para);';
let body = document.querySelector('body');
body.append(se);
</script>
</body>
</html>
Output in Edge browser:
With the update of Chrome from 43 to 44, the following syntax no longer works:
window.location.href = "javascript:alert()"
I'm trying to load the contents of a page from local storage. I'm doing this by returning the page contents as the result of a javascript function call. I need to specify a URL as the target for window. Rather than specifying http://...., I used to be able to specify javascript as the scheme in the URL and specify the name of the function to invoke.
Apparently, Google took this feature away in version 44. Has anyone run into this and figured out an alternative?
For others finding this page, it's an official bug now
The syntax is working for page itself, but not working for an iframe. This used to work in versions prior to Chrome 44. Even in Chrome 44, document is built and all events are fired, but the page won't be visible in iframe. The frame will start showing the contents if style attribute position for iframe is removed and added again. Here is the sample code illustrating workaround for Chrome 44.
chrome44.html
<html>
<script>
function getFrame(theFrameID)
{
return document.getElementById(theFrameID);
}
function loadFrameSource()
{
var aFrame = getFrame("frm");
aFrame.src = "chrome44frame.html";
// had to add the following line as a workaround for Chrome 44
aFrame.style.position = "absolute";
}
</script>
<body onload="loadFrameSource()">
<iframe id="frm" style="position : absolute;">
</body>
</html>
chrome44frame.html
<html>
<script>
function getHtml()
{
// Hard coding frame content for illustrating here, but actual script does more
return "<html> <body onload=\"document.getElementById('evnt').innerText='onload event fired.';\">href is Successful in changing html. <div id='evnt'>, but onload event didn't fire.</div></body> </html>";
}
function loadPage()
{
document.location.href="javascript:getHtml('href')";
// Had to add the following line as a workaround for Chrome 44
window.parent.getFrame("frm").style.position = "";
}
loadPage();
</script>
</html>
OS version: Windows 8.1
MSIE (Microsoft Internet Explorer) version: 11.0.9600.16476 (KB2898785)
I have a simple HTML code that is creating my ActiveX Control (AX).
This code always worked in all previous versions of MSIE, but with the new MSIE-11 on Windows 8.1 it stopped working.
I bring here the HTML code that demonstrates the problem.
Please note:
The static HTML code that creates my AX (<OBJECT>) always work,
The dynamic JavaScript code that creates my AX (document.createElement) fails on MSIE 11
<BODY>
<BR>
<BR>
<INPUT TYPE="BUTTON" VALUE="CreateAX" OnClick="CreateAX()" STYLE="WIDTH: 89PX" />
<BR>
<BR>
<!-- This always work: -->
<OBJECT ID = "MyCtrl"
CLASSID = "CLSID:F417FD96-3D17-4556-80AA-F7CEEE1E3FD8"
WIDTH = 100
HEIGHT = 100>
</OBJECT>
<BR>
<BR>
</BODY>
<SCRIPT LANGUAGE="javascript" TYPE="text/javascript">
function CreateAX()
{
// This will not work on MSIE 11
var playbackObjectGlobal = document.createElement('object');
playbackObjectGlobal.setAttribute('id', 'MyCtrl2');
playbackObjectGlobal.setAttribute('classid','CLSID:F417FD96-3D17-4556-80AA-F7CEEE1E3FD8');
playbackObjectGlobal.setAttribute('width', '200');
playbackObjectGlobal.setAttribute('height', '200');
playbackObjectGlobal.setAttribute('hidden', 'false');
document.body.appendChild(playbackObjectGlobal);
}
</SCRIPT>
After debugging I found that my boject does get created but without a window (m_hWnd==NULL) thus all GUI related features and events are disable.
Anyone is familiar with this problem?
Thanks, PazO
After opening a call with Microsoft support I managed to resolve this issue. I will bring here Microsoft answer as is:
With further debugging, we seems to be hitting known behavior and design change in dynamically adding objects.
Earlier, certain actions could force an object to instantiate prior to entering the control tree, which was breaking a number of sites.As part of fixing that, we now fully commit an object when it enters the tree and its identity cannot be changed after that point. Because the repro page doesn't set the CLSID property until after the object enters the tree, the value is ignored.If the step to set the CLSID on the control is moved before the object is inserted into the tree, the control should instantiate correctly.In addition to the above changes, we also removed “Hidden” attribute as it was setting the object in hidden mode.
Before fix:
function CreateAX()
{
var playbackObjectGlobal = document.createElement('object');
playbackObjectGlobal.setAttribute('id', 'MyCtrl2');
playbackObjectGlobal.setAttribute('classid','CLSID:F417FD96-3D17-4556-80AA-F7CEEE1E3FD8');
playbackObjectGlobal.setAttribute('width', '200');
playbackObjectGlobal.setAttribute('height', '200');
playbackObjectGlobal.setAttribute('hidden', 'false');
document.body.appendChild(playbackObjectGlobal);
}
After fix:
function CreateAX_New()
{
var playbackObjectGlobal = document.createElement('object');
// *Change-1* Next two lines swiched places:
playbackObjectGlobal.setAttribute('classid','CLSID:F417FD96-3D17-4556-80AA-F7CEEE1E3FD8');
playbackObjectGlobal.setAttribute('id', 'MyCtrl2');
// *Change-2* Object is appended before sizes are set:
document.body.appendChild(playbackObjectGlobal);
playbackObjectGlobal.setAttribute('width', '200');
playbackObjectGlobal.setAttribute('height', '200');
// *Change-3* The 'Hidden' tag was removed
}
Now all is working for me on Windows 8.1, MSIE-11.
ActiveX is highly restricted on Windows8/IE11 when in Metro mode.
It is locked down to only allowing a very limited set of controls, almost all of which are internal MS controls.
If you need to use any ActiveX controls that aren't in that list, it will only work if you're using Win8 in desktop mode. Even then, you may still need to manually set the browser settings to enable ActiveX.
See this post for more info.
I noticed a change in Firefox 25 beta, soon to be released: Go to chrome://browser/content/devtools/webconsole.xul, view source, and in the current Firefox (24) you see:
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="devtools-webconsole"
macanimationtype="document"
fullscreenbutton="true"
title="&window.title;"
browserConsoleTitle="&browserConsole.title;"
windowtype="devtools:webconsole"
width="900" height="350"
persist="screenX screenY width height sizemode">
<script type="text/javascript" src="chrome://global/content/globalOverlay.js"/>
<script type="text/javascript" src="webconsole.js"/>
<commandset id="editMenuCommands"/>
...
In Firefox 25, however, the reference to webconsole.js is replaced with an inline script, function goUpdateConsoleCommands() with two goUpdateCommand calls.
Devtools-tweaks is not working for the network popup now, because the webconsole.js link no longer exists on that window. The code here has nothing to modify to change the popup: https://github.com/programmin1/DevTools-Tweaks/blob/master/content/netWinOverlay.js
Is there any easy way to re-hook this functionality into the network popup? Will JSON/xml parsing be introduced in Firefox soon, making this unnecessary?
The WebConsoleFrame stuff was ported to the add-on SDK loader in bug 877262.
Judging from the code, the following should give access to the WebConsoleFrame the add-on "uses":
var {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
var {WebConsoleFrame} = devtools.require("devtools/webconsole/webconsole");
As for the second part of your question: Will the devtools support the same stuff natively?
I'm not sure. You should get in touch with the devtools team and ask them and have a look at the existing bugs: Open devtools bugs containing "json". And/or file a new enhancement bug.
In firefox 19 and firebug 1.X I encountered a strange bug when trying //#sourceurl.
Basically I'm dynamically adding script tag through dom manipulation as you can see in the sample below. This does not work.
Maybe it's a limitation of ff but I find it odd that it works in chrome and not in ff.
Can you confirm this and do you have any bypass to this bug?
Ps: I don't want to use global eval() as it crash in ie when using document.write
<html>
<head>
<script type="text/javascript">
var count=0;
function addNewScriptToHead()
{
var newScriptElem;
var newScriptText;
newScriptElem = document.createElement('script');
newScriptElem.setAttribute('type', 'text/javascript');
newScriptElem.setAttribute('id', '' + count);
newScriptElem.text= 'console.log("Yay !");//# sourceURL=root/test'+count++ +'.js';
document.body.appendChild(newScriptElem);
};
</script>
</head>
<body>
<button onclick="addNewScriptToHead()">add script</button><br><br>
</body>
</html>
Experimentation has lead me to believe the following:
As of version 20.0, FF still does not support //# sourceURL directly in its inbuilt web console.
//# sourceURL does work with the Firebug addon in FF, but not completely as expected/hoped.
A. It only works for eval. It doesn't work at all for appended
script nodes like in the original question.
B. Errors will have a line number and the URL, and you can click
the error to see the line of code. However, console.log does not
seem to be affected and shows no line number or URL.
Testing this feature from within FF's web console is not advised. I got different results than testing directly in HTML at least some of the time.