2 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #import "AXObjectCache.h"
30 #import "BeforeUnloadEvent.h"
31 #import "BlockExceptions.h"
32 #import "BrowserExtensionMac.h"
33 #import "CSSComputedStyleDeclaration.h"
35 #import "ClipboardEvent.h"
37 #import "DOMInternal.h"
41 #import "EventNames.h"
44 #import "FoundationExtras.h"
45 #import "FramePrivate.h"
46 #import "GraphicsContext.h"
47 #import "HTMLDocument.h"
48 #import "HTMLFormElement.h"
49 #import "HTMLFrameElement.h"
50 #import "HTMLGenericFormElement.h"
51 #import "HTMLInputElement.h"
53 #import "HTMLTableCellElement.h"
54 #import "WebCoreEditCommand.h"
55 #import "FormDataMac.h"
56 #import "WebCorePageState.h"
58 #import "MouseEventWithHitTestResults.h"
59 #import "PlatformKeyboardEvent.h"
60 #import "PlatformScrollBar.h"
61 #import "PlatformWheelEvent.h"
63 #import "RegularExpression.h"
64 #import "RenderImage.h"
65 #import "RenderListItem.h"
66 #import "RenderPart.h"
67 #import "RenderTableCell.h"
68 #import "RenderTheme.h"
69 #import "RenderView.h"
70 #import "TextIterator.h"
71 #import "ResourceLoader.h"
72 #import "WebCoreFrameBridge.h"
73 #import "WebCoreViewFactory.h"
74 #import "WebDashboardRegion.h"
75 #import "WebScriptObjectPrivate.h"
77 #import "htmlediting.h"
78 #import "kjs_window.h"
79 #import "visible_units.h"
80 #import "WebCoreSystemInterface.h"
81 #import <Carbon/Carbon.h>
82 #import <JavaScriptCore/NP_jsobject.h>
83 #import <JavaScriptCore/npruntime_impl.h>
85 #undef _webcore_TIMING
87 @interface NSObject (WebPlugIn)
88 - (id)objectForWebScript;
89 - (NPObject *)createPluginScriptableObject;
93 using namespace KJS::Bindings;
96 using KJS::PausedTimeouts;
97 using KJS::SavedBuiltins;
98 using KJS::SavedProperties;
102 using namespace EventNames;
103 using namespace HTMLNames;
105 NSEvent* FrameMac::_currentEvent = nil;
107 static NSMutableDictionary* createNSDictionary(const HashMap<String, String>& map)
109 NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithCapacity:map.size()];
110 HashMap<String, String>::const_iterator end = map.end();
111 for (HashMap<String, String>::const_iterator it = map.begin(); it != end; ++it) {
112 NSString* key = it->first;
113 NSString* object = it->second;
114 [dict setObject:object forKey:key];
119 static const unsigned int escChar = 27;
120 static SEL selectorForKeyEvent(const PlatformKeyboardEvent* event)
122 // FIXME: This helper function is for the autofill code so the bridge can pass a selector to the form delegate.
123 // Eventually, we should move all of the autofill code down to WebKit and remove the need for this function by
124 // not relying on the selector in the new implementation.
125 String key = event->unmodifiedText();
126 if (key.length() != 1)
131 case NSUpArrowFunctionKey:
132 selector = @selector(moveUp:); break;
133 case NSDownArrowFunctionKey:
134 selector = @selector(moveDown:); break;
136 selector = @selector(cancel:); break;
138 selector = @selector(insertTab:); break;
139 case NSBackTabCharacter:
140 selector = @selector(insertBacktab:); break;
141 case NSNewlineCharacter:
142 case NSCarriageReturnCharacter:
143 case NSEnterCharacter:
144 selector = @selector(insertNewline:); break;
150 FrameMac::FrameMac(Page* page, Element* ownerElement)
151 : Frame(page, ownerElement)
153 , _mouseDownView(nil)
154 , _sendingEventToSubview(false)
155 , _mouseDownMayStartSelect(false)
156 , _activationEventNumber(0)
158 , _windowScriptObject(0)
159 , _windowScriptNPObject(0)
161 d->m_extension = new BrowserExtensionMac(this);
164 FrameMac::~FrameMac()
168 clearRecordedFormValues();
170 [_bridge clearFrame];
171 HardRelease(_bridge);
177 void FrameMac::freeClipboard()
180 _dragClipboard->setAccessPolicy(ClipboardMac::Numb);
183 bool FrameMac::openURL(const KURL &url)
185 BEGIN_BLOCK_OBJC_EXCEPTIONS;
187 // FIXME: The lack of args here to get the reload flag from
188 // indicates a problem in how we use Frame::processObjectRequest,
189 // where we are opening the URL before the args are set up.
190 [_bridge loadURL:url.getNSURL()
191 referrer:[_bridge referrer]
193 userGesture:userGestureHint()
199 END_BLOCK_OBJC_EXCEPTIONS;
204 void FrameMac::openURLRequest(const ResourceRequest& request)
206 BEGIN_BLOCK_OBJC_EXCEPTIONS;
209 String argsReferrer = request.referrer();
210 if (!argsReferrer.isEmpty())
211 referrer = argsReferrer;
213 referrer = [_bridge referrer];
215 [_bridge loadURL:request.url().getNSURL()
217 reload:request.reload
218 userGesture:userGestureHint()
219 target:request.frameName
224 END_BLOCK_OBJC_EXCEPTIONS;
228 // Either get cached regexp or build one that matches any of the labels.
229 // The regexp we build is of the form: (STR1|STR2|STRN)
230 RegularExpression *regExpForLabels(NSArray *labels)
232 // All the ObjC calls in this method are simple array and string
233 // calls which we can assume do not raise exceptions
236 // Parallel arrays that we use to cache regExps. In practice the number of expressions
237 // that the app will use is equal to the number of locales is used in searching.
238 static const unsigned int regExpCacheSize = 4;
239 static NSMutableArray *regExpLabels = nil;
240 static Vector<RegularExpression*> regExps;
241 static RegularExpression wordRegExp = RegularExpression("\\w");
243 RegularExpression *result;
245 regExpLabels = [[NSMutableArray alloc] initWithCapacity:regExpCacheSize];
246 CFIndex cacheHit = [regExpLabels indexOfObject:labels];
247 if (cacheHit != NSNotFound)
248 result = regExps.at(cacheHit);
250 DeprecatedString pattern("(");
251 unsigned int numLabels = [labels count];
253 for (i = 0; i < numLabels; i++) {
254 DeprecatedString label = DeprecatedString::fromNSString((NSString *)[labels objectAtIndex:i]);
256 bool startsWithWordChar = false;
257 bool endsWithWordChar = false;
258 if (label.length() != 0) {
259 startsWithWordChar = wordRegExp.search(label.at(0)) >= 0;
260 endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0;
265 // Search for word boundaries only if label starts/ends with "word characters".
266 // If we always searched for word boundaries, this wouldn't work for languages
268 if (startsWithWordChar) {
269 pattern.append("\\b");
271 pattern.append(label);
272 if (endsWithWordChar) {
273 pattern.append("\\b");
277 result = new RegularExpression(pattern, false);
280 // add regexp to the cache, making sure it is at the front for LRU ordering
282 if (cacheHit != NSNotFound) {
283 // remove from old spot
284 [regExpLabels removeObjectAtIndex:cacheHit];
285 regExps.remove(cacheHit);
288 [regExpLabels insertObject:labels atIndex:0];
289 regExps.insert(0, result);
291 if ([regExpLabels count] > regExpCacheSize) {
292 [regExpLabels removeObjectAtIndex:regExpCacheSize];
293 RegularExpression *last = regExps.last();
294 regExps.removeLast();
301 NSString* FrameMac::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell)
303 RenderTableCell* cellRenderer = static_cast<RenderTableCell*>(cell->renderer());
305 if (cellRenderer && cellRenderer->isTableCell()) {
306 RenderTableCell* cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
308 if (cellAboveRenderer) {
309 HTMLTableCellElement *aboveCell =
310 static_cast<HTMLTableCellElement*>(cellAboveRenderer->element());
313 // search within the above cell we found for a match
314 for (Node *n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
315 if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
316 // For each text chunk, run the regexp
317 DeprecatedString nodeString = n->nodeValue().deprecatedString();
318 int pos = regExp->searchRev(nodeString);
320 return nodeString.mid(pos, regExp->matchedLength()).getNSString();
326 // Any reason in practice to search all cells in that are above cell?
330 NSString *FrameMac::searchForLabelsBeforeElement(NSArray *labels, Element *element)
332 RegularExpression *regExp = regExpForLabels(labels);
333 // We stop searching after we've seen this many chars
334 const unsigned int charsSearchedThreshold = 500;
335 // This is the absolute max we search. We allow a little more slop than
336 // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
337 const unsigned int maxCharsSearched = 600;
338 // If the starting element is within a table, the cell that contains it
339 HTMLTableCellElement *startingTableCell = 0;
340 bool searchedCellAbove = false;
342 // walk backwards in the node tree, until another element, or form, or end of tree
343 int unsigned lengthSearched = 0;
345 for (n = element->traversePreviousNode();
346 n && lengthSearched < charsSearchedThreshold;
347 n = n->traversePreviousNode())
349 if (n->hasTagName(formTag)
350 || (n->isHTMLElement()
351 && static_cast<HTMLElement*>(n)->isGenericFormElement()))
353 // We hit another form element or the start of the form - bail out
355 } else if (n->hasTagName(tdTag) && !startingTableCell) {
356 startingTableCell = static_cast<HTMLTableCellElement*>(n);
357 } else if (n->hasTagName(trTag) && startingTableCell) {
358 NSString *result = searchForLabelsAboveCell(regExp, startingTableCell);
362 searchedCellAbove = true;
363 } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
364 // For each text chunk, run the regexp
365 DeprecatedString nodeString = n->nodeValue().deprecatedString();
366 // add 100 for slop, to make it more likely that we'll search whole nodes
367 if (lengthSearched + nodeString.length() > maxCharsSearched)
368 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
369 int pos = regExp->searchRev(nodeString);
371 return nodeString.mid(pos, regExp->matchedLength()).getNSString();
373 lengthSearched += nodeString.length();
377 // If we started in a cell, but bailed because we found the start of the form or the
378 // previous element, we still might need to search the row above us for a label.
379 if (startingTableCell && !searchedCellAbove) {
380 return searchForLabelsAboveCell(regExp, startingTableCell);
386 NSString *FrameMac::matchLabelsAgainstElement(NSArray *labels, Element *element)
388 DeprecatedString name = element->getAttribute(nameAttr).deprecatedString();
389 // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
390 name.replace(RegularExpression("[[:digit:]]"), " ");
391 name.replace('_', ' ');
393 RegularExpression *regExp = regExpForLabels(labels);
394 // Use the largest match we can find in the whole name string
401 pos = regExp->search(name, start);
403 length = regExp->matchedLength();
404 if (length >= bestLength) {
413 return name.mid(bestPos, bestLength).getNSString();
417 void FrameMac::submitForm(const ResourceRequest& request)
419 BEGIN_BLOCK_OBJC_EXCEPTIONS;
421 // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way.
422 // We do not want to submit more than one form from the same page,
423 // nor do we want to submit a single form more than once.
424 // This flag prevents these from happening; not sure how other browsers prevent this.
425 // The flag is reset in each time we start handle a new mouse or key down event, and
426 // also in setView since this part may get reused for a page from the back/forward cache.
427 // The form multi-submit logic here is only needed when we are submitting a form that affects this frame.
428 // FIXME: Frame targeting is only one of the ways the submission could end up doing something other
429 // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly
430 // needed any more now that we reset d->m_submittedFormURL on each mouse or key down event.
431 WebCoreFrameBridge *target = request.frameName.isEmpty() ? _bridge : [_bridge findFrameNamed:request.frameName];
432 Frame *targetPart = [target impl];
433 bool willReplaceThisFrame = false;
434 for (Frame *p = this; p; p = p->tree()->parent()) {
435 if (p == targetPart) {
436 willReplaceThisFrame = true;
440 if (willReplaceThisFrame) {
441 if (d->m_submittedFormURL == request.url())
443 d->m_submittedFormURL = request.url();
446 ObjCDOMElement* submitForm = [DOMElement _elementWith:d->m_formAboutToBeSubmitted.get()];
447 NSMutableDictionary* formValues = createNSDictionary(d->m_formValuesAboutToBeSubmitted);
449 if (!request.doPost()) {
450 [_bridge loadURL:request.url().getNSURL()
451 referrer:[_bridge referrer]
452 reload:request.reload
454 target:request.frameName
455 triggeringEvent:_currentEvent
457 formValues:formValues];
459 ASSERT(request.contentType().startsWith("Content-Type: "));
460 [_bridge postWithURL:request.url().getNSURL()
461 referrer:[_bridge referrer]
462 target:request.frameName
463 data:arrayFromFormData(request.postData)
464 contentType:request.contentType().substring(14)
465 triggeringEvent:_currentEvent
467 formValues:formValues];
469 [formValues release];
470 clearRecordedFormValues();
472 END_BLOCK_OBJC_EXCEPTIONS;
475 void FrameMac::frameDetached()
477 Frame::frameDetached();
479 BEGIN_BLOCK_OBJC_EXCEPTIONS;
480 [Mac(this)->bridge() frameDetached];
481 END_BLOCK_OBJC_EXCEPTIONS;
484 void FrameMac::urlSelected(const ResourceRequest& request)
486 BEGIN_BLOCK_OBJC_EXCEPTIONS;
489 String argsReferrer = request.referrer();
490 if (!argsReferrer.isEmpty())
491 referrer = argsReferrer;
493 referrer = [_bridge referrer];
495 [_bridge loadURL:request.url().getNSURL()
497 reload:request.reload
499 target:request.frameName
500 triggeringEvent:_currentEvent
504 END_BLOCK_OBJC_EXCEPTIONS;
507 ObjectContentType FrameMac::objectContentType(const KURL& url, const String& mimeType)
509 return (ObjectContentType)[_bridge determineObjectFromMIMEType:mimeType URL:url.getNSURL()];
512 static NSArray* nsArray(const Vector<String>& vector)
514 unsigned len = vector.size();
515 NSMutableArray* array = [NSMutableArray arrayWithCapacity:len];
516 for (unsigned x = 0; x < len; x++)
517 [array addObject:vector[x]];
521 Plugin* FrameMac::createPlugin(Element* element, const KURL& url, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType)
523 BEGIN_BLOCK_OBJC_EXCEPTIONS;
525 return new Plugin(new Widget([_bridge viewForPluginWithURL:url.getNSURL()
526 attributeNames:nsArray(paramNames)
527 attributeValues:nsArray(paramValues)
529 DOMElement:(element ? [DOMElement _elementWith:element] : nil)
530 loadManually:d->m_doc->isPluginDocument()]));
532 END_BLOCK_OBJC_EXCEPTIONS;
536 void FrameMac::redirectDataToPlugin(Widget* pluginWidget)
538 [_bridge redirectDataToPlugin:pluginWidget->getView()];
542 Frame* FrameMac::createFrame(const KURL& url, const String& name, Element* ownerElement, const String& referrer)
544 BEGIN_BLOCK_OBJC_EXCEPTIONS;
546 BOOL allowsScrolling = YES;
547 int marginWidth = -1;
548 int marginHeight = -1;
549 if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) {
550 HTMLFrameElement* o = static_cast<HTMLFrameElement*>(ownerElement);
551 allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff;
552 marginWidth = o->getMarginWidth();
553 marginHeight = o->getMarginHeight();
556 WebCoreFrameBridge *childBridge = [_bridge createChildFrameNamed:name
557 withURL:url.getNSURL()
559 ownerElement:ownerElement
560 allowsScrolling:allowsScrolling
561 marginWidth:marginWidth
562 marginHeight:marginHeight];
563 return [childBridge impl];
565 END_BLOCK_OBJC_EXCEPTIONS;
569 void FrameMac::setView(FrameView *view)
571 Frame::setView(view);
573 // Only one form submission is allowed per view of a part.
574 // Since this part may be getting reused as a result of being
575 // pulled from the back/forward cache, reset this flag.
576 d->m_submittedFormURL = KURL();
579 void FrameMac::setTitle(const String &title)
582 text.replace('\\', backslashAsCurrencySymbol());
584 BEGIN_BLOCK_OBJC_EXCEPTIONS;
585 [_bridge setTitle:text];
586 END_BLOCK_OBJC_EXCEPTIONS;
589 void FrameMac::setStatusBarText(const String& status)
591 String text = status;
592 text.replace('\\', backslashAsCurrencySymbol());
594 // We want the temporaries allocated here to be released even before returning to the
595 // event loop; see <http://bugzilla.opendarwin.org/show_bug.cgi?id=9880>.
596 NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
598 BEGIN_BLOCK_OBJC_EXCEPTIONS;
599 [_bridge setStatusText:text];
600 END_BLOCK_OBJC_EXCEPTIONS;
605 void FrameMac::scheduleClose()
609 BEGIN_BLOCK_OBJC_EXCEPTIONS;
610 [_bridge closeWindowSoon];
611 END_BLOCK_OBJC_EXCEPTIONS;
614 void FrameMac::focusWindow()
616 BEGIN_BLOCK_OBJC_EXCEPTIONS;
618 // If we're a top level window, bring the window to the front.
619 if (!tree()->parent())
620 [_bridge activateWindow];
622 // Might not have a view yet: this could be a child frame that has not yet received its first byte of data.
623 // FIXME: Should remember that the frame needs focus. See <rdar://problem/4645685>.
625 NSView *view = d->m_view->getDocumentView();
626 if ([_bridge firstResponder] != view)
627 [_bridge makeFirstResponder:view];
630 END_BLOCK_OBJC_EXCEPTIONS;
633 void FrameMac::unfocusWindow()
635 // Might not have a view yet: this could be a child frame that has not yet received its first byte of data.
636 // FIXME: Should remember that the frame needs to unfocus. See <rdar://problem/4645685>.
640 BEGIN_BLOCK_OBJC_EXCEPTIONS;
641 NSView *view = d->m_view->getDocumentView();
642 if ([_bridge firstResponder] == view) {
643 // If we're a top level window, deactivate the window.
644 if (!tree()->parent())
645 [_bridge deactivateWindow];
647 // We want to shift focus to our parent.
648 FrameMac* parentFrame = static_cast<FrameMac*>(tree()->parent());
649 NSView* parentView = parentFrame->d->m_view->getDocumentView();
650 [parentFrame->bridge() makeFirstResponder:parentView];
653 END_BLOCK_OBJC_EXCEPTIONS;
656 String FrameMac::advanceToNextMisspelling(bool startBeforeSelection)
660 // The basic approach is to search in two phases - from the selection end to the end of the doc, and
661 // then we wrap and search from the doc start to (approximately) where we started.
663 // Start at the end of the selection, search to edge of document. Starting at the selection end makes
664 // repeated "check spelling" commands work.
665 Selection selection(selectionController()->selection());
666 RefPtr<Range> searchRange(rangeOfContents(document()));
667 bool startedWithSelection = false;
668 if (selection.start().node()) {
669 startedWithSelection = true;
670 if (startBeforeSelection) {
671 VisiblePosition start(selection.visibleStart());
672 // We match AppKit's rule: Start 1 character before the selection.
673 VisiblePosition oneBeforeStart = start.previous();
674 setStart(searchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
676 setStart(searchRange.get(), selection.visibleEnd());
679 // If we're not in an editable node, try to find one, make that our range to work in
680 Node *editableNode = searchRange->startContainer(exception);
681 if (!editableNode->isContentEditable()) {
682 editableNode = editableNode->nextEditable();
686 searchRange->setStartBefore(editableNode, exception);
687 startedWithSelection = false; // won't need to wrap
690 // topNode defines the whole range we want to operate on
691 Node *topNode = editableNode->rootEditableElement();
692 searchRange->setEnd(topNode, maxDeepOffset(topNode), exception);
694 // Make sure start of searchRange is not in the middle of a word. Jumping back a char and then
695 // forward by a word happens to do the trick.
696 if (startedWithSelection) {
697 VisiblePosition oneBeforeStart = startVisiblePosition(searchRange.get(), DOWNSTREAM).previous();
698 if (oneBeforeStart.isNotNull()) {
699 setStart(searchRange.get(), endOfWord(oneBeforeStart));
700 } // else we were already at the start of the editable node
703 if (searchRange->collapsed(exception))
704 return String(); // nothing to search in
706 // Get the spell checker if it is available
707 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
711 WordAwareIterator it(searchRange.get());
712 bool wrapped = false;
714 // We go to the end of our first range instead of the start of it, just to be sure
715 // we don't get foiled by any word boundary problems at the start. It means we might
716 // do a tiny bit more searching.
717 Node *searchEndAfterWrapNode = it.range()->endContainer(exception);
718 int searchEndAfterWrapOffset = it.range()->endOffset(exception);
721 if (!it.atEnd()) { // we may be starting at the end of the doc, and already by atEnd
722 const UChar* chars = it.characters();
723 int len = it.length();
724 if (len > 1 || !DeprecatedChar(chars[0]).isSpace()) {
725 NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(chars) length:len freeWhenDone:NO];
726 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:[_bridge spellCheckerDocumentTag] wordCount:NULL];
728 if (misspelling.length > 0) {
729 // Build up result range and string. Note the misspelling may span many text nodes,
730 // but the CharIterator insulates us from this complexity
731 RefPtr<Range> misspellingRange(rangeOfContents(document()));
732 CharacterIterator chars(it.range().get());
733 chars.advance(misspelling.location);
734 misspellingRange->setStart(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
735 DeprecatedString result = chars.string(misspelling.length);
736 misspellingRange->setEnd(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
738 selectionController()->setSelection(Selection(misspellingRange.get(), DOWNSTREAM));
740 // Mark misspelling in document.
741 document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
749 if (wrapped || !startedWithSelection) {
750 break; // finished the second range, or we did the whole doc with the first range
752 // we've gone from the selection to the end of doc, now wrap around
754 searchRange->setStart(topNode, 0, exception);
755 // going until the end of the very first chunk we tested is far enough
756 searchRange->setEnd(searchEndAfterWrapNode, searchEndAfterWrapOffset, exception);
757 it = WordAwareIterator(searchRange.get());
765 bool FrameMac::wheelEvent(NSEvent *event)
767 FrameView *v = d->m_view.get();
770 NSEvent *oldCurrentEvent = _currentEvent;
771 _currentEvent = HardRetain(event);
773 PlatformWheelEvent qEvent(event);
774 v->handleWheelEvent(qEvent);
776 ASSERT(_currentEvent == event);
778 _currentEvent = oldCurrentEvent;
780 if (qEvent.isAccepted())
787 void FrameMac::startRedirectionTimer()
789 stopRedirectionTimer();
791 Frame::startRedirectionTimer();
793 // Don't report history navigations, just actual redirection.
794 if (d->m_scheduledRedirection != historyNavigationScheduled) {
795 NSTimeInterval interval = d->m_redirectionTimer.nextFireInterval();
796 NSDate *fireDate = [[NSDate alloc] initWithTimeIntervalSinceNow:interval];
797 [_bridge reportClientRedirectToURL:KURL(d->m_redirectURL).getNSURL()
798 delay:d->m_delayRedirect
800 lockHistory:d->m_redirectLockHistory
801 isJavaScriptFormAction:d->m_executingJavaScriptFormAction];
806 void FrameMac::stopRedirectionTimer()
808 bool wasActive = d->m_redirectionTimer.isActive();
810 Frame::stopRedirectionTimer();
812 // Don't report history navigations, just actual redirection.
813 if (wasActive && d->m_scheduledRedirection != historyNavigationScheduled)
814 [_bridge reportClientRedirectCancelled:d->m_cancelWithLoadInProgress];
817 String FrameMac::userAgent() const
819 BEGIN_BLOCK_OBJC_EXCEPTIONS;
820 return [_bridge userAgentForURL:url().getNSURL()];
821 END_BLOCK_OBJC_EXCEPTIONS;
826 String FrameMac::mimeTypeForFileName(const String& fileName) const
828 BEGIN_BLOCK_OBJC_EXCEPTIONS;
829 return [_bridge MIMETypeForPath:fileName];
830 END_BLOCK_OBJC_EXCEPTIONS;
835 NSView* FrameMac::nextKeyViewInFrame(Node* n, SelectionDirection direction, bool* focusCallResultedInViewBeingCreated)
837 Document* doc = document();
841 RefPtr<Node> node = n;
843 node = direction == SelectingNext
844 ? doc->nextFocusNode(node.get()) : doc->previousFocusNode(node.get());
848 RenderObject* renderer = node->renderer();
850 if (!renderer->isWidget()) {
851 static_cast<Element*>(node.get())->focus();
852 // The call to focus might have triggered event handlers that causes the
853 // current renderer to be destroyed.
854 if (!(renderer = node->renderer()))
857 // FIXME: When all input elements are native, we should investigate if this extra check is needed
858 if (!renderer->isWidget()) {
859 [_bridge willMakeFirstResponderForNodeFocus];
860 return [_bridge documentView];
861 } else if (focusCallResultedInViewBeingCreated)
862 *focusCallResultedInViewBeingCreated = true;
865 if (Widget* widget = static_cast<RenderWidget*>(renderer)->widget()) {
867 if (widget->isFrameView())
868 view = Mac(static_cast<FrameView*>(widget)->frame())->nextKeyViewInFrame(0, direction);
870 view = widget->getView();
877 NSView *FrameMac::nextKeyViewInFrameHierarchy(Node *node, SelectionDirection direction)
879 bool focusCallResultedInViewBeingCreated = false;
880 NSView *next = nextKeyViewInFrame(node, direction, &focusCallResultedInViewBeingCreated);
882 if (FrameMac *parent = Mac(tree()->parent()))
883 next = parent->nextKeyViewInFrameHierarchy(ownerElement(), direction);
885 // remove focus from currently focused node if we're giving focus to another view
886 // unless the other view was created as a result of calling focus in nextKeyViewWithFrame.
887 // FIXME: The focusCallResultedInViewBeingCreated calls can be removed when all input element types
888 // have been made native.
889 if (next && (next != [_bridge documentView] && !focusCallResultedInViewBeingCreated))
890 if (Document *doc = document())
891 doc->setFocusNode(0);
893 // The common case where a view was created is when an <input> element changed from native
894 // to non-native. When this happens, HTMLGenericFormElement::attach() method will call setFocus()
895 // on the widget. For views with a field editor, setFocus() will set the active responder to be the field editor.
896 // In this case, we want to return the field editor as the next key view. Otherwise, the focus will be lost
897 // and a blur message will be sent.
898 // FIXME: This code can be removed when all input element types are native.
899 if (focusCallResultedInViewBeingCreated) {
900 if ([[next window] firstResponder] == [[next window] fieldEditor:NO forObject:next])
901 return [[next window] fieldEditor:NO forObject:next];
907 NSView *FrameMac::nextKeyView(Node *node, SelectionDirection direction)
910 BEGIN_BLOCK_OBJC_EXCEPTIONS;
912 next = nextKeyViewInFrameHierarchy(node, direction);
916 // Look at views from the top level part up, looking for a next key view that we can use.
918 next = direction == SelectingNext
919 ? [_bridge nextKeyViewOutsideWebFrameViews]
920 : [_bridge previousKeyViewOutsideWebFrameViews];
925 END_BLOCK_OBJC_EXCEPTIONS;
927 // If all else fails, make a loop by starting from 0.
928 return nextKeyViewInFrameHierarchy(0, direction);
931 NSView *FrameMac::nextKeyViewForWidget(Widget *startingWidget, SelectionDirection direction)
933 // Use the event filter object to figure out which RenderWidget owns this Widget and get to the DOM.
934 // Then get the next key view in the order determined by the DOM.
935 Node *node = nodeForWidget(startingWidget);
937 return Mac(frameForNode(node))->nextKeyView(node, direction);
940 bool FrameMac::currentEventIsMouseDownInWidget(Widget *candidate)
942 BEGIN_BLOCK_OBJC_EXCEPTIONS;
943 switch ([[NSApp currentEvent] type]) {
944 case NSLeftMouseDown:
945 case NSRightMouseDown:
946 case NSOtherMouseDown:
951 END_BLOCK_OBJC_EXCEPTIONS;
953 Node *node = nodeForWidget(candidate);
955 return frameForNode(node)->d->m_view->nodeUnderMouse() == node;
958 bool FrameMac::currentEventIsKeyboardOptionTab()
960 BEGIN_BLOCK_OBJC_EXCEPTIONS;
961 NSEvent *evt = [NSApp currentEvent];
962 if ([evt type] != NSKeyDown) {
966 if (([evt modifierFlags] & NSAlternateKeyMask) == 0) {
970 NSString *chars = [evt charactersIgnoringModifiers];
971 if ([chars length] != 1)
974 const unichar tabKey = 0x0009;
975 const unichar shiftTabKey = 0x0019;
976 unichar c = [chars characterAtIndex:0];
977 if (c != tabKey && c != shiftTabKey)
980 END_BLOCK_OBJC_EXCEPTIONS;
984 bool FrameMac::handleKeyboardOptionTabInView(NSView *view)
986 if (FrameMac::currentEventIsKeyboardOptionTab()) {
987 if (([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) != 0) {
988 [[view window] selectKeyViewPrecedingView:view];
990 [[view window] selectKeyViewFollowingView:view];
998 bool FrameMac::tabsToLinks() const
1000 if ([_bridge keyboardUIMode] & WebCoreKeyboardAccessTabsToLinks)
1001 return !FrameMac::currentEventIsKeyboardOptionTab();
1003 return FrameMac::currentEventIsKeyboardOptionTab();
1006 bool FrameMac::tabsToAllControls() const
1008 WebCoreKeyboardUIMode keyboardUIMode = [_bridge keyboardUIMode];
1009 BOOL handlingOptionTab = FrameMac::currentEventIsKeyboardOptionTab();
1011 // If tab-to-links is off, option-tab always highlights all controls
1012 if ((keyboardUIMode & WebCoreKeyboardAccessTabsToLinks) == 0 && handlingOptionTab) {
1016 // If system preferences say to include all controls, we always include all controls
1017 if (keyboardUIMode & WebCoreKeyboardAccessFull) {
1021 // Otherwise tab-to-links includes all controls, unless the sense is flipped via option-tab.
1022 if (keyboardUIMode & WebCoreKeyboardAccessTabsToLinks) {
1023 return !handlingOptionTab;
1026 return handlingOptionTab;
1029 KJS::Bindings::RootObject *FrameMac::executionContextForDOM()
1031 if (!jScriptEnabled())
1034 return bindingRootObject();
1037 KJS::Bindings::RootObject *FrameMac::bindingRootObject()
1039 assert(jScriptEnabled());
1040 if (!_bindingRoot) {
1042 _bindingRoot = new KJS::Bindings::RootObject(0); // The root gets deleted by JavaScriptCore.
1043 KJS::JSObject *win = KJS::Window::retrieveWindow(this);
1044 _bindingRoot->setRootObjectImp (win);
1045 _bindingRoot->setInterpreter(jScript()->interpreter());
1046 addPluginRootObject (_bindingRoot);
1048 return _bindingRoot;
1051 WebScriptObject *FrameMac::windowScriptObject()
1053 if (!jScriptEnabled())
1056 if (!_windowScriptObject) {
1058 KJS::JSObject *win = KJS::Window::retrieveWindow(this);
1059 _windowScriptObject = HardRetainWithNSRelease([[WebScriptObject alloc] _initWithJSObject:win originExecutionContext:bindingRootObject() executionContext:bindingRootObject()]);
1062 return _windowScriptObject;
1065 NPObject *FrameMac::windowScriptNPObject()
1067 if (!_windowScriptNPObject) {
1068 if (jScriptEnabled()) {
1069 // JavaScript is enabled, so there is a JavaScript window object. Return an NPObject bound to the window
1071 KJS::JSObject *win = KJS::Window::retrieveWindow(this);
1073 _windowScriptNPObject = _NPN_CreateScriptObject(0, win, bindingRootObject(), bindingRootObject());
1075 // JavaScript is not enabled, so we cannot bind the NPObject to the JavaScript window object.
1076 // Instead, we create an NPObject of a different class, one which is not bound to a JavaScript object.
1077 _windowScriptNPObject = _NPN_CreateNoScriptObject();
1081 return _windowScriptNPObject;
1084 Widget* FrameMac::createJavaAppletWidget(const IntSize& size, Element* element, const HashMap<String, String>& args)
1086 Widget* result = new Widget;
1088 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1090 NSMutableArray *attributeNames = [[NSMutableArray alloc] init];
1091 NSMutableArray *attributeValues = [[NSMutableArray alloc] init];
1093 DeprecatedString baseURLString;
1094 HashMap<String, String>::const_iterator end = args.end();
1095 for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) {
1096 if (it->first.lower() == "baseurl")
1097 baseURLString = it->second.deprecatedString();
1098 [attributeNames addObject:it->first];
1099 [attributeValues addObject:it->second];
1102 if (baseURLString.isEmpty())
1103 baseURLString = document()->baseURL();
1105 result->setView([_bridge viewForJavaAppletWithFrame:NSMakeRect(0, 0, size.width(), size.height())
1106 attributeNames:attributeNames
1107 attributeValues:attributeValues
1108 baseURL:completeURL(baseURLString).getNSURL()
1109 DOMElement:[DOMElement _elementWith:element]]);
1110 [attributeNames release];
1111 [attributeValues release];
1112 view()->addChild(result);
1114 END_BLOCK_OBJC_EXCEPTIONS;
1119 void FrameMac::partClearedInBegin()
1121 if (jScriptEnabled())
1122 [_bridge windowObjectCleared];
1125 void FrameMac::openURLFromPageCache(WebCorePageState *state)
1127 // It's safe to assume none of the WebCorePageState methods will raise
1128 // exceptions, since WebCorePageState is implemented by WebCore and
1131 Document *doc = [state document];
1132 Node *mousePressNode = [state mousePressNode];
1133 KURL *kurl = [state URL];
1134 SavedProperties *windowProperties = [state windowProperties];
1135 SavedProperties *locationProperties = [state locationProperties];
1136 SavedBuiltins *interpreterBuiltins = [state interpreterBuiltins];
1137 PausedTimeouts *timeouts = [state pausedTimeouts];
1139 cancelRedirection();
1141 // We still have to close the previous part page.
1144 d->m_bComplete = false;
1146 // Don't re-emit the load event.
1147 d->m_bLoadEventEmitted = true;
1149 // delete old status bar msg's from kjs (if it _was_ activated on last URL)
1150 if (jScriptEnabled()) {
1151 d->m_kjsStatusBarText = String();
1152 d->m_kjsDefaultStatusBarText = String();
1159 // initializing m_url to the new url breaks relative links when opening such a link after this call and _before_ begin() is called (when the first
1160 // data arrives) (Simon)
1161 if (url().protocol().startsWith("http") && !url().host().isEmpty() && url().path().isEmpty())
1162 d->m_url.setPath("/");
1164 // copy to m_workingURL after fixing url() above
1165 d->m_workingURL = url();
1169 // -----------begin-----------
1172 doc->setInPageCache(NO);
1174 d->m_bCleared = false;
1175 d->m_bComplete = false;
1176 d->m_bLoadEventEmitted = false;
1177 d->m_referrer = url().url();
1179 setView(doc->view());
1182 d->m_mousePressNode = mousePressNode;
1183 d->m_decoder = doc->decoder();
1185 updatePolicyBaseURL();
1189 restoreWindowProperties(windowProperties);
1190 restoreLocationProperties(locationProperties);
1191 restoreInterpreterBuiltins(*interpreterBuiltins);
1194 resumeTimeouts(timeouts);
1199 WebCoreFrameBridge *FrameMac::bridgeForWidget(const Widget *widget)
1201 ASSERT_ARG(widget, widget);
1203 FrameMac *frame = Mac(frameForWidget(widget));
1205 return frame->bridge();
1208 NSView *FrameMac::documentViewForNode(Node *node)
1210 WebCoreFrameBridge *bridge = Mac(frameForNode(node))->bridge();
1211 return [bridge documentView];
1214 void FrameMac::saveDocumentState()
1216 // Do not save doc state if the page has a password field and a form that would be submitted
1218 if (!(d->m_doc && d->m_doc->hasPasswordField() && d->m_doc->hasSecureForm())) {
1219 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1220 [_bridge saveDocumentState];
1221 END_BLOCK_OBJC_EXCEPTIONS;
1225 void FrameMac::restoreDocumentState()
1227 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1228 [_bridge restoreDocumentState];
1229 END_BLOCK_OBJC_EXCEPTIONS;
1232 String FrameMac::incomingReferrer() const
1234 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1235 return [_bridge incomingReferrer];
1236 END_BLOCK_OBJC_EXCEPTIONS;
1241 void FrameMac::runJavaScriptAlert(const String& message)
1243 String text = message;
1244 text.replace('\\', backslashAsCurrencySymbol());
1245 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1246 [_bridge runJavaScriptAlertPanelWithMessage:text];
1247 END_BLOCK_OBJC_EXCEPTIONS;
1250 bool FrameMac::runJavaScriptConfirm(const String& message)
1252 String text = message;
1253 text.replace('\\', backslashAsCurrencySymbol());
1255 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1256 return [_bridge runJavaScriptConfirmPanelWithMessage:text];
1257 END_BLOCK_OBJC_EXCEPTIONS;
1262 bool FrameMac::runJavaScriptPrompt(const String& prompt, const String& defaultValue, String& result)
1264 String promptText = prompt;
1265 promptText.replace('\\', backslashAsCurrencySymbol());
1266 String defaultValueText = defaultValue;
1267 defaultValueText.replace('\\', backslashAsCurrencySymbol());
1270 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1271 NSString *returnedText = nil;
1273 ok = [_bridge runJavaScriptTextInputPanelWithPrompt:prompt
1274 defaultText:defaultValue returningText:&returnedText];
1277 result = String(returnedText);
1278 result.replace(backslashAsCurrencySymbol(), '\\');
1282 END_BLOCK_OBJC_EXCEPTIONS;
1287 bool FrameMac::shouldInterruptJavaScript()
1289 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1290 return [_bridge shouldInterruptJavaScript];
1291 END_BLOCK_OBJC_EXCEPTIONS;
1296 bool FrameMac::locationbarVisible()
1298 return [_bridge areToolbarsVisible];
1301 bool FrameMac::menubarVisible()
1303 // The menubar is always on in Mac OS X UI
1307 bool FrameMac::personalbarVisible()
1309 return [_bridge areToolbarsVisible];
1312 bool FrameMac::statusbarVisible()
1314 return [_bridge isStatusbarVisible];
1317 bool FrameMac::toolbarVisible()
1319 return [_bridge areToolbarsVisible];
1322 void FrameMac::addMessageToConsole(const String &message, unsigned lineNumber, const String &sourceURL)
1324 NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
1325 (NSString *)message, @"message",
1326 [NSNumber numberWithInt: lineNumber], @"lineNumber",
1327 (NSString *)sourceURL, @"sourceURL",
1329 [_bridge addMessageToConsole:dictionary];
1332 void FrameMac::createEmptyDocument()
1334 // Although it's not completely clear from the name of this function,
1335 // it does nothing if we already have a document, and just creates an
1336 // empty one if we have no document at all.
1338 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1339 [_bridge loadEmptyDocumentSynchronously];
1340 END_BLOCK_OBJC_EXCEPTIONS;
1342 updateBaseURLForEmptyDocument();
1346 bool FrameMac::keyEvent(NSEvent *event)
1349 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1351 ASSERT([event type] == NSKeyDown || [event type] == NSKeyUp);
1353 // Check for cases where we are too early for events -- possible unmatched key up
1354 // from pressing return in the location bar.
1355 Document *doc = document();
1359 Node *node = doc->focusNode();
1361 if (doc->isHTMLDocument())
1364 node = doc->documentElement();
1369 if ([event type] == NSKeyDown) {
1370 prepareForUserAction();
1373 NSEvent *oldCurrentEvent = _currentEvent;
1374 _currentEvent = HardRetain(event);
1376 PlatformKeyboardEvent qEvent(event);
1377 result = !EventTargetNodeCast(node)->dispatchKeyEvent(qEvent);
1379 // We want to send both a down and a press for the initial key event.
1380 // To get KHTML to do this, we send a second KeyPress with "is repeat" set to true,
1381 // which causes it to send a press to the DOM.
1382 // That's not a great hack; it would be good to do this in a better way.
1383 if ([event type] == NSKeyDown && ![event isARepeat]) {
1384 PlatformKeyboardEvent repeatEvent(event, true);
1385 if (!EventTargetNodeCast(node)->dispatchKeyEvent(repeatEvent))
1389 ASSERT(_currentEvent == event);
1391 _currentEvent = oldCurrentEvent;
1395 END_BLOCK_OBJC_EXCEPTIONS;
1400 void FrameMac::handleMousePressEvent(const MouseEventWithHitTestResults& event)
1402 bool singleClick = [_currentEvent clickCount] <= 1;
1404 // If we got the event back, that must mean it wasn't prevented,
1405 // so it's allowed to start a drag or selection.
1406 _mouseDownMayStartSelect = canMouseDownStartSelect(event.targetNode());
1408 // Careful that the drag starting logic stays in sync with eventMayStartDrag()
1409 setMouseDownMayStartDrag(singleClick);
1411 d->m_mousePressNode = event.targetNode();
1413 if (!passWidgetMouseDownEventToWidget(event)) {
1414 // We don't do this at the start of mouse down handling (before calling into WebCore),
1415 // because we don't want to do it until we know we didn't hit a widget.
1416 NSView *view = d->m_view->getDocumentView();
1419 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1420 if ([_bridge firstResponder] != view) {
1421 [_bridge makeFirstResponder:view];
1423 END_BLOCK_OBJC_EXCEPTIONS;
1426 Frame::handleMousePressEvent(event);
1430 bool FrameMac::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event)
1432 // Figure out which view to send the event to.
1433 RenderObject *target = event.targetNode() ? event.targetNode()->renderer() : 0;
1434 if (!target || !target->isWidget())
1437 // Doubleclick events don't exist in Cocoa. Since passWidgetMouseDownEventToWidget will
1438 // just pass _currentEvent down to the widget, we don't want to call it for events that
1439 // don't correspond to Cocoa events. The mousedown/ups will have already been passed on as
1440 // part of the pressed/released handling.
1441 return passMouseDownEventToWidget(static_cast<RenderWidget*>(target)->widget());
1444 bool FrameMac::passWidgetMouseDownEventToWidget(RenderWidget *renderWidget)
1446 return passMouseDownEventToWidget(renderWidget->widget());
1449 bool FrameMac::passMouseDownEventToWidget(Widget* widget)
1451 // FIXME: this method always returns true
1454 LOG_ERROR("hit a RenderWidget without a corresponding Widget, means a frame is half-constructed");
1458 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1460 NSView *nodeView = widget->getView();
1462 ASSERT([nodeView superview]);
1463 NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:nil]];
1465 LOG_ERROR("KHTML says we hit a RenderWidget, but AppKit doesn't agree we hit the corresponding NSView");
1469 if ([_bridge firstResponder] == view) {
1470 // In the case where we just became first responder, we should send the mouseDown:
1471 // to the NSTextField, not the NSTextField's editor. This code makes sure that happens.
1472 // If we don't do this, we see a flash of selected text when clicking in a text field.
1473 // FIXME: This is the only caller of textViewWasFirstResponderAtMouseDownTime. When we
1474 // eliminate all use of NSTextField/NSTextView in form fields we can eliminate this code,
1475 // and textViewWasFirstResponderAtMouseDownTime:, and the instance variable WebHTMLView
1476 // keeps solely to support textViewWasFirstResponderAtMouseDownTime:.
1477 if ([view isKindOfClass:[NSTextView class]] && ![_bridge textViewWasFirstResponderAtMouseDownTime:(NSTextView *)view]) {
1478 NSView *superview = view;
1479 while (superview != nodeView) {
1480 superview = [superview superview];
1482 if ([superview isKindOfClass:[NSControl class]]) {
1483 NSControl *control = static_cast<NSControl*>(superview);
1484 if ([control currentEditor] == view) {
1492 // Normally [NSWindow sendEvent:] handles setting the first responder.
1493 // But in our case, the event was sent to the view representing the entire web page.
1494 if ([_currentEvent clickCount] <= 1 && [view acceptsFirstResponder] && [view needsPanelToBecomeKey]) {
1495 [_bridge makeFirstResponder:view];
1499 // We need to "defer loading" and defer timers while we are tracking the mouse.
1500 // That's because we don't want the new page to load while the user is holding the mouse down.
1502 BOOL wasDeferringLoading = [_bridge defersLoading];
1503 if (!wasDeferringLoading)
1504 [_bridge setDefersLoading:YES];
1505 BOOL wasDeferringTimers = isDeferringTimers();
1506 if (!wasDeferringTimers)
1507 setDeferringTimers(true);
1509 ASSERT(!_sendingEventToSubview);
1510 _sendingEventToSubview = true;
1511 [view mouseDown:_currentEvent];
1512 _sendingEventToSubview = false;
1514 if (!wasDeferringTimers)
1515 setDeferringTimers(false);
1516 if (!wasDeferringLoading)
1517 [_bridge setDefersLoading:NO];
1519 // Remember which view we sent the event to, so we can direct the release event properly.
1520 _mouseDownView = view;
1521 _mouseDownWasInSubframe = false;
1523 END_BLOCK_OBJC_EXCEPTIONS;
1528 bool FrameMac::lastEventIsMouseUp() const
1530 // Many AK widgets run their own event loops and consume events while the mouse is down.
1531 // When they finish, currentEvent is the mouseUp that they exited on. We need to update
1532 // the khtml state with this mouseUp, which khtml never saw. This method lets us detect
1535 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1536 NSEvent *currentEventAfterHandlingMouseDown = [NSApp currentEvent];
1537 if (_currentEvent != currentEventAfterHandlingMouseDown) {
1538 if ([currentEventAfterHandlingMouseDown type] == NSLeftMouseUp) {
1542 END_BLOCK_OBJC_EXCEPTIONS;
1547 // Note that this does the same kind of check as [target isDescendantOf:superview].
1548 // There are two differences: This is a lot slower because it has to walk the whole
1549 // tree, and this works in cases where the target has already been deallocated.
1550 static bool findViewInSubviews(NSView *superview, NSView *target)
1552 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1553 NSEnumerator *e = [[superview subviews] objectEnumerator];
1555 while ((subview = [e nextObject])) {
1556 if (subview == target || findViewInSubviews(subview, target)) {
1560 END_BLOCK_OBJC_EXCEPTIONS;
1565 NSView *FrameMac::mouseDownViewIfStillGood()
1567 // Since we have no way of tracking the lifetime of _mouseDownView, we have to assume that
1568 // it could be deallocated already. We search for it in our subview tree; if we don't find
1569 // it, we set it to nil.
1570 NSView *mouseDownView = _mouseDownView;
1571 if (!mouseDownView) {
1574 FrameView *topFrameView = d->m_view.get();
1575 NSView *topView = topFrameView ? topFrameView->getView() : nil;
1576 if (!topView || !findViewInSubviews(topView, mouseDownView)) {
1577 _mouseDownView = nil;
1580 return mouseDownView;
1583 bool FrameMac::eventMayStartDrag(NSEvent *event) const
1585 // This is a pre-flight check of whether the event might lead to a drag being started. Be careful
1586 // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we setMouseDownMayStartDrag
1587 // in handleMousePressEvent
1589 if ([event type] != NSLeftMouseDown || [event clickCount] != 1) {
1593 BOOL DHTMLFlag, UAFlag;
1594 [_bridge allowDHTMLDrag:&DHTMLFlag UADrag:&UAFlag];
1595 if (!DHTMLFlag && !UAFlag) {
1599 NSPoint loc = [event locationInWindow];
1600 IntPoint mouseDownPos = d->m_view->windowToContents(IntPoint(loc));
1601 RenderObject::NodeInfo nodeInfo(true, false);
1602 renderer()->layer()->hitTest(nodeInfo, mouseDownPos);
1604 return nodeInfo.innerNode()->renderer()->draggableNode(DHTMLFlag, UAFlag, mouseDownPos.x(), mouseDownPos.y(), srcIsDHTML);
1607 // The link drag hysteresis is much larger than the others because there
1608 // needs to be enough space to cancel the link press without starting a link drag,
1609 // and because dragging links is rare.
1610 const float LinkDragHysteresis = 40.0;
1611 const float ImageDragHysteresis = 5.0;
1612 const float TextDragHysteresis = 3.0;
1613 const float GeneralDragHysteresis = 3.0;
1614 const float TextDragDelay = 0.15;
1616 bool FrameMac::dragHysteresisExceeded(float dragLocationX, float dragLocationY) const
1618 IntPoint dragViewportLocation((int)dragLocationX, (int)dragLocationY);
1619 IntPoint dragLocation = d->m_view->windowToContents(dragViewportLocation);
1620 IntSize delta = dragLocation - m_mouseDownPos;
1622 float threshold = GeneralDragHysteresis;
1623 if (_dragSrcIsImage)
1624 threshold = ImageDragHysteresis;
1625 else if (_dragSrcIsLink)
1626 threshold = LinkDragHysteresis;
1627 else if (_dragSrcInSelection)
1628 threshold = TextDragHysteresis;
1630 return fabsf(delta.width()) >= threshold || fabsf(delta.height()) >= threshold;
1633 void FrameMac::handleMouseMoveEvent(const MouseEventWithHitTestResults& event)
1635 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1637 if ([_currentEvent type] == NSLeftMouseDragged) {
1638 NSView *view = mouseDownViewIfStillGood();
1641 _sendingEventToSubview = true;
1642 [view mouseDragged:_currentEvent];
1643 _sendingEventToSubview = false;
1647 // Careful that the drag starting logic stays in sync with eventMayStartDrag()
1649 if (mouseDownMayStartDrag() && !_dragSrc) {
1650 BOOL tempFlag1, tempFlag2;
1651 [_bridge allowDHTMLDrag:&tempFlag1 UADrag:&tempFlag2];
1652 _dragSrcMayBeDHTML = tempFlag1;
1653 _dragSrcMayBeUA = tempFlag2;
1654 if (!_dragSrcMayBeDHTML && !_dragSrcMayBeUA) {
1655 setMouseDownMayStartDrag(false); // no element is draggable
1659 if (mouseDownMayStartDrag() && !_dragSrc) {
1660 // try to find an element that wants to be dragged
1661 RenderObject::NodeInfo nodeInfo(true, false);
1662 renderer()->layer()->hitTest(nodeInfo, m_mouseDownPos);
1663 Node *node = nodeInfo.innerNode();
1664 _dragSrc = (node && node->renderer()) ? node->renderer()->draggableNode(_dragSrcMayBeDHTML, _dragSrcMayBeUA, m_mouseDownPos.x(), m_mouseDownPos.y(), _dragSrcIsDHTML) : 0;
1666 setMouseDownMayStartDrag(false); // no element is draggable
1668 // remember some facts about this source, while we have a NodeInfo handy
1669 node = nodeInfo.URLElement();
1670 _dragSrcIsLink = node && node->isLink();
1672 node = nodeInfo.innerNonSharedNode();
1673 _dragSrcIsImage = node && node->renderer() && node->renderer()->isImage();
1675 _dragSrcInSelection = isPointInsideSelection(m_mouseDownPos);
1679 // For drags starting in the selection, the user must wait between the mousedown and mousedrag,
1680 // or else we bail on the dragging stuff and allow selection to occur
1681 if (mouseDownMayStartDrag() && _dragSrcInSelection && [_currentEvent timestamp] - _mouseDownTimestamp < TextDragDelay) {
1682 setMouseDownMayStartDrag(false);
1683 // ...but if this was the first click in the window, we don't even want to start selection
1684 if (_activationEventNumber == [_currentEvent eventNumber]) {
1685 _mouseDownMayStartSelect = false;
1689 if (mouseDownMayStartDrag()) {
1690 // We are starting a text/image/url drag, so the cursor should be an arrow
1691 d->m_view->setCursor(pointerCursor());
1693 NSPoint dragLocation = [_currentEvent locationInWindow];
1694 if (dragHysteresisExceeded(dragLocation.x, dragLocation.y)) {
1696 // Once we're past the hysteresis point, we don't want to treat this gesture as a click
1697 d->m_view->invalidateClick();
1699 NSImage *dragImage = nil; // we use these values if WC is out of the loop
1700 NSPoint dragLoc = NSZeroPoint;
1701 NSDragOperation srcOp = NSDragOperationNone;
1702 BOOL wcWrotePasteboard = NO;
1703 if (_dragSrcMayBeDHTML) {
1704 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1705 // Must be done before ondragstart adds types and data to the pboard,
1706 // also done for security, as it erases data from the last drag
1707 [pasteboard declareTypes:[NSArray array] owner:nil];
1709 freeClipboard(); // would only happen if we missed a dragEnd. Do it anyway, just
1710 // to make sure it gets numbified
1711 _dragClipboard = new ClipboardMac(true, pasteboard, ClipboardMac::Writable, this);
1713 // If this is drag of an element, get set up to generate a default image. Otherwise
1714 // WebKit will generate the default, the element doesn't override.
1715 if (_dragSrcIsDHTML) {
1717 _dragSrc->renderer()->absolutePosition(srcX, srcY);
1718 IntSize delta = m_mouseDownPos - IntPoint(srcX, srcY);
1719 _dragClipboard->setDragImageElement(_dragSrc.get(), IntPoint() + delta);
1722 setMouseDownMayStartDrag(dispatchDragSrcEvent(dragstartEvent, m_mouseDown) && mayCopy());
1723 // Invalidate clipboard here against anymore pasteboard writing for security. The drag
1724 // image can still be changed as we drag, but not the pasteboard data.
1725 _dragClipboard->setAccessPolicy(ClipboardMac::ImageWritable);
1727 if (mouseDownMayStartDrag()) {
1728 // gather values from DHTML element, if it set any
1729 _dragClipboard->sourceOperation(&srcOp);
1731 NSArray *types = [pasteboard types];
1732 wcWrotePasteboard = types && [types count] > 0;
1734 if (_dragSrcMayBeDHTML) {
1735 dragImage = _dragClipboard->dragNSImage(&dragLoc);
1738 // Yuck, dragSourceMovedTo() can be called as a result of kicking off the drag with
1739 // dragImage! Because of that dumb reentrancy, we may think we've not started the
1740 // drag when that happens. So we have to assume it's started before we kick it off.
1741 _dragClipboard->setDragHasStarted();
1745 if (mouseDownMayStartDrag()) {
1746 BOOL startedDrag = [_bridge startDraggingImage:dragImage at:dragLoc operation:srcOp event:_currentEvent sourceIsDHTML:_dragSrcIsDHTML DHTMLWroteData:wcWrotePasteboard];
1747 if (!startedDrag && _dragSrcMayBeDHTML) {
1748 // WebKit canned the drag at the last minute - we owe _dragSrc a DRAGEND event
1749 PlatformMouseEvent event(PlatformMouseEvent::currentEvent);
1750 dispatchDragSrcEvent(dragendEvent, event);
1751 setMouseDownMayStartDrag(false);
1755 if (!mouseDownMayStartDrag()) {
1756 // something failed to start the drag, cleanup
1762 // No more default handling (like selection), whether we're past the hysteresis bounds or not
1765 if (!mouseDownMayStartSelect() && !mouseDownMayStartAutoscroll())
1769 // If we allowed the other side of the bridge to handle a drag
1770 // last time, then m_bMousePressed might still be set. So we
1771 // clear it now to make sure the next move after a drag
1772 // doesn't look like a drag.
1773 d->m_bMousePressed = false;
1776 Frame::handleMouseMoveEvent(event);
1778 END_BLOCK_OBJC_EXCEPTIONS;
1781 // Returns whether caller should continue with "the default processing", which is the same as
1782 // the event handler NOT setting the return value to false
1783 bool FrameMac::dispatchCPPEvent(const AtomicString &eventType, ClipboardMac::AccessPolicy policy)
1785 Node* target = selectionController()->start().element();
1786 if (!target && document())
1787 target = document()->body();
1790 if (target->isShadowNode())
1791 target = target->shadowParentNode();
1793 RefPtr<ClipboardMac> clipboard = new ClipboardMac(false, [NSPasteboard generalPasteboard], (ClipboardMac::AccessPolicy)policy);
1795 ExceptionCode ec = 0;
1796 RefPtr<Event> evt = new ClipboardEvent(eventType, true, true, clipboard.get());
1797 EventTargetNodeCast(target)->dispatchEvent(evt, ec, true);
1798 bool noDefaultProcessing = evt->defaultPrevented();
1800 // invalidate clipboard here for security
1801 clipboard->setAccessPolicy(ClipboardMac::Numb);
1803 return !noDefaultProcessing;
1806 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items. They
1807 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
1808 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
1809 // normally selectable to implement copy/paste (like divs, or a document body).
1811 bool FrameMac::mayDHTMLCut()
1813 return mayCopy() && !dispatchCPPEvent(beforecutEvent, ClipboardMac::Numb);
1816 bool FrameMac::mayDHTMLCopy()
1818 return mayCopy() && !dispatchCPPEvent(beforecopyEvent, ClipboardMac::Numb);
1821 bool FrameMac::mayDHTMLPaste()
1823 return !dispatchCPPEvent(beforepasteEvent, ClipboardMac::Numb);
1826 bool FrameMac::tryDHTMLCut()
1831 // Must be done before oncut adds types and data to the pboard,
1832 // also done for security, as it erases data from the last copy/paste.
1833 [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
1835 return !dispatchCPPEvent(cutEvent, ClipboardMac::Writable);
1838 bool FrameMac::tryDHTMLCopy()
1843 // Must be done before oncopy adds types and data to the pboard,
1844 // also done for security, as it erases data from the last copy/paste.
1845 [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
1847 return !dispatchCPPEvent(copyEvent, ClipboardMac::Writable);
1850 bool FrameMac::tryDHTMLPaste()
1852 return !dispatchCPPEvent(pasteEvent, ClipboardMac::Readable);
1855 void FrameMac::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event)
1857 NSView *view = mouseDownViewIfStillGood();
1859 // If this was the first click in the window, we don't even want to clear the selection.
1860 // This case occurs when the user clicks on a draggable element, since we have to process
1861 // the mouse down and drag events to see if we might start a drag. For other first clicks
1862 // in a window, we just don't acceptFirstMouse, and the whole down-drag-up sequence gets
1863 // ignored upstream of this layer.
1864 if (_activationEventNumber != [_currentEvent eventNumber])
1865 Frame::handleMouseReleaseEvent(event);
1868 stopAutoscrollTimer();
1870 _sendingEventToSubview = true;
1871 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1872 [view mouseUp:_currentEvent];
1873 END_BLOCK_OBJC_EXCEPTIONS;
1874 _sendingEventToSubview = false;
1877 bool FrameMac::passSubframeEventToSubframe(MouseEventWithHitTestResults& event, Frame* subframePart)
1879 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1881 switch ([_currentEvent type]) {
1882 case NSMouseMoved: {
1883 ASSERT(subframePart);
1884 [Mac(subframePart)->bridge() mouseMoved:_currentEvent];
1888 case NSLeftMouseDown: {
1889 Node *node = event.targetNode();
1893 RenderObject *renderer = node->renderer();
1894 if (!renderer || !renderer->isWidget()) {
1897 Widget *widget = static_cast<RenderWidget*>(renderer)->widget();
1898 if (!widget || !widget->isFrameView())
1900 if (!passWidgetMouseDownEventToWidget(static_cast<RenderWidget*>(renderer))) {
1903 _mouseDownWasInSubframe = true;
1906 case NSLeftMouseUp: {
1907 if (!_mouseDownWasInSubframe) {
1910 NSView *view = mouseDownViewIfStillGood();
1914 ASSERT(!_sendingEventToSubview);
1915 _sendingEventToSubview = true;
1916 [view mouseUp:_currentEvent];
1917 _sendingEventToSubview = false;
1920 case NSLeftMouseDragged: {
1921 if (!_mouseDownWasInSubframe) {
1924 NSView *view = mouseDownViewIfStillGood();
1928 ASSERT(!_sendingEventToSubview);
1929 _sendingEventToSubview = true;
1930 [view mouseDragged:_currentEvent];
1931 _sendingEventToSubview = false;
1937 END_BLOCK_OBJC_EXCEPTIONS;
1942 bool FrameMac::passWheelEventToWidget(Widget* widget)
1944 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1946 if ([_currentEvent type] != NSScrollWheel || _sendingEventToSubview || !widget)
1949 NSView *nodeView = widget->getView();
1951 ASSERT([nodeView superview]);
1952 NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:nil]];
1955 _sendingEventToSubview = true;
1956 [view scrollWheel:_currentEvent];
1957 _sendingEventToSubview = false;
1961 END_BLOCK_OBJC_EXCEPTIONS;
1965 void FrameMac::mouseDown(NSEvent *event)
1967 FrameView *v = d->m_view.get();
1968 if (!v || _sendingEventToSubview)
1971 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1973 prepareForUserAction();
1975 _mouseDownView = nil;
1978 NSEvent *oldCurrentEvent = _currentEvent;
1979 _currentEvent = HardRetain(event);
1980 m_mouseDown = PlatformMouseEvent(event);
1981 NSPoint loc = [event locationInWindow];
1982 m_mouseDownPos = d->m_view->windowToContents(IntPoint(loc));
1983 _mouseDownTimestamp = [event timestamp];
1985 setMouseDownMayStartDrag(false);
1986 _mouseDownMayStartSelect = false;
1987 setMouseDownMayStartAutoscroll(false);
1989 v->handleMousePressEvent(event);
1991 ASSERT(_currentEvent == event);
1993 _currentEvent = oldCurrentEvent;
1995 END_BLOCK_OBJC_EXCEPTIONS;
1998 void FrameMac::mouseDragged(NSEvent *event)
2000 FrameView *v = d->m_view.get();
2001 if (!v || _sendingEventToSubview) {
2005 BEGIN_BLOCK_OBJC_EXCEPTIONS;
2007 NSEvent *oldCurrentEvent = _currentEvent;
2008 _currentEvent = HardRetain(event);
2010 v->handleMouseMoveEvent(event);
2012 ASSERT(_currentEvent == event);
2014 _currentEvent = oldCurrentEvent;
2016 END_BLOCK_OBJC_EXCEPTIONS;
2019 void FrameMac::mouseUp(NSEvent *event)
2021 FrameView *v = d->m_view.get();
2022 if (!v || _sendingEventToSubview)
2025 BEGIN_BLOCK_OBJC_EXCEPTIONS;
2027 NSEvent *oldCurrentEvent = _currentEvent;
2028 _currentEvent = HardRetain(event);
2030 // Our behavior here is a little different that Qt. Qt always sends
2031 // a mouse release event, even for a double click. To correct problems
2032 // in khtml's DOM click event handling we do not send a release here
2033 // for a double click. Instead we send that event from FrameView's
2034 // handleMouseDoubleClickEvent. Note also that the third click of
2035 // a triple click is treated as a single click, but the fourth is then
2036 // treated as another double click. Hence the "% 2" below.
2037 int clickCount = [event clickCount];
2038 if (clickCount > 0 && clickCount % 2 == 0)
2039 v->handleMouseDoubleClickEvent(event);
2041 v->handleMouseReleaseEvent(event);
2043 ASSERT(_currentEvent == event);
2045 _currentEvent = oldCurrentEvent;
2047 _mouseDownView = nil;
2049 END_BLOCK_OBJC_EXCEPTIONS;
2053 A hack for the benefit of AK's PopUpButton, which uses the Carbon menu manager, which thus
2054 eats all subsequent events after it is starts its modal tracking loop. After the interaction
2055 is done, this routine is used to fix things up. When a mouse down started us tracking in
2056 the widget, we post a fake mouse up to balance the mouse down we started with. When a
2057 key down started us tracking in the widget, we post a fake key up to balance things out.
2058 In addition, we post a fake mouseMoved to get the cursor in sync with whatever we happen to
2059 be over after the tracking is done.
2061 void FrameMac::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent)
2063 BEGIN_BLOCK_OBJC_EXCEPTIONS;
2065 _sendingEventToSubview = false;
2066 int eventType = [initiatingEvent type];
2067 if (eventType == NSLeftMouseDown || eventType == NSKeyDown) {
2068 NSEvent *fakeEvent = nil;
2069 if (eventType == NSLeftMouseDown) {
2070 fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
2071 location:[initiatingEvent locationInWindow]
2072 modifierFlags:[initiatingEvent modifierFlags]
2073 timestamp:[initiatingEvent timestamp]
2074 windowNumber:[initiatingEvent windowNumber]
2075 context:[initiatingEvent context]
2076 eventNumber:[initiatingEvent eventNumber]
2077 clickCount:[initiatingEvent clickCount]
2078 pressure:[initiatingEvent pressure]];
2082 else { // eventType == NSKeyDown
2083 fakeEvent = [NSEvent keyEventWithType:NSKeyUp
2084 location:[initiatingEvent locationInWindow]
2085 modifierFlags:[initiatingEvent modifierFlags]
2086 timestamp:[initiatingEvent timestamp]
2087 windowNumber:[initiatingEvent windowNumber]
2088 context:[initiatingEvent context]
2089 characters:[initiatingEvent characters]
2090 charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers]
2091 isARepeat:[initiatingEvent isARepeat]
2092 keyCode:[initiatingEvent keyCode]];
2093 keyEvent(fakeEvent);
2095 // FIXME: We should really get the current modifierFlags here, but there's no way to poll
2096 // them in Cocoa, and because the event stream was stolen by the Carbon menu code we have
2097 // no up-to-date cache of them anywhere.
2098 fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
2099 location:[[_bridge window] convertScreenToBase:[NSEvent mouseLocation]]
2100 modifierFlags:[initiatingEvent modifierFlags]
2101 timestamp:[initiatingEvent timestamp]
2102 windowNumber:[initiatingEvent windowNumber]
2103 context:[initiatingEvent context]
2107 mouseMoved(fakeEvent);
2110 END_BLOCK_OBJC_EXCEPTIONS;
2113 void FrameMac::mouseMoved(NSEvent *event)
2115 FrameView *v = d->m_view.get();
2116 // Reject a mouse moved if the button is down - screws up tracking during autoscroll
2117 // These happen because WebKit sometimes has to fake up moved events.
2118 if (!v || d->m_bMousePressed || _sendingEventToSubview)
2121 BEGIN_BLOCK_OBJC_EXCEPTIONS;
2123 NSEvent *oldCurrentEvent = _currentEvent;
2124 _currentEvent = HardRetain(event);
2126 v->handleMouseMoveEvent(event);
2128 ASSERT(_currentEvent == event);
2130 _currentEvent = oldCurrentEvent;
2132 END_BLOCK_OBJC_EXCEPTIONS;
2135 // Called as we walk up the element chain for nodes with CSS property -webkit-user-drag == auto
2136 bool FrameMac::shouldDragAutoNode(Node* node, const IntPoint& point) const
2138 // We assume that WebKit only cares about dragging things that can be leaf nodes (text, images, urls).
2139 // This saves a bunch of expensive calls (creating WC and WK element dicts) as we walk farther up
2140 // the node hierarchy, and we also don't have to cook up a way to ask WK about non-leaf nodes
2141 // (since right now WK just hit-tests using a cached lastMouseDown).
2142 if (!node->hasChildNodes() && d->m_view) {
2143 NSPoint eventLoc = d->m_view->contentsToWindow(point);
2144 return [_bridge mayStartDragAtEventLocation:eventLoc];
2149 bool FrameMac::sendContextMenuEvent(NSEvent *event)
2151 Document* doc = d->m_doc.get();
2152 FrameView* v = d->m_view.get();
2157 BEGIN_BLOCK_OBJC_EXCEPTIONS;
2159 NSEvent *oldCurrentEvent = _currentEvent;
2160 _currentEvent = HardRetain(event);
2162 PlatformMouseEvent mouseEvent(event);
2164 IntPoint viewportPos = v->windowToContents(mouseEvent.pos());
2165 MouseEventWithHitTestResults mev = doc->prepareMouseEvent(false, true, false, viewportPos, mouseEvent);
2167 swallowEvent = v->dispatchMouseEvent(contextmenuEvent, mev.targetNode(), true, 0, mouseEvent, true);
2168 if (!swallowEvent && !isPointInsideSelection(viewportPos) &&
2169 ([_bridge selectWordBeforeMenuEvent] || [_bridge isEditable]
2170 || (mev.targetNode() && mev.targetNode()->isContentEditable()))) {
2171 _mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
2172 selectClosestWordFromMouseEvent(mouseEvent, mev.targetNode());
2175 ASSERT(_currentEvent == event);
2177 _currentEvent = oldCurrentEvent;
2179 return swallowEvent;
2181 END_BLOCK_OBJC_EXCEPTIONS;
2186 struct ListItemInfo {
2191 NSFileWrapper *FrameMac::fileWrapperForElement(Element *e)
2193 NSFileWrapper *wrapper = nil;
2194 BEGIN_BLOCK_OBJC_EXCEPTIONS;
2196 const AtomicString& attr = e->getAttribute(srcAttr);
2197 if (!attr.isEmpty()) {
2198 NSURL *URL = completeURL(attr.deprecatedString()).getNSURL();
2199 wrapper = [_bridge fileWrapperForURL:URL];
2202 RenderImage *renderer = static_cast<RenderImage*>(e->renderer());
2203 if (renderer->cachedImage() && !renderer->cachedImage()->isErrorImage()) {
2204 wrapper = [[NSFileWrapper alloc] initRegularFileWithContents:(NSData*)(renderer->cachedImage()->image()->getTIFFRepresentation())];
2205 [wrapper setPreferredFilename:@"image.tiff"];
2206 [wrapper autorelease];
2212 END_BLOCK_OBJC_EXCEPTIONS;
2217 static Element *listParent(Element *item)
2219 while (!item->hasTagName(ulTag) && !item->hasTagName(olTag)) {
2220 item = static_cast<Element*>(item->parentNode());
2227 static Node* isTextFirstInListItem(Node *e)
2229 if (!e->isTextNode())
2231 Node* par = e->parentNode();
2233 if (par->firstChild() != e)
2235 if (par->hasTagName(liTag))
2238 par = par->parentNode();
2243 // FIXME: Enhance TextIterator to optionally add attributes, then just call through to that.
2245 #define BULLET_CHAR 0x2022
2246 #define SQUARE_CHAR 0x25AA
2247 #define CIRCLE_CHAR 0x25E6
2249 NSAttributedString *FrameMac::attributedString(Node *startNode, int startOffset, Node *endNode, int endOffset)
2252 NSMutableAttributedString *result;
2253 BEGIN_BLOCK_OBJC_EXCEPTIONS;
2255 Range range(document(), startNode, startOffset, endNode, endOffset);
2256 if (!range.boundaryPointsValid())
2259 Node* firstNode = range.startNode();
2262 Node* pastEndNode = range.pastEndNode();
2264 result = [[[NSMutableAttributedString alloc] init] autorelease];
2266 bool hasNewLine = true;
2267 bool addedSpace = true;
2268 NSAttributedString *pendingStyledSpace = nil;
2269 bool hasParagraphBreak = true;
2270 const Element *linkStartNode = 0;
2271 unsigned linkStartLocation = 0;
2272 Vector<Element*> listItems;
2273 Vector<ListItemInfo> listItemLocations;
2274 float maxMarkerWidth = 0;
2276 Node *currentNode = firstNode;
2278 // If the first item is the entire text of a list item, use the list item node as the start of the
2279 // selection, not the text node. The user's intent was probably to select the list.
2280 if (currentNode->isTextNode() && startOffset == 0) {
2281 Node *startListNode = isTextFirstInListItem(firstNode);
2283 firstNode = startListNode;
2284 currentNode = firstNode;
2288 while (currentNode && currentNode != pastEndNode) {
2289 RenderObject *renderer = currentNode->renderer();
2291 RenderStyle *style = renderer->style();
2292 NSFont *font = style->font().primaryFont()->getNSFont();
2293 bool needSpace = pendingStyledSpace != nil;
2294 if (currentNode->isTextNode()) {
2298 [pendingStyledSpace release];
2299 pendingStyledSpace = nil;
2302 DeprecatedString text;
2303 DeprecatedString str = currentNode->nodeValue().deprecatedString();
2304 int start = (currentNode == firstNode) ? startOffset : -1;
2305 int end = (currentNode == endNode) ? endOffset : -1;
2306 if (renderer->isText()) {
2307 if (!style->collapseWhiteSpace()) {
2308 if (needSpace && !addedSpace) {
2309 if (text.isEmpty() && linkStartLocation == [result length])
2310 ++linkStartLocation;
2311 [result appendAttributedString:pendingStyledSpace];
2313 int runStart = (start == -1) ? 0 : start;
2314 int runEnd = (end == -1) ? str.length() : end;
2315 text += str.mid(runStart, runEnd-runStart);
2316 [pendingStyledSpace release];
2317 pendingStyledSpace = nil;
2318 addedSpace = u_charDirection(str[runEnd - 1].unicode()) == U_WHITE_SPACE_NEUTRAL;
2321 RenderText* textObj = static_cast<RenderText*>(renderer);
2322 if (!textObj->firstTextBox() && str.length() > 0 && !addedSpace) {
2323 // We have no runs, but we do have a length. This means we must be
2324 // whitespace that collapsed away at the end of a line.
2330 for (InlineTextBox* box = textObj->firstTextBox(); box; box = box->nextTextBox()) {
2331 int runStart = (start == -1) ? box->m_start : start;
2332 int runEnd = (end == -1) ? box->m_start + box->m_len : end;
2333 runEnd = min(runEnd, box->m_start + box->m_len);
2334 if (runStart >= box->m_start &&
2335 runStart < box->m_start + box->m_len) {
2336 if (box == textObj->firstTextBox() && box->m_start == runStart && runStart > 0)
2337 needSpace = true; // collapsed space at the start
2338 if (needSpace && !addedSpace) {
2339 if (pendingStyledSpace != nil) {
2340 if (text.isEmpty() && linkStartLocation == [result length])
2341 ++linkStartLocation;
2342 [result appendAttributedString:pendingStyledSpace];
2346 DeprecatedString runText = str.mid(runStart, runEnd - runStart);
2347 runText.replace('\n', ' ');
2349 int nextRunStart = box->nextTextBox() ? box->nextTextBox()->m_start : str.length(); // collapsed space between runs or at the end
2350 needSpace = nextRunStart > runEnd;
2351 [pendingStyledSpace release];
2352 pendingStyledSpace = nil;
2353 addedSpace = u_charDirection(str[runEnd - 1].unicode()) == U_WHITE_SPACE_NEUTRAL;
2356 if (end != -1 && runEnd >= end)
2363 text.replace('\\', renderer->backslashAsCurrencySymbol());
2365 if (text.length() > 0 || needSpace) {
2366 NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2367 [attrs setObject:font forKey:NSFontAttributeName];
2368 if (style && style->color().isValid() && style->color().alpha() != 0)
2369 [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2370 if (style && style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
2371 [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2373 if (text.length() > 0) {
2374 hasParagraphBreak = false;
2375 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString() attributes:attrs];
2376 [result appendAttributedString: partialString];
2377 [partialString release];
2381 [pendingStyledSpace release];
2382 pendingStyledSpace = [[NSAttributedString alloc] initWithString:@" " attributes:attrs];
2388 // This is our simple HTML -> ASCII transformation:
2389 DeprecatedString text;
2390 if (currentNode->hasTagName(aTag)) {
2391 // Note the start of the <a> element. We will add the NSLinkAttributeName
2392 // attribute to the attributed string when navigating to the next sibling
2394 linkStartLocation = [result length];
2395 linkStartNode = static_cast<Element*>(currentNode);
2396 } else if (currentNode->hasTagName(brTag)) {
2399 } else if (currentNode->hasTagName(liTag)) {
2400 DeprecatedString listText;
2401 Element *itemParent = listParent(static_cast<Element*>(currentNode));
2407 listItems.append(static_cast<Element*>(currentNode));
2408 info.start = [result length];
2410 listItemLocations.append (info);
2413 if (itemParent && renderer->isListItem()) {
2414 RenderListItem* listRenderer = static_cast<RenderListItem*>(renderer);
2416 maxMarkerWidth = MAX([font pointSize], maxMarkerWidth);
2417 switch(style->listStyleType()) {
2419 listText += ((DeprecatedChar)BULLET_CHAR);
2422 listText += ((DeprecatedChar)CIRCLE_CHAR);
2425 listText += ((DeprecatedChar)SQUARE_CHAR);
2430 DeprecatedString marker = listRenderer->markerStringValue();
2432 // Use AppKit metrics. Will be rendered by AppKit.
2433 float markerWidth = [marker.getNSString() sizeWithAttributes:[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]].width;
2434 maxMarkerWidth = MAX(markerWidth, maxMarkerWidth);
2440 NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2441 [attrs setObject:font forKey:NSFontAttributeName];
2442 if (style && style->color().isValid())
2443 [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2444 if (style && style->backgroundColor().isValid())
2445 [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2447 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:listText.getNSString() attributes:attrs];
2449 [result appendAttributedString: partialString];
2450 [partialString release];
2452 } else if (currentNode->hasTagName(olTag) || currentNode->hasTagName(ulTag)) {
2456 } else if (currentNode->hasTagName(blockquoteTag)
2457 || currentNode->hasTagName(ddTag)
2458 || currentNode->hasTagName(divTag)
2459 || currentNode->hasTagName(dlTag)
2460 || currentNode->hasTagName(dtTag)
2461 || currentNode->hasTagName(hrTag)
2462 || currentNode->hasTagName(listingTag)
2463 || currentNode->hasTagName(preTag)
2464 || currentNode->hasTagName(tdTag)
2465 || currentNode->hasTagName(thTag)) {
2469 } else if (currentNode->hasTagName(h1Tag)
2470 || currentNode->hasTagName(h2Tag)
2471 || currentNode->hasTagName(h3Tag)
2472 || currentNode->hasTagName(h4Tag)
2473 || currentNode->hasTagName(h5Tag)
2474 || currentNode->hasTagName(h6Tag)
2475 || currentNode->hasTagName(pTag)
2476 || currentNode->hasTagName(trTag)) {
2480 // In certain cases, emit a paragraph break.
2481 int bottomMargin = renderer->collapsedMarginBottom();
2482 int fontSize = style->fontDescription().computedPixelSize();
2483 if (bottomMargin * 2 >= fontSize) {
2484 if (!hasParagraphBreak) {
2486 hasParagraphBreak = true;
2492 else if (currentNode->hasTagName(imgTag)) {
2493 if (pendingStyledSpace != nil) {
2494 if (linkStartLocation == [result length])
2495 ++linkStartLocation;
2496 [result appendAttributedString:pendingStyledSpace];
2497 [pendingStyledSpace release];
2498 pendingStyledSpace = nil;
2500 NSFileWrapper *fileWrapper = fileWrapperForElement(static_cast<Element*>(currentNode));
2501 NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:fileWrapper];
2502 NSAttributedString *iString = [NSAttributedString attributedStringWithAttachment:attachment];
2503 [result appendAttributedString: iString];
2504 [attachment release];
2507 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2508 [result appendAttributedString: partialString];
2509 [partialString release];
2513 Node *nextNode = currentNode->firstChild();
2515 nextNode = currentNode->nextSibling();
2517 while (!nextNode && currentNode->parentNode()) {
2518 DeprecatedString text;
2519 currentNode = currentNode->parentNode();
2520 if (currentNode == pastEndNode)
2522 nextNode = currentNode->nextSibling();
2524 if (currentNode->hasTagName(aTag)) {
2525 // End of a <a> element. Create an attributed string NSLinkAttributeName attribute
2526 // for the range of the link. Note that we create the attributed string from the DOM, which
2527 // will have corrected any illegally nested <a> elements.
2528 if (linkStartNode && currentNode == linkStartNode) {
2529 String href = parseURL(linkStartNode->getAttribute(hrefAttr));
2530 KURL kURL = Mac(linkStartNode->document()->frame())->completeURL(href.deprecatedString());
2532 NSURL *URL = kURL.getNSURL();
2533 NSRange tempRange = { linkStartLocation, [result length]-linkStartLocation }; // workaround for 4213314
2534 [result addAttribute:NSLinkAttributeName value:URL range:tempRange];
2538 else if (currentNode->hasTagName(olTag) || currentNode->hasTagName(ulTag)) {
2542 } else if (currentNode->hasTagName(liTag)) {
2544 int i, count = listItems.size();
2545 for (i = 0; i < count; i++){
2546 if (listItems[i] == currentNode){
2547 listItemLocations[i].end = [result length];
2554 } else if (currentNode->hasTagName(blockquoteTag) ||
2555 currentNode->hasTagName(ddTag) ||
2556 currentNode->hasTagName(divTag) ||
2557 currentNode->hasTagName(dlTag) ||
2558 currentNode->hasTagName(dtTag) ||
2559 currentNode->hasTagName(hrTag) ||
2560 currentNode->hasTagName(listingTag) ||
2561 currentNode->hasTagName(preTag) ||
2562 currentNode->hasTagName(tdTag) ||
2563 currentNode->hasTagName(thTag)) {
2567 } else if (currentNode->hasTagName(pTag) ||
2568 currentNode->hasTagName(trTag) ||
2569 currentNode->hasTagName(h1Tag) ||
2570 currentNode->hasTagName(h2Tag) ||
2571 currentNode->hasTagName(h3Tag) ||
2572 currentNode->hasTagName(h4Tag) ||
2573 currentNode->hasTagName(h5Tag) ||
2574 currentNode->hasTagName(h6Tag)) {
2577 // An extra newline is needed at the start, not the end, of these types of tags,
2578 // so don't add another here.
2582 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2583 [result appendAttributedString:partialString];
2584 [partialString release];
2587 currentNode = nextNode;
2590 [pendingStyledSpace release];
2592 // Apply paragraph styles from outside in. This ensures that nested lists correctly
2593 // override their parent's paragraph style.
2595 unsigned i, count = listItems.size();
2598 #ifdef POSITION_LIST
2599 Node *containingBlock;
2600 int containingBlockX, containingBlockY;
2602 // Determine the position of the outermost containing block. All paragraph
2603 // styles and tabs should be relative to this position. So, the horizontal position of
2604 // each item in the list (in the resulting attributed string) will be relative to position
2605 // of the outermost containing block.
2607 containingBlock = firstNode;
2608 while (containingBlock->renderer()->isInline()){
2609 containingBlock = containingBlock->parentNode();
2611 containingBlock->renderer()->absolutePosition(containingBlockX, containingBlockY);
2615 for (i = 0; i < count; i++){
2617 info = listItemLocations[i];
2619 if (info.end < info.start)
2620 info.end = [result length];
2622 RenderObject *r = e->renderer();
2623 RenderStyle *style = r->style();
2626 NSFont *font = style->font().primaryFont()->getNSFont();
2627 float pointSize = [font pointSize];
2629 #ifdef POSITION_LIST
2631 r->absolutePosition(rx, ry);
2632 rx -= containingBlockX;
2634 // Ensure that the text is indented at least enough to allow for the markers.
2635 rx = MAX(rx, (int)maxMarkerWidth);
2637 rx = (int)MAX(maxMarkerWidth, pointSize);
2640 // The bullet text will be right aligned at the first tab marker, followed
2641 // by a space, followed by the list item text. The space is arbitrarily
2642 // picked as pointSize*2/3. The space on the first line of the text item
2643 // is established by a left aligned tab, on subsequent lines it's established
2644 // by the head indent.
2645 NSMutableParagraphStyle *mps = [[NSMutableParagraphStyle alloc] init];
2646 [mps setFirstLineHeadIndent: 0];
2647 [mps setHeadIndent: rx];
2648 [mps setTabStops:[NSArray arrayWithObjects:
2649 [[[NSTextTab alloc] initWithType:NSRightTabStopType location:rx-(pointSize*2/3)] autorelease],
2650 [[[NSTextTab alloc] initWithType:NSLeftTabStopType location:rx] autorelease],
2652 NSRange tempRange = { info.start, info.end-info.start }; // workaround for 4213314
2653 [result addAttribute:NSParagraphStyleAttributeName value:mps range:tempRange];
2660 END_BLOCK_OBJC_EXCEPTIONS;
2665 NSImage *FrameMac::imageFromRect(NSRect rect) const
2667 NSView *view = d->m_view->getDocumentView();
2671 NSImage *resultImage;
2672 BEGIN_BLOCK_OBJC_EXCEPTIONS;
2674 NSRect bounds = [view bounds];
2676 // Round image rect size in window coordinate space to avoid pixel cracks at HiDPI (4622794)
2677 rect = [view convertRect:rect toView:nil];
2678 rect.size.height = roundf(rect.size.height);
2679 rect.size.width = roundf(rect.size.width);
2680 rect = [view convertRect:rect fromView:nil];
2682 resultImage = [[[NSImage alloc] initWithSize:rect.size] autorelease];
2684 if (rect.size.width != 0 && rect.size.height != 0) {
2685 [resultImage setFlipped:YES];
2686 [resultImage lockFocus];
2688 CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2690 CGContextSaveGState(context);
2691 CGContextTranslateCTM(context, bounds.origin.x - rect.origin.x, bounds.origin.y - rect.origin.y);
2692 [view drawRect:rect];
2693 CGContextRestoreGState(context);
2695 [resultImage unlockFocus];
2696 [resultImage setFlipped:NO];
2701 END_BLOCK_OBJC_EXCEPTIONS;
2706 NSImage* FrameMac::selectionImage(bool forceWhiteText) const
2708 d->m_paintRestriction = forceWhiteText ? PaintRestrictionSelectionOnlyWhiteText : PaintRestrictionSelectionOnly;
2709 NSImage *result = imageFromRect(visibleSelectionRect());
2710 d->m_paintRestriction = PaintRestrictionNone;
2714 NSImage *FrameMac::snapshotDragImage(Node *node, NSRect *imageRect, NSRect *elementRect) const
2716 RenderObject *renderer = node->renderer();
2720 renderer->updateDragState(true); // mark dragged nodes (so they pick up the right CSS)
2721 d->m_doc->updateLayout(); // forces style recalc - needed since changing the drag state might
2722 // imply new styles, plus JS could have changed other things
2723 IntRect topLevelRect;
2724 NSRect paintingRect = renderer->paintingRootRect(topLevelRect);
2726 d->m_elementToDraw = node; // invoke special sub-tree drawing mode
2727 NSImage *result = imageFromRect(paintingRect);
2728 renderer->updateDragState(false);
2729 d->m_doc->updateLayout();
2730 d->m_elementToDraw = 0;
2733 *elementRect = topLevelRect;
2735 *imageRect = paintingRect;
2739 NSFont *FrameMac::fontForSelection(bool *hasMultipleFonts) const
2741 if (hasMultipleFonts)
2742 *hasMultipleFonts = false;
2744 if (!selectionController()->isRange()) {
2746 RenderStyle *style = styleForSelectionStart(nodeToRemove); // sets nodeToRemove
2750 result = style->font().primaryFont()->getNSFont();
2754 nodeToRemove->remove(ec);
2763 RefPtr<Range> range = selectionController()->toRange();
2764 Node *startNode = range->editingStartPosition().node();
2765 if (startNode != nil) {
2766 Node *pastEnd = range->pastEndNode();
2767 // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
2768 // unreproducible case where this didn't happen, so check for nil also.
2769 for (Node *n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
2770 RenderObject *renderer = n->renderer();
2773 // FIXME: Are there any node types that have renderers, but that we should be skipping?
2774 NSFont *f = renderer->style()->font().primaryFont()->getNSFont();
2777 if (!hasMultipleFonts)
2779 } else if (font != f) {
2780 *hasMultipleFonts = true;
2789 NSDictionary *FrameMac::fontAttributesForSelectionStart() const
2792 RenderStyle *style = styleForSelectionStart(nodeToRemove);
2796 NSMutableDictionary *result = [NSMutableDictionary dictionary];
2798 if (style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
2799 [result setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2801 if (style->font().primaryFont()->getNSFont())
2802 [result setObject:style->font().primaryFont()->getNSFont() forKey:NSFontAttributeName];
2804 if (style->color().isValid() && style->color() != Color::black)
2805 [result setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2807 ShadowData *shadow = style->textShadow();
2809 NSShadow *s = [[NSShadow alloc] init];
2810 [s setShadowOffset:NSMakeSize(shadow->x, shadow->y)];
2811 [s setShadowBlurRadius:shadow->blur];
2812 [s setShadowColor:nsColor(shadow->color)];
2813 [result setObject:s forKey:NSShadowAttributeName];
2816 int decoration = style->textDecorationsInEffect();
2817 if (decoration & LINE_THROUGH)
2818 [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
2820 int superscriptInt = 0;
2821 switch (style->verticalAlign()) {
2824 case BASELINE_MIDDLE:
2832 superscriptInt = -1;
2839 [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
2841 if (decoration & UNDERLINE)
2842 [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
2845 ExceptionCode ec = 0;
2846 nodeToRemove->remove(ec);
2853 NSWritingDirection FrameMac::baseWritingDirectionForSelectionStart() const
2855 NSWritingDirection result = NSWritingDirectionLeftToRight;
2857 Position pos = selectionController()->selection().visibleStart().deepEquivalent();
2858 Node *node = pos.node();
2859 if (!node || !node->renderer() || !node->renderer()->containingBlock())
2861 RenderStyle *style = node->renderer()->containingBlock()->style();
2865 switch (style->direction()) {
2867 result = NSWritingDirectionLeftToRight;
2870 result = NSWritingDirectionRightToLeft;
2877 void FrameMac::tokenizerProcessedData()
2881 [_bridge tokenizerProcessedData];
2884 void FrameMac::setBridge(WebCoreFrameBridge *bridge)
2886 if (_bridge == bridge)
2890 HardRelease(_bridge);
2894 String FrameMac::overrideMediaType() const
2896 NSString *overrideType = [_bridge overrideMediaType];
2898 return overrideType;
2902 NSColor *FrameMac::bodyBackgroundColor() const
2904 if (document() && document()->body() && document()->body()->renderer()) {
2905 Color bgColor = document()->body()->renderer()->style()->backgroundColor();
2906 if (bgColor.isValid())
2907 return nsColor(bgColor);
2912 WebCoreKeyboardUIMode FrameMac::keyboardUIMode() const
2914 BEGIN_BLOCK_OBJC_EXCEPTIONS;
2915 return [_bridge keyboardUIMode];
2916 END_BLOCK_OBJC_EXCEPTIONS;
2918 return WebCoreKeyboardAccessDefault;
2921 void FrameMac::didTellBridgeAboutLoad(const String& URL)
2923 urlsBridgeKnowsAbout.add(URL);
2926 bool FrameMac::haveToldBridgeAboutLoad(const String& URL)
2928 return urlsBridgeKnowsAbout.contains(URL);
2931 void FrameMac::clear()
2933 urlsBridgeKnowsAbout.clear();
2934 setMarkedTextRange(0, nil, nil);
2938 void FrameMac::print()
2940 [Mac(this)->_bridge print];
2943 KJS::Bindings::Instance *FrameMac::getAppletInstanceForWidget(Widget *widget)
2945 NSView *aView = widget->getView();
2950 // Get a pointer to the actual Java applet instance.
2951 if ([_bridge respondsToSelector:@selector(getAppletInView:)])
2952 applet = [_bridge getAppletInView:aView];
2954 applet = [_bridge pollForAppletInView:aView];
2957 // Wrap the Java instance in a language neutral binding and hand
2958 // off ownership to the APPLET element.
2959 KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2960 KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::JavaLanguage, applet, executionContext);
2967 static KJS::Bindings::Instance *getInstanceForView(NSView *aView)
2969 if ([aView respondsToSelector:@selector(objectForWebScript)]){
2970 id object = [aView objectForWebScript];
2972 KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2973 return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::ObjectiveCLanguage, object, executionContext);
2976 else if ([aView respondsToSelector:@selector(createPluginScriptableObject)]) {
2977 NPObject *object = [aView createPluginScriptableObject];
2979 KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction()(aView);
2980 KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance(KJS::Bindings::Instance::CLanguage, object, executionContext);
2982 // -createPluginScriptableObject returns a retained NPObject. The caller is expected to release it.
2983 _NPN_ReleaseObject(object);
2991 KJS::Bindings::Instance *FrameMac::getEmbedInstanceForWidget(Widget *widget)
2993 return getInstanceForView(widget->getView());
2996 KJS::Bindings::Instance *FrameMac::getObjectInstanceForWidget(Widget *widget)
2998 return getInstanceForView(widget->getView());
3001 void FrameMac::addPluginRootObject(KJS::Bindings::RootObject *root)
3003 m_rootObjects.append(root);
3006 void FrameMac::cleanupPluginObjects()
3008 // Delete old plug-in data structures
3011 unsigned count = m_rootObjects.size();
3012 for (unsigned i = 0; i < count; i++)
3013 m_rootObjects[i]->removeAllNativeReferences();
3014 m_rootObjects.clear();
3017 HardRelease(_windowScriptObject);
3018 _windowScriptObject = 0;
3020 if (_windowScriptNPObject) {
3021 // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
3022 // script object properly.
3023 // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
3024 _NPN_DeallocateObject(_windowScriptNPObject);
3025 _windowScriptNPObject = 0;
3029 void FrameMac::registerCommandForUndoOrRedo(PassRefPtr<EditCommand> cmd, bool isRedo)
3032 WebUndoAction action = static_cast<WebUndoAction>(cmd->editingAction());
3033 NSUndoManager* undoManager = [_bridge undoManager];
3034 WebCoreEditCommand* command = [WebCoreEditCommand commandWithEditCommand:cmd];
3035 NSString* actionName = [_bridge nameForUndoAction:action];
3036 [undoManager registerUndoWithTarget:_bridge selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:command];
3038 [undoManager setActionName:actionName];
3039 _haveUndoRedoOperations = YES;
3042 void FrameMac::registerCommandForUndo(PassRefPtr<EditCommand> cmd)
3044 registerCommandForUndoOrRedo(cmd, false);
3047 void FrameMac::registerCommandForRedo(PassRefPtr<EditCommand> cmd)
3049 registerCommandForUndoOrRedo(cmd, true);
3052 void FrameMac::clearUndoRedoOperations()
3054 if (_haveUndoRedoOperations) {
3055 // workaround for <rdar://problem/4645507> NSUndoManager dies
3056 // with uncaught exception when undo items cleared while
3058 NSUndoManager *undoManager = [_bridge undoManager];
3059 int groupingLevel = [undoManager groupingLevel];
3060 for (int i = 0; i < groupingLevel; ++i)
3061 [undoManager endUndoGrouping];
3063 [undoManager removeAllActionsWithTarget:_bridge];
3065 for (int i = 0; i < groupingLevel; ++i)
3066 [undoManager beginUndoGrouping];
3068 _haveUndoRedoOperations = NO;
3072 void FrameMac::issueUndoCommand()
3075 [[_bridge undoManager] undo];
3078 void FrameMac::issueRedoCommand()
3081 [[_bridge undoManager] redo];
3084 void FrameMac::issueCutCommand()
3086 [_bridge issueCutCommand];
3089 void FrameMac::issueCopyCommand()
3091 [_bridge issueCopyCommand];
3094 void FrameMac::issuePasteCommand()
3096 [_bridge issuePasteCommand];
3099 void FrameMac::issuePasteAndMatchStyleCommand()
3101 [_bridge issuePasteAndMatchStyleCommand];
3104 void FrameMac::issueTransposeCommand()
3106 [_bridge issueTransposeCommand];
3109 bool FrameMac::canUndo() const
3111 return [[Mac(this)->_bridge undoManager] canUndo];
3114 bool FrameMac::canRedo() const
3116 return [[Mac(this)->_bridge undoManager] canRedo];
3119 bool FrameMac::canPaste() const
3121 return [Mac(this)->_bridge canPaste];
3124 void FrameMac::markMisspellingsInAdjacentWords(const VisiblePosition &p)
3126 if (![_bridge isContinuousSpellCheckingEnabled])
3128 markMisspellings(Selection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
3131 void FrameMac::markMisspellings(const Selection& selection)
3133 // This function is called with a selection already expanded to word boundaries.
3134 // Might be nice to assert that here.
3136 if (![_bridge isContinuousSpellCheckingEnabled])
3139 RefPtr<Range> searchRange(selection.toRange());
3140 if (!searchRange || searchRange->isDetached())
3143 // If we're not in an editable node, bail.
3145 Node *editableNode = searchRange->startContainer(exception);
3146 if (!editableNode->isContentEditable())
3149 // Get the spell checker if it is available
3150 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3154 WordAwareIterator it(searchRange.get());
3156 while (!it.atEnd()) { // we may be starting at the end of the doc, and already by atEnd
3157 const UChar* chars = it.characters();
3158 int len = it.length();
3159 if (len > 1 || !DeprecatedChar(chars[0]).isSpace()) {
3160 NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(chars) length:len freeWhenDone:NO];
3162 // Loop over the chunk to find each misspelling in it.
3163 while (startIndex < len) {
3164 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:startIndex language:nil wrap:NO inSpellDocumentWithTag:[_bridge spellCheckerDocumentTag] wordCount:NULL];
3165 if (misspelling.length == 0)
3168 // Build up result range and string. Note the misspelling may span many text nodes,
3169 // but the CharIterator insulates us from this complexity
3170 RefPtr<Range> misspellingRange(rangeOfContents(document()));
3171 CharacterIterator chars(it.range().get());
3172 chars.advance(misspelling.location);
3173 misspellingRange->setStart(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3174 chars.advance(misspelling.length);
3175 misspellingRange->setEnd(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3176 // Mark misspelling in document.
3177 document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
3178 startIndex = misspelling.location + misspelling.length;
3188 void FrameMac::respondToChangedSelection(const Selection &oldSelection, bool closeTyping)
3191 if ([_bridge isContinuousSpellCheckingEnabled]) {
3192 Selection oldAdjacentWords;
3194 // If this is a change in selection resulting from a delete operation, oldSelection may no longer
3195 // be in the document.
3196 if (oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
3197 VisiblePosition oldStart(oldSelection.visibleStart());
3198 oldAdjacentWords = Selection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
3201 VisiblePosition newStart(selectionController()->selection().visibleStart());
3202 Selection newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
3204 // When typing we check spelling elsewhere, so don't redo it here.
3205 if (closeTyping && oldAdjacentWords != newAdjacentWords)
3206 markMisspellings(oldAdjacentWords);
3208 // This only erases a marker in the first word of the selection.
3209 // Perhaps peculiar, but it matches AppKit.
3210 document()->removeMarkers(newAdjacentWords.toRange().get(), DocumentMarker::Spelling);
3212 // When continuous spell checking is off, no markers appear after the selection changes.
3213 document()->removeMarkers(DocumentMarker::Spelling);
3216 [_bridge respondToChangedSelection];
3219 bool FrameMac::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const
3221 return [_bridge shouldChangeSelectedDOMRange:[DOMRange _rangeWith:oldSelection.toRange().get()]
3222 toDOMRange:[DOMRange _rangeWith:newSelection.toRange().get()]
3223 affinity:static_cast<NSSelectionAffinity>(affinity)
3224 stillSelecting:stillSelecting];
3227 bool FrameMac::shouldDeleteSelection(const Selection& selection) const
3229 return [_bridge shouldDeleteSelectedDOMRange:[DOMRange _rangeWith:selection.toRange().get()]];
3232 void FrameMac::respondToChangedContents(const Selection& selection)
3234 if (AXObjectCache::accessibilityEnabled()) {
3235 Node* node = selection.start().node();
3237 renderer()->document()->axObjectCache()->postNotification(node->renderer(), "AXValueChanged");
3239 [_bridge respondToChangedContents];
3242 bool FrameMac::isContentEditable() const
3244 return Frame::isContentEditable() || [_bridge isEditable];
3247 bool FrameMac::shouldBeginEditing(const Range *range) const
3250 return [_bridge shouldBeginEditing:[DOMRange _rangeWith:const_cast<Range*>(range)]];
3253 bool FrameMac::shouldEndEditing(const Range *range) const
3256 return [_bridge shouldEndEditing:[DOMRange _rangeWith:const_cast<Range*>(range)]];
3259 void FrameMac::didBeginEditing() const
3261 [_bridge didBeginEditing];
3264 void FrameMac::didEndEditing() const
3266 [_bridge didEndEditing];
3269 void FrameMac::textFieldDidBeginEditing(Element* input)
3271 BEGIN_BLOCK_OBJC_EXCEPTIONS;
3272 [_bridge textFieldDidBeginEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
3273 END_BLOCK_OBJC_EXCEPTIONS;
3276 void FrameMac::textFieldDidEndEditing(Element* input)
3278 BEGIN_BLOCK_OBJC_EXCEPTIONS;
3279 [_bridge textFieldDidEndEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
3280 END_BLOCK_OBJC_EXCEPTIONS;
3283 void FrameMac::textDidChangeInTextField(Element* input)
3285 BEGIN_BLOCK_OBJC_EXCEPTIONS;
3286 [_bridge textDidChangeInTextField:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
3287 END_BLOCK_OBJC_EXCEPTIONS;
3290 void FrameMac::textDidChangeInTextArea(Element* textarea)
3292 BEGIN_BLOCK_OBJC_EXCEPTIONS;
3293 [_bridge textDidChangeInTextArea:(DOMHTMLTextAreaElement *)[DOMElement _elementWith:textarea]];
3294 END_BLOCK_OBJC_EXCEPTIONS;
3297 bool FrameMac::doTextFieldCommandFromEvent(Element* input, const PlatformKeyboardEvent* event)
3299 // FIXME: We might eventually need to make sure key bindings for editing work even with
3300 // events created with the DOM API. Those don't have a PlatformKeyboardEvent.
3304 BOOL result = false;
3305 BEGIN_BLOCK_OBJC_EXCEPTIONS;
3306 SEL selector = selectorForKeyEvent(event);
3308 result = [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:selector];
3309 END_BLOCK_OBJC_EXCEPTIONS;
3313 void FrameMac::textWillBeDeletedInTextField(Element* input)
3315 // We're using the deleteBackward selector for all deletion operations since the autofill code treats all deletions the same way.
3316 BEGIN_BLOCK_OBJC_EXCEPTIONS;
3317 [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:@selector(deleteBackward:)];
3318 END_BLOCK_OBJC_EXCEPTIONS;
3321 bool FrameMac::inputManagerHasMarkedText() const
3323 BEGIN_BLOCK_OBJC_EXCEPTIONS;
3324 return [[NSInputManager currentInputManager] hasMarkedText];
3325 END_BLOCK_OBJC_EXCEPTIONS
3329 const short enableRomanKeyboardsOnly = -23;
3330 void FrameMac::setSecureKeyboardEntry(bool enable)
3333 EnableSecureEventInput();
3334 // FIXME: KeyScript is deprecated in Leopard, we need a new solution for this <rdar://problem/4727607>
3335 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
3336 KeyScript(enableRomanKeyboardsOnly);
3339 DisableSecureEventInput();
3340 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
3341 KeyScript(smKeyEnableKybds);
3346 bool FrameMac::isSecureKeyboardEntry()
3348 return IsSecureEventInputEnabled();
3351 static void convertAttributesToUnderlines(Vector<MarkedTextUnderline>& result, const Range *markedTextRange, NSArray *attributes, NSArray *ranges)
3354 int baseOffset = markedTextRange->startOffset(exception);
3356 unsigned length = [attributes count];
3357 ASSERT([ranges count] == length);
3359 for (unsigned i = 0; i < length; i++) {
3360 NSNumber *style = [[attributes objectAtIndex:i] objectForKey:NSUnderlineStyleAttributeName];
3363 NSRange range = [[ranges objectAtIndex:i] rangeValue];
3364 NSColor *color = [[attributes objectAtIndex:i] objectForKey:NSUnderlineColorAttributeName];
3365 Color qColor = Color::black;
3367 NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
3368 qColor = Color(makeRGBA((int)(255 * [deviceColor redComponent]),
3369 (int)(255 * [deviceColor blueComponent]),
3370 (int)(255 * [deviceColor greenComponent]),
3371 (int)(255 * [deviceColor alphaComponent])));
3374 result.append(MarkedTextUnderline(range.location + baseOffset,
3375 range.location + baseOffset + range.length,
3377 [style intValue] > 1));
3381 void FrameMac::setMarkedTextRange(const Range *range, NSArray *attributes, NSArray *ranges)
3385 ASSERT(!range || range->startContainer(exception) == range->endContainer(exception));
3386 ASSERT(!range || range->collapsed(exception) || range->startContainer(exception)->isTextNode());
3388 d->m_markedTextUnderlines.clear();
3389 if (attributes == nil)
3390 d->m_markedTextUsesUnderlines = false;
3392 d->m_markedTextUsesUnderlines = true;
3393 convertAttributesToUnderlines(d->m_markedTextUnderlines, range, attributes, ranges);
3396 if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3397 m_markedTextRange->startContainer(exception)->renderer()->repaint();
3399 if (range && range->collapsed(exception))
3400 m_markedTextRange = 0;
3402 m_markedTextRange = const_cast<Range*>(range);
3404 if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3405 m_markedTextRange->startContainer(exception)->renderer()->repaint();
3408 bool FrameMac::canGoBackOrForward(int distance) const
3410 return [_bridge canGoBackOrForward:distance];
3413 void FrameMac::didFirstLayout()
3415 [_bridge didFirstLayout];
3418 NSMutableDictionary *FrameMac::dashboardRegionsDictionary()
3420 Document *doc = document();
3424 const Vector<DashboardRegionValue>& regions = doc->dashboardRegions();
3425 size_t n = regions.size();
3427 // Convert the DeprecatedValueList<DashboardRegionValue> into a NSDictionary of WebDashboardRegions
3428 NSMutableDictionary *webRegions = [NSMutableDictionary dictionaryWithCapacity:n];
3429 for (size_t i = 0; i < n; i++) {
3430 const DashboardRegionValue& region = regions[i];
3432 if (region.type == StyleDashboardRegion::None)
3435 NSString *label = region.label;
3436 WebDashboardRegionType type = WebDashboardRegionTypeNone;
3437 if (region.type == StyleDashboardRegion::Circle)
3438 type = WebDashboardRegionTypeCircle;
3439 else if (region.type == StyleDashboardRegion::Rectangle)
3440 type = WebDashboardRegionTypeRectangle;
3441 NSMutableArray *regionValues = [webRegions objectForKey:label];
3442 if (!regionValues) {
3443 regionValues = [[NSMutableArray alloc] initWithCapacity:1];
3444 [webRegions setObject:regionValues forKey:label];
3445 [regionValues release];
3448 WebDashboardRegion *webRegion = [[WebDashboardRegion alloc] initWithRect:region.bounds clip:region.clip type:type];
3449 [regionValues addObject:webRegion];
3450 [webRegion release];
3456 void FrameMac::dashboardRegionsChanged()
3458 NSMutableDictionary *webRegions = dashboardRegionsDictionary();
3459 [_bridge dashboardRegionsChanged:webRegions];
3462 void FrameMac::willPopupMenu(NSMenu * menu)
3464 [_bridge willPopupMenu:menu];
3467 bool FrameMac::isCharacterSmartReplaceExempt(UChar c, bool isPreviousChar)
3469 return [_bridge isCharacterSmartReplaceExempt:c isPreviousCharacter:isPreviousChar];
3472 void FrameMac::handledOnloadEvents()
3474 [_bridge handledOnloadEvents];
3477 bool FrameMac::shouldClose()
3479 BEGIN_BLOCK_OBJC_EXCEPTIONS;
3481 if (![_bridge canRunBeforeUnloadConfirmPanel])
3484 RefPtr<Document> doc = document();
3487 HTMLElement* body = doc->body();
3491 RefPtr<BeforeUnloadEvent> event = new BeforeUnloadEvent;
3492 event->setTarget(doc.get());
3493 doc->handleWindowEvent(event.get(), false);
3495 if (!event->defaultPrevented() && doc)
3496 doc->defaultEventHandler(event.get());
3497 if (event->result().isNull())
3500 String text = event->result();
3501 text.replace('\\', backslashAsCurrencySymbol());
3503 return [_bridge runBeforeUnloadConfirmPanelWithMessage:text];
3505 END_BLOCK_OBJC_EXCEPTIONS;
3510 void FrameMac::dragSourceMovedTo(const PlatformMouseEvent& event)
3512 if (_dragSrc && _dragSrcMayBeDHTML)
3513 // for now we don't care if event handler cancels default behavior, since there is none
3514 dispatchDragSrcEvent(dragEvent, event);
3517 void FrameMac::dragSourceEndedAt(const PlatformMouseEvent& event, NSDragOperation operation)
3519 if (_dragSrc && _dragSrcMayBeDHTML) {
3520 _dragClipboard->setDestinationOperation(operation);
3521 // for now we don't care if event handler cancels default behavior, since there is none
3522 dispatchDragSrcEvent(dragendEvent, event);
3528 // returns if we should continue "default processing", i.e., whether eventhandler canceled
3529 bool FrameMac::dispatchDragSrcEvent(const AtomicString &eventType, const PlatformMouseEvent& event) const
3531 bool noDefaultProc = d->m_view->dispatchDragEvent(eventType, _dragSrc.get(), event, _dragClipboard.get());
3532 return !noDefaultProc;
3535 void Frame::setNeedsReapplyStyles()
3537 [Mac(this)->bridge() setNeedsReapplyStyles];
3540 FloatRect FrameMac::customHighlightLineRect(const AtomicString& type, const FloatRect& lineRect)
3542 return [bridge() customHighlightRect:type forLine:lineRect];
3545 void FrameMac::paintCustomHighlight(const AtomicString& type, const FloatRect& boxRect, const FloatRect& lineRect, bool text, bool line)
3547 [bridge() paintCustomHighlight:type forBox:boxRect onLine:lineRect behindText:text entireLine:line];
3550 KURL FrameMac::originalRequestURL() const
3552 return [_bridge originalRequestURL];
3555 bool FrameMac::isLoadTypeReload()
3557 return [_bridge isLoadTypeReload];