Fixing a comment.
[WebKit-https.git] / WebCore / html / HTMLAnchorElement.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  *           (C) 2000 Simon Hausmann <hausmann@kde.org>
7  * Copyright (C) 2003, 2006 Apple Computer, Inc.
8  *           (C) 2006 Graham Dennis (graham.dennis@gmail.com)
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 #include "config.h"
27 #include "HTMLAnchorElement.h"
28
29 #include "Document.h"
30 #include "Event.h"
31 #include "EventHandler.h"
32 #include "EventNames.h"
33 #include "Frame.h"
34 #include "FrameLoader.h"
35 #include "HTMLImageElement.h"
36 #include "HTMLNames.h"
37 #include "KeyboardEvent.h"
38 #include "MouseEvent.h"
39 #include "MutationEvent.h"
40 #include "RenderFlow.h"
41 #include "RenderImage.h"
42 #include "ResourceRequest.h"
43 #include "SelectionController.h"
44 #include "Settings.h"
45 #include "UIEvent.h"
46 #include "csshelper.h"
47
48 namespace WebCore {
49
50 using namespace HTMLNames;
51 using namespace EventNames;
52
53 HTMLAnchorElement::HTMLAnchorElement(Document* doc)
54     : HTMLElement(aTag, doc)
55     , m_rootEditableElementForSelectionOnMouseDown(0)
56     , m_wasShiftKeyDownOnMouseDown(false)
57 {
58 }
59
60 HTMLAnchorElement::HTMLAnchorElement(const QualifiedName& tagName, Document* doc)
61     : HTMLElement(tagName, doc)
62     , m_rootEditableElementForSelectionOnMouseDown(0)
63     , m_wasShiftKeyDownOnMouseDown(false)
64 {
65 }
66
67 HTMLAnchorElement::~HTMLAnchorElement()
68 {
69 }
70
71 bool HTMLAnchorElement::supportsFocus() const
72 {
73     if (isContentEditable())
74         return HTMLElement::supportsFocus();
75     return isFocusable() || (m_isLink && document() && !document()->haveStylesheetsLoaded());
76 }
77
78 bool HTMLAnchorElement::isFocusable() const
79 {
80     if (isContentEditable())
81         return HTMLElement::isFocusable();
82
83     // FIXME: Even if we are not visible, we might have a child that is visible.
84     // Dave wants to fix that some day with a "has visible content" flag or the like.
85     if (!(m_isLink && renderer() && renderer()->style()->visibility() == VISIBLE))
86         return false;
87
88     // Before calling absoluteRects, check for the common case where the renderer
89     // or one of the continuations is non-empty, since this is a faster check and
90     // almost always returns true.
91     for (RenderObject* r = renderer(); r; r = r->continuation())
92         if (r->width() > 0 && r->height() > 0)
93             return true;
94
95     Vector<IntRect> rects;
96     int x, y;
97     renderer()->absolutePosition(x, y);
98     renderer()->absoluteRects(rects, x, y);
99     size_t n = rects.size();
100     for (size_t i = 0; i < n; ++i)
101         if (!rects[i].isEmpty())
102             return true;
103
104     return false;
105 }
106
107 bool HTMLAnchorElement::isMouseFocusable() const
108 {
109     return false;
110 }
111
112 bool HTMLAnchorElement::isKeyboardFocusable(KeyboardEvent* event) const
113 {
114     if (!isFocusable())
115         return false;
116     
117     if (!document()->frame())
118         return false;
119
120     return document()->frame()->eventHandler()->tabsToLinks(event);
121 }
122
123 void HTMLAnchorElement::defaultEventHandler(Event* evt)
124 {
125     // React on clicks and on keypresses.
126     // Don't make this KEYUP_EVENT again, it makes khtml follow links it shouldn't,
127     // when pressing Enter in the combo.
128     if (m_isLink && (evt->type() == clickEvent || (evt->type() == keydownEvent && m_focused))) {
129         MouseEvent* e = 0;
130         if (evt->type() == clickEvent)
131             e = static_cast<MouseEvent*>(evt);
132
133         KeyboardEvent* k = 0;
134         if (evt->type() == keydownEvent)
135             k = static_cast<KeyboardEvent*>(evt);
136
137         if (e && e->button() == 2) {
138             HTMLElement::defaultEventHandler(evt);
139             return;
140         }
141
142         // If the link is editable, then we need to check the settings to see whether or not to follow the link
143         if (isContentEditable()) {
144             EditableLinkBehavior editableLinkBehavior = EditableLinkDefaultBehavior;
145             if (document()->frame() && document()->frame()->settings())
146                 editableLinkBehavior = document()->frame()->settings()->editableLinkBehavior();
147                 
148             switch (editableLinkBehavior) {
149                 // Always follow the link (Safari 2.0 behavior)
150                 default:
151                 case EditableLinkDefaultBehavior:
152                 case EditableLinkAlwaysLive:
153                     break;
154
155                 case EditableLinkNeverLive:
156                     HTMLElement::defaultEventHandler(evt);
157                     return;
158
159                 // If the selection prior to clicking on this link resided in the same editable block as this link,
160                 // and the shift key isn't pressed, we don't want to follow the link
161                 case EditableLinkLiveWhenNotFocused:
162                     if (e && !e->shiftKey() && m_rootEditableElementForSelectionOnMouseDown == rootEditableElement()) {
163                         HTMLElement::defaultEventHandler(evt);
164                         return;
165                     }
166                     break;
167
168                 // Only follow the link if the shift key is down (WinIE/Firefox behavior)
169                 case EditableLinkOnlyLiveWithShiftKey:
170                     if (e && !e->shiftKey()) {
171                         HTMLElement::defaultEventHandler(evt);
172                         return;
173                     }
174                     break;
175             }
176         }
177
178         if (k) {
179             if (k->keyIdentifier() != "Enter") {
180                 HTMLElement::defaultEventHandler(evt);
181                 return;
182             }
183             evt->setDefaultHandled();
184             dispatchSimulatedClick(evt);
185             return;
186         }
187
188         String url = parseURL(getAttribute(hrefAttr));
189
190         String target = getAttribute(targetAttr);
191         if (e && e->button() == 1)
192             target = "_blank";
193
194         ASSERT(evt->target());
195         ASSERT(evt->target()->toNode());
196         if (evt->target()->toNode()->hasTagName(imgTag)) {
197             HTMLImageElement* img = static_cast<HTMLImageElement*>(evt->target()->toNode());
198             if (img && img->isServerMap()) {
199                 RenderImage* r = static_cast<RenderImage*>(img->renderer());
200                 if(r && e) {
201                     int absx, absy;
202                     r->absolutePosition(absx, absy);
203                     int x = e->pageX() - absx;
204                     int y = e->pageY() - absy;
205                     url += "?";
206                     url += DeprecatedString::number(x);
207                     url += ",";
208                     url += DeprecatedString::number(y);
209                 } else {
210                     evt->setDefaultHandled();
211                     HTMLElement::defaultEventHandler(evt);
212                     return;
213                 }
214             }
215         }
216
217         if (!evt->defaultPrevented() && document()->frame())
218             document()->frame()->loader()->urlSelected(document()->completeURL(url), target, evt);
219
220         evt->setDefaultHandled();
221     } else if (m_isLink && isContentEditable()) {
222     // This keeps track of the editable block that the selection was in (if it was in one) just before the link was clicked
223     // for the LiveWhenNotFocused editable link behavior
224         if (evt->type() == mousedownEvent && document()->frame() && document()->frame()->selectionController()) {
225             MouseEvent* e = static_cast<MouseEvent*>(evt);
226
227             m_rootEditableElementForSelectionOnMouseDown = document()->frame()->selectionController()->rootEditableElement();
228             m_wasShiftKeyDownOnMouseDown = e && e->shiftKey();
229         } else if (evt->type() == mouseoverEvent) {
230         // These are cleared on mouseover and not mouseout because their values are needed for drag events, but these happen
231         // after mouse out events.
232             m_rootEditableElementForSelectionOnMouseDown = 0;
233             m_wasShiftKeyDownOnMouseDown = false;
234         }
235     }
236
237     HTMLElement::defaultEventHandler(evt);
238 }
239
240 void HTMLAnchorElement::setActive(bool down, bool pause)
241 {
242     if (isContentEditable()) {
243         EditableLinkBehavior editableLinkBehavior = EditableLinkDefaultBehavior;
244         if (document()->frame() && document()->frame()->settings())
245             editableLinkBehavior = document()->frame()->settings()->editableLinkBehavior();
246             
247         switch(editableLinkBehavior) {
248             default:
249             case EditableLinkDefaultBehavior:
250             case EditableLinkAlwaysLive:
251                 break;
252
253             case EditableLinkNeverLive:
254                 return;
255
256             // Don't set the link to be active if the current selection is in the same editable block as
257             // this link
258             case EditableLinkLiveWhenNotFocused:
259                 if (down && document()->frame() && document()->frame()->selectionController() &&
260                     document()->frame()->selectionController()->rootEditableElement() == rootEditableElement())
261                     return;
262                 break;
263             
264             case EditableLinkOnlyLiveWithShiftKey:
265                 return;
266         }
267
268     }
269     
270     ContainerNode::setActive(down, pause);
271 }
272
273 void HTMLAnchorElement::parseMappedAttribute(MappedAttribute *attr)
274 {
275     if (attr->name() == hrefAttr) {
276         bool wasLink = m_isLink;
277         m_isLink = !attr->isNull();
278         if (wasLink != m_isLink)
279             setChanged();
280     } else if (attr->name() == nameAttr ||
281              attr->name() == titleAttr ||
282              attr->name() == relAttr) {
283         // Do nothing.
284     } else
285         HTMLElement::parseMappedAttribute(attr);
286 }
287
288 void HTMLAnchorElement::accessKeyAction(bool sendToAnyElement)
289 {
290     // send the mouse button events if the caller specified sendToAnyElement
291     dispatchSimulatedClick(0, sendToAnyElement);
292 }
293
294 bool HTMLAnchorElement::isURLAttribute(Attribute *attr) const
295 {
296     return attr->name() == hrefAttr;
297 }
298
299 String HTMLAnchorElement::accessKey() const
300 {
301     return getAttribute(accesskeyAttr);
302 }
303
304 void HTMLAnchorElement::setAccessKey(const String &value)
305 {
306     setAttribute(accesskeyAttr, value);
307 }
308
309 String HTMLAnchorElement::charset() const
310 {
311     return getAttribute(charsetAttr);
312 }
313
314 void HTMLAnchorElement::setCharset(const String &value)
315 {
316     setAttribute(charsetAttr, value);
317 }
318
319 String HTMLAnchorElement::coords() const
320 {
321     return getAttribute(coordsAttr);
322 }
323
324 void HTMLAnchorElement::setCoords(const String &value)
325 {
326     setAttribute(coordsAttr, value);
327 }
328
329 String HTMLAnchorElement::href() const
330 {
331     String href = getAttribute(hrefAttr);
332     if (href.isNull())
333         return href;
334     return document()->completeURL(href);
335 }
336
337 void HTMLAnchorElement::setHref(const String &value)
338 {
339     setAttribute(hrefAttr, value);
340 }
341
342 String HTMLAnchorElement::hreflang() const
343 {
344     return getAttribute(hreflangAttr);
345 }
346
347 void HTMLAnchorElement::setHreflang(const String &value)
348 {
349     setAttribute(hreflangAttr, value);
350 }
351
352 String HTMLAnchorElement::name() const
353 {
354     return getAttribute(nameAttr);
355 }
356
357 void HTMLAnchorElement::setName(const String &value)
358 {
359     setAttribute(nameAttr, value);
360 }
361
362 String HTMLAnchorElement::rel() const
363 {
364     return getAttribute(relAttr);
365 }
366
367 void HTMLAnchorElement::setRel(const String &value)
368 {
369     setAttribute(relAttr, value);
370 }
371
372 String HTMLAnchorElement::rev() const
373 {
374     return getAttribute(revAttr);
375 }
376
377 void HTMLAnchorElement::setRev(const String &value)
378 {
379     setAttribute(revAttr, value);
380 }
381
382 String HTMLAnchorElement::shape() const
383 {
384     return getAttribute(shapeAttr);
385 }
386
387 void HTMLAnchorElement::setShape(const String &value)
388 {
389     setAttribute(shapeAttr, value);
390 }
391
392 void HTMLAnchorElement::setTabIndex(int tabIndex)
393 {
394     setAttribute(tabindexAttr, String::number(tabIndex));
395 }
396
397 String HTMLAnchorElement::target() const
398 {
399     return getAttribute(targetAttr);
400 }
401
402 void HTMLAnchorElement::setTarget(const String &value)
403 {
404     setAttribute(targetAttr, value);
405 }
406
407 String HTMLAnchorElement::type() const
408 {
409     return getAttribute(typeAttr);
410 }
411
412 void HTMLAnchorElement::setType(const String &value)
413 {
414     setAttribute(typeAttr, value);
415 }
416
417 String HTMLAnchorElement::hash() const
418 {
419     return '#' + KURL(href().deprecatedString()).ref();
420 }
421
422 String HTMLAnchorElement::host() const
423 {
424     return KURL(href().deprecatedString()).host();
425 }
426
427 String HTMLAnchorElement::hostname() const
428 {
429     KURL url(href().deprecatedString());
430     if (url.port()==0)
431         return url.host();
432     else
433         return url.host() + ":" + String::number(url.port());
434 }
435
436 String HTMLAnchorElement::pathname() const
437 {
438     return KURL(href().deprecatedString()).path();
439 }
440
441 String HTMLAnchorElement::port() const
442 {
443     return DeprecatedString::number(KURL(href().deprecatedString()).port());
444 }
445
446 String HTMLAnchorElement::protocol() const
447 {
448     return KURL(href().deprecatedString()).protocol() + ":";
449 }
450
451 String HTMLAnchorElement::search() const
452 {
453     return KURL(href().deprecatedString()).query();
454 }
455
456 String HTMLAnchorElement::text() const
457 {
458     document()->updateLayoutIgnorePendingStylesheets();
459     return innerText();
460 }
461
462 bool HTMLAnchorElement::isLiveLink() const
463 {
464     if (!m_isLink)
465         return false;
466     if (!isContentEditable())
467         return true;
468     
469     EditableLinkBehavior editableLinkBehavior = EditableLinkDefaultBehavior;
470     if (document() && document()->frame() && document()->frame()->settings())
471         editableLinkBehavior = document()->frame()->settings()->editableLinkBehavior();
472         
473     switch(editableLinkBehavior) {
474         default:
475         case EditableLinkDefaultBehavior:
476         case EditableLinkAlwaysLive:
477             return true;
478
479         case EditableLinkNeverLive:
480             return false;
481
482         // Don't set the link to be live if the current selection is in the same editable block as
483         // this link or if the shift key is down
484         case EditableLinkLiveWhenNotFocused:
485             return m_wasShiftKeyDownOnMouseDown || m_rootEditableElementForSelectionOnMouseDown != rootEditableElement();
486             
487         case EditableLinkOnlyLiveWithShiftKey:
488             return m_wasShiftKeyDownOnMouseDown;
489     }
490     // not reached
491     ASSERT(0);
492     return false;
493 }
494
495 }