2006-10-19 Steve Falkenburg <sfalken@apple.com>
[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 #include "config.h"
26 #include "HTMLAnchorElement.h"
27
28 #include "Document.h"
29 #include "Event.h"
30 #include "EventNames.h"
31 #include "Frame.h"
32 #include "HTMLImageElement.h"
33 #include "HTMLNames.h"
34 #include "KeyboardEvent.h"
35 #include "MouseEvent.h"
36 #include "MutationEvent.h"
37 #include "RenderFlow.h"
38 #include "RenderImage.h"
39 #include "SelectionController.h"
40 #include "Settings.h"
41 #include "UIEvent.h"
42 #include "csshelper.h"
43
44 namespace WebCore {
45
46 using namespace HTMLNames;
47 using namespace EventNames;
48
49 HTMLAnchorElement::HTMLAnchorElement(Document* doc)
50     : HTMLElement(aTag, doc)
51     , m_rootEditableElementForSelectionOnMouseDown(0)
52 {
53 }
54
55 HTMLAnchorElement::HTMLAnchorElement(const QualifiedName& tagName, Document* doc)
56     : HTMLElement(tagName, doc)
57     , m_rootEditableElementForSelectionOnMouseDown(0)
58 {
59 }
60
61 HTMLAnchorElement::~HTMLAnchorElement()
62 {
63 }
64
65 bool HTMLAnchorElement::supportsFocus() const
66 {
67     if (isContentEditable())
68         return HTMLElement::supportsFocus();
69     return isFocusable() || (m_isLink && document() && !document()->haveStylesheetsLoaded());
70 }
71
72 bool HTMLAnchorElement::isFocusable() const
73 {
74     if (isContentEditable())
75         return HTMLElement::isFocusable();
76
77     // FIXME: Even if we are not visible, we might have a child that is visible.
78     // Dave wants to fix that some day with a "has visible content" flag or the like.
79     if (!(m_isLink && renderer() && renderer()->style()->visibility() == VISIBLE))
80         return false;
81
82     // Before calling absoluteRects, check for the common case where the renderer
83     // or one of the continuations is non-empty, since this is a faster check and
84     // almost always returns true.
85     for (RenderObject* r = renderer(); r; r = r->continuation())
86         if (r->width() > 0 && r->height() > 0)
87             return true;
88
89     Vector<IntRect> rects;
90     int x, y;
91     renderer()->absolutePosition(x, y);
92     renderer()->absoluteRects(rects, x, y);
93     size_t n = rects.size();
94     for (size_t i = 0; i < n; ++i)
95         if (!rects[i].isEmpty())
96             return true;
97
98     return false;
99 }
100
101 bool HTMLAnchorElement::isMouseFocusable() const
102 {
103     return false;
104 }
105
106 bool HTMLAnchorElement::isKeyboardFocusable() const
107 {
108     if (!isFocusable())
109         return false;
110     
111     if (!document()->frame())
112         return false;
113
114     return document()->frame()->tabsToLinks();
115 }
116
117 void HTMLAnchorElement::defaultEventHandler(Event *evt)
118 {
119     // React on clicks and on keypresses.
120     // Don't make this KEYUP_EVENT again, it makes khtml follow links it shouldn't,
121     // when pressing Enter in the combo.
122     if (m_isLink && (evt->type() == clickEvent || (evt->type() == keydownEvent && m_focused))) {
123         MouseEvent* e = 0;
124         if (evt->type() == clickEvent)
125             e = static_cast<MouseEvent*>(evt);
126
127         KeyboardEvent* k = 0;
128         if (evt->type() == keydownEvent)
129             k = static_cast<KeyboardEvent*>(evt);
130
131         if (e && e->button() == 2) {
132             HTMLElement::defaultEventHandler(evt);
133             return;
134         }
135
136         // If the link is editable, then we need to check the settings to see whether or not to follow the link
137         if (isContentEditable()) {
138             Settings::EditableLinkBehavior editableLinkBehavior = Settings::EditableLinkDefaultBehavior;
139             if (document()->frame() && document()->frame()->settings())
140                 editableLinkBehavior = document()->frame()->settings()->editableLinkBehavior();
141                 
142             switch(editableLinkBehavior) {
143                 // Always follow the link (Safari 2.0 behavior)
144                 default:
145                 case Settings::EditableLinkDefaultBehavior:
146                 case Settings::EditableLinkAlwaysLive:
147                     break;
148                     
149                 // If the selection prior to clicking on this link resided in the same editable block as this link,
150                 // and the shift key isn't pressed, we don't want to follow the link
151                 case Settings::EditableLinkLiveWhenNotFocused:
152                     if (e && !e->shiftKey() && m_rootEditableElementForSelectionOnMouseDown == rootEditableElement()) {
153                         HTMLElement::defaultEventHandler(evt);
154                         return;
155                     }
156                     break;
157                 
158                 // Only follow the link if the shift key is down (WinIE/Firefox behavior)
159                 case Settings::EditableLinkOnlyLiveWithShiftKey:
160                     if (e && !e->shiftKey()) {
161                         HTMLElement::defaultEventHandler(evt);
162                         return;
163                     }
164                     break;
165             }
166         }
167
168         if (k) {
169             if (k->keyIdentifier() != "Enter") {
170                 HTMLElement::defaultEventHandler(evt);
171                 return;
172             }
173             if (k->keyEvent()) {
174                 evt->setDefaultHandled();
175                 click(false);
176                 return;
177             }
178         }
179
180         DeprecatedString url = parseURL(getAttribute(hrefAttr)).deprecatedString();
181         String utarget = getAttribute(targetAttr);
182
183         if (e && e->button() == 1)
184             utarget = "_blank";
185
186         if (evt->target()->hasTagName(imgTag)) {
187             HTMLImageElement* img = static_cast<HTMLImageElement*>(evt->target());
188             if (img && img->isServerMap()) {
189                 RenderImage* r = static_cast<RenderImage*>(img->renderer());
190                 if(r && e) {
191                     int absx, absy;
192                     r->absolutePosition(absx, absy);
193                     int x = e->pageX() - absx;
194                     int y = e->pageY() - absy;
195                     url += "?";
196                     url += DeprecatedString::number(x);
197                     url += ",";
198                     url += DeprecatedString::number(y);
199                 } else {
200                     evt->setDefaultHandled();
201                     HTMLElement::defaultEventHandler(evt);
202                     return;
203                 }
204             }
205         }
206
207         if (!evt->defaultPrevented() && document()->frame())
208             document()->frame()->urlSelected(url, utarget, evt);
209
210         evt->setDefaultHandled();
211     } else if (m_isLink && isContentEditable()) {
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 (evt->type() == mousedownEvent && document()->frame() && document()->frame()->selectionController())
215             m_rootEditableElementForSelectionOnMouseDown = document()->frame()->selectionController()->rootEditableElement();
216         else if (evt->type() == mouseoutEvent)
217             m_rootEditableElementForSelectionOnMouseDown = 0;
218     }
219
220     HTMLElement::defaultEventHandler(evt);
221 }
222
223 void HTMLAnchorElement::setActive(bool down, bool pause)
224 {
225     if (isContentEditable()) {
226         Settings::EditableLinkBehavior editableLinkBehavior = Settings::EditableLinkDefaultBehavior;
227         if (document()->frame() && document()->frame()->settings())
228             editableLinkBehavior = document()->frame()->settings()->editableLinkBehavior();
229             
230         switch(editableLinkBehavior) {
231             default:
232             case Settings::EditableLinkDefaultBehavior:
233             case Settings::EditableLinkAlwaysLive:
234                 break;
235                 
236             // Don't set the link to be active if the current selection is in the same editable block as
237             // this link
238             case Settings::EditableLinkLiveWhenNotFocused:
239                 if (down && document()->frame() && document()->frame()->selectionController() &&
240                     document()->frame()->selectionController()->rootEditableElement() == rootEditableElement())
241                     return;
242                 break;
243             
244             case Settings::EditableLinkOnlyLiveWithShiftKey:
245                 return;
246         }
247
248     }
249     
250     ContainerNode::setActive(down, pause);
251 }
252
253 void HTMLAnchorElement::parseMappedAttribute(MappedAttribute *attr)
254 {
255     if (attr->name() == hrefAttr) {
256         bool wasLink = m_isLink;
257         m_isLink = !attr->isNull();
258         if (wasLink != m_isLink)
259             setChanged();
260     } else if (attr->name() == nameAttr ||
261              attr->name() == titleAttr ||
262              attr->name() == relAttr) {
263         // Do nothing.
264     } else
265         HTMLElement::parseMappedAttribute(attr);
266 }
267
268 void HTMLAnchorElement::accessKeyAction(bool sendToAnyElement)
269 {
270     // send the mouse button events iff the
271     // caller specified sendToAnyElement
272     click(sendToAnyElement);
273 }
274
275 bool HTMLAnchorElement::isURLAttribute(Attribute *attr) const
276 {
277     return attr->name() == hrefAttr;
278 }
279
280 String HTMLAnchorElement::accessKey() const
281 {
282     return getAttribute(accesskeyAttr);
283 }
284
285 void HTMLAnchorElement::setAccessKey(const String &value)
286 {
287     setAttribute(accesskeyAttr, value);
288 }
289
290 String HTMLAnchorElement::charset() const
291 {
292     return getAttribute(charsetAttr);
293 }
294
295 void HTMLAnchorElement::setCharset(const String &value)
296 {
297     setAttribute(charsetAttr, value);
298 }
299
300 String HTMLAnchorElement::coords() const
301 {
302     return getAttribute(coordsAttr);
303 }
304
305 void HTMLAnchorElement::setCoords(const String &value)
306 {
307     setAttribute(coordsAttr, value);
308 }
309
310 String HTMLAnchorElement::href() const
311 {
312     String href = getAttribute(hrefAttr);
313     if (href.isNull())
314         return href;
315     return document()->completeURL(href);
316 }
317
318 void HTMLAnchorElement::setHref(const String &value)
319 {
320     setAttribute(hrefAttr, value);
321 }
322
323 String HTMLAnchorElement::hreflang() const
324 {
325     return getAttribute(hreflangAttr);
326 }
327
328 void HTMLAnchorElement::setHreflang(const String &value)
329 {
330     setAttribute(hreflangAttr, value);
331 }
332
333 String HTMLAnchorElement::name() const
334 {
335     return getAttribute(nameAttr);
336 }
337
338 void HTMLAnchorElement::setName(const String &value)
339 {
340     setAttribute(nameAttr, value);
341 }
342
343 String HTMLAnchorElement::rel() const
344 {
345     return getAttribute(relAttr);
346 }
347
348 void HTMLAnchorElement::setRel(const String &value)
349 {
350     setAttribute(relAttr, value);
351 }
352
353 String HTMLAnchorElement::rev() const
354 {
355     return getAttribute(revAttr);
356 }
357
358 void HTMLAnchorElement::setRev(const String &value)
359 {
360     setAttribute(revAttr, value);
361 }
362
363 String HTMLAnchorElement::shape() const
364 {
365     return getAttribute(shapeAttr);
366 }
367
368 void HTMLAnchorElement::setShape(const String &value)
369 {
370     setAttribute(shapeAttr, value);
371 }
372
373 int HTMLAnchorElement::tabIndex() const
374 {
375     return getAttribute(tabindexAttr).toInt();
376 }
377
378 void HTMLAnchorElement::setTabIndex(int tabIndex)
379 {
380     setAttribute(tabindexAttr, String::number(tabIndex));
381 }
382
383 String HTMLAnchorElement::target() const
384 {
385     return getAttribute(targetAttr);
386 }
387
388 void HTMLAnchorElement::setTarget(const String &value)
389 {
390     setAttribute(targetAttr, value);
391 }
392
393 String HTMLAnchorElement::type() const
394 {
395     return getAttribute(typeAttr);
396 }
397
398 void HTMLAnchorElement::setType(const String &value)
399 {
400     setAttribute(typeAttr, value);
401 }
402
403 String HTMLAnchorElement::hash() const
404 {
405     return '#' + KURL(href().deprecatedString()).ref();
406 }
407
408 String HTMLAnchorElement::host() const
409 {
410     return KURL(href().deprecatedString()).host();
411 }
412
413 String HTMLAnchorElement::hostname() const
414 {
415     KURL url(href().deprecatedString());
416     if (url.port()==0)
417         return url.host();
418     else
419         return url.host() + ":" + String::number(url.port());
420 }
421
422 String HTMLAnchorElement::pathname() const
423 {
424     return KURL(href().deprecatedString()).path();
425 }
426
427 String HTMLAnchorElement::port() const
428 {
429     return DeprecatedString::number(KURL(href().deprecatedString()).port());
430 }
431
432 String HTMLAnchorElement::protocol() const
433 {
434     return KURL(href().deprecatedString()).protocol() + ":";
435 }
436
437 String HTMLAnchorElement::search() const
438 {
439     return KURL(href().deprecatedString()).query();
440 }
441
442 String HTMLAnchorElement::text() const
443 {
444     document()->updateLayoutIgnorePendingStylesheets();
445     return innerText();
446 }
447
448
449 }