|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 #include "nsCOMPtr.h" |
|
6 #include "nsXMLContentSink.h" |
|
7 #include "nsIFragmentContentSink.h" |
|
8 #include "nsIXMLContentSink.h" |
|
9 #include "nsContentSink.h" |
|
10 #include "nsIExpatSink.h" |
|
11 #include "nsIDTD.h" |
|
12 #include "nsIDocument.h" |
|
13 #include "nsIDOMDocumentFragment.h" |
|
14 #include "nsIContent.h" |
|
15 #include "nsGkAtoms.h" |
|
16 #include "nsINodeInfo.h" |
|
17 #include "nsContentCreatorFunctions.h" |
|
18 #include "nsError.h" |
|
19 #include "nsIConsoleService.h" |
|
20 #include "nsIScriptError.h" |
|
21 #include "nsNetUtil.h" |
|
22 #include "nsTHashtable.h" |
|
23 #include "nsHashKeys.h" |
|
24 #include "nsTArray.h" |
|
25 #include "nsCycleCollectionParticipant.h" |
|
26 #include "nsIDocShell.h" |
|
27 #include "nsScriptLoader.h" |
|
28 #include "mozilla/css/Loader.h" |
|
29 #include "mozilla/dom/DocumentFragment.h" |
|
30 #include "mozilla/dom/ProcessingInstruction.h" |
|
31 |
|
32 using namespace mozilla::dom; |
|
33 |
|
34 class nsXMLFragmentContentSink : public nsXMLContentSink, |
|
35 public nsIFragmentContentSink |
|
36 { |
|
37 public: |
|
38 nsXMLFragmentContentSink(); |
|
39 virtual ~nsXMLFragmentContentSink(); |
|
40 |
|
41 NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW |
|
42 |
|
43 // nsISupports |
|
44 NS_DECL_ISUPPORTS_INHERITED |
|
45 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsXMLFragmentContentSink, |
|
46 nsXMLContentSink) |
|
47 |
|
48 // nsIExpatSink |
|
49 NS_IMETHOD HandleDoctypeDecl(const nsAString & aSubset, |
|
50 const nsAString & aName, |
|
51 const nsAString & aSystemId, |
|
52 const nsAString & aPublicId, |
|
53 nsISupports* aCatalogData); |
|
54 NS_IMETHOD HandleProcessingInstruction(const char16_t *aTarget, |
|
55 const char16_t *aData); |
|
56 NS_IMETHOD HandleXMLDeclaration(const char16_t *aVersion, |
|
57 const char16_t *aEncoding, |
|
58 int32_t aStandalone); |
|
59 NS_IMETHOD ReportError(const char16_t* aErrorText, |
|
60 const char16_t* aSourceText, |
|
61 nsIScriptError *aError, |
|
62 bool *_retval); |
|
63 |
|
64 // nsIContentSink |
|
65 NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode); |
|
66 NS_IMETHOD DidBuildModel(bool aTerminated); |
|
67 NS_IMETHOD SetDocumentCharset(nsACString& aCharset); |
|
68 virtual nsISupports *GetTarget(); |
|
69 NS_IMETHOD DidProcessATokenImpl(); |
|
70 |
|
71 // nsIXMLContentSink |
|
72 |
|
73 // nsIFragmentContentSink |
|
74 NS_IMETHOD FinishFragmentParsing(nsIDOMDocumentFragment** aFragment); |
|
75 NS_IMETHOD SetTargetDocument(nsIDocument* aDocument); |
|
76 NS_IMETHOD WillBuildContent(); |
|
77 NS_IMETHOD DidBuildContent(); |
|
78 NS_IMETHOD IgnoreFirstContainer(); |
|
79 NS_IMETHOD SetPreventScriptExecution(bool aPreventScriptExecution); |
|
80 |
|
81 protected: |
|
82 virtual bool SetDocElement(int32_t aNameSpaceID, |
|
83 nsIAtom *aTagName, |
|
84 nsIContent *aContent); |
|
85 virtual nsresult CreateElement(const char16_t** aAtts, uint32_t aAttsCount, |
|
86 nsINodeInfo* aNodeInfo, uint32_t aLineNumber, |
|
87 nsIContent** aResult, bool* aAppendContent, |
|
88 mozilla::dom::FromParser aFromParser); |
|
89 virtual nsresult CloseElement(nsIContent* aContent); |
|
90 |
|
91 virtual void MaybeStartLayout(bool aIgnorePendingSheets); |
|
92 |
|
93 // nsContentSink overrides |
|
94 virtual nsresult ProcessStyleLink(nsIContent* aElement, |
|
95 const nsSubstring& aHref, |
|
96 bool aAlternate, |
|
97 const nsSubstring& aTitle, |
|
98 const nsSubstring& aType, |
|
99 const nsSubstring& aMedia); |
|
100 nsresult LoadXSLStyleSheet(nsIURI* aUrl); |
|
101 void StartLayout(); |
|
102 |
|
103 nsCOMPtr<nsIDocument> mTargetDocument; |
|
104 // the fragment |
|
105 nsCOMPtr<nsIContent> mRoot; |
|
106 bool mParseError; |
|
107 }; |
|
108 |
|
109 static nsresult |
|
110 NewXMLFragmentContentSinkHelper(nsIFragmentContentSink** aResult) |
|
111 { |
|
112 nsXMLFragmentContentSink* it = new nsXMLFragmentContentSink(); |
|
113 |
|
114 NS_ADDREF(*aResult = it); |
|
115 |
|
116 return NS_OK; |
|
117 } |
|
118 |
|
119 nsresult |
|
120 NS_NewXMLFragmentContentSink(nsIFragmentContentSink** aResult) |
|
121 { |
|
122 return NewXMLFragmentContentSinkHelper(aResult); |
|
123 } |
|
124 |
|
125 nsXMLFragmentContentSink::nsXMLFragmentContentSink() |
|
126 : mParseError(false) |
|
127 { |
|
128 mRunsToCompletion = true; |
|
129 } |
|
130 |
|
131 nsXMLFragmentContentSink::~nsXMLFragmentContentSink() |
|
132 { |
|
133 } |
|
134 |
|
135 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLFragmentContentSink) |
|
136 NS_INTERFACE_MAP_ENTRY(nsIFragmentContentSink) |
|
137 NS_INTERFACE_MAP_END_INHERITING(nsXMLContentSink) |
|
138 |
|
139 NS_IMPL_ADDREF_INHERITED(nsXMLFragmentContentSink, nsXMLContentSink) |
|
140 NS_IMPL_RELEASE_INHERITED(nsXMLFragmentContentSink, nsXMLContentSink) |
|
141 |
|
142 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLFragmentContentSink) |
|
143 |
|
144 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLFragmentContentSink, |
|
145 nsXMLContentSink) |
|
146 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTargetDocument) |
|
147 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) |
|
148 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
149 |
|
150 NS_IMETHODIMP |
|
151 nsXMLFragmentContentSink::WillBuildModel(nsDTDMode aDTDMode) |
|
152 { |
|
153 if (mRoot) { |
|
154 return NS_OK; |
|
155 } |
|
156 |
|
157 mState = eXMLContentSinkState_InDocumentElement; |
|
158 |
|
159 NS_ASSERTION(mTargetDocument, "Need a document!"); |
|
160 |
|
161 mRoot = new DocumentFragment(mNodeInfoManager); |
|
162 |
|
163 return NS_OK; |
|
164 } |
|
165 |
|
166 NS_IMETHODIMP |
|
167 nsXMLFragmentContentSink::DidBuildModel(bool aTerminated) |
|
168 { |
|
169 nsRefPtr<nsParserBase> kungFuDeathGrip(mParser); |
|
170 |
|
171 // Drop our reference to the parser to get rid of a circular |
|
172 // reference. |
|
173 mParser = nullptr; |
|
174 |
|
175 return NS_OK; |
|
176 } |
|
177 |
|
178 NS_IMETHODIMP |
|
179 nsXMLFragmentContentSink::SetDocumentCharset(nsACString& aCharset) |
|
180 { |
|
181 NS_NOTREACHED("fragments shouldn't set charset"); |
|
182 return NS_OK; |
|
183 } |
|
184 |
|
185 nsISupports * |
|
186 nsXMLFragmentContentSink::GetTarget() |
|
187 { |
|
188 return mTargetDocument; |
|
189 } |
|
190 |
|
191 //////////////////////////////////////////////////////////////////////// |
|
192 |
|
193 bool |
|
194 nsXMLFragmentContentSink::SetDocElement(int32_t aNameSpaceID, |
|
195 nsIAtom* aTagName, |
|
196 nsIContent *aContent) |
|
197 { |
|
198 // this is a fragment, not a document |
|
199 return false; |
|
200 } |
|
201 |
|
202 nsresult |
|
203 nsXMLFragmentContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount, |
|
204 nsINodeInfo* aNodeInfo, uint32_t aLineNumber, |
|
205 nsIContent** aResult, bool* aAppendContent, |
|
206 FromParser /*aFromParser*/) |
|
207 { |
|
208 // Claim to not be coming from parser, since we don't do any of the |
|
209 // fancy CloseElement stuff. |
|
210 nsresult rv = nsXMLContentSink::CreateElement(aAtts, aAttsCount, |
|
211 aNodeInfo, aLineNumber, |
|
212 aResult, aAppendContent, |
|
213 NOT_FROM_PARSER); |
|
214 |
|
215 // When we aren't grabbing all of the content we, never open a doc |
|
216 // element, we run into trouble on the first element, so we don't append, |
|
217 // and simply push this onto the content stack. |
|
218 if (mContentStack.Length() == 0) { |
|
219 *aAppendContent = false; |
|
220 } |
|
221 |
|
222 return rv; |
|
223 } |
|
224 |
|
225 nsresult |
|
226 nsXMLFragmentContentSink::CloseElement(nsIContent* aContent) |
|
227 { |
|
228 // don't do fancy stuff in nsXMLContentSink |
|
229 if (mPreventScriptExecution && aContent->Tag() == nsGkAtoms::script && |
|
230 (aContent->IsHTML() || aContent->IsSVG())) { |
|
231 nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aContent); |
|
232 NS_ASSERTION(sele, "script did QI correctly!"); |
|
233 sele->PreventExecution(); |
|
234 } |
|
235 return NS_OK; |
|
236 } |
|
237 |
|
238 void |
|
239 nsXMLFragmentContentSink::MaybeStartLayout(bool aIgnorePendingSheets) |
|
240 { |
|
241 return; |
|
242 } |
|
243 |
|
244 //////////////////////////////////////////////////////////////////////// |
|
245 |
|
246 NS_IMETHODIMP |
|
247 nsXMLFragmentContentSink::HandleDoctypeDecl(const nsAString & aSubset, |
|
248 const nsAString & aName, |
|
249 const nsAString & aSystemId, |
|
250 const nsAString & aPublicId, |
|
251 nsISupports* aCatalogData) |
|
252 { |
|
253 NS_NOTREACHED("fragments shouldn't have doctype declarations"); |
|
254 |
|
255 return NS_OK; |
|
256 } |
|
257 |
|
258 NS_IMETHODIMP |
|
259 nsXMLFragmentContentSink::HandleProcessingInstruction(const char16_t *aTarget, |
|
260 const char16_t *aData) |
|
261 { |
|
262 FlushText(); |
|
263 |
|
264 const nsDependentString target(aTarget); |
|
265 const nsDependentString data(aData); |
|
266 |
|
267 nsRefPtr<ProcessingInstruction> node = |
|
268 NS_NewXMLProcessingInstruction(mNodeInfoManager, target, data); |
|
269 |
|
270 // no special processing here. that should happen when the fragment moves into the document |
|
271 return AddContentAsLeaf(node); |
|
272 } |
|
273 |
|
274 NS_IMETHODIMP |
|
275 nsXMLFragmentContentSink::HandleXMLDeclaration(const char16_t *aVersion, |
|
276 const char16_t *aEncoding, |
|
277 int32_t aStandalone) |
|
278 { |
|
279 NS_NOTREACHED("fragments shouldn't have XML declarations"); |
|
280 return NS_OK; |
|
281 } |
|
282 |
|
283 NS_IMETHODIMP |
|
284 nsXMLFragmentContentSink::ReportError(const char16_t* aErrorText, |
|
285 const char16_t* aSourceText, |
|
286 nsIScriptError *aError, |
|
287 bool *_retval) |
|
288 { |
|
289 NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!"); |
|
290 |
|
291 // The expat driver should report the error. |
|
292 *_retval = true; |
|
293 |
|
294 mParseError = true; |
|
295 |
|
296 #ifdef DEBUG |
|
297 // Report the error to stderr. |
|
298 fprintf(stderr, |
|
299 "\n%s\n%s\n\n", |
|
300 NS_LossyConvertUTF16toASCII(aErrorText).get(), |
|
301 NS_LossyConvertUTF16toASCII(aSourceText).get()); |
|
302 #endif |
|
303 |
|
304 // The following code is similar to the cleanup in nsXMLContentSink::ReportError() |
|
305 mState = eXMLContentSinkState_InProlog; |
|
306 |
|
307 // Clear the current content |
|
308 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mRoot)); |
|
309 if (node) { |
|
310 for (;;) { |
|
311 nsCOMPtr<nsIDOMNode> child, dummy; |
|
312 node->GetLastChild(getter_AddRefs(child)); |
|
313 if (!child) |
|
314 break; |
|
315 node->RemoveChild(child, getter_AddRefs(dummy)); |
|
316 } |
|
317 } |
|
318 |
|
319 // Clear any buffered-up text we have. It's enough to set the length to 0. |
|
320 // The buffer itself is allocated when we're created and deleted in our |
|
321 // destructor, so don't mess with it. |
|
322 mTextLength = 0; |
|
323 |
|
324 return NS_OK; |
|
325 } |
|
326 |
|
327 nsresult |
|
328 nsXMLFragmentContentSink::ProcessStyleLink(nsIContent* aElement, |
|
329 const nsSubstring& aHref, |
|
330 bool aAlternate, |
|
331 const nsSubstring& aTitle, |
|
332 const nsSubstring& aType, |
|
333 const nsSubstring& aMedia) |
|
334 { |
|
335 // don't process until moved to document |
|
336 return NS_OK; |
|
337 } |
|
338 |
|
339 nsresult |
|
340 nsXMLFragmentContentSink::LoadXSLStyleSheet(nsIURI* aUrl) |
|
341 { |
|
342 NS_NOTREACHED("fragments shouldn't have XSL style sheets"); |
|
343 return NS_ERROR_UNEXPECTED; |
|
344 } |
|
345 |
|
346 void |
|
347 nsXMLFragmentContentSink::StartLayout() |
|
348 { |
|
349 NS_NOTREACHED("fragments shouldn't layout"); |
|
350 } |
|
351 |
|
352 //////////////////////////////////////////////////////////////////////// |
|
353 |
|
354 NS_IMETHODIMP |
|
355 nsXMLFragmentContentSink::FinishFragmentParsing(nsIDOMDocumentFragment** aFragment) |
|
356 { |
|
357 *aFragment = nullptr; |
|
358 mTargetDocument = nullptr; |
|
359 mNodeInfoManager = nullptr; |
|
360 mScriptLoader = nullptr; |
|
361 mCSSLoader = nullptr; |
|
362 mContentStack.Clear(); |
|
363 mDocumentURI = nullptr; |
|
364 mDocShell = nullptr; |
|
365 mDocElement = nullptr; |
|
366 mCurrentHead = nullptr; |
|
367 if (mParseError) { |
|
368 //XXX PARSE_ERR from DOM3 Load and Save would be more appropriate |
|
369 mRoot = nullptr; |
|
370 mParseError = false; |
|
371 return NS_ERROR_DOM_SYNTAX_ERR; |
|
372 } else if (mRoot) { |
|
373 nsresult rv = CallQueryInterface(mRoot, aFragment); |
|
374 mRoot = nullptr; |
|
375 return rv; |
|
376 } else { |
|
377 return NS_OK; |
|
378 } |
|
379 } |
|
380 |
|
381 NS_IMETHODIMP |
|
382 nsXMLFragmentContentSink::SetTargetDocument(nsIDocument* aTargetDocument) |
|
383 { |
|
384 NS_ENSURE_ARG_POINTER(aTargetDocument); |
|
385 |
|
386 mTargetDocument = aTargetDocument; |
|
387 mNodeInfoManager = aTargetDocument->NodeInfoManager(); |
|
388 |
|
389 return NS_OK; |
|
390 } |
|
391 |
|
392 NS_IMETHODIMP |
|
393 nsXMLFragmentContentSink::WillBuildContent() |
|
394 { |
|
395 PushContent(mRoot); |
|
396 |
|
397 return NS_OK; |
|
398 } |
|
399 |
|
400 NS_IMETHODIMP |
|
401 nsXMLFragmentContentSink::DidBuildContent() |
|
402 { |
|
403 // Note: we need to FlushText() here because if we don't, we might not get |
|
404 // an end element to do it for us, so make sure. |
|
405 if (!mParseError) { |
|
406 FlushText(); |
|
407 } |
|
408 PopContent(); |
|
409 |
|
410 return NS_OK; |
|
411 } |
|
412 |
|
413 NS_IMETHODIMP |
|
414 nsXMLFragmentContentSink::DidProcessATokenImpl() |
|
415 { |
|
416 return NS_OK; |
|
417 } |
|
418 |
|
419 NS_IMETHODIMP |
|
420 nsXMLFragmentContentSink::IgnoreFirstContainer() |
|
421 { |
|
422 NS_NOTREACHED("XML isn't as broken as HTML"); |
|
423 return NS_ERROR_FAILURE; |
|
424 } |
|
425 |
|
426 NS_IMETHODIMP |
|
427 nsXMLFragmentContentSink::SetPreventScriptExecution(bool aPrevent) |
|
428 { |
|
429 mPreventScriptExecution = aPrevent; |
|
430 return NS_OK; |
|
431 } |