LayoutTests:
[WebKit-https.git] / WebCore / bridge / mac / FrameMac.mm
index c890e4d..ff491ac 100644 (file)
@@ -47,6 +47,7 @@
 #import "HTMLFormElement.h"
 #import "HTMLFrameElement.h"
 #import "HTMLGenericFormElement.h"
+#import "HTMLInputElement.h"
 #import "HTMLNames.h"
 #import "HTMLTableCellElement.h"
 #import "WebCoreEditCommand.h"
 #import "RenderTheme.h"
 #import "RenderView.h"
 #import "TextIterator.h"
-#import "TransferJob.h"
+#import "ResourceLoader.h"
 #import "WebCoreFrameBridge.h"
 #import "WebCoreViewFactory.h"
 #import "WebDashboardRegion.h"
+#import "WebScriptObjectPrivate.h"
 #import "csshelper.h"
+#import "htmlediting.h"
 #import "kjs_window.h"
 #import "visible_units.h"
+#import "WebCoreSystemInterface.h"
+#import <Carbon/Carbon.h>
 #import <JavaScriptCore/NP_jsobject.h>
-#import <JavaScriptCore/WebScriptObjectPrivate.h>
 #import <JavaScriptCore/npruntime_impl.h>
 
 #undef _webcore_TIMING
 
 @interface NSObject (WebPlugIn)
 - (id)objectForWebScript;
-- (void *)pluginScriptableObject;
+- (NPObject *)createPluginScriptableObject;
 @end
 
 using namespace std;
@@ -610,9 +614,15 @@ void FrameMac::setStatusBarText(const String& status)
     String text = status;
     text.replace('\\', backslashAsCurrencySymbol());
     
+    // We want the temporaries allocated here to be released even before returning to the 
+    // event loop; see <http://bugzilla.opendarwin.org/show_bug.cgi?id=9880>.
+    NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
+
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
     [_bridge setStatusText:text];
     END_BLOCK_OBJC_EXCEPTIONS;
+
+    [localPool release];
 }
 
 void FrameMac::scheduleClose()
