~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/extensions/transformiix/source/xslt/txMozillaXMLOutput.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 
2
/* ***** BEGIN LICENSE BLOCK *****
 
3
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
4
 *
 
5
 * The contents of this file are subject to the Mozilla Public License Version
 
6
 * 1.1 (the "License"); you may not use this file except in compliance with
 
7
 * the License. You may obtain a copy of the License at
 
8
 * http://www.mozilla.org/MPL/
 
9
 *
 
10
 * Software distributed under the License is distributed on an "AS IS" basis,
 
11
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
12
 * for the specific language governing rights and limitations under the
 
13
 * License.
 
14
 *
 
15
 * The Original Code is the TransforMiiX XSLT processor.
 
16
 *
 
17
 * The Initial Developer of the Original Code is
 
18
 * Netscape Communications Corporation.
 
19
 * Portions created by the Initial Developer are Copyright (C) 2001
 
20
 * the Initial Developer. All Rights Reserved.
 
21
 *
 
22
 * Contributor(s):
 
23
 *   Peter Van der Beken <peterv@netscape.com>
 
24
 *
 
25
 * Alternatively, the contents of this file may be used under the terms of
 
26
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 
27
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
28
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
29
 * of those above. If you wish to allow use of your version of this file only
 
30
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
31
 * use your version of this file under the terms of the MPL, indicate your
 
32
 * decision by deleting the provisions above and replace them with the notice
 
33
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
34
 * the provisions above, a recipient may use your version of this file under
 
35
 * the terms of any one of the MPL, the GPL or the LGPL.
 
36
 *
 
37
 * ***** END LICENSE BLOCK ***** */
 
38
 
 
39
#include "txMozillaXMLOutput.h"
 
40
 
 
41
#include "nsIDocument.h"
 
42
#include "nsIDocShell.h"
 
43
#include "nsIScriptLoader.h"
 
44
#include "nsIDOMDocument.h"
 
45
#include "nsIDOMComment.h"
 
46
#include "nsIDOMDocumentType.h"
 
47
#include "nsIDOMDOMImplementation.h"
 
48
#include "nsIDOMNodeList.h"
 
49
#include "nsIDOMProcessingInstruction.h"
 
50
#include "nsIDOMText.h"
 
51
#include "nsIDOMHTMLTableSectionElem.h"
 
52
#include "nsIDOMHTMLScriptElement.h"
 
53
#include "nsIDOMNSDocument.h"
 
54
#include "nsIParser.h"
 
55
#include "nsIRefreshURI.h"
 
56
#include "nsIScriptGlobalObject.h"
 
57
#include "nsITextContent.h"
 
58
#include "nsIXMLContent.h"
 
59
#include "nsContentCID.h"
 
60
#include "nsNetUtil.h"
 
61
#include "nsUnicharUtils.h"
 
62
#include "txAtoms.h"
 
63
#include "TxLog.h"
 
64
#include "nsIConsoleService.h"
 
65
#include "nsIDOMDocumentFragment.h"
 
66
#include "nsINameSpaceManager.h"
 
67
#include "nsICSSStyleSheet.h"
 
68
#include "txStringUtils.h"
 
69
#include "txURIUtils.h"
 
70
#include "nsIHTMLDocument.h"
 
71
#include "nsIStyleSheetLinkingElement.h"
 
72
#include "nsIDocumentTransformer.h"
 
73
 
 
74
extern nsINameSpaceManager* gTxNameSpaceManager;
 
75
 
 
76
static NS_DEFINE_CID(kXMLDocumentCID, NS_XMLDOCUMENT_CID);
 
77
static NS_DEFINE_CID(kHTMLDocumentCID, NS_HTMLDOCUMENT_CID);
 
78
 
 
79
#define kXHTMLNameSpaceURI "http://www.w3.org/1999/xhtml"
 
80
 
 
81
#define TX_ENSURE_CURRENTNODE                           \
 
82
    NS_ASSERTION(mCurrentNode, "mCurrentNode is NULL"); \
 
83
    if (!mCurrentNode)                                  \
 
84
        return
 
85
 
 
86
txMozillaXMLOutput::txMozillaXMLOutput(const nsAString& aRootName,
 
87
                                       PRInt32 aRootNsID,
 
88
                                       txOutputFormat* aFormat,
 
89
                                       nsIDOMDocument* aSourceDocument,
 
90
                                       nsIDOMDocument* aResultDocument,
 
91
                                       nsITransformObserver* aObserver)
 
92
    : mBadChildLevel(0),
 
93
      mTableState(NORMAL),
 
94
      mDontAddCurrent(PR_FALSE),
 
95
      mHaveTitleElement(PR_FALSE),
 
96
      mHaveBaseElement(PR_FALSE),
 
97
      mCreatingNewDocument(PR_TRUE)
 
98
{
 
99
    if (aObserver) {
 
100
        mNotifier = new txTransformNotifier();
 
101
        if (mNotifier) {
 
102
            mNotifier->Init(aObserver);
 
103
        }
 
104
    }
 
105
 
 
106
    mOutputFormat.merge(*aFormat);
 
107
    mOutputFormat.setFromDefaults();
 
108
 
 
109
    createResultDocument(aRootName, aRootNsID, aSourceDocument, aResultDocument);
 
110
}
 
111
 
 
112
txMozillaXMLOutput::txMozillaXMLOutput(txOutputFormat* aFormat,
 
113
                                       nsIDOMDocumentFragment* aFragment)
 
