SharePoint XML parsing bug

Here’s a fun little problem I ran into when I tried to deploy custom Page Layouts to a SharePoint farm inside a very restrictive environment. I named this article “bug” because I actually think this is a bug or at least an oversight in the code.

Symptoms

On Internet Explorer 10/11, whenever I clicked the Page Layout button on the ribbon to change the layout of the page, the drop-down menu that should be displayed was not. No error was logged in the developer console and it appeared to work fine in other browsers. A really strange symptom for a really strange problem.

Page layout ribbon highlighted

Cause and solution

The SharePoint JavaScript contains a bunch of useful utilities. Some can be used by developers themselves and others are used by the system internally. It turns out that the drop-down menu is originally constructed as a set of XML nodes in a string and that SharePoint is trying to find the “best” way to parse this into an XML document. The first thing it does is try to determine if ActiveX objects are supported, which for IE they are. Then it tries to instantiate one of two add-ons that provide this functionality. The problem occurs if the user has disabled both of these add-ons or if an admin has deployed a GPO (Group Policy Object) that does the same. In this situation SharePoint will simply fail silently and give you no help at all with debugging the issue.

The solution for me was to white-list the Class ID for the add-on XML DOM Document 6.0 in a GPO thus allowing SharePoint to use it to parse XML.

IE Add-on XML DOM Document 6

Diagnosing the issue

This was one hell of an issue to debug. It seemed that the only symptom was the missing drop-down though I’m sure there are other problems that we haven’t yet discovered. All other custom functionality we had developed worked fine. The custom layout also appeared to have been deployed correctly and it was part of the available layouts for the site. One good thing we discovered early on was that even without any custom code whatsoever deployed to the SharePoint farm the error was still there. This at least ruled out our own code as the culprit.

From before I knew that such menus are constructed using a Page Component (at least for custom components) ([1], [2]). Page Compoents can be used to construct most of the controls you see on the SharePoint ribbon (if not all). I’ve previously built just such a drop down that populates from an AJAX call so I sort of know what’s going on behind the scenes. In my code I actually had the exact same issue when my component did not deliver the contents of the menu to the state object for the page component. My drop down would simply not appear yet the button looked functional. The Page Component plumbing code repeats the request for “menu content” over and over as long as it doesn’t get any with certain interval between each request (about 10 ms by default if I remember correctly, but this can be modified). This will result in a seemingly non-functional UI, but the browser keeps on working hoping that my component will populate the menu at some point in the future. From this I figured that the problem may be with the Page Component code that is supposed to populate the drop down and it gave me a place to start.

Before I started digging into this I went over to the web.config file for my web application and enabled debugging (quick post on this later). Enabling debugging here makes SharePoint load the debug version of almost all JavaScript files instead of the minified ones greatly simplifying setting breakpoints and following stacks through the code.

First up was figuring out why the menu was not populated. To do this I needed to find out what JavaScript was responsible for populating this particular control. All Ribbon controls have to be defined somewhere and SharePoints own controls are almost guaranteed to be found inside the 15 hive somewhere (C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15). I inspected the ribbon button using the browser developer tools and found this tag:

There is a lot of noise here, but the important bit is the id attribute of the anchor tag. It contains the actual id of the control responsible for the component and a modifier since the icon is large. The ID of the Page Layout control therefore becomes:

This is something I can search for! Using Visual Studios excellent “Find in Files” search I point it to the 15-hive and go.

Find in files in 15-hive

One hit on ProvisionedUI.xml:316 (C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\TEMPLATE\FEATURES\Publishing\ProvisionedUI.xml):

This XML describes the specific component as well as which command a click should execute. PopulateQueryCommand gives you the ID of this command. It is not an actual JavaScript function though, rather an identifier that the Page Component code uses to route execution to their proper handlers. Another 15-hive search for GetPageLayoutsXml resulted in multiple hits on the file SP.UI.Pub.Ribbon.debug.js (C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\TEMPLATE\LAYOUTS\SP.UI.Pub.Ribbon.debug.js). From my own component I knew the id needed to be inside the handleCommand function and one of the hits (line 497) was exactly there!

Here is where the Page Component code populates the state object with the XML specification for the drop down. As we can see it calls to a function named

A quick search within the SP.UI.Pub.Ribbon.debug.js uncovered the function definition at line 22. Heading back to the browsers developer tools I dropped a breakpoint on the first line within the function and reloaded the page. Sure enough, when I clicked the Page Layout button the breakpoint was hit.

Hit breakpoint inside _initPageLayoutMenu$p function

Now that I had an actual bit of code to look at I could start to trace the problem. The first thing I did was follow the code through and out of the _initPageLayoutMenu$p function (also called Pub_Ribbon_PubRibbonComponent$_initPageLayoutMenu$p apparently). It appears to be responsible for constructing the xml describing the drop-down menu as a string and returning it out. From what I could see it seemed to do it’s job just fine. The problem is probably located after the call to this code. Let’s move over to the call stack and inspect the surrounding code of each call down to this function.

Debug stack for the page layouts populator function

Moving up one level we get to the code that populates the “state” object I mentioned before

Here is where the raw XML string is placed into the properties (props) or state object that the surrounding code for the Page Component mechanism reads and uses to construct the actual menu. This is not the final HTML code that is inserted into the page, but rather a more abstract representation of the menu. Hmm… this will probably have to be parsed and processed somewhere. Let’s keep following the code and see what it does.

