/src/mozilla-central/dom/xul/nsXULContentSink.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* vim: set ts=8 sts=4 et sw=4 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | /* |
8 | | * An implementation for a Gecko-style content sink that knows how |
9 | | * to build a content model (the "prototype" document) from XUL. |
10 | | * |
11 | | * For more information on XUL, |
12 | | * see http://developer.mozilla.org/en/docs/XUL |
13 | | */ |
14 | | |
15 | | #include "nsXULContentSink.h" |
16 | | |
17 | | #include "jsfriendapi.h" |
18 | | |
19 | | #include "nsCOMPtr.h" |
20 | | #include "nsHTMLStyleSheet.h" |
21 | | #include "nsIContentSink.h" |
22 | | #include "nsIDocument.h" |
23 | | #include "nsIDOMEventListener.h" |
24 | | #include "nsIFormControl.h" |
25 | | #include "mozilla/dom/NodeInfo.h" |
26 | | #include "nsIScriptContext.h" |
27 | | #include "nsIScriptGlobalObject.h" |
28 | | #include "nsIServiceManager.h" |
29 | | #include "nsIURL.h" |
30 | | #include "nsNameSpaceManager.h" |
31 | | #include "nsParserBase.h" |
32 | | #include "nsViewManager.h" |
33 | | #include "nsIScriptSecurityManager.h" |
34 | | #include "nsLayoutCID.h" |
35 | | #include "nsNetUtil.h" |
36 | | #include "nsString.h" |
37 | | #include "nsReadableUtils.h" |
38 | | #include "nsXULElement.h" |
39 | | #include "mozilla/Logging.h" |
40 | | #include "nsCRT.h" |
41 | | |
42 | | #include "nsXULPrototypeDocument.h" // XXXbe temporary |
43 | | #include "mozilla/css/Loader.h" |
44 | | |
45 | | #include "nsUnicharUtils.h" |
46 | | #include "nsGkAtoms.h" |
47 | | #include "nsContentUtils.h" |
48 | | #include "nsAttrName.h" |
49 | | #include "nsXMLContentSink.h" |
50 | | #include "nsIConsoleService.h" |
51 | | #include "nsIScriptError.h" |
52 | | #include "nsContentTypeParser.h" |
53 | | #include "XULDocument.h" |
54 | | |
55 | | static mozilla::LazyLogModule gContentSinkLog("nsXULContentSink");; |
56 | | |
57 | | //---------------------------------------------------------------------- |
58 | | |
59 | | XULContentSinkImpl::ContextStack::ContextStack() |
60 | | : mTop(nullptr), mDepth(0) |
61 | 0 | { |
62 | 0 | } |
63 | | |
64 | | XULContentSinkImpl::ContextStack::~ContextStack() |
65 | 0 | { |
66 | 0 | while (mTop) { |
67 | 0 | Entry* doomed = mTop; |
68 | 0 | mTop = mTop->mNext; |
69 | 0 | delete doomed; |
70 | 0 | } |
71 | 0 | } |
72 | | |
73 | | nsresult |
74 | | XULContentSinkImpl::ContextStack::Push(nsXULPrototypeNode* aNode, State aState) |
75 | 0 | { |
76 | 0 | Entry* entry = new Entry(aNode, aState, mTop); |
77 | 0 |
|
78 | 0 | mTop = entry; |
79 | 0 |
|
80 | 0 | ++mDepth; |
81 | 0 | return NS_OK; |
82 | 0 | } |
83 | | |
84 | | nsresult |
85 | | XULContentSinkImpl::ContextStack::Pop(State* aState) |
86 | 0 | { |
87 | 0 | if (mDepth == 0) |
88 | 0 | return NS_ERROR_UNEXPECTED; |
89 | 0 | |
90 | 0 | Entry* entry = mTop; |
91 | 0 | mTop = mTop->mNext; |
92 | 0 | --mDepth; |
93 | 0 |
|
94 | 0 | *aState = entry->mState; |
95 | 0 | delete entry; |
96 | 0 |
|
97 | 0 | return NS_OK; |
98 | 0 | } |
99 | | |
100 | | |
101 | | nsresult |
102 | | XULContentSinkImpl::ContextStack::GetTopNode(RefPtr<nsXULPrototypeNode>& aNode) |
103 | 0 | { |
104 | 0 | if (mDepth == 0) |
105 | 0 | return NS_ERROR_UNEXPECTED; |
106 | 0 | |
107 | 0 | aNode = mTop->mNode; |
108 | 0 | return NS_OK; |
109 | 0 | } |
110 | | |
111 | | |
112 | | nsresult |
113 | | XULContentSinkImpl::ContextStack::GetTopChildren(nsPrototypeArray** aChildren) |
114 | 0 | { |
115 | 0 | if (mDepth == 0) |
116 | 0 | return NS_ERROR_UNEXPECTED; |
117 | 0 | |
118 | 0 | *aChildren = &(mTop->mChildren); |
119 | 0 | return NS_OK; |
120 | 0 | } |
121 | | |
122 | | void |
123 | | XULContentSinkImpl::ContextStack::Clear() |
124 | 0 | { |
125 | 0 | Entry *cur = mTop; |
126 | 0 | while (cur) { |
127 | 0 | // Release the root element (and its descendants). |
128 | 0 | Entry *next = cur->mNext; |
129 | 0 | delete cur; |
130 | 0 | cur = next; |
131 | 0 | } |
132 | 0 |
|
133 | 0 | mTop = nullptr; |
134 | 0 | mDepth = 0; |
135 | 0 | } |
136 | | |
137 | | void |
138 | | XULContentSinkImpl::ContextStack::Traverse(nsCycleCollectionTraversalCallback& aCb) |
139 | 0 | { |
140 | 0 | nsCycleCollectionTraversalCallback& cb = aCb; |
141 | 0 | for (ContextStack::Entry* tmp = mTop; tmp; tmp = tmp->mNext) { |
142 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode) |
143 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildren) |
144 | 0 | } |
145 | 0 | } |
146 | | |
147 | | //---------------------------------------------------------------------- |
148 | | |
149 | | |
150 | | XULContentSinkImpl::XULContentSinkImpl() |
151 | | : mText(nullptr), |
152 | | mTextLength(0), |
153 | | mTextSize(0), |
154 | | mConstrainSize(true), |
155 | | mState(eInProlog) |
156 | 0 | { |
157 | 0 | } |
158 | | |
159 | | |
160 | | XULContentSinkImpl::~XULContentSinkImpl() |
161 | 0 | { |
162 | 0 | // The context stack _should_ be empty, unless something has gone wrong. |
163 | 0 | NS_ASSERTION(mContextStack.Depth() == 0, "Context stack not empty?"); |
164 | 0 | mContextStack.Clear(); |
165 | 0 |
|
166 | 0 | free(mText); |
167 | 0 | } |
168 | | |
169 | | //---------------------------------------------------------------------- |
170 | | // nsISupports interface |
171 | | |
172 | | NS_IMPL_CYCLE_COLLECTION_CLASS(XULContentSinkImpl) |
173 | | |
174 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XULContentSinkImpl) |
175 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager) |
176 | 0 | tmp->mContextStack.Clear(); |
177 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototype) |
178 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser) |
179 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
180 | | |
181 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XULContentSinkImpl) |
182 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager) |
183 | 0 | tmp->mContextStack.Traverse(cb); |
184 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototype) |
185 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser) |
186 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
187 | | |
188 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULContentSinkImpl) |
189 | 0 | NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXMLContentSink) |
190 | 0 | NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink) |
191 | 0 | NS_INTERFACE_MAP_ENTRY(nsIExpatSink) |
192 | 0 | NS_INTERFACE_MAP_ENTRY(nsIContentSink) |
193 | 0 | NS_INTERFACE_MAP_END |
194 | | |
195 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(XULContentSinkImpl) |
196 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(XULContentSinkImpl) |
197 | | |
198 | | //---------------------------------------------------------------------- |
199 | | // nsIContentSink interface |
200 | | |
201 | | NS_IMETHODIMP |
202 | | XULContentSinkImpl::WillBuildModel(nsDTDMode aDTDMode) |
203 | 0 | { |
204 | | #if FIXME |
205 | | if (! mParentContentSink) { |
206 | | // If we're _not_ an overlay, then notify the document that |
207 | | // the load is beginning. |
208 | | mDocument->BeginLoad(); |
209 | | } |
210 | | #endif |
211 | |
|
212 | 0 | return NS_OK; |
213 | 0 | } |
214 | | |
215 | | NS_IMETHODIMP |
216 | | XULContentSinkImpl::DidBuildModel(bool aTerminated) |
217 | 0 | { |
218 | 0 | nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument); |
219 | 0 | if (doc) { |
220 | 0 | doc->EndLoad(); |
221 | 0 | mDocument = nullptr; |
222 | 0 | } |
223 | 0 |
|
224 | 0 | // Drop our reference to the parser to get rid of a circular |
225 | 0 | // reference. |
226 | 0 | mParser = nullptr; |
227 | 0 | return NS_OK; |
228 | 0 | } |
229 | | |
230 | | NS_IMETHODIMP |
231 | | XULContentSinkImpl::WillInterrupt(void) |
232 | 0 | { |
233 | 0 | // XXX Notify the docshell, if necessary |
234 | 0 | return NS_OK; |
235 | 0 | } |
236 | | |
237 | | NS_IMETHODIMP |
238 | | XULContentSinkImpl::WillResume(void) |
239 | 0 | { |
240 | 0 | // XXX Notify the docshell, if necessary |
241 | 0 | return NS_OK; |
242 | 0 | } |
243 | | |
244 | | NS_IMETHODIMP |
245 | | XULContentSinkImpl::SetParser(nsParserBase* aParser) |
246 | 0 | { |
247 | 0 | mParser = aParser; |
248 | 0 | return NS_OK; |
249 | 0 | } |
250 | | |
251 | | void |
252 | | XULContentSinkImpl::SetDocumentCharset(NotNull<const Encoding*> aEncoding) |
253 | 0 | { |
254 | 0 | nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument); |
255 | 0 | if (doc) { |
256 | 0 | doc->SetDocumentCharacterSet(aEncoding); |
257 | 0 | } |
258 | 0 | } |
259 | | |
260 | | nsISupports * |
261 | | XULContentSinkImpl::GetTarget() |
262 | 0 | { |
263 | 0 | nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument); |
264 | 0 | return doc; |
265 | 0 | } |
266 | | |
267 | | //---------------------------------------------------------------------- |
268 | | |
269 | | nsresult |
270 | | XULContentSinkImpl::Init(nsIDocument* aDocument, |
271 | | nsXULPrototypeDocument* aPrototype) |
272 | 0 | { |
273 | 0 | MOZ_ASSERT(aDocument != nullptr, "null ptr"); |
274 | 0 | if (! aDocument) |
275 | 0 | return NS_ERROR_NULL_POINTER; |
276 | 0 | |
277 | 0 | mDocument = do_GetWeakReference(aDocument); |
278 | 0 | mPrototype = aPrototype; |
279 | 0 |
|
280 | 0 | mDocumentURL = mPrototype->GetURI(); |
281 | 0 | mNodeInfoManager = aPrototype->GetNodeInfoManager(); |
282 | 0 | if (! mNodeInfoManager) |
283 | 0 | return NS_ERROR_UNEXPECTED; |
284 | 0 | |
285 | 0 | mState = eInProlog; |
286 | 0 | return NS_OK; |
287 | 0 | } |
288 | | |
289 | | |
290 | | //---------------------------------------------------------------------- |
291 | | // |
292 | | // Text buffering |
293 | | // |
294 | | |
295 | | bool |
296 | | XULContentSinkImpl::IsDataInBuffer(char16_t* buffer, int32_t length) |
297 | 0 | { |
298 | 0 | for (int32_t i = 0; i < length; ++i) { |
299 | 0 | if (buffer[i] == ' ' || |
300 | 0 | buffer[i] == '\t' || |
301 | 0 | buffer[i] == '\n' || |
302 | 0 | buffer[i] == '\r') |
303 | 0 | continue; |
304 | 0 | |
305 | 0 | return true; |
306 | 0 | } |
307 | 0 | return false; |
308 | 0 | } |
309 | | |
310 | | |
311 | | nsresult |
312 | | XULContentSinkImpl::FlushText(bool aCreateTextNode) |
313 | 0 | { |
314 | 0 | nsresult rv; |
315 | 0 |
|
316 | 0 | do { |
317 | 0 | // Don't do anything if there's no text to create a node from, or |
318 | 0 | // if they've told us not to create a text node |
319 | 0 | if (! mTextLength) |
320 | 0 | break; |
321 | 0 | |
322 | 0 | if (! aCreateTextNode) |
323 | 0 | break; |
324 | 0 | |
325 | 0 | RefPtr<nsXULPrototypeNode> node; |
326 | 0 | rv = mContextStack.GetTopNode(node); |
327 | 0 | if (NS_FAILED(rv)) return rv; |
328 | 0 | |
329 | 0 | bool stripWhitespace = false; |
330 | 0 | if (node->mType == nsXULPrototypeNode::eType_Element) { |
331 | 0 | mozilla::dom::NodeInfo *nodeInfo = |
332 | 0 | static_cast<nsXULPrototypeElement*>(node.get())->mNodeInfo; |
333 | 0 |
|
334 | 0 | if (nodeInfo->NamespaceEquals(kNameSpaceID_XUL)) |
335 | 0 | stripWhitespace = !nodeInfo->Equals(nsGkAtoms::label) && |
336 | 0 | !nodeInfo->Equals(nsGkAtoms::description); |
337 | 0 | } |
338 | 0 |
|
339 | 0 | // Don't bother if there's nothing but whitespace. |
340 | 0 | if (stripWhitespace && ! IsDataInBuffer(mText, mTextLength)) |
341 | 0 | break; |
342 | 0 | |
343 | 0 | // Don't bother if we're not in XUL document body |
344 | 0 | if (mState != eInDocumentElement || mContextStack.Depth() == 0) |
345 | 0 | break; |
346 | 0 | |
347 | 0 | nsXULPrototypeText* text = new nsXULPrototypeText(); |
348 | 0 | text->mValue.Assign(mText, mTextLength); |
349 | 0 | if (stripWhitespace) |
350 | 0 | text->mValue.Trim(" \t\n\r"); |
351 | 0 |
|
352 | 0 | // hook it up |
353 | 0 | nsPrototypeArray* children = nullptr; |
354 | 0 | rv = mContextStack.GetTopChildren(&children); |
355 | 0 | if (NS_FAILED(rv)) return rv; |
356 | 0 | |
357 | 0 | // transfer ownership of 'text' to the children array |
358 | 0 | children->AppendElement(text); |
359 | 0 | } while (0); |
360 | 0 |
|
361 | 0 | // Reset our text buffer |
362 | 0 | mTextLength = 0; |
363 | 0 | return NS_OK; |
364 | 0 | } |
365 | | |
366 | | //---------------------------------------------------------------------- |
367 | | |
368 | | nsresult |
369 | | XULContentSinkImpl::NormalizeAttributeString(const char16_t *aExpatName, |
370 | | nsAttrName &aName) |
371 | 0 | { |
372 | 0 | int32_t nameSpaceID; |
373 | 0 | RefPtr<nsAtom> prefix, localName; |
374 | 0 | nsContentUtils::SplitExpatName(aExpatName, getter_AddRefs(prefix), |
375 | 0 | getter_AddRefs(localName), &nameSpaceID); |
376 | 0 |
|
377 | 0 | if (nameSpaceID == kNameSpaceID_None) { |
378 | 0 | aName.SetTo(localName); |
379 | 0 |
|
380 | 0 | return NS_OK; |
381 | 0 | } |
382 | 0 | |
383 | 0 | RefPtr<mozilla::dom::NodeInfo> ni; |
384 | 0 | ni = mNodeInfoManager->GetNodeInfo(localName, prefix, |
385 | 0 | nameSpaceID, |
386 | 0 | nsINode::ATTRIBUTE_NODE); |
387 | 0 | aName.SetTo(ni); |
388 | 0 |
|
389 | 0 | return NS_OK; |
390 | 0 | } |
391 | | |
392 | | nsresult |
393 | | XULContentSinkImpl::CreateElement(mozilla::dom::NodeInfo *aNodeInfo, |
394 | | nsXULPrototypeElement** aResult) |
395 | 0 | { |
396 | 0 | nsXULPrototypeElement* element = new nsXULPrototypeElement(); |
397 | 0 | element->mNodeInfo = aNodeInfo; |
398 | 0 |
|
399 | 0 | *aResult = element; |
400 | 0 | return NS_OK; |
401 | 0 | } |
402 | | |
403 | | /**** BEGIN NEW APIs ****/ |
404 | | |
405 | | NS_IMETHODIMP |
406 | | XULContentSinkImpl::HandleStartElement(const char16_t *aName, |
407 | | const char16_t **aAtts, |
408 | | uint32_t aAttsCount, |
409 | | uint32_t aLineNumber, |
410 | | uint32_t aColumnNumber) |
411 | 0 | { |
412 | 0 | // XXX Hopefully the parser will flag this before we get here. If |
413 | 0 | // we're in the epilog, there should be no new elements |
414 | 0 | MOZ_ASSERT(mState != eInEpilog, "tag in XUL doc epilog"); |
415 | 0 | MOZ_ASSERT(aAttsCount % 2 == 0, "incorrect aAttsCount"); |
416 | 0 |
|
417 | 0 | // Adjust aAttsCount so it's the actual number of attributes |
418 | 0 | aAttsCount /= 2; |
419 | 0 |
|
420 | 0 | if (mState == eInEpilog) |
421 | 0 | return NS_ERROR_UNEXPECTED; |
422 | 0 | |
423 | 0 | if (mState != eInScript) { |
424 | 0 | FlushText(); |
425 | 0 | } |
426 | 0 |
|
427 | 0 | int32_t nameSpaceID; |
428 | 0 | RefPtr<nsAtom> prefix, localName; |
429 | 0 | nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix), |
430 | 0 | getter_AddRefs(localName), &nameSpaceID); |
431 | 0 |
|
432 | 0 | RefPtr<mozilla::dom::NodeInfo> nodeInfo; |
433 | 0 | nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID, |
434 | 0 | nsINode::ELEMENT_NODE); |
435 | 0 |
|
436 | 0 | nsresult rv = NS_OK; |
437 | 0 | switch (mState) { |
438 | 0 | case eInProlog: |
439 | 0 | // We're the root document element |
440 | 0 | rv = OpenRoot(aAtts, aAttsCount, nodeInfo); |
441 | 0 | break; |
442 | 0 |
|
443 | 0 | case eInDocumentElement: |
444 | 0 | rv = OpenTag(aAtts, aAttsCount, aLineNumber, nodeInfo); |
445 | 0 | break; |
446 | 0 |
|
447 | 0 | case eInEpilog: |
448 | 0 | case eInScript: |
449 | 0 | MOZ_LOG(gContentSinkLog, LogLevel::Warning, |
450 | 0 | ("xul: warning: unexpected tags in epilog at line %d", |
451 | 0 | aLineNumber)); |
452 | 0 | rv = NS_ERROR_UNEXPECTED; // XXX |
453 | 0 | break; |
454 | 0 | } |
455 | 0 |
|
456 | 0 | return rv; |
457 | 0 | } |
458 | | |
459 | | NS_IMETHODIMP |
460 | | XULContentSinkImpl::HandleEndElement(const char16_t *aName) |
461 | 0 | { |
462 | 0 | // Never EVER return anything but NS_OK or |
463 | 0 | // NS_ERROR_HTMLPARSER_BLOCK from this method. Doing so will blow |
464 | 0 | // the parser's little mind all over the planet. |
465 | 0 | nsresult rv; |
466 | 0 |
|
467 | 0 | RefPtr<nsXULPrototypeNode> node; |
468 | 0 | rv = mContextStack.GetTopNode(node); |
469 | 0 |
|
470 | 0 | if (NS_FAILED(rv)) { |
471 | 0 | return NS_OK; |
472 | 0 | } |
473 | 0 | |
474 | 0 | switch (node->mType) { |
475 | 0 | case nsXULPrototypeNode::eType_Element: { |
476 | 0 | // Flush any text _now_, so that we'll get text nodes created |
477 | 0 | // before popping the stack. |
478 | 0 | FlushText(); |
479 | 0 |
|
480 | 0 | // Pop the context stack and do prototype hookup. |
481 | 0 | nsPrototypeArray* children = nullptr; |
482 | 0 | rv = mContextStack.GetTopChildren(&children); |
483 | 0 | if (NS_FAILED(rv)) return rv; |
484 | 0 | |
485 | 0 | nsXULPrototypeElement* element = |
486 | 0 | static_cast<nsXULPrototypeElement*>(node.get()); |
487 | 0 |
|
488 | 0 | int32_t count = children->Length(); |
489 | 0 | if (count) { |
490 | 0 | element->mChildren.SetCapacity(count); |
491 | 0 |
|
492 | 0 | for (int32_t i = 0; i < count; ++i) |
493 | 0 | element->mChildren.AppendElement(children->ElementAt(i)); |
494 | 0 |
|
495 | 0 | } |
496 | 0 | } |
497 | 0 | break; |
498 | 0 |
|
499 | 0 | case nsXULPrototypeNode::eType_Script: { |
500 | 0 | nsXULPrototypeScript* script = |
501 | 0 | static_cast<nsXULPrototypeScript*>(node.get()); |
502 | 0 |
|
503 | 0 | // If given a src= attribute, we must ignore script tag content. |
504 | 0 | if (!script->mSrcURI && !script->HasScriptObject()) { |
505 | 0 | nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument); |
506 | 0 |
|
507 | 0 | script->mOutOfLine = false; |
508 | 0 | if (doc) |
509 | 0 | script->Compile(mText, mTextLength, mDocumentURL, |
510 | 0 | script->mLineNo, doc); |
511 | 0 | } |
512 | 0 |
|
513 | 0 | FlushText(false); |
514 | 0 | } |
515 | 0 | break; |
516 | 0 |
|
517 | 0 | default: |
518 | 0 | NS_ERROR("didn't expect that"); |
519 | 0 | break; |
520 | 0 | } |
521 | 0 |
|
522 | 0 | rv = mContextStack.Pop(&mState); |
523 | 0 | NS_ASSERTION(NS_SUCCEEDED(rv), "context stack corrupted"); |
524 | 0 | if (NS_FAILED(rv)) return rv; |
525 | 0 | |
526 | 0 | if (mContextStack.Depth() == 0) { |
527 | 0 | // The root element should -always- be an element, because |
528 | 0 | // it'll have been created via XULContentSinkImpl::OpenRoot(). |
529 | 0 | NS_ASSERTION(node->mType == nsXULPrototypeNode::eType_Element, "root is not an element"); |
530 | 0 | if (node->mType != nsXULPrototypeNode::eType_Element) |
531 | 0 | return NS_ERROR_UNEXPECTED; |
532 | 0 | |
533 | 0 | // Now that we're done parsing, set the prototype document's |
534 | 0 | // root element. This transfers ownership of the prototype |
535 | 0 | // element tree to the prototype document. |
536 | 0 | nsXULPrototypeElement* element = |
537 | 0 | static_cast<nsXULPrototypeElement*>(node.get()); |
538 | 0 |
|
539 | 0 | mPrototype->SetRootElement(element); |
540 | 0 | mState = eInEpilog; |
541 | 0 | } |
542 | 0 |
|
543 | 0 | return NS_OK; |
544 | 0 | } |
545 | | |
546 | | NS_IMETHODIMP |
547 | | XULContentSinkImpl::HandleComment(const char16_t *aName) |
548 | 0 | { |
549 | 0 | FlushText(); |
550 | 0 | return NS_OK; |
551 | 0 | } |
552 | | |
553 | | NS_IMETHODIMP |
554 | | XULContentSinkImpl::HandleCDataSection(const char16_t *aData, uint32_t aLength) |
555 | 0 | { |
556 | 0 | FlushText(); |
557 | 0 | return AddText(aData, aLength); |
558 | 0 | } |
559 | | |
560 | | NS_IMETHODIMP |
561 | | XULContentSinkImpl::HandleDoctypeDecl(const nsAString & aSubset, |
562 | | const nsAString & aName, |
563 | | const nsAString & aSystemId, |
564 | | const nsAString & aPublicId, |
565 | | nsISupports* aCatalogData) |
566 | 0 | { |
567 | 0 | return NS_OK; |
568 | 0 | } |
569 | | |
570 | | NS_IMETHODIMP |
571 | | XULContentSinkImpl::HandleCharacterData(const char16_t *aData, |
572 | | uint32_t aLength) |
573 | 0 | { |
574 | 0 | if (aData && mState != eInProlog && mState != eInEpilog) { |
575 | 0 | return AddText(aData, aLength); |
576 | 0 | } |
577 | 0 | return NS_OK; |
578 | 0 | } |
579 | | |
580 | | NS_IMETHODIMP |
581 | | XULContentSinkImpl::HandleProcessingInstruction(const char16_t *aTarget, |
582 | | const char16_t *aData) |
583 | 0 | { |
584 | 0 | FlushText(); |
585 | 0 |
|
586 | 0 | const nsDependentString target(aTarget); |
587 | 0 | const nsDependentString data(aData); |
588 | 0 |
|
589 | 0 | // Note: the created nsXULPrototypePI has mRefCnt == 1 |
590 | 0 | RefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI(); |
591 | 0 | pi->mTarget = target; |
592 | 0 | pi->mData = data; |
593 | 0 |
|
594 | 0 | if (mState == eInProlog) { |
595 | 0 | // Note: passing in already addrefed pi |
596 | 0 | return mPrototype->AddProcessingInstruction(pi); |
597 | 0 | } |
598 | 0 | |
599 | 0 | nsresult rv; |
600 | 0 | nsPrototypeArray* children = nullptr; |
601 | 0 | rv = mContextStack.GetTopChildren(&children); |
602 | 0 | if (NS_FAILED(rv)) { |
603 | 0 | return rv; |
604 | 0 | } |
605 | 0 | |
606 | 0 | if (!children->AppendElement(pi)) { |
607 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
608 | 0 | } |
609 | 0 | |
610 | 0 | return NS_OK; |
611 | 0 | } |
612 | | |
613 | | |
614 | | NS_IMETHODIMP |
615 | | XULContentSinkImpl::HandleXMLDeclaration(const char16_t *aVersion, |
616 | | const char16_t *aEncoding, |
617 | | int32_t aStandalone) |
618 | 0 | { |
619 | 0 | return NS_OK; |
620 | 0 | } |
621 | | |
622 | | |
623 | | NS_IMETHODIMP |
624 | | XULContentSinkImpl::ReportError(const char16_t* aErrorText, |
625 | | const char16_t* aSourceText, |
626 | | nsIScriptError *aError, |
627 | | bool *_retval) |
628 | 0 | { |
629 | 0 | MOZ_ASSERT(aError && aSourceText && aErrorText, "Check arguments!!!"); |
630 | 0 |
|
631 | 0 | // The expat driver should report the error. |
632 | 0 | *_retval = true; |
633 | 0 |
|
634 | 0 | nsresult rv = NS_OK; |
635 | 0 |
|
636 | 0 | // make sure to empty the context stack so that |
637 | 0 | // <parsererror> could become the root element. |
638 | 0 | mContextStack.Clear(); |
639 | 0 |
|
640 | 0 | mState = eInProlog; |
641 | 0 |
|
642 | 0 | // Clear any buffered-up text we have. It's enough to set the length to 0. |
643 | 0 | // The buffer itself is allocated when we're created and deleted in our |
644 | 0 | // destructor, so don't mess with it. |
645 | 0 | mTextLength = 0; |
646 | 0 |
|
647 | 0 | // return leaving the document empty if we're asked to not add a <parsererror> root node |
648 | 0 | nsCOMPtr<nsIDocument> idoc = do_QueryReferent(mDocument); |
649 | 0 | if (idoc && idoc->SuppressParserErrorElement()) { |
650 | 0 | return NS_OK; |
651 | 0 | }; |
652 | 0 |
|
653 | 0 | const char16_t* noAtts[] = { 0, 0 }; |
654 | 0 |
|
655 | 0 | NS_NAMED_LITERAL_STRING(errorNs, |
656 | 0 | "http://www.mozilla.org/newlayout/xml/parsererror.xml"); |
657 | 0 |
|
658 | 0 | nsAutoString parsererror(errorNs); |
659 | 0 | parsererror.Append((char16_t)0xFFFF); |
660 | 0 | parsererror.AppendLiteral("parsererror"); |
661 | 0 |
|
662 | 0 | rv = HandleStartElement(parsererror.get(), noAtts, 0, 0, 0); |
663 | 0 | NS_ENSURE_SUCCESS(rv,rv); |
664 | 0 |
|
665 | 0 | rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText)); |
666 | 0 | NS_ENSURE_SUCCESS(rv,rv); |
667 | 0 |
|
668 | 0 | nsAutoString sourcetext(errorNs); |
669 | 0 | sourcetext.Append((char16_t)0xFFFF); |
670 | 0 | sourcetext.AppendLiteral("sourcetext"); |
671 | 0 |
|
672 | 0 | rv = HandleStartElement(sourcetext.get(), noAtts, 0, 0, 0); |
673 | 0 | NS_ENSURE_SUCCESS(rv,rv); |
674 | 0 |
|
675 | 0 | rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText)); |
676 | 0 | NS_ENSURE_SUCCESS(rv,rv); |
677 | 0 |
|
678 | 0 | rv = HandleEndElement(sourcetext.get()); |
679 | 0 | NS_ENSURE_SUCCESS(rv,rv); |
680 | 0 |
|
681 | 0 | rv = HandleEndElement(parsererror.get()); |
682 | 0 | NS_ENSURE_SUCCESS(rv,rv); |
683 | 0 |
|
684 | 0 | return rv; |
685 | 0 | } |
686 | | |
687 | | nsresult |
688 | | XULContentSinkImpl::OpenRoot(const char16_t** aAttributes, |
689 | | const uint32_t aAttrLen, |
690 | | mozilla::dom::NodeInfo *aNodeInfo) |
691 | 0 | { |
692 | 0 | NS_ASSERTION(mState == eInProlog, "how'd we get here?"); |
693 | 0 | if (mState != eInProlog) |
694 | 0 | return NS_ERROR_UNEXPECTED; |
695 | 0 | |
696 | 0 | nsresult rv; |
697 | 0 |
|
698 | 0 | if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) || |
699 | 0 | aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) { |
700 | 0 | MOZ_LOG(gContentSinkLog, LogLevel::Error, |
701 | 0 | ("xul: script tag not allowed as root content element")); |
702 | 0 |
|
703 | 0 | return NS_ERROR_UNEXPECTED; |
704 | 0 | } |
705 | 0 |
|
706 | 0 | // Create the element |
707 | 0 | nsXULPrototypeElement* element; |
708 | 0 | rv = CreateElement(aNodeInfo, &element); |
709 | 0 |
|
710 | 0 | if (NS_FAILED(rv)) { |
711 | 0 | if (MOZ_LOG_TEST(gContentSinkLog, LogLevel::Error)) { |
712 | 0 | nsAutoString anodeC; |
713 | 0 | aNodeInfo->GetName(anodeC); |
714 | 0 | MOZ_LOG(gContentSinkLog, LogLevel::Error, |
715 | 0 | ("xul: unable to create element '%s' at line %d", |
716 | 0 | NS_ConvertUTF16toUTF8(anodeC).get(), |
717 | 0 | -1)); // XXX pass in line number |
718 | 0 | } |
719 | 0 |
|
720 | 0 | return rv; |
721 | 0 | } |
722 | 0 |
|
723 | 0 | // Push the element onto the context stack, so that child |
724 | 0 | // containers will hook up to us as their parent. |
725 | 0 | rv = mContextStack.Push(element, mState); |
726 | 0 | if (NS_FAILED(rv)) { |
727 | 0 | element->Release(); |
728 | 0 | return rv; |
729 | 0 | } |
730 | 0 | |
731 | 0 | // Add the attributes |
732 | 0 | rv = AddAttributes(aAttributes, aAttrLen, element); |
733 | 0 | if (NS_FAILED(rv)) return rv; |
734 | 0 | |
735 | 0 | mState = eInDocumentElement; |
736 | 0 | return NS_OK; |
737 | 0 | } |
738 | | |
739 | | nsresult |
740 | | XULContentSinkImpl::OpenTag(const char16_t** aAttributes, |
741 | | const uint32_t aAttrLen, |
742 | | const uint32_t aLineNumber, |
743 | | mozilla::dom::NodeInfo *aNodeInfo) |
744 | 0 | { |
745 | 0 | nsresult rv; |
746 | 0 |
|
747 | 0 | // Create the element |
748 | 0 | nsXULPrototypeElement* element; |
749 | 0 | rv = CreateElement(aNodeInfo, &element); |
750 | 0 |
|
751 | 0 | if (NS_FAILED(rv)) { |
752 | 0 | if (MOZ_LOG_TEST(gContentSinkLog, LogLevel::Error)) { |
753 | 0 | nsAutoString anodeC; |
754 | 0 | aNodeInfo->GetName(anodeC); |
755 | 0 | MOZ_LOG(gContentSinkLog, LogLevel::Error, |
756 | 0 | ("xul: unable to create element '%s' at line %d", |
757 | 0 | NS_ConvertUTF16toUTF8(anodeC).get(), |
758 | 0 | aLineNumber)); |
759 | 0 | } |
760 | 0 |
|
761 | 0 | return rv; |
762 | 0 | } |
763 | 0 |
|
764 | 0 | // Link this element to its parent. |
765 | 0 | nsPrototypeArray* children = nullptr; |
766 | 0 | rv = mContextStack.GetTopChildren(&children); |
767 | 0 | if (NS_FAILED(rv)) { |
768 | 0 | delete element; |
769 | 0 | return rv; |
770 | 0 | } |
771 | 0 | |
772 | 0 | // Add the attributes |
773 | 0 | rv = AddAttributes(aAttributes, aAttrLen, element); |
774 | 0 | if (NS_FAILED(rv)) return rv; |
775 | 0 | |
776 | 0 | children->AppendElement(element); |
777 | 0 |
|
778 | 0 | if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) || |
779 | 0 | aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) { |
780 | 0 | // Do scripty things now |
781 | 0 | rv = OpenScript(aAttributes, aLineNumber); |
782 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
783 | 0 |
|
784 | 0 | NS_ASSERTION(mState == eInScript || mState == eInDocumentElement, |
785 | 0 | "Unexpected state"); |
786 | 0 | if (mState == eInScript) { |
787 | 0 | // OpenScript has pushed the nsPrototypeScriptElement onto the |
788 | 0 | // stack, so we're done. |
789 | 0 | return NS_OK; |
790 | 0 | } |
791 | 0 | } |
792 | 0 | |
793 | 0 | // Push the element onto the context stack, so that child |
794 | 0 | // containers will hook up to us as their parent. |
795 | 0 | rv = mContextStack.Push(element, mState); |
796 | 0 | if (NS_FAILED(rv)) return rv; |
797 | 0 | |
798 | 0 | mState = eInDocumentElement; |
799 | 0 | return NS_OK; |
800 | 0 | } |
801 | | |
802 | | nsresult |
803 | | XULContentSinkImpl::OpenScript(const char16_t** aAttributes, |
804 | | const uint32_t aLineNumber) |
805 | 0 | { |
806 | 0 | bool isJavaScript = true; |
807 | 0 | nsresult rv; |
808 | 0 |
|
809 | 0 | // Look for SRC attribute and look for a LANGUAGE attribute |
810 | 0 | nsAutoString src; |
811 | 0 | while (*aAttributes) { |
812 | 0 | const nsDependentString key(aAttributes[0]); |
813 | 0 | if (key.EqualsLiteral("src")) { |
814 | 0 | src.Assign(aAttributes[1]); |
815 | 0 | } else if (key.EqualsLiteral("type")) { |
816 | 0 | nsDependentString str(aAttributes[1]); |
817 | 0 | nsContentTypeParser parser(str); |
818 | 0 | nsAutoString mimeType; |
819 | 0 | rv = parser.GetType(mimeType); |
820 | 0 | if (NS_FAILED(rv)) { |
821 | 0 | if (rv == NS_ERROR_INVALID_ARG) { |
822 | 0 | // Fail immediately rather than checking if later things |
823 | 0 | // are okay. |
824 | 0 | return NS_OK; |
825 | 0 | } |
826 | 0 | // We do want the warning here |
827 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
828 | 0 | } |
829 | 0 |
|
830 | 0 | if (nsContentUtils::IsJavascriptMIMEType(mimeType)) { |
831 | 0 | isJavaScript = true; |
832 | 0 |
|
833 | 0 | // Get the version string, and ensure that JavaScript supports it. |
834 | 0 | nsAutoString versionName; |
835 | 0 | rv = parser.GetParameter("version", versionName); |
836 | 0 |
|
837 | 0 | if (NS_SUCCEEDED(rv)) { |
838 | 0 | nsContentUtils::ReportToConsoleNonLocalized( |
839 | 0 | NS_LITERAL_STRING("Versioned JavaScripts are no longer supported. " |
840 | 0 | "Please remove the version parameter."), |
841 | 0 | nsIScriptError::errorFlag, |
842 | 0 | NS_LITERAL_CSTRING("XUL Document"), |
843 | 0 | nullptr, mDocumentURL, EmptyString(), aLineNumber); |
844 | 0 | isJavaScript = false; |
845 | 0 | } else if (rv != NS_ERROR_INVALID_ARG) { |
846 | 0 | return rv; |
847 | 0 | } |
848 | 0 | } else { |
849 | 0 | isJavaScript = false; |
850 | 0 | } |
851 | 0 | } else if (key.EqualsLiteral("language")) { |
852 | 0 | // Language is deprecated, and the impl in ScriptLoader ignores the |
853 | 0 | // various version strings anyway. So we make no attempt to support |
854 | 0 | // languages other than JS for language= |
855 | 0 | nsAutoString lang(aAttributes[1]); |
856 | 0 | if (nsContentUtils::IsJavaScriptLanguage(lang)) { |
857 | 0 | isJavaScript = true; |
858 | 0 | } |
859 | 0 | } |
860 | 0 | aAttributes += 2; |
861 | 0 | } |
862 | 0 |
|
863 | 0 | // Don't process scripts that aren't JavaScript. |
864 | 0 | if (!isJavaScript) { |
865 | 0 | return NS_OK; |
866 | 0 | } |
867 | 0 | |
868 | 0 | nsCOMPtr<nsIDocument> doc(do_QueryReferent(mDocument)); |
869 | 0 | nsCOMPtr<nsIScriptGlobalObject> globalObject; |
870 | 0 | if (doc) |
871 | 0 | globalObject = do_QueryInterface(doc->GetWindow()); |
872 | 0 | RefPtr<nsXULPrototypeScript> script = |
873 | 0 | new nsXULPrototypeScript(aLineNumber); |
874 | 0 |
|
875 | 0 | // If there is a SRC attribute... |
876 | 0 | if (! src.IsEmpty()) { |
877 | 0 | // Use the SRC attribute value to load the URL |
878 | 0 | rv = NS_NewURI(getter_AddRefs(script->mSrcURI), src, nullptr, mDocumentURL); |
879 | 0 |
|
880 | 0 | // Check if this document is allowed to load a script from this source |
881 | 0 | // NOTE: if we ever allow scripts added via the DOM to run, we need to |
882 | 0 | // add a CheckLoadURI call for that as well. |
883 | 0 | if (NS_SUCCEEDED(rv)) { |
884 | 0 | if (!mSecMan) |
885 | 0 | mSecMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); |
886 | 0 | if (NS_SUCCEEDED(rv)) { |
887 | 0 | nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument, &rv); |
888 | 0 |
|
889 | 0 | if (NS_SUCCEEDED(rv)) { |
890 | 0 | rv = mSecMan-> |
891 | 0 | CheckLoadURIWithPrincipal(doc->NodePrincipal(), |
892 | 0 | script->mSrcURI, |
893 | 0 | nsIScriptSecurityManager::ALLOW_CHROME); |
894 | 0 | } |
895 | 0 | } |
896 | 0 | } |
897 | 0 |
|
898 | 0 | if (NS_FAILED(rv)) { |
899 | 0 | return rv; |
900 | 0 | } |
901 | 0 | |
902 | 0 | // Attempt to deserialize an out-of-line script from the FastLoad |
903 | 0 | // file right away. Otherwise we'll end up reloading the script and |
904 | 0 | // corrupting the FastLoad file trying to serialize it, in the case |
905 | 0 | // where it's already there. |
906 | 0 | script->DeserializeOutOfLine(nullptr, mPrototype); |
907 | 0 | } |
908 | 0 |
|
909 | 0 | nsPrototypeArray* children = nullptr; |
910 | 0 | rv = mContextStack.GetTopChildren(&children); |
911 | 0 | if (NS_FAILED(rv)) { |
912 | 0 | return rv; |
913 | 0 | } |
914 | 0 | |
915 | 0 | children->AppendElement(script); |
916 | 0 |
|
917 | 0 | mConstrainSize = false; |
918 | 0 |
|
919 | 0 | mContextStack.Push(script, mState); |
920 | 0 | mState = eInScript; |
921 | 0 |
|
922 | 0 | return NS_OK; |
923 | 0 | } |
924 | | |
925 | | nsresult |
926 | | XULContentSinkImpl::AddAttributes(const char16_t** aAttributes, |
927 | | const uint32_t aAttrLen, |
928 | | nsXULPrototypeElement* aElement) |
929 | 0 | { |
930 | 0 | // Add tag attributes to the element |
931 | 0 | nsresult rv; |
932 | 0 |
|
933 | 0 | // Create storage for the attributes |
934 | 0 | nsXULPrototypeAttribute* attrs = nullptr; |
935 | 0 | if (aAttrLen > 0) { |
936 | 0 | attrs = new nsXULPrototypeAttribute[aAttrLen]; |
937 | 0 | } |
938 | 0 |
|
939 | 0 | aElement->mAttributes = attrs; |
940 | 0 | aElement->mNumAttributes = aAttrLen; |
941 | 0 |
|
942 | 0 | // Copy the attributes into the prototype |
943 | 0 | uint32_t i; |
944 | 0 | for (i = 0; i < aAttrLen; ++i) { |
945 | 0 | rv = NormalizeAttributeString(aAttributes[i * 2], attrs[i].mName); |
946 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
947 | 0 |
|
948 | 0 | rv = aElement->SetAttrAt(i, nsDependentString(aAttributes[i * 2 + 1]), |
949 | 0 | mDocumentURL); |
950 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
951 | 0 |
|
952 | 0 | if (MOZ_LOG_TEST(gContentSinkLog, LogLevel::Debug)) { |
953 | 0 | nsAutoString extraWhiteSpace; |
954 | 0 | int32_t cnt = mContextStack.Depth(); |
955 | 0 | while (--cnt >= 0) |
956 | 0 | extraWhiteSpace.AppendLiteral(" "); |
957 | 0 | nsAutoString qnameC,valueC; |
958 | 0 | qnameC.Assign(aAttributes[0]); |
959 | 0 | valueC.Assign(aAttributes[1]); |
960 | 0 | MOZ_LOG(gContentSinkLog, LogLevel::Debug, |
961 | 0 | ("xul: %.5d. %s %s=%s", |
962 | 0 | -1, // XXX pass in line number |
963 | 0 | NS_ConvertUTF16toUTF8(extraWhiteSpace).get(), |
964 | 0 | NS_ConvertUTF16toUTF8(qnameC).get(), |
965 | 0 | NS_ConvertUTF16toUTF8(valueC).get())); |
966 | 0 | } |
967 | 0 | } |
968 | 0 |
|
969 | 0 | return NS_OK; |
970 | 0 | } |
971 | | |
972 | | nsresult |
973 | | XULContentSinkImpl::AddText(const char16_t* aText, |
974 | | int32_t aLength) |
975 | 0 | { |
976 | 0 | // Create buffer when we first need it |
977 | 0 | if (0 == mTextSize) { |
978 | 0 | mText = (char16_t *) malloc(sizeof(char16_t) * 4096); |
979 | 0 | if (nullptr == mText) { |
980 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
981 | 0 | } |
982 | 0 | mTextSize = 4096; |
983 | 0 | } |
984 | 0 |
|
985 | 0 | // Copy data from string into our buffer; flush buffer when it fills up |
986 | 0 | int32_t offset = 0; |
987 | 0 | while (0 != aLength) { |
988 | 0 | int32_t amount = mTextSize - mTextLength; |
989 | 0 | if (amount > aLength) { |
990 | 0 | amount = aLength; |
991 | 0 | } |
992 | 0 | if (0 == amount) { |
993 | 0 | if (mConstrainSize) { |
994 | 0 | nsresult rv = FlushText(); |
995 | 0 | if (NS_OK != rv) { |
996 | 0 | return rv; |
997 | 0 | } |
998 | 0 | } else { |
999 | 0 | CheckedInt32 size = mTextSize; |
1000 | 0 | size += aLength; |
1001 | 0 | if (!size.isValid()) { |
1002 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1003 | 0 | } |
1004 | 0 | mTextSize = size.value(); |
1005 | 0 |
|
1006 | 0 | mText = (char16_t *) realloc(mText, sizeof(char16_t) * mTextSize); |
1007 | 0 | if (nullptr == mText) { |
1008 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1009 | 0 | } |
1010 | 0 | } |
1011 | 0 | } |
1012 | 0 | memcpy(&mText[mTextLength],aText + offset, sizeof(char16_t) * amount); |
1013 | 0 |
|
1014 | 0 | mTextLength += amount; |
1015 | 0 | offset += amount; |
1016 | 0 | aLength -= amount; |
1017 | 0 | } |
1018 | 0 |
|
1019 | 0 | return NS_OK; |
1020 | 0 | } |