3af5d21c22981391128792d27fd9a08db9d98172
[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::scheduleClose()
371 {
372     if (!shouldClose())
373         return;
374     BEGIN_BLOCK_OBJC_EXCEPTIONS;
375     [_bridge closeWindowSoon];
376     END_BLOCK_OBJC_EXCEPTIONS;
377 }
378
379 void FrameMac::focusWindow()
380 {
381     BEGIN_BLOCK_OBJC_EXCEPTIONS;
382
383     // If we're a top level window, bring the window to the front.
384     if (!tree()->parent())
385         page()->chrome()->focus();
386
387     // Might not have a view yet: this could be a child frame that has not yet received its first byte of data.
388     // FIXME: Should remember that the frame needs focus.  See <rdar://problem/4645685>.
389     if (d->m_view) {
390         NSView *view = d->m_view->getDocumentView();
391         if ([_bridge firstResponder] != view)
392             [_bridge makeFirstResponder:view];
393     }
394
395     END_BLOCK_OBJC_EXCEPTIONS;
396 }
397
398 void FrameMac::unfocusWindow()
399 {
400     // Might not have a view yet: this could be a child frame that has not yet received its first byte of data.
401     // FIXME: Should remember that the frame needs to unfocus.  See <rdar://problem/4645685>.
402     if (!d->m_view)
403         return;
404
405     BEGIN_BLOCK_OBJC_EXCEPTIONS;
406     NSView *view = d->m_view->getDocumentView();
407     if ([_bridge firstResponder] == view) {
408         // If we're a top level window, deactivate the window.
409         if (!tree()->parent())
410             page()->chrome()->unfocus();
411         else {
412             // We want to shift focus to our parent.
413             FrameMac* parentFrame = static_cast<FrameMac*>(tree()->parent());
414             NSView* parentView = parentFrame->d->m_view->getDocumentView();
415             [parentFrame->_bridge makeFirstResponder:parentView];
416         }
417     }
418     END_BLOCK_OBJC_EXCEPTIONS;
419 }
420     
421 String FrameMac::mimeTypeForFileName(const String& fileName) const
422 {
423     BEGIN_BLOCK_OBJC_EXCEPTIONS;
424     return [_bridge MIMETypeForPath:fileName];
425     END_BLOCK_OBJC_EXCEPTIONS;
426
427     return String();
428 }
429
430 KJS::Bindings::RootObject *FrameMac::executionContextForDOM()
431 {
432     if (!javaScriptEnabled())
433         return 0;
434
435     return bindingRootObject();
436 }
437
438 KJS::Bindings::RootObject *FrameMac::bindingRootObject()
439 {
440     assert(javaScriptEnabled());
441     if (!_bindingRoot) {
442         JSLock lock;
443         _bindingRoot = new KJS::Bindings::RootObject(0);    // The root gets deleted by JavaScriptCore.
444         KJS::JSObject *win = KJS::Window::retrieveWindow(this);
445         _bindingRoot->setRootObjectImp (win);
446         _bindingRoot->setInterpreter(scriptProxy()->interpreter());
447         addPluginRootObject (_bindingRoot);
448     }
449     return _bindingRoot;
450 }
451
452 WebScriptObject *FrameMac::windowScriptObject()
453 {
454     if (!javaScriptEnabled())
455         return 0;
456
457     if (!_windowScriptObject) {
458         KJS::JSLock lock;
459         KJS::JSObject *win = KJS::Window::retrieveWindow(this);
460         _windowScriptObject = HardRetainWithNSRelease([[WebScriptObject alloc] _initWithJSObject:win originExecutionContext:bindingRootObject() executionContext:bindingRootObject()]);
461     }
462
463     return _windowScriptObject;
464 }
465
466 NPObject *FrameMac::windowScriptNPObject()
467 {
468     if (!_windowScriptNPObject) {
469         if (javaScriptEnabled()) {
470             // JavaScript is enabled, so there is a JavaScript window object.  Return an NPObject bound to the window
471             // object.
472             KJS::JSObject *win = KJS::Window::retrieveWindow(this);
473             assert(win);
474             _windowScriptNPObject = _NPN_CreateScriptObject(0, win, bindingRootObject(), bindingRootObject());
475         } else {
476             // JavaScript is not enabled, so we cannot bind the NPObject to the JavaScript window object.
477             // Instead, we create an NPObject of a different class, one which is not bound to a JavaScript object.
478             _windowScriptNPObject = _NPN_CreateNoScriptObject();
479         }
480     }
481
482     return _windowScriptNPObject;
483 }
484
485 WebCoreFrameBridge *FrameMac::bridgeForWidget(const Widget* widget)
486 {
487     ASSERT_ARG(widget, widget);
488     
489     FrameMac* frame = Mac(frameForWidget(widget));
490     ASSERT(frame);
491     return frame->_bridge;
492 }
493
494 void FrameMac::runJavaScriptAlert(const String& message)
495 {
496     String text = message;
497     text.replace('\\', backslashAsCurrencySymbol());
498     BEGIN_BLOCK_OBJC_EXCEPTIONS;
499     [_bridge runJavaScriptAlertPanelWithMessage:text];
500     END_BLOCK_OBJC_EXCEPTIONS;
501 }
502
503 bool FrameMac::runJavaScriptConfirm(const String& message)
504 {
505     String text = message;
506     text.replace('\\', backslashAsCurrencySymbol());
507
508     BEGIN_BLOCK_OBJC_EXCEPTIONS;
509     return [_bridge runJavaScriptConfirmPanelWithMessage:text];
510     END_BLOCK_OBJC_EXCEPTIONS;
511
512     return false;
513 }
514
515 bool FrameMac::runJavaScriptPrompt(const String& prompt, const String& defaultValue, String& result)
516 {
517     String promptText = prompt;
518     promptText.replace('\\', backslashAsCurrencySymbol());
519     String defaultValueText = defaultValue;
520     defaultValueText.replace('\\', backslashAsCurrencySymbol());
521
522     bool ok;
523     BEGIN_BLOCK_OBJC_EXCEPTIONS;
524     NSString *returnedText = nil;
525
526     ok = [_bridge runJavaScriptTextInputPanelWithPrompt:prompt
527         defaultText:defaultValue returningText:&returnedText];
528
529     if (ok) {
530         result = String(returnedText);
531         result.replace(backslashAsCurrencySymbol(), '\\');
532     }
533
534     return ok;
535     END_BLOCK_OBJC_EXCEPTIONS;
536     
537     return false;
538 }
539
540 bool FrameMac::shouldInterruptJavaScript()
541 {
542     BEGIN_BLOCK_OBJC_EXCEPTIONS;
543     return [_bridge shouldInterruptJavaScript];
544     END_BLOCK_OBJC_EXCEPTIONS;
545     
546     return false;
547 }
548
549 NSImage *FrameMac::imageFromRect(NSRect rect) const
550 {
551     NSView *view = d->m_view->getDocumentView();
552     if (!view)
553         return nil;
554     
555     NSImage *resultImage;
556     BEGIN_BLOCK_OBJC_EXCEPTIONS;
557     
558     NSRect bounds = [view bounds];
559     
560     // Round image rect size in window coordinate space to avoid pixel cracks at HiDPI (4622794)
561     rect = [view convertRect:rect toView:nil];
562     rect.size.height = roundf(rect.size.height);
563     rect.size.width = roundf(rect.size.width);
564     rect = [view convertRect:rect fromView:nil];
565     
566     resultImage = [[[NSImage alloc] initWithSize:rect.size] autorelease];
567
568     if (rect.size.width != 0 && rect.size.height != 0) {
569         [resultImage setFlipped:YES];
570         [resultImage lockFocus];
571
572         CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
573
574         CGContextSaveGState(context);
575         CGContextTranslateCTM(context, bounds.origin.x - rect.origin.x, bounds.origin.y - rect.origin.y);
576         [view drawRect:rect];
577         CGContextRestoreGState(context);
578         [resultImage unlockFocus];
579         [resultImage setFlipped:NO];
580     }
581
582     return resultImage;
583
584     END_BLOCK_OBJC_EXCEPTIONS;
585     
586     return nil;
587 }
588
589 NSImage* FrameMac::selectionImage(bool forceWhiteText) const
590 {
591     d->m_paintRestriction = forceWhiteText ? PaintRestrictionSelectionOnlyWhiteText : PaintRestrictionSelectionOnly;
592     NSImage *result = imageFromRect(visibleSelectionRect());
593     d->m_paintRestriction = PaintRestrictionNone;
594     return result;
595 }
596
597 NSImage *FrameMac::snapshotDragImage(Node *node, NSRect *imageRect, NSRect *elementRect) const
598 {
599     RenderObject *renderer = node->renderer();
600     if (!renderer)
601         return nil;
602     
603     renderer->updateDragState(true);    // mark dragged nodes (so they pick up the right CSS)
604     d->m_doc->updateLayout();        // forces style recalc - needed since changing the drag state might
605                                         // imply new styles, plus JS could have changed other things
606     IntRect topLevelRect;
607     NSRect paintingRect = renderer->paintingRootRect(topLevelRect);
608
609     d->m_elementToDraw = node;              // invoke special sub-tree drawing mode
610     NSImage *result = imageFromRect(paintingRect);
611     renderer->updateDragState(false);
612     d->m_doc->updateLayout();
613     d->m_elementToDraw = 0;
614
615     if (elementRect)
616         *elementRect = topLevelRect;
617     if (imageRect)
618         *imageRect = paintingRect;
619     return result;
620 }
621
622 NSDictionary *FrameMac::fontAttributesForSelectionStart() const
623 {
624     Node *nodeToRemove;
625     RenderStyle *style = styleForSelectionStart(nodeToRemove);
626     if (!style)
627         return nil;
628
629     NSMutableDictionary *result = [NSMutableDictionary dictionary];
630
631     if (style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
632         [result setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
633
634     if (style->font().primaryFont()->getNSFont())
635         [result setObject:style->font().primaryFont()->getNSFont() forKey:NSFontAttributeName];
636
637     if (style->color().isValid() && style->color() != Color::black)
638         [result setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
639
640     ShadowData *shadow = style->textShadow();
641     if (shadow) {
642         NSShadow *s = [[NSShadow alloc] init];
643         [s setShadowOffset:NSMakeSize(shadow->x, shadow->y)];
644         [s setShadowBlurRadius:shadow->blur];
645         [s setShadowColor:nsColor(shadow->color)];
646         [result setObject:s forKey:NSShadowAttributeName];
647     }
648
649     int decoration = style->textDecorationsInEffect();
650     if (decoration & LINE_THROUGH)
651         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
652
653     int superscriptInt = 0;
654     switch (style->verticalAlign()) {
655         case BASELINE:
656         case BOTTOM:
657         case BASELINE_MIDDLE:
658         case LENGTH:
659         case MIDDLE:
660         case TEXT_BOTTOM:
661         case TEXT_TOP:
662         case TOP:
663             break;
664         case SUB:
665             superscriptInt = -1;
666             break;
667         case SUPER:
668             superscriptInt = 1;
669             break;
670     }
671     if (superscriptInt)
672         [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
673
674     if (decoration & UNDERLINE)
675         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
676
677     if (nodeToRemove) {
678         ExceptionCode ec = 0;
679         nodeToRemove->remove(ec);
680         ASSERT(ec == 0);
681     }
682
683     return result;
684 }
685
686 NSWritingDirection FrameMac::baseWritingDirectionForSelectionStart() const
687 {
688     NSWritingDirection result = NSWritingDirectionLeftToRight;
689
690     Position pos = selectionController()->selection().visibleStart().deepEquivalent();
691     Node *node = pos.node();
692     if (!node || !node->renderer() || !node->renderer()->containingBlock())
693         return result;
694     RenderStyle *style = node->renderer()->containingBlock()->style();
695     if (!style)
696         return result;
697         
698     switch (style->direction()) {
699         case LTR:
700             result = NSWritingDirectionLeftToRight;
701             break;
702         case RTL:
703             result = NSWritingDirectionRightToLeft;
704             break;
705     }
706
707     return result;
708 }
709
710 void FrameMac::setBridge(WebCoreFrameBridge *bridge)
711
712     if (_bridge == bridge)
713         return;
714
715     HardRetain(bridge);
716     HardRelease(_bridge);
717     _bridge = bridge;
718 }
719
720 void FrameMac::print()
721 {
722     [Mac(this)->_bridge print];
723 }
724
725 KJS::Bindings::Instance *FrameMac::getAppletInstanceForWidget(Widget *widget)
726 {
727     NSView *aView = widget->getView();
728     if (!aView)
729         return 0;
730     jobject applet;
731     
732     // Get a pointer to the actual Java applet instance.
733     if ([_bridge respondsToSelector:@selector(getAppletInView:)])
734         applet = [_bridge getAppletInView:aView];
735     else
736         applet = [_bridge pollForAppletInView:aView];
737     
738     if (applet) {
739         // Wrap the Java instance in a language neutral binding and hand
740         // off ownership to the APPLET element.
741         KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
742         KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::JavaLanguage, applet, executionContext);        
743         return instance;
744     }
745     
746     return 0;
747 }
748
749 static KJS::Bindings::Instance *getInstanceForView(NSView *aView)
750 {
751     if ([aView respondsToSelector:@selector(objectForWebScript)]){
752         id object = [aView objectForWebScript];
753         if (object) {
754             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
755             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::ObjectiveCLanguage, object, executionContext);
756         }
757     }
758     else if ([aView respondsToSelector:@selector(createPluginScriptableObject)]) {
759         NPObject *object = [aView createPluginScriptableObject];
760         if (object) {
761             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction()(aView);
762             KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance(KJS::Bindings::Instance::CLanguage, object, executionContext);
763             
764             // -createPluginScriptableObject returns a retained NPObject.  The caller is expected to release it.
765             _NPN_ReleaseObject(object);
766             
767             return instance;
768         }
769     }
770     return 0;
771 }
772
773 KJS::Bindings::Instance *FrameMac::getEmbedInstanceForWidget(Widget *widget)
774 {
775     return getInstanceForView(widget->getView());
776 }
777
778 KJS::Bindings::Instance *FrameMac::getObjectInstanceForWidget(Widget *widget)
779 {
780     return getInstanceForView(widget->getView());
781 }
782
783 void FrameMac::addPluginRootObject(KJS::Bindings::RootObject *root)
784 {
785     m_rootObjects.append(root);
786 }
787
788 void FrameMac::cleanupPluginObjects()
789 {
790     // Delete old plug-in data structures
791     JSLock lock;
792     
793     unsigned count = m_rootObjects.size();
794     for (unsigned i = 0; i < count; i++)
795         m_rootObjects[i]->removeAllNativeReferences();
796     m_rootObjects.clear();
797     
798     _bindingRoot = 0;
799     HardRelease(_windowScriptObject);
800     _windowScriptObject = 0;
801     
802     if (_windowScriptNPObject) {
803         // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
804         // script object properly.
805         // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
806         _NPN_DeallocateObject(_windowScriptNPObject);
807         _windowScriptNPObject = 0;
808     }
809 }
810
811 void FrameMac::issueCutCommand()
812 {
813     [_bridge issueCutCommand];
814 }
815
816 void FrameMac::issueCopyCommand()
817 {
818     [_bridge issueCopyCommand];
819 }
820
821 void FrameMac::issuePasteCommand()
822 {
823     [_bridge issuePasteCommand];
824 }
825
826 void FrameMac::issuePasteAndMatchStyleCommand()
827 {
828     [_bridge issuePasteAndMatchStyleCommand];
829 }
830
831 void FrameMac::issueTransposeCommand()
832 {
833     [_bridge issueTransposeCommand];
834 }
835
836 void FrameMac::respondToChangedSelection(const Selection &oldSelection, bool closeTyping)
837 {
838     if (document()) {
839         if (editor()->isContinuousSpellCheckingEnabled()) {
840             Selection oldAdjacentWords;
841             
842             // If this is a change in selection resulting from a delete operation, oldSelection may no longer
843             // be in the document.
844             if (oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
845                 VisiblePosition oldStart(oldSelection.visibleStart());
846                 oldAdjacentWords = Selection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));   
847             }
848             
849             VisiblePosition newStart(selectionController()->selection().visibleStart());
850             Selection newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
851             
852             // When typing we check spelling elsewhere, so don't redo it here.
853             if (closeTyping && oldAdjacentWords != newAdjacentWords)
854                 editor()->markMisspellings(oldAdjacentWords);
855             
856             // This only erases a marker in the first word of the selection.
857             // Perhaps peculiar, but it matches AppKit.
858             document()->removeMarkers(newAdjacentWords.toRange().get(), DocumentMarker::Spelling);
859             document()->removeMarkers(newAdjacentWords.toRange().get(), DocumentMarker::Grammar);
860         } else {
861             // When continuous spell checking is off, existing markers disappear after the selection changes.
862             document()->removeMarkers(DocumentMarker::Spelling);
863             document()->removeMarkers(DocumentMarker::Grammar);
864         }
865     }
866     
867     [_bridge respondToChangedSelection];
868 }
869
870 bool FrameMac::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const
871 {
872     return [_bridge shouldChangeSelectedDOMRange:[DOMRange _rangeWith:oldSelection.toRange().get()]
873                                       toDOMRange:[DOMRange _rangeWith:newSelection.toRange().get()]
874                                         affinity:affinity
875                                   stillSelecting:stillSelecting];
876 }
877
878 bool FrameMac::shouldDeleteSelection(const Selection& selection) const
879 {
880     return [_bridge shouldDeleteSelectedDOMRange:[DOMRange _rangeWith:selection.toRange().get()]];
881 }
882
883 bool FrameMac::isContentEditable() const
884 {
885     return Frame::isContentEditable() || [_bridge isEditable];
886 }
887
888 void FrameMac::textFieldDidBeginEditing(Element* input)
889 {
890     BEGIN_BLOCK_OBJC_EXCEPTIONS;
891     [_bridge textFieldDidBeginEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
892     END_BLOCK_OBJC_EXCEPTIONS;
893 }
894
895 void FrameMac::textFieldDidEndEditing(Element* input)
896 {
897     BEGIN_BLOCK_OBJC_EXCEPTIONS;
898     [_bridge textFieldDidEndEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
899     END_BLOCK_OBJC_EXCEPTIONS;
900 }
901
902 void FrameMac::textDidChangeInTextField(Element* input)
903 {
904     BEGIN_BLOCK_OBJC_EXCEPTIONS;
905     [_bridge textDidChangeInTextField:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
906     END_BLOCK_OBJC_EXCEPTIONS;
907 }
908
909 void FrameMac::textDidChangeInTextArea(Element* textarea)
910 {
911     BEGIN_BLOCK_OBJC_EXCEPTIONS;
912     [_bridge textDidChangeInTextArea:(DOMHTMLTextAreaElement *)[DOMElement _elementWith:textarea]];
913     END_BLOCK_OBJC_EXCEPTIONS;
914 }
915
916 bool FrameMac::doTextFieldCommandFromEvent(Element* input, KeyboardEvent* event)
917 {
918     bool result = false;
919     BEGIN_BLOCK_OBJC_EXCEPTIONS;
920     SEL selector = selectorForKeyEvent(event);
921     if (selector)
922         result = [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:selector];
923     END_BLOCK_OBJC_EXCEPTIONS;
924     return result;
925 }
926
927 void FrameMac::textWillBeDeletedInTextField(Element* input)
928 {
929     // We're using the deleteBackward selector for all deletion operations since the autofill code treats all deletions the same way.
930     BEGIN_BLOCK_OBJC_EXCEPTIONS;
931     [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:@selector(deleteBackward:)];
932     END_BLOCK_OBJC_EXCEPTIONS;
933 }
934
935 const short enableRomanKeyboardsOnly = -23;
936 void FrameMac::setSecureKeyboardEntry(bool enable)
937 {
938     if (enable) {
939         EnableSecureEventInput();
940 // FIXME: KeyScript is deprecated in Leopard, we need a new solution for this <rdar://problem/4727607>
941 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
942         KeyScript(enableRomanKeyboardsOnly);
943 #endif
944     } else {
945         DisableSecureEventInput();
946 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
947         KeyScript(smKeyEnableKybds);
948 #endif
949     }
950 }
951
952 bool FrameMac::isSecureKeyboardEntry()
953 {
954     return IsSecureEventInputEnabled();
955 }
956
957 static void convertAttributesToUnderlines(Vector<MarkedTextUnderline>& result, const Range *markedTextRange, NSArray *attributes, NSArray *ranges)
958 {
959     int exception = 0;
960     int baseOffset = markedTextRange->startOffset(exception);
961
962     unsigned length = [attributes count];
963     ASSERT([ranges count] == length);
964
965     for (unsigned i = 0; i < length; i++) {
966         NSNumber *style = [[attributes objectAtIndex:i] objectForKey:NSUnderlineStyleAttributeName];
967         if (!style)
968             continue;
969         NSRange range = [[ranges objectAtIndex:i] rangeValue];
970         NSColor *color = [[attributes objectAtIndex:i] objectForKey:NSUnderlineColorAttributeName];
971         Color qColor = Color::black;
972         if (color) {
973             NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
974             qColor = Color(makeRGBA((int)(255 * [deviceColor redComponent]),
975                                     (int)(255 * [deviceColor blueComponent]),
976                                     (int)(255 * [deviceColor greenComponent]),
977                                     (int)(255 * [deviceColor alphaComponent])));
978         }
979
980         result.append(MarkedTextUnderline(range.location + baseOffset, 
981                                           range.location + baseOffset + range.length, 
982                                           qColor,
983                                           [style intValue] > 1));
984     }
985 }
986
987 void FrameMac::setMarkedTextRange(const Range *range, NSArray *attributes, NSArray *ranges)
988 {
989     int exception = 0;
990
991     ASSERT(!range || range->startContainer(exception) == range->endContainer(exception));
992     ASSERT(!range || range->collapsed(exception) || range->startContainer(exception)->isTextNode());
993
994     d->m_markedTextUnderlines.clear();
995     if (attributes == nil)
996         d->m_markedTextUsesUnderlines = false;
997     else {
998         d->m_markedTextUsesUnderlines = true;
999         convertAttributesToUnderlines(d->m_markedTextUnderlines, range, attributes, ranges);
1000     }
1001
1002     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
1003         m_markedTextRange->startContainer(exception)->renderer()->repaint();
1004
1005     if (range && range->collapsed(exception))
1006         m_markedTextRange = 0;
1007     else
1008         m_markedTextRange = const_cast<Range*>(range);
1009
1010     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
1011         m_markedTextRange->startContainer(exception)->renderer()->repaint();
1012 }
1013
1014 NSMutableDictionary *FrameMac::dashboardRegionsDictionary()
1015 {
1016     Document *doc = document();
1017     if (!doc)
1018         return nil;
1019
1020     const Vector<DashboardRegionValue>& regions = doc->dashboardRegions();
1021     size_t n = regions.size();
1022
1023     // Convert the Vector<DashboardRegionValue> into a NSDictionary of WebDashboardRegions
1024     NSMutableDictionary *webRegions = [NSMutableDictionary dictionaryWithCapacity:n];
1025     for (size_t i = 0; i < n; i++) {
1026         const DashboardRegionValue& region = regions[i];
1027
1028         if (region.type == StyleDashboardRegion::None)
1029             continue;
1030         
1031         NSString *label = region.label;
1032         WebDashboardRegionType type = WebDashboardRegionTypeNone;
1033         if (region.type == StyleDashboardRegion::Circle)
1034             type = WebDashboardRegionTypeCircle;
1035         else if (region.type == StyleDashboardRegion::Rectangle)
1036             type = WebDashboardRegionTypeRectangle;
1037         NSMutableArray *regionValues = [webRegions objectForKey:label];
1038         if (!regionValues) {
1039             regionValues = [[NSMutableArray alloc] initWithCapacity:1];
1040             [webRegions setObject:regionValues forKey:label];
1041             [regionValues release];
1042         }
1043         
1044         WebDashboardRegion *webRegion = [[WebDashboardRegion alloc] initWithRect:region.bounds clip:region.clip type:type];
1045         [regionValues addObject:webRegion];
1046         [webRegion release];
1047     }
1048     
1049     return webRegions;
1050 }
1051
1052 void FrameMac::dashboardRegionsChanged()
1053 {
1054     NSMutableDictionary *webRegions = dashboardRegionsDictionary();
1055     [_bridge dashboardRegionsChanged:webRegions];
1056 }
1057
1058 void FrameMac::willPopupMenu(NSMenu * menu)
1059 {
1060     [_bridge willPopupMenu:menu];
1061 }
1062
1063 bool FrameMac::isCharacterSmartReplaceExempt(UChar c, bool isPreviousChar)
1064 {
1065     return [_bridge isCharacterSmartReplaceExempt:c isPreviousCharacter:isPreviousChar];
1066 }
1067
1068 bool FrameMac::shouldClose()
1069 {
1070     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1071
1072     if (![_bridge canRunBeforeUnloadConfirmPanel])
1073         return true;
1074
1075     RefPtr<Document> doc = document();
1076     if (!doc)
1077         return true;
1078     HTMLElement* body = doc->body();
1079     if (!body)
1080         return true;
1081
1082     RefPtr<BeforeUnloadEvent> event = new BeforeUnloadEvent;
1083     event->setTarget(doc);
1084     doc->handleWindowEvent(event.get(), false);
1085
1086     if (!event->defaultPrevented() && doc)
1087         doc->defaultEventHandler(event.get());
1088     if (event->result().isNull())
1089         return true;
1090
1091     String text = event->result();
1092     text.replace('\\', backslashAsCurrencySymbol());
1093
1094     return [_bridge runBeforeUnloadConfirmPanelWithMessage:text];
1095
1096     END_BLOCK_OBJC_EXCEPTIONS;
1097
1098     return true;
1099 }
1100
1101 void Frame::setNeedsReapplyStyles()
1102 {
1103     [Mac(this)->_bridge setNeedsReapplyStyles];
1104 }
1105
1106 FloatRect FrameMac::customHighlightLineRect(const AtomicString& type, const FloatRect& lineRect)
1107 {
1108     return [_bridge customHighlightRect:type forLine:lineRect];
1109 }
1110
1111 void FrameMac::paintCustomHighlight(const AtomicString& type, const FloatRect& boxRect, const FloatRect& lineRect, bool text, bool line)
1112 {
1113     [_bridge paintCustomHighlight:type forBox:boxRect onLine:lineRect behindText:text entireLine:line];
1114 }
1115
1116 } // namespace WebCore