runtime.js:2 Uncaught TypeError: e.createConditionsFromXml is not a function

  • 2
  • Problem
  • Updated 2 years ago
  • Not a Problem
I have a handful of users that are getting this error when trying to open a skuid page.  The page does not finish loading (you just see the standard salesforce border).  Chrome browser in use.
Photo of Jared Jones

Jared Jones

  • 2,214 Points 2k badge 2x thumb

Posted 2 years ago

  • 2
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,086 Points 10k badge 2x thumb
Hi Jared -

This seems eerily similar and likely the same issue that Pat reported a while back.  Check out https://community.skuid.com/skuid/topics/8-15-7-release-issue?topic-reply-list%5Bsettings%5D%5Bfilte... for more details.

Questions:

1) What version of Chrome are you running?
2) is the problem intermittent or does it occur every time?
3) What happens in IE or Firefox?'
4) which runtime.js file is being referenced in the stack trace?  Can you provide the full path?
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,086 Points 10k badge 2x thumb
Hi Jared -

I upgraded one of my machines to Chrome Version 53.0.2785.113 m today and spent more time with this.  The good news is that I was able to reproduce the issue.  

As you get a chance, if you could answer the 4 questions above just so I can ensure my assumptions are correct I'd appreciate it.

After digging through the guts of jQuery, I isolated the issue to a block of code within jQuery UI Widget:

    e.widget.extend = function(t) {        for (var i, s, n = u.call(arguments, 1), a = 0, o = n.length; o > a; a++)
            for (i in n[a])
                s = n[a][i],
                n[a].hasOwnProperty(i) && void 0 !== s && (t[i] = e.isPlainObject(s) ? e.isPlainObject(t[i]) ? e.widget.extend({}, t[i], s) : e.widget.extend({}, s) : s);
        return t
    }
What's happening here is that the options passed to a jQuery UI Widget (which TFG Collapsible Wrapper is) are being extended.  The problem is that intermittently, the jQuery function isPlainObject is returning true on a Skuid.component.Component object when it should be returning false.  This results in the component (which is passed as an option) to get it's properties copied instead of just having the option set to a reference of the component.  When this occurs, the TFG CW can't call "component.createConditionsFromXml" because the prototype method isn't on the object.

I took that knowledge and created a very simple repro of the problem in a basic skuid page (see below for the page).  If you create this page in your org, preview it and then open the debugger console, you will see the output of "Is Plain: ####" and "Is Not Plain: ###".  Some portion of the loops will return Is Plain when they should all return "Is Not Plain."  If you run this same page in Chrome Version 52.0.2743.116, all 1000 loops go to "Is Not Plain" as expected.

Digging in to isPlainObject, I think the problem stems from hasOwnProperty or the order of the keys returned from an object in a for loop.  I'm still working to further isolate this.

This problem appears to only present itself on Chrome 53 and above and is definitely a bug in Chrome I believe since Skuid is the same, the code in the page is the same, jQuery is the same, Operating System is the same, etc.  The only difference is the version of Chrome.

This issue is going to cause intermittent problems in other areas of Skuid not just the CW.  I did a search for Chrome bugs filed but can't find one yet about this issue.  I'll continue to search as I dig further.

In short, this appears to be an issue with Chrome 53 and not Skuid nor the CW.  

Skuid Team - Any insight and/or thoughts?

Here's the inline JS resource that creates a skuid.component.Component and loops 1000 times
(function($S, $){
var component = new $S.component.Component($('<div>'), 'testing', {})
   , counter = 0
   , isNotPlain = 0
   , isPlain = 0;
for (var i = 0; i < 1000; i++) {
    if ($.isPlainObject(component)) {
       isPlain++;
    } else {
       isNotPlain++;
    }
}
console.log('Is Plain: ' + isPlain);
console.log('Is Not Plain: ' + isNotPlain);
})(skuid, skuid.$);

Here's the sample page:
<skuidpage unsavedchangeswarning="yes" personalizationmode="server" showsidebar="true" showheader="true">   <models/>
   <components/>
   <resources>
      <labels/>
      <javascript>
         <jsitem location="inline" name="newInlineJS" cachelocation="false" url="">(function($S, $){
var component = new $S.component.Component($('&lt;div&gt;'), 'testing', {})
   , counter = 0
   , isNotPlain = 0
   , isPlain = 0;
for (var i = 0; i &lt; 1000; i++) {
    if ($.isPlainObject(component)) {
       isPlain++;
    } else {
       isNotPlain++;
    }
}
console.log('Is Plain: ' + isPlain);
console.log('Is Not Plain: ' + isNotPlain);
})(skuid, skuid.$);</jsitem>
      </javascript>
      <css/>
   </resources>
   <styles>
      <styleitem type="background" bgtype="none"/>
   </styles>