114
    : mBadChildLevel(0),
 
115
      mTableState(NORMAL),
 
116
      mDontAddCurrent(PR_FALSE),
 
117
      mHaveTitleElement(PR_FALSE),
 
118
      mHaveBaseElement(PR_FALSE),
 
119
      mCreatingNewDocument(PR_FALSE)
 
120
{
 
121
    mOutputFormat.merge(*aFormat);
 
122
    mOutputFormat.setFromDefaults();
 
123
 
 
124
    aFragment->GetOwnerDocument(getter_AddRefs(mDocument));
 
125
 
 
126
    nsCOMPtr<nsIDocument> doc = do_QueryInterface(mDocument);
 
127
    mDocumentIsHTML = doc && !doc->IsCaseSensitive();
 
128
 
 
129
    mCurrentNode = aFragment;
 
130
}
 
131
 
 
132
txMozillaXMLOutput::~txMozillaXMLOutput()
 
133
{
 
134
}
 
135
 
 
136
void txMozillaXMLOutput::attribute(const nsAString& aName,
 
137
                                   const PRInt32 aNsID,
 
138
                                   const nsAString& aValue)
 
139
{
 
140
    if (!mParentNode)
 
141
        // XXX Signal this? (can't add attributes after element closed)
 
142
        return;
 
143
 
 
144
    if (mBadChildLevel) {
 
145
        return;
 
146
    }
 
147
 
 
148
    nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mCurrentNode);
 
149
    NS_ASSERTION(element, "No element to add the attribute to.");
 
150
    if (!element)
 
151
        // XXX Signal this? (no element to add attributes to)
 
152
        return;
 
153
 
 
154
    if ((mOutputFormat.mMethod == eHTMLOutput) && (aNsID == kNameSpaceID_None)) {
 
155
        // Outputting HTML as XHTML, lowercase attribute names
 
156
        nsAutoString lowerName;
 
157
        TX_ToLowerCase(aName, lowerName);
 
158
        element->SetAttributeNS(nsString(), lowerName,
 
159
                                aValue);
 
160
    }
 
161
    else {
 
162
        nsAutoString nsURI;
 
163
        gTxNameSpaceManager->GetNameSpaceURI(aNsID, nsURI);
 
164
        element->SetAttributeNS(nsURI, aName, aValue);
 
165
    }
 
166
}
 
167
 
 
168
void txMozillaXMLOutput::characters(const nsAString& aData, PRBool aDOE)
 
169
{
 
170
    closePrevious(eCloseElement);
 
171
 
 
172
    if (mBadChildLevel) {
 
173
        return;
 
174
    }
 
175
 
 
176
    mText.Append(aData);
 
177
}
 
178
 
 
179
void txMozillaXMLOutput::comment(const nsAString& aData)
 
180
{
 
181
    closePrevious(eCloseElement | eFlushText);
 
182
 
 
183
    if (mBadChildLevel) {
 
184
        return;
 
185
    }
 
186
 
 
187
    TX_ENSURE_CURRENTNODE;
 
188
 
 
189
    nsCOMPtr<nsIDOMComment> comment;
 
190
    nsresult rv = mDocument->CreateComment(aData,
 
191
                                           getter_AddRefs(comment));
 
192
    NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create comment");
 
193
    nsCOMPtr<nsIDOMNode> resultNode;
 
194
    rv = mCurrentNode->AppendChild(comment, getter_AddRefs(resultNode));
 
195
    NS_ASSERTION(NS_SUCCEEDED(rv), "Can't append comment");
 
196
}
 
197
 
 
198
void txMozillaXMLOutput::endDocument()
 
199
{
 
200
    closePrevious(eCloseElement | eFlushText);
 
201
    // This should really be handled by nsIDocument::Reset
 
202
    if (mCreatingNewDocument && !mHaveTitleElement) {
 
203
        nsCOMPtr<nsIDOMNSDocument> domDoc = do_QueryInterface(mDocument);
 
204
        if (domDoc) {
 
205
            domDoc->SetTitle(nsString());
 
206
        }
 
207
    }
 
208
 
 
209
    if (!mRefreshString.IsEmpty()) {
 
210
        nsCOMPtr<nsIDocument> doc = do_QueryInterface(mDocument);
 
211
        nsIScriptGlobalObject *sgo = doc->GetScriptGlobalObject();
 
212
        if (sgo) {
 
213
            nsCOMPtr<nsIRefreshURI> refURI =
 
214
                do_QueryInterface(sgo->GetDocShell());
 
215
            if (refURI) {
 
216
                refURI->SetupRefreshURIFromHeader(doc->GetBaseURI(),
 
217
                                                  mRefreshString);
 
218
            }
 
219
        }
 
220
    }
 
221
 
 
222
    if (mNotifier) {
 
223
        mNotifier->OnTransformEnd();
 
224
    }
 
225
}
 
226
 
 
227
void txMozillaXMLOutput::endElement(const nsAString& aName, const PRInt32 aNsID)
 