Following execution back up the stack I eventually end up in a function called

and here is something interesting. A few lines down from the call that eventually led to the function constructing the XML there is a line that appears to try to construct some kind of XML parser around the XML string. Specifically line 3752 in cui.debug.js

What is this CUI.Builder thing doing I wonder…

It appears to be using some kind of utility (CUI.NativeUtility.createXMLDocFromString) to create an XML document probably in order to process it. It also seems that it can fail to construct such a document and in that case may return null. Let’s take a look at the createXMLDocFromString function. Two steps deeper we’re encounter this lovely function:

Hello… hello… what’s all this then? It appears that SharePoint is trying to determine the best way to construct an XML parser. It starts by defining a local function at the top GetActiveXObject(progIDs) and then it tries to determine which mechanism the browser supports. The very first check (line 20 above) tries to determine if the browser supports ActiveX objects. Since this is IE 10/11 it does. Cool… I guess… Then it tries to construct an instance of one of two types of such objects:

  • Msxml2.DOMDocument.6.0
  • Msxml2.DOMDocument

So far so good. But HOW exactly does it do this? Take a look at the GetActiveXObject local function. It seems to be trying to instantiate each item and return the first one that succeeds. If all fail it returns null and swallows any exception that may occur during instantiation. Seems innocent enough, but what would happen if say the user or a GPO prevented both these extensions from running? Well, the call to GetActiveXObject would return null to the msxmlDomDoc variable. On line 35 there is a check on this variable for null and if not it will use loadXML to parse the XML string constructed before. But if msxmlDomDoc is null it simply returns it (hence null) and gives up. It doesn’t even try to check if another parser is available even though IE10/11 supports DOMParser (the test on line 39). Could this be it? Let’s check if I have some disabled add-on that could possibly match those the code is looking for (IE Settings -> Manage add-ons)-

IE addins emphasis on XML DOM Document

AHA! Some GPO was disabling the add-on XML DOM Document 6.0. Could this possibly be the one of the ones SharePoint want’s to use? Opening more information about the the add-on doesn’t really help that much.

XML DOM Document more information

However there is an ID here (Class ID) that we can look up. A quick Google search later revealed this table (source):

Symbolic NameGUIDProgID
CLSID_DOMDocument60{88d96a05-f192-11d4-a65f-0040963251e5}Msxml2.DOMDocument.6.0
CLSID_FreeThreadedDOMDocument60{88d96a06-f192-11d4-a65f-0040963251e5}Msxml2.FreeThreadedDOMDocument.6.0
CLSID_MXHTMLWriter60{88d96a10-f192-11d4-a65f-0040963251e5}Msxml2.MXHTMLWriter.6.0
CLSID_MXNamespaceManager60{88d96a11-f192-11d4-a65f-0040963251e5}Msxml2.MXNamespaceManager.6.0
CLSID_MXXMLWriter60{88d96a0f-f192-11d4-a65f-0040963251e5}Msxml2.MXXMLWriter.6.0
CLSID_SAXAttributes60{88d96a0e-f192-11d4-a65f-0040963251e5}Msxml2.SAXAttributes.6.0
CLSID_SAXXMLReader60{88d96a0c-f192-11d4-a65f-0040963251e5}Msxml2.SAXXMLReader.6.0
CLSID_ServerXMLHTTP60{88d96a0b-f192-11d4-a65f-0040963251e5}Msxml2.ServerXMLHTTP.6.0
CLSID_XMLHTTP60{88d96a0a-f192-11d4-a65f-0040963251e5}Msxml2.XMLHTTP.6.0
CLSID_XMLSchemaCache60{88d96a07-f192-11d4-a65f-0040963251e5}Msxml2.XMLSchemaCache.6.0
CLSID_XSLTemplate60{88d96a08-f192-11d4-a65f-0040963251e5}Msxml2.XSLTemplate.6.0

Check out the top entry! That’s the exact ProgID SharePoint tries to use. Let’s go ahead and activate the add-on and see what happens. EUREKA!

Working page layouts drop down

So the problem was that a GPO was blocking all supported add-ons for working with XML files and the SharePoint JavaScript did not account for this situation. What it should have done was kept on going and trying to find another supported parser. Simply skip the else if and return null at the end unless a valid parser is found. For IE9 and up it would find DOMParser to be implemented and all would have been well. Unfortunately it didn’t, but at least I got a good hour and a half of head-scratching out of it.

This is a very tricky issue to figure out though since it may manifest in many different ways. The XML parser utility code in SharePoint is used by many components (including the Task list if I’m remembering correctly) so you could encounter a lot of strange behavior without an apparent connected cause. For me the original issue was a ribbon drop-down menu that did not appear when it should, but others may encounter failed AJAX queries or missing content. The common thread would be that it would always occur in IE, but we all know that an error in THAT BEAST can be caused by a myriad of other things. So what’s the moral of the story? Wish I had one definitive tip to give, but it seems the best thing to do would be to figure out what code is executed and trace from there comparing execution in IE with another (a real) browser and find out where they differ. Hopefully this post will help someone else avoid this process though. Happy coding!

PS: Here is a simple fix that would have eliminated the issue:

 

Leave a Reply

Your email address will not be published. Required fields are marked *