</skuidpage>
(Edited)
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,086 Points 10k badge 2x thumb
All -

I worked with Ben today to confirm my findings and assumptions and we both agreed this is a bug most likely introduced somewhere in Chrome 53.x.

I have filed a bug with Chrome at https://bugs.chromium.org/p/chromium/issues/detail?id=647887.
Photo of mB Andréas K.LeF

mB Andréas K.LeF

  • 1,244 Points 1k badge 2x thumb
I'm happy to finally see others getting this same issue and Barry thanks for doing us a favor and writing the fiddle and posting about it here and to the Chrome bug tracker.

I'm currently running a bleeding edge Chrome 55.0.2863.0 canary (64-bit) that still exhibits this same problem. If you're curious though, I've run the fiddle and I'm getting highly inconsistent (on a per-run basis) numbers for "isNotPlain" on my machine ranging from 300-700 roughly.

Now, if this issue does end up being a Chrome bug, I don't expect it to be fixed until November-December 2016 at the soonest.

I recommenced that anyone depending on components exhibiting such unexpected behavior immediately implement workarounds in the code to mitigate the issue and allow normal operation to continue
(Edited)
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,086 Points 10k badge 2x thumb
Hi Andreas -

Thanks for the update, disappointed to hear that this issue is still present in 55.x.  The inconsistent results are to be expected and is consistent with what I'm seeing in 53.x.  Hopefully we'll get some traction on the Chrome bug soon.

Regarding a workaround, I have implemented a workaround in the TFG Components.  You can get more information and links to the latest version at https://community.skuid.com/skuid/topics/collapsible-wrapper-custom-component?topic-reply-list%5Bset....

As for details on the workaround, let's start with a baseline:

1) The problem is definitely inside of the Chrome javascript engine (Chrome v8).  It is not inside of Skuid, TFG or jQuery.  This is proven with the fiddle.

2) The bug itself is related to iterating object property keys when there is a try..catch loop present.  It's a very strange bug but inside of the for loop, the key is correct, however after existing the for loop, the key is undefined.  You can see this in the new fiddle I've added to the Chrome bug.  See https://bugs.chromium.org/p/chromium/issues/detail?id=647887#c1 for details.

3) The workaround I implemented in TFG Components will only workaround the one instance that is known to trigger the bug.  It's related to the jQuery UI widget extend method which uses jQuery isPlainObject function.  The workaround does not fix the isPlainObject itself, rather it just manipulates the code inside of there to go down a different code path which avoids the "for" loop that iterates properties.  See the definition of isPlainObject in the jQuery source code at https://code.jquery.com/jquery-1.12.4.js.

For those interested in the workaround, the key is to force isPlainObject to not iterate object keys and instead, call in to the code path where hasOwnProperty returns false when checking for "constructor" and "isPrototypeOf".  This is done by creating an object chain similar to the following:

var WrapperBase = function(objToWrap) {   
    this.wrappedObj = objToWrap;
};
WrapperBase.prototype.getWrappedObj = function() {
    return this.wrappedObj;
};

var ComponentWrapper = function(component) {
    WrapperBase.call(this, component);        
};
ComponentWrapper.prototype = Object.create(WrapperBase.prototype);
ComponentWrapper.constructor = ComponentWrapper;
ComponentWrapper.prototype.getComponent = function() {
    return this.getWrappedObj();
};

Another option for a workaround is to replace the jQuery isPlainObject function itself.  Again, replacing this function will not fix the Chrome bug but at least it will solve the scenarios where isPlainObject is used somewhere in the code and where it falls prey to the intermittent bug in Chrome.

Hope this helps!
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,086 Points 10k badge 2x thumb
All - The chrome team has already implemented a fix for this issue and has indicated it will be backported in to Chrome 53 were it was introduced. Follow updates at https://bugs.chromium.org/p/chromium/issues/detail?id=647887
(Edited)
Photo of Barry Schnell

Barry Schnell, Champion

  • 18,086 Points 10k badge 2x thumb
All - The Chrome fix is now available in Chrome Version 53.0.2785.143 m.  I believe it made it to the 54 & 55 branches as well, just not sure which specific version.