@@ -627,17 +637,29 @@ void FrameMac::scheduleClose()
 void FrameMac::focusWindow()
 {
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
     // If we're a top level window, bring the window to the front.
     if (!tree()->parent())
         [_bridge activateWindow];
-    NSView *view = d->m_view->getDocumentView();
-    if ([_bridge firstResponder] != view)
-        [_bridge makeFirstResponder:view];
+
+    // Might not have a view yet: this could be a child frame that has not yet received its first byte of data.
+    // FIXME: Should remember that the frame needs focus.  See <rdar://problem/4645685>.
+    if (d->m_view) {
+        NSView *view = d->m_view->getDocumentView();
+        if ([_bridge firstResponder] != view)
+            [_bridge makeFirstResponder:view];
+    }
+
     END_BLOCK_OBJC_EXCEPTIONS;
 }
 
 void FrameMac::unfocusWindow()
 {
+    // Might not have a view yet: this could be a child frame that has not yet received its first byte of data.
+    // FIXME: Should remember that the frame needs to unfocus.  See <rdar://problem/4645685>.
+    if (!d->m_view)
+        return;
+
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
     NSView *view = d->m_view->getDocumentView();
     if ([_bridge firstResponder] == view) {
@@ -663,17 +685,18 @@ String FrameMac::advanceToNextMisspelling(bool startBeforeSelection)
     
     // Start at the end of the selection, search to edge of document.  Starting at the selection end makes
     // repeated "check spelling" commands work.
+    Selection selection(selectionController()->selection());
     RefPtr<Range> searchRange(rangeOfContents(document()));
     bool startedWithSelection = false;
-    if (selection().start().node()) {
+    if (selection.start().node()) {
         startedWithSelection = true;
         if (startBeforeSelection) {
-            VisiblePosition start(selection().start(), selection().affinity());
+            VisiblePosition start(selection.visibleStart());
             // We match AppKit's rule: Start 1 character before the selection.
             VisiblePosition oneBeforeStart = start.previous();
             setStart(searchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
         } else
-            setStart(searchRange.get(), VisiblePosition(selection().end(), selection().affinity()));
+            setStart(searchRange.get(), selection.visibleEnd());
     }
 
     // If we're not in an editable node, try to find one, make that our range to work in
@@ -689,7 +712,7 @@ String FrameMac::advanceToNextMisspelling(bool startBeforeSelection)
     
     // topNode defines the whole range we want to operate on 
     Node *topNode = editableNode->rootEditableElement();
-    searchRange->setEndAfter(topNode, exception);
+    searchRange->setEnd(topNode, maxDeepOffset(topNode), exception);
 
     // Make sure start of searchRange is not in the middle of a word.  Jumping back a char and then
     // forward by a word happens to do the trick.
@@ -735,7 +758,7 @@ String FrameMac::advanceToNextMisspelling(bool startBeforeSelection)
                     DeprecatedString result = chars.string(misspelling.length);
                     misspellingRange->setEnd(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
 
-                    setSelection(SelectionController(misspellingRange.get(), DOWNSTREAM));
+                    selectionController()->setSelection(Selection(misspellingRange.get(), DOWNSTREAM));
                     revealSelection();
                     // Mark misspelling in document.
                     document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
@@ -751,7 +774,7 @@ String FrameMac::advanceToNextMisspelling(bool startBeforeSelection)
             } else {
                 // we've gone from the selection to the end of doc, now wrap around
                 wrapped = YES;
-                searchRange->setStartBefore(topNode, exception);
+                searchRange->setStart(topNode, 0, exception);
                 // going until the end of the very first chunk we tested is far enough
                 searchRange->setEnd(searchEndAfterWrapNode, searchEndAfterWrapOffset, exception);
                 it = WordAwareIterator(searchRange.get());
@@ -870,24 +893,30 @@ String FrameMac::mimeTypeForFileName(const String& fileName) const
     return String();
 }
 
-NSView* FrameMac::nextKeyViewInFrame(Node* node, SelectionDirection direction, bool* focusCallResultedInViewBeingCreated)
+NSView* FrameMac::nextKeyViewInFrame(Node* n, SelectionDirection direction, bool* focusCallResultedInViewBeingCreated)
 {
     Document* doc = document();
     if (!doc)
         return nil;
     
+    RefPtr<Node> node = n;
     for (;;) {
         node = direction == SelectingNext
-            ? doc->nextFocusNode(node) : doc->previousFocusNode(node);
+            ? doc->nextFocusNode(node.get()) : doc->previousFocusNode(node.get());
         if (!node)
             return nil;
         
         RenderObject* renderer = node->renderer();
+        
         if (!renderer->isWidget()) {
-            static_cast<Element*>(node)->focus(); 
-            // The call to focus might have triggered event handlers that change the renderer type.
-            // FIXME: When all input elements are native, we won't need this extra check
-            if ((renderer = node->renderer()) && !renderer->isWidget()) {
+            static_cast<Element*>(node.get())->focus(); 
+            // The call to focus might have triggered event handlers that causes the 
+            // current renderer to be destroyed.
+            if (!(renderer = node->renderer()))
+                continue;
+                
+            // FIXME: When all input elements are native, we should investigate if this extra check is needed
+            if (!renderer->isWidget()) {
                 [_bridge willMakeFirstResponderForNodeFocus];
                 return [_bridge documentView];
             } else if (focusCallResultedInViewBeingCreated)
@@ -1448,7 +1477,11 @@ bool FrameMac::passMouseDownEventToWidget(Widget* widget)
         // In the case where we just became first responder, we should send the mouseDown:
         // to the NSTextField, not the NSTextField's editor. This code makes sure that happens.
         // If we don't do this, we see a flash of selected text when clicking in a text field.
-        if (![_bridge wasFirstResponderAtMouseDownTime:view] && [view isKindOfClass:[NSTextView class]]) {
+        // FIXME: This is the only caller of textViewWasFirstResponderAtMouseDownTime. When we
+        // eliminate all use of NSTextField/NSTextView in form fields we can eliminate this code,
+        // and textViewWasFirstResponderAtMouseDownTime:, and the instance variable WebHTMLView
+        // keeps solely to support textViewWasFirstResponderAtMouseDownTime:.
+        if ([view isKindOfClass:[NSTextView class]] && ![_bridge textViewWasFirstResponderAtMouseDownTime:(NSTextView *)view]) {
             NSView *superview = view;
             while (superview != nodeView) {
                 superview = [superview superview];
@@ -1693,7 +1726,7 @@ void FrameMac::handleMouseMoveEvent(const MouseEventWithHitTestResults& event)
                         _dragClipboard->setDragImageElement(_dragSrc.get(), IntPoint() + delta);
                     } 
 
-                    _mouseDownMayStartDrag = dispatchDragSrcEvent(dragstartEvent, m_mouseDown);
+                    _mouseDownMayStartDrag = dispatchDragSrcEvent(dragstartEvent, m_mouseDown) && mayCopy();
                     // Invalidate clipboard here against anymore pasteboard writing for security.  The drag
                     // image can still be changed as we drag, but not the pasteboard data.
                     _dragClipboard->setAccessPolicy(ClipboardMac::ImageWritable);
@@ -1720,7 +1753,7 @@ void FrameMac::handleMouseMoveEvent(const MouseEventWithHitTestResults& event)
                     BOOL startedDrag = [_bridge startDraggingImage:dragImage at:dragLoc operation:srcOp event:_currentEvent sourceIsDHTML:_dragSrcIsDHTML DHTMLWroteData:wcWrotePasteboard];
                     if (!startedDrag && _dragSrcMayBeDHTML) {
                         // WebKit canned the drag at the last minute - we owe _dragSrc a DRAGEND event
-                        PlatformMouseEvent event;
+                        PlatformMouseEvent event(PlatformMouseEvent::currentEvent);
                         dispatchDragSrcEvent(dragendEvent, event);
                         _mouseDownMayStartDrag = false;
                     }
@@ -1778,7 +1811,7 @@ void FrameMac::handleMouseMoveEvent(const MouseEventWithHitTestResults& event)
 // the event handler NOT setting the return value to false
 bool FrameMac::dispatchCPPEvent(const AtomicString &eventType, ClipboardMac::AccessPolicy policy)
 {
-    Node* target = d->m_selection.start().element();
+    Node* target = selectionController()->start().element();
     if (!target && document())
         target = document()->body();
     if (!target)
@@ -1804,23 +1837,26 @@ bool FrameMac::dispatchCPPEvent(const AtomicString &eventType, ClipboardMac::Acc
 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
 // normally selectable to implement copy/paste (like divs, or a document body).
 
-bool FrameMac::mayCut()
+bool FrameMac::mayDHTMLCut()
 {
-    return !dispatchCPPEvent(beforecutEvent, ClipboardMac::Numb);
+    return mayCopy() && !dispatchCPPEvent(beforecutEvent, ClipboardMac::Numb);
 }
 
-bool FrameMac::mayCopy()
+bool FrameMac::mayDHTMLCopy()
 {
-    return !dispatchCPPEvent(beforecopyEvent, ClipboardMac::Numb);
+    return mayCopy() && !dispatchCPPEvent(beforecopyEvent, ClipboardMac::Numb);
 }
 
-bool FrameMac::mayPaste()
+bool FrameMac::mayDHTMLPaste()
 {
     return !dispatchCPPEvent(beforepasteEvent, ClipboardMac::Numb);
 }
 
-bool FrameMac::tryCut()
+bool FrameMac::tryDHTMLCut()
 {
+    if (!mayCopy())
+        return false;
+
     // Must be done before oncut adds types and data to the pboard,
     // also done for security, as it erases data from the last copy/paste.
     [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
@@ -1828,8 +1864,11 @@ bool FrameMac::tryCut()
     return !dispatchCPPEvent(cutEvent, ClipboardMac::Writable);
 }
 
-bool FrameMac::tryCopy()
+bool FrameMac::tryDHTMLCopy()
 {
+    if (!mayCopy())
+        return false;
+
     // Must be done before oncopy adds types and data to the pboard,
     // also done for security, as it erases data from the last copy/paste.
     [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
@@ -1837,7 +1876,7 @@ bool FrameMac::tryCopy()
     return !dispatchCPPEvent(copyEvent, ClipboardMac::Writable);
 }
 
-bool FrameMac::tryPaste()
+bool FrameMac::tryDHTMLPaste()
 {
     return !dispatchCPPEvent(pasteEvent, ClipboardMac::Readable);
 }
@@ -2736,7 +2775,7 @@ NSFont *FrameMac::fontForSelection(bool *hasMultipleFonts) const
     if (hasMultipleFonts)
         *hasMultipleFonts = false;
 
-    if (!d->m_selection.isRange()) {
+    if (!selectionController()->isRange()) {
         Node *nodeToRemove;
         RenderStyle *style = styleForSelectionStart(nodeToRemove); // sets nodeToRemove
 
@@ -2755,7 +2794,7 @@ NSFont *FrameMac::fontForSelection(bool *hasMultipleFonts) const
 
     NSFont *font = nil;
 
-    RefPtr<Range> range = d->m_selection.toRange();
+    RefPtr<Range> range = selectionController()->toRange();
     Node *startNode = range->editingStartPosition().node();
     if (startNode != nil) {
         Node *pastEnd = range->pastEndNode();
@@ -2849,7 +2888,7 @@ NSWritingDirection FrameMac::baseWritingDirectionForSelectionStart() const
 {
     NSWritingDirection result = NSWritingDirectionLeftToRight;
 
-    Position pos = VisiblePosition(d->m_selection.start(), d->m_selection.affinity()).deepEquivalent();
+    Position pos = selectionController()->selection().visibleStart().deepEquivalent();
     Node *node = pos.node();
     if (!node || !node->renderer() || !node->renderer()->containingBlock())
         return result;
@@ -2968,11 +3007,16 @@ static KJS::Bindings::Instance *getInstanceForView(NSView *aView)
             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::ObjectiveCLanguage, object, executionContext);
         }
     }
-    else if ([aView respondsToSelector:@selector(pluginScriptableObject)]){
-        void *object = [aView pluginScriptableObject];
+    else if ([aView respondsToSelector:@selector(createPluginScriptableObject)]) {
+        NPObject *object = [aView createPluginScriptableObject];
         if (object) {
-            KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
-            return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::CLanguage, object, executionContext);
+            KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction()(aView);
+            KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance(KJS::Bindings::Instance::CLanguage, object, executionContext);
+            
+            // -createPluginScriptableObject returns a retained NPObject.  The caller is expected to release it.
+            _NPN_ReleaseObject(object);
+            
+            return instance;
         }
     }
     return 0;
@@ -3003,32 +3047,45 @@ void FrameMac::cleanupPluginRootObjects()
     m_rootObjects.clear();
 }
 
-void FrameMac::registerCommandForUndoOrRedo(const EditCommandPtr &cmd, bool isRedo)
+void FrameMac::registerCommandForUndoOrRedo(PassRefPtr<EditCommand> cmd, bool isRedo)
 {
-    ASSERT(cmd.get());
-    WebCoreEditCommand *kwq = [WebCoreEditCommand commandWithEditCommand:cmd.get()];
-    NSUndoManager *undoManager = [_bridge undoManager];
-    [undoManager registerUndoWithTarget:_bridge selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:kwq];
-    NSString *actionName = [_bridge nameForUndoAction:static_cast<WebUndoAction>(cmd.editingAction())];
+    ASSERT(cmd);
+    WebUndoAction action = static_cast<WebUndoAction>(cmd->editingAction());
+    NSUndoManager* undoManager = [_bridge undoManager];
+    WebCoreEditCommand* command = [WebCoreEditCommand commandWithEditCommand:cmd];
+    NSString* actionName = [_bridge nameForUndoAction:action];
+    [undoManager registerUndoWithTarget:_bridge selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:command];
     if (actionName)
         [undoManager setActionName:actionName];
     _haveUndoRedoOperations = YES;
 }
 
-void FrameMac::registerCommandForUndo(const EditCommandPtr &cmd)
+void FrameMac::registerCommandForUndo(PassRefPtr<EditCommand> cmd)
 {
-    registerCommandForUndoOrRedo(cmd, NO);
+    registerCommandForUndoOrRedo(cmd, false);
 }
 
-void FrameMac::registerCommandForRedo(const EditCommandPtr &cmd)
+void FrameMac::registerCommandForRedo(PassRefPtr<EditCommand> cmd)
 {
-    registerCommandForUndoOrRedo(cmd, YES);
+    registerCommandForUndoOrRedo(cmd, true);
 }
 
 void FrameMac::clearUndoRedoOperations()
 {
     if (_haveUndoRedoOperations) {
-        [[_bridge undoManager] removeAllActionsWithTarget:_bridge];
+        // workaround for <rdar://problem/4645507> NSUndoManager dies
+        // with uncaught exception when undo items cleared while
+        // groups are open
+        NSUndoManager *undoManager = [_bridge undoManager];
+        int groupingLevel = [undoManager groupingLevel];
+        for (int i = 0; i < groupingLevel; ++i)
+            [undoManager endUndoGrouping];
+        
+        [undoManager removeAllActionsWithTarget:_bridge];
+
+        for (int i = 0; i < groupingLevel; ++i)
+            [undoManager beginUndoGrouping];
+
         _haveUndoRedoOperations = NO;
     }
 }
@@ -3089,10 +3146,10 @@ void FrameMac::markMisspellingsInAdjacentWords(const VisiblePosition &p)
 {
     if (![_bridge isContinuousSpellCheckingEnabled])
         return;
-    markMisspellings(SelectionController(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
+    markMisspellings(Selection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
 }
 
-void FrameMac::markMisspellings(const SelectionController &selection)
+void FrameMac::markMisspellings(const Selectionselection)
 {
     // This function is called with a selection already expanded to word boundaries.
     // Might be nice to assert that here.
@@ -3149,21 +3206,21 @@ void FrameMac::markMisspellings(const SelectionController &selection)
     }
 }
 
-void FrameMac::respondToChangedSelection(const SelectionController &oldSelection, bool closeTyping)
+void FrameMac::respondToChangedSelection(const Selection &oldSelection, bool closeTyping)
 {
     if (document()) {
         if ([_bridge isContinuousSpellCheckingEnabled]) {
-            SelectionController oldAdjacentWords = SelectionController();
+            Selection oldAdjacentWords;
             
             // If this is a change in selection resulting from a delete operation, oldSelection may no longer
             // be in the document.
             if (oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
-                VisiblePosition oldStart(oldSelection.start(), oldSelection.affinity());
-                oldAdjacentWords = SelectionController(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));   
+                VisiblePosition oldStart(oldSelection.visibleStart());
+                oldAdjacentWords = Selection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));   
             }
 
-            VisiblePosition newStart(selection().start(), selection().affinity());
-            SelectionController newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
+            VisiblePosition newStart(selectionController()->selection().visibleStart());
+            Selection newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
 
             // When typing we check spelling elsewhere, so don't redo it here.
             if (closeTyping && oldAdjacentWords != newAdjacentWords)
@@ -3180,7 +3237,7 @@ void FrameMac::respondToChangedSelection(const SelectionController &oldSelection
     [_bridge respondToChangedSelection];
 }
 
-bool FrameMac::shouldChangeSelection(const SelectionController &oldSelection, const SelectionController &newSelection, EAffinity affinity, bool stillSelecting) const
+bool FrameMac::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const
 {
     return [_bridge shouldChangeSelectedDOMRange:[DOMRange _rangeWith:oldSelection.toRange().get()]
                                       toDOMRange:[DOMRange _rangeWith:newSelection.toRange().get()]
@@ -3188,15 +3245,18 @@ bool FrameMac::shouldChangeSelection(const SelectionController &oldSelection, co
                                   stillSelecting:stillSelecting];
 }
 
-bool FrameMac::shouldDeleteSelection(const SelectionController &selection) const
+bool FrameMac::shouldDeleteSelection(const Selectionselection) const
 {
     return [_bridge shouldDeleteSelectedDOMRange:[DOMRange _rangeWith:selection.toRange().get()]];
 }
 
-void FrameMac::respondToChangedContents()
+void FrameMac::respondToChangedContents(const Selection& selection)
 {
-    if (AXObjectCache::accessibilityEnabled())
-        renderer()->document()->axObjectCache()->postNotificationToTopWebArea(renderer(), "AXValueChanged");
+    if (AXObjectCache::accessibilityEnabled()) {
+        Node* node = selection.start().node();
+        if (node)
+            renderer()->document()->axObjectCache()->postNotification(node->renderer(), "AXValueChanged");
+    }
     [_bridge respondToChangedContents];
 }
 
@@ -3257,6 +3317,11 @@ void FrameMac::textDidChangeInTextArea(Element* textarea)
 
 bool FrameMac::doTextFieldCommandFromEvent(Element* input, const PlatformKeyboardEvent* event)
 {
+    // FIXME: We might eventually need to make sure key bindings for editing work even with
+    // events created with the DOM API. Those don't have a PlatformKeyboardEvent.
+    if (!event)
+        return false;
+
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
     return [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:selectorForKeyEvent(event)];
     END_BLOCK_OBJC_EXCEPTIONS;
@@ -3271,6 +3336,31 @@ void FrameMac::textWillBeDeletedInTextField(Element* input)
     END_BLOCK_OBJC_EXCEPTIONS;
 }
 
+bool FrameMac::inputManagerHasMarkedText() const
+{
+    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+    return [[NSInputManager currentInputManager] hasMarkedText];
+    END_BLOCK_OBJC_EXCEPTIONS
+    return false;
+}
+
+const short enableRomanKeyboardsOnly = -23;
+void FrameMac::setSecureKeyboardEntry(bool enable)
+{
+    if (enable) {
+        EnableSecureEventInput();
+        KeyScript(enableRomanKeyboardsOnly);
+    } else {
+        DisableSecureEventInput();
+        KeyScript(smKeyEnableKybds);
+    }
+}
+
+bool FrameMac::isSecureKeyboardEntry()
+{
+    return IsSecureEventInputEnabled();
+}
+
 static DeprecatedValueList<MarkedTextUnderline> convertAttributesToUnderlines(const Range *markedTextRange, NSArray *attributes, NSArray *ranges)
 {
     DeprecatedValueList<MarkedTextUnderline> result;