2006-12-18 Ada Chan <adachan@apple.com>
[WebKit-https.git] / WebCore / page / mac / FrameMac.mm
1 /*
2  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #import "config.h"
28 #import "FrameMac.h"
29
30 #import "AXObjectCache.h"
31 #import "BeforeUnloadEvent.h"
32 #import "BlockExceptions.h"
33 #import "Chrome.h"
34 #import "Cache.h"
35 #import "ClipboardEvent.h"
36 #import "ClipboardMac.h"
37 #import "Cursor.h"
38 #import "DOMInternal.h"
39 #import "DocumentLoader.h"
40 #import "EditCommand.h"
41 #import "EditorClient.h"
42 #import "Event.h"
43 #import "EventNames.h"
44 #import "FloatRect.h"
45 #import "FontData.h"
46 #import "FoundationExtras.h"
47 #import "FrameLoadRequest.h"
48 #import "FrameLoader.h"
49 #import "FrameLoaderClient.h"
50 #import "FrameLoaderTypes.h"
51 #import "FramePrivate.h"
52 #import "FrameView.h"
53 #import "GraphicsContext.h"
54 #import "HTMLDocument.h"
55 #import "HTMLFormElement.h"
56 #import "HTMLGenericFormElement.h"
57 #import "HTMLInputElement.h"
58 #import "HTMLNames.h"
59 #import "HTMLTableCellElement.h"
60 #import "HitTestRequest.h"
61 #import "HitTestResult.h"
62 #import "KeyboardEvent.h"
63 #import "Logging.h"
64 #import "MouseEventWithHitTestResults.h"
65 #import "Page.h"
66 #import "PlatformKeyboardEvent.h"
67 #import "PlatformScrollBar.h"
68 #import "PlatformWheelEvent.h"
69 #import "Plugin.h"
70 #import "RegularExpression.h"
71 #import "RenderImage.h"
72 #import "RenderListItem.h"
73 #import "RenderPart.h"
74 #import "RenderTableCell.h"
75 #import "RenderTheme.h"
76 #import "RenderView.h"
77 #import "ResourceHandle.h"
78 #import "SystemTime.h"
79 #import "TextResourceDecoder.h"
80 #import "WebCoreFrameBridge.h"
81 #import "WebCoreSystemInterface.h"
82 #import "WebCoreViewFactory.h"
83 #import "WebDashboardRegion.h"
84 #import "WebScriptObjectPrivate.h"
85 #import "csshelper.h"
86 #import "kjs_proxy.h"
87 #import "kjs_window.h"
88 #import "visible_units.h"
89 #import <Carbon/Carbon.h>
90 #import <JavaScriptCore/NP_jsobject.h>
91 #import <JavaScriptCore/npruntime_impl.h>
92
93 #undef _webcore_TIMING
94
95 @interface NSObject (WebPlugIn)
96 - (id)objectForWebScript;
97 - (NPObject *)createPluginScriptableObject;
98 @end
99
100 using namespace std;
101 using namespace KJS::Bindings;
102
103 using KJS::JSLock;
104
105 namespace WebCore {
106
107 using namespace EventNames;
108 using namespace HTMLNames;
109
110 static SEL selectorForKeyEvent(KeyboardEvent* event)
111 {
112     // FIXME: This helper function is for the auto-fill code so the bridge can pass a selector to the form delegate.  
113     // Eventually, we should move all of the auto-fill code down to WebKit and remove the need for this function by
114     // not relying on the selector in the new implementation.
115     String key = event->keyIdentifier();
116     if (key == "Up")
117         return @selector(moveUp:);
118     if (key == "Down")
119         return @selector(moveDown:);
120     if (key == "U+00001B")
121         return @selector(cancel:);
122     if (key == "U+000009") {
123         if (event->shiftKey())
124             return @selector(insertBacktab:);
125         return @selector(insertTab:);
126     }
127     if (key == "Enter")
128         return @selector(insertNewline:);
129     return 0;
130 }
131
132 FrameMac::FrameMac(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient)
133     : Frame(page, ownerElement, frameLoaderClient)
134     , _bridge(nil)
135     , _bindingRoot(0)
136     , _windowScriptObject(0)
137     , _windowScriptNPObject(0)
138 {
139 }
140
141 FrameMac::~FrameMac()
142 {
143     setView(0);
144     loader()->clearRecordedFormValues();    
145     
146     [_bridge clearFrame];
147     HardRelease(_bridge);
148     _bridge = nil;
149
150     loader()->cancelAndClear();
151 }
152
153 // Either get cached regexp or build one that matches any of the labels.
154 // The regexp we build is of the form:  (STR1|STR2|STRN)
155 RegularExpression *regExpForLabels(NSArray *labels)
156 {
157     // All the ObjC calls in this method are simple array and string
158     // calls which we can assume do not raise exceptions
159
160
161     // Parallel arrays that we use to cache regExps.  In practice the number of expressions
162     // that the app will use is equal to the number of locales is used in searching.
163     static const unsigned int regExpCacheSize = 4;
164     static NSMutableArray *regExpLabels = nil;
165     static Vector<RegularExpression*> regExps;
166     static RegularExpression wordRegExp = RegularExpression("\\w");
167
168     RegularExpression *result;
169     if (!regExpLabels)
170         regExpLabels = [[NSMutableArray alloc] initWithCapacity:regExpCacheSize];
171     CFIndex cacheHit = [regExpLabels indexOfObject:labels];
172     if (cacheHit != NSNotFound)
173         result = regExps.at(cacheHit);
174     else {
175         DeprecatedString pattern("(");
176         unsigned int numLabels = [labels count];
177         unsigned int i;
178         for (i = 0; i < numLabels; i++) {
179             DeprecatedString label = DeprecatedString::fromNSString((NSString *)[labels objectAtIndex:i]);
180
181             bool startsWithWordChar = false;
182             bool endsWithWordChar = false;
183             if (label.length() != 0) {
184                 startsWithWordChar = wordRegExp.search(label.at(0)) >= 0;
185                 endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0;
186             }
187             
188             if (i != 0)
189                 pattern.append("|");
190             // Search for word boundaries only if label starts/ends with "word characters".
191             // If we always searched for word boundaries, this wouldn't work for languages
192             // such as Japanese.
193             if (startsWithWordChar) {
194                 pattern.append("\\b");
195             }
196             pattern.append(label);
197             if (endsWithWordChar) {
198                 pattern.append("\\b");
199             }
200         }
201         pattern.append(")");
202         result = new RegularExpression(pattern, false);
203     }
204
205     // add regexp to the cache, making sure it is at the front for LRU ordering
206     if (cacheHit != 0) {
207         if (cacheHit != NSNotFound) {
208             // remove from old spot
209             [regExpLabels removeObjectAtIndex:cacheHit];
210             regExps.remove(cacheHit);
211         }
212         // add to start
213         [regExpLabels insertObject:labels atIndex:0];
214         regExps.insert(0, result);
215         // trim if too big
216         if ([regExpLabels count] > regExpCacheSize) {
217             [regExpLabels removeObjectAtIndex:regExpCacheSize];
218             RegularExpression *last = regExps.last();
219             regExps.removeLast();
220             delete last;
221         }
222     }
223     return result;
224 }
225
226 NSString* FrameMac::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell)
227 {
228     RenderTableCell* cellRenderer = static_cast<RenderTableCell*>(cell->renderer());
229
230     if (cellRenderer && cellRenderer->isTableCell()) {
231         RenderTableCell* cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
232
233         if (cellAboveRenderer) {
234             HTMLTableCellElement *aboveCell =
235                 static_cast<HTMLTableCellElement*>(cellAboveRenderer->element());
236
237             if (aboveCell) {
238                 // search within the above cell we found for a match
239                 for (Node *n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
240                     if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
241                         // For each text chunk, run the regexp
242                         DeprecatedString nodeString = n->nodeValue().deprecatedString();
243                         int pos = regExp->searchRev(nodeString);
244                         if (pos >= 0)
245                             return nodeString.mid(pos, regExp->matchedLength()).getNSString();
246                     }
247                 }
248             }
249         }
250     }
251     // Any reason in practice to search all cells in that are above cell?
252     return nil;
253 }
254
255 NSString *FrameMac::searchForLabelsBeforeElement(NSArray *labels, Element *element)
256 {
257     RegularExpression *regExp = regExpForLabels(labels);
258     // We stop searching after we've seen this many chars
259     const unsigned int charsSearchedThreshold = 500;
260     // This is the absolute max we search.  We allow a little more slop than
261     // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
262     const unsigned int maxCharsSearched = 600;
263     // If the starting element is within a table, the cell that contains it
264     HTMLTableCellElement *startingTableCell = 0;
265     bool searchedCellAbove = false;
266
267     // walk backwards in the node tree, until another element, or form, or end of tree
268     int unsigned lengthSearched = 0;
269     Node *n;
270     for (n = element->traversePreviousNode();
271          n && lengthSearched < charsSearchedThreshold;
272          n = n->traversePreviousNode())
273     {
274         if (n->hasTagName(formTag)
275             || (n->isHTMLElement()
276                 && static_cast<HTMLElement*>(n)->isGenericFormElement()))
277         {
278             // We hit another form element or the start of the form - bail out
279             break;
280         } else if (n->hasTagName(tdTag) && !startingTableCell) {
281             startingTableCell = static_cast<HTMLTableCellElement*>(n);
282         } else if (n->hasTagName(trTag) && startingTableCell) {
283             NSString *result = searchForLabelsAboveCell(regExp, startingTableCell);
284             if (result) {
285                 return result;
286             }
287             searchedCellAbove = true;
288         } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
289             // For each text chunk, run the regexp
290             DeprecatedString nodeString = n->nodeValue().deprecatedString();
291             // add 100 for slop, to make it more likely that we'll search whole nodes
292             if (lengthSearched + nodeString.length() > maxCharsSearched)
293                 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
294             int pos = regExp->searchRev(nodeString);
295             if (pos >= 0)
296                 return nodeString.mid(pos, regExp->matchedLength()).getNSString();
297             else
298                 lengthSearched += nodeString.length();
299         }
300     }
301
302     // If we started in a cell, but bailed because we found the start of the form or the
303     // previous element, we still might need to search the row above us for a label.
304     if (startingTableCell && !searchedCellAbove) {
305          return searchForLabelsAboveCell(regExp, startingTableCell);
306     } else {
307         return nil;
308     }
309 }
310
311 NSString *FrameMac::matchLabelsAgainstElement(NSArray *labels, Element *element)
312 {
313     DeprecatedString name = element->getAttribute(nameAttr).deprecatedString();
314     // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
315     name.replace(RegularExpression("[[:digit:]]"), " ");
316     name.replace('_', ' ');
317     
318     RegularExpression *regExp = regExpForLabels(labels);
319     // Use the largest match we can find in the whole name string
320     int pos;
321     int length;
322     int bestPos = -1;
323     int bestLength = -1;
324     int start = 0;
325     do {
326         pos = regExp->search(name, start);
327         if (pos != -1) {
328             length = regExp->matchedLength();
329             if (length >= bestLength) {
330                 bestPos = pos;
331                 bestLength = length;
332             }
333             start = pos+1;
334         }
335     } while (pos != -1);
336
337     if (bestPos != -1)
338         return name.mid(bestPos, bestLength).getNSString();
339     return nil;
340 }
341
342 void FrameMac::setView(FrameView *view)
343 {
344     Frame::setView(view);
345     
346 #ifdef MULTIPLE_FORM_SUBMISSION_PROTECTION
347     // Only one form submission is allowed per view of a part.
348     // Since this part may be getting reused as a result of being
349     // pulled from the back/forward cache, reset this flag.
350     loader()->resetMultipleFormSubmissionProtection();
351 #endif
352 }
353
354 void FrameMac::setStatusBarText(const String& status)
355 {
356     String text = status;
357     text.replace('\\', backslashAsCurrencySymbol());
358     
359     // We want the temporaries allocated here to be released even before returning to the 
360     // event loop; see <http://bugs.webkit.org/show_bug.cgi?id=9880>.
361     NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
362
363     BEGIN_BLOCK_OBJC_EXCEPTIONS;
364     [_bridge setStatusText:text];
365     END_BLOCK_OBJC_EXCEPTIONS;
366
367     [localPool release];
368 }
369
370 void FrameMac::focusWindow()
371 {
372     BEGIN_BLOCK_OBJC_EXCEPTIONS;
373
374     // If we're a top level window, bring the window to the front.
375     if (!tree()->parent())
376         page()->chrome()->focus();
377
378     // Might not have a view yet: this could be a child frame that has not yet received its first byte of data.
379     // FIXME: Should remember that the frame needs focus.  See <rdar://problem/4645685>.
380     if (d->m_view) {
381         NSView *view = d->m_view->getDocumentView();
382         if ([_bridge firstResponder] != view)
383             [_bridge makeFirstResponder:view];
384     }
385
386     END_BLOCK_OBJC_EXCEPTIONS;
387 }
388
389 void FrameMac::unfocusWindow()
390 {
391     // Might not have a view yet: this could be a child frame that has not yet received its first byte of data.
392     // FIXME: Should remember that the frame needs to unfocus.  See <rdar://problem/4645685>.
393     if (!d->m_view)
394         return;
395
396     BEGIN_BLOCK_OBJC_EXCEPTIONS;
397     NSView *view = d->m_view->getDocumentView();
398     if ([_bridge firstResponder] == view) {
399         // If we're a top level window, deactivate the window.
400         if (!tree()->parent())
401             page()->chrome()->unfocus();
402         else {
403             // We want to shift focus to our parent.
404             FrameMac* parentFrame = static_cast<FrameMac*>(tree()->parent());
405             NSView* parentView = parentFrame->d->m_view->getDocumentView();
406             [parentFrame->_bridge makeFirstResponder:parentView];
407         }
408     }
409     END_BLOCK_OBJC_EXCEPTIONS;
410 }
411     
412 String FrameMac::mimeTypeForFileName(const String& fileName) const
413 {
414     BEGIN_BLOCK_OBJC_EXCEPTIONS;
415     return [_bridge MIMETypeForPath:fileName];
416     END_BLOCK_OBJC_EXCEPTIONS;
417
418     return String();
419 }
420
421 KJS::Bindings::RootObject *FrameMac::executionContextForDOM()
422 {
423     if (!javaScriptEnabled())
424         return 0;
425
426     return bindingRootObject();
427 }
428
429 KJS::Bindings::RootObject *FrameMac::bindingRootObject()
430 {
431     assert(javaScriptEnabled());
432     if (!_bindingRoot) {
433         JSLock lock;
434         _bindingRoot = new KJS::Bindings::RootObject(0);    // The root gets deleted by JavaScriptCore.
435         KJS::JSObject *win = KJS::Window::retrieveWindow(this);
436         _bindingRoot->setRootObjectImp (win);
437         _bindingRoot->setInterpreter(scriptProxy()->interpreter());
438         addPluginRootObject (_bindingRoot);
439     }
440     return _bindingRoot;
441 }
442
443 WebScriptObject *FrameMac::windowScriptObject()
444 {
445     if (!javaScriptEnabled())
446         return 0;
447
448     if (!_windowScriptObject) {
449         KJS::JSLock lock;
450         KJS::JSObject *win = KJS::Window::retrieveWindow(this);
451         _windowScriptObject = HardRetainWithNSRelease([[WebScriptObject alloc] _initWithJSObject:win originExecutionContext:bindingRootObject() executionContext:bindingRootObject()]);
452     }
453
454     return _windowScriptObject;
455 }
456
457 NPObject *FrameMac::windowScriptNPObject()
458 {
459     if (!_windowScriptNPObject) {
460         if (javaScriptEnabled()) {
461             // JavaScript is enabled, so there is a JavaScript window object.  Return an NPObject bound to the window
462             // object.
463             KJS::JSObject *win = KJS::Window::retrieveWindow(this);
464             assert(win);
465             _windowScriptNPObject = _NPN_CreateScriptObject(0, win, bindingRootObject(), bindingRootObject());
466         } else {
467             // JavaScript is not enabled, so we cannot bind the NPObject to the JavaScript window object.
468             // Instead, we create an NPObject of a different class, one which is not bound to a JavaScript object.
469             _windowScriptNPObject = _NPN_CreateNoScriptObject();
470         }
471     }
472
473     return _windowScriptNPObject;
474 }
475
476 WebCoreFrameBridge *FrameMac::bridgeForWidget(const Widget* widget)
477 {
478     ASSERT_ARG(widget, widget);
479     
480     FrameMac* frame = Mac(frameForWidget(widget));
481     ASSERT(frame);
482     return frame->_bridge;
483 }
484
485 void FrameMac::runJavaScriptAlert(const String& message)
486 {
487     String text = message;
488     text.replace('\\', backslashAsCurrencySymbol());
489     BEGIN_BLOCK_OBJC_EXCEPTIONS;
490     [_bridge runJavaScriptAlertPanelWithMessage:text];
491     END_BLOCK_OBJC_EXCEPTIONS;
492 }
493
494 bool FrameMac::runJavaScriptConfirm(const String& message)
495 {
496     String text = message;
497     text.replace('\\', backslashAsCurrencySymbol());
498
499     BEGIN_BLOCK_OBJC_EXCEPTIONS;
500     return [_bridge runJavaScriptConfirmPanelWithMessage:text];
501     END_BLOCK_OBJC_EXCEPTIONS;
502
503     return false;
504 }
505
506 bool FrameMac::runJavaScriptPrompt(const String& prompt, const String& defaultValue, String& result)
507 {
508     String promptText = prompt;
509     promptText.replace('\\', backslashAsCurrencySymbol());
510     String defaultValueText = defaultValue;
511     defaultValueText.replace('\\', backslashAsCurrencySymbol());
512
513     bool ok;
514     BEGIN_BLOCK_OBJC_EXCEPTIONS;
515     NSString *returnedText = nil;
516
517     ok = [_bridge runJavaScriptTextInputPanelWithPrompt:prompt
518         defaultText:defaultValue returningText:&returnedText];
519
520     if (ok) {
521         result = String(returnedText);
522         result.replace(backslashAsCurrencySymbol(), '\\');
523     }
524
525     return ok;
526     END_BLOCK_OBJC_EXCEPTIONS;
527     
528     return false;
529 }
530
531 bool FrameMac::shouldInterruptJavaScript()
532 {
533     BEGIN_BLOCK_OBJC_EXCEPTIONS;
534     return [_bridge shouldInterruptJavaScript];
535     END_BLOCK_OBJC_EXCEPTIONS;
536     
537     return false;
538 }
539
540 NSImage *FrameMac::imageFromRect(NSRect rect) const
541 {
542     NSView *view = d->m_view->getDocumentView();
543     if (!view)
544         return nil;
545     
546     NSImage *resultImage;
547     BEGIN_BLOCK_OBJC_EXCEPTIONS;
548     
549     NSRect bounds = [view bounds];
550     
551     // Round image rect size in window coordinate space to avoid pixel cracks at HiDPI (4622794)
552     rect = [view convertRect:rect toView:nil];
553     rect.size.height = roundf(rect.size.height);
554     rect.size.width = roundf(rect.size.width);
555     rect = [view convertRect:rect fromView:nil];
556     
557     resultImage = [[[NSImage alloc] initWithSize:rect.size] autorelease];
558
559     if (rect.size.width != 0 && rect.size.height != 0) {
560         [resultImage setFlipped:YES];
561         [resultImage lockFocus];
562
563         CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
564
565         CGContextSaveGState(context);
566         CGContextTranslateCTM(context, bounds.origin.x - rect.origin.x, bounds.origin.y - rect.origin.y);
567         [view drawRect:rect];
568         CGContextRestoreGState(context);
569         [resultImage unlockFocus];
570         [resultImage setFlipped:NO];
571     }
572
573     return resultImage;
574
575     END_BLOCK_OBJC_EXCEPTIONS;
576     
577     return nil;
578 }
579
580 NSImage* FrameMac::selectionImage(bool forceWhiteText) const
581 {
582     d->m_paintRestriction = forceWhiteText ? PaintRestrictionSelectionOnlyWhiteText : PaintRestrictionSelectionOnly;
583     NSImage *result = imageFromRect(visibleSelectionRect());
584     d->m_paintRestriction = PaintRestrictionNone;
585     return result;
586 }
587
588 NSImage *FrameMac::snapshotDragImage(Node *node, NSRect *imageRect, NSRect *elementRect) const
589 {
590     RenderObject *renderer = node->renderer();
591     if (!renderer)
592         return nil;
593     
594     renderer->updateDragState(true);    // mark dragged nodes (so they pick up the right CSS)
595     d->m_doc->updateLayout();        // forces style recalc - needed since changing the drag state might
596                                         // imply new styles, plus JS could have changed other things
597     IntRect topLevelRect;
598     NSRect paintingRect = renderer->paintingRootRect(topLevelRect);
599
600     d->m_elementToDraw = node;              // invoke special sub-tree drawing mode
601     NSImage *result = imageFromRect(paintingRect);
602     renderer->updateDragState(false);
603     d->m_doc->updateLayout();
604     d->m_elementToDraw = 0;
605
606     if (elementRect)
607         *elementRect = topLevelRect;
608     if (imageRect)
609         *imageRect = paintingRect;
610     return result;
611 }
612
613 NSDictionary *FrameMac::fontAttributesForSelectionStart() const
614 {
615     Node *nodeToRemove;
616     RenderStyle *style = styleForSelectionStart(nodeToRemove);
617     if (!style)
618         return nil;
619
620     NSMutableDictionary *result = [NSMutableDictionary dictionary];
621
622     if (style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
623         [result setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
624
625     if (style->font().primaryFont()->getNSFont())
626         [result setObject:style->font().primaryFont()->getNSFont() forKey:NSFontAttributeName];
627
628     if (style->color().isValid() && style->color() != Color::black)
629         [result setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
630
631     ShadowData *shadow = style->textShadow();
632     if (shadow) {
633         NSShadow *s = [[NSShadow alloc] init];
634         [s setShadowOffset:NSMakeSize(shadow->x, shadow->y)];
635         [s setShadowBlurRadius:shadow->blur];
636         [s setShadowColor:nsColor(shadow->color)];
637         [result setObject:s forKey:NSShadowAttributeName];
638     }
639
640     int decoration = style->textDecorationsInEffect();
641     if (decoration & LINE_THROUGH)
642         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
643
644     int superscriptInt = 0;
645     switch (style->verticalAlign()) {
646         case BASELINE:
647         case BOTTOM:
648         case BASELINE_MIDDLE:
649         case LENGTH:
650         case MIDDLE:
651         case TEXT_BOTTOM:
652         case TEXT_TOP:
653         case TOP:
654             break;
655         case SUB:
656             superscriptInt = -1;
657             break;
658         case SUPER:
659             superscriptInt = 1;
660             break;
661     }
662     if (superscriptInt)
663         [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
664
665     if (decoration & UNDERLINE)
666         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
667
668     if (nodeToRemove) {
669         ExceptionCode ec = 0;
670         nodeToRemove->remove(ec);
671         ASSERT(ec == 0);
672     }
673
674     return result;
675 }
676
677 NSWritingDirection FrameMac::baseWritingDirectionForSelectionStart() const
678 {
679     NSWritingDirection result = NSWritingDirectionLeftToRight;
680
681     Position pos = selectionController()->selection().visibleStart().deepEquivalent();
682     Node *node = pos.node();
683     if (!node || !node->renderer() || !node->renderer()->containingBlock())
684         return result;
685     RenderStyle *style = node->renderer()->containingBlock()->style();
686     if (!style)
687         return result;
688         
689     switch (style->direction()) {
690         case LTR:
691             result = NSWritingDirectionLeftToRight;
692             break;
693         case RTL:
694             result = NSWritingDirectionRightToLeft;
695             break;
696     }
697
698     return result;
699 }
700
701 void FrameMac::setBridge(WebCoreFrameBridge *bridge)
702
703     if (_bridge == bridge)
704         return;
705
706     HardRetain(bridge);
707     HardRelease(_bridge);
708     _bridge = bridge;
709 }
710
711 void FrameMac::print()
712 {
713     [Mac(this)->_bridge print];
714 }
715
716 KJS::Bindings::Instance *FrameMac::getAppletInstanceForWidget(Widget *widget)
717 {
718     NSView *aView = widget->getView();
719     if (!aView)
720         return 0;
721     jobject applet;
722     
723     // Get a pointer to the actual Java applet instance.
724     if ([_bridge respondsToSelector:@selector(getAppletInView:)])
725         applet = [_bridge getAppletInView:aView];
726     else
727         applet = [_bridge pollForAppletInView:aView];
728     
729     if (applet) {
730         // Wrap the Java instance in a language neutral binding and hand
731         // off ownership to the APPLET element.
732         KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
733         KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::JavaLanguage, applet, executionContext);        
734         return instance;
735     }
736     
737     return 0;
738 }
739
740 static KJS::Bindings::Instance *getInstanceForView(NSView *aView)
741 {
742     if ([aView respondsToSelector:@selector(objectForWebScript)]){
743         id object = [aView objectForWebScript];
744         if (object) {
745             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
746             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::ObjectiveCLanguage, object, executionContext);
747         }
748     }
749     else if ([aView respondsToSelector:@selector(createPluginScriptableObject)]) {
750         NPObject *object = [aView createPluginScriptableObject];
751         if (object) {
752             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction()(aView);
753             KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance(KJS::Bindings::Instance::CLanguage, object, executionContext);
754             
755             // -createPluginScriptableObject returns a retained NPObject.  The caller is expected to release it.
756             _NPN_ReleaseObject(object);
757             
758             return instance;
759         }
760     }
761     return 0;
762 }
763
764 KJS::Bindings::Instance *FrameMac::getEmbedInstanceForWidget(Widget *widget)
765 {
766     return getInstanceForView(widget->getView());
767 }
768
769 KJS::Bindings::Instance *FrameMac::getObjectInstanceForWidget(Widget *widget)
770 {
771     return getInstanceForView(widget->getView());
772 }
773
774 void FrameMac::addPluginRootObject(KJS::Bindings::RootObject *root)
775 {
776     m_rootObjects.append(root);
777 }
778
779 void FrameMac::cleanupPluginObjects()
780 {
781     // Delete old plug-in data structures
782     JSLock lock;
783     
784     unsigned count = m_rootObjects.size();
785     for (unsigned i = 0; i < count; i++)
786         m_rootObjects[i]->removeAllNativeReferences();
787     m_rootObjects.clear();
788     
789     _bindingRoot = 0;
790     HardRelease(_windowScriptObject);
791     _windowScriptObject = 0;
792     
793     if (_windowScriptNPObject) {
794         // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
795         // script object properly.
796         // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
797         _NPN_DeallocateObject(_windowScriptNPObject);
798         _windowScriptNPObject = 0;
799     }
800 }
801
802 void FrameMac::issueCutCommand()
803 {
804     [_bridge issueCutCommand];
805 }
806
807 void FrameMac::issueCopyCommand()
808 {
809     [_bridge issueCopyCommand];
810 }
811
812 void FrameMac::issuePasteCommand()
813 {
814     [_bridge issuePasteCommand];
815 }
816
817 void FrameMac::issuePasteAndMatchStyleCommand()
818 {
819     [_bridge issuePasteAndMatchStyleCommand];
820 }
821
822 void FrameMac::issueTransposeCommand()
823 {
824     [_bridge issueTransposeCommand];
825 }
826
827 void FrameMac::respondToChangedSelection(const Selection &oldSelection, bool closeTyping)
828 {
829     if (document()) {
830         if (editor()->isContinuousSpellCheckingEnabled()) {
831             Selection oldAdjacentWords;
832             
833             // If this is a change in selection resulting from a delete operation, oldSelection may no longer
834             // be in the document.
835             if (oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
836                 VisiblePosition oldStart(oldSelection.visibleStart());
837                 oldAdjacentWords = Selection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));   
838             }
839             
840             VisiblePosition newStart(selectionController()->selection().visibleStart());
841             Selection newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
842             
843             // When typing we check spelling elsewhere, so don't redo it here.
844             if (closeTyping && oldAdjacentWords != newAdjacentWords)
845                 editor()->markMisspellings(oldAdjacentWords);
846             
847             // This only erases a marker in the first word of the selection.
848             // Perhaps peculiar, but it matches AppKit.
849             document()->removeMarkers(newAdjacentWords.toRange().get(), DocumentMarker::Spelling);
850             document()->removeMarkers(newAdjacentWords.toRange().get(), DocumentMarker::Grammar);
851         } else {
852             // When continuous spell checking is off, existing markers disappear after the selection changes.
853             document()->removeMarkers(DocumentMarker::Spelling);
854             document()->removeMarkers(DocumentMarker::Grammar);
855         }
856     }
857     
858     [_bridge respondToChangedSelection];
859 }
860
861 bool FrameMac::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const
862 {
863     return [_bridge shouldChangeSelectedDOMRange:[DOMRange _rangeWith:oldSelection.toRange().get()]
864                                       toDOMRange:[DOMRange _rangeWith:newSelection.toRange().get()]
865                                         affinity:affinity
866                                   stillSelecting:stillSelecting];
867 }
868
869 bool FrameMac::shouldDeleteSelection(const Selection& selection) const
870 {
871     return [_bridge shouldDeleteSelectedDOMRange:[DOMRange _rangeWith:selection.toRange().get()]];
872 }
873
874 bool FrameMac::isContentEditable() const
875 {
876     return Frame::isContentEditable() || [_bridge isEditable];
877 }
878
879 void FrameMac::textFieldDidBeginEditing(Element* input)
880 {
881     BEGIN_BLOCK_OBJC_EXCEPTIONS;
882     [_bridge textFieldDidBeginEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
883     END_BLOCK_OBJC_EXCEPTIONS;
884 }
885
886 void FrameMac::textFieldDidEndEditing(Element* input)
887 {
888     BEGIN_BLOCK_OBJC_EXCEPTIONS;
889     [_bridge textFieldDidEndEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
890     END_BLOCK_OBJC_EXCEPTIONS;
891 }
892
893 void FrameMac::textDidChangeInTextField(Element* input)
894 {
895     BEGIN_BLOCK_OBJC_EXCEPTIONS;
896     [_bridge textDidChangeInTextField:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
897     END_BLOCK_OBJC_EXCEPTIONS;
898 }
899
900 void FrameMac::textDidChangeInTextArea(Element* textarea)
901 {
902     BEGIN_BLOCK_OBJC_EXCEPTIONS;
903     [_bridge textDidChangeInTextArea:(DOMHTMLTextAreaElement *)[DOMElement _elementWith:textarea]];
904     END_BLOCK_OBJC_EXCEPTIONS;
905 }
906
907 bool FrameMac::doTextFieldCommandFromEvent(Element* input, KeyboardEvent* event)
908 {
909     bool result = false;
910     BEGIN_BLOCK_OBJC_EXCEPTIONS;
911     SEL selector = selectorForKeyEvent(event);
912     if (selector)
913         result = [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:selector];
914     END_BLOCK_OBJC_EXCEPTIONS;
915     return result;
916 }
917
918 void FrameMac::textWillBeDeletedInTextField(Element* input)
919 {
920     // We're using the deleteBackward selector for all deletion operations since the autofill code treats all deletions the same way.
921     BEGIN_BLOCK_OBJC_EXCEPTIONS;
922     [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:@selector(deleteBackward:)];
923     END_BLOCK_OBJC_EXCEPTIONS;
924 }
925
926 const short enableRomanKeyboardsOnly = -23;
927 void FrameMac::setSecureKeyboardEntry(bool enable)
928 {
929     if (enable) {
930         EnableSecureEventInput();
931 // FIXME: KeyScript is deprecated in Leopard, we need a new solution for this <rdar://problem/4727607>
932 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
933         KeyScript(enableRomanKeyboardsOnly);
934 #endif
935     } else {
936         DisableSecureEventInput();
937 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
938         KeyScript(smKeyEnableKybds);
939 #endif
940     }
941 }
942
943 bool FrameMac::isSecureKeyboardEntry()
944 {
945     return IsSecureEventInputEnabled();
946 }
947
948 static void convertAttributesToUnderlines(Vector<MarkedTextUnderline>& result, const Range *markedTextRange, NSArray *attributes, NSArray *ranges)
949 {
950     int exception = 0;
951     int baseOffset = markedTextRange->startOffset(exception);
952
953     unsigned length = [attributes count];
954     ASSERT([ranges count] == length);
955
956     for (unsigned i = 0; i < length; i++) {
957         NSNumber *style = [[attributes objectAtIndex:i] objectForKey:NSUnderlineStyleAttributeName];
958         if (!style)
959             continue;
960         NSRange range = [[ranges objectAtIndex:i] rangeValue];
961         NSColor *color = [[attributes objectAtIndex:i] objectForKey:NSUnderlineColorAttributeName];
962         Color qColor = Color::black;
963         if (color) {
964             NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
965             qColor = Color(makeRGBA((int)(255 * [deviceColor redComponent]),
966                                     (int)(255 * [deviceColor blueComponent]),
967                                     (int)(255 * [deviceColor greenComponent]),
968                                     (int)(255 * [deviceColor alphaComponent])));
969         }
970
971         result.append(MarkedTextUnderline(range.location + baseOffset, 
972                                           range.location + baseOffset + range.length, 
973                                           qColor,
974                                           [style intValue] > 1));
975     }
976 }
977
978 void FrameMac::setMarkedTextRange(const Range *range, NSArray *attributes, NSArray *ranges)
979 {
980     int exception = 0;
981
982     ASSERT(!range || range->startContainer(exception) == range->endContainer(exception));
983     ASSERT(!range || range->collapsed(exception) || range->startContainer(exception)->isTextNode());
984
985     d->m_markedTextUnderlines.clear();
986     if (attributes == nil)
987         d->m_markedTextUsesUnderlines = false;
988     else {
989         d->m_markedTextUsesUnderlines = true;
990         convertAttributesToUnderlines(d->m_markedTextUnderlines, range, attributes, ranges);
991     }
992
993     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
994         m_markedTextRange->startContainer(exception)->renderer()->repaint();
995
996     if (range && range->collapsed(exception))
997         m_markedTextRange = 0;
998     else
999         m_markedTextRange = const_cast<Range*>(range);
1000
1001     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
1002         m_markedTextRange->startContainer(exception)->renderer()->repaint();
1003 }
1004
1005 NSMutableDictionary *FrameMac::dashboardRegionsDictionary()
1006 {
1007     Document *doc = document();
1008     if (!doc)
1009         return nil;
1010
1011     const Vector<DashboardRegionValue>& regions = doc->dashboardRegions();
1012     size_t n = regions.size();
1013
1014     // Convert the Vector<DashboardRegionValue> into a NSDictionary of WebDashboardRegions
1015     NSMutableDictionary *webRegions = [NSMutableDictionary dictionaryWithCapacity:n];
1016     for (size_t i = 0; i < n; i++) {
1017         const DashboardRegionValue& region = regions[i];
1018
1019         if (region.type == StyleDashboardRegion::None)
1020             continue;
1021         
1022         NSString *label = region.label;
1023         WebDashboardRegionType type = WebDashboardRegionTypeNone;
1024         if (region.type == StyleDashboardRegion::Circle)
1025             type = WebDashboardRegionTypeCircle;
1026         else if (region.type == StyleDashboardRegion::Rectangle)
1027             type = WebDashboardRegionTypeRectangle;
1028         NSMutableArray *regionValues = [webRegions objectForKey:label];
1029         if (!regionValues) {
1030             regionValues = [[NSMutableArray alloc] initWithCapacity:1];
1031             [webRegions setObject:regionValues forKey:label];
1032             [regionValues release];
1033         }
1034         
1035         WebDashboardRegion *webRegion = [[WebDashboardRegion alloc] initWithRect:region.bounds clip:region.clip type:type];
1036         [regionValues addObject:webRegion];
1037         [webRegion release];
1038     }
1039     
1040     return webRegions;
1041 }
1042
1043 void FrameMac::dashboardRegionsChanged()
1044 {
1045     NSMutableDictionary *webRegions = dashboardRegionsDictionary();
1046     [_bridge dashboardRegionsChanged:webRegions];
1047 }
1048
1049 void FrameMac::willPopupMenu(NSMenu * menu)
1050 {
1051     [_bridge willPopupMenu:menu];
1052 }
1053
1054 bool FrameMac::isCharacterSmartReplaceExempt(UChar c, bool isPreviousChar)
1055 {
1056     return [_bridge isCharacterSmartReplaceExempt:c isPreviousCharacter:isPreviousChar];
1057 }
1058
1059 void Frame::setNeedsReapplyStyles()
1060 {
1061     [Mac(this)->_bridge setNeedsReapplyStyles];
1062 }
1063
1064 FloatRect FrameMac::customHighlightLineRect(const AtomicString& type, const FloatRect& lineRect)
1065 {
1066     return [_bridge customHighlightRect:type forLine:lineRect];
1067 }
1068
1069 void FrameMac::paintCustomHighlight(const AtomicString& type, const FloatRect& boxRect, const FloatRect& lineRect, bool text, bool line)
1070 {
1071     [_bridge paintCustomHighlight:type forBox:boxRect onLine:lineRect behindText:text entireLine:line];
1072 }
1073
1074 } // namespace WebCore