OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | |
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) | |
4 * (C) 2001 Dirk Mueller (mueller@kde.org) | |
5 * (C) 2006 Alexey Proskuryakov (ap@webkit.org) | |
6 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. | |
7 * | |
8 * This library is free software; you can redistribute it and/or | |
9 * modify it under the terms of the GNU Library General Public | |
10 * License as published by the Free Software Foundation; either | |
11 * version 2 of the License, or (at your option) any later version. | |
12 * | |
13 * This library is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 * Library General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU Library General Public License | |
19 * along with this library; see the file COPYING.LIB. If not, write to | |
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
21 * Boston, MA 02110-1301, USA. | |
22 */ | |
23 | |
24 #include "config.h" | |
25 #include "Document.h" | |
26 | |
27 #include "AnimationController.h" | |
28 #include "AXObjectCache.h" | |
29 #include "CDATASection.h" | |
30 #include "CSSHelper.h" | |
31 #include "CSSStyleSelector.h" | |
32 #include "CSSStyleSheet.h" | |
33 #include "CSSValueKeywords.h" | |
34 #include "CString.h" | |
35 #include "CachedCSSStyleSheet.h" | |
36 #include "Comment.h" | |
37 #include "CookieJar.h" | |
38 #include "DOMImplementation.h" | |
39 #include "DOMWindow.h" | |
40 #include "DocLoader.h" | |
41 #include "DocumentFragment.h" | |
42 #include "DocumentLoader.h" | |
43 #include "DocumentType.h" | |
44 #include "EditingText.h" | |
45 #include "Editor.h" | |
46 #include "EntityReference.h" | |
47 #include "Event.h" | |
48 #include "EventHandler.h" | |
49 #include "EventListener.h" | |
50 #include "EventNames.h" | |
51 #include "ExceptionCode.h" | |
52 #include "FocusController.h" | |
53 #include "FontCache.h" | |
54 #include "Frame.h" | |
55 #include "FrameLoader.h" | |
56 #include "FrameTree.h" | |
57 #include "FrameView.h" | |
58 #include "HTMLBodyElement.h" | |
59 #include "HTMLCanvasElement.h" | |
60 #include "HTMLDocument.h" | |
61 #include "HTMLElementFactory.h" | |
62 #include "HTMLFrameOwnerElement.h" | |
63 #include "HTMLHeadElement.h" | |
64 #include "HTMLImageLoader.h" | |
65 #include "HTMLInputElement.h" | |
66 #include "HTMLLinkElement.h" | |
67 #include "HTMLMapElement.h" | |
68 #include "HTMLNameCollection.h" | |
69 #include "HTMLNames.h" | |
70 #include "HTMLStyleElement.h" | |
71 #include "HTMLTitleElement.h" | |
72 #include "HTTPParsers.h" | |
73 #include "HistoryItem.h" | |
74 #include "HitTestRequest.h" | |
75 #include "HitTestResult.h" | |
76 #include "KeyboardEvent.h" | |
77 #include "Logging.h" | |
78 #include "MessageEvent.h" | |
79 #include "MouseEvent.h" | |
80 #include "MouseEventWithHitTestResults.h" | |
81 #include "MutationEvent.h" | |
82 #include "NameNodeList.h" | |
83 #include "NodeFilter.h" | |
84 #include "NodeIterator.h" | |
85 #include "NodeWithIndex.h" | |
86 #include "OverflowEvent.h" | |
87 #include "Page.h" | |
88 #include "PlatformKeyboardEvent.h" | |
89 #include "ProcessingInstruction.h" | |
90 #include "ProgressEvent.h" | |
91 #include "RegisteredEventListener.h" | |
92 #include "RegularExpression.h" | |
93 #include "RenderArena.h" | |
94 #include "RenderView.h" | |
95 #include "RenderWidget.h" | |
96 #include "SecurityOrigin.h" | |
97 #include "SegmentedString.h" | |
98 #include "SelectionController.h" | |
99 #include "Settings.h" | |
100 #include "StringHash.h" | |
101 #include "StyleSheetList.h" | |
102 #include "SystemTime.h" | |
103 #include "TextEvent.h" | |
104 #include "TextIterator.h" | |
105 #include "TextResourceDecoder.h" | |
106 #include "TreeWalker.h" | |
107 #include "UIEvent.h" | |
108 #include "WheelEvent.h" | |
109 #include "XMLHttpRequest.h" | |
110 #include "XMLNames.h" | |
111 #include "XMLTokenizer.h" | |
112 #include "ScriptController.h" | |
113 | |
114 #if USE(JSC) | |
115 #include <kjs/JSLock.h> | |
116 #include "JSDOMBinding.h" | |
117 #endif | |
118 | |
119 #if ENABLE(DATABASE) | |
120 #include "Database.h" | |
121 #include "DatabaseThread.h" | |
122 #endif | |
123 | |
124 #if ENABLE(XPATH) | |
125 #include "XPathEvaluator.h" | |
126 #include "XPathExpression.h" | |
127 #include "XPathNSResolver.h" | |
128 #include "XPathResult.h" | |
129 #endif | |
130 | |
131 #if ENABLE(XSLT) | |
132 #include "XSLTProcessor.h" | |
133 #endif | |
134 | |
135 #if ENABLE(XBL) | |
136 #include "XBLBindingManager.h" | |
137 #endif | |
138 | |
139 #if ENABLE(SVG) | |
140 #include "SVGDocumentExtensions.h" | |
141 #include "SVGElementFactory.h" | |
142 #include "SVGZoomEvent.h" | |
143 #include "SVGStyleElement.h" | |
144 #endif | |
145 | |
146 #if defined(__APPLE__) | |
147 // we need to be PLATFORM(CHROMIUM) for this file, even if we're not building | |
148 // that particular target, for the a11y ifdefs. | |
149 #define WTF_PLATFORM_CHROMIUM 1 | |
150 #endif | |
151 | |
152 using namespace std; | |
153 using namespace WTF; | |
154 using namespace Unicode; | |
155 | |
156 namespace WebCore { | |
157 | |
158 using namespace EventNames; | |
159 using namespace HTMLNames; | |
160 | |
161 // #define INSTRUMENT_LAYOUT_SCHEDULING 1 | |
162 | |
163 // This amount of time must have elapsed before we will even consider scheduling
a layout without a delay. | |
164 // FIXME: For faster machines this value can really be lowered to 200. 250 is a
dequate, but a little high | |
165 // for dual G5s. :) | |
166 static const int cLayoutScheduleThreshold = 250; | |
167 | |
168 // Use 1 to represent the document's default form. | |
169 static HTMLFormElement* const defaultForm = reinterpret_cast<HTMLFormElement*>(1
); | |
170 | |
171 // Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's | |
172 static const unsigned PHI = 0x9e3779b9U; | |
173 | |
174 // DOM Level 2 says (letters added): | |
175 // | |
176 // a) Name start characters must have one of the categories Ll, Lu, Lo, Lt, Nl. | |
177 // b) Name characters other than Name-start characters must have one of the cate
gories Mc, Me, Mn, Lm, or Nd. | |
178 // c) Characters in the compatibility area (i.e. with character code greater tha
n #xF900 and less than #xFFFE) are not allowed in XML names. | |
179 // d) Characters which have a font or compatibility decomposition (i.e. those wi
th a "compatibility formatting tag" in field 5 of the database -- marked by fiel
d 5 beginning with a "<") are not allowed. | |
180 // e) The following characters are treated as name-start characters rather than
name characters, because the property file classifies them as Alphabetic: [#x02B
B-#x02C1], #x0559, #x06E5, #x06E6. | |
181 // f) Characters #x20DD-#x20E0 are excluded (in accordance with Unicode, section
5.14). | |
182 // g) Character #x00B7 is classified as an extender, because the property list s
o identifies it. | |
183 // h) Character #x0387 is added as a name character, because #x00B7 is its canon
ical equivalent. | |
184 // i) Characters ':' and '_' are allowed as name-start characters. | |
185 // j) Characters '-' and '.' are allowed as name characters. | |
186 // | |
187 // It also contains complete tables. If we decide it's better, we could include
those instead of the following code. | |
188 | |
189 static inline bool isValidNameStart(UChar32 c) | |
190 { | |
191 // rule (e) above | |
192 if ((c >= 0x02BB && c <= 0x02C1) || c == 0x559 || c == 0x6E5 || c == 0x6E6) | |
193 return true; | |
194 | |
195 // rule (i) above | |
196 if (c == ':' || c == '_') | |
197 return true; | |
198 | |
199 // rules (a) and (f) above | |
200 const uint32_t nameStartMask = Letter_Lowercase | Letter_Uppercase | Letter_
Other | Letter_Titlecase | Number_Letter; | |
201 if (!(Unicode::category(c) & nameStartMask)) | |
202 return false; | |
203 | |
204 // rule (c) above | |
205 if (c >= 0xF900 && c < 0xFFFE) | |
206 return false; | |
207 | |
208 // rule (d) above | |
209 DecompositionType decompType = decompositionType(c); | |
210 if (decompType == DecompositionFont || decompType == DecompositionCompat) | |
211 return false; | |
212 | |
213 return true; | |
214 } | |
215 | |
216 static inline bool isValidNamePart(UChar32 c) | |
217 { | |
218 // rules (a), (e), and (i) above | |
219 if (isValidNameStart(c)) | |
220 return true; | |
221 | |
222 // rules (g) and (h) above | |
223 if (c == 0x00B7 || c == 0x0387) | |
224 return true; | |
225 | |
226 // rule (j) above | |
227 if (c == '-' || c == '.') | |
228 return true; | |
229 | |
230 // rules (b) and (f) above | |
231 const uint32_t otherNamePartMask = Mark_NonSpacing | Mark_Enclosing | Mark_S
pacingCombining | Letter_Modifier | Number_DecimalDigit; | |
232 if (!(Unicode::category(c) & otherNamePartMask)) | |
233 return false; | |
234 | |
235 // rule (c) above | |
236 if (c >= 0xF900 && c < 0xFFFE) | |
237 return false; | |
238 | |
239 // rule (d) above | |
240 DecompositionType decompType = decompositionType(c); | |
241 if (decompType == DecompositionFont || decompType == DecompositionCompat) | |
242 return false; | |
243 | |
244 return true; | |
245 } | |
246 | |
247 static Widget* widgetForNode(Node* focusedNode) | |
248 { | |
249 if (!focusedNode) | |
250 return 0; | |
251 RenderObject* renderer = focusedNode->renderer(); | |
252 if (!renderer || !renderer->isWidget()) | |
253 return 0; | |
254 return static_cast<RenderWidget*>(renderer)->widget(); | |
255 } | |
256 | |
257 static bool acceptsEditingFocus(Node *node) | |
258 { | |
259 ASSERT(node); | |
260 ASSERT(node->isContentEditable()); | |
261 | |
262 Node *root = node->rootEditableElement(); | |
263 Frame* frame = node->document()->frame(); | |
264 if (!frame || !root) | |
265 return false; | |
266 | |
267 return frame->editor()->shouldBeginEditing(rangeOfContents(root).get()); | |
268 } | |
269 | |
270 static HashSet<Document*>* changedDocuments = 0; | |
271 | |
272 Document::Document(Frame* frame, bool isXHTML) | |
273 : ContainerNode(0) | |
274 , m_domtree_version(0) | |
275 , m_styleSheets(StyleSheetList::create(this)) | |
276 , m_title("") | |
277 , m_titleSetExplicitly(false) | |
278 , m_imageLoadEventTimer(this, &Document::imageLoadEventTimerFired) | |
279 , m_updateFocusAppearanceTimer(this, &Document::updateFocusAppearanceTimerFi
red) | |
280 #if ENABLE(XSLT) | |
281 , m_transformSource(0) | |
282 #endif | |
283 , m_xmlVersion("1.0") | |
284 , m_xmlStandalone(false) | |
285 , m_dominantScript(USCRIPT_INVALID_CODE) | |
286 #if ENABLE(XBL) | |
287 , m_bindingManager(new XBLBindingManager(this)) | |
288 #endif | |
289 , m_savedRenderer(0) | |
290 , m_secureForms(0) | |
291 , m_designMode(inherit) | |
292 , m_selfOnlyRefCount(0) | |
293 #if ENABLE(SVG) | |
294 , m_svgExtensions(0) | |
295 #endif | |
296 #if ENABLE(DASHBOARD_SUPPORT) | |
297 , m_hasDashboardRegions(false) | |
298 , m_dashboardRegionsDirty(false) | |
299 #endif | |
300 , m_accessKeyMapValid(false) | |
301 , m_createRenderers(true) | |
302 , m_inPageCache(false) | |
303 , m_useSecureKeyboardEntryWhenActive(false) | |
304 , m_isXHTML(isXHTML) | |
305 , m_numNodeListCaches(0) | |
306 #if ENABLE(DATABASE) | |
307 , m_hasOpenDatabases(false) | |
308 #endif | |
309 #if USE(LOW_BANDWIDTH_DISPLAY) | |
310 , m_inLowBandwidthDisplay(false) | |
311 #endif | |
312 { | |
313 m_document.resetSkippingRef(this); | |
314 | |
315 m_printing = false; | |
316 | |
317 m_ignoreAutofocus = false; | |
318 | |
319 m_frame = frame; | |
320 m_renderArena = 0; | |
321 | |
322 m_axObjectCache = 0; | |
323 | |
324 m_docLoader = new DocLoader(this); | |
325 | |
326 visuallyOrdered = false; | |
327 m_bParsing = false; | |
328 m_docChanged = false; | |
329 m_tokenizer = 0; | |
330 m_wellFormed = false; | |
331 | |
332 setParseMode(Strict); | |
333 | |
334 m_textColor = Color::black; | |
335 m_listenerTypes = 0; | |
336 m_inDocument = true; | |
337 m_inStyleRecalc = false; | |
338 m_closeAfterStyleRecalc = false; | |
339 m_usesDescendantRules = false; | |
340 m_usesSiblingRules = false; | |
341 m_usesFirstLineRules = false; | |
342 m_usesFirstLetterRules = false; | |
343 m_gotoAnchorNeededAfterStylesheetsLoad = false; | |
344 | |
345 m_styleSelector = 0; | |
346 m_didCalculateStyleSelector = false; | |
347 m_pendingStylesheets = 0; | |
348 m_ignorePendingStylesheets = false; | |
349 m_hasNodesWithPlaceholderStyle = false; | |
350 m_pendingSheetLayout = NoLayoutWithPendingSheets; | |
351 | |
352 m_cssTarget = 0; | |
353 | |
354 resetLinkColor(); | |
355 resetVisitedLinkColor(); | |
356 resetActiveLinkColor(); | |
357 | |
358 m_processingLoadEvent = false; | |
359 m_startTime = currentTime(); | |
360 m_overMinimumLayoutThreshold = false; | |
361 | |
362 initSecurityContext(); | |
363 initDNSPrefetchEnabled(); | |
364 | |
365 static int docID = 0; | |
366 m_docID = docID++; | |
367 } | |
368 | |
369 void Document::removedLastRef() | |
370 { | |
371 ASSERT(!m_deletionHasBegun); | |
372 if (m_selfOnlyRefCount) { | |
373 // If removing a child removes the last self-only ref, we don't | |
374 // want the document to be destructed until after | |
375 // removeAllChildren returns, so we guard ourselves with an | |
376 // extra self-only ref. | |
377 | |
378 DocPtr<Document> guard(this); | |
379 | |
380 // We must make sure not to be retaining any of our children through | |
381 // these extra pointers or we will create a reference cycle. | |
382 m_docType = 0; | |
383 m_focusedNode = 0; | |
384 m_hoverNode = 0; | |
385 m_activeNode = 0; | |
386 m_titleElement = 0; | |
387 m_documentElement = 0; | |
388 | |
389 removeAllChildren(); | |
390 | |
391 deleteAllValues(m_markers); | |
392 m_markers.clear(); | |
393 | |
394 delete m_tokenizer; | |
395 m_tokenizer = 0; | |
396 | |
397 #ifndef NDEBUG | |
398 m_inRemovedLastRefFunction = false; | |
399 #endif | |
400 } else { | |
401 #ifndef NDEBUG | |
402 m_deletionHasBegun = true; | |
403 #endif | |
404 delete this; | |
405 } | |
406 } | |
407 | |
408 Document::~Document() | |
409 { | |
410 ASSERT(!renderer()); | |
411 ASSERT(!m_inPageCache); | |
412 ASSERT(!m_savedRenderer); | |
413 ASSERT(m_ranges.isEmpty()); | |
414 | |
415 removeAllEventListeners(); | |
416 | |
417 #if ENABLE(SVG) | |
418 delete m_svgExtensions; | |
419 #endif | |
420 | |
421 XMLHttpRequest::detachRequests(this); | |
422 #if USE(JSC) | |
423 { | |
424 KJS::JSLock lock(false); | |
425 ScriptInterpreter::forgetAllDOMNodesForDocument(this); | |
426 } | |
427 #endif | |
428 | |
429 if (m_docChanged && changedDocuments) | |
430 changedDocuments->remove(this); | |
431 delete m_tokenizer; | |
432 m_document.resetSkippingRef(0); | |
433 delete m_styleSelector; | |
434 delete m_docLoader; | |
435 | |
436 if (m_renderArena) { | |
437 delete m_renderArena; | |
438 m_renderArena = 0; | |
439 } | |
440 | |
441 #if ENABLE(XSLT) | |
442 xmlFreeDoc((xmlDocPtr)m_transformSource); | |
443 #endif | |
444 | |
445 #if ENABLE(XBL) | |
446 delete m_bindingManager; | |
447 #endif | |
448 | |
449 deleteAllValues(m_markers); | |
450 | |
451 clearAXObjectCache(); | |
452 | |
453 m_decoder = 0; | |
454 | |
455 unsigned count = sizeof(m_nameCollectionInfo) / sizeof(m_nameCollectionInfo[
0]); | |
456 for (unsigned i = 0; i < count; i++) | |
457 deleteAllValues(m_nameCollectionInfo[i]); | |
458 | |
459 #if ENABLE(DATABASE) | |
460 if (m_databaseThread) { | |
461 ASSERT(m_databaseThread->terminationRequested()); | |
462 m_databaseThread = 0; | |
463 } | |
464 #endif | |
465 | |
466 if (m_styleSheets) | |
467 m_styleSheets->documentDestroyed(); | |
468 | |
469 m_document = 0; | |
470 } | |
471 | |
472 void Document::resetLinkColor() | |
473 { | |
474 m_linkColor = Color(0, 0, 238); | |
475 } | |
476 | |
477 void Document::resetVisitedLinkColor() | |
478 { | |
479 m_visitedLinkColor = Color(85, 26, 139); | |
480 } | |
481 | |
482 void Document::resetActiveLinkColor() | |
483 { | |
484 m_activeLinkColor.setNamedColor("red"); | |
485 } | |
486 | |
487 void Document::setDocType(PassRefPtr<DocumentType> docType) | |
488 { | |
489 // This should never be called more than once. | |
490 // Note: This is not a public DOM method and can only be called by the parse
r. | |
491 ASSERT(!m_docType || !docType); | |
492 if (m_docType && docType) | |
493 return; | |
494 m_docType = docType; | |
495 if (m_docType) | |
496 m_docType->setDocument(this); | |
497 determineParseMode(); | |
498 } | |
499 | |
500 DOMImplementation* Document::implementation() const | |
501 { | |
502 if (!m_implementation) | |
503 m_implementation = DOMImplementation::create(); | |
504 return m_implementation.get(); | |
505 } | |
506 | |
507 void Document::childrenChanged(bool changedByParser, Node* beforeChange, Node* a
fterChange, int childCountDelta) | |
508 { | |
509 ContainerNode::childrenChanged(changedByParser, beforeChange, afterChange, c
hildCountDelta); | |
510 | |
511 // Invalidate the document element we have cached in case it was replaced. | |
512 m_documentElement = 0; | |
513 } | |
514 | |
515 Element* Document::documentElement() const | |
516 { | |
517 if (!m_documentElement) { | |
518 Node* n = firstChild(); | |
519 while (n && !n->isElementNode()) | |
520 n = n->nextSibling(); | |
521 m_documentElement = static_cast<Element*>(n); | |
522 } | |
523 | |
524 return m_documentElement.get(); | |
525 } | |
526 | |
527 PassRefPtr<Element> Document::createElement(const AtomicString& name, ExceptionC
ode& ec) | |
528 { | |
529 if (!isValidName(name)) { | |
530 ec = INVALID_CHARACTER_ERR; | |
531 return 0; | |
532 } | |
533 | |
534 if (m_isXHTML) | |
535 return HTMLElementFactory::createHTMLElement(name, this, 0, false); | |
536 | |
537 return createElement(QualifiedName(nullAtom, name, nullAtom), false, ec); | |
538 } | |
539 | |
540 PassRefPtr<DocumentFragment> Document::createDocumentFragment() | |
541 { | |
542 return new DocumentFragment(document()); | |
543 } | |
544 | |
545 PassRefPtr<Text> Document::createTextNode(const String& data) | |
546 { | |
547 return new Text(this, data); | |
548 } | |
549 | |
550 PassRefPtr<Comment> Document::createComment(const String& data) | |
551 { | |
552 return new Comment(this, data); | |
553 } | |
554 | |
555 PassRefPtr<CDATASection> Document::createCDATASection(const String& data, Except
ionCode& ec) | |
556 { | |
557 if (isHTMLDocument()) { | |
558 ec = NOT_SUPPORTED_ERR; | |
559 return 0; | |
560 } | |
561 return new CDATASection(this, data); | |
562 } | |
563 | |
564 PassRefPtr<ProcessingInstruction> Document::createProcessingInstruction(const St
ring& target, const String& data, ExceptionCode& ec) | |
565 { | |
566 if (!isValidName(target)) { | |
567 ec = INVALID_CHARACTER_ERR; | |
568 return 0; | |
569 } | |
570 if (isHTMLDocument()) { | |
571 ec = NOT_SUPPORTED_ERR; | |
572 return 0; | |
573 } | |
574 return new ProcessingInstruction(this, target, data); | |
575 } | |
576 | |
577 PassRefPtr<EntityReference> Document::createEntityReference(const String& name,
ExceptionCode& ec) | |
578 { | |
579 if (!isValidName(name)) { | |
580 ec = INVALID_CHARACTER_ERR; | |
581 return 0; | |
582 } | |
583 if (isHTMLDocument()) { | |
584 ec = NOT_SUPPORTED_ERR; | |
585 return 0; | |
586 } | |
587 return new EntityReference(this, name); | |
588 } | |
589 | |
590 PassRefPtr<EditingText> Document::createEditingTextNode(const String& text) | |
591 { | |
592 return new EditingText(this, text); | |
593 } | |
594 | |
595 PassRefPtr<CSSStyleDeclaration> Document::createCSSStyleDeclaration() | |
596 { | |
597 return CSSMutableStyleDeclaration::create(); | |
598 } | |
599 | |
600 PassRefPtr<Node> Document::importNode(Node* importedNode, bool deep, ExceptionCo
de& ec) | |
601 { | |
602 ec = 0; | |
603 | |
604 if (!importedNode | |
605 #if ENABLE(SVG) && ENABLE(DASHBOARD_SUPPORT) | |
606 || (importedNode->isSVGElement() && page() && page()->settings()->usesDa
shboardBackwardCompatibilityMode()) | |
607 #endif | |
608 ) { | |
609 ec = NOT_SUPPORTED_ERR; | |
610 return 0; | |
611 } | |
612 | |
613 switch (importedNode->nodeType()) { | |
614 case TEXT_NODE: | |
615 return createTextNode(importedNode->nodeValue()); | |
616 case CDATA_SECTION_NODE: | |
617 return createCDATASection(importedNode->nodeValue(), ec); | |
618 case ENTITY_REFERENCE_NODE: | |
619 return createEntityReference(importedNode->nodeName(), ec); | |
620 case PROCESSING_INSTRUCTION_NODE: | |
621 return createProcessingInstruction(importedNode->nodeName(), importe
dNode->nodeValue(), ec); | |
622 case COMMENT_NODE: | |
623 return createComment(importedNode->nodeValue()); | |
624 case ELEMENT_NODE: { | |
625 Element* oldElement = static_cast<Element*>(importedNode); | |
626 RefPtr<Element> newElement = createElementNS(oldElement->namespaceUR
I(), oldElement->tagQName().toString(), ec); | |
627 | |
628 if (ec) | |
629 return 0; | |
630 | |
631 NamedAttrMap* attrs = oldElement->attributes(true); | |
632 if (attrs) { | |
633 unsigned length = attrs->length(); | |
634 for (unsigned i = 0; i < length; i++) { | |
635 Attribute* attr = attrs->attributeItem(i); | |
636 newElement->setAttribute(attr->name(), attr->value().impl(),
ec); | |
637 if (ec) | |
638 return 0; | |
639 } | |
640 } | |
641 | |
642 newElement->copyNonAttributeProperties(oldElement); | |
643 | |
644 if (deep) { | |
645 for (Node* oldChild = oldElement->firstChild(); oldChild; oldChi
ld = oldChild->nextSibling()) { | |
646 RefPtr<Node> newChild = importNode(oldChild, true, ec); | |
647 if (ec) | |
648 return 0; | |
649 newElement->appendChild(newChild.release(), ec); | |
650 if (ec) | |
651 return 0; | |
652 } | |
653 } | |
654 | |
655 return newElement.release(); | |
656 } | |
657 case ATTRIBUTE_NODE: { | |
658 RefPtr<Attr> newAttr = new Attr(0, this, static_cast<Attr*>(imported
Node)->attr()->clone()); | |
659 newAttr->createTextChild(); | |
660 return newAttr.release(); | |
661 } | |
662 case DOCUMENT_FRAGMENT_NODE: { | |
663 DocumentFragment* oldFragment = static_cast<DocumentFragment*>(impor
tedNode); | |
664 RefPtr<DocumentFragment> newFragment = createDocumentFragment(); | |
665 if (deep) { | |
666 for (Node* oldChild = oldFragment->firstChild(); oldChild; oldCh
ild = oldChild->nextSibling()) { | |
667 RefPtr<Node> newChild = importNode(oldChild, true, ec); | |
668 if (ec) | |
669 return 0; | |
670 newFragment->appendChild(newChild.release(), ec); | |
671 if (ec) | |
672 return 0; | |
673 } | |
674 } | |
675 | |
676 return newFragment.release(); | |
677 } | |
678 case ENTITY_NODE: | |
679 case NOTATION_NODE: | |
680 // FIXME: It should be possible to import these node types, however
in DOM3 the DocumentType is readonly, so there isn't much sense in doing that. | |
681 // Ability to add these imported nodes to a DocumentType will be con
sidered for addition to a future release of the DOM. | |
682 case DOCUMENT_NODE: | |
683 case DOCUMENT_TYPE_NODE: | |
684 case XPATH_NAMESPACE_NODE: | |
685 break; | |
686 } | |
687 | |
688 ec = NOT_SUPPORTED_ERR; | |
689 return 0; | |
690 } | |
691 | |
692 | |
693 PassRefPtr<Node> Document::adoptNode(PassRefPtr<Node> source, ExceptionCode& ec) | |
694 { | |
695 if (!source) { | |
696 ec = NOT_SUPPORTED_ERR; | |
697 return 0; | |
698 } | |
699 | |
700 if (source->isReadOnlyNode()) { | |
701 ec = NO_MODIFICATION_ALLOWED_ERR; | |
702 return 0; | |
703 } | |
704 | |
705 switch (source->nodeType()) { | |
706 case ENTITY_NODE: | |
707 case NOTATION_NODE: | |
708 case DOCUMENT_NODE: | |
709 case DOCUMENT_TYPE_NODE: | |
710 case XPATH_NAMESPACE_NODE: | |
711 ec = NOT_SUPPORTED_ERR; | |
712 return 0; | |
713 case ATTRIBUTE_NODE: { | |
714 Attr* attr = static_cast<Attr*>(source.get()); | |
715 if (attr->ownerElement()) | |
716 attr->ownerElement()->removeAttributeNode(attr, ec); | |
717 attr->setSpecified(true); | |
718 break; | |
719 } | |
720 default: | |
721 if (source->parentNode()) | |
722 source->parentNode()->removeChild(source.get(), ec); | |
723 } | |
724 | |
725 for (Node* node = source.get(); node; node = node->traverseNextNode(source.g
et())) | |
726 node->setDocument(this); | |
727 | |
728 return source; | |
729 } | |
730 | |
731 bool Document::hasPrefixNamespaceMismatch(const QualifiedName& qName) | |
732 { | |
733 static const AtomicString xmlnsNamespaceURI("http://www.w3.org/2000/xmlns/")
; | |
734 static const AtomicString xmlns("xmlns"); | |
735 static const AtomicString xml("xml"); | |
736 | |
737 // These checks are from DOM Core Level 2, createElementNS | |
738 // http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-DocCrElNS | |
739 if (!qName.prefix().isEmpty() && qName.namespaceURI().isNull()) // createEle
mentNS(null, "html:div") | |
740 return true; | |
741 if (qName.prefix() == xml && qName.namespaceURI() != XMLNames::xmlNamespaceU
RI) // createElementNS("http://www.example.com", "xml:lang") | |
742 return true; | |
743 | |
744 // Required by DOM Level 3 Core and unspecified by DOM Level 2 Core: | |
745 // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocC
rElNS | |
746 // createElementNS("http://www.w3.org/2000/xmlns/", "foo:bar"), createElemen
tNS(null, "xmlns:bar") | |
747 if ((qName.prefix() == xmlns && qName.namespaceURI() != xmlnsNamespaceURI) |
| (qName.prefix() != xmlns && qName.namespaceURI() == xmlnsNamespaceURI)) | |
748 return true; | |
749 | |
750 return false; | |
751 } | |
752 | |
753 // FIXME: This should really be in a possible ElementFactory class | |
754 PassRefPtr<Element> Document::createElement(const QualifiedName& qName, bool cre
atedByParser, ExceptionCode& ec) | |
755 { | |
756 RefPtr<Element> e; | |
757 | |
758 // FIXME: Use registered namespaces and look up in a hash to find the right
factory. | |
759 if (qName.namespaceURI() == xhtmlNamespaceURI) | |
760 e = HTMLElementFactory::createHTMLElement(qName.localName(), this, 0, cr
eatedByParser); | |
761 #if ENABLE(SVG) | |
762 else if (qName.namespaceURI() == SVGNames::svgNamespaceURI) | |
763 e = SVGElementFactory::createSVGElement(qName, this, createdByParser); | |
764 #endif | |
765 | |
766 if (!e) | |
767 e = new Element(qName, document()); | |
768 | |
769 // FIXME: The element factories should be fixed to not ignore qName.prefix() | |
770 // Instead they should pass the entire qName into element creation so we don
't | |
771 // need to manually set the prefix after creation. | |
772 // Then this code can become ASSERT(qName == e.qname()); | |
773 // and Document::createElement can stop taking ExceptionCode& as well. | |
774 if (e && !qName.prefix().isNull()) { | |
775 ec = 0; | |
776 e->setPrefix(qName.prefix(), ec); | |
777 if (ec) | |
778 return 0; | |
779 } | |
780 | |
781 return e.release(); | |
782 } | |
783 | |
784 PassRefPtr<Element> Document::createElementNS(const String& namespaceURI, const
String& qualifiedName, ExceptionCode& ec) | |
785 { | |
786 String prefix, localName; | |
787 if (!parseQualifiedName(qualifiedName, prefix, localName, ec)) | |
788 return 0; | |
789 | |
790 QualifiedName qName(prefix, localName, namespaceURI); | |
791 if (hasPrefixNamespaceMismatch(qName)) { | |
792 ec = NAMESPACE_ERR; | |
793 return 0; | |
794 } | |
795 | |
796 return createElement(qName, false, ec); | |
797 } | |
798 | |
799 Element* Document::getElementById(const AtomicString& elementId) const | |
800 { | |
801 if (elementId.isEmpty()) | |
802 return 0; | |
803 | |
804 Element* element = m_elementsById.get(elementId.impl()); | |
805 if (element) | |
806 return element; | |
807 | |
808 if (m_duplicateIds.contains(elementId.impl())) { | |
809 // We know there's at least one node with this id, but we don't know wha
t the first one is. | |
810 for (Node *n = traverseNextNode(); n != 0; n = n->traverseNextNode()) { | |
811 if (n->isElementNode()) { | |
812 element = static_cast<Element*>(n); | |
813 if (element->hasID() && element->getAttribute(idAttr) == element
Id) { | |
814 m_duplicateIds.remove(elementId.impl()); | |
815 m_elementsById.set(elementId.impl(), element); | |
816 return element; | |
817 } | |
818 } | |
819 } | |
820 ASSERT_NOT_REACHED(); | |
821 } | |
822 return 0; | |
823 } | |
824 | |
825 String Document::readyState() const | |
826 { | |
827 if (Frame* f = frame()) { | |
828 if (f->loader()->isComplete()) | |
829 return "complete"; | |
830 if (parsing()) | |
831 return "loading"; | |
832 return "loaded"; | |
833 // FIXME: What does "interactive" mean? | |
834 // FIXME: Missing support for "uninitialized". | |
835 } | |
836 return String(); | |
837 } | |
838 | |
839 String Document::inputEncoding() const | |
840 { | |
841 if (TextResourceDecoder* d = decoder()) | |
842 return d->encoding().name(); | |
843 return String(); | |
844 } | |
845 | |
846 String Document::defaultCharset() const | |
847 { | |
848 if (Settings* settings = this->settings()) | |
849 return settings->defaultTextEncodingName(); | |
850 return String(); | |
851 } | |
852 | |
853 void Document::setCharset(const String& charset) | |
854 { | |
855 if (!decoder()) | |
856 return; | |
857 decoder()->setEncoding(charset, TextResourceDecoder::UserChosenEncoding); | |
858 } | |
859 | |
860 UScriptCode Document::dominantScript() const | |
861 { | |
862 struct EncodingScript { | |
863 const char* encoding; | |
864 UScriptCode script; | |
865 }; | |
866 | |
867 // inputEncoding() always returns a canonical name. We use | |
868 // MIME names and IANA names (if the former is not available). | |
869 static const EncodingScript encodingScriptList[] = { | |
870 { "GB2312", USCRIPT_SIMPLIFIED_HAN }, | |
871 { "GBK", USCRIPT_SIMPLIFIED_HAN }, | |
872 { "GB18030", USCRIPT_SIMPLIFIED_HAN }, | |
873 { "Big5", USCRIPT_TRADITIONAL_HAN }, | |
874 { "Big5-HKSCS", USCRIPT_TRADITIONAL_HAN }, | |
875 { "Shift_JIS", USCRIPT_HIRAGANA}, | |
876 { "EUC-JP", USCRIPT_HIRAGANA }, // Japanese (USCRIPT_JAPANESE) | |
877 { "ISO-2022-KR", USCRIPT_HIRAGANA }, | |
878 { "EUC-KR", USCRIPT_HANGUL }, // Korean (USCRIPT_KOREAN) | |
879 { "TIS-620", USCRIPT_THAI }, | |
880 { "ISO-8859-1", USCRIPT_LATIN }, | |
881 { "ISO-8859-15", USCRIPT_LATIN }, | |
882 { "windows-1252", USCRIPT_LATIN }, | |
883 { "ISO-8859-2", USCRIPT_LATIN }, | |
884 { "windows-1250", USCRIPT_LATIN }, | |
885 { "ISO-8859-3", USCRIPT_LATIN }, | |
886 { "ISO-8859-4", USCRIPT_LATIN }, | |
887 { "ISO-8859-13", USCRIPT_LATIN }, | |
888 { "windows-1257", USCRIPT_LATIN }, | |
889 { "ISO-8859-5", USCRIPT_CYRILLIC }, | |
890 { "windows-1251", USCRIPT_CYRILLIC }, | |
891 { "KOI8-R", USCRIPT_CYRILLIC }, | |
892 { "KOI8-U", USCRIPT_CYRILLIC }, | |
893 { "ISO-8859-6", USCRIPT_ARABIC }, | |
894 { "windows-1256", USCRIPT_ARABIC }, | |
895 { "ISO-8859-7", USCRIPT_GREEK }, | |
896 { "windows-1253", USCRIPT_GREEK }, | |
897 { "ISO-8859-8", USCRIPT_HEBREW }, | |
898 { "windows-1255", USCRIPT_HEBREW }, | |
899 { "ISO-8859-9", USCRIPT_LATIN }, // Turkish | |
900 { "windows-1254", USCRIPT_LATIN }, | |
901 { "ISO-8859-10", USCRIPT_LATIN }, // Nordic | |
902 { "ISO-8859-14", USCRIPT_LATIN }, // Celtic | |
903 { "ISO-8859-16", USCRIPT_LATIN }, // Romanian | |
904 { "windows-1258", USCRIPT_LATIN }, // Vietnamese | |
905 }; | |
906 | |
907 static HashMap<String, UScriptCode> encodingScriptMap; | |
908 | |
909 if (encodingScriptMap.isEmpty()) { | |
910 for (unsigned i = 0; i < sizeof(encodingScriptList) / sizeof(encodingScr
iptList[0]); ++i) | |
911 encodingScriptMap.set(encodingScriptList[i].encoding, | |
912 encodingScriptList[i].script); | |
913 } | |
914 | |
915 if (m_dominantScript != USCRIPT_INVALID_CODE) | |
916 return m_dominantScript; | |
917 String encoding = inputEncoding(); | |
918 if (encoding.isEmpty()) | |
919 return m_dominantScript; | |
920 | |
921 HashMap<String, UScriptCode>::iterator it = encodingScriptMap.find(encoding)
; | |
922 if (it != encodingScriptMap.end()) | |
923 m_dominantScript = it->second; | |
924 else | |
925 // TODO(jungshik) : should return a script corresponding to the locale. | |
926 m_dominantScript = USCRIPT_COMMON; | |
927 return m_dominantScript; | |
928 } | |
929 | |
930 void Document::setXMLVersion(const String& version, ExceptionCode& ec) | |
931 { | |
932 if (!implementation()->hasFeature("XML", String())) { | |
933 ec = NOT_SUPPORTED_ERR; | |
934 return; | |
935 } | |
936 | |
937 // FIXME: Also raise NOT_SUPPORTED_ERR if the version is set to a value that
is not supported by this Document. | |
938 | |
939 m_xmlVersion = version; | |
940 } | |
941 | |
942 void Document::setXMLStandalone(bool standalone, ExceptionCode& ec) | |
943 { | |
944 if (!implementation()->hasFeature("XML", String())) { | |
945 ec = NOT_SUPPORTED_ERR; | |
946 return; | |
947 } | |
948 | |
949 m_xmlStandalone = standalone; | |
950 } | |
951 | |
952 void Document::setDocumentURI(const String& uri) | |
953 { | |
954 m_documentURI = uri; | |
955 updateBaseURL(); | |
956 } | |
957 | |
958 KURL Document::baseURI() const | |
959 { | |
960 return m_baseURL; | |
961 } | |
962 | |
963 Element* Document::elementFromPoint(int x, int y) const | |
964 { | |
965 if (!renderer()) | |
966 return 0; | |
967 | |
968 HitTestRequest request(true, true); | |
969 HitTestResult result(IntPoint(x, y)); | |
970 renderer()->layer()->hitTest(request, result); | |
971 | |
972 Node* n = result.innerNode(); | |
973 while (n && !n->isElementNode()) | |
974 n = n->parentNode(); | |
975 if (n) | |
976 n = n->shadowAncestorNode(); | |
977 return static_cast<Element*>(n); | |
978 } | |
979 | |
980 void Document::addElementById(const AtomicString& elementId, Element* element) | |
981 { | |
982 typedef HashMap<AtomicStringImpl*, Element*>::iterator iterator; | |
983 if (!m_duplicateIds.contains(elementId.impl())) { | |
984 // Fast path. The ID is not already in m_duplicateIds, so we assume that
it's | |
985 // also not already in m_elementsById and do an add. If that add succeed
s, we're done. | |
986 pair<iterator, bool> addResult = m_elementsById.add(elementId.impl(), el
ement); | |
987 if (addResult.second) | |
988 return; | |
989 // The add failed, so this ID was already cached in m_elementsById. | |
990 // There are multiple elements with this ID. Remove the m_elementsById | |
991 // cache for this ID so getElementById searches for it next time it is c
alled. | |
992 m_elementsById.remove(addResult.first); | |
993 m_duplicateIds.add(elementId.impl()); | |
994 } else { | |
995 // There are multiple elements with this ID. If it exists, remove the m_
elementsById | |
996 // cache for this ID so getElementById searches for it next time it is c
alled. | |
997 iterator cachedItem = m_elementsById.find(elementId.impl()); | |
998 if (cachedItem != m_elementsById.end()) { | |
999 m_elementsById.remove(cachedItem); | |
1000 m_duplicateIds.add(elementId.impl()); | |
1001 } | |
1002 } | |
1003 m_duplicateIds.add(elementId.impl()); | |
1004 } | |
1005 | |
1006 void Document::removeElementById(const AtomicString& elementId, Element* element
) | |
1007 { | |
1008 if (m_elementsById.get(elementId.impl()) == element) | |
1009 m_elementsById.remove(elementId.impl()); | |
1010 else | |
1011 m_duplicateIds.remove(elementId.impl()); | |
1012 } | |
1013 | |
1014 Element* Document::getElementByAccessKey(const String& key) const | |
1015 { | |
1016 if (key.isEmpty()) | |
1017 return 0; | |
1018 if (!m_accessKeyMapValid) { | |
1019 for (Node* n = firstChild(); n; n = n->traverseNextNode()) { | |
1020 if (!n->isElementNode()) | |
1021 continue; | |
1022 Element* element = static_cast<Element*>(n); | |
1023 const AtomicString& accessKey = element->getAttribute(accesskeyAttr)
; | |
1024 if (!accessKey.isEmpty()) | |
1025 m_elementsByAccessKey.set(accessKey.impl(), element); | |
1026 } | |
1027 m_accessKeyMapValid = true; | |
1028 } | |
1029 return m_elementsByAccessKey.get(key.impl()); | |
1030 } | |
1031 | |
1032 void Document::updateTitle() | |
1033 { | |
1034 if (Frame* f = frame()) | |
1035 f->loader()->setTitle(m_title); | |
1036 } | |
1037 | |
1038 void Document::setTitle(const String& title, Element* titleElement) | |
1039 { | |
1040 if (!titleElement) { | |
1041 // Title set by JavaScript -- overrides any title elements. | |
1042 m_titleSetExplicitly = true; | |
1043 if (!isHTMLDocument()) | |
1044 m_titleElement = 0; | |
1045 else if (!m_titleElement) { | |
1046 if (HTMLElement* headElement = head()) { | |
1047 ExceptionCode ec = 0; | |
1048 m_titleElement = createElement("title", ec); | |
1049 ASSERT(!ec); | |
1050 headElement->appendChild(m_titleElement, ec); | |
1051 ASSERT(!ec); | |
1052 } | |
1053 } | |
1054 } else if (titleElement != m_titleElement) { | |
1055 if (m_titleElement || m_titleSetExplicitly) | |
1056 // Only allow the first title element to change the title -- others
have no effect. | |
1057 return; | |
1058 m_titleElement = titleElement; | |
1059 } | |
1060 | |
1061 if (m_title == title) | |
1062 return; | |
1063 | |
1064 m_title = title; | |
1065 updateTitle(); | |
1066 | |
1067 if (m_titleSetExplicitly && m_titleElement && m_titleElement->hasTagName(tit
leTag)) | |
1068 static_cast<HTMLTitleElement*>(m_titleElement.get())->setText(m_title); | |
1069 } | |
1070 | |
1071 void Document::removeTitle(Element* titleElement) | |
1072 { | |
1073 if (m_titleElement != titleElement) | |
1074 return; | |
1075 | |
1076 m_titleElement = 0; | |
1077 m_titleSetExplicitly = false; | |
1078 | |
1079 // Update title based on first title element in the head, if one exists. | |
1080 if (HTMLElement* headElement = head()) { | |
1081 for (Node* e = headElement->firstChild(); e; e = e->nextSibling()) | |
1082 if (e->hasTagName(titleTag)) { | |
1083 HTMLTitleElement* titleElement = static_cast<HTMLTitleElement*>(
e); | |
1084 setTitle(titleElement->text(), titleElement); | |
1085 break; | |
1086 } | |
1087 } | |
1088 | |
1089 if (!m_titleElement && !m_title.isEmpty()) { | |
1090 m_title = ""; | |
1091 updateTitle(); | |
1092 } | |
1093 } | |
1094 | |
1095 String Document::nodeName() const | |
1096 { | |
1097 return "#document"; | |
1098 } | |
1099 | |
1100 Node::NodeType Document::nodeType() const | |
1101 { | |
1102 return DOCUMENT_NODE; | |
1103 } | |
1104 | |
1105 FrameView* Document::view() const | |
1106 { | |
1107 return m_frame ? m_frame->view() : 0; | |
1108 } | |
1109 | |
1110 Page* Document::page() const | |
1111 { | |
1112 return m_frame ? m_frame->page() : 0; | |
1113 } | |
1114 | |
1115 Settings* Document::settings() const | |
1116 { | |
1117 return m_frame ? m_frame->settings() : 0; | |
1118 } | |
1119 | |
1120 PassRefPtr<Range> Document::createRange() | |
1121 { | |
1122 return Range::create(this); | |
1123 } | |
1124 | |
1125 PassRefPtr<NodeIterator> Document::createNodeIterator(Node* root, unsigned whatT
oShow, | |
1126 PassRefPtr<NodeFilter> filter, bool expandEntityReferences, ExceptionCode& e
c) | |
1127 { | |
1128 if (!root) { | |
1129 ec = NOT_SUPPORTED_ERR; | |
1130 return 0; | |
1131 } | |
1132 return NodeIterator::create(root, whatToShow, filter, expandEntityReferences
); | |
1133 } | |
1134 | |
1135 PassRefPtr<TreeWalker> Document::createTreeWalker(Node *root, unsigned whatToSho
w, | |
1136 PassRefPtr<NodeFilter> filter, bool expandEntityReferences, ExceptionCode& e
c) | |
1137 { | |
1138 if (!root) { | |
1139 ec = NOT_SUPPORTED_ERR; | |
1140 return 0; | |
1141 } | |
1142 return TreeWalker::create(root, whatToShow, filter, expandEntityReferences); | |
1143 } | |
1144 | |
1145 void Document::setDocumentChanged(bool b) | |
1146 { | |
1147 if (b) { | |
1148 if (!m_docChanged) { | |
1149 if (!changedDocuments) | |
1150 changedDocuments = new HashSet<Document*>; | |
1151 changedDocuments->add(this); | |
1152 } | |
1153 if (m_accessKeyMapValid) { | |
1154 m_accessKeyMapValid = false; | |
1155 m_elementsByAccessKey.clear(); | |
1156 } | |
1157 } else { | |
1158 if (m_docChanged && changedDocuments) | |
1159 changedDocuments->remove(this); | |
1160 } | |
1161 | |
1162 m_docChanged = b; | |
1163 } | |
1164 | |
1165 void Document::recalcStyle(StyleChange change) | |
1166 { | |
1167 // we should not enter style recalc while painting | |
1168 if (frame() && frame()->isPainting()) { | |
1169 ASSERT(!frame()->isPainting()); | |
1170 return; | |
1171 } | |
1172 | |
1173 if (m_inStyleRecalc) | |
1174 return; // Guard against re-entrancy. -dwh | |
1175 | |
1176 m_inStyleRecalc = true; | |
1177 suspendPostAttachCallbacks(); | |
1178 | |
1179 ASSERT(!renderer() || renderArena()); | |
1180 if (!renderer() || !renderArena()) | |
1181 goto bail_out; | |
1182 | |
1183 if (change == Force) { | |
1184 // style selector may set this again during recalc | |
1185 m_hasNodesWithPlaceholderStyle = false; | |
1186 | |
1187 RenderStyle* oldStyle = renderer()->style(); | |
1188 if (oldStyle) | |
1189 oldStyle->ref(); | |
1190 RenderStyle* _style = new (m_renderArena) RenderStyle(); | |
1191 _style->ref(); | |
1192 _style->setDisplay(BLOCK); | |
1193 _style->setVisuallyOrdered(visuallyOrdered); | |
1194 _style->setZoom(frame()->pageZoomFactor()); | |
1195 m_styleSelector->setStyle(_style); | |
1196 | |
1197 FontDescription fontDescription; | |
1198 fontDescription.setUsePrinterFont(printing()); | |
1199 // TODO(jungshik): Eventually, we need to derive the dominant script | |
1200 // for the current node based on 'xml:lang' and 'lang' specified for | |
1201 // it or inherited from its parent rather than using the document-wide | |
1202 // value (inferred from charset). Note also that it does not work | |
1203 // for 'script-agnostic' charsets like UTF-8. In that case, Firefox | |
1204 // uses the script corresponding to the application locale. | |
1205 // While a document is loaded, this function is called multiple | |
1206 // times. At the beginning, the document charset is not known and | |
1207 // dominantScript remains invalid. Only when it's determined, we | |
1208 // change the font accordingly. | |
1209 // See http://bugs.webkit.org/show_bug.cgi?id=10874 and | |
1210 // https://bugs.webkit.org/show_bug.cgi?id=18085 | |
1211 UScriptCode script = dominantScript(); | |
1212 if (script != USCRIPT_INVALID_CODE) | |
1213 fontDescription.setDominantScript(script); | |
1214 if (Settings* settings = this->settings()) { | |
1215 fontDescription.setRenderingMode(settings->fontRenderingMode()); | |
1216 if (printing() && !settings->shouldPrintBackgrounds()) | |
1217 _style->setForceBackgroundsToWhite(true); | |
1218 const AtomicString& stdfont = settings->standardFontFamily(); | |
1219 if (!stdfont.isEmpty()) { | |
1220 fontDescription.firstFamily().setFamily(stdfont); | |
1221 FontFamily& currFamily = fontDescription.firstFamily(); | |
1222 if (script != USCRIPT_INVALID_CODE) { | |
1223 // TODO(jungshik) : I might as well modify |genericFamily| of | |
1224 // |fontDescription| here, but I'm wary of a potential breakag
e. | |
1225 // For now, just use a temporary variable. | |
1226 FontDescription tmpDescription; | |
1227 tmpDescription.setGenericFamily(FontDescription::StandardFamil
y); | |
1228 AtomicString docFont = FontCache::getGenericFontForScript( | |
1229 script, tmpDescription); | |
1230 if (!docFont.isEmpty()) { | |
1231 RefPtr<SharedFontFamily> newFamily(SharedFontFamily::creat
e()); | |
1232 newFamily->setFamily(docFont); | |
1233 currFamily.appendFamily(newFamily); | |
1234 currFamily = *newFamily; | |
1235 } | |
1236 } | |
1237 currFamily.appendFamily(0); | |
1238 } | |
1239 fontDescription.setKeywordSize(CSSValueMedium - CSSValueXxSmall + 1)
; | |
1240 m_styleSelector->setFontSize(fontDescription, m_styleSelector->fontS
izeForKeyword(CSSValueMedium, inCompatMode(), false)); | |
1241 } | |
1242 | |
1243 _style->setFontDescription(fontDescription); | |
1244 _style->font().update(m_styleSelector->fontSelector()); | |
1245 if (inCompatMode()) | |
1246 _style->setHtmlHacks(true); // enable html specific rendering tricks | |
1247 | |
1248 StyleChange ch = diff(_style, oldStyle); | |
1249 if (renderer() && ch != NoChange) | |
1250 renderer()->setStyle(_style); | |
1251 if (change != Force) | |
1252 change = ch; | |
1253 | |
1254 _style->deref(m_renderArena); | |
1255 if (oldStyle) | |
1256 oldStyle->deref(m_renderArena); | |
1257 } | |
1258 | |
1259 for (Node* n = firstChild(); n; n = n->nextSibling()) | |
1260 if (change >= Inherit || n->hasChangedChild() || n->changed()) | |
1261 n->recalcStyle(change); | |
1262 | |
1263 if (changed() && view()) | |
1264 view()->layout(); | |
1265 | |
1266 bail_out: | |
1267 setChanged(NoStyleChange); | |
1268 setHasChangedChild(false); | |
1269 setDocumentChanged(false); | |
1270 | |
1271 resumePostAttachCallbacks(); | |
1272 m_inStyleRecalc = false; | |
1273 | |
1274 // If we wanted to call implicitClose() during recalcStyle, do so now that w
e're finished. | |
1275 if (m_closeAfterStyleRecalc) { | |
1276 m_closeAfterStyleRecalc = false; | |
1277 implicitClose(); | |
1278 } | |
1279 } | |
1280 | |
1281 void Document::updateRendering() | |
1282 { | |
1283 if (hasChangedChild() && !inPageCache()) | |
1284 recalcStyle(NoChange); | |
1285 | |
1286 // Tell the animation controller that the style is available and it can star
t animations | |
1287 if (m_frame) | |
1288 m_frame->animation()->styleAvailable(); | |
1289 } | |
1290 | |
1291 void Document::updateDocumentsRendering() | |
1292 { | |
1293 if (!changedDocuments) | |
1294 return; | |
1295 | |
1296 while (changedDocuments->size()) { | |
1297 HashSet<Document*>::iterator it = changedDocuments->begin(); | |
1298 Document* doc = *it; | |
1299 changedDocuments->remove(it); | |
1300 | |
1301 doc->m_docChanged = false; | |
1302 doc->updateRendering(); | |
1303 } | |
1304 } | |
1305 | |
1306 void Document::updateLayout() | |
1307 { | |
1308 if (Element* oe = ownerElement()) | |
1309 oe->document()->updateLayout(); | |
1310 | |
1311 // FIXME: Dave Hyatt's pretty sure we can remove this because layout calls r
ecalcStyle as needed. | |
1312 updateRendering(); | |
1313 | |
1314 // Only do a layout if changes have occurred that make it necessary. | |
1315 FrameView* v = view(); | |
1316 if (v && renderer() && (v->layoutPending() || renderer()->needsLayout())) | |
1317 v->layout(); | |
1318 } | |
1319 | |
1320 // FIXME: This is a bad idea and needs to be removed eventually. | |
1321 // Other browsers load stylesheets before they continue parsing the web page. | |
1322 // Since we don't, we can run JavaScript code that needs answers before the | |
1323 // stylesheets are loaded. Doing a layout ignoring the pending stylesheets | |
1324 // lets us get reasonable answers. The long term solution to this problem is | |
1325 // to instead suspend JavaScript execution. | |
1326 void Document::updateLayoutIgnorePendingStylesheets() | |
1327 { | |
1328 bool oldIgnore = m_ignorePendingStylesheets; | |
1329 | |
1330 if (!haveStylesheetsLoaded()) { | |
1331 m_ignorePendingStylesheets = true; | |
1332 // FIXME: We are willing to attempt to suppress painting with outdated s
tyle info only once. Our assumption is that it would be | |
1333 // dangerous to try to stop it a second time, after page content has alr
eady been loaded and displayed | |
1334 // with accurate style information. (Our suppression involves blanking
the whole page at the | |
1335 // moment. If it were more refined, we might be able to do something be
tter.) | |
1336 // It's worth noting though that this entire method is a hack, since wha
t we really want to do is | |
1337 // suspend JS instead of doing a layout with inaccurate information. | |
1338 if (body() && !body()->renderer() && m_pendingSheetLayout == NoLayoutWit
hPendingSheets) { | |
1339 m_pendingSheetLayout = DidLayoutWithPendingSheets; | |
1340 updateStyleSelector(); | |
1341 } else if (m_hasNodesWithPlaceholderStyle) | |
1342 // If new nodes have been added or style recalc has been done with s
tyle sheets still pending, some nodes | |
1343 // may not have had their real style calculated yet. Normally this g
ets cleaned when style sheets arrive | |
1344 // but here we need up-to-date style immediatly. | |
1345 recalcStyle(Force); | |
1346 } | |
1347 | |
1348 updateLayout(); | |
1349 | |
1350 m_ignorePendingStylesheets = oldIgnore; | |
1351 } | |
1352 | |
1353 void Document::attach() | |
1354 { | |
1355 ASSERT(!attached()); | |
1356 ASSERT(!m_inPageCache); | |
1357 ASSERT(!m_axObjectCache); | |
1358 | |
1359 if (!m_renderArena) | |
1360 m_renderArena = new RenderArena(); | |
1361 | |
1362 // Create the rendering tree | |
1363 setRenderer(new (m_renderArena) RenderView(this, view())); | |
1364 | |
1365 if (!m_styleSelector) { | |
1366 bool matchAuthorAndUserStyles = true; | |
1367 if (Settings* docSettings = settings()) | |
1368 matchAuthorAndUserStyles = docSettings->authorAndUserStylesEnabled()
; | |
1369 m_styleSelector = new CSSStyleSelector(this, userStyleSheet(), m_styleSh
eets.get(), m_mappedElementSheet.get(), !inCompatMode(), matchAuthorAndUserStyle
s); | |
1370 } | |
1371 | |
1372 recalcStyle(Force); | |
1373 | |
1374 RenderObject* render = renderer(); | |
1375 setRenderer(0); | |
1376 | |
1377 ContainerNode::attach(); | |
1378 | |
1379 setRenderer(render); | |
1380 } | |
1381 | |
1382 void Document::detach() | |
1383 { | |
1384 ASSERT(attached()); | |
1385 ASSERT(!m_inPageCache); | |
1386 | |
1387 clearAXObjectCache(); | |
1388 | |
1389 RenderObject* render = renderer(); | |
1390 | |
1391 // indicate destruction mode, i.e. attached() but renderer == 0 | |
1392 setRenderer(0); | |
1393 | |
1394 // Empty out these lists as a performance optimization, since detaching | |
1395 // all the individual render objects will cause all the RenderImage | |
1396 // objects to remove themselves from the lists. | |
1397 m_imageLoadEventDispatchSoonList.clear(); | |
1398 m_imageLoadEventDispatchingList.clear(); | |
1399 | |
1400 m_hoverNode = 0; | |
1401 m_focusedNode = 0; | |
1402 m_activeNode = 0; | |
1403 | |
1404 ContainerNode::detach(); | |
1405 | |
1406 if (render) | |
1407 render->destroy(); | |
1408 | |
1409 // This is required, as our Frame might delete itself as soon as it detaches | |
1410 // us. However, this violates Node::detach() symantics, as it's never | |
1411 // possible to re-attach. Eventually Document::detach() should be renamed | |
1412 // or this call made explicit in each of the callers of Document::detach(). | |
1413 clearFramePointer(); | |
1414 | |
1415 if (m_renderArena) { | |
1416 delete m_renderArena; | |
1417 m_renderArena = 0; | |
1418 } | |
1419 } | |
1420 | |
1421 void Document::clearFramePointer() | |
1422 { | |
1423 m_frame = 0; | |
1424 } | |
1425 | |
1426 void Document::removeAllEventListenersFromAllNodes() | |
1427 { | |
1428 m_windowEventListeners.clear(); | |
1429 removeAllDisconnectedNodeEventListeners(); | |
1430 for (Node *n = this; n; n = n->traverseNextNode()) { | |
1431 if (!n->isEventTargetNode()) | |
1432 continue; | |
1433 EventTargetNodeCast(n)->removeAllEventListeners(); | |
1434 } | |
1435 } | |
1436 | |
1437 void Document::registerDisconnectedNodeWithEventListeners(Node* node) | |
1438 { | |
1439 m_disconnectedNodesWithEventListeners.add(node); | |
1440 } | |
1441 | |
1442 void Document::unregisterDisconnectedNodeWithEventListeners(Node* node) | |
1443 { | |
1444 m_disconnectedNodesWithEventListeners.remove(node); | |
1445 } | |
1446 | |
1447 void Document::removeAllDisconnectedNodeEventListeners() | |
1448 { | |
1449 HashSet<Node*>::iterator end = m_disconnectedNodesWithEventListeners.end(); | |
1450 for (HashSet<Node*>::iterator i = m_disconnectedNodesWithEventListeners.begi
n(); i != end; ++i) | |
1451 EventTargetNodeCast(*i)->removeAllEventListeners(); | |
1452 m_disconnectedNodesWithEventListeners.clear(); | |
1453 } | |
1454 | |
1455 void Document::clearAXObjectCache() | |
1456 { | |
1457 // clear cache in top document | |
1458 if (m_axObjectCache) { | |
1459 delete m_axObjectCache; | |
1460 m_axObjectCache = 0; | |
1461 return; | |
1462 } | |
1463 | |
1464 // ask the top-level document to clear its cache | |
1465 Document* doc = topDocument(); | |
1466 if (doc != this) | |
1467 doc->clearAXObjectCache(); | |
1468 } | |
1469 | |
1470 AXObjectCache* Document::axObjectCache() const | |
1471 { | |
1472 // The only document that actually has a AXObjectCache is the top-level | |
1473 // document. This is because we need to be able to get from any WebCoreAXOb
ject | |
1474 // to any other WebCoreAXObject on the same page. Using a single cache allo
ws | |
1475 // lookups across nested webareas (i.e. multiple documents). | |
1476 | |
1477 if (m_axObjectCache) { | |
1478 // return already known top-level cache | |
1479 if (!ownerElement()) | |
1480 return m_axObjectCache; | |
1481 | |
1482 // In some pages with frames, the cache is created before the sub-webare
a is | |
1483 // inserted into the tree. Here, we catch that case and just toss the o
ld | |
1484 // cache and start over. | |
1485 // NOTE: This recovery may no longer be needed. I have been unable to tr
igger | |
1486 // it again. See rdar://5794454 | |
1487 // FIXME: Can this be fixed when inserting the subframe instead of now? | |
1488 // FIXME: If this function was called to get the cache in order to remov
e | |
1489 // an AXObject, we are now deleting the cache as a whole and returning a | |
1490 // new empty cache that does not contain the AXObject! That should actua
lly | |
1491 // be OK. I am concerned about other cases like this where accessing the | |
1492 // cache blows away the AXObject being operated on. | |
1493 delete m_axObjectCache; | |
1494 m_axObjectCache = 0; | |
1495 } | |
1496 | |
1497 // ask the top-level document for its cache | |
1498 Document* doc = topDocument(); | |
1499 if (doc != this) | |
1500 return doc->axObjectCache(); | |
1501 | |
1502 // this is the top-level document, so install a new cache | |
1503 m_axObjectCache = new AXObjectCache; | |
1504 return m_axObjectCache; | |
1505 } | |
1506 | |
1507 void Document::setVisuallyOrdered() | |
1508 { | |
1509 visuallyOrdered = true; | |
1510 if (renderer()) | |
1511 renderer()->style()->setVisuallyOrdered(true); | |
1512 } | |
1513 | |
1514 Tokenizer* Document::createTokenizer() | |
1515 { | |
1516 // FIXME: this should probably pass the frame instead | |
1517 return new XMLTokenizer(this, view()); | |
1518 } | |
1519 | |
1520 void Document::open(Document* ownerDocument) | |
1521 { | |
1522 if (ownerDocument) { | |
1523 setURL(ownerDocument->url()); | |
1524 m_cookieURL = ownerDocument->cookieURL(); | |
1525 m_securityOrigin = ownerDocument->securityOrigin(); | |
1526 } | |
1527 | |
1528 if (m_frame) { | |
1529 if (m_frame->loader()->isLoadingMainResource() || (tokenizer() && tokeni
zer()->executingScript())) | |
1530 return; | |
1531 | |
1532 if (m_frame->loader()->state() == FrameStateProvisional) | |
1533 m_frame->loader()->stopAllLoaders(); | |
1534 } | |
1535 | |
1536 implicitOpen(); | |
1537 | |
1538 if (m_frame) | |
1539 m_frame->loader()->didExplicitOpen(); | |
1540 } | |
1541 | |
1542 void Document::cancelParsing() | |
1543 { | |
1544 if (m_tokenizer) { | |
1545 // We have to clear the tokenizer to avoid possibly triggering | |
1546 // the onload handler when closing as a side effect of a cancel-style | |
1547 // change, such as opening a new document or closing the window while | |
1548 // still parsing | |
1549 delete m_tokenizer; | |
1550 m_tokenizer = 0; | |
1551 close(); | |
1552 } | |
1553 } | |
1554 | |
1555 void Document::implicitOpen() | |
1556 { | |
1557 cancelParsing(); | |
1558 | |
1559 clear(); | |
1560 m_tokenizer = createTokenizer(); | |
1561 setParsing(true); | |
1562 } | |
1563 | |
1564 HTMLElement* Document::body() | |
1565 { | |
1566 Node* de = documentElement(); | |
1567 if (!de) | |
1568 return 0; | |
1569 | |
1570 // try to prefer a FRAMESET element over BODY | |
1571 Node* body = 0; | |
1572 for (Node* i = de->firstChild(); i; i = i->nextSibling()) { | |
1573 if (i->hasTagName(framesetTag)) | |
1574 return static_cast<HTMLElement*>(i); | |
1575 | |
1576 if (i->hasTagName(bodyTag)) | |
1577 body = i; | |
1578 } | |
1579 return static_cast<HTMLElement*>(body); | |
1580 } | |
1581 | |
1582 void Document::setBody(PassRefPtr<HTMLElement> newBody, ExceptionCode& ec) | |
1583 { | |
1584 if (!newBody || !documentElement()) { | |
1585 ec = HIERARCHY_REQUEST_ERR; | |
1586 return; | |
1587 } | |
1588 | |
1589 HTMLElement* b = body(); | |
1590 if (!b) | |
1591 documentElement()->appendChild(newBody, ec); | |
1592 else | |
1593 documentElement()->replaceChild(newBody, b, ec); | |
1594 } | |
1595 | |
1596 HTMLHeadElement* Document::head() | |
1597 { | |
1598 Node* de = documentElement(); | |
1599 if (!de) | |
1600 return 0; | |
1601 | |
1602 for (Node* e = de->firstChild(); e; e = e->nextSibling()) | |
1603 if (e->hasTagName(headTag)) | |
1604 return static_cast<HTMLHeadElement*>(e); | |
1605 | |
1606 return 0; | |
1607 } | |
1608 | |
1609 void Document::close() | |
1610 { | |
1611 Frame* frame = this->frame(); | |
1612 if (frame) { | |
1613 // This code calls implicitClose() if all loading has completed. | |
1614 FrameLoader* frameLoader = frame->loader(); | |
1615 frameLoader->endIfNotLoadingMainResource(); | |
1616 frameLoader->checkCompleted(); | |
1617 } else { | |
1618 // Because we have no frame, we don't know if all loading has completed, | |
1619 // so we just call implicitClose() immediately. FIXME: This might fire | |
1620 // the load event prematurely <http://bugs.webkit.org/show_bug.cgi?id=14
568>. | |
1621 implicitClose(); | |
1622 } | |
1623 } | |
1624 | |
1625 void Document::implicitClose() | |
1626 { | |
1627 // If we're in the middle of recalcStyle, we need to defer the close until t
he style information is accurate and all elements are re-attached. | |
1628 if (m_inStyleRecalc) { | |
1629 m_closeAfterStyleRecalc = true; | |
1630 return; | |
1631 } | |
1632 | |
1633 bool wasLocationChangePending = frame() && frame()->loader()->isScheduledLoc
ationChangePending(); | |
1634 bool doload = !parsing() && m_tokenizer && !m_processingLoadEvent && !wasLoc
ationChangePending; | |
1635 | |
1636 if (!doload) | |
1637 return; | |
1638 | |
1639 m_processingLoadEvent = true; | |
1640 | |
1641 m_wellFormed = m_tokenizer && m_tokenizer->wellFormed(); | |
1642 | |
1643 // We have to clear the tokenizer, in case someone document.write()s from th
e | |
1644 // onLoad event handler, as in Radar 3206524. | |
1645 delete m_tokenizer; | |
1646 m_tokenizer = 0; | |
1647 | |
1648 // Parser should have picked up all preloads by now | |
1649 m_docLoader->clearPreloads(); | |
1650 | |
1651 // Create a body element if we don't already have one. See Radar 3758785. | |
1652 if (!this->body() && isHTMLDocument()) { | |
1653 if (Node* documentElement = this->documentElement()) { | |
1654 ExceptionCode ec = 0; | |
1655 documentElement->appendChild(new HTMLBodyElement(this), ec); | |
1656 ASSERT(!ec); | |
1657 } | |
1658 } | |
1659 | |
1660 // FIXME: We kick off the icon loader when the Document is done parsing. | |
1661 // There are earlier opportunities we could start it: | |
1662 // -When the <head> finishes parsing | |
1663 // -When any new HTMLLinkElement is inserted into the document | |
1664 // But those add a dynamic component to the favicon that has UI | |
1665 // ramifications, and we need to decide what is the Right Thing To Do(tm) | |
1666 Frame* f = frame(); | |
1667 if (f) | |
1668 f->loader()->startIconLoader(); | |
1669 | |
1670 // Resume the animations (or start them) | |
1671 if (f) | |
1672 f->animation()->resumeAnimations(this); | |
1673 | |
1674 dispatchImageLoadEventsNow(); | |
1675 this->dispatchWindowEvent(loadEvent, false, false); | |
1676 if (f) | |
1677 f->loader()->handledOnloadEvents(); | |
1678 #ifdef INSTRUMENT_LAYOUT_SCHEDULING | |
1679 if (!ownerElement()) | |
1680 printf("onload fired at %d\n", elapsedTime()); | |
1681 #endif | |
1682 | |
1683 m_processingLoadEvent = false; | |
1684 | |
1685 // An event handler may have removed the frame | |
1686 if (!frame()) | |
1687 return; | |
1688 | |
1689 // Make sure both the initial layout and reflow happen after the onload | |
1690 // fires. This will improve onload scores, and other browsers do it. | |
1691 // If they wanna cheat, we can too. -dwh | |
1692 | |
1693 if (frame()->loader()->isScheduledLocationChangePending() && elapsedTime() <
cLayoutScheduleThreshold) { | |
1694 // Just bail out. Before or during the onload we were shifted to another
page. | |
1695 // The old i-Bench suite does this. When this happens don't bother paint
ing or laying out. | |
1696 view()->unscheduleRelayout(); | |
1697 return; | |
1698 } | |
1699 | |
1700 frame()->loader()->checkCallImplicitClose(); | |
1701 | |
1702 // Now do our painting/layout, but only if we aren't in a subframe or if we'
re in a subframe | |
1703 // that has been sized already. Otherwise, our view size would be incorrect
, so doing any | |
1704 // layout/painting now would be pointless. | |
1705 if (!ownerElement() || (ownerElement()->renderer() && !ownerElement()->rende
rer()->needsLayout())) { | |
1706 updateRendering(); | |
1707 | |
1708 // Always do a layout after loading if needed. | |
1709 if (view() && renderer() && (!renderer()->firstChild() || renderer()->ne
edsLayout())) | |
1710 view()->layout(); | |
1711 | |
1712 // Paint immediately after the document is ready. We do this to ensure
that any timers set by the | |
1713 // onload don't have a chance to fire before we would have painted. To
avoid over-flushing we only | |
1714 // worry about this for the top-level document. | |
1715 #if !PLATFORM(MAC) | |
1716 // FIXME: This causes a timing issue with the dispatchDidFinishLoad dele
gate callback. | |
1717 // See <rdar://problem/5092361> | |
1718 if (view() && !ownerElement()) | |
1719 view()->update(); | |
1720 #endif | |
1721 } | |
1722 | |
1723 #if PLATFORM(MAC) | |
1724 if (renderer() && AXObjectCache::accessibilityEnabled()) | |
1725 axObjectCache()->postNotificationToElement(renderer(), "AXLoadComplete")
; | |
1726 #endif | |
1727 | |
1728 #if ENABLE(SVG) | |
1729 // FIXME: Officially, time 0 is when the outermost <svg> receives its | |
1730 // SVGLoad event, but we don't implement those yet. This is close enough | |
1731 // for now. In some cases we should have fired earlier. | |
1732 if (svgExtensions()) | |
1733 accessSVGExtensions()->startAnimations(); | |
1734 #endif | |
1735 } | |
1736 | |
1737 void Document::setParsing(bool b) | |
1738 { | |
1739 m_bParsing = b; | |
1740 if (!m_bParsing && view()) | |
1741 view()->scheduleRelayout(); | |
1742 | |
1743 #ifdef INSTRUMENT_LAYOUT_SCHEDULING | |
1744 if (!ownerElement() && !m_bParsing) | |
1745 printf("Parsing finished at %d\n", elapsedTime()); | |
1746 #endif | |
1747 } | |
1748 | |
1749 bool Document::shouldScheduleLayout() | |
1750 { | |
1751 // We can update layout if: | |
1752 // (a) we actually need a layout | |
1753 // (b) our stylesheets are all loaded | |
1754 // (c) we have a <body> | |
1755 return (renderer() && renderer()->needsLayout() && haveStylesheetsLoaded() &
& | |
1756 documentElement() && documentElement()->renderer() && | |
1757 (!documentElement()->hasTagName(htmlTag) || body())); | |
1758 } | |
1759 | |
1760 int Document::minimumLayoutDelay() | |
1761 { | |
1762 if (m_overMinimumLayoutThreshold) | |
1763 return 0; | |
1764 | |
1765 int elapsed = elapsedTime(); | |
1766 m_overMinimumLayoutThreshold = elapsed > cLayoutScheduleThreshold; | |
1767 | |
1768 // We'll want to schedule the timer to fire at the minimum layout threshold. | |
1769 return max(0, cLayoutScheduleThreshold - elapsed); | |
1770 } | |
1771 | |
1772 int Document::elapsedTime() const | |
1773 { | |
1774 return static_cast<int>((currentTime() - m_startTime) * 1000); | |
1775 } | |
1776 | |
1777 void Document::write(const String& text, Document* ownerDocument) | |
1778 { | |
1779 #ifdef INSTRUMENT_LAYOUT_SCHEDULING | |
1780 if (!ownerElement()) | |
1781 printf("Beginning a document.write at %d\n", elapsedTime()); | |
1782 #endif | |
1783 | |
1784 if (!m_tokenizer) { | |
1785 open(ownerDocument); | |
1786 ASSERT(m_tokenizer); | |
1787 if (!m_tokenizer) | |
1788 return; | |
1789 write("<html>", ownerDocument); | |
1790 } | |
1791 m_tokenizer->write(text, false); | |
1792 | |
1793 #ifdef INSTRUMENT_LAYOUT_SCHEDULING | |
1794 if (!ownerElement()) | |
1795 printf("Ending a document.write at %d\n", elapsedTime()); | |
1796 #endif | |
1797 } | |
1798 | |
1799 void Document::writeln(const String& text, Document* ownerDocument) | |
1800 { | |
1801 write(text, ownerDocument); | |
1802 write("\n", ownerDocument); | |
1803 } | |
1804 | |
1805 void Document::finishParsing() | |
1806 { | |
1807 #ifdef INSTRUMENT_LAYOUT_SCHEDULING | |
1808 if (!ownerElement()) | |
1809 printf("Received all data at %d\n", elapsedTime()); | |
1810 #endif | |
1811 | |
1812 // Let the tokenizer go through as much data as it can. There will be three
possible outcomes after | |
1813 // finish() is called: | |
1814 // (1) All remaining data is parsed, document isn't loaded yet | |
1815 // (2) All remaining data is parsed, document is loaded, tokenizer gets dele
ted | |
1816 // (3) Data is still remaining to be parsed. | |
1817 if (m_tokenizer) | |
1818 m_tokenizer->finish(); | |
1819 } | |
1820 | |
1821 void Document::clear() | |
1822 { | |
1823 delete m_tokenizer; | |
1824 m_tokenizer = 0; | |
1825 | |
1826 removeChildren(); | |
1827 | |
1828 m_windowEventListeners.clear(); | |
1829 } | |
1830 | |
1831 void Document::setURL(const KURL& url) | |
1832 { | |
1833 const KURL& newURL = url.isEmpty() ? blankURL() : url; | |
1834 if (newURL == m_url) | |
1835 return; | |
1836 | |
1837 m_url = newURL; | |
1838 m_documentURI = m_url.string(); | |
1839 updateBaseURL(); | |
1840 } | |
1841 | |
1842 void Document::setBaseElementURL(const KURL& baseElementURL) | |
1843 { | |
1844 m_baseElementURL = baseElementURL; | |
1845 updateBaseURL(); | |
1846 } | |
1847 | |
1848 void Document::updateBaseURL() | |
1849 { | |
1850 m_baseURL = m_baseElementURL.isEmpty() ? KURL(documentURI()) : m_baseElement
URL; | |
1851 if (!m_baseURL.isValid()) | |
1852 m_baseURL = KURL(); | |
1853 | |
1854 if (m_elemSheet) | |
1855 m_elemSheet->setHref(m_baseURL.string()); | |
1856 if (m_mappedElementSheet) | |
1857 m_mappedElementSheet->setHref(m_baseURL.string()); | |
1858 } | |
1859 | |
1860 void Document::setCSSStyleSheet(const String& url, const String& charset, const
CachedCSSStyleSheet* sheet) | |
1861 { | |
1862 m_sheet = CSSStyleSheet::create(this, url, charset); | |
1863 m_sheet->parseString(sheet->sheetText()); | |
1864 | |
1865 updateStyleSelector(); | |
1866 } | |
1867 | |
1868 #if FRAME_LOADS_USER_STYLESHEET | |
1869 void Document::setUserStyleSheet(const String& sheet) | |
1870 { | |
1871 if (m_usersheet != sheet) { | |
1872 m_usersheet = sheet; | |
1873 updateStyleSelector(); | |
1874 } | |
1875 } | |
1876 #endif | |
1877 | |
1878 String Document::userStyleSheet() const | |
1879 { | |
1880 #if FRAME_LOADS_USER_STYLESHEET | |
1881 return m_usersheet; | |
1882 #else | |
1883 Page* page = this->page(); | |
1884 if (!page) | |
1885 return String(); | |
1886 return page->userStyleSheet(); | |
1887 #endif | |
1888 } | |
1889 | |
1890 CSSStyleSheet* Document::elementSheet() | |
1891 { | |
1892 if (!m_elemSheet) | |
1893 m_elemSheet = CSSStyleSheet::create(this, m_baseURL.string()); | |
1894 return m_elemSheet.get(); | |
1895 } | |
1896 | |
1897 CSSStyleSheet* Document::mappedElementSheet() | |
1898 { | |
1899 if (!m_mappedElementSheet) | |
1900 m_mappedElementSheet = CSSStyleSheet::create(this, m_baseURL.string()); | |
1901 return m_mappedElementSheet.get(); | |
1902 } | |
1903 | |
1904 static Node* nextNodeWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent*
event) | |
1905 { | |
1906 // Search is inclusive of start | |
1907 for (Node* n = start; n; n = n->traverseNextNode()) | |
1908 if (n->isKeyboardFocusable(event) && n->tabIndex() == tabIndex) | |
1909 return n; | |
1910 | |
1911 return 0; | |
1912 } | |
1913 | |
1914 static Node* previousNodeWithExactTabIndex(Node* start, int tabIndex, KeyboardEv
ent* event) | |
1915 { | |
1916 // Search is inclusive of start | |
1917 for (Node* n = start; n; n = n->traversePreviousNode()) | |
1918 if (n->isKeyboardFocusable(event) && n->tabIndex() == tabIndex) | |
1919 return n; | |
1920 | |
1921 return 0; | |
1922 } | |
1923 | |
1924 static Node* nextNodeWithGreaterTabIndex(Node* start, int tabIndex, KeyboardEven
t* event) | |
1925 { | |
1926 // Search is inclusive of start | |
1927 int winningTabIndex = SHRT_MAX + 1; | |
1928 Node* winner = 0; | |
1929 for (Node* n = start; n; n = n->traverseNextNode()) | |
1930 if (n->isKeyboardFocusable(event) && n->tabIndex() > tabIndex && n->tabI
ndex() < winningTabIndex) { | |
1931 winner = n; | |
1932 winningTabIndex = n->tabIndex(); | |
1933 } | |
1934 | |
1935 return winner; | |
1936 } | |
1937 | |
1938 static Node* previousNodeWithLowerTabIndex(Node* start, int tabIndex, KeyboardEv
ent* event) | |
1939 { | |
1940 // Search is inclusive of start | |
1941 int winningTabIndex = 0; | |
1942 Node* winner = 0; | |
1943 for (Node* n = start; n; n = n->traversePreviousNode()) | |
1944 if (n->isKeyboardFocusable(event) && n->tabIndex() < tabIndex && n->tabI
ndex() > winningTabIndex) { | |
1945 winner = n; | |
1946 winningTabIndex = n->tabIndex(); | |
1947 } | |
1948 | |
1949 return winner; | |
1950 } | |
1951 | |
1952 Node* Document::nextFocusableNode(Node* start, KeyboardEvent* event) | |
1953 { | |
1954 if (start) { | |
1955 // If a node is excluded from the normal tabbing cycle, the next focusab
le node is determined by tree order | |
1956 if (start->tabIndex() < 0) { | |
1957 for (Node* n = start->traverseNextNode(); n; n = n->traverseNextNode
()) | |
1958 if (n->isKeyboardFocusable(event) && n->tabIndex() >= 0) | |
1959 return n; | |
1960 } | |
1961 | |
1962 // First try to find a node with the same tabindex as start that comes a
fter start in the document. | |
1963 if (Node* winner = nextNodeWithExactTabIndex(start->traverseNextNode(),
start->tabIndex(), event)) | |
1964 return winner; | |
1965 | |
1966 if (start->tabIndex() == 0) | |
1967 // We've reached the last node in the document with a tabindex of 0.
This is the end of the tabbing order. | |
1968 return 0; | |
1969 } | |
1970 | |
1971 // Look for the first node in the document that: | |
1972 // 1) has the lowest tabindex that is higher than start's tabindex (or 0, if
start is null), and | |
1973 // 2) comes first in the document, if there's a tie. | |
1974 if (Node* winner = nextNodeWithGreaterTabIndex(this, start ? start->tabIndex
() : 0, event)) | |
1975 return winner; | |
1976 | |
1977 // There are no nodes with a tabindex greater than start's tabindex, | |
1978 // so find the first node with a tabindex of 0. | |
1979 return nextNodeWithExactTabIndex(this, 0, event); | |
1980 } | |
1981 | |
1982 Node* Document::previousFocusableNode(Node* start, KeyboardEvent* event) | |
1983 { | |
1984 Node* last; | |
1985 for (last = this; last->lastChild(); last = last->lastChild()) | |
1986 ; // Empty loop. | |
1987 | |
1988 // First try to find the last node in the document that comes before start a
nd has the same tabindex as start. | |
1989 // If start is null, find the last node in the document with a tabindex of 0
. | |
1990 Node* startingNode; | |
1991 int startingTabIndex; | |
1992 if (start) { | |
1993 startingNode = start->traversePreviousNode(); | |
1994 startingTabIndex = start->tabIndex(); | |
1995 } else { | |
1996 startingNode = last; | |
1997 startingTabIndex = 0; | |
1998 } | |
1999 | |
2000 // However, if a node is excluded from the normal tabbing cycle, the previou
s focusable node is determined by tree order | |
2001 if (startingTabIndex < 0) { | |
2002 for (Node* n = startingNode; n; n = n->traversePreviousNode()) | |
2003 if (n->isKeyboardFocusable(event) && n->tabIndex() >= 0) | |
2004 return n; | |
2005 } | |
2006 | |
2007 if (Node* winner = previousNodeWithExactTabIndex(startingNode, startingTabIn
dex, event)) | |
2008 return winner; | |
2009 | |
2010 // There are no nodes before start with the same tabindex as start, so look
for a node that: | |
2011 // 1) has the highest non-zero tabindex (that is less than start's tabindex)
, and | |
2012 // 2) comes last in the document, if there's a tie. | |
2013 startingTabIndex = (start && start->tabIndex()) ? start->tabIndex() : SHRT_M
AX; | |
2014 return previousNodeWithLowerTabIndex(last, startingTabIndex, event); | |
2015 } | |
2016 | |
2017 int Document::nodeAbsIndex(Node *node) | |
2018 { | |
2019 ASSERT(node->document() == this); | |
2020 | |
2021 int absIndex = 0; | |
2022 for (Node *n = node; n && n != this; n = n->traversePreviousNode()) | |
2023 absIndex++; | |
2024 return absIndex; | |
2025 } | |
2026 | |
2027 Node *Document::nodeWithAbsIndex(int absIndex) | |
2028 { | |
2029 Node *n = this; | |
2030 for (int i = 0; n && (i < absIndex); i++) { | |
2031 n = n->traverseNextNode(); | |
2032 } | |
2033 return n; | |
2034 } | |
2035 | |
2036 void Document::processHttpEquiv(const String &equiv, const String &content) | |
2037 { | |
2038 ASSERT(!equiv.isNull() && !content.isNull()); | |
2039 | |
2040 Frame *frame = this->frame(); | |
2041 | |
2042 if (equalIgnoringCase(equiv, "default-style")) { | |
2043 // The preferred style set has been overridden as per section | |
2044 // 14.3.2 of the HTML4.0 specification. We need to update the | |
2045 // sheet used variable and then update our style selector. | |
2046 // For more info, see the test at: | |
2047 // http://www.hixie.ch/tests/evil/css/import/main/preferred.html | |
2048 // -dwh | |
2049 m_selectedStylesheetSet = content; | |
2050 m_preferredStylesheetSet = content; | |
2051 updateStyleSelector(); | |
2052 } else if (equalIgnoringCase(equiv, "refresh")) { | |
2053 double delay; | |
2054 String url; | |
2055 if (frame && parseHTTPRefresh(content, true, delay, url)) { | |
2056 if (url.isEmpty()) | |
2057 url = frame->loader()->url().string(); | |
2058 else | |
2059 url = completeURL(url).string(); | |
2060 frame->loader()->scheduleHTTPRedirection(delay, url); | |
2061 } | |
2062 } else if (equalIgnoringCase(equiv, "set-cookie")) { | |
2063 // FIXME: make setCookie work on XML documents too; e.g. in case of <htm
l:meta .....> | |
2064 if (isHTMLDocument()) | |
2065 static_cast<HTMLDocument*>(this)->setCookie(content); | |
2066 } else if (equalIgnoringCase(equiv, "content-language")) | |
2067 setContentLanguage(content); | |
2068 else if (equalIgnoringCase(equiv, "x-dns-prefetch-control")) | |
2069 setDNSPrefetchControl(content); | |
2070 } | |
2071 | |
2072 MouseEventWithHitTestResults Document::prepareMouseEvent(const HitTestRequest& r
equest, const IntPoint& documentPoint, const PlatformMouseEvent& event) | |
2073 { | |
2074 ASSERT(!renderer() || renderer()->isRenderView()); | |
2075 | |
2076 if (!renderer()) | |
2077 return MouseEventWithHitTestResults(event, HitTestResult(IntPoint())); | |
2078 | |
2079 HitTestResult result(documentPoint); | |
2080 renderer()->layer()->hitTest(request, result); | |
2081 | |
2082 if (!request.readonly) | |
2083 updateRendering(); | |
2084 | |
2085 return MouseEventWithHitTestResults(event, result); | |
2086 } | |
2087 | |
2088 // DOM Section 1.1.1 | |
2089 bool Document::childTypeAllowed(NodeType type) | |
2090 { | |
2091 switch (type) { | |
2092 case ATTRIBUTE_NODE: | |
2093 case CDATA_SECTION_NODE: | |
2094 case DOCUMENT_FRAGMENT_NODE: | |
2095 case DOCUMENT_NODE: | |
2096 case ENTITY_NODE: | |
2097 case ENTITY_REFERENCE_NODE: | |
2098 case NOTATION_NODE: | |
2099 case TEXT_NODE: | |
2100 case XPATH_NAMESPACE_NODE: | |
2101 return false; | |
2102 case COMMENT_NODE: | |
2103 case PROCESSING_INSTRUCTION_NODE: | |
2104 return true; | |
2105 case DOCUMENT_TYPE_NODE: | |
2106 case ELEMENT_NODE: | |
2107 // Documents may contain no more than one of each of these. | |
2108 // (One Element and one DocumentType.) | |
2109 for (Node* c = firstChild(); c; c = c->nextSibling()) | |
2110 if (c->nodeType() == type) | |
2111 return false; | |
2112 return true; | |
2113 } | |
2114 return false; | |
2115 } | |
2116 | |
2117 bool Document::canReplaceChild(Node* newChild, Node* oldChild) | |
2118 { | |
2119 if (!oldChild) | |
2120 // ContainerNode::replaceChild will raise a NOT_FOUND_ERR. | |
2121 return true; | |
2122 | |
2123 if (oldChild->nodeType() == newChild->nodeType()) | |
2124 return true; | |
2125 | |
2126 int numDoctypes = 0; | |
2127 int numElements = 0; | |
2128 | |
2129 // First, check how many doctypes and elements we have, not counting | |
2130 // the child we're about to remove. | |
2131 for (Node* c = firstChild(); c; c = c->nextSibling()) { | |
2132 if (c == oldChild) | |
2133 continue; | |
2134 | |
2135 switch (c->nodeType()) { | |
2136 case DOCUMENT_TYPE_NODE: | |
2137 numDoctypes++; | |
2138 break; | |
2139 case ELEMENT_NODE: | |
2140 numElements++; | |
2141 break; | |
2142 default: | |
2143 break; | |
2144 } | |
2145 } | |
2146 | |
2147 // Then, see how many doctypes and elements might be added by the new child. | |
2148 if (newChild->nodeType() == DOCUMENT_FRAGMENT_NODE) { | |
2149 for (Node* c = firstChild(); c; c = c->nextSibling()) { | |
2150 switch (c->nodeType()) { | |
2151 case ATTRIBUTE_NODE: | |
2152 case CDATA_SECTION_NODE: | |
2153 case DOCUMENT_FRAGMENT_NODE: | |
2154 case DOCUMENT_NODE: | |
2155 case ENTITY_NODE: | |
2156 case ENTITY_REFERENCE_NODE: | |
2157 case NOTATION_NODE: | |
2158 case TEXT_NODE: | |
2159 case XPATH_NAMESPACE_NODE: | |
2160 return false; | |
2161 case COMMENT_NODE: | |
2162 case PROCESSING_INSTRUCTION_NODE: | |
2163 break; | |
2164 case DOCUMENT_TYPE_NODE: | |
2165 numDoctypes++; | |
2166 break; | |
2167 case ELEMENT_NODE: | |
2168 numElements++; | |
2169 break; | |
2170 } | |
2171 } | |
2172 } else { | |
2173 switch (newChild->nodeType()) { | |
2174 case ATTRIBUTE_NODE: | |
2175 case CDATA_SECTION_NODE: | |
2176 case DOCUMENT_FRAGMENT_NODE: | |
2177 case DOCUMENT_NODE: | |
2178 case ENTITY_NODE: | |
2179 case ENTITY_REFERENCE_NODE: | |
2180 case NOTATION_NODE: | |
2181 case TEXT_NODE: | |
2182 case XPATH_NAMESPACE_NODE: | |
2183 return false; | |
2184 case COMMENT_NODE: | |
2185 case PROCESSING_INSTRUCTION_NODE: | |
2186 return true; | |
2187 case DOCUMENT_TYPE_NODE: | |
2188 numDoctypes++; | |
2189 break; | |
2190 case ELEMENT_NODE: | |
2191 numElements++; | |
2192 break; | |
2193 } | |
2194 } | |
2195 | |
2196 if (numElements > 1 || numDoctypes > 1) | |
2197 return false; | |
2198 | |
2199 return true; | |
2200 } | |
2201 | |
2202 PassRefPtr<Node> Document::cloneNode(bool /*deep*/) | |
2203 { | |
2204 // Spec says cloning Document nodes is "implementation dependent" | |
2205 // so we do not support it... | |
2206 return 0; | |
2207 } | |
2208 | |
2209 StyleSheetList* Document::styleSheets() | |
2210 { | |
2211 return m_styleSheets.get(); | |
2212 } | |
2213 | |
2214 String Document::preferredStylesheetSet() const | |
2215 { | |
2216 return m_preferredStylesheetSet; | |
2217 } | |
2218 | |
2219 String Document::selectedStylesheetSet() const | |
2220 { | |
2221 return m_selectedStylesheetSet; | |
2222 } | |
2223 | |
2224 void Document::setSelectedStylesheetSet(const String& aString) | |
2225 { | |
2226 m_selectedStylesheetSet = aString; | |
2227 updateStyleSelector(); | |
2228 if (renderer()) | |
2229 renderer()->repaint(); | |
2230 } | |
2231 | |
2232 // This method is called whenever a top-level stylesheet has finished loading. | |
2233 void Document::removePendingSheet() | |
2234 { | |
2235 // Make sure we knew this sheet was pending, and that our count isn't out of
sync. | |
2236 ASSERT(m_pendingStylesheets > 0); | |
2237 | |
2238 m_pendingStylesheets--; | |
2239 | |
2240 #ifdef INSTRUMENT_LAYOUT_SCHEDULING | |
2241 if (!ownerElement()) | |
2242 printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", e
lapsedTime(), m_pendingStylesheets); | |
2243 #endif | |
2244 | |
2245 updateStyleSelector(); | |
2246 | |
2247 if (!m_pendingStylesheets && m_tokenizer) | |
2248 m_tokenizer->executeScriptsWaitingForStylesheets(); | |
2249 | |
2250 if (!m_pendingStylesheets && m_gotoAnchorNeededAfterStylesheetsLoad && m_fra
me) | |
2251 m_frame->loader()->gotoAnchor(); | |
2252 } | |
2253 | |
2254 void Document::updateStyleSelector() | |
2255 { | |
2256 // Don't bother updating, since we haven't loaded all our style info yet | |
2257 // and haven't calculated the style selector for the first time. | |
2258 if (!m_didCalculateStyleSelector && !haveStylesheetsLoaded()) | |
2259 return; | |
2260 | |
2261 if (didLayoutWithPendingStylesheets() && m_pendingStylesheets <= 0) { | |
2262 m_pendingSheetLayout = IgnoreLayoutWithPendingSheets; | |
2263 if (renderer()) | |
2264 renderer()->repaint(); | |
2265 } | |
2266 | |
2267 #ifdef INSTRUMENT_LAYOUT_SCHEDULING | |
2268 if (!ownerElement()) | |
2269 printf("Beginning update of style selector at time %d.\n", elapsedTime()
); | |
2270 #endif | |
2271 | |
2272 recalcStyleSelector(); | |
2273 recalcStyle(Force); | |
2274 | |
2275 #ifdef INSTRUMENT_LAYOUT_SCHEDULING | |
2276 if (!ownerElement()) | |
2277 printf("Finished update of style selector at time %d\n", elapsedTime()); | |
2278 #endif | |
2279 | |
2280 if (renderer()) { | |
2281 renderer()->setNeedsLayoutAndPrefWidthsRecalc(); | |
2282 if (view()) | |
2283 view()->scheduleRelayout(); | |
2284 } | |
2285 } | |
2286 | |
2287 void Document::addStyleSheetCandidateNode(Node* node, bool createdByParser) | |
2288 { | |
2289 // Until the <body> exists, we have no choice but to compare document positi
ons, | |
2290 // since styles outside of the body and head continue to be shunted into the
head | |
2291 // (and thus can shift to end up before dynamically added DOM content that i
s also | |
2292 // outside the body). | |
2293 if ((createdByParser && body()) || m_styleSheetCandidateNodes.isEmpty()) { | |
2294 m_styleSheetCandidateNodes.add(node); | |
2295 return; | |
2296 } | |
2297 | |
2298 // Determine an appropriate insertion point. | |
2299 ListHashSet<Node*>::iterator begin = m_styleSheetCandidateNodes.begin(); | |
2300 ListHashSet<Node*>::iterator end = m_styleSheetCandidateNodes.end(); | |
2301 ListHashSet<Node*>::iterator it = end; | |
2302 Node* followingNode = 0; | |
2303 do { | |
2304 --it; | |
2305 Node* n = *it; | |
2306 unsigned short position = n->compareDocumentPosition(node); | |
2307 if (position == DOCUMENT_POSITION_FOLLOWING) { | |
2308 m_styleSheetCandidateNodes.insertBefore(followingNode, node); | |
2309 return; | |
2310 } | |
2311 followingNode = n; | |
2312 } while (it != begin); | |
2313 | |
2314 m_styleSheetCandidateNodes.insertBefore(followingNode, node); | |
2315 } | |
2316 | |
2317 void Document::removeStyleSheetCandidateNode(Node* node) | |
2318 { | |
2319 m_styleSheetCandidateNodes.remove(node); | |
2320 } | |
2321 | |
2322 void Document::recalcStyleSelector() | |
2323 { | |
2324 if (!renderer() || !attached()) | |
2325 return; | |
2326 | |
2327 StyleSheetVector sheets; | |
2328 | |
2329 bool matchAuthorAndUserStyles = true; | |
2330 if (Settings* settings = this->settings()) | |
2331 matchAuthorAndUserStyles = settings->authorAndUserStylesEnabled(); | |
2332 | |
2333 ListHashSet<Node*>::iterator begin = m_styleSheetCandidateNodes.begin(); | |
2334 ListHashSet<Node*>::iterator end = m_styleSheetCandidateNodes.end(); | |
2335 if (!matchAuthorAndUserStyles) | |
2336 end = begin; | |
2337 for (ListHashSet<Node*>::iterator it = begin; it != end; ++it) { | |
2338 Node* n = *it; | |
2339 | |
2340 StyleSheet* sheet = 0; | |
2341 | |
2342 if (n->nodeType() == PROCESSING_INSTRUCTION_NODE) { | |
2343 // Processing instruction (XML documents only) | |
2344 ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(n); | |
2345 sheet = pi->sheet(); | |
2346 #if ENABLE(XSLT) | |
2347 // Don't apply XSL transforms to already transformed documents -- <r
dar://problem/4132806> | |
2348 if (pi->isXSL() && !transformSourceDocument()) { | |
2349 // Don't apply XSL transforms until loading is finished. | |
2350 if (!parsing()) | |
2351 applyXSLTransform(pi); | |
2352 return; | |
2353 } | |
2354 #endif | |
2355 if (!sheet && !pi->localHref().isEmpty()) { | |
2356 // Processing instruction with reference to an element in this d
ocument - e.g. | |
2357 // <?xml-stylesheet href="#mystyle">, with the element | |
2358 // <foo id="mystyle">heading { color: red; }</foo> at some locat
ion in | |
2359 // the document | |
2360 Element* elem = getElementById(pi->localHref().impl()); | |
2361 if (elem) { | |
2362 String sheetText(""); | |
2363 for (Node* c = elem->firstChild(); c; c = c->nextSibling())
{ | |
2364 if (c->nodeType() == TEXT_NODE || c->nodeType() == CDATA
_SECTION_NODE) | |
2365 sheetText += c->nodeValue(); | |
2366 } | |
2367 | |
2368 RefPtr<CSSStyleSheet> cssSheet = CSSStyleSheet::create(this)
; | |
2369 cssSheet->parseString(sheetText); | |
2370 pi->setCSSStyleSheet(cssSheet); | |
2371 sheet = cssSheet.get(); | |
2372 } | |
2373 } | |
2374 } else if (n->isHTMLElement() && (n->hasTagName(linkTag) || n->hasTagNam
e(styleTag)) | |
2375 #if ENABLE(SVG) | |
2376 || (n->isSVGElement() && n->hasTagName(SVGNames::styleTag)) | |
2377 #endif | |
2378 ) { | |
2379 Element* e = static_cast<Element*>(n); | |
2380 AtomicString title = e->getAttribute(titleAttr); | |
2381 bool enabledViaScript = false; | |
2382 if (e->hasLocalName(linkTag)) { | |
2383 // <LINK> element | |
2384 HTMLLinkElement* l = static_cast<HTMLLinkElement*>(n); | |
2385 if (l->isDisabled()) | |
2386 continue; | |
2387 enabledViaScript = l->isEnabledViaScript(); | |
2388 if (l->isLoading()) { | |
2389 // it is loading but we should still decide which style shee
t set to use | |
2390 if (!enabledViaScript && !title.isEmpty() && m_preferredStyl
esheetSet.isEmpty()) { | |
2391 const AtomicString& rel = e->getAttribute(relAttr); | |
2392 if (!rel.contains("alternate")) { | |
2393 m_preferredStylesheetSet = title; | |
2394 m_selectedStylesheetSet = title; | |
2395 } | |
2396 } | |
2397 continue; | |
2398 } | |
2399 if (!l->sheet()) | |
2400 title = nullAtom; | |
2401 } | |
2402 | |
2403 // Get the current preferred styleset. This is the | |
2404 // set of sheets that will be enabled. | |
2405 #if ENABLE(SVG) | |
2406 if (n->isSVGElement() && n->hasTagName(SVGNames::styleTag)) | |
2407 sheet = static_cast<SVGStyleElement*>(n)->sheet(); | |
2408 else | |
2409 #endif | |
2410 if (e->hasLocalName(linkTag)) | |
2411 sheet = static_cast<HTMLLinkElement*>(n)->sheet(); | |
2412 else | |
2413 // <STYLE> element | |
2414 sheet = static_cast<HTMLStyleElement*>(n)->sheet(); | |
2415 | |
2416 // Check to see if this sheet belongs to a styleset | |
2417 // (thus making it PREFERRED or ALTERNATE rather than | |
2418 // PERSISTENT). | |
2419 if (!enabledViaScript && !title.isEmpty()) { | |
2420 // Yes, we have a title. | |
2421 if (m_preferredStylesheetSet.isEmpty()) { | |
2422 // No preferred set has been established. If | |
2423 // we are NOT an alternate sheet, then establish | |
2424 // us as the preferred set. Otherwise, just ignore | |
2425 // this sheet. | |
2426 AtomicString rel = e->getAttribute(relAttr); | |
2427 if (e->hasLocalName(styleTag) || !rel.contains("alternate")) | |
2428 m_preferredStylesheetSet = m_selectedStylesheetSet = tit
le; | |
2429 } | |
2430 | |
2431 if (title != m_preferredStylesheetSet) | |
2432 sheet = 0; | |
2433 } | |
2434 } | |
2435 | |
2436 if (sheet) | |
2437 sheets.append(sheet); | |
2438 } | |
2439 | |
2440 m_styleSheets->swap(sheets); | |
2441 | |
2442 // Create a new style selector | |
2443 delete m_styleSelector; | |
2444 m_styleSelector = new CSSStyleSelector(this, userStyleSheet(), m_styleSheets
.get(), m_mappedElementSheet.get(), !inCompatMode(), matchAuthorAndUserStyles); | |
2445 m_didCalculateStyleSelector = true; | |
2446 } | |
2447 | |
2448 void Document::setHoverNode(PassRefPtr<Node> newHoverNode) | |
2449 { | |
2450 m_hoverNode = newHoverNode; | |
2451 } | |
2452 | |
2453 void Document::setActiveNode(PassRefPtr<Node> newActiveNode) | |
2454 { | |
2455 m_activeNode = newActiveNode; | |
2456 } | |
2457 | |
2458 void Document::focusedNodeRemoved() | |
2459 { | |
2460 setFocusedNode(0); | |
2461 } | |
2462 | |
2463 void Document::removeFocusedNodeOfSubtree(Node* node, bool amongChildrenOnly) | |
2464 { | |
2465 if (!m_focusedNode || this->inPageCache()) // If the document is in the page
cache, then we don't need to clear out the focused node. | |
2466 return; | |
2467 | |
2468 bool nodeInSubtree = false; | |
2469 if (amongChildrenOnly) | |
2470 nodeInSubtree = m_focusedNode->isDescendantOf(node); | |
2471 else | |
2472 nodeInSubtree = (m_focusedNode == node) || m_focusedNode->isDescendantOf
(node); | |
2473 | |
2474 if (nodeInSubtree) | |
2475 document()->focusedNodeRemoved(); | |
2476 } | |
2477 | |
2478 void Document::hoveredNodeDetached(Node* node) | |
2479 { | |
2480 if (!m_hoverNode || (node != m_hoverNode && (!m_hoverNode->isTextNode() || n
ode != m_hoverNode->parent()))) | |
2481 return; | |
2482 | |
2483 m_hoverNode = node->parent(); | |
2484 while (m_hoverNode && !m_hoverNode->renderer()) | |
2485 m_hoverNode = m_hoverNode->parent(); | |
2486 if (frame()) | |
2487 frame()->eventHandler()->scheduleHoverStateUpdate(); | |
2488 } | |
2489 | |
2490 void Document::activeChainNodeDetached(Node* node) | |
2491 { | |
2492 if (!m_activeNode || (node != m_activeNode && (!m_activeNode->isTextNode() |
| node != m_activeNode->parent()))) | |
2493 return; | |
2494 | |
2495 m_activeNode = node->parent(); | |
2496 while (m_activeNode && !m_activeNode->renderer()) | |
2497 m_activeNode = m_activeNode->parent(); | |
2498 } | |
2499 | |
2500 #if ENABLE(DASHBOARD_SUPPORT) | |
2501 const Vector<DashboardRegionValue>& Document::dashboardRegions() const | |
2502 { | |
2503 return m_dashboardRegions; | |
2504 } | |
2505 | |
2506 void Document::setDashboardRegions(const Vector<DashboardRegionValue>& regions) | |
2507 { | |
2508 m_dashboardRegions = regions; | |
2509 setDashboardRegionsDirty(false); | |
2510 } | |
2511 #endif | |
2512 | |
2513 bool Document::setFocusedNode(PassRefPtr<Node> newFocusedNode) | |
2514 { | |
2515 // Make sure newFocusedNode is actually in this document | |
2516 if (newFocusedNode && (newFocusedNode->document() != this)) | |
2517 return true; | |
2518 | |
2519 if (m_focusedNode == newFocusedNode) | |
2520 return true; | |
2521 | |
2522 if (m_inPageCache) | |
2523 return false; | |
2524 | |
2525 bool focusChangeBlocked = false; | |
2526 RefPtr<Node> oldFocusedNode = m_focusedNode; | |
2527 m_focusedNode = 0; | |
2528 | |
2529 // Remove focus from the existing focus node (if any) | |
2530 if (oldFocusedNode && !oldFocusedNode->m_inDetach) { | |
2531 if (oldFocusedNode->active()) | |
2532 oldFocusedNode->setActive(false); | |
2533 | |
2534 oldFocusedNode->setFocus(false); | |
2535 | |
2536 // Dispatch a change event for text fields or textareas that have been e
dited | |
2537 RenderObject *r = static_cast<RenderObject*>(oldFocusedNode.get()->rende
rer()); | |
2538 if (r && (r->isTextArea() || r->isTextField()) && r->isEdited()) { | |
2539 EventTargetNodeCast(oldFocusedNode.get())->dispatchHTMLEvent(changeE
vent, true, false); | |
2540 if ((r = static_cast<RenderObject*>(oldFocusedNode.get()->renderer()
))) | |
2541 r->setEdited(false); | |
2542 } | |
2543 | |
2544 // Dispatch the blur event and let the node do any other blur related ac
tivities (important for text fields) | |
2545 EventTargetNodeCast(oldFocusedNode.get())->dispatchBlurEvent(); | |
2546 | |
2547 if (m_focusedNode) { | |
2548 // handler shifted focus | |
2549 focusChangeBlocked = true; | |
2550 newFocusedNode = 0; | |
2551 } | |
2552 EventTargetNodeCast(oldFocusedNode.get())->dispatchUIEvent(DOMFocusOutEv
ent); | |
2553 if (m_focusedNode) { | |
2554 // handler shifted focus | |
2555 focusChangeBlocked = true; | |
2556 newFocusedNode = 0; | |
2557 } | |
2558 if ((oldFocusedNode.get() == this) && oldFocusedNode->hasOneRef()) | |
2559 return true; | |
2560 | |
2561 if (oldFocusedNode.get() == oldFocusedNode->rootEditableElement()) | |
2562 frame()->editor()->didEndEditing(); | |
2563 } | |
2564 | |
2565 if (newFocusedNode) { | |
2566 if (newFocusedNode == newFocusedNode->rootEditableElement() && !acceptsE
ditingFocus(newFocusedNode.get())) { | |
2567 // delegate blocks focus change | |
2568 focusChangeBlocked = true; | |
2569 goto SetFocusedNodeDone; | |
2570 } | |
2571 // Set focus on the new node | |
2572 m_focusedNode = newFocusedNode.get(); | |
2573 | |
2574 // Dispatch the focus event and let the node do any other focus related
activities (important for text fields) | |
2575 EventTargetNodeCast(m_focusedNode.get())->dispatchFocusEvent(); | |
2576 | |
2577 if (m_focusedNode != newFocusedNode) { | |
2578 // handler shifted focus | |
2579 focusChangeBlocked = true; | |
2580 goto SetFocusedNodeDone; | |
2581 } | |
2582 EventTargetNodeCast(m_focusedNode.get())->dispatchUIEvent(DOMFocusInEven
t); | |
2583 if (m_focusedNode != newFocusedNode) { | |
2584 // handler shifted focus | |
2585 focusChangeBlocked = true; | |
2586 goto SetFocusedNodeDone; | |
2587 } | |
2588 m_focusedNode->setFocus(); | |
2589 | |
2590 if (m_focusedNode.get() == m_focusedNode->rootEditableElement()) | |
2591 frame()->editor()->didBeginEditing(); | |
2592 | |
2593 // eww, I suck. set the qt focus correctly | |
2594 // ### find a better place in the code for this | |
2595 if (view()) { | |
2596 Widget *focusWidget = widgetForNode(m_focusedNode.get()); | |
2597 if (focusWidget) { | |
2598 // Make sure a widget has the right size before giving it focus. | |
2599 // Otherwise, we are testing edge cases of the Widget code. | |
2600 // Specifically, in WebCore this does not work well for text fie
lds. | |
2601 updateLayout(); | |
2602 // Re-get the widget in case updating the layout changed things. | |
2603 focusWidget = widgetForNode(m_focusedNode.get()); | |
2604 } | |
2605 if (focusWidget) | |
2606 focusWidget->setFocus(); | |
2607 else | |
2608 view()->setFocus(); | |
2609 } | |
2610 } | |
2611 | |
2612 #if PLATFORM(MAC) && !PLATFORM(CHROMIUM) | |
2613 if (!focusChangeBlocked && m_focusedNode && AXObjectCache::accessibilityEnab
led()) | |
2614 axObjectCache()->handleFocusedUIElementChanged(); | |
2615 #endif | |
2616 | |
2617 SetFocusedNodeDone: | |
2618 updateRendering(); | |
2619 return !focusChangeBlocked; | |
2620 } | |
2621 | |
2622 void Document::setCSSTarget(Node* n) | |
2623 { | |
2624 if (m_cssTarget) | |
2625 m_cssTarget->setChanged(); | |
2626 m_cssTarget = n; | |
2627 if (n) | |
2628 n->setChanged(); | |
2629 } | |
2630 | |
2631 Node* Document::getCSSTarget() const | |
2632 { | |
2633 return m_cssTarget; | |
2634 } | |
2635 | |
2636 void Document::attachNodeIterator(NodeIterator *ni) | |
2637 { | |
2638 m_nodeIterators.add(ni); | |
2639 } | |
2640 | |
2641 void Document::detachNodeIterator(NodeIterator *ni) | |
2642 { | |
2643 m_nodeIterators.remove(ni); | |
2644 } | |
2645 | |
2646 void Document::nodeChildrenChanged(ContainerNode* container) | |
2647 { | |
2648 if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail())
{ | |
2649 HashSet<Range*>::const_iterator end = m_ranges.end(); | |
2650 for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; +
+it) | |
2651 (*it)->nodeChildrenChanged(container); | |
2652 } | |
2653 } | |
2654 | |
2655 void Document::nodeWillBeRemoved(Node* n) | |
2656 { | |
2657 HashSet<NodeIterator*>::const_iterator nodeIteratorsEnd = m_nodeIterators.en
d(); | |
2658 for (HashSet<NodeIterator*>::const_iterator it = m_nodeIterators.begin(); it
!= nodeIteratorsEnd; ++it) | |
2659 (*it)->nodeWillBeRemoved(n); | |
2660 | |
2661 if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail())
{ | |
2662 HashSet<Range*>::const_iterator rangesEnd = m_ranges.end(); | |
2663 for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != ranges
End; ++it) | |
2664 (*it)->nodeWillBeRemoved(n); | |
2665 } | |
2666 | |
2667 if (Frame* frame = this->frame()) { | |
2668 frame->selection()->nodeWillBeRemoved(n); | |
2669 frame->dragCaretController()->nodeWillBeRemoved(n); | |
2670 } | |
2671 } | |
2672 | |
2673 void Document::textInserted(Node* text, unsigned offset, unsigned length) | |
2674 { | |
2675 if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail())
{ | |
2676 HashSet<Range*>::const_iterator end = m_ranges.end(); | |
2677 for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; +
+it) | |
2678 (*it)->textInserted(text, offset, length); | |
2679 } | |
2680 | |
2681 // Update the markers for spelling and grammar checking. | |
2682 shiftMarkers(text, offset, length); | |
2683 } | |
2684 | |
2685 void Document::textRemoved(Node* text, unsigned offset, unsigned length) | |
2686 { | |
2687 if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail())
{ | |
2688 HashSet<Range*>::const_iterator end = m_ranges.end(); | |
2689 for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; +
+it) | |
2690 (*it)->textRemoved(text, offset, length); | |
2691 } | |
2692 | |
2693 // Update the markers for spelling and grammar checking. | |
2694 removeMarkers(text, offset, length); | |
2695 shiftMarkers(text, offset + length, 0 - length); | |
2696 } | |
2697 | |
2698 void Document::textNodesMerged(Text* oldNode, unsigned offset) | |
2699 { | |
2700 if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail())
{ | |
2701 NodeWithIndex oldNodeWithIndex(oldNode); | |
2702 HashSet<Range*>::const_iterator end = m_ranges.end(); | |
2703 for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; +
+it) | |
2704 (*it)->textNodesMerged(oldNodeWithIndex, offset); | |
2705 } | |
2706 | |
2707 // FIXME: This should update markers for spelling and grammar checking. | |
2708 } | |
2709 | |
2710 void Document::textNodeSplit(Text* oldNode) | |
2711 { | |
2712 if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail())
{ | |
2713 HashSet<Range*>::const_iterator end = m_ranges.end(); | |
2714 for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; +
+it) | |
2715 (*it)->textNodeSplit(oldNode); | |
2716 } | |
2717 | |
2718 // FIXME: This should update markers for spelling and grammar checking. | |
2719 } | |
2720 | |
2721 // FIXME: eventually, this should return a DOMWindow stored in the document. | |
2722 DOMWindow* Document::domWindow() const | |
2723 { | |
2724 if (!frame()) | |
2725 return 0; | |
2726 return frame()->domWindow(); | |
2727 } | |
2728 | |
2729 PassRefPtr<Event> Document::createEvent(const String& eventType, ExceptionCode&
ec) | |
2730 { | |
2731 if (eventType == "UIEvents" || eventType == "UIEvent") | |
2732 return UIEvent::create(); | |
2733 if (eventType == "MouseEvents" || eventType == "MouseEvent") | |
2734 return MouseEvent::create(); | |
2735 if (eventType == "MutationEvents" || eventType == "MutationEvent") | |
2736 return MutationEvent::create(); | |
2737 if (eventType == "KeyboardEvents" || eventType == "KeyboardEvent") | |
2738 return KeyboardEvent::create(); | |
2739 if (eventType == "HTMLEvents" || eventType == "Event" || eventType == "Event
s") | |
2740 return Event::create(); | |
2741 if (eventType == "ProgressEvent") | |
2742 return ProgressEvent::create(); | |
2743 if (eventType == "TextEvent") | |
2744 return TextEvent::create(); | |
2745 if (eventType == "OverflowEvent") | |
2746 return OverflowEvent::create(); | |
2747 if (eventType == "WheelEvent") | |
2748 return WheelEvent::create(); | |
2749 #if ENABLE(SVG) | |
2750 if (eventType == "SVGEvents") | |
2751 return Event::create(); | |
2752 if (eventType == "SVGZoomEvents") | |
2753 return SVGZoomEvent::create(); | |
2754 #endif | |
2755 if (eventType == "MessageEvent") | |
2756 return MessageEvent::create(); | |
2757 ec = NOT_SUPPORTED_ERR; | |
2758 return 0; | |
2759 } | |
2760 | |
2761 CSSStyleDeclaration* Document::getOverrideStyle(Element*, const String&) | |
2762 { | |
2763 return 0; | |
2764 } | |
2765 | |
2766 void Document::handleWindowEvent(Event *evt, bool useCapture) | |
2767 { | |
2768 if (m_windowEventListeners.isEmpty()) | |
2769 return; | |
2770 | |
2771 // if any html event listeners are registered on the window, then dispatch t
hem here | |
2772 RegisteredEventListenerList listenersCopy = m_windowEventListeners; | |
2773 RegisteredEventListenerList::iterator it = listenersCopy.begin(); | |
2774 | |
2775 for (; it != listenersCopy.end(); ++it) | |
2776 if ((*it)->eventType() == evt->type() && (*it)->useCapture() == useCaptu
re && !(*it)->removed()) | |
2777 (*it)->listener()->handleEvent(evt, true); | |
2778 } | |
2779 | |
2780 void Document::setHTMLWindowEventListener(const AtomicString &eventType, PassRef
Ptr<EventListener> listener) | |
2781 { | |
2782 // If we already have it we don't want removeWindowEventListener to delete i
t | |
2783 removeHTMLWindowEventListener(eventType); | |
2784 if (listener) | |
2785 addWindowEventListener(eventType, listener, false); | |
2786 } | |
2787 | |
2788 EventListener *Document::getHTMLWindowEventListener(const AtomicString& eventTyp
e) | |
2789 { | |
2790 RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); | |
2791 for (; it != m_windowEventListeners.end(); ++it) { | |
2792 if ((*it)->eventType() == eventType && (*it)->listener()->isHTMLEventLis
tener()) | |
2793 return (*it)->listener(); | |
2794 } | |
2795 return 0; | |
2796 } | |
2797 | |
2798 void Document::removeHTMLWindowEventListener(const AtomicString& eventType) | |
2799 { | |
2800 RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); | |
2801 for (; it != m_windowEventListeners.end(); ++it) { | |
2802 if ((*it)->eventType() == eventType && (*it)->listener()->isHTMLEventLis
tener()) { | |
2803 if (eventType == unloadEvent) | |
2804 removePendingFrameUnloadEventCount(); | |
2805 else if (eventType == beforeunloadEvent) | |
2806 removePendingFrameBeforeUnloadEventCount(); | |
2807 m_windowEventListeners.remove(it); | |
2808 return; | |
2809 } | |
2810 } | |
2811 } | |
2812 | |
2813 void Document::addWindowEventListener(const AtomicString &eventType, PassRefPtr<
EventListener> listener, bool useCapture) | |
2814 { | |
2815 if (eventType == unloadEvent) | |
2816 addPendingFrameUnloadEventCount(); | |
2817 else if (eventType == beforeunloadEvent) | |
2818 addPendingFrameBeforeUnloadEventCount(); | |
2819 // Remove existing identical listener set with identical arguments. | |
2820 // The DOM 2 spec says that "duplicate instances are discarded" in this case
. | |
2821 removeWindowEventListener(eventType, listener.get(), useCapture); | |
2822 m_windowEventListeners.append(RegisteredEventListener::create(eventType, lis
tener, useCapture)); | |
2823 } | |
2824 | |
2825 void Document::removeWindowEventListener(const AtomicString& eventType, EventLis
tener* listener, bool useCapture) | |
2826 { | |
2827 RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); | |
2828 for (; it != m_windowEventListeners.end(); ++it) { | |
2829 RegisteredEventListener& r = **it; | |
2830 if (r.eventType() == eventType && r.listener() == listener && r.useCaptu
re() == useCapture) { | |
2831 if (eventType == unloadEvent) | |
2832 removePendingFrameUnloadEventCount(); | |
2833 else if (eventType == beforeunloadEvent) | |
2834 removePendingFrameBeforeUnloadEventCount(); | |
2835 m_windowEventListeners.remove(it); | |
2836 return; | |
2837 } | |
2838 } | |
2839 } | |
2840 | |
2841 bool Document::hasWindowEventListener(const AtomicString &eventType) | |
2842 { | |
2843 RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); | |
2844 for (; it != m_windowEventListeners.end(); ++it) | |
2845 if ((*it)->eventType() == eventType) { | |
2846 return true; | |
2847 } | |
2848 return false; | |
2849 } | |
2850 | |
2851 void Document::addPendingFrameUnloadEventCount() | |
2852 { | |
2853 if (m_frame) | |
2854 m_frame->eventHandler()->addPendingFrameUnloadEventCount(); | |
2855 } | |
2856 | |
2857 void Document::removePendingFrameUnloadEventCount() | |
2858 { | |
2859 if (m_frame) | |
2860 m_frame->eventHandler()->removePendingFrameUnloadEventCount(); | |
2861 } | |
2862 | |
2863 void Document::addPendingFrameBeforeUnloadEventCount() | |
2864 { | |
2865 if (m_frame) | |
2866 m_frame->eventHandler()->addPendingFrameBeforeUnloadEventCount(); | |
2867 } | |
2868 | |
2869 void Document::removePendingFrameBeforeUnloadEventCount() | |
2870 { | |
2871 if (m_frame) | |
2872 m_frame->eventHandler()->removePendingFrameBeforeUnloadEventCount(); | |
2873 } | |
2874 | |
2875 bool Document::hasUnloadEventListener() | |
2876 { | |
2877 return (hasWindowEventListener(unloadEvent) || | |
2878 hasWindowEventListener(beforeunloadEvent)); | |
2879 } | |
2880 | |
2881 PassRefPtr<EventListener> Document::createHTMLEventListener(const String& functi
onName, const String& code, Node *node) | |
2882 { | |
2883 if (Frame* frm = frame()) | |
2884 if (frm->script()->isEnabled()) | |
2885 return frm->script()->createHTMLEventHandler(functionName, code, nod
e); | |
2886 return 0; | |
2887 } | |
2888 | |
2889 void Document::setHTMLWindowEventListener(const AtomicString& eventType, Attribu
te* attr) | |
2890 { | |
2891 setHTMLWindowEventListener(eventType, | |
2892 createHTMLEventListener(attr->localName().string(), attr->value(), 0)); | |
2893 } | |
2894 | |
2895 void Document::dispatchImageLoadEventSoon(HTMLImageLoader *image) | |
2896 { | |
2897 m_imageLoadEventDispatchSoonList.append(image); | |
2898 if (!m_imageLoadEventTimer.isActive()) | |
2899 m_imageLoadEventTimer.startOneShot(0); | |
2900 } | |
2901 | |
2902 void Document::removeImage(HTMLImageLoader* image) | |
2903 { | |
2904 // Remove instances of this image from both lists. | |
2905 // Use loops because we allow multiple instances to get into the lists. | |
2906 while (m_imageLoadEventDispatchSoonList.removeRef(image)) { } | |
2907 while (m_imageLoadEventDispatchingList.removeRef(image)) { } | |
2908 if (m_imageLoadEventDispatchSoonList.isEmpty()) | |
2909 m_imageLoadEventTimer.stop(); | |
2910 } | |
2911 | |
2912 void Document::dispatchImageLoadEventsNow() | |
2913 { | |
2914 // need to avoid re-entering this function; if new dispatches are | |
2915 // scheduled before the parent finishes processing the list, they | |
2916 // will set a timer and eventually be processed | |
2917 if (!m_imageLoadEventDispatchingList.isEmpty()) { | |
2918 return; | |
2919 } | |
2920 | |
2921 m_imageLoadEventTimer.stop(); | |
2922 | |
2923 m_imageLoadEventDispatchingList = m_imageLoadEventDispatchSoonList; | |
2924 m_imageLoadEventDispatchSoonList.clear(); | |
2925 for (DeprecatedPtrListIterator<HTMLImageLoader> it(m_imageLoadEventDispatchi
ngList); it.current();) { | |
2926 HTMLImageLoader* image = it.current(); | |
2927 // Must advance iterator *before* dispatching call. | |
2928 // Otherwise, it might be advanced automatically if dispatching the call
had a side effect | |
2929 // of destroying the current HTMLImageLoader, and then we would advance
past the *next* item, | |
2930 // missing one altogether. | |
2931 ++it; | |
2932 image->dispatchLoadEvent(); | |
2933 } | |
2934 m_imageLoadEventDispatchingList.clear(); | |
2935 } | |
2936 | |
2937 void Document::imageLoadEventTimerFired(Timer<Document>*) | |
2938 { | |
2939 dispatchImageLoadEventsNow(); | |
2940 } | |
2941 | |
2942 Element* Document::ownerElement() const | |
2943 { | |
2944 if (!frame()) | |
2945 return 0; | |
2946 return frame()->ownerElement(); | |
2947 } | |
2948 | |
2949 String Document::cookie() const | |
2950 { | |
2951 return cookies(this, cookieURL()); | |
2952 } | |
2953 | |
2954 void Document::setCookie(const String& value) | |
2955 { | |
2956 setCookies(this, cookieURL(), policyBaseURL(), value); | |
2957 } | |
2958 | |
2959 String Document::referrer() const | |
2960 { | |
2961 if (frame()) | |
2962 return frame()->loader()->referrer(); | |
2963 return String(); | |
2964 } | |
2965 | |
2966 String Document::domain() const | |
2967 { | |
2968 return m_securityOrigin->domain(); | |
2969 } | |
2970 | |
2971 void Document::setDomain(const String& newDomain) | |
2972 { | |
2973 // Both NS and IE specify that changing the domain is only allowed when | |
2974 // the new domain is a suffix of the old domain. | |
2975 | |
2976 // FIXME: We should add logging indicating why a domain was not allowed. | |
2977 | |
2978 // If the new domain is the same as the old domain, still call | |
2979 // m_securityOrigin.setDomainForDOM. This will change the | |
2980 // security check behavior. For example, if a page loaded on port 8000 | |
2981 // assigns its current domain using document.domain, the page will | |
2982 // allow other pages loaded on different ports in the same domain that | |
2983 // have also assigned to access this page. | |
2984 if (equalIgnoringCase(domain(), newDomain)) { | |
2985 m_securityOrigin->setDomainFromDOM(newDomain); | |
2986 return; | |
2987 } | |
2988 | |
2989 int oldLength = domain().length(); | |
2990 int newLength = newDomain.length(); | |
2991 // e.g. newDomain = webkit.org (10) and domain() = www.webkit.org (14) | |
2992 if (newLength >= oldLength) | |
2993 return; | |
2994 | |
2995 String test = domain(); | |
2996 // Check that it's a subdomain, not e.g. "ebkit.org" | |
2997 if (test[oldLength - newLength - 1] != '.') | |
2998 return; | |
2999 | |
3000 // Now test is "webkit.org" from domain() | |
3001 // and we check that it's the same thing as newDomain | |
3002 test.remove(0, oldLength - newLength); | |
3003 if (test != newDomain) | |
3004 return; | |
3005 | |
3006 m_securityOrigin->setDomainFromDOM(newDomain); | |
3007 } | |
3008 | |
3009 String Document::lastModified() const | |
3010 { | |
3011 Frame* f = frame(); | |
3012 if (!f) | |
3013 return String(); | |
3014 DocumentLoader* loader = f->loader()->documentLoader(); | |
3015 if (!loader) | |
3016 return String(); | |
3017 return loader->response().httpHeaderField("Last-Modified"); | |
3018 } | |
3019 | |
3020 static bool isValidNameNonASCII(const UChar* characters, unsigned length) | |
3021 { | |
3022 unsigned i = 0; | |
3023 | |
3024 UChar32 c; | |
3025 U16_NEXT(characters, i, length, c) | |
3026 if (!isValidNameStart(c)) | |
3027 return false; | |
3028 | |
3029 while (i < length) { | |
3030 U16_NEXT(characters, i, length, c) | |
3031 if (!isValidNamePart(c)) | |
3032 return false; | |
3033 } | |
3034 | |
3035 return true; | |
3036 } | |
3037 | |
3038 static inline bool isValidNameASCII(const UChar* characters, unsigned length) | |
3039 { | |
3040 UChar c = characters[0]; | |
3041 if (!(isASCIIAlpha(c) || c == ':' || c == '_')) | |
3042 return false; | |
3043 | |
3044 for (unsigned i = 1; i < length; ++i) { | |
3045 c = characters[i]; | |
3046 if (!(isASCIIAlphanumeric(c) || c == ':' || c == '_' || c == '-' || c ==
'.')) | |
3047 return false; | |
3048 } | |
3049 | |
3050 return true; | |
3051 } | |
3052 | |
3053 bool Document::isValidName(const String& name) | |
3054 { | |
3055 unsigned length = name.length(); | |
3056 if (!length) | |
3057 return false; | |
3058 | |
3059 const UChar* characters = name.characters(); | |
3060 return isValidNameASCII(characters, length) || isValidNameNonASCII(character
s, length); | |
3061 } | |
3062 | |
3063 bool Document::parseQualifiedName(const String& qualifiedName, String& prefix, S
tring& localName, ExceptionCode& ec) | |
3064 { | |
3065 unsigned length = qualifiedName.length(); | |
3066 | |
3067 if (length == 0) { | |
3068 ec = INVALID_CHARACTER_ERR; | |
3069 return false; | |
3070 } | |
3071 | |
3072 bool nameStart = true; | |
3073 bool sawColon = false; | |
3074 int colonPos = 0; | |
3075 | |
3076 const UChar* s = qualifiedName.characters(); | |
3077 for (unsigned i = 0; i < length;) { | |
3078 UChar32 c; | |
3079 U16_NEXT(s, i, length, c) | |
3080 if (c == ':') { | |
3081 if (sawColon) { | |
3082 ec = NAMESPACE_ERR; | |
3083 return false; // multiple colons: not allowed | |
3084 } | |
3085 nameStart = true; | |
3086 sawColon = true; | |
3087 colonPos = i - 1; | |
3088 } else if (nameStart) { | |
3089 if (!isValidNameStart(c)) { | |
3090 ec = INVALID_CHARACTER_ERR; | |
3091 return false; | |
3092 } | |
3093 nameStart = false; | |
3094 } else { | |
3095 if (!isValidNamePart(c)) { | |
3096 ec = INVALID_CHARACTER_ERR; | |
3097 return false; | |
3098 } | |
3099 } | |
3100 } | |
3101 | |
3102 if (!sawColon) { | |
3103 prefix = String(); | |
3104 localName = qualifiedName; | |
3105 } else { | |
3106 prefix = qualifiedName.substring(0, colonPos); | |
3107 if (prefix.isEmpty()) { | |
3108 ec = NAMESPACE_ERR; | |
3109 return false; | |
3110 } | |
3111 localName = qualifiedName.substring(colonPos + 1); | |
3112 } | |
3113 | |
3114 if (localName.isEmpty()) { | |
3115 ec = NAMESPACE_ERR; | |
3116 return false; | |
3117 } | |
3118 | |
3119 return true; | |
3120 } | |
3121 | |
3122 void Document::addImageMap(HTMLMapElement* imageMap) | |
3123 { | |
3124 const AtomicString& name = imageMap->getName(); | |
3125 if (!name.impl()) | |
3126 return; | |
3127 | |
3128 // Add the image map, unless there's already another with that name. | |
3129 // "First map wins" is the rule other browsers seem to implement. | |
3130 m_imageMapsByName.add(name.impl(), imageMap); | |
3131 } | |
3132 | |
3133 void Document::removeImageMap(HTMLMapElement* imageMap) | |
3134 { | |
3135 // Remove the image map by name. | |
3136 // But don't remove some other image map that just happens to have the same
name. | |
3137 // FIXME: Use a HashCountedSet as we do for IDs to find the first remaining
map | |
3138 // once a map has been removed. | |
3139 const AtomicString& name = imageMap->getName(); | |
3140 if (!name.impl()) | |
3141 return; | |
3142 | |
3143 ImageMapsByName::iterator it = m_imageMapsByName.find(name.impl()); | |
3144 if (it != m_imageMapsByName.end() && it->second == imageMap) | |
3145 m_imageMapsByName.remove(it); | |
3146 } | |
3147 | |
3148 HTMLMapElement *Document::getImageMap(const String& url) const | |
3149 { | |
3150 if (url.isNull()) | |
3151 return 0; | |
3152 int hashPos = url.find('#'); | |
3153 String name = (hashPos < 0 ? url : url.substring(hashPos + 1)).impl(); | |
3154 AtomicString mapName = isHTMLDocument() ? name.lower() : name; | |
3155 return m_imageMapsByName.get(mapName.impl()); | |
3156 } | |
3157 | |
3158 void Document::setDecoder(PassRefPtr<TextResourceDecoder> decoder) | |
3159 { | |
3160 m_decoder = decoder; | |
3161 } | |
3162 | |
3163 UChar Document::backslashAsCurrencySymbol() const | |
3164 { | |
3165 if (!m_decoder) | |
3166 return '\\'; | |
3167 return m_decoder->encoding().backslashAsCurrencySymbol(); | |
3168 } | |
3169 | |
3170 KURL Document::completeURL(const String& url) const | |
3171 { | |
3172 // Always return a null URL when passed a null string. | |
3173 // FIXME: Should we change the KURL constructor to have this behavior? | |
3174 if (url.isNull()) | |
3175 return KURL(); | |
3176 if (!m_decoder) | |
3177 return KURL(m_baseURL, url); | |
3178 return KURL(m_baseURL, url, m_decoder->encoding()); | |
3179 } | |
3180 | |
3181 bool Document::inPageCache() | |
3182 { | |
3183 return m_inPageCache; | |
3184 } | |
3185 | |
3186 void Document::setInPageCache(bool flag) | |
3187 { | |
3188 if (m_inPageCache == flag) | |
3189 return; | |
3190 | |
3191 m_inPageCache = flag; | |
3192 if (flag) { | |
3193 ASSERT(m_savedRenderer == 0); | |
3194 m_savedRenderer = renderer(); | |
3195 if (FrameView* v = view()) | |
3196 v->resetScrollbars(); | |
3197 } else { | |
3198 ASSERT(renderer() == 0 || renderer() == m_savedRenderer); | |
3199 ASSERT(m_renderArena); | |
3200 setRenderer(m_savedRenderer); | |
3201 m_savedRenderer = 0; | |
3202 } | |
3203 } | |
3204 | |
3205 void Document::willSaveToCache() | |
3206 { | |
3207 HashSet<Element*>::iterator end = m_pageCacheCallbackElements.end(); | |
3208 for (HashSet<Element*>::iterator i = m_pageCacheCallbackElements.begin(); i
!= end; ++i) | |
3209 (*i)->willSaveToCache(); | |
3210 } | |
3211 | |
3212 void Document::didRestoreFromCache() | |
3213 { | |
3214 HashSet<Element*>::iterator end = m_pageCacheCallbackElements.end(); | |
3215 for (HashSet<Element*>::iterator i = m_pageCacheCallbackElements.begin(); i
!= end; ++i) | |
3216 (*i)->didRestoreFromCache(); | |
3217 } | |
3218 | |
3219 void Document::registerForCacheCallbacks(Element* e) | |
3220 { | |
3221 m_pageCacheCallbackElements.add(e); | |
3222 } | |
3223 | |
3224 void Document::unregisterForCacheCallbacks(Element* e) | |
3225 { | |
3226 m_pageCacheCallbackElements.remove(e); | |
3227 } | |
3228 | |
3229 void Document::setShouldCreateRenderers(bool f) | |
3230 { | |
3231 m_createRenderers = f; | |
3232 } | |
3233 | |
3234 bool Document::shouldCreateRenderers() | |
3235 { | |
3236 return m_createRenderers; | |
3237 } | |
3238 | |
3239 // Support for Javascript execCommand, and related methods | |
3240 | |
3241 static Editor::Command command(Document* document, const String& commandName, bo
ol userInterface = false) | |
3242 { | |
3243 Frame* frame = document->frame(); | |
3244 if (!frame || frame->document() != document) | |
3245 return Editor::Command(); | |
3246 return frame->editor()->command(commandName, | |
3247 userInterface ? CommandFromDOMWithUserInterface : CommandFromDOM); | |
3248 } | |
3249 | |
3250 bool Document::execCommand(const String& commandName, bool userInterface, const
String& value) | |
3251 { | |
3252 return command(this, commandName, userInterface).execute(value); | |
3253 } | |
3254 | |
3255 bool Document::queryCommandEnabled(const String& commandName) | |
3256 { | |
3257 return command(this, commandName).isEnabled(); | |
3258 } | |
3259 | |
3260 bool Document::queryCommandIndeterm(const String& commandName) | |
3261 { | |
3262 return command(this, commandName).state() == MixedTriState; | |
3263 } | |
3264 | |
3265 bool Document::queryCommandState(const String& commandName) | |
3266 { | |
3267 return command(this, commandName).state() != FalseTriState; | |
3268 } | |
3269 | |
3270 bool Document::queryCommandSupported(const String& commandName) | |
3271 { | |
3272 return command(this, commandName).isSupported(); | |
3273 } | |
3274 | |
3275 String Document::queryCommandValue(const String& commandName) | |
3276 { | |
3277 return command(this, commandName).value(); | |
3278 } | |
3279 | |
3280 static IntRect placeholderRectForMarker() | |
3281 { | |
3282 return IntRect(-1, -1, -1, -1); | |
3283 } | |
3284 | |
3285 void Document::addMarker(Range *range, DocumentMarker::MarkerType type, String d
escription) | |
3286 { | |
3287 // Use a TextIterator to visit the potentially multiple nodes the range cove
rs. | |
3288 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance
()) { | |
3289 RefPtr<Range> textPiece = markedText.range(); | |
3290 int exception = 0; | |
3291 DocumentMarker marker = {type, textPiece->startOffset(exception), textPi
ece->endOffset(exception), description}; | |
3292 addMarker(textPiece->startContainer(exception), marker); | |
3293 } | |
3294 } | |
3295 | |
3296 void Document::removeMarkers(Range* range, DocumentMarker::MarkerType markerType
) | |
3297 { | |
3298 if (m_markers.isEmpty()) | |
3299 return; | |
3300 | |
3301 ExceptionCode ec = 0; | |
3302 Node* startContainer = range->startContainer(ec); | |
3303 Node* endContainer = range->endContainer(ec); | |
3304 | |
3305 Node* pastLastNode = range->pastLastNode(); | |
3306 for (Node* node = range->firstNode(); node != pastLastNode; node = node->tra
verseNextNode()) { | |
3307 int startOffset = node == startContainer ? range->startOffset(ec) : 0; | |
3308 int endOffset = node == endContainer ? range->endOffset(ec) : INT_MAX; | |
3309 int length = endOffset - startOffset; | |
3310 removeMarkers(node, startOffset, length, markerType); | |
3311 } | |
3312 } | |
3313 | |
3314 // Markers are stored in order sorted by their start offset. | |
3315 // Markers of the same type do not overlap each other. | |
3316 | |
3317 void Document::addMarker(Node* node, DocumentMarker newMarker) | |
3318 { | |
3319 ASSERT(newMarker.endOffset >= newMarker.startOffset); | |
3320 if (newMarker.endOffset == newMarker.startOffset) | |
3321 return; | |
3322 | |
3323 MarkerMapVectorPair* vectorPair = m_markers.get(node); | |
3324 | |
3325 if (!vectorPair) { | |
3326 vectorPair = new MarkerMapVectorPair; | |
3327 vectorPair->first.append(newMarker); | |
3328 vectorPair->second.append(placeholderRectForMarker()); | |
3329 m_markers.set(node, vectorPair); | |
3330 } else { | |
3331 Vector<DocumentMarker>& markers = vectorPair->first; | |
3332 Vector<IntRect>& rects = vectorPair->second; | |
3333 size_t numMarkers = markers.size(); | |
3334 ASSERT(numMarkers == rects.size()); | |
3335 size_t i; | |
3336 // Iterate over all markers whose start offset is less than or equal to
the new marker's. | |
3337 // If one of them is of the same type as the new marker and touches it o
r intersects with it | |
3338 // (there is at most one), remove it and adjust the new marker's start o
ffset to encompass it. | |
3339 for (i = 0; i < numMarkers; ++i) { | |
3340 DocumentMarker marker = markers[i]; | |
3341 if (marker.startOffset > newMarker.startOffset) | |
3342 break; | |
3343 if (marker.type == newMarker.type && marker.endOffset >= newMarker.s
tartOffset) { | |
3344 newMarker.startOffset = marker.startOffset; | |
3345 markers.remove(i); | |
3346 rects.remove(i); | |
3347 numMarkers--; | |
3348 break; | |
3349 } | |
3350 } | |
3351 size_t j = i; | |
3352 // Iterate over all markers whose end offset is less than or equal to th
e new marker's, | |
3353 // removing markers of the same type as the new marker which touch it or
intersect with it, | |
3354 // adjusting the new marker's end offset to cover them if necessary. | |
3355 while (j < numMarkers) { | |
3356 DocumentMarker marker = markers[j]; | |
3357 if (marker.startOffset > newMarker.endOffset) | |
3358 break; | |
3359 if (marker.type == newMarker.type) { | |
3360 markers.remove(j); | |
3361 rects.remove(j); | |
3362 if (newMarker.endOffset <= marker.endOffset) { | |
3363 newMarker.endOffset = marker.endOffset; | |
3364 break; | |
3365 } | |
3366 numMarkers--; | |
3367 } else | |
3368 j++; | |
3369 } | |
3370 // At this point i points to the node before which we want to insert. | |
3371 markers.insert(i, newMarker); | |
3372 rects.insert(i, placeholderRectForMarker()); | |
3373 } | |
3374 | |
3375 // repaint the affected node | |
3376 if (node->renderer()) | |
3377 node->renderer()->repaint(); | |
3378 } | |
3379 | |
3380 // copies markers from srcNode to dstNode, applying the specified shift delta to
the copies. The shift is | |
3381 // useful if, e.g., the caller has created the dstNode from a non-prefix substri
ng of the srcNode. | |
3382 void Document::copyMarkers(Node *srcNode, unsigned startOffset, int length, Node
*dstNode, int delta, DocumentMarker::MarkerType markerType) | |
3383 { | |
3384 if (length <= 0) | |
3385 return; | |
3386 | |
3387 MarkerMapVectorPair* vectorPair = m_markers.get(srcNode); | |
3388 if (!vectorPair) | |
3389 return; | |
3390 | |
3391 ASSERT(vectorPair->first.size() == vectorPair->second.size()); | |
3392 | |
3393 bool docDirty = false; | |
3394 unsigned endOffset = startOffset + length - 1; | |
3395 Vector<DocumentMarker>& markers = vectorPair->first; | |
3396 for (size_t i = 0; i != markers.size(); ++i) { | |
3397 DocumentMarker marker = markers[i]; | |
3398 | |
3399 // stop if we are now past the specified range | |
3400 if (marker.startOffset > endOffset) | |
3401 break; | |
3402 | |
3403 // skip marker that is before the specified range or is the wrong type | |
3404 if (marker.endOffset < startOffset || (marker.type != markerType && mark
erType != DocumentMarker::AllMarkers)) | |
3405 continue; | |
3406 | |
3407 // pin the marker to the specified range and apply the shift delta | |
3408 docDirty = true; | |
3409 if (marker.startOffset < startOffset) | |
3410 marker.startOffset = startOffset; | |
3411 if (marker.endOffset > endOffset) | |
3412 marker.endOffset = endOffset; | |
3413 marker.startOffset += delta; | |
3414 marker.endOffset += delta; | |
3415 | |
3416 addMarker(dstNode, marker); | |
3417 } | |
3418 | |
3419 // repaint the affected node | |
3420 if (docDirty && dstNode->renderer()) | |
3421 dstNode->renderer()->repaint(); | |
3422 } | |
3423 | |
3424 void Document::removeMarkers(Node* node, unsigned startOffset, int length, Docum
entMarker::MarkerType markerType) | |
3425 { | |
3426 if (length <= 0) | |
3427 return; | |
3428 | |
3429 MarkerMapVectorPair* vectorPair = m_markers.get(node); | |
3430 if (!vectorPair) | |
3431 return; | |
3432 | |
3433 Vector<DocumentMarker>& markers = vectorPair->first; | |
3434 Vector<IntRect>& rects = vectorPair->second; | |
3435 ASSERT(markers.size() == rects.size()); | |
3436 bool docDirty = false; | |
3437 unsigned endOffset = startOffset + length; | |
3438 for (size_t i = 0; i < markers.size();) { | |
3439 DocumentMarker marker = markers[i]; | |
3440 | |
3441 // markers are returned in order, so stop if we are now past the specifi
ed range | |
3442 if (marker.startOffset >= endOffset) | |
3443 break; | |
3444 | |
3445 // skip marker that is wrong type or before target | |
3446 if (marker.endOffset < startOffset || (marker.type != markerType && mark
erType != DocumentMarker::AllMarkers)) { | |
3447 i++; | |
3448 continue; | |
3449 } | |
3450 | |
3451 // at this point we know that marker and target intersect in some way | |
3452 docDirty = true; | |
3453 | |
3454 // pitch the old marker and any associated rect | |
3455 markers.remove(i); | |
3456 rects.remove(i); | |
3457 | |
3458 // add either of the resulting slices that are left after removing targe
t | |
3459 if (startOffset > marker.startOffset) { | |
3460 DocumentMarker newLeft = marker; | |
3461 newLeft.endOffset = startOffset; | |
3462 markers.insert(i, newLeft); | |
3463 rects.insert(i, placeholderRectForMarker()); | |
3464 // i now points to the newly-inserted node, but we want to skip that
one | |
3465 i++; | |
3466 } | |
3467 if (marker.endOffset > endOffset) { | |
3468 DocumentMarker newRight = marker; | |
3469 newRight.startOffset = endOffset; | |
3470 markers.insert(i, newRight); | |
3471 rects.insert(i, placeholderRectForMarker()); | |
3472 // i now points to the newly-inserted node, but we want to skip that
one | |
3473 i++; | |
3474 } | |
3475 } | |
3476 | |
3477 if (markers.isEmpty()) { | |
3478 ASSERT(rects.isEmpty()); | |
3479 m_markers.remove(node); | |
3480 delete vectorPair; | |
3481 } | |
3482 | |
3483 // repaint the affected node | |
3484 if (docDirty && node->renderer()) | |
3485 node->renderer()->repaint(); | |
3486 } | |
3487 | |
3488 DocumentMarker* Document::markerContainingPoint(const IntPoint& point, DocumentM
arker::MarkerType markerType) | |
3489 { | |
3490 // outer loop: process each node that contains any markers | |
3491 MarkerMap::iterator end = m_markers.end(); | |
3492 for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != e
nd; ++nodeIterator) { | |
3493 // inner loop; process each marker in this node | |
3494 MarkerMapVectorPair* vectorPair = nodeIterator->second; | |
3495 Vector<DocumentMarker>& markers = vectorPair->first; | |
3496 Vector<IntRect>& rects = vectorPair->second; | |
3497 ASSERT(markers.size() == rects.size()); | |
3498 unsigned markerCount = markers.size(); | |
3499 for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex)
{ | |
3500 DocumentMarker& marker = markers[markerIndex]; | |
3501 | |
3502 // skip marker that is wrong type | |
3503 if (marker.type != markerType && markerType != DocumentMarker::AllMa
rkers) | |
3504 continue; | |
3505 | |
3506 IntRect& r = rects[markerIndex]; | |
3507 | |
3508 // skip placeholder rects | |
3509 if (r == placeholderRectForMarker()) | |
3510 continue; | |
3511 | |
3512 if (r.contains(point)) | |
3513 return ▮ | |
3514 } | |
3515 } | |
3516 | |
3517 return 0; | |
3518 } | |
3519 | |
3520 Vector<DocumentMarker> Document::markersForNode(Node* node) | |
3521 { | |
3522 MarkerMapVectorPair* vectorPair = m_markers.get(node); | |
3523 if (vectorPair) | |
3524 return vectorPair->first; | |
3525 return Vector<DocumentMarker>(); | |
3526 } | |
3527 | |
3528 Vector<IntRect> Document::renderedRectsForMarkers(DocumentMarker::MarkerType mar
kerType) | |
3529 { | |
3530 Vector<IntRect> result; | |
3531 | |
3532 // outer loop: process each node | |
3533 MarkerMap::iterator end = m_markers.end(); | |
3534 for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != e
nd; ++nodeIterator) { | |
3535 // inner loop; process each marker in this node | |
3536 MarkerMapVectorPair* vectorPair = nodeIterator->second; | |
3537 Vector<DocumentMarker>& markers = vectorPair->first; | |
3538 Vector<IntRect>& rects = vectorPair->second; | |
3539 ASSERT(markers.size() == rects.size()); | |
3540 unsigned markerCount = markers.size(); | |
3541 for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex)
{ | |
3542 DocumentMarker marker = markers[markerIndex]; | |
3543 | |
3544 // skip marker that is wrong type | |
3545 if (marker.type != markerType && markerType != DocumentMarker::AllMa
rkers) | |
3546 continue; | |
3547 | |
3548 IntRect r = rects[markerIndex]; | |
3549 // skip placeholder rects | |
3550 if (r == placeholderRectForMarker()) | |
3551 continue; | |
3552 | |
3553 result.append(r); | |
3554 } | |
3555 } | |
3556 | |
3557 return result; | |
3558 } | |
3559 | |
3560 void Document::removeMarkers(Node* node) | |
3561 { | |
3562 MarkerMap::iterator i = m_markers.find(node); | |
3563 if (i != m_markers.end()) { | |
3564 delete i->second; | |
3565 m_markers.remove(i); | |
3566 if (RenderObject* renderer = node->renderer()) | |
3567 renderer->repaint(); | |
3568 } | |
3569 } | |
3570 | |
3571 void Document::removeMarkers(DocumentMarker::MarkerType markerType) | |
3572 { | |
3573 // outer loop: process each markered node in the document | |
3574 MarkerMap markerMapCopy = m_markers; | |
3575 MarkerMap::iterator end = markerMapCopy.end(); | |
3576 for (MarkerMap::iterator i = markerMapCopy.begin(); i != end; ++i) { | |
3577 Node* node = i->first.get(); | |
3578 bool nodeNeedsRepaint = false; | |
3579 | |
3580 // inner loop: process each marker in the current node | |
3581 MarkerMapVectorPair* vectorPair = i->second; | |
3582 Vector<DocumentMarker>& markers = vectorPair->first; | |
3583 Vector<IntRect>& rects = vectorPair->second; | |
3584 ASSERT(markers.size() == rects.size()); | |
3585 for (size_t i = 0; i != markers.size();) { | |
3586 DocumentMarker marker = markers[i]; | |
3587 | |
3588 // skip nodes that are not of the specified type | |
3589 if (marker.type != markerType && markerType != DocumentMarker::AllMa
rkers) { | |
3590 ++i; | |
3591 continue; | |
3592 } | |
3593 | |
3594 // pitch the old marker | |
3595 markers.remove(i); | |
3596 rects.remove(i); | |
3597 nodeNeedsRepaint = true; | |
3598 // markerIterator now points to the next node | |
3599 } | |
3600 | |
3601 // Redraw the node if it changed. Do this before the node is removed fro
m m_markers, since | |
3602 // m_markers might contain the last reference to the node. | |
3603 if (nodeNeedsRepaint) { | |
3604 RenderObject* renderer = node->renderer(); | |
3605 if (renderer) | |
3606 renderer->repaint(); | |
3607 } | |
3608 | |
3609 // delete the node's list if it is now empty | |
3610 if (markers.isEmpty()) { | |
3611 ASSERT(rects.isEmpty()); | |
3612 m_markers.remove(node); | |
3613 delete vectorPair; | |
3614 } | |
3615 } | |
3616 } | |
3617 | |
3618 void Document::repaintMarkers(DocumentMarker::MarkerType markerType) | |
3619 { | |
3620 // outer loop: process each markered node in the document | |
3621 MarkerMap::iterator end = m_markers.end(); | |
3622 for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { | |
3623 Node* node = i->first.get(); | |
3624 | |
3625 // inner loop: process each marker in the current node | |
3626 MarkerMapVectorPair* vectorPair = i->second; | |
3627 Vector<DocumentMarker>& markers = vectorPair->first; | |
3628 bool nodeNeedsRepaint = false; | |
3629 for (size_t i = 0; i != markers.size(); ++i) { | |
3630 DocumentMarker marker = markers[i]; | |
3631 | |
3632 // skip nodes that are not of the specified type | |
3633 if (marker.type == markerType || markerType == DocumentMarker::AllMa
rkers) { | |
3634 nodeNeedsRepaint = true; | |
3635 break; | |
3636 } | |
3637 } | |
3638 | |
3639 if (!nodeNeedsRepaint) | |
3640 continue; | |
3641 | |
3642 // cause the node to be redrawn | |
3643 if (RenderObject* renderer = node->renderer()) | |
3644 renderer->repaint(); | |
3645 } | |
3646 } | |
3647 | |
3648 void Document::setRenderedRectForMarker(Node* node, DocumentMarker marker, const
IntRect& r) | |
3649 { | |
3650 MarkerMapVectorPair* vectorPair = m_markers.get(node); | |
3651 if (!vectorPair) { | |
3652 ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marke
r we don't already know about | |
3653 return; | |
3654 } | |
3655 | |
3656 Vector<DocumentMarker>& markers = vectorPair->first; | |
3657 ASSERT(markers.size() == vectorPair->second.size()); | |
3658 unsigned markerCount = markers.size(); | |
3659 for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { | |
3660 DocumentMarker m = markers[markerIndex]; | |
3661 if (m == marker) { | |
3662 vectorPair->second[markerIndex] = r; | |
3663 return; | |
3664 } | |
3665 } | |
3666 | |
3667 ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marker we
don't already know about | |
3668 } | |
3669 | |
3670 void Document::invalidateRenderedRectsForMarkersInRect(const IntRect& r) | |
3671 { | |
3672 // outer loop: process each markered node in the document | |
3673 MarkerMap::iterator end = m_markers.end(); | |
3674 for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { | |
3675 | |
3676 // inner loop: process each rect in the current node | |
3677 MarkerMapVectorPair* vectorPair = i->second; | |
3678 Vector<IntRect>& rects = vectorPair->second; | |
3679 | |
3680 unsigned rectCount = rects.size(); | |
3681 for (unsigned rectIndex = 0; rectIndex < rectCount; ++rectIndex) | |
3682 if (rects[rectIndex].intersects(r)) | |
3683 rects[rectIndex] = placeholderRectForMarker(); | |
3684 } | |
3685 } | |
3686 | |
3687 void Document::shiftMarkers(Node *node, unsigned startOffset, int delta, Documen
tMarker::MarkerType markerType) | |
3688 { | |
3689 MarkerMapVectorPair* vectorPair = m_markers.get(node); | |
3690 if (!vectorPair) | |
3691 return; | |
3692 | |
3693 Vector<DocumentMarker>& markers = vectorPair->first; | |
3694 Vector<IntRect>& rects = vectorPair->second; | |
3695 ASSERT(markers.size() == rects.size()); | |
3696 | |
3697 bool docDirty = false; | |
3698 for (size_t i = 0; i != markers.size(); ++i) { | |
3699 DocumentMarker &marker = markers[i]; | |
3700 if (marker.startOffset >= startOffset && (markerType == DocumentMarker::
AllMarkers || marker.type == markerType)) { | |
3701 ASSERT((int)marker.startOffset + delta >= 0); | |
3702 marker.startOffset += delta; | |
3703 marker.endOffset += delta; | |
3704 docDirty = true; | |
3705 | |
3706 // Marker moved, so previously-computed rendered rectangle is now in
valid | |
3707 rects[i] = placeholderRectForMarker(); | |
3708 } | |
3709 } | |
3710 | |
3711 // repaint the affected node | |
3712 if (docDirty && node->renderer()) | |
3713 node->renderer()->repaint(); | |
3714 } | |
3715 | |
3716 #if ENABLE(XSLT) | |
3717 | |
3718 void Document::applyXSLTransform(ProcessingInstruction* pi) | |
3719 { | |
3720 RefPtr<XSLTProcessor> processor = XSLTProcessor::create(); | |
3721 processor->setXSLStyleSheet(static_cast<XSLStyleSheet*>(pi->sheet())); | |
3722 String resultMIMEType; | |
3723 String newSource; | |
3724 String resultEncoding; | |
3725 if (!processor->transformToString(this, resultMIMEType, newSource, resultEnc
oding)) | |
3726 return; | |
3727 // FIXME: If the transform failed we should probably report an error (like M
ozilla does). | |
3728 processor->createDocumentFromSource(newSource, resultEncoding, resultMIMETyp
e, this, frame()); | |
3729 } | |
3730 | |
3731 void Document::setTransformSource(void* doc) | |
3732 { | |
3733 if (doc == m_transformSource) | |
3734 return; | |
3735 | |
3736 xmlFreeDoc((xmlDocPtr)m_transformSource); | |
3737 m_transformSource = doc; | |
3738 } | |
3739 | |
3740 #endif | |
3741 | |
3742 void Document::setDesignMode(InheritedBool value) | |
3743 { | |
3744 m_designMode = value; | |
3745 } | |
3746 | |
3747 Document::InheritedBool Document::getDesignMode() const | |
3748 { | |
3749 return m_designMode; | |
3750 } | |
3751 | |
3752 bool Document::inDesignMode() const | |
3753 { | |
3754 for (const Document* d = this; d; d = d->parentDocument()) { | |
3755 if (d->m_designMode != inherit) | |
3756 return d->m_designMode; | |
3757 } | |
3758 return false; | |
3759 } | |
3760 | |
3761 Document *Document::parentDocument() const | |
3762 { | |
3763 Frame *childPart = frame(); | |
3764 if (!childPart) | |
3765 return 0; | |
3766 Frame *parent = childPart->tree()->parent(); | |
3767 if (!parent) | |
3768 return 0; | |
3769 return parent->document(); | |
3770 } | |
3771 | |
3772 Document *Document::topDocument() const | |
3773 { | |
3774 Document *doc = const_cast<Document *>(this); | |
3775 Element *element; | |
3776 while ((element = doc->ownerElement())) | |
3777 doc = element->document(); | |
3778 | |
3779 return doc; | |
3780 } | |
3781 | |
3782 PassRefPtr<Attr> Document::createAttributeNS(const String& namespaceURI, const S
tring& qualifiedName, ExceptionCode& ec, bool shouldIgnoreNamespaceChecks) | |
3783 { | |
3784 String prefix, localName; | |
3785 if (!parseQualifiedName(qualifiedName, prefix, localName, ec)) | |
3786 return 0; | |
3787 | |
3788 QualifiedName qName(prefix, localName, namespaceURI); | |
3789 if (!shouldIgnoreNamespaceChecks && hasPrefixNamespaceMismatch(qName)) { | |
3790 ec = NAMESPACE_ERR; | |
3791 return 0; | |
3792 } | |
3793 | |
3794 // Spec: DOM Level 2 Core: http://www.w3.org/TR/DOM-Level-2-Core/core.html#I
D-DocCrAttrNS | |
3795 if (!shouldIgnoreNamespaceChecks && qName.localName() == "xmlns" && qName.na
mespaceURI() != "http://www.w3.org/2000/xmlns/") { | |
3796 ec = NAMESPACE_ERR; | |
3797 return 0; | |
3798 } | |
3799 | |
3800 // FIXME: Assume this is a mapped attribute, since createAttribute isn't nam
espace-aware. There's no harm to XML | |
3801 // documents if we're wrong. | |
3802 return new Attr(0, this, MappedAttribute::create(qName, StringImpl::empty())
); | |
3803 } | |
3804 | |
3805 #if ENABLE(SVG) | |
3806 const SVGDocumentExtensions* Document::svgExtensions() | |
3807 { | |
3808 return m_svgExtensions; | |
3809 } | |
3810 | |
3811 SVGDocumentExtensions* Document::accessSVGExtensions() | |
3812 { | |
3813 if (!m_svgExtensions) | |
3814 m_svgExtensions = new SVGDocumentExtensions(this); | |
3815 return m_svgExtensions; | |
3816 } | |
3817 #endif | |
3818 | |
3819 PassRefPtr<HTMLCollection> Document::images() | |
3820 { | |
3821 return HTMLCollection::create(this, HTMLCollection::DocImages); | |
3822 } | |
3823 | |
3824 PassRefPtr<HTMLCollection> Document::applets() | |
3825 { | |
3826 return HTMLCollection::create(this, HTMLCollection::DocApplets); | |
3827 } | |
3828 | |
3829 PassRefPtr<HTMLCollection> Document::embeds() | |
3830 { | |
3831 return HTMLCollection::create(this, HTMLCollection::DocEmbeds); | |
3832 } | |
3833 | |
3834 PassRefPtr<HTMLCollection> Document::plugins() | |
3835 { | |
3836 // This is an alias for embeds() required for the JS DOM bindings. | |
3837 return HTMLCollection::create(this, HTMLCollection::DocEmbeds); | |
3838 } | |
3839 | |
3840 PassRefPtr<HTMLCollection> Document::objects() | |
3841 { | |
3842 return HTMLCollection::create(this, HTMLCollection::DocObjects); | |
3843 } | |
3844 | |
3845 PassRefPtr<HTMLCollection> Document::scripts() | |
3846 { | |
3847 return HTMLCollection::create(this, HTMLCollection::DocScripts); | |
3848 } | |
3849 | |
3850 PassRefPtr<HTMLCollection> Document::links() | |
3851 { | |
3852 return HTMLCollection::create(this, HTMLCollection::DocLinks); | |
3853 } | |
3854 | |
3855 PassRefPtr<HTMLCollection> Document::forms() | |
3856 { | |
3857 return HTMLCollection::create(this, HTMLCollection::DocForms); | |
3858 } | |
3859 | |
3860 PassRefPtr<HTMLCollection> Document::anchors() | |
3861 { | |
3862 return HTMLCollection::create(this, HTMLCollection::DocAnchors); | |
3863 } | |
3864 | |
3865 PassRefPtr<HTMLCollection> Document::all() | |
3866 { | |
3867 return HTMLCollection::create(this, HTMLCollection::DocAll); | |
3868 } | |
3869 | |
3870 PassRefPtr<HTMLCollection> Document::windowNamedItems(const String &name) | |
3871 { | |
3872 return HTMLNameCollection::create(this, HTMLCollection::WindowNamedItems, na
me); | |
3873 } | |
3874 | |
3875 PassRefPtr<HTMLCollection> Document::documentNamedItems(const String &name) | |
3876 { | |
3877 return HTMLNameCollection::create(this, HTMLCollection::DocumentNamedItems,
name); | |
3878 } | |
3879 | |
3880 HTMLCollection::CollectionInfo* Document::nameCollectionInfo(HTMLCollection::Typ
e type, const AtomicString& name) | |
3881 { | |
3882 ASSERT(type >= HTMLCollection::FirstNamedDocumentCachedType); | |
3883 unsigned index = type - HTMLCollection::FirstNamedDocumentCachedType; | |
3884 ASSERT(index < HTMLCollection::NumNamedDocumentCachedTypes); | |
3885 | |
3886 NamedCollectionMap& map = m_nameCollectionInfo[index]; | |
3887 NamedCollectionMap::iterator iter = map.find(name.impl()); | |
3888 if (iter == map.end()) | |
3889 iter = map.add(name.impl(), new HTMLCollection::CollectionInfo).first; | |
3890 return iter->second; | |
3891 } | |
3892 | |
3893 void Document::finishedParsing() | |
3894 { | |
3895 setParsing(false); | |
3896 | |
3897 ExceptionCode ec = 0; | |
3898 dispatchEvent(Event::create(DOMContentLoadedEvent, true, false), ec); | |
3899 | |
3900 if (Frame* f = frame()) | |
3901 f->loader()->finishedParsing(); | |
3902 } | |
3903 | |
3904 Vector<String> Document::formElementsState() const | |
3905 { | |
3906 Vector<String> stateVector; | |
3907 stateVector.reserveCapacity(m_formElementsWithState.size() * 3); | |
3908 typedef ListHashSet<HTMLFormControlElementWithState*>::const_iterator Iterat
or; | |
3909 Iterator end = m_formElementsWithState.end(); | |
3910 for (Iterator it = m_formElementsWithState.begin(); it != end; ++it) { | |
3911 HTMLFormControlElementWithState* e = *it; | |
3912 String value; | |
3913 if (e->saveState(value)) { | |
3914 stateVector.append(e->name().string()); | |
3915 stateVector.append(e->type().string()); | |
3916 stateVector.append(value); | |
3917 } | |
3918 } | |
3919 return stateVector; | |
3920 } | |
3921 | |
3922 #if ENABLE(XPATH) | |
3923 | |
3924 PassRefPtr<XPathExpression> Document::createExpression(const String& expression, | |
3925 XPathNSResolver* resolver
, | |
3926 ExceptionCode& ec) | |
3927 { | |
3928 if (!m_xpathEvaluator) | |
3929 m_xpathEvaluator = XPathEvaluator::create(); | |
3930 return m_xpathEvaluator->createExpression(expression, resolver, ec); | |
3931 } | |
3932 | |
3933 PassRefPtr<XPathNSResolver> Document::createNSResolver(Node* nodeResolver) | |
3934 { | |
3935 if (!m_xpathEvaluator) | |
3936 m_xpathEvaluator = XPathEvaluator::create(); | |
3937 return m_xpathEvaluator->createNSResolver(nodeResolver); | |
3938 } | |
3939 | |
3940 PassRefPtr<XPathResult> Document::evaluate(const String& expression, | |
3941 Node* contextNode, | |
3942 XPathNSResolver* resolver, | |
3943 unsigned short type, | |
3944 XPathResult* result, | |
3945 ExceptionCode& ec) | |
3946 { | |
3947 if (!m_xpathEvaluator) | |
3948 m_xpathEvaluator = XPathEvaluator::create(); | |
3949 return m_xpathEvaluator->evaluate(expression, contextNode, resolver, type, r
esult, ec); | |
3950 } | |
3951 | |
3952 #endif // ENABLE(XPATH) | |
3953 | |
3954 void Document::setStateForNewFormElements(const Vector<String>& stateVector) | |
3955 { | |
3956 // Walk the state vector backwards so that the value to use for each | |
3957 // name/type pair first is the one at the end of each individual vector | |
3958 // in the FormElementStateMap. We're using them like stacks. | |
3959 typedef FormElementStateMap::iterator Iterator; | |
3960 m_formElementsWithState.clear(); | |
3961 for (size_t i = stateVector.size() / 3 * 3; i; i -= 3) { | |
3962 AtomicString a = stateVector[i - 3]; | |
3963 AtomicString b = stateVector[i - 2]; | |
3964 const String& c = stateVector[i - 1]; | |
3965 FormElementKey key(a.impl(), b.impl()); | |
3966 Iterator it = m_stateForNewFormElements.find(key); | |
3967 if (it != m_stateForNewFormElements.end()) | |
3968 it->second.append(c); | |
3969 else { | |
3970 Vector<String> v(1); | |
3971 v[0] = c; | |
3972 m_stateForNewFormElements.set(key, v); | |
3973 } | |
3974 } | |
3975 } | |
3976 | |
3977 bool Document::hasStateForNewFormElements() const | |
3978 { | |
3979 return !m_stateForNewFormElements.isEmpty(); | |
3980 } | |
3981 | |
3982 bool Document::takeStateForFormElement(AtomicStringImpl* name, AtomicStringImpl*
type, String& state) | |
3983 { | |
3984 typedef FormElementStateMap::iterator Iterator; | |
3985 Iterator it = m_stateForNewFormElements.find(FormElementKey(name, type)); | |
3986 if (it == m_stateForNewFormElements.end()) | |
3987 return false; | |
3988 ASSERT(it->second.size()); | |
3989 state = it->second.last(); | |
3990 if (it->second.size() > 1) | |
3991 it->second.removeLast(); | |
3992 else | |
3993 m_stateForNewFormElements.remove(it); | |
3994 return true; | |
3995 } | |
3996 | |
3997 FormElementKey::FormElementKey(AtomicStringImpl* name, AtomicStringImpl* type) | |
3998 : m_name(name), m_type(type) | |
3999 { | |
4000 ref(); | |
4001 } | |
4002 | |
4003 FormElementKey::~FormElementKey() | |
4004 { | |
4005 deref(); | |
4006 } | |
4007 | |
4008 FormElementKey::FormElementKey(const FormElementKey& other) | |
4009 : m_name(other.name()), m_type(other.type()) | |
4010 { | |
4011 ref(); | |
4012 } | |
4013 | |
4014 FormElementKey& FormElementKey::operator=(const FormElementKey& other) | |
4015 { | |
4016 other.ref(); | |
4017 deref(); | |
4018 m_name = other.name(); | |
4019 m_type = other.type(); | |
4020 return *this; | |
4021 } | |
4022 | |
4023 void FormElementKey::ref() const | |
4024 { | |
4025 if (name()) | |
4026 name()->ref(); | |
4027 if (type()) | |
4028 type()->ref(); | |
4029 } | |
4030 | |
4031 void FormElementKey::deref() const | |
4032 { | |
4033 if (name()) | |
4034 name()->deref(); | |
4035 if (type()) | |
4036 type()->deref(); | |
4037 } | |
4038 | |
4039 unsigned FormElementKeyHash::hash(const FormElementKey& k) | |
4040 { | |
4041 ASSERT(sizeof(k) % (sizeof(uint16_t) * 2) == 0); | |
4042 | |
4043 unsigned l = sizeof(k) / (sizeof(uint16_t) * 2); | |
4044 const uint16_t* s = reinterpret_cast<const uint16_t*>(&k); | |
4045 uint32_t hash = PHI; | |
4046 | |
4047 // Main loop | |
4048 for (; l > 0; l--) { | |
4049 hash += s[0]; | |
4050 uint32_t tmp = (s[1] << 11) ^ hash; | |
4051 hash = (hash << 16) ^ tmp; | |
4052 s += 2; | |
4053 hash += hash >> 11; | |
4054 } | |
4055 | |
4056 // Force "avalanching" of final 127 bits | |
4057 hash ^= hash << 3; | |
4058 hash += hash >> 5; | |
4059 hash ^= hash << 2; | |
4060 hash += hash >> 15; | |
4061 hash ^= hash << 10; | |
4062 | |
4063 // this avoids ever returning a hash code of 0, since that is used to | |
4064 // signal "hash not computed yet", using a value that is likely to be | |
4065 // effectively the same as 0 when the low bits are masked | |
4066 if (hash == 0) | |
4067 hash = 0x80000000; | |
4068 | |
4069 return hash; | |
4070 } | |
4071 | |
4072 void Document::setIconURL(const String& iconURL, const String& type) | |
4073 { | |
4074 // FIXME - <rdar://problem/4727645> - At some point in the future, we might
actually honor the "type" | |
4075 if (m_iconURL.isEmpty()) | |
4076 m_iconURL = iconURL; | |
4077 else if (!type.isEmpty()) | |
4078 m_iconURL = iconURL; | |
4079 } | |
4080 | |
4081 void Document::setUseSecureKeyboardEntryWhenActive(bool usesSecureKeyboard) | |
4082 { | |
4083 if (m_useSecureKeyboardEntryWhenActive == usesSecureKeyboard) | |
4084 return; | |
4085 | |
4086 m_useSecureKeyboardEntryWhenActive = usesSecureKeyboard; | |
4087 m_frame->updateSecureKeyboardEntryIfActive(); | |
4088 } | |
4089 | |
4090 bool Document::useSecureKeyboardEntryWhenActive() const | |
4091 { | |
4092 return m_useSecureKeyboardEntryWhenActive; | |
4093 } | |
4094 | |
4095 void Document::initSecurityContext() | |
4096 { | |
4097 if (m_securityOrigin && !m_securityOrigin->isEmpty()) | |
4098 return; // m_securityOrigin has already been initialized. | |
4099 | |
4100 if (!m_frame) { | |
4101 // No source for a security context. | |
4102 // This can occur via document.implementation.createDocument(). | |
4103 m_cookieURL = KURL(""); | |
4104 m_securityOrigin = SecurityOrigin::createEmpty(); | |
4105 return; | |
4106 } | |
4107 | |
4108 // In the common case, create the security context from the currently | |
4109 // loading URL. | |
4110 const KURL& url = m_frame->loader()->url(); | |
4111 m_cookieURL = url; | |
4112 m_securityOrigin = SecurityOrigin::create(url); | |
4113 | |
4114 if (FrameLoader::allowSubstituteDataAccessToLocal()) { | |
4115 // If this document was loaded with substituteData, then the document ca
n | |
4116 // load local resources. See https://bugs.webkit.org/show_bug.cgi?id=16
756 | |
4117 // and https://bugs.webkit.org/show_bug.cgi?id=19760 for further | |
4118 // discussion. | |
4119 DocumentLoader* documentLoader = m_frame->loader()->documentLoader(); | |
4120 if (documentLoader && documentLoader->substituteData().isValid()) | |
4121 m_securityOrigin->grantLoadLocalResources(); | |
4122 } | |
4123 | |
4124 if (!m_securityOrigin->isEmpty()) | |
4125 return; | |
4126 | |
4127 // If we do not obtain a meaningful origin from the URL, then we try to | |
4128 // find one via the frame hierarchy. | |
4129 | |
4130 Frame* ownerFrame = m_frame->tree()->parent(); | |
4131 if (!ownerFrame) | |
4132 ownerFrame = m_frame->loader()->opener(); | |
4133 | |
4134 if (ownerFrame && ownerFrame->document()) { | |
4135 m_cookieURL = ownerFrame->document()->cookieURL(); | |
4136 // We alias the SecurityOrigins to match Firefox, see Bug 15313 | |
4137 // https://bugs.webkit.org/show_bug.cgi?id=15313 | |
4138 m_securityOrigin = ownerFrame->document()->securityOrigin(); | |
4139 } | |
4140 } | |
4141 | |
4142 void Document::setSecurityOrigin(SecurityOrigin* securityOrigin) | |
4143 { | |
4144 m_securityOrigin = securityOrigin; | |
4145 initDNSPrefetchEnabled(); | |
4146 } | |
4147 | |
4148 void Document::updateFocusAppearanceSoon() | |
4149 { | |
4150 if (!m_updateFocusAppearanceTimer.isActive()) | |
4151 m_updateFocusAppearanceTimer.startOneShot(0); | |
4152 } | |
4153 | |
4154 void Document::cancelFocusAppearanceUpdate() | |
4155 { | |
4156 m_updateFocusAppearanceTimer.stop(); | |
4157 } | |
4158 | |
4159 void Document::updateFocusAppearanceTimerFired(Timer<Document>*) | |
4160 { | |
4161 Node* node = focusedNode(); | |
4162 if (!node) | |
4163 return; | |
4164 if (!node->isElementNode()) | |
4165 return; | |
4166 | |
4167 updateLayout(); | |
4168 | |
4169 Element* element = static_cast<Element*>(node); | |
4170 if (element->isFocusable()) | |
4171 element->updateFocusAppearance(false); | |
4172 } | |
4173 | |
4174 // FF method for accessing the selection added for compatability. | |
4175 DOMSelection* Document::getSelection() const | |
4176 { | |
4177 return frame() ? frame()->domWindow()->getSelection() : 0; | |
4178 } | |
4179 | |
4180 static inline int findSlashDotDotSlash(const UChar* characters, size_t length) | |
4181 { | |
4182 if (length < 4) | |
4183 return -1; | |
4184 unsigned loopLimit = length - 3; | |
4185 for (unsigned i = 0; i < loopLimit; ++i) { | |
4186 if (characters[i] == '/' && characters[i + 1] == '.' && characters[i + 2
] == '.' && characters[i + 3] == '/') | |
4187 return i; | |
4188 } | |
4189 return -1; | |
4190 } | |
4191 | |
4192 static inline int findSlashSlash(const UChar* characters, size_t length, int pos
ition) | |
4193 { | |
4194 if (length < 2) | |
4195 return -1; | |
4196 unsigned loopLimit = length - 1; | |
4197 for (unsigned i = position; i < loopLimit; ++i) { | |
4198 if (characters[i] == '/' && characters[i + 1] == '/') | |
4199 return i; | |
4200 } | |
4201 return -1; | |
4202 } | |
4203 | |
4204 static inline int findSlashDotSlash(const UChar* characters, size_t length) | |
4205 { | |
4206 if (length < 3) | |
4207 return -1; | |
4208 unsigned loopLimit = length - 2; | |
4209 for (unsigned i = 0; i < loopLimit; ++i) { | |
4210 if (characters[i] == '/' && characters[i + 1] == '.' && characters[i + 2
] == '/') | |
4211 return i; | |
4212 } | |
4213 return -1; | |
4214 } | |
4215 | |
4216 static inline bool containsColonSlashSlash(const UChar* characters, unsigned len
gth) | |
4217 { | |
4218 if (length < 3) | |
4219 return false; | |
4220 unsigned loopLimit = length - 2; | |
4221 for (unsigned i = 0; i < loopLimit; ++i) { | |
4222 if (characters[i] == ':' && characters[i + 1] == '/' && characters[i + 2
] == '/') | |
4223 return true; | |
4224 } | |
4225 return false; | |
4226 } | |
4227 | |
4228 static inline void cleanPath(Vector<UChar, 512>& path) | |
4229 { | |
4230 // FIXME: Shold not do this in the query or anchor part. | |
4231 int pos; | |
4232 while ((pos = findSlashDotDotSlash(path.data(), path.size())) != -1) { | |
4233 int prev = reverseFind(path.data(), path.size(), '/', pos - 1); | |
4234 // don't remove the host, i.e. http://foo.org/../foo.html | |
4235 if (prev < 0 || (prev > 3 && path[prev - 2] == ':' && path[prev - 1] ==
'/')) | |
4236 path.remove(pos, 3); | |
4237 else | |
4238 path.remove(prev, pos - prev + 3); | |
4239 } | |
4240 | |
4241 // FIXME: Shold not do this in the query part. | |
4242 // Set refPos to -2 to mean "I haven't looked for the anchor yet". | |
4243 // We don't want to waste a function call on the search for the the anchor | |
4244 // in the vast majority of cases where there is no "//" in the path. | |
4245 pos = 0; | |
4246 int refPos = -2; | |
4247 while ((pos = findSlashSlash(path.data(), path.size(), pos)) != -1) { | |
4248 if (refPos == -2) | |
4249 refPos = find(path.data(), path.size(), '#'); | |
4250 if (refPos > 0 && pos >= refPos) | |
4251 break; | |
4252 | |
4253 if (pos == 0 || path[pos - 1] != ':') | |
4254 path.remove(pos); | |
4255 else | |
4256 pos += 2; | |
4257 } | |
4258 | |
4259 // FIXME: Shold not do this in the query or anchor part. | |
4260 while ((pos = findSlashDotSlash(path.data(), path.size())) != -1) | |
4261 path.remove(pos, 2); | |
4262 } | |
4263 | |
4264 static inline bool matchLetter(UChar c, UChar lowercaseLetter) | |
4265 { | |
4266 return (c | 0x20) == lowercaseLetter; | |
4267 } | |
4268 | |
4269 static inline bool needsTrailingSlash(const UChar* characters, unsigned length) | |
4270 { | |
4271 if (length < 6) | |
4272 return false; | |
4273 if (!matchLetter(characters[0], 'h') | |
4274 || !matchLetter(characters[1], 't') | |
4275 || !matchLetter(characters[2], 't') | |
4276 || !matchLetter(characters[3], 'p')) | |
4277 return false; | |
4278 if (!(characters[4] == ':' | |
4279 || (matchLetter(characters[4], 's') && characters[5] == ':'))) | |
4280 return false; | |
4281 | |
4282 unsigned pos = characters[4] == ':' ? 5 : 6; | |
4283 | |
4284 // Skip initial two slashes if present. | |
4285 if (pos + 1 < length && characters[pos] == '/' && characters[pos + 1] == '/'
) | |
4286 pos += 2; | |
4287 | |
4288 // Find next slash. | |
4289 while (pos < length && characters[pos] != '/') | |
4290 ++pos; | |
4291 | |
4292 return pos == length; | |
4293 } | |
4294 | |
4295 unsigned Document::visitedLinkHash(const AtomicString& attributeURL) const | |
4296 { | |
4297 const UChar* characters = attributeURL.characters(); | |
4298 unsigned length = attributeURL.length(); | |
4299 if (!length) | |
4300 return 0; | |
4301 | |
4302 // This is a poor man's completeURL. Faster with less memory allocation. | |
4303 // FIXME: It's missing a lot of what completeURL does and a lot of what KURL
does. | |
4304 // For example, it does not handle international domain names properly. | |
4305 | |
4306 // FIXME: It is wrong that we do not do further processing on strings that h
ave "://" in them: | |
4307 // 1) The "://" could be in the query or anchor. | |
4308 // 2) The URL's path could have a "/./" or a "/../" or a "//" sequence in
it. | |
4309 | |
4310 // FIXME: needsTrailingSlash does not properly return true for a URL that ha
s no path, but does | |
4311 // have a query or anchor. | |
4312 | |
4313 bool hasColonSlashSlash = containsColonSlashSlash(characters, length); | |
4314 | |
4315 if (hasColonSlashSlash && !needsTrailingSlash(characters, length)) | |
4316 return AlreadyHashed::avoidDeletedValue(attributeURL.string().impl()->ha
sh()); | |
4317 | |
4318 Vector<UChar, 512> buffer; | |
4319 | |
4320 if (hasColonSlashSlash) { | |
4321 // FIXME: This is incorrect for URLs that have a query or anchor; the "/
" needs to go at the | |
4322 // end of the path, *before* the query or anchor. | |
4323 buffer.append(characters, length); | |
4324 buffer.append('/'); | |
4325 return AlreadyHashed::avoidDeletedValue(StringImpl::computeHash(buffer.d
ata(), buffer.size())); | |
4326 } | |
4327 | |
4328 switch (characters[0]) { | |
4329 case '/': | |
4330 buffer.append(m_baseURL.string().characters(), m_baseURL.pathStart()
); | |
4331 break; | |
4332 case '#': | |
4333 buffer.append(m_baseURL.string().characters(), m_baseURL.pathEnd()); | |
4334 break; | |
4335 default: | |
4336 buffer.append(m_baseURL.string().characters(), m_baseURL.pathAfterLa
stSlash()); | |
4337 break; | |
4338 } | |
4339 buffer.append(characters, length); | |
4340 cleanPath(buffer); | |
4341 if (needsTrailingSlash(buffer.data(), buffer.size())) { | |
4342 // FIXME: This is incorrect for URLs that have a query or anchor; the "/
" needs to go at the | |
4343 // end of the path, *before* the query or anchor. | |
4344 buffer.append('/'); | |
4345 } | |
4346 | |
4347 return AlreadyHashed::avoidDeletedValue(StringImpl::computeHash(buffer.data(
), buffer.size())); | |
4348 } | |
4349 | |
4350 #if ENABLE(DATABASE) | |
4351 | |
4352 void Document::addOpenDatabase(Database* database) | |
4353 { | |
4354 if (!m_openDatabaseSet) | |
4355 m_openDatabaseSet.set(new DatabaseSet); | |
4356 | |
4357 ASSERT(!m_openDatabaseSet->contains(database)); | |
4358 m_openDatabaseSet->add(database); | |
4359 } | |
4360 | |
4361 void Document::removeOpenDatabase(Database* database) | |
4362 { | |
4363 ASSERT(m_openDatabaseSet && m_openDatabaseSet->contains(database)); | |
4364 if (!m_openDatabaseSet) | |
4365 return; | |
4366 | |
4367 m_openDatabaseSet->remove(database); | |
4368 } | |
4369 | |
4370 DatabaseThread* Document::databaseThread() | |
4371 { | |
4372 if (!m_databaseThread && !m_hasOpenDatabases) { | |
4373 // Create the database thread on first request - but not if at least one
database was already opened, | |
4374 // because in that case we already had a database thread and terminated
it and should not create another. | |
4375 m_databaseThread = DatabaseThread::create(this); | |
4376 if (!m_databaseThread->start()) | |
4377 m_databaseThread = 0; | |
4378 } | |
4379 | |
4380 return m_databaseThread.get(); | |
4381 } | |
4382 | |
4383 void Document::stopDatabases() | |
4384 { | |
4385 if (m_openDatabaseSet) { | |
4386 DatabaseSet::iterator i = m_openDatabaseSet->begin(); | |
4387 DatabaseSet::iterator end = m_openDatabaseSet->end(); | |
4388 for (; i != end; ++i) { | |
4389 (*i)->stop(); | |
4390 if (m_databaseThread) | |
4391 m_databaseThread->unscheduleDatabaseTasks(*i); | |
4392 } | |
4393 } | |
4394 | |
4395 if (m_databaseThread) | |
4396 m_databaseThread->requestTermination(); | |
4397 } | |
4398 | |
4399 #endif | |
4400 | |
4401 void Document::attachRange(Range* range) | |
4402 { | |
4403 ASSERT(!m_ranges.contains(range)); | |
4404 m_ranges.add(range); | |
4405 } | |
4406 | |
4407 void Document::detachRange(Range* range) | |
4408 { | |
4409 ASSERT(m_ranges.contains(range)); | |
4410 m_ranges.remove(range); | |
4411 } | |
4412 | |
4413 CanvasRenderingContext2D* Document::getCSSCanvasContext(const String& type, cons
t String& name, int width, int height) | |
4414 { | |
4415 HTMLCanvasElement* result = getCSSCanvasElement(name); | |
4416 if (!result) | |
4417 return 0; | |
4418 result->setSize(IntSize(width, height)); | |
4419 return result->getContext(type); | |
4420 } | |
4421 | |
4422 HTMLCanvasElement* Document::getCSSCanvasElement(const String& name) | |
4423 { | |
4424 RefPtr<HTMLCanvasElement> result = m_cssCanvasElements.get(name).get(); | |
4425 if (!result) { | |
4426 result = new HTMLCanvasElement(this); | |
4427 m_cssCanvasElements.set(name, result); | |
4428 } | |
4429 return result.get(); | |
4430 } | |
4431 | |
4432 void Document::initDNSPrefetchEnabled() | |
4433 { | |
4434 m_haveExplicitlyDisabledDNSPrefetch = false; | |
4435 m_isDNSPrefetchEnabled = (securityOrigin()->protocol() == "http"); | |
4436 | |
4437 // Inherit DNS prefetch opt-out from parent frame | |
4438 if (Document* parent = parentDocument()) | |
4439 if (!parent->isDNSPrefetchEnabled()) | |
4440 m_isDNSPrefetchEnabled = false; | |
4441 } | |
4442 | |
4443 void Document::setDNSPrefetchControl(const String& dnsPrefetchControl) | |
4444 { | |
4445 if (equalIgnoringCase(dnsPrefetchControl, "on") && | |
4446 !m_haveExplicitlyDisabledDNSPrefetch) { | |
4447 m_isDNSPrefetchEnabled = true; | |
4448 return; | |
4449 } | |
4450 | |
4451 m_isDNSPrefetchEnabled = false; | |
4452 m_haveExplicitlyDisabledDNSPrefetch = true; | |
4453 } | |
4454 | |
4455 } // namespace WebCore | |
OLD | NEW |