| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) | 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| 4 * (C) 2000 Simon Hausmann <hausmann@kde.org> | 4 * (C) 2000 Simon Hausmann <hausmann@kde.org> |
| 5 * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserv
ed. | 5 * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserv
ed. |
| 6 * (C) 2006 Graham Dennis (graham.dennis@gmail.com) | 6 * (C) 2006 Graham Dennis (graham.dennis@gmail.com) |
| 7 * | 7 * |
| 8 * This library is free software; you can redistribute it and/or | 8 * This library is free software; you can redistribute it and/or |
| 9 * modify it under the terms of the GNU Library General Public | 9 * modify it under the terms of the GNU Library General Public |
| 10 * License as published by the Free Software Foundation; either | 10 * License as published by the Free Software Foundation; either |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 102 double m_tapDownTimestamp; | 102 double m_tapDownTimestamp; |
| 103 bool m_hadHREFChanged; | 103 bool m_hadHREFChanged; |
| 104 bool m_hadTapUnconfirmed; | 104 bool m_hadTapUnconfirmed; |
| 105 bool m_hasIssuedPreconnect; | 105 bool m_hasIssuedPreconnect; |
| 106 }; | 106 }; |
| 107 | 107 |
| 108 using namespace HTMLNames; | 108 using namespace HTMLNames; |
| 109 | 109 |
| 110 HTMLAnchorElement::HTMLAnchorElement(const QualifiedName& tagName, Document& doc
ument) | 110 HTMLAnchorElement::HTMLAnchorElement(const QualifiedName& tagName, Document& doc
ument) |
| 111 : HTMLElement(tagName, document) | 111 : HTMLElement(tagName, document) |
| 112 , m_hasRootEditableElementForSelectionOnMouseDown(false) | |
| 113 , m_wasShiftKeyDownOnMouseDown(false) | |
| 114 , m_linkRelations(0) | 112 , m_linkRelations(0) |
| 115 , m_cachedVisitedLinkHash(0) | 113 , m_cachedVisitedLinkHash(0) |
| 116 { | 114 { |
| 117 ScriptWrappable::init(this); | 115 ScriptWrappable::init(this); |
| 118 } | 116 } |
| 119 | 117 |
| 120 PassRefPtr<HTMLAnchorElement> HTMLAnchorElement::create(Document& document) | 118 PassRefPtr<HTMLAnchorElement> HTMLAnchorElement::create(Document& document) |
| 121 { | 119 { |
| 122 return adoptRef(new HTMLAnchorElement(aTag, document)); | 120 return adoptRef(new HTMLAnchorElement(aTag, document)); |
| 123 } | 121 } |
| 124 | 122 |
| 125 PassRefPtr<HTMLAnchorElement> HTMLAnchorElement::create(const QualifiedName& tag
Name, Document& document) | 123 PassRefPtr<HTMLAnchorElement> HTMLAnchorElement::create(const QualifiedName& tag
Name, Document& document) |
| 126 { | 124 { |
| 127 return adoptRef(new HTMLAnchorElement(tagName, document)); | 125 return adoptRef(new HTMLAnchorElement(tagName, document)); |
| 128 } | 126 } |
| 129 | 127 |
| 130 HTMLAnchorElement::~HTMLAnchorElement() | 128 HTMLAnchorElement::~HTMLAnchorElement() |
| 131 { | 129 { |
| 132 clearRootEditableElementForSelectionOnMouseDown(); | |
| 133 } | 130 } |
| 134 | 131 |
| 135 bool HTMLAnchorElement::supportsFocus() const | 132 bool HTMLAnchorElement::supportsFocus() const |
| 136 { | 133 { |
| 137 if (rendererIsEditable()) | 134 if (rendererIsEditable()) |
| 138 return HTMLElement::supportsFocus(); | 135 return HTMLElement::supportsFocus(); |
| 139 // If not a link we should still be able to focus the element if it has tabI
ndex. | 136 // If not a link we should still be able to focus the element if it has tabI
ndex. |
| 140 return isLink() || HTMLElement::supportsFocus(); | 137 return isLink() || HTMLElement::supportsFocus(); |
| 141 } | 138 } |
| 142 | 139 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 187 int y = absolutePosition.y(); | 184 int y = absolutePosition.y(); |
| 188 url.append('?'); | 185 url.append('?'); |
| 189 url.appendNumber(x); | 186 url.appendNumber(x); |
| 190 url.append(','); | 187 url.append(','); |
| 191 url.appendNumber(y); | 188 url.appendNumber(y); |
| 192 } | 189 } |
| 193 | 190 |
| 194 void HTMLAnchorElement::defaultEventHandler(Event* event) | 191 void HTMLAnchorElement::defaultEventHandler(Event* event) |
| 195 { | 192 { |
| 196 if (isLink()) { | 193 if (isLink()) { |
| 197 if (focused() && isEnterKeyKeydownEvent(event) && treatLinkAsLiveForEven
tType(NonMouseEvent)) { | 194 if (focused() && isEnterKeyKeydownEvent(event) && isLiveLink()) { |
| 198 event->setDefaultHandled(); | 195 event->setDefaultHandled(); |
| 199 dispatchSimulatedClick(event); | 196 dispatchSimulatedClick(event); |
| 200 return; | 197 return; |
| 201 } | 198 } |
| 202 | 199 |
| 203 prefetchEventHandler()->handleEvent(event); | 200 prefetchEventHandler()->handleEvent(event); |
| 204 | 201 |
| 205 if (isLinkClick(event) && treatLinkAsLiveForEventType(eventType(event)))
{ | 202 if (isLinkClick(event) && isLiveLink()) { |
| 206 handleClick(event); | 203 handleClick(event); |
| 207 prefetchEventHandler()->reset(); | 204 prefetchEventHandler()->reset(); |
| 208 return; | 205 return; |
| 209 } | 206 } |
| 210 | |
| 211 if (rendererIsEditable()) { | |
| 212 // This keeps track of the editable block that the selection was in
(if it was in one) just before the link was clicked | |
| 213 // for the LiveWhenNotFocused editable link behavior | |
| 214 if (event->type() == EventTypeNames::mousedown && event->isMouseEven
t() && toMouseEvent(event)->button() != RightButton && document().frame()) { | |
| 215 setRootEditableElementForSelectionOnMouseDown(document().frame()
->selection().rootEditableElement()); | |
| 216 m_wasShiftKeyDownOnMouseDown = toMouseEvent(event)->shiftKey(); | |
| 217 } else if (event->type() == EventTypeNames::mouseover) { | |
| 218 // These are cleared on mouseover and not mouseout because their
values are needed for drag events, | |
| 219 // but drag events happen after mouse out events. | |
| 220 clearRootEditableElementForSelectionOnMouseDown(); | |
| 221 m_wasShiftKeyDownOnMouseDown = false; | |
| 222 } | |
| 223 } | |
| 224 } | 207 } |
| 225 | 208 |
| 226 HTMLElement::defaultEventHandler(event); | 209 HTMLElement::defaultEventHandler(event); |
| 227 } | 210 } |
| 228 | 211 |
| 229 void HTMLAnchorElement::setActive(bool down) | 212 void HTMLAnchorElement::setActive(bool down) |
| 230 { | 213 { |
| 231 if (rendererIsEditable()) { | 214 if (rendererIsEditable()) |
| 232 EditableLinkBehavior editableLinkBehavior = EditableLinkDefaultBehavior; | 215 return; |
| 233 if (Settings* settings = document().settings()) | |
| 234 editableLinkBehavior = settings->editableLinkBehavior(); | |
| 235 | |
| 236 switch (editableLinkBehavior) { | |
| 237 default: | |
| 238 case EditableLinkDefaultBehavior: | |
| 239 case EditableLinkAlwaysLive: | |
| 240 break; | |
| 241 | |
| 242 case EditableLinkNeverLive: | |
| 243 return; | |
| 244 | |
| 245 // Don't set the link to be active if the current selection is in th
e same editable block as | |
| 246 // this link | |
| 247 case EditableLinkLiveWhenNotFocused: | |
| 248 if (down && document().frame() && document().frame()->selection(
).rootEditableElement() == rootEditableElement()) | |
| 249 return; | |
| 250 break; | |
| 251 | |
| 252 case EditableLinkOnlyLiveWithShiftKey: | |
| 253 return; | |
| 254 } | |
| 255 | |
| 256 } | |
| 257 | 216 |
| 258 ContainerNode::setActive(down); | 217 ContainerNode::setActive(down); |
| 259 } | 218 } |
| 260 | 219 |
| 261 void HTMLAnchorElement::parseAttribute(const QualifiedName& name, const AtomicSt
ring& value) | 220 void HTMLAnchorElement::parseAttribute(const QualifiedName& name, const AtomicSt
ring& value) |
| 262 { | 221 { |
| 263 if (name == hrefAttr) { | 222 if (name == hrefAttr) { |
| 264 bool wasLink = isLink(); | 223 bool wasLink = isLink(); |
| 265 setIsLink(!value.isNull()); | 224 setIsLink(!value.isNull()); |
| 266 if (wasLink != isLink()) { | 225 if (wasLink != isLink()) { |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 385 } | 344 } |
| 386 | 345 |
| 387 | 346 |
| 388 String HTMLAnchorElement::text() | 347 String HTMLAnchorElement::text() |
| 389 { | 348 { |
| 390 return innerText(); | 349 return innerText(); |
| 391 } | 350 } |
| 392 | 351 |
| 393 bool HTMLAnchorElement::isLiveLink() const | 352 bool HTMLAnchorElement::isLiveLink() const |
| 394 { | 353 { |
| 395 return isLink() && treatLinkAsLiveForEventType(m_wasShiftKeyDownOnMouseDown
? MouseEventWithShiftKey : MouseEventWithoutShiftKey); | 354 return isLink() && !rendererIsEditable(); |
| 396 } | 355 } |
| 397 | 356 |
| 398 void HTMLAnchorElement::sendPings(const KURL& destinationURL) | 357 void HTMLAnchorElement::sendPings(const KURL& destinationURL) |
| 399 { | 358 { |
| 400 const AtomicString& pingValue = getAttribute(pingAttr); | 359 const AtomicString& pingValue = getAttribute(pingAttr); |
| 401 if (pingValue.isNull() || !document().settings() || !document().settings()->
hyperlinkAuditingEnabled()) | 360 if (pingValue.isNull() || !document().settings() || !document().settings()->
hyperlinkAuditingEnabled()) |
| 402 return; | 361 return; |
| 403 | 362 |
| 404 UseCounter::count(document(), UseCounter::HTMLAnchorElementPingAttribute); | 363 UseCounter::count(document(), UseCounter::HTMLAnchorElementPingAttribute); |
| 405 | 364 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 438 frame->loader().client()->loadURLExternally(request, NavigationPolicyDow
nload, fastGetAttribute(downloadAttr)); | 397 frame->loader().client()->loadURLExternally(request, NavigationPolicyDow
nload, fastGetAttribute(downloadAttr)); |
| 439 } else { | 398 } else { |
| 440 FrameLoadRequest frameRequest(&document(), request, target()); | 399 FrameLoadRequest frameRequest(&document(), request, target()); |
| 441 frameRequest.setTriggeringEvent(event); | 400 frameRequest.setTriggeringEvent(event); |
| 442 if (hasRel(RelationNoReferrer)) | 401 if (hasRel(RelationNoReferrer)) |
| 443 frameRequest.setShouldSendReferrer(NeverSendReferrer); | 402 frameRequest.setShouldSendReferrer(NeverSendReferrer); |
| 444 frame->loader().load(frameRequest); | 403 frame->loader().load(frameRequest); |
| 445 } | 404 } |
| 446 } | 405 } |
| 447 | 406 |
| 448 HTMLAnchorElement::EventType HTMLAnchorElement::eventType(Event* event) | |
| 449 { | |
| 450 if (!event->isMouseEvent()) | |
| 451 return NonMouseEvent; | |
| 452 return toMouseEvent(event)->shiftKey() ? MouseEventWithShiftKey : MouseEvent
WithoutShiftKey; | |
| 453 } | |
| 454 | |
| 455 bool HTMLAnchorElement::treatLinkAsLiveForEventType(EventType eventType) const | |
| 456 { | |
| 457 if (!rendererIsEditable()) | |
| 458 return true; | |
| 459 | |
| 460 Settings* settings = document().settings(); | |
| 461 if (!settings) | |
| 462 return true; | |
| 463 | |
| 464 switch (settings->editableLinkBehavior()) { | |
| 465 case EditableLinkDefaultBehavior: | |
| 466 case EditableLinkAlwaysLive: | |
| 467 return true; | |
| 468 | |
| 469 case EditableLinkNeverLive: | |
| 470 return false; | |
| 471 | |
| 472 // If the selection prior to clicking on this link resided in the same edita
ble block as this link, | |
| 473 // and the shift key isn't pressed, we don't want to follow the link. | |
| 474 case EditableLinkLiveWhenNotFocused: | |
| 475 return eventType == MouseEventWithShiftKey || (eventType == MouseEventWi
thoutShiftKey && rootEditableElementForSelectionOnMouseDown() != rootEditableEle
ment()); | |
| 476 | |
| 477 case EditableLinkOnlyLiveWithShiftKey: | |
| 478 return eventType == MouseEventWithShiftKey; | |
| 479 } | |
| 480 | |
| 481 ASSERT_NOT_REACHED(); | |
| 482 return false; | |
| 483 } | |
| 484 | |
| 485 bool isEnterKeyKeydownEvent(Event* event) | 407 bool isEnterKeyKeydownEvent(Event* event) |
| 486 { | 408 { |
| 487 return event->type() == EventTypeNames::keydown && event->isKeyboardEvent()
&& toKeyboardEvent(event)->keyIdentifier() == "Enter"; | 409 return event->type() == EventTypeNames::keydown && event->isKeyboardEvent()
&& toKeyboardEvent(event)->keyIdentifier() == "Enter"; |
| 488 } | 410 } |
| 489 | 411 |
| 490 bool isLinkClick(Event* event) | 412 bool isLinkClick(Event* event) |
| 491 { | 413 { |
| 492 return event->type() == EventTypeNames::click && (!event->isMouseEvent() ||
toMouseEvent(event)->button() != RightButton); | 414 return event->type() == EventTypeNames::click && (!event->isMouseEvent() ||
toMouseEvent(event)->button() != RightButton); |
| 493 } | 415 } |
| 494 | 416 |
| 495 bool HTMLAnchorElement::willRespondToMouseClickEvents() | 417 bool HTMLAnchorElement::willRespondToMouseClickEvents() |
| 496 { | 418 { |
| 497 return isLink() || HTMLElement::willRespondToMouseClickEvents(); | 419 return isLink() || HTMLElement::willRespondToMouseClickEvents(); |
| 498 } | 420 } |
| 499 | 421 |
| 500 typedef HashMap<const HTMLAnchorElement*, RefPtr<Element> > RootEditableElementM
ap; | |
| 501 | |
| 502 static RootEditableElementMap& rootEditableElementMap() | |
| 503 { | |
| 504 DEFINE_STATIC_LOCAL(RootEditableElementMap, map, ()); | |
| 505 return map; | |
| 506 } | |
| 507 | |
| 508 Element* HTMLAnchorElement::rootEditableElementForSelectionOnMouseDown() const | |
| 509 { | |
| 510 if (!m_hasRootEditableElementForSelectionOnMouseDown) | |
| 511 return 0; | |
| 512 return rootEditableElementMap().get(this); | |
| 513 } | |
| 514 | |
| 515 void HTMLAnchorElement::clearRootEditableElementForSelectionOnMouseDown() | |
| 516 { | |
| 517 if (!m_hasRootEditableElementForSelectionOnMouseDown) | |
| 518 return; | |
| 519 rootEditableElementMap().remove(this); | |
| 520 m_hasRootEditableElementForSelectionOnMouseDown = false; | |
| 521 } | |
| 522 | |
| 523 void HTMLAnchorElement::setRootEditableElementForSelectionOnMouseDown(Element* e
lement) | |
| 524 { | |
| 525 if (!element) { | |
| 526 clearRootEditableElementForSelectionOnMouseDown(); | |
| 527 return; | |
| 528 } | |
| 529 | |
| 530 rootEditableElementMap().set(this, element); | |
| 531 m_hasRootEditableElementForSelectionOnMouseDown = true; | |
| 532 } | |
| 533 | |
| 534 HTMLAnchorElement::PrefetchEventHandler* HTMLAnchorElement::prefetchEventHandler
() | 422 HTMLAnchorElement::PrefetchEventHandler* HTMLAnchorElement::prefetchEventHandler
() |
| 535 { | 423 { |
| 536 if (!m_prefetchEventHandler) | 424 if (!m_prefetchEventHandler) |
| 537 m_prefetchEventHandler = PrefetchEventHandler::create(this); | 425 m_prefetchEventHandler = PrefetchEventHandler::create(this); |
| 538 | 426 |
| 539 return m_prefetchEventHandler.get(); | 427 return m_prefetchEventHandler.get(); |
| 540 } | 428 } |
| 541 | 429 |
| 542 HTMLAnchorElement::PrefetchEventHandler::PrefetchEventHandler(HTMLAnchorElement*
anchorElement) | 430 HTMLAnchorElement::PrefetchEventHandler::PrefetchEventHandler(HTMLAnchorElement*
anchorElement) |
| 543 : m_anchorElement(anchorElement) | 431 : m_anchorElement(anchorElement) |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 697 preconnectToURL(url, motivation); | 585 preconnectToURL(url, motivation); |
| 698 m_hasIssuedPreconnect = true; | 586 m_hasIssuedPreconnect = true; |
| 699 } | 587 } |
| 700 | 588 |
| 701 bool HTMLAnchorElement::isInteractiveContent() const | 589 bool HTMLAnchorElement::isInteractiveContent() const |
| 702 { | 590 { |
| 703 return isLink(); | 591 return isLink(); |
| 704 } | 592 } |
| 705 | 593 |
| 706 } | 594 } |
| OLD | NEW |