228
{
 
229
    TX_ENSURE_CURRENTNODE;
 
230
 
 
231
    if (mBadChildLevel) {
 
232
        --mBadChildLevel;
 
233
        PR_LOG(txLog::xslt, PR_LOG_DEBUG,
 
234
               ("endElement, mBadChildLevel = %d\n", mBadChildLevel));
 
235
        return;
 
236
    }
 
237
    
 
238
#ifdef DEBUG
 
239
    if (mTableState != ADDED_TBODY) {
 
240
        nsAutoString nodeName;
 
241
        mCurrentNode->GetNodeName(nodeName);
 
242
        NS_ASSERTION(nodeName.Equals(aName,
 
243
                                     nsCaseInsensitiveStringComparator()),
 
244
                     "Unbalanced startElement and endElement calls!");
 
245
    }
 
246
    else {
 
247
        nsCOMPtr<nsIDOMNode> parent;
 
248
        mCurrentNode->GetParentNode(getter_AddRefs(parent));
 
249
        nsAutoString nodeName;
 
250
        parent->GetNodeName(nodeName);
 
251
        NS_ASSERTION(nodeName.Equals(aName,
 
252
                                     nsCaseInsensitiveStringComparator()),
 
253
                     "Unbalanced startElement and endElement calls!");
 
254
    }
 
255
#endif
 
256
 
 
257
    closePrevious(eCloseElement | eFlushText);
 
258
 
 
259
    // Handle html-elements
 
260
    if ((mOutputFormat.mMethod == eHTMLOutput && aNsID == kNameSpaceID_None) ||
 
261
        aNsID == kNameSpaceID_XHTML) {
 
262
        nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mCurrentNode);
 
263
        NS_ASSERTION(element, "endElement'ing non-element");
 
264
        endHTMLElement(element);
 
265
    }
 
266
 
 
267
    // Add the element to the tree if it wasn't added before and take one step
 
268
    // up the tree
 
269
    // we can't use GetParentNode to check if mCurrentNode is the
 
270
    // "non-added node" since that does strange things when we've called
 
271
    // SetDocument manually
 
272
    if (mCurrentNode == mNonAddedNode) {
 
273
        nsCOMPtr<nsIDocument> document = do_QueryInterface(mNonAddedParent);
 
274
        if (document && !mRootContent) {
 
275
            mRootContent = do_QueryInterface(mCurrentNode);
 
276
            mRootContent->SetDocument(document, PR_FALSE, PR_TRUE);
 
277
            document->SetRootContent(mRootContent);
 
278
        }
 
279
        else {
 
280
            nsCOMPtr<nsIDOMNode> resultNode;
 
281
            mNonAddedParent->AppendChild(mCurrentNode,
 
282
                                         getter_AddRefs(resultNode));
 
283
        }
 
284
        mCurrentNode = mNonAddedParent;
 
285
        mNonAddedParent = nsnull;
 
286
        mNonAddedNode = nsnull;
 
287
    }
 
288
    else {
 
289
        nsCOMPtr<nsIDOMNode> parent;
 
290
        mCurrentNode->GetParentNode(getter_AddRefs(parent));
 
291
        mCurrentNode = parent;
 
292
    }
 
293
 
 
294
    mTableState =
 
295
        NS_STATIC_CAST(TableState, NS_PTR_TO_INT32(mTableStateStack.pop()));
 
296
}
 
297
 
 
298
void txMozillaXMLOutput::getOutputDocument(nsIDOMDocument** aDocument)
 
299
{
 
300
    *aDocument = mDocument;
 
301
    NS_IF_ADDREF(*aDocument);
 
302
}
 
303
 
 
304
void txMozillaXMLOutput::processingInstruction(const nsAString& aTarget, const nsAString& aData)
 
305
{
 
306
    if (mOutputFormat.mMethod == eHTMLOutput)
 
307
        return;
 
308
 
 
309
    closePrevious(eCloseElement | eFlushText);
 
310
 
 
311
    TX_ENSURE_CURRENTNODE;
 
312
 
 
313
    nsCOMPtr<nsIDOMProcessingInstruction> pi;
 
314
    nsresult rv = mDocument->CreateProcessingInstruction(aTarget, aData,
 
315
                                                         getter_AddRefs(pi));
 
316
    NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create processing instruction");
 
317
    if (NS_FAILED(rv))
 
318
        return;
 
319
 
 
320
    nsCOMPtr<nsIStyleSheetLinkingElement> ssle;
 
321
    if (mCreatingNewDocument) {
 
322
        ssle = do_QueryInterface(pi);
 
323
        if (ssle) {
 
324
            ssle->InitStyleLinkElement(nsnull, PR_FALSE);
 
325
            ssle->SetEnableUpdates(PR_FALSE);
 
326
        }
 
327
    }
 
328
 
 
329
    nsCOMPtr<nsIDOMNode> resultNode;
 
330
    rv = mCurrentNode->AppendChild(pi, getter_AddRefs(resultNode));
 
331
    NS_ASSERTION(NS_SUCCEEDED(rv), "Can't append processing instruction");
 
332
    if (NS_FAILED(rv))
 
333
        return;
 
334
 
 
335
    if (ssle) {
 
336
        ssle->SetEnableUpdates(PR_TRUE);
 
337
        rv = ssle->UpdateStyleSheet(nsnull, mNotifier);
 
338
        if (rv == NS_ERROR_HTMLPARSER_BLOCK) {
 
339
            nsCOMPtr<nsIStyleSheet> stylesheet;
 
340
            ssle->GetStyleSheet(*getter_AddRefs(stylesheet));
 
341
            if (mNotifier) {
 
342
                mNotifier->AddStyleSheet(stylesheet);
 
343
            }
 
344
        }
 
345
    }
 
346
}
 
347
 
 
348
void txMozillaXMLOutput::startDocument()
 
