0
votes

EDIT

With all the edits to my question it had grown quite lengthy. So let me try to shorten it up a bit and make it easier to follow.

I am building an XUL application using XULRunner. I have it load a dummy XUL page, and then I am looking to use XMLHttprequest to load everything from my server (local ampps server), using PHP to do all the real work. My PHP is setting the XML Content-Type header, and formatting all the output data as XML.

Here is what the JavaScript function, that handles the XMLHttprequest and the response, currently looks like.

function RequestData()
{
if (window.XMLHttpRequest)
{
    var url = 'newmenu.xml';
    var request = new XMLHttpRequest();
}
else
{
    var url = 'http://localdomain.prog/';
    var request = Components.classes['@mozilla.org/xmlextras/xmlhttprequest;1'].createInstance(Components.interfaces.nsIXMLHttpRequest);
}

request.onload = function(aEvent)
{
    var xmlDoc = aEvent.target.responseXML;

    var oldmenu = document.getElementById('menubarwrapper');

    oldmenu.parentNode.replaceChild(xmlDoc.documentElement, oldmenu);
};
request.onerror = function(aEvent)
{
    window.alert("Error Status: " + aEvent.target.status);
};
request.open('POST', url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
request.send('pageid=menu');
}

The RequestData() is called with the window onload event.

My original code looked to do nothing, but as I researched and tested I eventually got XULRunner to put out some errors in the error console. Ultimately it lead to what, I now assume, were working versions but I just didn't know it.

The Error Console was putting out this message (and still is)

Warning: XUL box for window element contained an inline toolbox child, forcing all its children to be wrapped in a block.

In order to find out if my code worked I had to get it into Firefox. Hence the reason for the if (window.XMLHttpRequest), as it allows me to test with both Firefox and XULRunner. I then took the XML that my PHP was generating and made a local file, as Firefox will not allow an XMLHttprequest to load a remote file (even if it is technically local).

The above code does import the XML and replaces the <menubar id="menubarwrapper">...</menubar>. But, in both Firefox and XULRunner the menu disappears. I can see all the elements if use Firebug, but why they are no longer visible is beyond me, and there are no errors in the Firebug console. This is where I am currently stumped.

In-case its of any use, below is a copy of the dummy XUL file I load.

<?xml version="1.0"?>
<?xml-stylesheet href="main.css" type="text/css"?>

<window id="main" title="My App" width="1000" height="800" sizemode="maximized" orient="vertical" persist="screenX screenY width height" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<script type="application/javascript" src="main.js"/>

<toolbox id="toolboxwrapper">
    <menubar id="menubarwrapper">
        <menu id="file-menu" label="File" accesskey="F">
            <menupopup id="file-popup">
                <menuitem label="Refresh" funcname="RefreshWin"/>
                <menuitem label="Open Window" funcname="OpenWin" acceltext="Ctrl+O" accesskey="O" />
                <menuseparator/>
                <menuitem label="Exit" funcname="ExitProg"/>
            </menupopup>
        </menu>
        <menu id="edit-menu" label="Edit" accesskey="E">
            <menupopup id="edit-popup">
                <menuitem label="Undo"/>
                <menuitem label="Redo"/>
            </menupopup>
        </menu>
    </menubar>
</toolbox>
</window>

The XML that my PHP generates is quite lengthy, but basically it is the <menubar> element with all of its child elements, similar to above, but with a lot more <menu> and <menuitem> elements.

2
Try DOM Inspector to verify what your document DOM looks like after it has been modified. What element is xmlDoc.documentElement? - Kashif
@Kashif - I haven't used DOM inspector, but after loading it, at first glance, it appears to have similar information to what is in Firebug. I did create a test in Firefox to see what firebug was outputting. See my second edit for more details. - Krik
@Kashif - forgot to answer your question. xmlDoc.documentElement is the <toolbox> element that is generated by my PHP - Krik
Just a general observation, which may or may not be related to your problem: You need to adoptNode a node from a different DOM before inserting it. - nmaier
I implemented something like this myself, actually, XHR'ing some XML/XUL and placing it in an existing window (I did it to get fake-overlays for restartless add-ons). Maybe this will help. One important thing, IIRC was to remove white-space-only text nodes. - nmaier

2 Answers

0
votes

Without more information, this is just a guess at what the problem is.

What may be happening is that the XML data which is retrieved from your XMLHttpRequest() is being interpreted as XML/HTML and not XUL. This could result in the behavior which you describe. You can check this by using the DOM Inspector to look at the elements which were inserted with your line:

oldmenu.parentNode.replaceChild(xmlDoc.documentElement, oldmenu);

What you should look for is what Namespace URI shows for those elements. If it is not http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul then the elements are not being treated as XUL.

One way to solve this would be to do:

oldmenu.insertAdjacentHTML("afterend", aEvent.target.responseText);
oldmenu.setAttribute("hidden","true");
//Alternately (if you don't want to keep the placeholder <menubar>):
//oldmenu.parentNode.removeChild(oldmenu);
0
votes

I got it working but the code is 80+ lines longer than it should ever need to be. I tried every variation of importNode, replaceChild, appendChild, adoptNode, removeChild, ect. I could think of. I am fairly certain I could write a book on how many ways you could handle any given XML element or node.

Here's an example of how absurd the issue was. This will work

function AddNewElem()
{
    var menubar = document.getElementById('menubarwrapper');

    var menuitem = '<menu id="help-menu" label="Help" accesskey="H" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">';
    menuitem += '<menupopup id="help-popup">';
    menuitem += '<menuitem id="test-menu" label="Test" acceltext="Ctrl+T" accesskey="T" />';
    menuitem += '<menuseparator/>';
    menuitem += '<menuitem id="about-menu" label="About" acceltext="Ctrl+A" accesskey="A" />';
    menuitem += '</menupopup>';
    menuitem += '</menu>';

    var parser = new DOMParser();
    var xmlDoc = parser.parseFromString(menuitem, 'text/xml').documentElement;

    menubar.appendChild(xmlDoc);
}

but this will not

function RequestData()
{
    var url = 'newmenu.txt';
    var request = new XMLHttpRequest();

    request.onload = function(aEvent)
    {
        var menubar = document.getElementById('menubarwrapper');

        var responsetxt = aEvent.target.responseText;

        var parser = new DOMParser();
        var xmlDoc = parser.parseFromString(responsetxt, 'text/xml').documentElement;

        menubar.appendChild(xmlDoc);
    };
    request.onerror = function(aEvent)
    {
        alert("Error Status: " + aEvent.target.status);
    };
    request.open('POST', url, true);
    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    request.send('pageid=menu');
}

newmenu.txt is just the contents of menuitem in the preceding piece of code. I even went as far as to serialize the xmlDoc back to a string and then parse it again and still wouldn't work. I tried a ton of dumb ideas, and many I knew wouldn't work, on the off chance I might get a clue as to the problem. When you crash Firefox you know you have pushed too hard.

But I digress. So for the sake of others trying to use do a XHR with XULrunner below is what I had to do.

function RequestData()
{
    var url = 'http://localdomain.prog/';
    var request = Components.classes['@mozilla.org/xmlextras/xmlhttprequest;1'].createInstance(Components.interfaces.nsIXMLHttpRequest);

    request.onload = function(aEvent)
    {
        var xmlDoc = aEvent.target.responseXML;

        var toolbar = document.getElementById('toolboxwrapper');
        var menubar = document.getElementById('menubarwrapper');
        toolbar.removeChild(menubar);

        var menubar = document.createElement('menubar');
        menubar.setAttribute('id', 'menubarwrapper');

        var docfrag = document.createDocumentFragment();
        docfrag.appendChild(menubar);

        var newmenus = xmlDoc.getElementsByTagName('menu');
        var menuslen = newmenus.length;
        for (var i = 0; i < menuslen; i++)
        {
            docfrag = CreateMenu(newmenus[i], docfrag);
        }

        toolbar.appendChild(docfrag);
    };
    request.onerror = function(aEvent)
    {
        window.alert("Error Status: " + aEvent.target.status);
    };
    request.open('POST', url, true);
    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    request.send('pageid=menu');
}

function CreateMenu(obj, docfrag)
{
    var fragbar = docfrag.getElementById('menubarwrapper');

    var menu = document.createElement('menu');

    menu = SetMenuAttr(obj, 'id', menu);
    menu = SetMenuAttr(obj, 'label', menu);
    menu = SetMenuAttr(obj, 'accesskey', menu);

    var fragmenu = fragbar.appendChild(menu);

    var popup = obj.getElementsByTagName('menupopup')[0];

    var menupopup = document.createElement('menupopup');
    menupopup = SetMenuAttr(popup, 'id', menupopup);

    var fragpopup = fragmenu.appendChild(menupopup);

    if (popup.hasChildNodes())
    {
        var childmenu = popup.childNodes;

        var menulen = childmenu.length;

        for (var i = 0; i < menulen; i++)
        {
            if (childmenu[i].nodeName == 'menuitem' || childmenu[i].nodeName == 'menu')
            {
                if (childmenu[i].nodeName == 'menuitem')
                {
                    var menuitem = CreateMenuitem(childmenu[i]);
                }

                if (childmenu[i].nodeName == 'menu')
                {
                    var menuitem = CreateMenu(childmenu[i]);
                }
                fragpopup.appendChild(menuitem);
            }
        }
    }
    return docfrag;
}

function CreateMenuitem(obj)
{
    var menuitem = document.createElement('menuitem');

    menuitem = SetMenuAttr(obj, 'id', menuitem);
    menuitem = SetMenuAttr(obj, 'label', menuitem);
    menuitem = SetMenuAttr(obj, 'accesskey', menuitem);
    menuitem = SetMenuAttr(obj, 'acceltext', menuitem);
    menuitem = SetMenuAttr(obj, 'disabled', menuitem);

    return menuitem;
}

function SetMenuAttr(obj, attr, newobj)
{
    if (obj.hasAttribute(attr))
    {
        newobj.setAttribute(attr, obj.getAttribute(attr));
    }
    return newobj;
}

Basically had to retrieve each element and its attributes and make new elements. By far its not the preferred solution. For myself, if I have to write one line of code more than is absolutely necessary, it is wrong. The solution should have been 2 lines.