1/*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2006 James G. Speth (speth@end.com)
4 * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#import "config.h"
29#import "DOMInternal.h" // import first to make the private/public trick work
30#import "DOM.h"
31
32#import "CachedImage.h"
33#import "DOMElementInternal.h"
34#import "DOMHTMLCanvasElement.h"
35#import "DOMHTMLTableCellElementInternal.h"
36#import "DOMNodeInternal.h"
37#import "DOMPrivate.h"
38#import "DOMRangeInternal.h"
39#import "Font.h"
40#import "Frame.h"
41#import "FrameSnapshottingMac.h"
42#import "HTMLElement.h"
43#import "HTMLNames.h"
44#import "HTMLParserIdioms.h"
45#import "HTMLTableCellElement.h"
46#import "Image.h"
47#import "JSNode.h"
48#import "NodeFilter.h"
49#import "Range.h"
50#import "RenderImage.h"
51#import "ScriptController.h"
52#import "WebScriptObjectPrivate.h"
53#import <JavaScriptCore/APICast.h>
54#import <wtf/HashMap.h>
55
56using namespace JSC;
57using namespace WebCore;
58
59// FIXME: Would be nice to break this up into separate files to match how other WebKit
60// code is organized.
61
62//------------------------------------------------------------------------------------------
63// DOMNode
64
65namespace WebCore {
66
67typedef HashMap<const QualifiedName::QualifiedNameImpl*, Class> ObjCClassMap;
68static ObjCClassMap* elementClassMap;
69
70static void addElementClass(const QualifiedName& tag, Class objCClass)
71{
72    elementClassMap->set(tag.impl(), objCClass);
73}
74
75static void createElementClassMap()
76{
77    // Create the table.
78    elementClassMap = new ObjCClassMap;
79
80    // FIXME: Reflect marquee once the API has been determined.
81
82    // Populate it with HTML and SVG element classes.
83    addElementClass(HTMLNames::aTag, [DOMHTMLAnchorElement class]);
84    addElementClass(HTMLNames::appletTag, [DOMHTMLAppletElement class]);
85    addElementClass(HTMLNames::areaTag, [DOMHTMLAreaElement class]);
86    addElementClass(HTMLNames::baseTag, [DOMHTMLBaseElement class]);
87    addElementClass(HTMLNames::basefontTag, [DOMHTMLBaseFontElement class]);
88    addElementClass(HTMLNames::bodyTag, [DOMHTMLBodyElement class]);
89    addElementClass(HTMLNames::brTag, [DOMHTMLBRElement class]);
90    addElementClass(HTMLNames::buttonTag, [DOMHTMLButtonElement class]);
91    addElementClass(HTMLNames::canvasTag, [DOMHTMLCanvasElement class]);
92    addElementClass(HTMLNames::captionTag, [DOMHTMLTableCaptionElement class]);
93    addElementClass(HTMLNames::colTag, [DOMHTMLTableColElement class]);
94    addElementClass(HTMLNames::colgroupTag, [DOMHTMLTableColElement class]);
95    addElementClass(HTMLNames::delTag, [DOMHTMLModElement class]);
96    addElementClass(HTMLNames::dirTag, [DOMHTMLDirectoryElement class]);
97    addElementClass(HTMLNames::divTag, [DOMHTMLDivElement class]);
98    addElementClass(HTMLNames::dlTag, [DOMHTMLDListElement class]);
99    addElementClass(HTMLNames::embedTag, [DOMHTMLEmbedElement class]);
100    addElementClass(HTMLNames::fieldsetTag, [DOMHTMLFieldSetElement class]);
101    addElementClass(HTMLNames::fontTag, [DOMHTMLFontElement class]);
102    addElementClass(HTMLNames::formTag, [DOMHTMLFormElement class]);
103    addElementClass(HTMLNames::frameTag, [DOMHTMLFrameElement class]);
104    addElementClass(HTMLNames::framesetTag, [DOMHTMLFrameSetElement class]);
105    addElementClass(HTMLNames::h1Tag, [DOMHTMLHeadingElement class]);
106    addElementClass(HTMLNames::h2Tag, [DOMHTMLHeadingElement class]);
107    addElementClass(HTMLNames::h3Tag, [DOMHTMLHeadingElement class]);
108    addElementClass(HTMLNames::h4Tag, [DOMHTMLHeadingElement class]);
109    addElementClass(HTMLNames::h5Tag, [DOMHTMLHeadingElement class]);
110    addElementClass(HTMLNames::h6Tag, [DOMHTMLHeadingElement class]);
111    addElementClass(HTMLNames::headTag, [DOMHTMLHeadElement class]);
112    addElementClass(HTMLNames::hrTag, [DOMHTMLHRElement class]);
113    addElementClass(HTMLNames::htmlTag, [DOMHTMLHtmlElement class]);
114    addElementClass(HTMLNames::iframeTag, [DOMHTMLIFrameElement class]);
115    addElementClass(HTMLNames::imgTag, [DOMHTMLImageElement class]);
116    addElementClass(HTMLNames::inputTag, [DOMHTMLInputElement class]);
117    addElementClass(HTMLNames::insTag, [DOMHTMLModElement class]);
118    addElementClass(HTMLNames::labelTag, [DOMHTMLLabelElement class]);
119    addElementClass(HTMLNames::legendTag, [DOMHTMLLegendElement class]);
120    addElementClass(HTMLNames::liTag, [DOMHTMLLIElement class]);
121    addElementClass(HTMLNames::linkTag, [DOMHTMLLinkElement class]);
122    addElementClass(HTMLNames::listingTag, [DOMHTMLPreElement class]);
123    addElementClass(HTMLNames::mapTag, [DOMHTMLMapElement class]);
124    addElementClass(HTMLNames::marqueeTag, [DOMHTMLMarqueeElement class]);
125    addElementClass(HTMLNames::menuTag, [DOMHTMLMenuElement class]);
126    addElementClass(HTMLNames::metaTag, [DOMHTMLMetaElement class]);
127    addElementClass(HTMLNames::objectTag, [DOMHTMLObjectElement class]);
128    addElementClass(HTMLNames::olTag, [DOMHTMLOListElement class]);
129    addElementClass(HTMLNames::optgroupTag, [DOMHTMLOptGroupElement class]);
130    addElementClass(HTMLNames::optionTag, [DOMHTMLOptionElement class]);
131    addElementClass(HTMLNames::pTag, [DOMHTMLParagraphElement class]);
132    addElementClass(HTMLNames::paramTag, [DOMHTMLParamElement class]);
133    addElementClass(HTMLNames::preTag, [DOMHTMLPreElement class]);
134    addElementClass(HTMLNames::qTag, [DOMHTMLQuoteElement class]);
135    addElementClass(HTMLNames::scriptTag, [DOMHTMLScriptElement class]);
136    addElementClass(HTMLNames::selectTag, [DOMHTMLSelectElement class]);
137    addElementClass(HTMLNames::styleTag, [DOMHTMLStyleElement class]);
138    addElementClass(HTMLNames::tableTag, [DOMHTMLTableElement class]);
139    addElementClass(HTMLNames::tbodyTag, [DOMHTMLTableSectionElement class]);
140    addElementClass(HTMLNames::tdTag, [DOMHTMLTableCellElement class]);
141    addElementClass(HTMLNames::textareaTag, [DOMHTMLTextAreaElement class]);
142    addElementClass(HTMLNames::tfootTag, [DOMHTMLTableSectionElement class]);
143    addElementClass(HTMLNames::thTag, [DOMHTMLTableCellElement class]);
144    addElementClass(HTMLNames::theadTag, [DOMHTMLTableSectionElement class]);
145    addElementClass(HTMLNames::titleTag, [DOMHTMLTitleElement class]);
146    addElementClass(HTMLNames::trTag, [DOMHTMLTableRowElement class]);
147    addElementClass(HTMLNames::ulTag, [DOMHTMLUListElement class]);
148    addElementClass(HTMLNames::xmpTag, [DOMHTMLPreElement class]);
149}
150
151static Class lookupElementClass(const QualifiedName& tag)
152{
153    // Do a special lookup to ignore element prefixes
154    if (tag.hasPrefix())
155        return elementClassMap->get(QualifiedName(nullAtom, tag.localName(), tag.namespaceURI()).impl());
156
157    return elementClassMap->get(tag.impl());
158}
159
160static Class elementClass(const QualifiedName& tag, Class defaultClass)
161{
162    if (!elementClassMap)
163        createElementClassMap();
164    Class objcClass = lookupElementClass(tag);
165    if (!objcClass)
166        objcClass = defaultClass;
167    return objcClass;
168}
169
170static NSArray *kit(const Vector<IntRect>& rects)
171{
172    size_t size = rects.size();
173    NSMutableArray *array = [NSMutableArray arrayWithCapacity:size];
174    for (size_t i = 0; i < size; ++i)
175        [array addObject:[NSValue valueWithRect:rects[i]]];
176    return array;
177}
178
179} // namespace WebCore
180
181@implementation DOMNode (WebCoreInternal)
182
183- (NSString *)description
184{
185    if (!_internal)
186        return [NSString stringWithFormat:@"<%@: null>", [[self class] description]];
187
188    NSString *value = [self nodeValue];
189    if (value)
190        return [NSString stringWithFormat:@"<%@ [%@]: %p '%@'>",
191            [[self class] description], [self nodeName], _internal, value];
192
193    return [NSString stringWithFormat:@"<%@ [%@]: %p>", [[self class] description], [self nodeName], _internal];
194}
195
196- (JSC::Bindings::RootObject*)_rootObject
197{
198    WebCore::Frame* frame = core(self)->document()->frame();
199    if (!frame)
200        return 0;
201    return frame->script()->bindingRootObject();
202}
203
204@end
205
206Class kitClass(WebCore::Node* impl)
207{
208    switch (impl->nodeType()) {
209        case WebCore::Node::ELEMENT_NODE:
210            if (impl->isHTMLElement())
211                return WebCore::elementClass(toHTMLElement(impl)->tagQName(), [DOMHTMLElement class]);
212            return [DOMElement class];
213        case WebCore::Node::ATTRIBUTE_NODE:
214            return [DOMAttr class];
215        case WebCore::Node::TEXT_NODE:
216            return [DOMText class];
217        case WebCore::Node::CDATA_SECTION_NODE:
218            return [DOMCDATASection class];
219        case WebCore::Node::ENTITY_REFERENCE_NODE:
220            return [DOMEntityReference class];
221        case WebCore::Node::ENTITY_NODE:
222            return [DOMEntity class];
223        case WebCore::Node::PROCESSING_INSTRUCTION_NODE:
224            return [DOMProcessingInstruction class];
225        case WebCore::Node::COMMENT_NODE:
226            return [DOMComment class];
227        case WebCore::Node::DOCUMENT_NODE:
228            if (static_cast<WebCore::Document*>(impl)->isHTMLDocument())
229                return [DOMHTMLDocument class];
230            return [DOMDocument class];
231        case WebCore::Node::DOCUMENT_TYPE_NODE:
232            return [DOMDocumentType class];
233        case WebCore::Node::DOCUMENT_FRAGMENT_NODE:
234            return [DOMDocumentFragment class];
235        case WebCore::Node::NOTATION_NODE:
236            return [DOMNotation class];
237        case WebCore::Node::XPATH_NAMESPACE_NODE:
238            // FIXME: Create an XPath objective C wrapper
239            // See http://bugs.webkit.org/show_bug.cgi?id=8755
240            return nil;
241    }
242    ASSERT_NOT_REACHED();
243    return nil;
244}
245
246id <DOMEventTarget> kit(WebCore::EventTarget* eventTarget)
247{
248    if (!eventTarget)
249        return nil;
250
251    if (WebCore::Node* node = eventTarget->toNode())
252        return kit(node);
253
254    // We don't have an ObjC binding for XMLHttpRequest.
255
256    return nil;
257}
258
259@implementation DOMNode (DOMNodeExtensions)
260
261- (NSRect)boundingBox
262{
263    // FIXME: Could we move this function to WebCore::Node and autogenerate?
264    core(self)->document()->updateLayoutIgnorePendingStylesheets();
265    WebCore::RenderObject* renderer = core(self)->renderer();
266    if (!renderer)
267        return NSZeroRect;
268    return renderer->absoluteBoundingBoxRect();
269}
270
271- (NSArray *)lineBoxRects
272{
273    return [self textRects];
274}
275
276@end
277
278@implementation DOMNode (DOMNodeExtensionsPendingPublic)
279
280- (NSImage *)renderedImage
281{
282    // FIXME: Could we move this function to WebCore::Node and autogenerate?
283    WebCore::Node* node = core(self);
284    WebCore::Frame* frame = node->document()->frame();
285    if (!frame)
286        return nil;
287    return frame->nodeImage(node).get();
288}
289
290- (NSArray *)textRects
291{
292    core(self)->document()->updateLayoutIgnorePendingStylesheets();
293    if (!core(self)->renderer())
294        return nil;
295    Vector<WebCore::IntRect> rects;
296    core(self)->textRects(rects);
297    return kit(rects);
298}
299
300@end
301
302@implementation DOMNode (WebPrivate)
303
304+ (id)_nodeFromJSWrapper:(JSObjectRef)jsWrapper
305{
306    JSObject* object = toJS(jsWrapper);
307
308    if (!object->inherits(&JSNode::s_info))
309        return nil;
310
311    WebCore::Node* node = jsCast<JSNode*>(object)->impl();
312    return kit(node);
313}
314
315@end
316
317@implementation DOMRange (DOMRangeExtensions)
318
319- (NSRect)boundingBox
320{
321    // FIXME: The call to updateLayoutIgnorePendingStylesheets should be moved into WebCore::Range.
322    core(self)->ownerDocument()->updateLayoutIgnorePendingStylesheets();
323    return core(self)->boundingBox();
324}
325
326- (NSImage *)renderedImageForcingBlackText:(BOOL)forceBlackText
327{
328    WebCore::Range* range = core(self);
329    WebCore::Frame* frame = range->ownerDocument()->frame();
330    if (!frame)
331        return nil;
332
333    return WebCore::rangeImage(frame, range, forceBlackText);
334}
335
336- (NSArray *)textRects
337{
338    // FIXME: The call to updateLayoutIgnorePendingStylesheets should be moved into WebCore::Range.
339    Vector<WebCore::IntRect> rects;
340    core(self)->ownerDocument()->updateLayoutIgnorePendingStylesheets();
341    core(self)->textRects(rects);
342    return kit(rects);
343}
344
345- (NSArray *)lineBoxRects
346{
347    // FIXME: Remove this once all clients stop using it and we drop Leopard support.
348    return [self textRects];
349}
350
351@end
352
353//------------------------------------------------------------------------------------------
354// DOMElement
355
356@implementation DOMElement (DOMElementAppKitExtensions)
357
358- (NSImage*)image
359{
360    // FIXME: Could we move this function to WebCore::Node and autogenerate?
361    WebCore::RenderObject* renderer = core(self)->renderer();
362    if (!renderer || !renderer->isRenderImage())
363        return nil;
364    WebCore::CachedImage* cachedImage = static_cast<WebCore::RenderImage*>(renderer)->cachedImage();
365    if (!cachedImage || cachedImage->errorOccurred())
366        return nil;
367    return cachedImage->imageForRenderer(renderer)->getNSImage();
368}
369
370@end
371
372@implementation DOMElement (WebPrivate)
373
374- (NSFont *)_font
375{
376    // FIXME: Could we move this function to WebCore::Element and autogenerate?
377    WebCore::RenderObject* renderer = core(self)->renderer();
378    if (!renderer)
379        return nil;
380    return renderer->style()->font().primaryFont()->getNSFont();
381}
382
383- (NSData *)_imageTIFFRepresentation
384{
385    // FIXME: Could we move this function to WebCore::Element and autogenerate?
386    WebCore::RenderObject* renderer = core(self)->renderer();
387    if (!renderer || !renderer->isRenderImage())
388        return nil;
389    WebCore::CachedImage* cachedImage = toRenderImage(renderer)->cachedImage();
390    if (!cachedImage || cachedImage->errorOccurred())
391        return nil;
392    return (NSData *)cachedImage->imageForRenderer(renderer)->getTIFFRepresentation();
393}
394
395- (NSURL *)_getURLAttribute:(NSString *)name
396{
397    // FIXME: Could we move this function to WebCore::Element and autogenerate?
398    ASSERT(name);
399    WebCore::Element* element = core(self);
400    ASSERT(element);
401    return element->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(element->getAttribute(name)));
402}
403
404- (BOOL)isFocused
405{
406    // FIXME: Could we move this function to WebCore::Element and autogenerate?
407    WebCore::Element* element = core(self);
408    return element->document()->focusedElement() == element;
409}
410
411@end
412
413//------------------------------------------------------------------------------------------
414// DOMRange
415
416@implementation DOMRange (WebPrivate)
417
418- (NSString *)description
419{
420    if (!_internal)
421        return @"<DOMRange: null>";
422    return [NSString stringWithFormat:@"<DOMRange: %@ %d %@ %d>",
423               [self startContainer], [self startOffset], [self endContainer], [self endOffset]];
424}
425
426// FIXME: This should be removed as soon as all internal Apple uses of it have been replaced with
427// calls to the public method - (NSString *)text.
428- (NSString *)_text
429{
430    return [self text];
431}
432
433@end
434
435//------------------------------------------------------------------------------------------
436// DOMRGBColor
437
438@implementation DOMRGBColor (WebPrivate)
439
440// FIXME: This should be removed as soon as all internal Apple uses of it have been replaced with
441// calls to the public method - (NSColor *)color.
442- (NSColor *)_color
443{
444    return [self color];
445}
446
447@end
448
449
450@implementation DOMHTMLTableCellElement (WebPrivate)
451
452- (DOMHTMLTableCellElement *)_cellAbove
453{
454    return kit(core(self)->cellAbove());
455}
456
457@end
458
459//------------------------------------------------------------------------------------------
460// DOMNodeFilter
461
462DOMNodeFilter *kit(WebCore::NodeFilter* impl)
463{
464    if (!impl)
465        return nil;
466
467    if (DOMNodeFilter *wrapper = getDOMWrapper(impl))
468        return [[wrapper retain] autorelease];
469
470    DOMNodeFilter *wrapper = [[DOMNodeFilter alloc] _init];
471    wrapper->_internal = reinterpret_cast<DOMObjectInternal*>(impl);
472    impl->ref();
473    addDOMWrapper(wrapper, impl);
474    return [wrapper autorelease];
475}
476
477WebCore::NodeFilter* core(DOMNodeFilter *wrapper)
478{
479    return wrapper ? reinterpret_cast<WebCore::NodeFilter*>(wrapper->_internal) : 0;
480}
481
482@implementation DOMNodeFilter
483
484- (void)dealloc
485{
486    if (_internal)
487        reinterpret_cast<WebCore::NodeFilter*>(_internal)->deref();
488    [super dealloc];
489}
490
491- (void)finalize
492{
493    if (_internal)
494        reinterpret_cast<WebCore::NodeFilter*>(_internal)->deref();
495    [super finalize];
496}
497
498- (short)acceptNode:(DOMNode *)node
499{
500    return core(self)->acceptNode(core(node));
501}
502
503@end
504