349
{
 
350
    if (mNotifier) {
 
351
        mNotifier->OnTransformStart();
 
352
    }
 
353
}
 
354
 
 
355
void txMozillaXMLOutput::startElement(const nsAString& aName,
 
356
                                      const PRInt32 aNsID)
 
357
{
 
358
    TX_ENSURE_CURRENTNODE;
 
359
 
 
360
    if (mBadChildLevel) {
 
361
        ++mBadChildLevel;
 
362
        PR_LOG(txLog::xslt, PR_LOG_DEBUG,
 
363
               ("startElement, mBadChildLevel = %d\n", mBadChildLevel));
 
364
        return;
 
365
    }
 
366
 
 
367
    closePrevious(eCloseElement | eFlushText);
 
368
 
 
369
    if (mBadChildLevel) {
 
370
        // eCloseElement couldn't add the parent, we fail as well
 
371
        ++mBadChildLevel;
 
372
        PR_LOG(txLog::xslt, PR_LOG_DEBUG,
 
373
               ("startElement, mBadChildLevel = %d\n", mBadChildLevel));
 
374
        return;
 
375
    }
 
376
 
 
377
    nsresult rv = mTableStateStack.push(NS_INT32_TO_PTR(mTableState));
 
378
    if (NS_FAILED(rv)) {
 
379
        return;
 
380
    }
 
381
    mTableState = NORMAL;
 
382
 
 
383
    nsCOMPtr<nsIDOMElement> element;
 
384
    mDontAddCurrent = PR_FALSE;
 
385
 
 
386
    if ((mOutputFormat.mMethod == eHTMLOutput) && (aNsID == kNameSpaceID_None)) {
 
387
        if (mDocumentIsHTML) {
 
388
            rv = mDocument->CreateElement(aName,
 
389
                                          getter_AddRefs(element));
 
390
        }
 
391
        else {
 
392
            nsAutoString lcname;
 
393
            ToLowerCase(aName, lcname);
 
394
            rv = mDocument->CreateElementNS(NS_LITERAL_STRING(kXHTMLNameSpaceURI),
 
395
                                            lcname,
 
396
                                            getter_AddRefs(element));
 
397
        }
 
398
        if (NS_FAILED(rv)) {
 
399
            return;
 
400
        }
 
401
 
 
402
        startHTMLElement(element, PR_FALSE);
 
403
    }
 
404
    else {
 
405
        nsAutoString nsURI;
 
406
        gTxNameSpaceManager->GetNameSpaceURI(aNsID, nsURI);
 
407
        rv = mDocument->CreateElementNS(nsURI, aName,
 
408
                                        getter_AddRefs(element));
 
409
        NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create element");
 
410
        if (NS_FAILED(rv)) {
 
411
            return;
 
412
        }
 
413
 
 
414
        if (aNsID == kNameSpaceID_XHTML)
 
415
            startHTMLElement(element, PR_TRUE);
 
416
    }
 
417
 
 
418
    if (mCreatingNewDocument) {
 
419
        nsCOMPtr<nsIContent> cont = do_QueryInterface(element);
 
420
        NS_ASSERTION(cont, "element doesn't implement nsIContent");
 
421
        nsCOMPtr<nsIDocument> doc = do_QueryInterface(mDocument);
 
422
        cont->SetDocument(doc, PR_FALSE, PR_TRUE);
 
423
    }
 
424
    mParentNode = mCurrentNode;
 
425
    mCurrentNode = do_QueryInterface(element);
 
426
}
 
427
 
 
428
void txMozillaXMLOutput::closePrevious(PRInt8 aAction)
 
