| 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 |