we are facing a pretty strange problem in our web aplication on IE11 (other IE versions work fine).
The application is based on SmartGWT ( http://www.smartclient.com/product/smartgwt.jsp ) - GWT wrapper on SmartClient javascript framework.
IE11 goes into never ending cycles.
It happens very randomly and we have no steps to reproduce it. The complexity of our application makes it impossible to post a sample of the code.
Most often it happens when users work with the application, then they minimize the browser window and after some time they restore it and try to continue working.
The never ending cycles are caused by strange comparison results, when expression 'true === true' results in false.
The comparison code is:
$wnd.isc.isA.Canvas(obj) === true
The code is executed in scope of iframe containing javascript compiled by GWT.
$wnd is the main(top) window of the web application where the SmartClient javascripts are loaded.
The isc.isACanvas(..) is a method returning true or false, depending on whether the object passed as parameter is of Canvas type - Canvas is a special class from SmartClient framework not the HTML Canvas element.
isc.isA.Canvas = function (object) {
return (object != null && object._isA_Canvas);
}
_isA_Canvas is set on object to true (boolean true not 'true' as a string) when the object is being created.
I have added some testing code to the part where the problematic comparison is used - here's a simplified version:
var trueCheckCount = 0;
function isCreated(id){
var obj = $wnd.window[id]; // objects are stored in window by id
var comparisonResult = $wnd.isc.isA.Canvas(obj) === true;
if (!comparisonResult ) {
if ($wnd.isc.isA.Canvas(obj) == true) {
alert("TRUE != TRUE (was OK: " + trueCheckCount + "x before)");
} else {
trueCheckCount++;
}
}
return comparisonResult;
}
In different test runs, the alert was shown after different number of passes. E.g. on the first run it passed 358 698 times, on the second run it passed 330 125 times …
Does anybody have any idea what could be the problem?
How can 'true !== true' ever happen?
Environment:
IE11 (Windows7/8)
SmartGWT: v9.0p_2014-03-02/LGPL Development Only (built 2014-03-02)
GWT: 2.4
Some additional debugging information can be seen on screenshot:
https://drive.google.com/file/d/0B8h18b-AMFzXa3NZZ2dOX2txb1k/edit?usp=sharing
The problem was caused by a bug in IE11.
MS released the fix. It's included in December Internet Explorer Cumulative Update KB3008923.
Installing this update solved our problem.
The technical details we got were:
The bug in question is related to reclaiming of JIT functions and cross-site thunks.
A function with a cross-site thunk is getting reclaimed.
We then change the entryPoint to the InterpreterThunk, losing the cross-site thunk in the process.
Marshalling isn't done when calling this function.
In the repro in question, we end up with a Boolean True object from a different scriptContext, which doesn't match the one in the current scriptContext when comparing with ===.
The problem is 99% in SmartGWT. I recommend you to report this issue to the SmartGWT team and use the latest GWT compiler of course. We dealt with SmartGWT before and from my experience I should say it was a pretty buggy library back then.
You have two issues in your debug code that could be giving you a false impression of the issue:
if ($wnd.isc.isA.Canvas(obj) == true) {
alert("TRUE != TRUE (was OK: " + trueCheckCount + "x before)");
} else {
Firstly, you're doing a double-equal comparison here, where earlier you'd done a === comparison. These are different, and will give different results if the variable being tested is not a boolean.
So for example, if $wnd.isc.isA.Canvas(obj) is outputting an integer 1 or something like that rather than true, then you would get exactly the problem you're describing.
Rather than doing another comparison why not get an accurate picture of the contents of the variable by using JSON.stringify
console.log(JSON.stringify($wnd.isc.isA.Canvas(obj)));
This will show you exactly what the value is, which should then show you fairly clearly why the comparisons aren't doing what you expect.
Secondly, using an alert() box to show errors is not a good idea, especially in a complex application. You should always use console.log() for debugging rather than alert(), because alert() can cause some JS code to alter its behaviour, which can make debugging really difficult.
This is because alert() blocks JS execution while it's displaying, which means that any event handlers or other asyncronous code that get triggered during that time can end up running out of the expected sequence.
I don't think this is necessarily the issue for you here, given the code sample provided, but if you have any Ajax calls, setInterval()s or other similar async code anywhere then you really do need to avoid using alert() for debugging purposes.
Hope that helps.
Related
I'm trying to create code that requires the least number of bytes and that works for all browsers including IE 7.
In this example, the program calls dosomething('x1') and dosomething('x2').
If I have code like this:
var items,item,index,count;
items=Array('x1','x2');
count=items.length;
for (index=0;index<count;index++){
item=items[index];
dosomething(item);
}
Could I reduce it to this and have it still function exactly the same in all browsers:
var a=Array('x1','x2'),c=a.length,i;
for (i=0;i<c;i++){
f(a[i]);
}
I understand I changed the variable names and calling function name but my goal is to use the least number of bytes possible in the code to make the code execute.
I'm just not sure if declaring a variable equal to a property of a value from a previous variable in the same list of declarations would actually return correct results.
In other words, does var a=Array('x1','x2'),c=a.length... work, or do I have to specifically do var a=Array('x1','x2');var c=a.length; to make it work in all browsers including IE 7?
This is what the Google Closure Compiler service returned:
var a,b,c,d;a=["x1","x2"];d=a.length;for(c=0;c<d;c++)b=a[c],dosomething(b);
You can find many different Javascript compressors online to automate the process you are hand coding now. Yet, it's always good to understand how they work as it helps to write code that is better compressed.
As for IE, you can test your code by changing the emulations settings in the IE debugger panel. Just press F12, click the Emulation tab, and adjust the document mode to 7 (IE7).
Hope this is enough to get you started in the right direction.
You can use Array.map from IE 9
var items = Array('x1','x2');
items.map(dosomething(item));
Ive got this labratory equipment that is connected to my PC. It uses special OCX file to communicate with the device (reading, setting parameters and such). I got this code from manual that seems to be working. I get a message box saying "Magnification =1272.814 Last error=API not initialized".
<HTML>
<HEAD>
<SCRIPT LANGUAGE="VBScript">
<!--
Sub window_onLoad()
Dim Value
Dim er
call Api1.Initialise("")
call Api1.Get("AP_MAG",Value)
call Api1.GetLastError(er)
call window.alert("Magnification = " + CStr(Value)+"Last error="+er)
call Api1.ClosingControl()
end sub
-->
</SCRIPT>
<TITLE>New Page</TITLE>
</HEAD>
<BODY>
<object classid="CLSID:71BD42C4-EBD3-11D0-AB3A-444553540000" id="Api1">
<PARAM NAME="_Version" VALUE="65536">
<PARAM NAME="_ExtentX" VALUE="2096">
<PARAM NAME="_ExtentY" VALUE="1058">
<PARAM NAME="_StockProps" VALUE="0">
</OBJECT>
</BODY>
</HTML>
So because I have 0% knowledge in vbs and about 10% in jscript I`m trying to rewrite the same thing in Javascript. And I also have some necessary code already written in js.
<script language="JScript">
var Api1=new ActiveXObject("ApiCtrl");
var value;
var er;
Api1.Initialise("");
Api1.Get("AP_MAG",value);
Api1.GetLastError(er);
window.alert("Magnification = " + value+"\n Last error="+er);
Api1.ClosingControl();
</script>
Unfortunately I get a type mismatch error in either .Get or .GetLastError methods either with var value; var er; or var value=""; var er="";
Here is what API manual has to say
long GetLastError(VARIANT* Error)
[out] Error is the error string
associated with the error code for the last error
Remarks: This call will return a VT_BSTR VARIANT associated with the last error. Return
Value: If the call succeeds, it returns 0. If the call fails, an error
code is returned from the function.
long Get(LPCTSTR lpszParam, VARIANT* vValue)
[in] lpszParam is the name of the parameter e.g. “AP_MAG”
[in][out] vValue is the value of the parameter Remarks: This call will get the
value of the parameter specified and return it in vValue. In C++,
before calling this functions you have to specify the variant type
(vValue.vt) to either VT_R4 or VT_BSTR. If no variant type is defined
for vValue, it defaults to VT_R4 for analogue parameters (AP_XXXX) and
VT_BSTR for digital parameters (DP_XXXX). If the variant type is VT_R4
for an analogue parameter, then the floating point representation is
returned in the variant. If a VT_BSTR variant is passed, analogue
values are returned as scaled strings with the units appended (e.g.
AP_WD would return “= 10mm”). For digital parameters, VT_R4 variants
result in a state number and VT_BSTR variants result in a state string
(e.g. DP_RUNUPSTATE would return state 0 or “Shutdown” or the
equivalent in the language being supported). In C++, if the variant
type was specified as VT_BSTR then the API will internally allocate a
BSTR which the caller has to de-allocate using the SDK call
::SysFreeString (vValue.bstrVal)
Welcome to StackOverflow!
Well, each language is made with purpose. Then come to deal with ActiveX objects in browser (or WSH) environment, VBScript is the best choice, while JavaScript is most worst.
JavaScript hasn't so-called out parameters. That mean all function arguments are passed by value (as copy). Lets show you this with examples.
' VBScript
Dim X, Y
X = 1
Y = 2
Foo X, Y
MsgBox "Outer X = " & X & ", Y = " & Y
'> Local args: 6, 8
'> Outer X = 1, Y = 8
Sub Foo(ByVal arg1, ByRef arg2)
arg1 = 6
arg2 = 8
MsgBox "Local args: " & arg1 & ", " & arg2
End Sub
By default in VBS the arguments are passed by reference, so ByRef prefix in function arguments declaration is optional. I include it for clarity.
What the example illustrate is the meaning of "by reference" or "out" parameter. It behave like return value because it modify referenced variable. While modifying "by value" variable has no effect outside of the function scope, because we modify a "copy" of that variable.
// JavaScript
function foo(arg1) {
arg1 = 2;
alert('Local var = ' + arg1);
}
var x = 0;
foo(x);
alert('Outer var = ' + x);
// Local var = 2
// Outer var = 0
Now take a look at this thread. Looks like there is a kind of partial solution by using empty objects. I'm not sure in which cases that will work, but for sure it's very limited hack.
If this not help in your case, then looks like it's time to go with VBScript. Starting with VBS is easy anyway. It's the most user friendly language I ever touch. I was need days, even weeks with other languages only to get started, while just after a few hours with VBS I was able to use it freely.
[EDIT] Well, I made a lot more efforts to reply as may looks like at the glance :) Starting with the language limitation you met. Afterwards going to explain the nature of that limitation (what's "in/out" parameter), and the best way to do that is via example, and this is what I did. Afterwards I show you the only workaround out there to deal with this limitation in JS. Can we consider this as complete answer?
You not mention whether you test this "empty-object-trick", but as you still asking I presume you did that and it's not work with your OCX, right? Then, in this case, you're just forced to deal with your OCX via VBScript, what was my answer from the beginning. And as you prefer to stay with JS then you need to integrate a piece of VB code in your solution.
And as you noted too, this VBs/Js integration is a whole new question. Yes, good question of course, but it's a metter of new topic.
Ok, lets say that the question you append below: "why it should work with passing objects as a function parameter", is still a part of the main question. Well, as you see, even people using JS daily (am not one of them) has no idea what happens "behind the hood", i.e. do not expect an answer on what the JS-engine do in this case, or how this cheat the JS-engine to do something that it's not designed to do. Personally, as I use JS very rarely and not for such tasks, am not even sure if this trick works at all. But as the JS-guys assert it works (in some cases) then we s'd trust them. But that's all about. If this approach fail then it's not an option.
Now what's remain is a bit of homework, you s'd research all available methods for VBs/Js integration, also test them to see which one is most applicable to your domain, and if by chance you meet with difficulties, just then come-back to the forum with new topic and the concrete issue you're trying to resolve.
And to become as helpful as possible, I'll facilitate you with several references to get started.
Here is the plan...
1. If it's possible to work without VBs/Js integration then use stay-alone .VBS files (in WSH environment), else ...
2. In case you work in browser environment (HTML or HTA) then you can embed both (VBs/Js), and your integration w'd be simple.
3. Or may integrate VBs/Js with Windows Script Files (.wsf).
4. Or use ScriptControl that allow running VBScript from within JScript (or backward/opposite).
Links:
Using the ScriptControl
How To Call Functions Using the Script Control
An example VBs/Js integration using ScriptControl via
Batch-Embeded-Script
What is Batch-Embeded-Script:
VBS/Batch Hybrid
JS/Batch Hybrid
5. Some other method (if you find, that am not aware of).
Well, after all this improvements I not see what I can append more, and as I think, now
my answer is more than complete. If you agree with my answer then accept it by clicking on the big white arrow. Of course, if you expect to get better reply from other users, you may still wait, but keep in mind that unanswered questions stay active just for awhile and then become closed.
I am writing a greasemonkey script. Recently i had this same problem twice and i have no idea why is this happening.
function colli(){
.....
var oPriorityMass = bynID('massadderPriority');//my own document.getElementById() function
var aPriorities = [];
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
.....
}
So the mystery of this is, one day i had oPriorityMass named as oPririoty. It was working fine, but the whole function was not yet complete and i started working on another functions for my script. These functions have no connection with each other.
Few days later i decided to go back to my function in the above example and finish it. I ran a test on it without modifying anything and got an error in the firefox's (4) javascript error console saying that oPriority.chilNodes[cEntry] is undefined. NOTE, few days back i have tested it exactly the same way and there was no such problem at all.
Ok, so, i decided to rename oPriority to oPriorityMass. Magically, problem got solved.
At first i thought, maybe there was some conflict of 2 objects, with the same name being used in different functions, which somehow continued to live even outside of function scope. My script is currently over 6000 lines big, but i did a search and found out that oPriority was not mentioned anywhere else but in this exact function.
Can somebody tell me, how and why is this happening? I mentioned same thing happened twice now and they happened in different functions, but the same problem node.childNodes[c] is undefined yet node is not null and node.childNodes.length show correct child count.
What is going on? How do i avoid such problems?
Thank you
EDIT: The error given by error console is
Error: uncaught exception: TypeError: oPriorityMass.childNodes[cEntry] is undefined
In response to Brocks comment:
GM_log(oPriorityMass.childNodes[cEntry]) returns undefined as a message. So node.childNodes[c] is the thing that is undefined in general.
My script creates a div window. Later, the above function uses elements in this div. Elements do have unique IDs and i am 100% sure the original site don't know about them.
My script has a start/stop button to run one or the other function when i need to.
I have been refreshing the page and running my script function now. I have noticed that sometimes (but not always) script will fail with the described error on the first run, however, if i run it again (without refreshing the page) it starts working.
The page has a javascript that modifies it. It changes some of it's element widths so it changes when the browser is resized. But i know it has no effect on my div as it is left unchanged when i resize browser.
EDIT2:
function bynID(sID) {
return top.document.getElementById(ns(sID));
}
function ns(sText) {
return g_sScriptName + '_' + sText;
}
ns function just adds the script name in front of the ID. I use it when creating HTML element so my elements never have the same id as the web page. So bynID() is simple function that saves some typing time when i need to get element by ID.
I have modified my colli() function to include check
if (oPriorityMass) {
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
}
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
The loadPage function does 1 AJAX call, then i run few XPATH queries on it, but the actual contents are never appended/shown on the page, just kept inside document.createElement('div'), then this function calls colli(). So now, as i have modified my function, i checked the error console and saw that it may take up to 5 tries for it to start working correctly. 5 x 2seconds, thats 10 seconds. It is never 5 retries always, may vary There's got to be something else going on?
In Firefox, childNodes can include #text nodes. You should check to make sure that childNodes[cEntry] has nodeType == 1 or has a getAttribute method before trying to call it. e.g.
<div id="d0">
</div>
<div id="d1"></div>
In the above in Firefox and similar browsers (i.e. based on Gecko and WebKit based browsers like Safari), d0 has one child node, a text node, and d1 has no child nodes.
So I would do something like:
var sCollNumber, el0, el1;
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
el0 = oPriorityMass.childNodes[cEntry];
// Make sure have an HTMLElement that will
// have a getAttribute method
if (el0.nodeType == 1) {
sCollNumber = el0.getAttribute('coll');
el1 = bynID('adder' + sCollNumber + '_check');
// Make sure el1 is not falsey before attempting to
// access properties
if (el1 && el1.checked)
// Never call parseInt on strings without a radix
// Or use some other method to convert to Number
aPriorities.push(parseInt(sCollNumber, 10));
}
}
Given that sCollNumber seems like it is a string integer (just guessing but it seems likely), you can also use:
Number(sCollNumber)
or
+sCollNumber
whichever suits and is more maintainable.
So, according to your last edit, it now works, with the delay, right?
But when I suggested the delay it was not meant to do (even more?) ajax calls while waiting!!
NOT:
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
More like:
setTimeout (colli, 2000);
So the ajax and the other stuff that loadPage does could explain the excessive delay.
The random behavior could be caused by:
return top.document.getElementById(ns(sID));
This will cause erratic behavior if any frames or iframes are present, and you do not block operation on frames. (If you do block such operation then top is redundant and unnecessary.)
GM does not operate correctly in such cases -- depending on what the script does -- often seeming to "switch" from top scope to frame scope or vice versa.
So, it's probably best to change that to:
return document.getElementById (ns (sID) );
And make sure you have:
if (window.top != window.self) //-- Don't run on frames or iframes
return;
as the top lines of code.
Beyond that, it's near impossible to see the problem, because of insufficient information.
Either boil the problem into a Complete, Self Contained, Recipe for duplicating the failure.
OR, post or link to the Complete, Unedited, Script.
I would like to know if there is anything wrong with the below statement.
document.getElementById(monthId).options[document.getElementById(monthId).selectedIndex].value
Am asking this because, sometimes it seems to work fine and the rest of the time, it throws up an error - Object doesn't support this property or method.
BTW, monthId is the clientID of the dropdown present in a gridview in an asp.net page.
Thanks!
If no value is selected in the dropdown list, selectedIndex would be -1.
It's hard to evaluate without some more code as context. But without sanity checks around this line of code I would expect it to fail with an index out of bounds type exception when there is no selected index.
I tend to error check when using getElementById. I would expect that that is where your problem is.
Try this, and then test it in a debugger, but I will put an alert in.
var elem = document.getElementById(monthId);
if (elem.options) {
options[document.getElementById(monthId).selectedIndex].value
} else {
alert("elem doesn't have an options property");
}
You may want to not assume that the value property exists either, and do the same basic thing as I did here.
Once you get it working smoothly, where you know what is going to happen, you can start to remove the unneeded variables and go back to your original line, but for debugging, it is simpler to have one operation on each line and use separate variables, so that the debugger can show you what is happening.
You may want to understand the difference between undefined and null, and there are various pages on this topic but this one isn't too bad.
http://weblogs.asp.net/bleroy/archive/2005/02/15/Three-common-mistakes-in-JavaScript-2F00-EcmaScript.aspx
You can debug your problem by adding a breakpoint to your code in IE development tools, Firebug, Opera dragonfly or Chrome development tools and check your values.
Or you could add alert statements to check your values. Personally i think the code goes awry when selectedIndex is -1 (selectedIndex = -1 would occur when nothing is selected).
Check for yourself:
alert(document.getElementById(monthId)); // Returns null if nothing is found
alert(document.getElementById(monthId).selectedIndex); // If the selectedIndex is below 0 it could cause your error
document.getElementById(monthId).options[document.getElementById(monthId).selectedIndex].value
a quick, probably easy question whose answer is probably "best practice"
I'm following a tutorial for a custom-template mobile Safari webapp, and to change views around this code is used:
function btnSave_ClickHandler(event)
{
var views = document.getElementById('stackLayout');
var front = document.getElementById('mainScreen');
if (views && views.object && front) {
views.object.setCurrentView(front, true);
}
}
My question is just about the if conditional statement. What is this triplet saying, and why do each of those things need to be verified before the view can be changed? Does views.object just test to see if the views variable responds to the object method? Why is this important?
EDIT - This is/was the main point of this question, and it regards not Javascript as a language and how if loops work, but rather WHY these 3 things specifically need to be checked:
Under what scenarios might views and front not exist?
I don't typically write my code so redundantly. If the name of my MySQL table isn't changing, I'll just say UPDATE 'mytable' WHERE... instead of the much more verbose (and in my view, redundant)
$mytable = "TheSQLTableName";
if ($mytable == an actual table && $mytable exists && entries can be updated){
UPDATE $mytable;
}
Whereas if the table's name (or in the JS example, the view's names) ARE NOT "hard coded" but are instead a user input or otherwise mutable, I might right my code as the DashCode example has it. So tell me, can these values "go wrong" anyhow?
Thanks!
The if is testing those 3 pointers to make sure they are non-null. A null pointer is 0 which converts to false. If any of those 3 pointer are 0 (null) then it won't try to use them.
I'm not sure what dereferencing a null pointer does in Javascript but it's an error and may cause an exception. The if is just avoiding that possibility.