429
{
 
430
    TX_ENSURE_CURRENTNODE;
 
431
 
 
432
    nsresult rv;
 
433
    if ((aAction & eCloseElement) && mParentNode) {
 
434
        nsCOMPtr<nsIDocument> document = do_QueryInterface(mParentNode);
 
435
        nsCOMPtr<nsIDOMElement> currentElement = do_QueryInterface(mCurrentNode);
 
436
 
 
437
        if (document && currentElement && mRootContent) {
 
438
            // We already have a document element, but the XSLT spec allows this.
 
439
            // As a workaround, create a wrapper object and use that as the
 
440
            // document element.
 
441
            nsCOMPtr<nsIDOMElement> wrapper;
 
442
 
 
443
            rv = mDocument->CreateElementNS(NS_LITERAL_STRING(kTXNameSpaceURI),
 
444
                                            NS_LITERAL_STRING(kTXWrapper),
 
445
                                            getter_AddRefs(wrapper));
 
446
            NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create wrapper element");
 
447
 
 
448
            nsCOMPtr<nsIDOMNode> child, resultNode;
 
449
            PRUint32 i, childCount = document->GetChildCount();
 
450
            for (i = 0; i < childCount; ++i) {
 
451
                nsCOMPtr<nsIContent> childContent = document->GetChildAt(0);
 
452
                if (childContent == mRootContent) {
 
453
                    document->SetRootContent(nsnull);
 
454
                }
 
455
                child = do_QueryInterface(childContent);
 
456
                wrapper->AppendChild(child, getter_AddRefs(resultNode));
 
457
            }
 
458
 
 
459
            mParentNode = wrapper;
 
460
            mRootContent = do_QueryInterface(wrapper);
 
461
            mRootContent->SetDocument(document, PR_FALSE, PR_TRUE);
 
462
            document->SetRootContent(mRootContent);
 
463
        }
 
464
 
 
465
        if (mDontAddCurrent && !mNonAddedParent) {
 
466
            mNonAddedParent = mParentNode;
 
467
            mNonAddedNode = mCurrentNode;
 
468
        }
 
469
        else {
 
470
            if (document && currentElement && !mRootContent) {
 
471
                mRootContent = do_QueryInterface(mCurrentNode);
 
472
                mRootContent->SetDocument(document, PR_FALSE, PR_TRUE);
 
473
                document->SetRootContent(mRootContent);
 
474
            }
 
475
            else {
 
476
                nsCOMPtr<nsIDOMNode> resultNode;
 
477
 
 
478
                rv = mParentNode->AppendChild(mCurrentNode, getter_AddRefs(resultNode));
 
479
                if (NS_FAILED(rv)) {
 
480
                    mBadChildLevel = 1;
 
481
                    mCurrentNode = mParentNode;
 
482
                    PR_LOG(txLog::xslt, PR_LOG_DEBUG,
 
483
                           ("closePrevious, mBadChildLevel = %d\n",
 
484
                            mBadChildLevel));
 
485
                    // warning to the console
 
486
                    nsCOMPtr<nsIConsoleService> consoleSvc = 
 
487
                        do_GetService("@mozilla.org/consoleservice;1", &rv);
 
488
                    if (consoleSvc) {
 
489
                        consoleSvc->LogStringMessage(
 
490
                            NS_LITERAL_STRING("failed to create XSLT content").get());
 
491
                    }
 
492
                }
 
493
            }
 
494
        }
 
495
        mParentNode = nsnull;
 
496
    }
 
497
    else if ((aAction & eFlushText) && !mText.IsEmpty()) {
 
498
        nsCOMPtr<nsIDOMText> text;
 
499
        rv = mDocument->CreateTextNode(mText, getter_AddRefs(text));
 
500
        NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create text node");
 
501
 
 
502
        nsCOMPtr<nsIDOMNode> resultNode;
 
503
        rv = mCurrentNode->AppendChild(text, getter_AddRefs(resultNode));
 
504
        NS_ASSERTION(NS_SUCCEEDED(rv), "Can't append text node");
 
505
 
 
506
        mText.Truncate();
 
507
    }
 
508
}
 
509
 
 
510
void txMozillaXMLOutput::startHTMLElement(nsIDOMElement* aElement, PRBool aXHTML)
 
511
{
 
512
    nsresult rv = NS_OK;
 
513
    nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
 
514
    nsIAtom *atom = content->Tag();
 
515
 
 
516
    mDontAddCurrent = (atom == txHTMLAtoms::script);
 
517
 
 
518
    if ((atom != txHTMLAtoms::tr || aXHTML) &&
 
519
        NS_PTR_TO_INT32(mTableStateStack.peek()) == ADDED_TBODY) {
 
520
        nsCOMPtr<nsIDOMNode> parent;
 
521
        mCurrentNode->GetParentNode(getter_AddRefs(parent));
 
522
        mCurrentNode.swap(parent);
 
523
        mTableStateStack.pop();
 
524
    }
 
525
 
 
526
    if (atom == txHTMLAtoms::table && !aXHTML) {
 
527
        mTableState = TABLE;
 
528
    }
 
529
    else if (atom == txHTMLAtoms::tr && !aXHTML &&
 
530
             NS_PTR_TO_INT32(mTableStateStack.peek()) == TABLE) {
 
531
        nsCOMPtr<nsIDOMElement> elem;
 
532
        rv = createHTMLElement(NS_LITERAL_STRING("tbody"),
 
533
                               getter_AddRefs(elem));
 
534
        if (NS_FAILED(rv)) {
 
535
            return;
 
536
        }
 
537
        nsCOMPtr<nsIDOMNode> dummy;
 
538
        rv = mCurrentNode->AppendChild(elem, getter_AddRefs(dummy));
 
539
        if (NS_FAILED(rv)) {
 
540
            return;
 
541
        }
 
542
        rv = mTableStateStack.push(NS_INT32_TO_PTR(ADDED_TBODY));
 
543
        if (NS_FAILED(rv)) {
 
544
            return;
 
545
        }
 
546
        mCurrentNode = elem;
 
547
    }
 
548
    else if (atom == txHTMLAtoms::head &&
 
549
             mOutputFormat.mMethod == eHTMLOutput) {
 
550
        // Insert META tag, according to spec, 16.2, like
 
551
        // <META http-equiv="Content-Type" content="text/html; charset=EUC-JP">
 
552
        nsCOMPtr<nsIDOMElement> meta;
 
553
        rv = createHTMLElement(NS_LITERAL_STRING("meta"),
 
554
                               getter_AddRefs(meta));
 
555
        if (NS_FAILED(rv)) {
 
556
            return;
 
557
        }
 
558
        rv = meta->SetAttribute(NS_LITERAL_STRING("http-equiv"),
 
559
                                NS_LITERAL_STRING("Content-Type"));
 
560
        NS_ASSERTION(NS_SUCCEEDED(rv), "Can't set http-equiv on meta");
 
561
        nsAutoString metacontent;
 
562
        metacontent.Append(mOutputFormat.mMediaType);
 
563
        metacontent.Append(NS_LITERAL_STRING("; charset="));
 
564
        metacontent.Append(mOutputFormat.mEncoding);
 
565
        rv = meta->SetAttribute(NS_LITERAL_STRING("content"),
 
566
                                metacontent);
 
567
        NS_ASSERTION(NS_SUCCEEDED(rv), "Can't set content on meta");
 
568
        nsCOMPtr<nsIDOMNode> dummy;
 
569
        rv = aElement->AppendChild(meta, getter_AddRefs(dummy));
 
570
        NS_ASSERTION(NS_SUCCEEDED(rv), "Can't append meta");
 
571
    }
 
572
    else if (mCreatingNewDocument) {
 
573
        nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
 
574
            do_QueryInterface(aElement);
 
575
        if (ssle) {
 
576
            ssle->InitStyleLinkElement(nsnull, PR_FALSE);
 
577
            ssle->SetEnableUpdates(PR_FALSE);
 
578
        }
 
579
    }
 
580
}
 
581
 
 
582
void txMozillaXMLOutput::endHTMLElement(nsIDOMElement* aElement)
 
583
{
 
584
    nsresult rv;
 
585
    nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
 
586
    NS_ASSERTION(content, "Can't QI to nsIContent");
 
587
 
 
588
    nsIAtom *atom = content->Tag();
 
589
 
 
590
    if (mTableState == ADDED_TBODY) {
 
591
        NS_ASSERTION(atom == txHTMLAtoms::tbody,
 
592
                     "Element flagged as added tbody isn't a tbody");
 
593
        nsCOMPtr<nsIDOMNode> parent;
 
594
        mCurrentNode->GetParentNode(getter_AddRefs(parent));
 
595
        mCurrentNode = parent;
 
596
        mTableState = NS_STATIC_CAST(TableState,
 
597
                                     NS_PTR_TO_INT32(mTableStateStack.pop()));
 
598
 
 
599
        return;
 
600
    }
 
601
 
 
602
    // Load scripts
 
603
    if (mNotifier && atom == txHTMLAtoms::script) {
 
604
        // Add this script element to the array of loading script elements.
 
605
        nsCOMPtr<nsIDOMHTMLScriptElement> scriptElement =
 
606
            do_QueryInterface(mCurrentNode);
 
607
        NS_ASSERTION(scriptElement, "Need script element");
 
608
        mNotifier->AddScriptElement(scriptElement);
 
609
    }
 
610
    // Set document title
 
611
    else if (mCreatingNewDocument &&
 
612
             atom == txHTMLAtoms::title && !mHaveTitleElement) {
 
613
        // The first title wins
 
614
        mHaveTitleElement = PR_TRUE;
 
615
        nsCOMPtr<nsIDOMNSDocument> domDoc = do_QueryInterface(mDocument);
 
616
        nsCOMPtr<nsIDOMNode> textNode;
 
617
        aElement->GetFirstChild(getter_AddRefs(textNode));
 
618
        if (domDoc && textNode) {
 
619
            nsAutoString text;
 
620
            textNode->GetNodeValue(text);
 
621
            text.CompressWhitespace();
 
622
            domDoc->SetTitle(text);
 
623
        }
 
624
    }
 
625
    else if (mCreatingNewDocument && atom == txHTMLAtoms::base &&
 
626
             !mHaveBaseElement) {
 
627
        // The first base wins
 
628
        mHaveBaseElement = PR_TRUE;
 
629
 
 
630
        nsCOMPtr<nsIDocument> doc = do_QueryInterface(mDocument);
 
631
        NS_ASSERTION(doc, "document doesn't implement nsIDocument");
 
632
        nsAutoString value;
 
633
        content->GetAttr(kNameSpaceID_None, txHTMLAtoms::target, value);
 
634
        doc->SetBaseTarget(value);
 
635
 
 
636
        content->GetAttr(kNameSpaceID_None, txHTMLAtoms::href, value);
 
637
        nsCOMPtr<nsIURI> baseURI;
 
638
        rv = NS_NewURI(getter_AddRefs(baseURI), value, nsnull);
 
639
        if (NS_FAILED(rv))
 
640
            return;
 
641
        doc->SetBaseURI(baseURI); // The document checks if it is legal to set this base
 
642
    }
 
643
    else if (mCreatingNewDocument && atom == txHTMLAtoms::meta) {
 
644
        // handle HTTP-EQUIV data
 
645
        nsAutoString httpEquiv;
 
646
        content->GetAttr(kNameSpaceID_None, txHTMLAtoms::httpEquiv, httpEquiv);
 
647
        if (httpEquiv.IsEmpty())
 
648
            return;
 
649
 
 
650
        nsAutoString value;
 
651
        content->GetAttr(kNameSpaceID_None, txHTMLAtoms::content, value);
 
652
        if (value.IsEmpty())
 
653
            return;
 
654
        
 
655
        TX_ToLowerCase(httpEquiv);
 
656
        nsCOMPtr<nsIAtom> header = do_GetAtom(httpEquiv);
 
657
        processHTTPEquiv(header, value);
 
658
    }
 
659
 
 
660
    // Handle all sorts of stylesheets
 
661
    if (mCreatingNewDocument) {
 
662
        nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
 
663
            do_QueryInterface(aElement);
 
664
        if (ssle) {
 
665
            ssle->SetEnableUpdates(PR_TRUE);
 
666
            rv = ssle->UpdateStyleSheet(nsnull, mNotifier);
 
667
            if (rv == NS_ERROR_HTMLPARSER_BLOCK) {
 
668
                nsCOMPtr<nsIStyleSheet> stylesheet;
 
669
                ssle->GetStyleSheet(*getter_AddRefs(stylesheet));
 
670
                if (mNotifier) {
 
671
                    mNotifier->AddStyleSheet(stylesheet);
 
672
                }
 
673
            }
 
674
        }
 
675
    }
 
676
}
 
677
 
 
678
void txMozillaXMLOutput::processHTTPEquiv(nsIAtom* aHeader, const nsAString& aValue)
 
679
{
 
680
    // For now we only handle "refresh". There's a longer list in
 
681
    // HTMLContentSink::ProcessHeaderData
 
682
    if (aHeader == txHTMLAtoms::refresh)
 
683
        CopyUCS2toASCII(aValue, mRefreshString);
 
684
}
 
685
 
 
686
nsresult
 
687
txMozillaXMLOutput::createResultDocument(const nsAString& aName, PRInt32 aNsID,
 
688
                                         nsIDOMDocument* aSourceDocument,
 
689
                                         nsIDOMDocument* aResultDocument)
 
690
{
 
691
    nsresult rv;
 
692
 
 
693
    nsCOMPtr<nsIDocument> doc;
 
694
    if (!aResultDocument) {
 
695
        // Create the document
 
696
        if (mOutputFormat.mMethod == eHTMLOutput) {
 
697
            doc = do_CreateInstance(kHTMLDocumentCID, &rv);
 
698
            NS_ENSURE_SUCCESS(rv, rv);
 
699
 
 
700
            mDocumentIsHTML = PR_TRUE;
 
701
        }
 
702
        else {
 
703
            // We should check the root name/namespace here and create the
 
704
            // appropriate document
 
705
            doc = do_CreateInstance(kXMLDocumentCID, &rv);
 
706
            NS_ENSURE_SUCCESS(rv, rv);
 
707
 
 
708
            mDocumentIsHTML = PR_FALSE;
 
709
        }
 
710
        mDocument = do_QueryInterface(doc);
 
711
    }
 
712
    else {
 
713
        mDocument = aResultDocument;
 
714
        doc = do_QueryInterface(aResultDocument);
 
715
        
 
716
        nsCOMPtr<nsIDocument> doc = do_QueryInterface(aResultDocument);
 
717
        mDocumentIsHTML = doc && !doc->IsCaseSensitive();
 
718
    }
 
719
 
 
720
    mCurrentNode = mDocument;
 
721
 
 
722
    // Reset and set up the document
 
723
    URIUtils::ResetWithSource(doc, aSourceDocument);
 
724
 
 
725
    // Set the charset
 
726
    if (!mOutputFormat.mEncoding.IsEmpty()) {
 
727
        doc->SetDocumentCharacterSet(
 
728
            NS_LossyConvertUTF16toASCII(mOutputFormat.mEncoding));
 
729
        doc->SetDocumentCharacterSetSource(kCharsetFromOtherComponent);
 
730
    }
 
731
 
 
732
    // Set the mime-type
 
733
    if (!mOutputFormat.mMediaType.IsEmpty()) {
 
734
        doc->SetContentType(mOutputFormat.mMediaType);
 
735
    }
 
736
    else if (mOutputFormat.mMethod == eHTMLOutput) {
 
737
        doc->SetContentType(NS_LITERAL_STRING("text/html"));
 
738
    }
 
739
    else {
 
740
        doc->SetContentType(NS_LITERAL_STRING("text/xml"));
 
741
    }
 
742
 
 
743
    // Set up script loader of the result document.
 
744
    nsIScriptLoader *loader = doc->GetScriptLoader();
 
745
    if (loader) {
 
746
        if (mNotifier) {
 
747
            loader->AddObserver(mNotifier);
 
748
        }
 
749
        else {
 
750
            // Don't load scripts, we can't notify the caller when they're loaded.
 
751
            loader->SetEnabled(PR_FALSE);
 
752
        }
 
753
    }
 
754
 
 
755
    if (mNotifier) {
 
756
        mNotifier->SetOutputDocument(mDocument);
 
757
    }
 
758
 
 
759
    // Do this after calling OnDocumentCreated to ensure that the
 
760
    // PresShell/PresContext has been hooked up and get notified.
 
761
    nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
 
762
    if (htmlDoc) {
 
763
        htmlDoc->SetCompatibilityMode(eCompatibility_FullStandards);
 
764
    }
 
765
 
 
766
    // Add a doc-type if requested
 
767
    if (!mOutputFormat.mSystemId.IsEmpty()) {
 
768
        nsCOMPtr<nsIDOMDOMImplementation> implementation;
 
769
        rv = aSourceDocument->GetImplementation(getter_AddRefs(implementation));
 
770
        NS_ENSURE_SUCCESS(rv, rv);
 
771
        nsAutoString qName;
 
772
        if (mOutputFormat.mMethod == eHTMLOutput) {
 
773
            qName.Assign(NS_LITERAL_STRING("html"));
 
774
        }
 
775
        else {
 
776
            qName.Assign(aName);
 
777
        }
 
778
        nsCOMPtr<nsIDOMDocumentType> documentType;
 
779
        rv = implementation->CreateDocumentType(qName,
 
780
                                                mOutputFormat.mPublicId,
 
781
                                                mOutputFormat.mSystemId,
 
782
                                                getter_AddRefs(documentType));
 
783
        NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create doctype");
 
784
        nsCOMPtr<nsIDOMNode> tmp;
 
785
        mDocument->AppendChild(documentType, getter_AddRefs(tmp));
 
786
    }
 
787
 
 
788
    return NS_OK;
 
789
}
 
790
 
 
791
nsresult
 
792
txMozillaXMLOutput::createHTMLElement(const nsAString& aName,
 
793
                                      nsIDOMElement** aResult)
 
794
{
 
795
    if (mDocumentIsHTML) {
 
796
        return mDocument->CreateElement(aName, aResult);
 
797
    }
 
798
 
 
799
    return mDocument->CreateElementNS(NS_LITERAL_STRING(kXHTMLNameSpaceURI),
 
800
                                      aName, aResult);
 
801
}
 
802
 
 
803
txTransformNotifier::txTransformNotifier()
 
804
    : mInTransform(PR_FALSE)
 
805
      
 
806
{
 
807
}
 
808
 
 
809
txTransformNotifier::~txTransformNotifier()
 
810
{
 
811
}
 
812
 
 
813
NS_IMPL_ISUPPORTS2(txTransformNotifier,
 
814
                   nsIScriptLoaderObserver,
 
815
                   nsICSSLoaderObserver)
 
816
 
 
817
NS_IMETHODIMP
 
818
txTransformNotifier::ScriptAvailable(nsresult aResult, 
 
819
                                     nsIDOMHTMLScriptElement *aElement, 
 
820
                                     PRBool aIsInline,
 
821
                                     PRBool aWasPending,
 
822
                                     nsIURI *aURI, 
 
823
                                     PRInt32 aLineNo,
 
824
                                     const nsAString& aScript)
 
825
{
 
826
    if (NS_FAILED(aResult)) {
 
827
        mScriptElements.RemoveObject(aElement);
 
828
        SignalTransformEnd();
 
829
    }
 
830
 
 
831
    return NS_OK;
 
832
}
 
833
 
 
834
NS_IMETHODIMP 
 
835
txTransformNotifier::ScriptEvaluated(nsresult aResult, 
 
836
                                     nsIDOMHTMLScriptElement *aElement,
 
837
                                     PRBool aIsInline,
 
838
                                     PRBool aWasPending)
 
839
{
 
840
    mScriptElements.RemoveObject(aElement);
 
841
    SignalTransformEnd();
 
842
    return NS_OK;
 
843
}
 
844
 
 
845
NS_IMETHODIMP 
 
846
txTransformNotifier::StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aNotify)
 
847
{
 
848
    // Check that the stylesheet was in the mStylesheets array, if not it is an
 
849
    // alternate and we don't want to call SignalTransformEnd since we don't
 
850
    // wait on alternates before calling OnTransformDone and so the load of the
 
851
    // alternate could finish after we called OnTransformDone already.
 
852
    // See http://bugzilla.mozilla.org/show_bug.cgi?id=215465.
 
853
    if (mStylesheets.RemoveObject(aSheet)) {
 
854
        SignalTransformEnd();
 
855
    }
 
856
 
 
857
    return NS_OK;
 
858
}
 
859
 
 
860
void
 
861
txTransformNotifier::Init(nsITransformObserver* aObserver)
 
862
{
 
863
    mObserver = aObserver;
 
864
}
 
865
 
 
866
void
 
867
txTransformNotifier::AddScriptElement(nsIDOMHTMLScriptElement* aElement)
 
868
{
 
869
    mScriptElements.AppendObject(aElement);
 
870
}
 
871
 
 
872
void
 
873
txTransformNotifier::AddStyleSheet(nsIStyleSheet* aStyleSheet)
 
874
{
 
875
    mStylesheets.AppendObject(aStyleSheet);
 
876
}
 
877
 
 
878
void
 
879
txTransformNotifier::OnTransformEnd()
 
880
{
 
881
    mInTransform = PR_FALSE;
 
882
    SignalTransformEnd();
 
883
}
 
884
 
 
885
void
 
886
txTransformNotifier::OnTransformStart()
 
887
{
 
888
    mInTransform = PR_TRUE;
 
889
}
 
890
 
 
891
void
 
892
txTransformNotifier::SetOutputDocument(nsIDOMDocument* aDocument)
 
893
{
 
894
    mDocument = aDocument;
 
895
 
 
896
    // Notify the contentsink that the document is created
 
897
    mObserver->OnDocumentCreated(mDocument);
 
898
}
 
899
 
 
900
void
 
901
txTransformNotifier::SignalTransformEnd()
 
902
{
 
903
    if (mInTransform || mScriptElements.Count() > 0 ||
 
904
        mStylesheets.Count() > 0) {
 
905
        return;
 
906
    }
 
907
 
 
908
    // Make sure that we don't get deleted while this function is executed and
 
909
    // we remove ourselfs from the scriptloader
 
910
    nsCOMPtr<nsIScriptLoaderObserver> kungFuDeathGrip(this);
 
911
 
 
912
    // XXX Need a better way to determine transform success/failure
 
913
    if (mDocument) {
 
914
        nsCOMPtr<nsIDocument> doc = do_QueryInterface(mDocument);
 
915
        nsIScriptLoader *loader = doc->GetScriptLoader();
 
916
        if (loader) {
 
917
            loader->RemoveObserver(this);
 
918
        }
 
919
 
 
920
        mObserver->OnTransformDone(NS_OK, mDocument);
 
921
    }
 
922
    else {
 
923
        // XXX Need better error message and code.
 
924
        mObserver->OnTransformDone(NS_ERROR_FAILURE, nsnull);
 
925
    }
 
926
}