|
1 /* |
|
2 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. |
|
3 * (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com) |
|
4 * |
|
5 * Redistribution and use in source and binary forms, with or without |
|
6 * modification, are permitted provided that the following conditions |
|
7 * are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * 2. Redistributions in binary form must reproduce the above copyright |
|
12 * notice, this list of conditions and the following disclaimer in the |
|
13 * documentation and/or other materials provided with the distribution. |
|
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
|
15 * its contributors may be used to endorse or promote products derived |
|
16 * from this software without specific prior written permission. |
|
17 * |
|
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
|
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
|
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
28 */ |
|
29 |
|
30 #import "WebHTMLView.h" |
|
31 |
|
32 #import "DOMCSSStyleDeclarationInternal.h" |
|
33 #import "DOMDocumentFragmentInternal.h" |
|
34 #import "DOMDocumentInternal.h" |
|
35 #import "DOMNodeInternal.h" |
|
36 #import "DOMRangeInternal.h" |
|
37 #import "WebArchive.h" |
|
38 #import "WebClipView.h" |
|
39 #import "WebDOMOperationsInternal.h" |
|
40 #import "WebDataSourceInternal.h" |
|
41 #import "WebDefaultUIDelegate.h" |
|
42 #import "WebDelegateImplementationCaching.h" |
|
43 #import "WebDocumentInternal.h" |
|
44 #import "WebDynamicScrollBarsView.h" |
|
45 #import "WebEditingDelegate.h" |
|
46 #import "WebElementDictionary.h" |
|
47 #import "WebFrameInternal.h" |
|
48 #import "WebFramePrivate.h" |
|
49 #import "WebFrameViewInternal.h" |
|
50 #import "WebHTMLRepresentationPrivate.h" |
|
51 #import "WebHTMLViewInternal.h" |
|
52 #import "WebKitLogging.h" |
|
53 #import "WebKitNSStringExtras.h" |
|
54 #import "WebKitVersionChecks.h" |
|
55 #import "WebLocalizableStrings.h" |
|
56 #import "WebNSAttributedStringExtras.h" |
|
57 #import "WebNSEventExtras.h" |
|
58 #import "WebNSFileManagerExtras.h" |
|
59 #import "WebNSImageExtras.h" |
|
60 #import "WebNSObjectExtras.h" |
|
61 #import "WebNSPasteboardExtras.h" |
|
62 #import "WebNSPrintOperationExtras.h" |
|
63 #import "WebNSURLExtras.h" |
|
64 #import "WebNSViewExtras.h" |
|
65 #import "WebNetscapePluginView.h" |
|
66 #import "WebNodeHighlight.h" |
|
67 #import "WebPluginController.h" |
|
68 #import "WebPreferences.h" |
|
69 #import "WebPreferencesPrivate.h" |
|
70 #import "WebResourcePrivate.h" |
|
71 #import "WebStringTruncator.h" |
|
72 #import "WebTextCompletionController.h" |
|
73 #import "WebTypesInternal.h" |
|
74 #import "WebUIDelegatePrivate.h" |
|
75 #import "WebViewInternal.h" |
|
76 #import <AppKit/NSAccessibility.h> |
|
77 #import <ApplicationServices/ApplicationServices.h> |
|
78 #import <WebCore/CSSMutableStyleDeclaration.h> |
|
79 #import <WebCore/CachedImage.h> |
|
80 #import <WebCore/CachedResourceClient.h> |
|
81 #import <WebCore/ColorMac.h> |
|
82 #import <WebCore/ContextMenu.h> |
|
83 #import <WebCore/ContextMenuController.h> |
|
84 #import <WebCore/Document.h> |
|
85 #import <WebCore/DocumentFragment.h> |
|
86 #import <WebCore/DragController.h> |
|
87 #import <WebCore/Editor.h> |
|
88 #import <WebCore/EditorDeleteAction.h> |
|
89 #import <WebCore/Element.h> |
|
90 #import <WebCore/EventHandler.h> |
|
91 #import <WebCore/ExceptionHandlers.h> |
|
92 #import <WebCore/FloatRect.h> |
|
93 #import <WebCore/FocusController.h> |
|
94 #import <WebCore/Frame.h> |
|
95 #import <WebCore/FrameLoader.h> |
|
96 #import <WebCore/FrameView.h> |
|
97 #import <WebCore/HTMLNames.h> |
|
98 #import <WebCore/HitTestResult.h> |
|
99 #import <WebCore/Image.h> |
|
100 #import <WebCore/KeyboardEvent.h> |
|
101 #import <WebCore/LegacyWebArchive.h> |
|
102 #import <WebCore/MIMETypeRegistry.h> |
|
103 #import <WebCore/Page.h> |
|
104 #import <WebCore/PlatformKeyboardEvent.h> |
|
105 #import <WebCore/Range.h> |
|
106 #import <WebCore/RuntimeApplicationChecks.h> |
|
107 #import <WebCore/SelectionController.h> |
|
108 #import <WebCore/SharedBuffer.h> |
|
109 #import <WebCore/SimpleFontData.h> |
|
110 #import <WebCore/Text.h> |
|
111 #import <WebCore/WebCoreObjCExtras.h> |
|
112 #import <WebCore/WebFontCache.h> |
|
113 #import <WebCore/markup.h> |
|
114 #import <WebKit/DOM.h> |
|
115 #import <WebKit/DOMExtensions.h> |
|
116 #import <WebKit/DOMPrivate.h> |
|
117 #import <WebKitSystemInterface.h> |
|
118 #import <dlfcn.h> |
|
119 #import <limits> |
|
120 #import <runtime/InitializeThreading.h> |
|
121 #import <wtf/Threading.h> |
|
122 |
|
123 #if USE(ACCELERATED_COMPOSITING) |
|
124 #import <QuartzCore/QuartzCore.h> |
|
125 #endif |
|
126 |
|
127 using namespace WebCore; |
|
128 using namespace HTMLNames; |
|
129 using namespace WTF; |
|
130 using namespace std; |
|
131 |
|
132 @interface NSWindow (BorderViewAccess) |
|
133 - (NSView*)_web_borderView; |
|
134 @end |
|
135 |
|
136 @implementation NSWindow (BorderViewAccess) |
|
137 - (NSView*)_web_borderView |
|
138 { |
|
139 return _borderView; |
|
140 } |
|
141 @end |
|
142 |
|
143 @interface WebResponderChainSink : NSResponder { |
|
144 NSResponder* _lastResponderInChain; |
|
145 BOOL _receivedUnhandledCommand; |
|
146 } |
|
147 - (id)initWithResponderChain:(NSResponder *)chain; |
|
148 - (void)detach; |
|
149 - (BOOL)receivedUnhandledCommand; |
|
150 @end |
|
151 |
|
152 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view) |
|
153 static BOOL forceNSViewHitTest; |
|
154 |
|
155 // if YES, do the "top WebHTMLView" hit test (which we'd like to do all the time but can't because of Java requirements [see bug 4349721]) |
|
156 static BOOL forceWebHTMLViewHitTest; |
|
157 |
|
158 static WebHTMLView *lastHitView; |
|
159 |
|
160 static bool needsCursorRectsSupportAtPoint(NSWindow* window, NSPoint point) |
|
161 { |
|
162 forceNSViewHitTest = YES; |
|
163 NSView* view = [[window _web_borderView] hitTest:point]; |
|
164 forceNSViewHitTest = NO; |
|
165 |
|
166 // WebHTMLView doesn't use cursor rects. |
|
167 if ([view isKindOfClass:[WebHTMLView class]]) |
|
168 return false; |
|
169 |
|
170 // Neither do NPAPI plug-ins. |
|
171 if ([view isKindOfClass:[WebBaseNetscapePluginView class]]) |
|
172 return false; |
|
173 |
|
174 // Non-Web content, WebPDFView, and WebKit plug-ins use normal cursor handling. |
|
175 return true; |
|
176 } |
|
177 |
|
178 #ifndef BUILDING_ON_TIGER |
|
179 |
|
180 static IMP oldSetCursorForMouseLocationIMP; |
|
181 |
|
182 // Overriding an internal method is a hack; <rdar://problem/7662987> tracks finding a better solution. |
|
183 static void setCursor(NSWindow *self, SEL cmd, NSPoint point) |
|
184 { |
|
185 if (needsCursorRectsSupportAtPoint(self, point)) |
|
186 oldSetCursorForMouseLocationIMP(self, cmd, point); |
|
187 } |
|
188 |
|
189 #else |
|
190 |
|
191 static IMP oldResetCursorRectsIMP; |
|
192 static IMP oldSetCursorIMP; |
|
193 static BOOL canSetCursor = YES; |
|
194 |
|
195 static void resetCursorRects(NSWindow* self, SEL cmd) |
|
196 { |
|
197 canSetCursor = needsCursorRectsSupportAtPoint(self, [self mouseLocationOutsideOfEventStream]); |
|
198 oldResetCursorRectsIMP(self, cmd); |
|
199 canSetCursor = YES; |
|
200 } |
|
201 |
|
202 static void setCursor(NSCursor* self, SEL cmd) |
|
203 { |
|
204 if (canSetCursor) |
|
205 oldSetCursorIMP(self, cmd); |
|
206 } |
|
207 |
|
208 #endif |
|
209 |
|
210 extern "C" { |
|
211 |
|
212 // Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers. |
|
213 |
|
214 extern NSString *NSMarkedClauseSegmentAttributeName; |
|
215 extern NSString *NSTextInputReplacementRangeAttributeName; |
|
216 |
|
217 } |
|
218 |
|
219 @interface NSView (WebNSViewDetails) |
|
220 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView; |
|
221 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect; |
|
222 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView; |
|
223 - (NSRect)_dirtyRect; |
|
224 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants; |
|
225 - (BOOL)_drawnByAncestor; |
|
226 - (void)_propagateDirtyRectsToOpaqueAncestors; |
|
227 - (void)_windowChangedKeyState; |
|
228 #if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD) |
|
229 - (void)_updateLayerGeometryFromView; |
|
230 #endif |
|
231 @end |
|
232 |
|
233 #if USE(ACCELERATED_COMPOSITING) |
|
234 static IMP oldSetNeedsDisplayInRectIMP; |
|
235 |
|
236 static void setNeedsDisplayInRect(NSView *self, SEL cmd, NSRect invalidRect) |
|
237 { |
|
238 if (![self _drawnByAncestor]) { |
|
239 oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect); |
|
240 return; |
|
241 } |
|
242 |
|
243 static Class webFrameViewClass = [WebFrameView class]; |
|
244 WebFrameView *enclosingWebFrameView = (WebFrameView *)self; |
|
245 while (enclosingWebFrameView && ![enclosingWebFrameView isKindOfClass:webFrameViewClass]) |
|
246 enclosingWebFrameView = (WebFrameView *)[enclosingWebFrameView superview]; |
|
247 |
|
248 if (!enclosingWebFrameView) { |
|
249 oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect); |
|
250 return; |
|
251 } |
|
252 |
|
253 Frame* coreFrame = core([enclosingWebFrameView webFrame]); |
|
254 FrameView* frameView = coreFrame ? coreFrame->view() : 0; |
|
255 if (!frameView || !frameView->isEnclosedInCompositingLayer()) { |
|
256 oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect); |
|
257 return; |
|
258 } |
|
259 |
|
260 NSRect invalidRectInWebFrameViewCoordinates = [enclosingWebFrameView convertRect:invalidRect fromView:self]; |
|
261 IntRect invalidRectInFrameViewCoordinates(invalidRectInWebFrameViewCoordinates); |
|
262 if (![enclosingWebFrameView isFlipped]) |
|
263 invalidRectInFrameViewCoordinates.setY(frameView->frameRect().size().height() - invalidRectInFrameViewCoordinates.bottom()); |
|
264 |
|
265 frameView->invalidateRect(invalidRectInFrameViewCoordinates); |
|
266 } |
|
267 #endif // USE(ACCELERATED_COMPOSITING) |
|
268 |
|
269 @interface NSApplication (WebNSApplicationDetails) |
|
270 - (void)speakString:(NSString *)string; |
|
271 @end |
|
272 |
|
273 @interface NSWindow (WebNSWindowDetails) |
|
274 - (id)_newFirstResponderAfterResigning; |
|
275 @end |
|
276 |
|
277 @interface NSAttributedString (WebNSAttributedStringDetails) |
|
278 - (id)_initWithDOMRange:(DOMRange *)range; |
|
279 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources; |
|
280 @end |
|
281 |
|
282 @interface NSSpellChecker (WebNSSpellCheckerDetails) |
|
283 - (void)learnWord:(NSString *)word; |
|
284 @end |
|
285 |
|
286 // By imaging to a width a little wider than the available pixels, |
|
287 // thin pages will be scaled down a little, matching the way they |
|
288 // print in IE and Camino. This lets them use fewer sheets than they |
|
289 // would otherwise, which is presumably why other browsers do this. |
|
290 // Wide pages will be scaled down more than this. |
|
291 const float _WebHTMLViewPrintingMinimumShrinkFactor = 1.25; |
|
292 |
|
293 // This number determines how small we are willing to reduce the page content |
|
294 // in order to accommodate the widest line. If the page would have to be |
|
295 // reduced smaller to make the widest line fit, we just clip instead (this |
|
296 // behavior matches MacIE and Mozilla, at least) |
|
297 const float _WebHTMLViewPrintingMaximumShrinkFactor = 2; |
|
298 |
|
299 // This number determines how short the last printed page of a multi-page print session |
|
300 // can be before we try to shrink the scale in order to reduce the number of pages, and |
|
301 // thus eliminate the orphan. |
|
302 #define LastPrintedPageOrphanRatio 0.1f |
|
303 |
|
304 // This number determines the amount the scale factor is adjusted to try to eliminate orphans. |
|
305 // It has no direct mathematical relationship to LastPrintedPageOrphanRatio, due to variable |
|
306 // numbers of pages, logic to avoid breaking elements, and CSS-supplied hard page breaks. |
|
307 #define PrintingOrphanShrinkAdjustment 1.1f |
|
308 |
|
309 #define AUTOSCROLL_INTERVAL 0.1f |
|
310 |
|
311 #define DRAG_LABEL_BORDER_X 4.0f |
|
312 //Keep border_y in synch with DragController::LinkDragBorderInset |
|
313 #define DRAG_LABEL_BORDER_Y 2.0f |
|
314 #define DRAG_LABEL_RADIUS 5.0f |
|
315 #define DRAG_LABEL_BORDER_Y_OFFSET 2.0f |
|
316 |
|
317 #define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP 120.0f |
|
318 #define MAX_DRAG_LABEL_WIDTH 320.0f |
|
319 |
|
320 #define DRAG_LINK_LABEL_FONT_SIZE 11.0f |
|
321 #define DRAG_LINK_URL_FONT_SIZE 10.0f |
|
322 |
|
323 // Any non-zero value will do, but using something recognizable might help us debug some day. |
|
324 #define TRACKING_RECT_TAG 0xBADFACE |
|
325 |
|
326 // FIXME: This constant is copied from AppKit's _NXSmartPaste constant. |
|
327 #define WebSmartPastePboardType @"NeXT smart paste pasteboard type" |
|
328 |
|
329 #define STANDARD_WEIGHT 5 |
|
330 #define MIN_BOLD_WEIGHT 7 |
|
331 #define STANDARD_BOLD_WEIGHT 9 |
|
332 |
|
333 // Fake URL scheme. |
|
334 #define WebDataProtocolScheme @"webkit-fake-url" |
|
335 |
|
336 // <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present |
|
337 // in some NIB files, so NSUnarchiver must be still able to look up this now-unused class. |
|
338 @interface WebCoreScrollView : NSScrollView |
|
339 @end |
|
340 |
|
341 @implementation WebCoreScrollView |
|
342 @end |
|
343 |
|
344 // We need this to be able to safely reference the CachedImage for the promised drag data |
|
345 static CachedResourceClient* promisedDataClient() |
|
346 { |
|
347 static CachedResourceClient* staticCachedResourceClient = new CachedResourceClient; |
|
348 return staticCachedResourceClient; |
|
349 } |
|
350 |
|
351 @interface WebHTMLView (WebHTMLViewFileInternal) |
|
352 - (BOOL)_imageExistsAtPaths:(NSArray *)paths; |
|
353 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText; |
|
354 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard; |
|
355 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText; |
|
356 - (void)_removeMouseMovedObserverUnconditionally; |
|
357 - (void)_removeSuperviewObservers; |
|
358 - (void)_removeWindowObservers; |
|
359 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action; |
|
360 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action; |
|
361 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action; |
|
362 - (DOMRange *)_selectedRange; |
|
363 - (BOOL)_shouldDeleteRange:(DOMRange *)range; |
|
364 - (NSView *)_hitViewForEvent:(NSEvent *)event; |
|
365 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString; |
|
366 - (DOMRange *)_documentRange; |
|
367 - (void)_setMouseDownEvent:(NSEvent *)event; |
|
368 - (WebHTMLView *)_topHTMLView; |
|
369 - (BOOL)_isTopHTMLView; |
|
370 - (void)_web_setPrintingModeRecursive; |
|
371 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize; |
|
372 - (void)_web_clearPrintingModeRecursive; |
|
373 @end |
|
374 |
|
375 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
376 |
|
377 @interface WebHTMLView (WebHTMLViewTextCheckingInternal) |
|
378 - (void)orderFrontSubstitutionsPanel:(id)sender; |
|
379 - (BOOL)smartInsertDeleteEnabled; |
|
380 - (void)setSmartInsertDeleteEnabled:(BOOL)flag; |
|
381 - (void)toggleSmartInsertDelete:(id)sender; |
|
382 - (BOOL)isAutomaticQuoteSubstitutionEnabled; |
|
383 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag; |
|
384 - (void)toggleAutomaticQuoteSubstitution:(id)sender; |
|
385 - (BOOL)isAutomaticLinkDetectionEnabled; |
|
386 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag; |
|
387 - (void)toggleAutomaticLinkDetection:(id)sender; |
|
388 - (BOOL)isAutomaticDashSubstitutionEnabled; |
|
389 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag; |
|
390 - (void)toggleAutomaticDashSubstitution:(id)sender; |
|
391 - (BOOL)isAutomaticTextReplacementEnabled; |
|
392 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag; |
|
393 - (void)toggleAutomaticTextReplacement:(id)sender; |
|
394 - (BOOL)isAutomaticSpellingCorrectionEnabled; |
|
395 - (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag; |
|
396 - (void)toggleAutomaticSpellingCorrection:(id)sender; |
|
397 @end |
|
398 |
|
399 #endif |
|
400 |
|
401 @interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick. |
|
402 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize; |
|
403 @end |
|
404 |
|
405 @class NSTextInputContext; |
|
406 @interface NSResponder (AppKitDetails) |
|
407 - (NSTextInputContext *)inputContext; |
|
408 @end |
|
409 |
|
410 @interface NSObject (NSTextInputContextDetails) |
|
411 - (BOOL)wantsToHandleMouseEvents; |
|
412 - (BOOL)handleMouseEvent:(NSEvent *)event; |
|
413 @end |
|
414 |
|
415 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput> |
|
416 - (void)_updateSelectionForInputManager; |
|
417 @end |
|
418 |
|
419 @interface WebHTMLView (WebEditingStyleSupport) |
|
420 - (DOMCSSStyleDeclaration *)_emptyStyle; |
|
421 - (NSString *)_colorAsString:(NSColor *)color; |
|
422 @end |
|
423 |
|
424 @interface NSView (WebHTMLViewFileInternal) |
|
425 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array; |
|
426 @end |
|
427 |
|
428 @interface NSMutableDictionary (WebHTMLViewFileInternal) |
|
429 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key; |
|
430 @end |
|
431 |
|
432 struct WebHTMLViewInterpretKeyEventsParameters { |
|
433 KeyboardEvent* event; |
|
434 BOOL eventWasHandled; |
|
435 BOOL shouldSaveCommand; |
|
436 // The Input Method may consume an event and not tell us, in |
|
437 // which case we should not bubble the event up the DOM |
|
438 BOOL consumedByIM; |
|
439 }; |
|
440 |
|
441 @interface WebHTMLViewPrivate : NSObject { |
|
442 @public |
|
443 BOOL closed; |
|
444 BOOL needsToApplyStyles; |
|
445 BOOL ignoringMouseDraggedEvents; |
|
446 BOOL printing; |
|
447 BOOL avoidingPrintOrphan; |
|
448 BOOL observingMouseMovedNotifications; |
|
449 BOOL observingSuperviewNotifications; |
|
450 BOOL observingWindowNotifications; |
|
451 |
|
452 id savedSubviews; |
|
453 BOOL subviewsSetAside; |
|
454 |
|
455 #if USE(ACCELERATED_COMPOSITING) |
|
456 NSView *layerHostingView; |
|
457 #endif |
|
458 |
|
459 NSEvent *mouseDownEvent; // Kept after handling the event. |
|
460 BOOL handlingMouseDownEvent; |
|
461 NSEvent *keyDownEvent; // Kept after handling the event. |
|
462 |
|
463 // A WebHTMLView has a single input context, but we return nil when in non-editable content to avoid making input methods do their work. |
|
464 // This state is saved each time selection changes, because computing it causes style recalc, which is not always safe to do. |
|
465 BOOL exposeInputContext; |
|
466 |
|
467 NSPoint lastScrollPosition; |
|
468 #ifndef BUILDING_ON_TIGER |
|
469 BOOL inScrollPositionChanged; |
|
470 #endif |
|
471 |
|
472 WebPluginController *pluginController; |
|
473 |
|
474 NSString *toolTip; |
|
475 NSToolTipTag lastToolTipTag; |
|
476 id trackingRectOwner; |
|
477 void *trackingRectUserData; |
|
478 |
|
479 NSTimer *autoscrollTimer; |
|
480 NSEvent *autoscrollTriggerEvent; |
|
481 |
|
482 NSArray *pageRects; |
|
483 |
|
484 NSMutableDictionary *highlighters; |
|
485 |
|
486 #ifdef BUILDING_ON_TIGER |
|
487 BOOL nextResponderDisabledOnce; |
|
488 #endif |
|
489 |
|
490 WebTextCompletionController *completionController; |
|
491 |
|
492 BOOL transparentBackground; |
|
493 |
|
494 WebHTMLViewInterpretKeyEventsParameters* interpretKeyEventsParameters; |
|
495 BOOL receivedNOOP; |
|
496 |
|
497 WebDataSource *dataSource; |
|
498 WebCore::CachedImage* promisedDragTIFFDataSource; |
|
499 |
|
500 CFRunLoopTimerRef updateMouseoverTimer; |
|
501 |
|
502 SEL selectorForDoCommandBySelector; |
|
503 |
|
504 #ifndef NDEBUG |
|
505 BOOL enumeratingSubviews; |
|
506 #endif |
|
507 } |
|
508 - (void)clear; |
|
509 @end |
|
510 |
|
511 static NSCellStateValue kit(TriState state) |
|
512 { |
|
513 switch (state) { |
|
514 case FalseTriState: |
|
515 return NSOffState; |
|
516 case TrueTriState: |
|
517 return NSOnState; |
|
518 case MixedTriState: |
|
519 return NSMixedState; |
|
520 } |
|
521 ASSERT_NOT_REACHED(); |
|
522 return NSOffState; |
|
523 } |
|
524 |
|
525 @implementation WebHTMLViewPrivate |
|
526 |
|
527 + (void)initialize |
|
528 { |
|
529 JSC::initializeThreading(); |
|
530 WTF::initializeMainThreadToProcessMainThread(); |
|
531 #ifndef BUILDING_ON_TIGER |
|
532 WebCoreObjCFinalizeOnMainThread(self); |
|
533 #endif |
|
534 |
|
535 #ifndef BUILDING_ON_TIGER |
|
536 if (!oldSetCursorForMouseLocationIMP) { |
|
537 Method setCursorMethod = class_getInstanceMethod([NSWindow class], @selector(_setCursorForMouseLocation:)); |
|
538 ASSERT(setCursorMethod); |
|
539 oldSetCursorForMouseLocationIMP = method_setImplementation(setCursorMethod, (IMP)setCursor); |
|
540 ASSERT(oldSetCursorForMouseLocationIMP); |
|
541 } |
|
542 |
|
543 #if USE(ACCELERATED_COMPOSITING) |
|
544 if (!oldSetNeedsDisplayInRectIMP) { |
|
545 Method setNeedsDisplayInRectMethod = class_getInstanceMethod([NSView class], @selector(setNeedsDisplayInRect:)); |
|
546 ASSERT(setNeedsDisplayInRectMethod); |
|
547 oldSetNeedsDisplayInRectIMP = method_setImplementation(setNeedsDisplayInRectMethod, (IMP)setNeedsDisplayInRect); |
|
548 ASSERT(oldSetNeedsDisplayInRectIMP); |
|
549 } |
|
550 #endif // USE(ACCELERATED_COMPOSITING) |
|
551 |
|
552 #else // defined(BUILDING_ON_TIGER) |
|
553 if (!oldSetCursorIMP) { |
|
554 Method setCursorMethod = class_getInstanceMethod([NSCursor class], @selector(set)); |
|
555 ASSERT(setCursorMethod); |
|
556 oldSetCursorIMP = method_setImplementation(setCursorMethod, (IMP)setCursor); |
|
557 ASSERT(oldSetCursorIMP); |
|
558 } |
|
559 if (!oldResetCursorRectsIMP) { |
|
560 Method resetCursorRectsMethod = class_getInstanceMethod([NSWindow class], @selector(resetCursorRects)); |
|
561 ASSERT(resetCursorRectsMethod); |
|
562 oldResetCursorRectsIMP = method_setImplementation(resetCursorRectsMethod, (IMP)resetCursorRects); |
|
563 ASSERT(oldResetCursorRectsIMP); |
|
564 } |
|
565 #endif |
|
566 |
|
567 } |
|
568 |
|
569 - (void)dealloc |
|
570 { |
|
571 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLViewPrivate class], self)) |
|
572 return; |
|
573 |
|
574 ASSERT(!autoscrollTimer); |
|
575 ASSERT(!autoscrollTriggerEvent); |
|
576 ASSERT(!updateMouseoverTimer); |
|
577 |
|
578 [mouseDownEvent release]; |
|
579 [keyDownEvent release]; |
|
580 [pluginController release]; |
|
581 [toolTip release]; |
|
582 [completionController release]; |
|
583 [dataSource release]; |
|
584 [highlighters release]; |
|
585 if (promisedDragTIFFDataSource) |
|
586 promisedDragTIFFDataSource->removeClient(promisedDataClient()); |
|
587 |
|
588 [super dealloc]; |
|
589 } |
|
590 |
|
591 - (void)finalize |
|
592 { |
|
593 ASSERT_MAIN_THREAD(); |
|
594 |
|
595 if (promisedDragTIFFDataSource) |
|
596 promisedDragTIFFDataSource->removeClient(promisedDataClient()); |
|
597 |
|
598 [super finalize]; |
|
599 } |
|
600 |
|
601 - (void)clear |
|
602 { |
|
603 [mouseDownEvent release]; |
|
604 [keyDownEvent release]; |
|
605 [pluginController release]; |
|
606 [toolTip release]; |
|
607 [completionController release]; |
|
608 [dataSource release]; |
|
609 [highlighters release]; |
|
610 if (promisedDragTIFFDataSource) |
|
611 promisedDragTIFFDataSource->removeClient(promisedDataClient()); |
|
612 |
|
613 mouseDownEvent = nil; |
|
614 keyDownEvent = nil; |
|
615 pluginController = nil; |
|
616 toolTip = nil; |
|
617 completionController = nil; |
|
618 dataSource = nil; |
|
619 highlighters = nil; |
|
620 promisedDragTIFFDataSource = 0; |
|
621 |
|
622 #if USE(ACCELERATED_COMPOSITING) |
|
623 layerHostingView = nil; |
|
624 #endif |
|
625 } |
|
626 |
|
627 @end |
|
628 |
|
629 @implementation WebHTMLView (WebHTMLViewFileInternal) |
|
630 |
|
631 - (DOMRange *)_documentRange |
|
632 { |
|
633 return [[[self _frame] DOMDocument] _documentRange]; |
|
634 } |
|
635 |
|
636 - (BOOL)_imageExistsAtPaths:(NSArray *)paths |
|
637 { |
|
638 NSEnumerator *enumerator = [paths objectEnumerator]; |
|
639 NSString *path; |
|
640 |
|
641 while ((path = [enumerator nextObject]) != nil) { |
|
642 NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]); |
|
643 if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType)) |
|
644 return YES; |
|
645 } |
|
646 |
|
647 return NO; |
|
648 } |
|
649 |
|
650 - (WebDataSource *)_dataSource |
|
651 { |
|
652 return _private->dataSource; |
|
653 } |
|
654 |
|
655 - (WebView *)_webView |
|
656 { |
|
657 return [_private->dataSource _webView]; |
|
658 } |
|
659 |
|
660 - (WebFrameView *)_frameView |
|
661 { |
|
662 return [[_private->dataSource webFrame] frameView]; |
|
663 } |
|
664 |
|
665 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths |
|
666 { |
|
667 DOMDocumentFragment *fragment; |
|
668 NSEnumerator *enumerator = [paths objectEnumerator]; |
|
669 NSMutableArray *domNodes = [[NSMutableArray alloc] init]; |
|
670 NSString *path; |
|
671 |
|
672 while ((path = [enumerator nextObject]) != nil) { |
|
673 // Non-image file types; _web_userVisibleString is appropriate here because this will |
|
674 // be pasted as visible text. |
|
675 NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString]; |
|
676 [domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]]; |
|
677 } |
|
678 |
|
679 fragment = [[self _frame] _documentFragmentWithNodesAsParagraphs:domNodes]; |
|
680 |
|
681 [domNodes release]; |
|
682 |
|
683 return [fragment firstChild] != nil ? fragment : nil; |
|
684 } |
|
685 |
|
686 + (NSArray *)_excludedElementsForAttributedStringConversion |
|
687 { |
|
688 static NSArray *elements = nil; |
|
689 if (elements == nil) { |
|
690 elements = [[NSArray alloc] initWithObjects: |
|
691 // Omit style since we want style to be inline so the fragment can be easily inserted. |
|
692 @"style", |
|
693 // Omit xml so the result is not XHTML. |
|
694 @"xml", |
|
695 // Omit tags that will get stripped when converted to a fragment anyway. |
|
696 @"doctype", @"html", @"head", @"body", |
|
697 // Omit deprecated tags. |
|
698 @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u", |
|
699 // Omit object so no file attachments are part of the fragment. |
|
700 @"object", nil]; |
|
701 CFRetain(elements); |
|
702 } |
|
703 return elements; |
|
704 } |
|
705 |
|
706 static NSURL* uniqueURLWithRelativePart(NSString *relativePart) |
|
707 { |
|
708 CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault); |
|
709 NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef); |
|
710 CFRelease(UUIDRef); |
|
711 NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]]; |
|
712 CFRelease(UUIDString); |
|
713 |
|
714 return URL; |
|
715 } |
|
716 |
|
717 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard |
|
718 inContext:(DOMRange *)context |
|
719 allowPlainText:(BOOL)allowPlainText |
|
720 { |
|
721 NSArray *types = [pasteboard types]; |
|
722 DOMDocumentFragment *fragment = nil; |
|
723 |
|
724 if ([types containsObject:WebArchivePboardType] && |
|
725 (fragment = [self _documentFragmentFromPasteboard:pasteboard |
|
726 forType:WebArchivePboardType |
|
727 inContext:context |
|
728 subresources:0])) |
|
729 return fragment; |
|
730 |
|
731 if ([types containsObject:NSFilenamesPboardType] && |
|
732 (fragment = [self _documentFragmentFromPasteboard:pasteboard |
|
733 forType:NSFilenamesPboardType |
|
734 inContext:context |
|
735 subresources:0])) |
|
736 return fragment; |
|
737 |
|
738 if ([types containsObject:NSHTMLPboardType] && |
|
739 (fragment = [self _documentFragmentFromPasteboard:pasteboard |
|
740 forType:NSHTMLPboardType |
|
741 inContext:context |
|
742 subresources:0])) |
|
743 return fragment; |
|
744 |
|
745 if ([types containsObject:NSRTFDPboardType] && |
|
746 (fragment = [self _documentFragmentFromPasteboard:pasteboard |
|
747 forType:NSRTFDPboardType |
|
748 inContext:context |
|
749 subresources:0])) |
|
750 return fragment; |
|
751 |
|
752 if ([types containsObject:NSRTFPboardType] && |
|
753 (fragment = [self _documentFragmentFromPasteboard:pasteboard |
|
754 forType:NSRTFPboardType |
|
755 inContext:context |
|
756 subresources:0])) |
|
757 return fragment; |
|
758 |
|
759 if ([types containsObject:NSTIFFPboardType] && |
|
760 (fragment = [self _documentFragmentFromPasteboard:pasteboard |
|
761 forType:NSTIFFPboardType |
|
762 inContext:context |
|
763 subresources:0])) |
|
764 return fragment; |
|
765 |
|
766 if ([types containsObject:NSPDFPboardType] && |
|
767 (fragment = [self _documentFragmentFromPasteboard:pasteboard |
|
768 forType:NSPDFPboardType |
|
769 inContext:context |
|
770 subresources:0])) |
|
771 return fragment; |
|
772 |
|
773 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) |
|
774 if ([types containsObject:NSPICTPboardType] && |
|
775 (fragment = [self _documentFragmentFromPasteboard:pasteboard |
|
776 forType:NSPICTPboardType |
|
777 inContext:context |
|
778 subresources:0])) |
|
779 return fragment; |
|
780 #endif |
|
781 |
|
782 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe |
|
783 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard. |
|
784 if ([types containsObject:(NSString*)kUTTypePNG] && |
|
785 (fragment = [self _documentFragmentFromPasteboard:pasteboard |
|
786 forType:(NSString*)kUTTypePNG |
|
787 inContext:context |
|
788 subresources:0])) |
|
789 return fragment; |
|
790 |
|
791 if ([types containsObject:NSURLPboardType] && |
|
792 (fragment = [self _documentFragmentFromPasteboard:pasteboard |
|
793 forType:NSURLPboardType |
|
794 inContext:context |
|
795 subresources:0])) |
|
796 return fragment; |
|
797 |
|
798 if (allowPlainText && [types containsObject:NSStringPboardType] && |
|
799 (fragment = [self _documentFragmentFromPasteboard:pasteboard |
|
800 forType:NSStringPboardType |
|
801 inContext:context |
|
802 subresources:0])) { |
|
803 return fragment; |
|
804 } |
|
805 |
|
806 return nil; |
|
807 } |
|
808 |
|
809 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard |
|
810 { |
|
811 NSArray *types = [pasteboard types]; |
|
812 |
|
813 if ([types containsObject:NSStringPboardType]) |
|
814 return [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]; |
|
815 |
|
816 NSAttributedString *attributedString = nil; |
|
817 NSString *string; |
|
818 |
|
819 if ([types containsObject:NSRTFDPboardType]) |
|
820 attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL]; |
|
821 if (attributedString == nil && [types containsObject:NSRTFPboardType]) |
|
822 attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL]; |
|
823 if (attributedString != nil) { |
|
824 string = [[attributedString string] copy]; |
|
825 [attributedString release]; |
|
826 return [string autorelease]; |
|
827 } |
|
828 |
|
829 if ([types containsObject:NSFilenamesPboardType]) { |
|
830 string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"]; |
|
831 if (string != nil) |
|
832 return string; |
|
833 } |
|
834 |
|
835 NSURL *URL; |
|
836 |
|
837 if ((URL = [NSURL URLFromPasteboard:pasteboard])) { |
|
838 string = [URL _web_userVisibleString]; |
|
839 if ([string length] > 0) |
|
840 return string; |
|
841 } |
|
842 |
|
843 return nil; |
|
844 } |
|
845 |
|
846 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText |
|
847 { |
|
848 WebView *webView = [[self _webView] retain]; |
|
849 [webView _setInsertionPasteboard:pasteboard]; |
|
850 |
|
851 DOMRange *range = [self _selectedRange]; |
|
852 |
|
853 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) |
|
854 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText]; |
|
855 if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted]) |
|
856 [[self _frame] _replaceSelectionWithFragment:fragment selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard] matchStyle:NO]; |
|
857 #else |
|
858 // Mail is ignoring the frament passed to the delegate and creates a new one. |
|
859 // We want to avoid creating the fragment twice. |
|
860 if (applicationIsAppleMail()) { |
|
861 if ([self _shouldInsertFragment:nil replacingDOMRange:range givenAction:WebViewInsertActionPasted]) { |
|
862 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText]; |
|
863 if (fragment) |
|
864 [[self _frame] _replaceSelectionWithFragment:fragment selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard] matchStyle:NO]; |
|
865 } |
|
866 } else { |
|
867 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText]; |
|
868 if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted]) |
|
869 [[self _frame] _replaceSelectionWithFragment:fragment selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard] matchStyle:NO]; |
|
870 } |
|
871 #endif |
|
872 [webView _setInsertionPasteboard:nil]; |
|
873 [webView release]; |
|
874 } |
|
875 |
|
876 - (void)_removeMouseMovedObserverUnconditionally |
|
877 { |
|
878 if (!_private || !_private->observingMouseMovedNotifications) |
|
879 return; |
|
880 |
|
881 [[NSNotificationCenter defaultCenter] removeObserver:self name:WKMouseMovedNotification() object:nil]; |
|
882 _private->observingMouseMovedNotifications = false; |
|
883 } |
|
884 |
|
885 - (void)_removeSuperviewObservers |
|
886 { |
|
887 if (!_private || !_private->observingSuperviewNotifications) |
|
888 return; |
|
889 |
|
890 NSView *superview = [self superview]; |
|
891 if (!superview || ![self window]) |
|
892 return; |
|
893 |
|
894 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; |
|
895 [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification object:superview]; |
|
896 [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification object:superview]; |
|
897 |
|
898 _private->observingSuperviewNotifications = false; |
|
899 } |
|
900 |
|
901 - (void)_removeWindowObservers |
|
902 { |
|
903 if (!_private->observingWindowNotifications) |
|
904 return; |
|
905 |
|
906 NSWindow *window = [self window]; |
|
907 if (!window) |
|
908 return; |
|
909 |
|
910 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; |
|
911 [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil]; |
|
912 [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil]; |
|
913 [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:window]; |
|
914 |
|
915 _private->observingWindowNotifications = false; |
|
916 } |
|
917 |
|
918 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action |
|
919 { |
|
920 WebView *webView = [self _webView]; |
|
921 DOMNode *child = [fragment firstChild]; |
|
922 if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]]) |
|
923 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action]; |
|
924 return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action]; |
|
925 } |
|
926 |
|
927 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action |
|
928 { |
|
929 WebView *webView = [self _webView]; |
|
930 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action]; |
|
931 } |
|
932 |
|
933 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action |
|
934 { |
|
935 return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action]; |
|
936 } |
|
937 |
|
938 - (DOMRange *)_selectedRange |
|
939 { |
|
940 Frame* coreFrame = core([self _frame]); |
|
941 return coreFrame ? kit(coreFrame->selection()->toNormalizedRange().get()) : nil; |
|
942 } |
|
943 |
|
944 - (BOOL)_shouldDeleteRange:(DOMRange *)range |
|
945 { |
|
946 Frame* coreFrame = core([self _frame]); |
|
947 return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range)); |
|
948 } |
|
949 |
|
950 - (NSView *)_hitViewForEvent:(NSEvent *)event |
|
951 { |
|
952 // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView. |
|
953 // Callers of this method, however, want to query the deepest view instead. |
|
954 forceNSViewHitTest = YES; |
|
955 NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]]; |
|
956 forceNSViewHitTest = NO; |
|
957 return hitView; |
|
958 } |
|
959 |
|
960 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString |
|
961 { |
|
962 // Put HTML on the pasteboard. |
|
963 if ([types containsObject:WebArchivePboardType]) { |
|
964 if (RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::createFromSelection(core([self _frame]))) { |
|
965 if (RetainPtr<CFDataRef> data = coreArchive ? coreArchive->rawDataRepresentation() : 0) |
|
966 [pasteboard setData:(NSData *)data.get() forType:WebArchivePboardType]; |
|
967 } |
|
968 } |
|
969 |
|
970 // Put the attributed string on the pasteboard (RTF/RTFD format). |
|
971 if ([types containsObject:NSRTFDPboardType]) { |
|
972 if (attributedString == nil) { |
|
973 attributedString = [self selectedAttributedString]; |
|
974 } |
|
975 NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil]; |
|
976 [pasteboard setData:RTFDData forType:NSRTFDPboardType]; |
|
977 } |
|
978 if ([types containsObject:NSRTFPboardType]) { |
|
979 if (attributedString == nil) { |
|
980 attributedString = [self selectedAttributedString]; |
|
981 } |
|
982 if ([attributedString containsAttachments]) { |
|
983 attributedString = [attributedString _web_attributedStringByStrippingAttachmentCharacters]; |
|
984 } |
|
985 NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil]; |
|
986 [pasteboard setData:RTFData forType:NSRTFPboardType]; |
|
987 } |
|
988 |
|
989 // Put plain string on the pasteboard. |
|
990 if ([types containsObject:NSStringPboardType]) { |
|
991 // Map to a plain old space because this is better for source code, other browsers do it, |
|
992 // and because HTML forces you to do this any time you want two spaces in a row. |
|
993 NSMutableString *s = [[self selectedString] mutableCopy]; |
|
994 const unichar NonBreakingSpaceCharacter = 0xA0; |
|
995 NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1]; |
|
996 [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])]; |
|
997 [pasteboard setString:s forType:NSStringPboardType]; |
|
998 [s release]; |
|
999 } |
|
1000 |
|
1001 if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) { |
|
1002 [pasteboard setData:nil forType:WebSmartPastePboardType]; |
|
1003 } |
|
1004 } |
|
1005 |
|
1006 - (void)_setMouseDownEvent:(NSEvent *)event |
|
1007 { |
|
1008 ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown); |
|
1009 |
|
1010 if (event == _private->mouseDownEvent) |
|
1011 return; |
|
1012 |
|
1013 [event retain]; |
|
1014 [_private->mouseDownEvent release]; |
|
1015 _private->mouseDownEvent = event; |
|
1016 } |
|
1017 |
|
1018 - (void)_cancelUpdateMouseoverTimer |
|
1019 { |
|
1020 if (_private->updateMouseoverTimer) { |
|
1021 CFRunLoopTimerInvalidate(_private->updateMouseoverTimer); |
|
1022 CFRelease(_private->updateMouseoverTimer); |
|
1023 _private->updateMouseoverTimer = NULL; |
|
1024 } |
|
1025 } |
|
1026 |
|
1027 - (WebHTMLView *)_topHTMLView |
|
1028 { |
|
1029 // FIXME: this can fail if the dataSource is nil, which happens when the WebView is tearing down from the window closing. |
|
1030 WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView]; |
|
1031 ASSERT(!view || [view isKindOfClass:[WebHTMLView class]]); |
|
1032 return view; |
|
1033 } |
|
1034 |
|
1035 - (BOOL)_isTopHTMLView |
|
1036 { |
|
1037 // FIXME: this should be a cached boolean that doesn't rely on _topHTMLView since that can fail (see _topHTMLView). |
|
1038 return self == [self _topHTMLView]; |
|
1039 } |
|
1040 |
|
1041 - (void)_web_setPrintingModeRecursive |
|
1042 { |
|
1043 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO]; |
|
1044 |
|
1045 #ifndef NDEBUG |
|
1046 _private->enumeratingSubviews = YES; |
|
1047 #endif |
|
1048 |
|
1049 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init]; |
|
1050 |
|
1051 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews]; |
|
1052 |
|
1053 unsigned count = [descendantWebHTMLViews count]; |
|
1054 for (unsigned i = 0; i < count; ++i) |
|
1055 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO]; |
|
1056 |
|
1057 [descendantWebHTMLViews release]; |
|
1058 |
|
1059 #ifndef NDEBUG |
|
1060 _private->enumeratingSubviews = NO; |
|
1061 #endif |
|
1062 } |
|
1063 |
|
1064 - (void)_web_clearPrintingModeRecursive |
|
1065 { |
|
1066 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO]; |
|
1067 |
|
1068 #ifndef NDEBUG |
|
1069 _private->enumeratingSubviews = YES; |
|
1070 #endif |
|
1071 |
|
1072 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init]; |
|
1073 |
|
1074 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews]; |
|
1075 |
|
1076 unsigned count = [descendantWebHTMLViews count]; |
|
1077 for (unsigned i = 0; i < count; ++i) |
|
1078 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO]; |
|
1079 |
|
1080 [descendantWebHTMLViews release]; |
|
1081 |
|
1082 #ifndef NDEBUG |
|
1083 _private->enumeratingSubviews = NO; |
|
1084 #endif |
|
1085 } |
|
1086 |
|
1087 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize |
|
1088 { |
|
1089 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES]; |
|
1090 |
|
1091 #ifndef NDEBUG |
|
1092 _private->enumeratingSubviews = YES; |
|
1093 #endif |
|
1094 |
|
1095 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init]; |
|
1096 |
|
1097 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews]; |
|
1098 |
|
1099 unsigned count = [descendantWebHTMLViews count]; |
|
1100 for (unsigned i = 0; i < count; ++i) |
|
1101 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES]; |
|
1102 |
|
1103 [descendantWebHTMLViews release]; |
|
1104 |
|
1105 #ifndef NDEBUG |
|
1106 _private->enumeratingSubviews = NO; |
|
1107 #endif |
|
1108 } |
|
1109 |
|
1110 @end |
|
1111 |
|
1112 @implementation WebHTMLView (WebPrivate) |
|
1113 |
|
1114 + (NSArray *)supportedMIMETypes |
|
1115 { |
|
1116 return [WebHTMLRepresentation supportedMIMETypes]; |
|
1117 } |
|
1118 |
|
1119 + (NSArray *)supportedImageMIMETypes |
|
1120 { |
|
1121 return [WebHTMLRepresentation supportedImageMIMETypes]; |
|
1122 } |
|
1123 |
|
1124 + (NSArray *)supportedNonImageMIMETypes |
|
1125 { |
|
1126 return [WebHTMLRepresentation supportedNonImageMIMETypes]; |
|
1127 } |
|
1128 |
|
1129 + (NSArray *)unsupportedTextMIMETypes |
|
1130 { |
|
1131 return [NSArray arrayWithObjects: |
|
1132 @"text/calendar", // iCal |
|
1133 @"text/x-calendar", |
|
1134 @"text/x-vcalendar", |
|
1135 @"text/vcalendar", |
|
1136 @"text/vcard", // vCard |
|
1137 @"text/x-vcard", |
|
1138 @"text/directory", |
|
1139 @"text/ldif", // Netscape Address Book |
|
1140 @"text/qif", // Quicken |
|
1141 @"text/x-qif", |
|
1142 @"text/x-csv", // CSV (for Address Book and Microsoft Outlook) |
|
1143 @"text/x-vcf", // vCard type used in Sun affinity app |
|
1144 @"text/rtf", // Rich Text Format |
|
1145 nil]; |
|
1146 } |
|
1147 |
|
1148 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent |
|
1149 { |
|
1150 // This is a workaround for: <rdar://problem/2981619> NSResponder_Private should include notification for FlagsChanged |
|
1151 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved |
|
1152 location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]] |
|
1153 modifierFlags:[flagsChangedEvent modifierFlags] |
|
1154 timestamp:[flagsChangedEvent timestamp] |
|
1155 windowNumber:[flagsChangedEvent windowNumber] |
|
1156 context:[flagsChangedEvent context] |
|
1157 eventNumber:0 clickCount:0 pressure:0]; |
|
1158 |
|
1159 // Pretend it's a mouse move. |
|
1160 [[NSNotificationCenter defaultCenter] |
|
1161 postNotificationName:WKMouseMovedNotification() object:self |
|
1162 userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]]; |
|
1163 } |
|
1164 |
|
1165 - (id)_bridge |
|
1166 { |
|
1167 // This method exists to maintain compatibility with Leopard's Dictionary.app, since it |
|
1168 // calls _bridge to get access to convertNSRangeToDOMRange: and convertDOMRangeToNSRange:. |
|
1169 // Return the WebFrame, which implements the compatibility methods. <rdar://problem/6002160> |
|
1170 return [self _frame]; |
|
1171 } |
|
1172 |
|
1173 - (void)_updateMouseoverWithFakeEvent |
|
1174 { |
|
1175 [self _cancelUpdateMouseoverTimer]; |
|
1176 |
|
1177 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved |
|
1178 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]] |
|
1179 modifierFlags:[[NSApp currentEvent] modifierFlags] |
|
1180 timestamp:[NSDate timeIntervalSinceReferenceDate] |
|
1181 windowNumber:[[self window] windowNumber] |
|
1182 context:[[NSApp currentEvent] context] |
|
1183 eventNumber:0 clickCount:0 pressure:0]; |
|
1184 |
|
1185 [self _updateMouseoverWithEvent:fakeEvent]; |
|
1186 } |
|
1187 |
|
1188 static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info) |
|
1189 { |
|
1190 WebHTMLView *view = (WebHTMLView *)info; |
|
1191 |
|
1192 [view _updateMouseoverWithFakeEvent]; |
|
1193 } |
|
1194 |
|
1195 - (void)_frameOrBoundsChanged |
|
1196 { |
|
1197 NSPoint origin = [[self superview] bounds].origin; |
|
1198 if (!NSEqualPoints(_private->lastScrollPosition, origin)) { |
|
1199 if (Frame* coreFrame = core([self _frame])) { |
|
1200 if (FrameView* coreView = coreFrame->view()) { |
|
1201 #ifndef BUILDING_ON_TIGER |
|
1202 _private->inScrollPositionChanged = YES; |
|
1203 #endif |
|
1204 coreView->scrollPositionChangedViaPlatformWidget(); |
|
1205 #ifndef BUILDING_ON_TIGER |
|
1206 _private->inScrollPositionChanged = NO; |
|
1207 #endif |
|
1208 } |
|
1209 } |
|
1210 |
|
1211 [_private->completionController endRevertingChange:NO moveLeft:NO]; |
|
1212 |
|
1213 WebView *webView = [self _webView]; |
|
1214 [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]]; |
|
1215 } |
|
1216 _private->lastScrollPosition = origin; |
|
1217 |
|
1218 if ([self window] && !_private->closed && !_private->updateMouseoverTimer) { |
|
1219 CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL }; |
|
1220 |
|
1221 // Use a 100ms delay so that the synthetic mouse over update doesn't cause cursor thrashing when pages are loading |
|
1222 // and scrolling rapidly back to back. |
|
1223 _private->updateMouseoverTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 0.1, 0, 0, 0, |
|
1224 _updateMouseoverTimerCallback, &context); |
|
1225 CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateMouseoverTimer, kCFRunLoopDefaultMode); |
|
1226 } |
|
1227 |
|
1228 #if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD) |
|
1229 [self _updateLayerHostingViewPosition]; |
|
1230 #endif |
|
1231 } |
|
1232 |
|
1233 - (void)_setAsideSubviews |
|
1234 { |
|
1235 ASSERT(!_private->subviewsSetAside); |
|
1236 ASSERT(_private->savedSubviews == nil); |
|
1237 _private->savedSubviews = _subviews; |
|
1238 #if USE(ACCELERATED_COMPOSITING) |
|
1239 // We need to keep the layer-hosting view in the subviews, otherwise the layers flash. |
|
1240 if (_private->layerHostingView) { |
|
1241 NSArray* newSubviews = [[NSArray alloc] initWithObjects:_private->layerHostingView, nil]; |
|
1242 _subviews = newSubviews; |
|
1243 } else |
|
1244 _subviews = nil; |
|
1245 #else |
|
1246 _subviews = nil; |
|
1247 #endif |
|
1248 _private->subviewsSetAside = YES; |
|
1249 } |
|
1250 |
|
1251 - (void)_restoreSubviews |
|
1252 { |
|
1253 ASSERT(_private->subviewsSetAside); |
|
1254 #if USE(ACCELERATED_COMPOSITING) |
|
1255 if (_private->layerHostingView) { |
|
1256 [_subviews release]; |
|
1257 _subviews = _private->savedSubviews; |
|
1258 } else { |
|
1259 ASSERT(_subviews == nil); |
|
1260 _subviews = _private->savedSubviews; |
|
1261 } |
|
1262 #else |
|
1263 ASSERT(_subviews == nil); |
|
1264 _subviews = _private->savedSubviews; |
|
1265 #endif |
|
1266 _private->savedSubviews = nil; |
|
1267 _private->subviewsSetAside = NO; |
|
1268 } |
|
1269 |
|
1270 #ifndef NDEBUG |
|
1271 |
|
1272 - (void)didAddSubview:(NSView *)subview |
|
1273 { |
|
1274 if (_private->enumeratingSubviews) |
|
1275 LOG(View, "A view of class %s was added during subview enumeration for layout or printing mode change. This view might paint without first receiving layout.", object_getClassName([subview class])); |
|
1276 } |
|
1277 #endif |
|
1278 |
|
1279 #ifdef BUILDING_ON_TIGER |
|
1280 |
|
1281 // This is called when we are about to draw, but before our dirty rect is propagated to our ancestors. |
|
1282 // That's the perfect time to do a layout, except that ideally we'd want to be sure that we're dirty |
|
1283 // before doing it. As a compromise, when we're opaque we do the layout only when actually asked to |
|
1284 // draw, but when we're transparent we do the layout at this stage so views behind us know that they |
|
1285 // need to be redrawn (in case the layout causes some things to get dirtied). |
|
1286 - (void)_propagateDirtyRectsToOpaqueAncestors |
|
1287 { |
|
1288 if (![[self _webView] drawsBackground]) |
|
1289 [self _web_layoutIfNeededRecursive]; |
|
1290 [super _propagateDirtyRectsToOpaqueAncestors]; |
|
1291 } |
|
1292 |
|
1293 #else |
|
1294 |
|
1295 - (void)viewWillDraw |
|
1296 { |
|
1297 // On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView |
|
1298 // So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed |
|
1299 // once the FIXME in _isTopHTMLView is fixed. |
|
1300 if (_private->dataSource && [self _isTopHTMLView]) |
|
1301 [self _web_layoutIfNeededRecursive]; |
|
1302 [super viewWillDraw]; |
|
1303 } |
|
1304 |
|
1305 #endif |
|
1306 |
|
1307 // Don't let AppKit even draw subviews. We take care of that. |
|
1308 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView |
|
1309 { |
|
1310 // This helps when we print as part of a larger print process. |
|
1311 // If the WebHTMLView itself is what we're printing, then we will never have to do this. |
|
1312 BOOL wasInPrintingMode = _private->printing; |
|
1313 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen]; |
|
1314 if (isPrinting) { |
|
1315 if (!wasInPrintingMode) |
|
1316 [self _web_setPrintingModeRecursive]; |
|
1317 #ifndef BUILDING_ON_TIGER |
|
1318 else |
|
1319 [self _web_layoutIfNeededRecursive]; |
|
1320 #endif |
|
1321 } else if (wasInPrintingMode) |
|
1322 [self _web_clearPrintingModeRecursive]; |
|
1323 |
|
1324 #ifndef BUILDING_ON_TIGER |
|
1325 // There are known cases where -viewWillDraw is not called on all views being drawn. |
|
1326 // See <rdar://problem/6964278> for example. Performing layout at this point prevents us from |
|
1327 // trying to paint without layout (which WebCore now refuses to do, instead bailing out without |
|
1328 // drawing at all), but we may still fail to update any regions dirtied by the layout which are |
|
1329 // not already dirty. |
|
1330 if ([self _needsLayout]) { |
|
1331 NSInteger rectCount; |
|
1332 [self getRectsBeingDrawn:0 count:&rectCount]; |
|
1333 if (rectCount) { |
|
1334 LOG_ERROR("View needs layout. Either -viewWillDraw wasn't called or layout was invalidated during the display operation. Performing layout now."); |
|
1335 [self _web_layoutIfNeededRecursive]; |
|
1336 } |
|
1337 } |
|
1338 #else |
|
1339 // Because Tiger does not have viewWillDraw we need to do layout here. |
|
1340 [self _web_layoutIfNeededRecursive]; |
|
1341 [_subviews makeObjectsPerformSelector:@selector(_propagateDirtyRectsToOpaqueAncestors)]; |
|
1342 #endif |
|
1343 |
|
1344 [self _setAsideSubviews]; |
|
1345 [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView]; |
|
1346 [self _restoreSubviews]; |
|
1347 |
|
1348 if (wasInPrintingMode != isPrinting) { |
|
1349 if (wasInPrintingMode) |
|
1350 [self _web_setPrintingModeRecursive]; |
|
1351 else |
|
1352 [self _web_clearPrintingModeRecursive]; |
|
1353 } |
|
1354 } |
|
1355 |
|
1356 // Don't let AppKit even draw subviews. We take care of that. |
|
1357 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect |
|
1358 { |
|
1359 BOOL needToSetAsideSubviews = !_private->subviewsSetAside; |
|
1360 |
|
1361 BOOL wasInPrintingMode = _private->printing; |
|
1362 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen]; |
|
1363 |
|
1364 if (needToSetAsideSubviews) { |
|
1365 // This helps when we print as part of a larger print process. |
|
1366 // If the WebHTMLView itself is what we're printing, then we will never have to do this. |
|
1367 if (isPrinting) { |
|
1368 if (!wasInPrintingMode) |
|
1369 [self _web_setPrintingModeRecursive]; |
|
1370 #ifndef BUILDING_ON_TIGER |
|
1371 else |
|
1372 [self _web_layoutIfNeededRecursive]; |
|
1373 #endif |
|
1374 } else if (wasInPrintingMode) |
|
1375 [self _web_clearPrintingModeRecursive]; |
|
1376 |
|
1377 #ifdef BUILDING_ON_TIGER |
|
1378 |
|
1379 // Because Tiger does not have viewWillDraw we need to do layout here. |
|
1380 NSRect boundsBeforeLayout = [self bounds]; |
|
1381 if (!NSIsEmptyRect(visRect)) |
|
1382 [self _web_layoutIfNeededRecursive]; |
|
1383 |
|
1384 // If layout changes the view's bounds, then we need to recompute the visRect. |
|
1385 // That's because the visRect passed to us was based on the bounds at the time |
|
1386 // we were called. This method is only displayed to draw "all", so it's safe |
|
1387 // to just call visibleRect to compute the entire rectangle. |
|
1388 if (!NSEqualRects(boundsBeforeLayout, [self bounds])) |
|
1389 visRect = [self visibleRect]; |
|
1390 |
|
1391 #endif |
|
1392 |
|
1393 [self _setAsideSubviews]; |
|
1394 } |
|
1395 |
|
1396 [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect]; |
|
1397 |
|
1398 if (needToSetAsideSubviews) { |
|
1399 if (wasInPrintingMode != isPrinting) { |
|
1400 if (wasInPrintingMode) |
|
1401 [self _web_setPrintingModeRecursive]; |
|
1402 else |
|
1403 [self _web_clearPrintingModeRecursive]; |
|
1404 } |
|
1405 |
|
1406 [self _restoreSubviews]; |
|
1407 } |
|
1408 } |
|
1409 |
|
1410 // Don't let AppKit even draw subviews. We take care of that. |
|
1411 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView |
|
1412 { |
|
1413 #ifdef BUILDING_ON_TIGER |
|
1414 // Because Tiger does not have viewWillDraw we need to do layout here. |
|
1415 [self _web_layoutIfNeededRecursive]; |
|
1416 #endif |
|
1417 |
|
1418 [self _setAsideSubviews]; |
|
1419 [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView]; |
|
1420 [self _restoreSubviews]; |
|
1421 } |
|
1422 |
|
1423 - (BOOL)_insideAnotherHTMLView |
|
1424 { |
|
1425 return self != [self _topHTMLView]; |
|
1426 } |
|
1427 |
|
1428 - (NSView *)hitTest:(NSPoint)point |
|
1429 { |
|
1430 // WebHTMLView objects handle all events for objects inside them. |
|
1431 // To get those events, we prevent hit testing from AppKit. |
|
1432 |
|
1433 // But there are three exceptions to this: |
|
1434 // 1) For right mouse clicks and control clicks we don't yet have an implementation |
|
1435 // that works for nested views, so we let the hit testing go through the |
|
1436 // standard NSView code path (needs to be fixed, see bug 4361618). |
|
1437 // 2) Java depends on doing a hit test inside it's mouse moved handling, |
|
1438 // so we let the hit testing go through the standard NSView code path |
|
1439 // when the current event is a mouse move (except when we are calling |
|
1440 // from _updateMouseoverWithEvent, so we have to use a global, |
|
1441 // forceWebHTMLViewHitTest, for that) |
|
1442 // 3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods |
|
1443 // both need to figure out which view to check with inside the WebHTMLView. |
|
1444 // They use a global to change the behavior of hitTest: so they can get the |
|
1445 // right view. The global is forceNSViewHitTest and the method they use to |
|
1446 // do the hit testing is _hitViewForEvent:. (But this does not work correctly |
|
1447 // when there is HTML overlapping the view, see bug 4361626) |
|
1448 // 4) NSAccessibilityHitTest relies on this for checking the cursor position. |
|
1449 // Our check for that is whether the event is NSFlagsChanged. This works |
|
1450 // for VoiceOver's Control-Option-F5 command (move focus to item under cursor) |
|
1451 // and Dictionary's Command-Control-D (open dictionary popup for item under cursor). |
|
1452 // This is of course a hack. |
|
1453 |
|
1454 if (_private->closed) |
|
1455 return nil; |
|
1456 |
|
1457 BOOL captureHitsOnSubviews; |
|
1458 if (forceNSViewHitTest) |
|
1459 captureHitsOnSubviews = NO; |
|
1460 else if (forceWebHTMLViewHitTest) |
|
1461 captureHitsOnSubviews = YES; |
|
1462 else { |
|
1463 // FIXME: Why doesn't this include mouse entered/exited events, or other mouse button events? |
|
1464 NSEvent *event = [[self window] currentEvent]; |
|
1465 captureHitsOnSubviews = !([event type] == NSMouseMoved |
|
1466 || [event type] == NSRightMouseDown |
|
1467 || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0) |
|
1468 || [event type] == NSFlagsChanged); |
|
1469 } |
|
1470 |
|
1471 if (!captureHitsOnSubviews) { |
|
1472 NSView* hitView = [super hitTest:point]; |
|
1473 #if USE(ACCELERATED_COMPOSITING) |
|
1474 if (_private && hitView == _private->layerHostingView) |
|
1475 hitView = self; |
|
1476 #endif |
|
1477 return hitView; |
|
1478 } |
|
1479 if ([[self superview] mouse:point inRect:[self frame]]) |
|
1480 return self; |
|
1481 return nil; |
|
1482 } |
|
1483 |
|
1484 - (void)_clearLastHitViewIfSelf |
|
1485 { |
|
1486 if (lastHitView == self) |
|
1487 lastHitView = nil; |
|
1488 } |
|
1489 |
|
1490 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside |
|
1491 { |
|
1492 ASSERT(_private->trackingRectOwner == nil); |
|
1493 _private->trackingRectOwner = owner; |
|
1494 _private->trackingRectUserData = data; |
|
1495 return TRACKING_RECT_TAG; |
|
1496 } |
|
1497 |
|
1498 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag |
|
1499 { |
|
1500 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG); |
|
1501 ASSERT(_private->trackingRectOwner == nil); |
|
1502 _private->trackingRectOwner = owner; |
|
1503 _private->trackingRectUserData = data; |
|
1504 return TRACKING_RECT_TAG; |
|
1505 } |
|
1506 |
|
1507 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count |
|
1508 { |
|
1509 ASSERT(count == 1); |
|
1510 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG); |
|
1511 ASSERT(_private->trackingRectOwner == nil); |
|
1512 _private->trackingRectOwner = owner; |
|
1513 _private->trackingRectUserData = userDataList[0]; |
|
1514 trackingNums[0] = TRACKING_RECT_TAG; |
|
1515 } |
|
1516 |
|
1517 - (void)removeTrackingRect:(NSTrackingRectTag)tag |
|
1518 { |
|
1519 if (tag == 0) |
|
1520 return; |
|
1521 |
|
1522 if (_private && (tag == TRACKING_RECT_TAG)) { |
|
1523 _private->trackingRectOwner = nil; |
|
1524 return; |
|
1525 } |
|
1526 |
|
1527 if (_private && (tag == _private->lastToolTipTag)) { |
|
1528 [super removeTrackingRect:tag]; |
|
1529 _private->lastToolTipTag = 0; |
|
1530 return; |
|
1531 } |
|
1532 |
|
1533 // If any other tracking rect is being removed, we don't know how it was created |
|
1534 // and it's possible there's a leak involved (see 3500217) |
|
1535 ASSERT_NOT_REACHED(); |
|
1536 } |
|
1537 |
|
1538 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count |
|
1539 { |
|
1540 int i; |
|
1541 for (i = 0; i < count; ++i) { |
|
1542 int tag = tags[i]; |
|
1543 if (tag == 0) |
|
1544 continue; |
|
1545 ASSERT(tag == TRACKING_RECT_TAG); |
|
1546 if (_private != nil) { |
|
1547 _private->trackingRectOwner = nil; |
|
1548 } |
|
1549 } |
|
1550 } |
|
1551 |
|
1552 - (void)_sendToolTipMouseExited |
|
1553 { |
|
1554 // Nothing matters except window, trackingNumber, and userData. |
|
1555 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited |
|
1556 location:NSMakePoint(0, 0) |
|
1557 modifierFlags:0 |
|
1558 timestamp:0 |
|
1559 windowNumber:[[self window] windowNumber] |
|
1560 context:NULL |
|
1561 eventNumber:0 |
|
1562 trackingNumber:TRACKING_RECT_TAG |
|
1563 userData:_private->trackingRectUserData]; |
|
1564 [_private->trackingRectOwner mouseExited:fakeEvent]; |
|
1565 } |
|
1566 |
|
1567 - (void)_sendToolTipMouseEntered |
|
1568 { |
|
1569 // Nothing matters except window, trackingNumber, and userData. |
|
1570 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered |
|
1571 location:NSMakePoint(0, 0) |
|
1572 modifierFlags:0 |
|
1573 timestamp:0 |
|
1574 windowNumber:[[self window] windowNumber] |
|
1575 context:NULL |
|
1576 eventNumber:0 |
|
1577 trackingNumber:TRACKING_RECT_TAG |
|
1578 userData:_private->trackingRectUserData]; |
|
1579 [_private->trackingRectOwner mouseEntered:fakeEvent]; |
|
1580 } |
|
1581 |
|
1582 - (void)_setToolTip:(NSString *)string |
|
1583 { |
|
1584 NSString *toolTip = [string length] == 0 ? nil : string; |
|
1585 NSString *oldToolTip = _private->toolTip; |
|
1586 if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) { |
|
1587 return; |
|
1588 } |
|
1589 if (oldToolTip) { |
|
1590 [self _sendToolTipMouseExited]; |
|
1591 [oldToolTip release]; |
|
1592 } |
|
1593 _private->toolTip = [toolTip copy]; |
|
1594 if (toolTip) { |
|
1595 // See radar 3500217 for why we remove all tooltips rather than just the single one we created. |
|
1596 [self removeAllToolTips]; |
|
1597 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000); |
|
1598 _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL]; |
|
1599 [self _sendToolTipMouseEntered]; |
|
1600 } |
|
1601 } |
|
1602 |
|
1603 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data |
|
1604 { |
|
1605 return [[_private->toolTip copy] autorelease]; |
|
1606 } |
|
1607 |
|
1608 - (void)_updateMouseoverWithEvent:(NSEvent *)event |
|
1609 { |
|
1610 if (_private->closed) |
|
1611 return; |
|
1612 |
|
1613 NSView *contentView = [[event window] contentView]; |
|
1614 NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil]; |
|
1615 |
|
1616 forceWebHTMLViewHitTest = YES; |
|
1617 NSView *hitView = [contentView hitTest:locationForHitTest]; |
|
1618 forceWebHTMLViewHitTest = NO; |
|
1619 |
|
1620 WebHTMLView *view = nil; |
|
1621 if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended]) |
|
1622 view = (WebHTMLView *)hitView; |
|
1623 |
|
1624 if (view) |
|
1625 [view retain]; |
|
1626 |
|
1627 if (lastHitView != view && lastHitView && [lastHitView _frame]) { |
|
1628 // If we are moving out of a view (or frame), let's pretend the mouse moved |
|
1629 // all the way out of that view. But we have to account for scrolling, because |
|
1630 // WebCore doesn't understand our clipping. |
|
1631 NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect]; |
|
1632 float yScroll = visibleRect.origin.y; |
|
1633 float xScroll = visibleRect.origin.x; |
|
1634 |
|
1635 NSEvent *event = [NSEvent mouseEventWithType:NSMouseMoved |
|
1636 location:NSMakePoint(-1 - xScroll, -1 - yScroll) |
|
1637 modifierFlags:[[NSApp currentEvent] modifierFlags] |
|
1638 timestamp:[NSDate timeIntervalSinceReferenceDate] |
|
1639 windowNumber:[[view window] windowNumber] |
|
1640 context:[[NSApp currentEvent] context] |
|
1641 eventNumber:0 clickCount:0 pressure:0]; |
|
1642 if (Frame* lastHitCoreFrame = core([lastHitView _frame])) |
|
1643 lastHitCoreFrame->eventHandler()->mouseMoved(event); |
|
1644 } |
|
1645 |
|
1646 lastHitView = view; |
|
1647 |
|
1648 if (view) { |
|
1649 if (Frame* coreFrame = core([view _frame])) |
|
1650 coreFrame->eventHandler()->mouseMoved(event); |
|
1651 |
|
1652 [view release]; |
|
1653 } |
|
1654 } |
|
1655 |
|
1656 // keep in sync with WebPasteboardHelper::insertablePasteboardTypes |
|
1657 + (NSArray *)_insertablePasteboardTypes |
|
1658 { |
|
1659 static NSArray *types = nil; |
|
1660 if (!types) { |
|
1661 types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPDFPboardType, |
|
1662 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) |
|
1663 NSPICTPboardType, |
|
1664 #endif |
|
1665 NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil]; |
|
1666 CFRetain(types); |
|
1667 } |
|
1668 return types; |
|
1669 } |
|
1670 |
|
1671 + (NSArray *)_selectionPasteboardTypes |
|
1672 { |
|
1673 // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423). |
|
1674 return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil]; |
|
1675 } |
|
1676 |
|
1677 - (NSImage *)_dragImageForURL:(NSString*)urlString withLabel:(NSString*)label |
|
1678 { |
|
1679 BOOL drawURLString = YES; |
|
1680 BOOL clipURLString = NO, clipLabelString = NO; |
|
1681 |
|
1682 if (!label) { |
|
1683 drawURLString = NO; |
|
1684 label = urlString; |
|
1685 } |
|
1686 |
|
1687 NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DRAG_LINK_LABEL_FONT_SIZE] |
|
1688 toHaveTrait:NSBoldFontMask]; |
|
1689 NSFont *urlFont = [NSFont systemFontOfSize: DRAG_LINK_URL_FONT_SIZE]; |
|
1690 NSSize labelSize; |
|
1691 labelSize.width = [label _web_widthWithFont: labelFont]; |
|
1692 labelSize.height = [labelFont ascender] - [labelFont descender]; |
|
1693 if (labelSize.width > MAX_DRAG_LABEL_WIDTH){ |
|
1694 labelSize.width = MAX_DRAG_LABEL_WIDTH; |
|
1695 clipLabelString = YES; |
|
1696 } |
|
1697 |
|
1698 NSSize imageSize, urlStringSize; |
|
1699 imageSize.width = labelSize.width + DRAG_LABEL_BORDER_X * 2.0f; |
|
1700 imageSize.height = labelSize.height + DRAG_LABEL_BORDER_Y * 2.0f; |
|
1701 if (drawURLString) { |
|
1702 urlStringSize.width = [urlString _web_widthWithFont: urlFont]; |
|
1703 urlStringSize.height = [urlFont ascender] - [urlFont descender]; |
|
1704 imageSize.height += urlStringSize.height; |
|
1705 if (urlStringSize.width > MAX_DRAG_LABEL_WIDTH) { |
|
1706 imageSize.width = max(MAX_DRAG_LABEL_WIDTH + DRAG_LABEL_BORDER_X * 2, MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP); |
|
1707 clipURLString = YES; |
|
1708 } else { |
|
1709 imageSize.width = max(labelSize.width + DRAG_LABEL_BORDER_X * 2, urlStringSize.width + DRAG_LABEL_BORDER_X * 2); |
|
1710 } |
|
1711 } |
|
1712 NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease]; |
|
1713 [dragImage lockFocus]; |
|
1714 |
|
1715 [[NSColor colorWithDeviceRed: 0.7f green: 0.7f blue: 0.7f alpha: 0.8f] set]; |
|
1716 |
|
1717 // Drag a rectangle with rounded corners/ |
|
1718 NSBezierPath *path = [NSBezierPath bezierPath]; |
|
1719 [path appendBezierPathWithOvalInRect: NSMakeRect(0.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)]; |
|
1720 [path appendBezierPathWithOvalInRect: NSMakeRect(0, imageSize.height - DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)]; |
|
1721 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2.0f, imageSize.height - DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)]; |
|
1722 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)]; |
|
1723 |
|
1724 [path appendBezierPathWithRect: NSMakeRect(DRAG_LABEL_RADIUS, 0.0f, imageSize.width - DRAG_LABEL_RADIUS * 2.0f, imageSize.height)]; |
|
1725 [path appendBezierPathWithRect: NSMakeRect(0.0f, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 10.0f, imageSize.height - 2.0f * DRAG_LABEL_RADIUS)]; |
|
1726 [path appendBezierPathWithRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS - 20.0f, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 20.0f, imageSize.height - 2.0f * DRAG_LABEL_RADIUS)]; |
|
1727 [path fill]; |
|
1728 |
|
1729 NSColor *topColor = [NSColor colorWithDeviceWhite:0.0f alpha:0.75f]; |
|
1730 NSColor *bottomColor = [NSColor colorWithDeviceWhite:1.0f alpha:0.5f]; |
|
1731 if (drawURLString) { |
|
1732 if (clipURLString) |
|
1733 urlString = [WebStringTruncator centerTruncateString: urlString toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:urlFont]; |
|
1734 |
|
1735 [urlString _web_drawDoubledAtPoint:NSMakePoint(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y - [urlFont descender]) |
|
1736 withTopColor:topColor bottomColor:bottomColor font:urlFont]; |
|
1737 } |
|
1738 |
|
1739 if (clipLabelString) |
|
1740 label = [WebStringTruncator rightTruncateString: label toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:labelFont]; |
|
1741 [label _web_drawDoubledAtPoint:NSMakePoint (DRAG_LABEL_BORDER_X, imageSize.height - DRAG_LABEL_BORDER_Y_OFFSET - [labelFont pointSize]) |
|
1742 withTopColor:topColor bottomColor:bottomColor font:labelFont]; |
|
1743 |
|
1744 [dragImage unlockFocus]; |
|
1745 |
|
1746 return dragImage; |
|
1747 } |
|
1748 |
|
1749 - (NSImage *)_dragImageForLinkElement:(NSDictionary *)element |
|
1750 { |
|
1751 NSURL *linkURL = [element objectForKey: WebElementLinkURLKey]; |
|
1752 |
|
1753 NSString *label = [element objectForKey: WebElementLinkLabelKey]; |
|
1754 NSString *urlString = [linkURL _web_userVisibleString]; |
|
1755 return [self _dragImageForURL:urlString withLabel:label]; |
|
1756 } |
|
1757 |
|
1758 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard |
|
1759 { |
|
1760 [self setPromisedDragTIFFDataSource:0]; |
|
1761 } |
|
1762 |
|
1763 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type |
|
1764 { |
|
1765 if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) { |
|
1766 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]]; |
|
1767 [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]]; |
|
1768 [archive release]; |
|
1769 } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) { |
|
1770 if (Image* image = [self promisedDragTIFFDataSource]->image()) |
|
1771 [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType]; |
|
1772 [self setPromisedDragTIFFDataSource:0]; |
|
1773 } |
|
1774 } |
|
1775 |
|
1776 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event |
|
1777 { |
|
1778 [self autoscroll:event]; |
|
1779 [self _startAutoscrollTimer:event]; |
|
1780 } |
|
1781 |
|
1782 - (WebPluginController *)_pluginController |
|
1783 { |
|
1784 return _private->pluginController; |
|
1785 } |
|
1786 |
|
1787 - (void)_layoutForPrinting |
|
1788 { |
|
1789 // Set printing mode temporarily so we can adjust the size of the view. This will allow |
|
1790 // AppKit's pagination code to use the correct height for the page content. Leaving printing |
|
1791 // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just |
|
1792 // turn it off again after adjusting the size. |
|
1793 [self _web_setPrintingModeRecursiveAndAdjustViewSize]; |
|
1794 [self _web_clearPrintingModeRecursive]; |
|
1795 } |
|
1796 |
|
1797 - (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString |
|
1798 { |
|
1799 if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) { |
|
1800 if (beforeString) |
|
1801 *beforeString = nil; |
|
1802 if (afterString) |
|
1803 *afterString = nil; |
|
1804 return; |
|
1805 } |
|
1806 |
|
1807 [[self _frame] _smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString]; |
|
1808 } |
|
1809 |
|
1810 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard |
|
1811 { |
|
1812 return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType]; |
|
1813 } |
|
1814 |
|
1815 - (void)_startAutoscrollTimer:(NSEvent *)triggerEvent |
|
1816 { |
|
1817 if (_private->autoscrollTimer == nil) { |
|
1818 _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL |
|
1819 target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain]; |
|
1820 _private->autoscrollTriggerEvent = [triggerEvent retain]; |
|
1821 } |
|
1822 } |
|
1823 |
|
1824 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection. |
|
1825 // We can't remove this yet because it's still in use by Mail. |
|
1826 - (NSRect)_selectionRect |
|
1827 { |
|
1828 return [self selectionRect]; |
|
1829 } |
|
1830 |
|
1831 - (void)_stopAutoscrollTimer |
|
1832 { |
|
1833 NSTimer *timer = _private->autoscrollTimer; |
|
1834 _private->autoscrollTimer = nil; |
|
1835 [_private->autoscrollTriggerEvent release]; |
|
1836 _private->autoscrollTriggerEvent = nil; |
|
1837 [timer invalidate]; |
|
1838 [timer release]; |
|
1839 } |
|
1840 |
|
1841 - (void)_autoscroll |
|
1842 { |
|
1843 // Guarantee that the autoscroll timer is invalidated, even if we don't receive |
|
1844 // a mouse up event. |
|
1845 BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft); |
|
1846 if (!isStillDown){ |
|
1847 [self _stopAutoscrollTimer]; |
|
1848 return; |
|
1849 } |
|
1850 |
|
1851 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged |
|
1852 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]] |
|
1853 modifierFlags:[[NSApp currentEvent] modifierFlags] |
|
1854 timestamp:[NSDate timeIntervalSinceReferenceDate] |
|
1855 windowNumber:[[self window] windowNumber] |
|
1856 context:[[NSApp currentEvent] context] |
|
1857 eventNumber:0 clickCount:0 pressure:0]; |
|
1858 [self mouseDragged:fakeEvent]; |
|
1859 } |
|
1860 |
|
1861 - (BOOL)_canEdit |
|
1862 { |
|
1863 Frame* coreFrame = core([self _frame]); |
|
1864 return coreFrame && coreFrame->editor()->canEdit(); |
|
1865 } |
|
1866 |
|
1867 - (BOOL)_canEditRichly |
|
1868 { |
|
1869 Frame* coreFrame = core([self _frame]); |
|
1870 return coreFrame && coreFrame->editor()->canEditRichly(); |
|
1871 } |
|
1872 |
|
1873 - (BOOL)_canAlterCurrentSelection |
|
1874 { |
|
1875 return [self _hasSelectionOrInsertionPoint] && [self _isEditable]; |
|
1876 } |
|
1877 |
|
1878 - (BOOL)_hasSelection |
|
1879 { |
|
1880 Frame* coreFrame = core([self _frame]); |
|
1881 return coreFrame && coreFrame->selection()->isRange(); |
|
1882 } |
|
1883 |
|
1884 - (BOOL)_hasSelectionOrInsertionPoint |
|
1885 { |
|
1886 Frame* coreFrame = core([self _frame]); |
|
1887 return coreFrame && coreFrame->selection()->isCaretOrRange(); |
|
1888 } |
|
1889 |
|
1890 - (BOOL)_hasInsertionPoint |
|
1891 { |
|
1892 Frame* coreFrame = core([self _frame]); |
|
1893 return coreFrame && coreFrame->selection()->isCaret(); |
|
1894 } |
|
1895 |
|
1896 - (BOOL)_isEditable |
|
1897 { |
|
1898 Frame* coreFrame = core([self _frame]); |
|
1899 return coreFrame && coreFrame->selection()->isContentEditable(); |
|
1900 } |
|
1901 |
|
1902 - (BOOL)_transparentBackground |
|
1903 { |
|
1904 return _private->transparentBackground; |
|
1905 } |
|
1906 |
|
1907 - (void)_setTransparentBackground:(BOOL)f |
|
1908 { |
|
1909 _private->transparentBackground = f; |
|
1910 } |
|
1911 |
|
1912 - (NSImage *)_selectionDraggingImage |
|
1913 { |
|
1914 if ([self _hasSelection]) { |
|
1915 NSImage *dragImage = core([self _frame])->selectionImage(); |
|
1916 [dragImage _web_dissolveToFraction:WebDragImageAlpha]; |
|
1917 return dragImage; |
|
1918 } |
|
1919 return nil; |
|
1920 } |
|
1921 |
|
1922 - (NSRect)_selectionDraggingRect |
|
1923 { |
|
1924 // Mail currently calls this method. We can eliminate it when Mail no longer calls it. |
|
1925 return [self selectionRect]; |
|
1926 } |
|
1927 |
|
1928 - (DOMNode *)_insertOrderedList |
|
1929 { |
|
1930 Frame* coreFrame = core([self _frame]); |
|
1931 return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil; |
|
1932 } |
|
1933 |
|
1934 - (DOMNode *)_insertUnorderedList |
|
1935 { |
|
1936 Frame* coreFrame = core([self _frame]); |
|
1937 return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil; |
|
1938 } |
|
1939 |
|
1940 - (BOOL)_canIncreaseSelectionListLevel |
|
1941 { |
|
1942 Frame* coreFrame = core([self _frame]); |
|
1943 return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel(); |
|
1944 } |
|
1945 |
|
1946 - (BOOL)_canDecreaseSelectionListLevel |
|
1947 { |
|
1948 Frame* coreFrame = core([self _frame]); |
|
1949 return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel(); |
|
1950 } |
|
1951 |
|
1952 - (DOMNode *)_increaseSelectionListLevel |
|
1953 { |
|
1954 Frame* coreFrame = core([self _frame]); |
|
1955 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil; |
|
1956 } |
|
1957 |
|
1958 - (DOMNode *)_increaseSelectionListLevelOrdered |
|
1959 { |
|
1960 Frame* coreFrame = core([self _frame]); |
|
1961 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil; |
|
1962 } |
|
1963 |
|
1964 - (DOMNode *)_increaseSelectionListLevelUnordered |
|
1965 { |
|
1966 Frame* coreFrame = core([self _frame]); |
|
1967 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil; |
|
1968 } |
|
1969 |
|
1970 - (void)_decreaseSelectionListLevel |
|
1971 { |
|
1972 Frame* coreFrame = core([self _frame]); |
|
1973 if (coreFrame) |
|
1974 coreFrame->editor()->decreaseSelectionListLevel(); |
|
1975 } |
|
1976 |
|
1977 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type |
|
1978 { |
|
1979 if (!_private->highlighters) |
|
1980 _private->highlighters = [[NSMutableDictionary alloc] init]; |
|
1981 [_private->highlighters setObject:highlighter forKey:type]; |
|
1982 } |
|
1983 |
|
1984 - (void)_removeHighlighterOfType:(NSString*)type |
|
1985 { |
|
1986 [_private->highlighters removeObjectForKey:type]; |
|
1987 } |
|
1988 |
|
1989 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard |
|
1990 { |
|
1991 ASSERT([self _hasSelection]); |
|
1992 NSArray *types = [self pasteboardTypesForSelection]; |
|
1993 |
|
1994 // Don't write RTFD to the pasteboard when the copied attributed string has no attachments. |
|
1995 NSAttributedString *attributedString = [self selectedAttributedString]; |
|
1996 NSMutableArray *mutableTypes = nil; |
|
1997 if (![attributedString containsAttachments]) { |
|
1998 mutableTypes = [types mutableCopy]; |
|
1999 [mutableTypes removeObject:NSRTFDPboardType]; |
|
2000 types = mutableTypes; |
|
2001 } |
|
2002 |
|
2003 [pasteboard declareTypes:types owner:[self _topHTMLView]]; |
|
2004 [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString]; |
|
2005 [mutableTypes release]; |
|
2006 } |
|
2007 |
|
2008 - (void)close |
|
2009 { |
|
2010 // Check for a nil _private here in case we were created with initWithCoder. In that case, the WebView is just throwing |
|
2011 // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case. |
|
2012 if (!_private || _private->closed) |
|
2013 return; |
|
2014 |
|
2015 _private->closed = YES; |
|
2016 |
|
2017 [self _cancelUpdateMouseoverTimer]; |
|
2018 [self _clearLastHitViewIfSelf]; |
|
2019 [self _removeMouseMovedObserverUnconditionally]; |
|
2020 [self _removeWindowObservers]; |
|
2021 [self _removeSuperviewObservers]; |
|
2022 [_private->pluginController destroyAllPlugins]; |
|
2023 [_private->pluginController setDataSource:nil]; |
|
2024 // remove tooltips before clearing _private so removeTrackingRect: will work correctly |
|
2025 [self removeAllToolTips]; |
|
2026 |
|
2027 [_private clear]; |
|
2028 |
|
2029 Page* page = core([self _webView]); |
|
2030 if (page) |
|
2031 page->dragController()->setDraggingImageURL(KURL()); |
|
2032 } |
|
2033 |
|
2034 - (BOOL)_hasHTMLDocument |
|
2035 { |
|
2036 Frame* coreFrame = core([self _frame]); |
|
2037 if (!coreFrame) |
|
2038 return NO; |
|
2039 Document* document = coreFrame->document(); |
|
2040 return document && document->isHTMLDocument(); |
|
2041 } |
|
2042 |
|
2043 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard |
|
2044 forType:(NSString *)pboardType |
|
2045 inContext:(DOMRange *)context |
|
2046 subresources:(NSArray **)subresources |
|
2047 { |
|
2048 if (pboardType == WebArchivePboardType) { |
|
2049 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]]; |
|
2050 if (subresources) |
|
2051 *subresources = [archive subresources]; |
|
2052 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive]; |
|
2053 [archive release]; |
|
2054 return fragment; |
|
2055 } |
|
2056 if (pboardType == NSFilenamesPboardType) |
|
2057 return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]]; |
|
2058 |
|
2059 if (pboardType == NSHTMLPboardType) { |
|
2060 NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType]; |
|
2061 // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785. |
|
2062 if ([HTMLString hasPrefix:@"Version:"]) { |
|
2063 NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch]; |
|
2064 if (range.location != NSNotFound) |
|
2065 HTMLString = [HTMLString substringFromIndex:range.location]; |
|
2066 } |
|
2067 if ([HTMLString length] == 0) |
|
2068 return nil; |
|
2069 |
|
2070 return [[self _frame] _documentFragmentWithMarkupString:HTMLString baseURLString:nil]; |
|
2071 } |
|
2072 |
|
2073 // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369. |
|
2074 // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found". |
|
2075 // FIXME: Remove this once bug 5052369 is fixed. |
|
2076 if ([self _hasHTMLDocument] && pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType) { |
|
2077 NSAttributedString *string = nil; |
|
2078 if (pboardType == NSRTFDPboardType) |
|
2079 string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL]; |
|
2080 if (string == nil) |
|
2081 string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL]; |
|
2082 if (string == nil) |
|
2083 return nil; |
|
2084 |
|
2085 NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys: |
|
2086 [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute, |
|
2087 self, @"WebResourceHandler", nil]; |
|
2088 NSArray *s; |
|
2089 |
|
2090 BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks]; |
|
2091 if (!wasDeferringCallbacks) |
|
2092 [[self _webView] setDefersCallbacks:YES]; |
|
2093 |
|
2094 DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length]) |
|
2095 document:[[self _frame] DOMDocument] |
|
2096 documentAttributes:documentAttributes |
|
2097 subresources:&s]; |
|
2098 if (subresources) |
|
2099 *subresources = s; |
|
2100 |
|
2101 NSEnumerator *e = [s objectEnumerator]; |
|
2102 WebResource *r; |
|
2103 while ((r = [e nextObject])) |
|
2104 [[self _dataSource] addSubresource:r]; |
|
2105 |
|
2106 if (!wasDeferringCallbacks) |
|
2107 [[self _webView] setDefersCallbacks:NO]; |
|
2108 |
|
2109 [documentAttributes release]; |
|
2110 [string release]; |
|
2111 return fragment; |
|
2112 } |
|
2113 if (pboardType == NSTIFFPboardType) { |
|
2114 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType] |
|
2115 URL:uniqueURLWithRelativePart(@"image.tiff") |
|
2116 MIMEType:@"image/tiff" |
|
2117 textEncodingName:nil |
|
2118 frameName:nil]; |
|
2119 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource]; |
|
2120 [resource release]; |
|
2121 return fragment; |
|
2122 } |
|
2123 if (pboardType == NSPDFPboardType) { |
|
2124 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPDFPboardType] |
|
2125 URL:uniqueURLWithRelativePart(@"application.pdf") |
|
2126 MIMEType:@"application/pdf" |
|
2127 textEncodingName:nil |
|
2128 frameName:nil]; |
|
2129 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource]; |
|
2130 [resource release]; |
|
2131 return fragment; |
|
2132 } |
|
2133 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) |
|
2134 if (pboardType == NSPICTPboardType) { |
|
2135 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType] |
|
2136 URL:uniqueURLWithRelativePart(@"image.pict") |
|
2137 MIMEType:@"image/pict" |
|
2138 textEncodingName:nil |
|
2139 frameName:nil]; |
|
2140 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource]; |
|
2141 [resource release]; |
|
2142 return fragment; |
|
2143 } |
|
2144 #endif |
|
2145 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe |
|
2146 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard. |
|
2147 if ([pboardType isEqualToString:(NSString*)kUTTypePNG]) { |
|
2148 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:(NSString*)kUTTypePNG] |
|
2149 URL:uniqueURLWithRelativePart(@"image.png") |
|
2150 MIMEType:@"image/png" |
|
2151 textEncodingName:nil |
|
2152 frameName:nil]; |
|
2153 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource]; |
|
2154 [resource release]; |
|
2155 return fragment; |
|
2156 } |
|
2157 if (pboardType == NSURLPboardType) { |
|
2158 NSURL *URL = [NSURL URLFromPasteboard:pasteboard]; |
|
2159 DOMDocument* document = [[self _frame] DOMDocument]; |
|
2160 ASSERT(document); |
|
2161 if (!document) |
|
2162 return nil; |
|
2163 DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"]; |
|
2164 NSString *URLString = [URL _web_originalDataAsString]; // Original data is ASCII-only, so there is no need to precompose. |
|
2165 if ([URLString length] == 0) |
|
2166 return nil; |
|
2167 NSString *URLTitleString = [[pasteboard stringForType:WebURLNamePboardType] precomposedStringWithCanonicalMapping]; |
|
2168 DOMText *text = [document createTextNode:URLTitleString]; |
|
2169 [anchor setHref:URLString]; |
|
2170 [anchor appendChild:text]; |
|
2171 DOMDocumentFragment *fragment = [document createDocumentFragment]; |
|
2172 [fragment appendChild:anchor]; |
|
2173 return fragment; |
|
2174 } |
|
2175 if (pboardType == NSStringPboardType) |
|
2176 return kit(createFragmentFromText(core(context), [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]).get()); |
|
2177 return nil; |
|
2178 } |
|
2179 |
|
2180 #if ENABLE(NETSCAPE_PLUGIN_API) |
|
2181 - (void)_pauseNullEventsForAllNetscapePlugins |
|
2182 { |
|
2183 NSArray *subviews = [self subviews]; |
|
2184 unsigned int subviewCount = [subviews count]; |
|
2185 unsigned int subviewIndex; |
|
2186 |
|
2187 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { |
|
2188 NSView *subview = [subviews objectAtIndex:subviewIndex]; |
|
2189 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]]) |
|
2190 [(WebBaseNetscapePluginView *)subview stopTimers]; |
|
2191 } |
|
2192 } |
|
2193 #endif |
|
2194 |
|
2195 #if ENABLE(NETSCAPE_PLUGIN_API) |
|
2196 - (void)_resumeNullEventsForAllNetscapePlugins |
|
2197 { |
|
2198 NSArray *subviews = [self subviews]; |
|
2199 unsigned int subviewCount = [subviews count]; |
|
2200 unsigned int subviewIndex; |
|
2201 |
|
2202 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { |
|
2203 NSView *subview = [subviews objectAtIndex:subviewIndex]; |
|
2204 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]]) |
|
2205 [(WebBaseNetscapePluginView *)subview restartTimers]; |
|
2206 } |
|
2207 } |
|
2208 #endif |
|
2209 |
|
2210 - (BOOL)_isUsingAcceleratedCompositing |
|
2211 { |
|
2212 #if USE(ACCELERATED_COMPOSITING) |
|
2213 return _private->layerHostingView != nil; |
|
2214 #else |
|
2215 return NO; |
|
2216 #endif |
|
2217 } |
|
2218 |
|
2219 - (NSView *)_compositingLayersHostingView |
|
2220 { |
|
2221 #if USE(ACCELERATED_COMPOSITING) |
|
2222 return _private->layerHostingView; |
|
2223 #else |
|
2224 return 0; |
|
2225 #endif |
|
2226 } |
|
2227 |
|
2228 - (BOOL)_isInPrintMode |
|
2229 { |
|
2230 return _private->printing; |
|
2231 } |
|
2232 |
|
2233 - (BOOL)_beginPrintModeWithPageWidth:(float)pageWidth shrinkToFit:(BOOL)shrinkToFit |
|
2234 { |
|
2235 Frame* frame = core([self _frame]); |
|
2236 if (!frame) |
|
2237 return NO; |
|
2238 |
|
2239 float minLayoutWidth = 0; |
|
2240 float maxLayoutWidth = 0; |
|
2241 |
|
2242 // If we are a frameset just print with the layout we have onscreen, otherwise relayout |
|
2243 // according to the page width. |
|
2244 if (!frame->document() || !frame->document()->isFrameSet()) { |
|
2245 minLayoutWidth = shrinkToFit ? pageWidth * _WebHTMLViewPrintingMinimumShrinkFactor : pageWidth; |
|
2246 maxLayoutWidth = shrinkToFit ? pageWidth * _WebHTMLViewPrintingMaximumShrinkFactor : pageWidth; |
|
2247 } |
|
2248 [self _setPrinting:YES minimumPageWidth:minLayoutWidth maximumPageWidth:maxLayoutWidth adjustViewSize:YES]; |
|
2249 |
|
2250 return YES; |
|
2251 } |
|
2252 |
|
2253 - (void)_endPrintMode |
|
2254 { |
|
2255 [self _setPrinting:NO minimumPageWidth:0 maximumPageWidth:0 adjustViewSize:YES]; |
|
2256 } |
|
2257 |
|
2258 - (CGFloat)_adjustedBottomOfPageWithTop:(CGFloat)top bottom:(CGFloat)bottom limit:(CGFloat)bottomLimit |
|
2259 { |
|
2260 Frame* frame = core([self _frame]); |
|
2261 if (!frame) |
|
2262 return bottom; |
|
2263 |
|
2264 FrameView* view = frame->view(); |
|
2265 if (!view) |
|
2266 return bottom; |
|
2267 |
|
2268 float newBottom; |
|
2269 view->adjustPageHeight(&newBottom, top, bottom, bottomLimit); |
|
2270 |
|
2271 #ifdef __LP64__ |
|
2272 // If the new bottom is equal to the old bottom (when both are treated as floats), we just return the original |
|
2273 // bottom. This prevents rounding errors that can occur when converting newBottom to a double. |
|
2274 if (fabs(static_cast<float>(bottom) - newBottom) <= numeric_limits<float>::epsilon()) |
|
2275 return bottom; |
|
2276 else |
|
2277 #endif |
|
2278 return newBottom; |
|
2279 } |
|
2280 |
|
2281 @end |
|
2282 |
|
2283 @implementation NSView (WebHTMLViewFileInternal) |
|
2284 |
|
2285 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array |
|
2286 { |
|
2287 unsigned count = [_subviews count]; |
|
2288 for (unsigned i = 0; i < count; ++i) { |
|
2289 NSView *child = [_subviews objectAtIndex:i]; |
|
2290 if ([child isKindOfClass:[WebHTMLView class]]) |
|
2291 [array addObject:child]; |
|
2292 [child _web_addDescendantWebHTMLViewsToArray:array]; |
|
2293 } |
|
2294 } |
|
2295 |
|
2296 @end |
|
2297 |
|
2298 @implementation NSMutableDictionary (WebHTMLViewFileInternal) |
|
2299 |
|
2300 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key |
|
2301 { |
|
2302 if (object == nil) { |
|
2303 [self removeObjectForKey:key]; |
|
2304 } else { |
|
2305 [self setObject:object forKey:key]; |
|
2306 } |
|
2307 } |
|
2308 |
|
2309 @end |
|
2310 |
|
2311 static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension) |
|
2312 { |
|
2313 NSString *extensionAsSuffix = [@"." stringByAppendingString:extension]; |
|
2314 return [filename _webkit_hasCaseInsensitiveSuffix:extensionAsSuffix] |
|
2315 || ([extension _webkit_isCaseInsensitiveEqualToString:@"jpeg"] |
|
2316 && [filename _webkit_hasCaseInsensitiveSuffix:@".jpg"]); |
|
2317 } |
|
2318 |
|
2319 #ifdef BUILDING_ON_TIGER |
|
2320 |
|
2321 // The following is a workaround for |
|
2322 // <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears |
|
2323 // The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:. |
|
2324 // Since the category will be searched before the real class, we'll prevent the flag from being |
|
2325 // set on the tool tip panel. |
|
2326 |
|
2327 @interface NSToolTipPanel : NSPanel |
|
2328 @end |
|
2329 |
|
2330 @interface NSToolTipPanel (WebHTMLViewFileInternal) |
|
2331 @end |
|
2332 |
|
2333 @implementation NSToolTipPanel (WebHTMLViewFileInternal) |
|
2334 |
|
2335 - (void)setAcceptsMouseMovedEvents:(BOOL)flag |
|
2336 { |
|
2337 // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events. |
|
2338 } |
|
2339 |
|
2340 @end |
|
2341 |
|
2342 #endif |
|
2343 |
|
2344 @interface NSArray (WebHTMLView) |
|
2345 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object; |
|
2346 @end |
|
2347 |
|
2348 @implementation WebHTMLView |
|
2349 |
|
2350 + (void)initialize |
|
2351 { |
|
2352 [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes] |
|
2353 returnTypes:[[self class] _insertablePasteboardTypes]]; |
|
2354 JSC::initializeThreading(); |
|
2355 WTF::initializeMainThreadToProcessMainThread(); |
|
2356 #ifndef BUILDING_ON_TIGER |
|
2357 WebCoreObjCFinalizeOnMainThread(self); |
|
2358 #endif |
|
2359 } |
|
2360 |
|
2361 - (id)initWithFrame:(NSRect)frame |
|
2362 { |
|
2363 self = [super initWithFrame:frame]; |
|
2364 if (!self) |
|
2365 return nil; |
|
2366 |
|
2367 [self setFocusRingType:NSFocusRingTypeNone]; |
|
2368 |
|
2369 // Make all drawing go through us instead of subviews. |
|
2370 [self _setDrawsOwnDescendants:YES]; |
|
2371 |
|
2372 _private = [[WebHTMLViewPrivate alloc] init]; |
|
2373 |
|
2374 _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self]; |
|
2375 |
|
2376 return self; |
|
2377 } |
|
2378 |
|
2379 - (void)dealloc |
|
2380 { |
|
2381 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLView class], self)) |
|
2382 return; |
|
2383 |
|
2384 // We can't assert that close has already been called because |
|
2385 // this view can be removed from it's superview, even though |
|
2386 // it could be needed later, so close if needed. |
|
2387 [self close]; |
|
2388 [_private release]; |
|
2389 _private = nil; |
|
2390 [super dealloc]; |
|
2391 } |
|
2392 |
|
2393 - (void)finalize |
|
2394 { |
|
2395 ASSERT_MAIN_THREAD(); |
|
2396 // We can't assert that close has already been called because |
|
2397 // this view can be removed from it's superview, even though |
|
2398 // it could be needed later, so close if needed. |
|
2399 [self close]; |
|
2400 [super finalize]; |
|
2401 } |
|
2402 |
|
2403 // Returns YES if the delegate returns YES (so we should do no more work). |
|
2404 - (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector |
|
2405 { |
|
2406 BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector; |
|
2407 _private->selectorForDoCommandBySelector = 0; |
|
2408 if (callerAlreadyCalledDelegate) |
|
2409 return NO; |
|
2410 WebView *webView = [self _webView]; |
|
2411 return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector]; |
|
2412 } |
|
2413 |
|
2414 typedef HashMap<SEL, String> SelectorNameMap; |
|
2415 |
|
2416 // Map selectors into Editor command names. |
|
2417 // This is not needed for any selectors that have the same name as the Editor command. |
|
2418 static const SelectorNameMap* createSelectorExceptionMap() |
|
2419 { |
|
2420 SelectorNameMap* map = new HashMap<SEL, String>; |
|
2421 |
|
2422 map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline"); |
|
2423 map->add(@selector(insertParagraphSeparator:), "InsertNewline"); |
|
2424 map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab"); |
|
2425 map->add(@selector(pageDown:), "MovePageDown"); |
|
2426 map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection"); |
|
2427 map->add(@selector(pageUp:), "MovePageUp"); |
|
2428 map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection"); |
|
2429 |
|
2430 return map; |
|
2431 } |
|
2432 |
|
2433 static String commandNameForSelector(SEL selector) |
|
2434 { |
|
2435 // Check the exception map first. |
|
2436 static const SelectorNameMap* exceptionMap = createSelectorExceptionMap(); |
|
2437 SelectorNameMap::const_iterator it = exceptionMap->find(selector); |
|
2438 if (it != exceptionMap->end()) |
|
2439 return it->second; |
|
2440 |
|
2441 // Remove the trailing colon. |
|
2442 // No need to capitalize the command name since Editor command names are |
|
2443 // not case sensitive. |
|
2444 const char* selectorName = sel_getName(selector); |
|
2445 size_t selectorNameLength = strlen(selectorName); |
|
2446 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':') |
|
2447 return String(); |
|
2448 return String(selectorName, selectorNameLength - 1); |
|
2449 } |
|
2450 |
|
2451 - (Editor::Command)coreCommandBySelector:(SEL)selector |
|
2452 { |
|
2453 Frame* coreFrame = core([self _frame]); |
|
2454 if (!coreFrame) |
|
2455 return Editor::Command(); |
|
2456 return coreFrame->editor()->command(commandNameForSelector(selector)); |
|
2457 } |
|
2458 |
|
2459 - (Editor::Command)coreCommandByName:(const char*)name |
|
2460 { |
|
2461 Frame* coreFrame = core([self _frame]); |
|
2462 if (!coreFrame) |
|
2463 return Editor::Command(); |
|
2464 return coreFrame->editor()->command(name); |
|
2465 } |
|
2466 |
|
2467 - (void)executeCoreCommandBySelector:(SEL)selector |
|
2468 { |
|
2469 if ([self callDelegateDoCommandBySelectorIfNeeded:selector]) |
|
2470 return; |
|
2471 [self coreCommandBySelector:selector].execute(); |
|
2472 } |
|
2473 |
|
2474 - (void)executeCoreCommandByName:(const char*)name |
|
2475 { |
|
2476 [self coreCommandByName:name].execute(); |
|
2477 } |
|
2478 |
|
2479 // These commands are forwarded to the Editor object in WebCore. |
|
2480 // Ideally we'd do this for all editing commands; more of the code |
|
2481 // should be moved from here to there, and more commands should be |
|
2482 // added to this list. |
|
2483 |
|
2484 // FIXME: Maybe we should set things up so that all these share a single method implementation function. |
|
2485 // The functions are identical. |
|
2486 |
|
2487 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; } |
|
2488 |
|
2489 WEBCORE_COMMAND(alignCenter) |
|
2490 WEBCORE_COMMAND(alignJustified) |
|
2491 WEBCORE_COMMAND(alignLeft) |
|
2492 WEBCORE_COMMAND(alignRight) |
|
2493 WEBCORE_COMMAND(copy) |
|
2494 WEBCORE_COMMAND(cut) |
|
2495 WEBCORE_COMMAND(delete) |
|
2496 WEBCORE_COMMAND(deleteBackward) |
|
2497 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter) |
|
2498 WEBCORE_COMMAND(deleteForward) |
|
2499 WEBCORE_COMMAND(deleteToBeginningOfLine) |
|
2500 WEBCORE_COMMAND(deleteToBeginningOfParagraph) |
|
2501 WEBCORE_COMMAND(deleteToEndOfLine) |
|
2502 WEBCORE_COMMAND(deleteToEndOfParagraph) |
|
2503 WEBCORE_COMMAND(deleteToMark) |
|
2504 WEBCORE_COMMAND(deleteWordBackward) |
|
2505 WEBCORE_COMMAND(deleteWordForward) |
|
2506 WEBCORE_COMMAND(ignoreSpelling) |
|
2507 WEBCORE_COMMAND(indent) |
|
2508 WEBCORE_COMMAND(insertBacktab) |
|
2509 WEBCORE_COMMAND(insertLineBreak) |
|
2510 WEBCORE_COMMAND(insertNewline) |
|
2511 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor) |
|
2512 WEBCORE_COMMAND(insertParagraphSeparator) |
|
2513 WEBCORE_COMMAND(insertTab) |
|
2514 WEBCORE_COMMAND(insertTabIgnoringFieldEditor) |
|
2515 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight) |
|
2516 WEBCORE_COMMAND(makeTextWritingDirectionNatural) |
|
2517 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft) |
|
2518 WEBCORE_COMMAND(moveBackward) |
|
2519 WEBCORE_COMMAND(moveBackwardAndModifySelection) |
|
2520 WEBCORE_COMMAND(moveDown) |
|
2521 WEBCORE_COMMAND(moveDownAndModifySelection) |
|
2522 WEBCORE_COMMAND(moveForward) |
|
2523 WEBCORE_COMMAND(moveForwardAndModifySelection) |
|
2524 WEBCORE_COMMAND(moveLeft) |
|
2525 WEBCORE_COMMAND(moveLeftAndModifySelection) |
|
2526 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection) |
|
2527 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection) |
|
2528 WEBCORE_COMMAND(moveRight) |
|
2529 WEBCORE_COMMAND(moveRightAndModifySelection) |
|
2530 WEBCORE_COMMAND(moveToBeginningOfDocument) |
|
2531 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection) |
|
2532 WEBCORE_COMMAND(moveToBeginningOfLine) |
|
2533 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection) |
|
2534 WEBCORE_COMMAND(moveToBeginningOfParagraph) |
|
2535 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection) |
|
2536 WEBCORE_COMMAND(moveToBeginningOfSentence) |
|
2537 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection) |
|
2538 WEBCORE_COMMAND(moveToEndOfDocument) |
|
2539 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection) |
|
2540 WEBCORE_COMMAND(moveToEndOfLine) |
|
2541 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection) |
|
2542 WEBCORE_COMMAND(moveToEndOfParagraph) |
|
2543 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection) |
|
2544 WEBCORE_COMMAND(moveToEndOfSentence) |
|
2545 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection) |
|
2546 WEBCORE_COMMAND(moveToLeftEndOfLine) |
|
2547 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection) |
|
2548 WEBCORE_COMMAND(moveToRightEndOfLine) |
|
2549 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection) |
|
2550 WEBCORE_COMMAND(moveUp) |
|
2551 WEBCORE_COMMAND(moveUpAndModifySelection) |
|
2552 WEBCORE_COMMAND(moveWordBackward) |
|
2553 WEBCORE_COMMAND(moveWordBackwardAndModifySelection) |
|
2554 WEBCORE_COMMAND(moveWordForward) |
|
2555 WEBCORE_COMMAND(moveWordForwardAndModifySelection) |
|
2556 WEBCORE_COMMAND(moveWordLeft) |
|
2557 WEBCORE_COMMAND(moveWordLeftAndModifySelection) |
|
2558 WEBCORE_COMMAND(moveWordRight) |
|
2559 WEBCORE_COMMAND(moveWordRightAndModifySelection) |
|
2560 WEBCORE_COMMAND(outdent) |
|
2561 WEBCORE_COMMAND(pageDown) |
|
2562 WEBCORE_COMMAND(pageDownAndModifySelection) |
|
2563 WEBCORE_COMMAND(pageUp) |
|
2564 WEBCORE_COMMAND(pageUpAndModifySelection) |
|
2565 WEBCORE_COMMAND(pasteAsPlainText) |
|
2566 WEBCORE_COMMAND(selectAll) |
|
2567 WEBCORE_COMMAND(selectLine) |
|
2568 WEBCORE_COMMAND(selectParagraph) |
|
2569 WEBCORE_COMMAND(selectSentence) |
|
2570 WEBCORE_COMMAND(selectToMark) |
|
2571 WEBCORE_COMMAND(selectWord) |
|
2572 WEBCORE_COMMAND(setMark) |
|
2573 WEBCORE_COMMAND(subscript) |
|
2574 WEBCORE_COMMAND(superscript) |
|
2575 WEBCORE_COMMAND(swapWithMark) |
|
2576 WEBCORE_COMMAND(transpose) |
|
2577 WEBCORE_COMMAND(underline) |
|
2578 WEBCORE_COMMAND(unscript) |
|
2579 WEBCORE_COMMAND(yank) |
|
2580 WEBCORE_COMMAND(yankAndSelect) |
|
2581 |
|
2582 #undef WEBCORE_COMMAND |
|
2583 |
|
2584 #define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return; |
|
2585 |
|
2586 - (IBAction)takeFindStringFromSelection:(id)sender |
|
2587 { |
|
2588 COMMAND_PROLOGUE |
|
2589 |
|
2590 if (![self _hasSelection]) { |
|
2591 NSBeep(); |
|
2592 return; |
|
2593 } |
|
2594 |
|
2595 [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self]; |
|
2596 } |
|
2597 |
|
2598 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types |
|
2599 { |
|
2600 [pasteboard declareTypes:types owner:[self _topHTMLView]]; |
|
2601 [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard]; |
|
2602 return YES; |
|
2603 } |
|
2604 |
|
2605 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType |
|
2606 { |
|
2607 BOOL isSendTypeOK = !sendType || ([[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]); |
|
2608 BOOL isReturnTypeOK = NO; |
|
2609 if (!returnType) |
|
2610 isReturnTypeOK = YES; |
|
2611 else if ([[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) { |
|
2612 // We can insert strings in any editable context. We can insert other types, like images, only in rich edit contexts. |
|
2613 isReturnTypeOK = [returnType isEqualToString:NSStringPboardType] || [self _canEditRichly]; |
|
2614 } |
|
2615 if (isSendTypeOK && isReturnTypeOK) |
|
2616 return self; |
|
2617 return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType]; |
|
2618 } |
|
2619 |
|
2620 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari |
|
2621 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the |
|
2622 // selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons: |
|
2623 // (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications |
|
2624 // might be using the selector, and we don't want to break them. |
|
2625 - (void)jumpToSelection:(id)sender |
|
2626 { |
|
2627 COMMAND_PROLOGUE |
|
2628 |
|
2629 if (Frame* coreFrame = core([self _frame])) |
|
2630 coreFrame->revealSelection(ScrollAlignment::alignCenterAlways); |
|
2631 } |
|
2632 |
|
2633 - (NSCellStateValue)selectionHasStyle:(CSSStyleDeclaration*)style |
|
2634 { |
|
2635 Frame* coreFrame = core([self _frame]); |
|
2636 if (!coreFrame) |
|
2637 return NSOffState; |
|
2638 return kit(coreFrame->editor()->selectionHasStyle(style)); |
|
2639 } |
|
2640 |
|
2641 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item |
|
2642 { |
|
2643 SEL action = [item action]; |
|
2644 RefPtr<Frame> frame = core([self _frame]); |
|
2645 |
|
2646 if (!frame) |
|
2647 return NO; |
|
2648 |
|
2649 if (Document* doc = frame->document()) { |
|
2650 if (doc->isPluginDocument()) |
|
2651 return NO; |
|
2652 if (doc->isImageDocument()) { |
|
2653 if (action == @selector(copy:)) |
|
2654 return frame->loader()->isComplete(); |
|
2655 return NO; |
|
2656 } |
|
2657 } |
|
2658 |
|
2659 if (action == @selector(changeSpelling:) |
|
2660 || action == @selector(_changeSpellingFromMenu:) |
|
2661 || action == @selector(checkSpelling:) |
|
2662 || action == @selector(complete:) |
|
2663 || action == @selector(pasteFont:)) |
|
2664 return [self _canEdit]; |
|
2665 |
|
2666 if (action == @selector(showGuessPanel:)) { |
|
2667 #ifndef BUILDING_ON_TIGER |
|
2668 // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior. |
|
2669 NSMenuItem *menuItem = (NSMenuItem *)item; |
|
2670 if ([menuItem isKindOfClass:[NSMenuItem class]]) { |
|
2671 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible]; |
|
2672 [menuItem setTitle:panelShowing |
|
2673 ? UI_STRING("Hide Spelling and Grammar", "menu item title") |
|
2674 : UI_STRING("Show Spelling and Grammar", "menu item title")]; |
|
2675 } |
|
2676 #endif |
|
2677 return [self _canEdit]; |
|
2678 } |
|
2679 |
|
2680 if (action == @selector(changeBaseWritingDirection:) |
|
2681 || action == @selector(makeBaseWritingDirectionLeftToRight:) |
|
2682 || action == @selector(makeBaseWritingDirectionRightToLeft:)) { |
|
2683 NSWritingDirection writingDirection; |
|
2684 |
|
2685 if (action == @selector(changeBaseWritingDirection:)) { |
|
2686 writingDirection = static_cast<NSWritingDirection>([item tag]); |
|
2687 if (writingDirection == NSWritingDirectionNatural) |
|
2688 return NO; |
|
2689 } else if (action == @selector(makeBaseWritingDirectionLeftToRight:)) |
|
2690 writingDirection = NSWritingDirectionLeftToRight; |
|
2691 else |
|
2692 writingDirection = NSWritingDirectionRightToLeft; |
|
2693 |
|
2694 NSMenuItem *menuItem = (NSMenuItem *)item; |
|
2695 if ([menuItem isKindOfClass:[NSMenuItem class]]) { |
|
2696 RefPtr<CSSStyleDeclaration> style = CSSMutableStyleDeclaration::create(); |
|
2697 ExceptionCode ec; |
|
2698 style->setProperty("direction", writingDirection == NSWritingDirectionLeftToRight ? "LTR" : "RTL", ec); |
|
2699 [menuItem setState:frame->editor()->selectionHasStyle(style.get())]; |
|
2700 } |
|
2701 return [self _canEdit]; |
|
2702 } |
|
2703 |
|
2704 if (action == @selector(makeBaseWritingDirectionNatural:)) { |
|
2705 NSMenuItem *menuItem = (NSMenuItem *)item; |
|
2706 if ([menuItem isKindOfClass:[NSMenuItem class]]) |
|
2707 [menuItem setState:NSOffState]; |
|
2708 return NO; |
|
2709 } |
|
2710 |
|
2711 if (action == @selector(toggleBaseWritingDirection:)) { |
|
2712 NSMenuItem *menuItem = (NSMenuItem *)item; |
|
2713 if ([menuItem isKindOfClass:[NSMenuItem class]]) { |
|
2714 RefPtr<CSSStyleDeclaration> style = CSSMutableStyleDeclaration::create(); |
|
2715 ExceptionCode ec; |
|
2716 style->setProperty("direction", "RTL", ec); |
|
2717 // Take control of the title of the menu item instead of just checking/unchecking it because |
|
2718 // a check would be ambiguous. |
|
2719 [menuItem setTitle:frame->editor()->selectionHasStyle(style.get()) |
|
2720 ? UI_STRING("Left to Right", "Left to Right context menu item") |
|
2721 : UI_STRING("Right to Left", "Right to Left context menu item")]; |
|
2722 } |
|
2723 return [self _canEdit]; |
|
2724 } |
|
2725 |
|
2726 if (action == @selector(changeAttributes:) |
|
2727 || action == @selector(changeColor:) |
|
2728 || action == @selector(changeFont:)) |
|
2729 return [self _canEditRichly]; |
|
2730 |
|
2731 if (action == @selector(capitalizeWord:) |
|
2732 || action == @selector(lowercaseWord:) |
|
2733 || action == @selector(uppercaseWord:)) |
|
2734 return [self _hasSelection] && [self _isEditable]; |
|
2735 |
|
2736 if (action == @selector(centerSelectionInVisibleArea:) |
|
2737 || action == @selector(jumpToSelection:) |
|
2738 || action == @selector(copyFont:)) |
|
2739 return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]); |
|
2740 |
|
2741 if (action == @selector(changeDocumentBackgroundColor:)) |
|
2742 return [[self _webView] isEditable] && [self _canEditRichly]; |
|
2743 |
|
2744 if (action == @selector(_ignoreSpellingFromMenu:) |
|
2745 || action == @selector(_learnSpellingFromMenu:) |
|
2746 || action == @selector(takeFindStringFromSelection:)) |
|
2747 return [self _hasSelection]; |
|
2748 |
|
2749 if (action == @selector(paste:) || action == @selector(pasteAsPlainText:)) |
|
2750 return frame && (frame->editor()->canDHTMLPaste() || frame->editor()->canPaste()); |
|
2751 |
|
2752 if (action == @selector(pasteAsRichText:)) |
|
2753 return frame && (frame->editor()->canDHTMLPaste() |
|
2754 || (frame->editor()->canPaste() && frame->selection()->isContentRichlyEditable())); |
|
2755 |
|
2756 if (action == @selector(performFindPanelAction:)) |
|
2757 return NO; |
|
2758 |
|
2759 if (action == @selector(_lookUpInDictionaryFromMenu:)) |
|
2760 return [self _hasSelection]; |
|
2761 |
|
2762 if (action == @selector(stopSpeaking:)) |
|
2763 return [NSApp isSpeaking]; |
|
2764 |
|
2765 #ifndef BUILDING_ON_TIGER |
|
2766 if (action == @selector(toggleGrammarChecking:)) { |
|
2767 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate |
|
2768 // the selector here because we implement it here, and we must implement it here because the AppKit |
|
2769 // code checks the first responder. |
|
2770 NSMenuItem *menuItem = (NSMenuItem *)item; |
|
2771 if ([menuItem isKindOfClass:[NSMenuItem class]]) |
|
2772 [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState]; |
|
2773 return YES; |
|
2774 } |
|
2775 #endif |
|
2776 |
|
2777 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
2778 if (action == @selector(orderFrontSubstitutionsPanel:)) { |
|
2779 NSMenuItem *menuItem = (NSMenuItem *)item; |
|
2780 if ([menuItem isKindOfClass:[NSMenuItem class]]) { |
|
2781 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible]; |
|
2782 [menuItem setTitle:panelShowing |
|
2783 ? UI_STRING("Hide Substitutions", "menu item title") |
|
2784 : UI_STRING("Show Substitutions", "menu item title")]; |
|
2785 } |
|
2786 return [self _canEdit]; |
|
2787 } |
|
2788 // FIXME 4799134: WebView is the bottleneck for this logic, but we must validate |
|
2789 // the selector here because we implement it here, and we must implement it here because the AppKit |
|
2790 // code checks the first responder. |
|
2791 if (action == @selector(toggleSmartInsertDelete:)) { |
|
2792 NSMenuItem *menuItem = (NSMenuItem *)item; |
|
2793 if ([menuItem isKindOfClass:[NSMenuItem class]]) |
|
2794 [menuItem setState:[self smartInsertDeleteEnabled] ? NSOnState : NSOffState]; |
|
2795 return [self _canEdit]; |
|
2796 } |
|
2797 if (action == @selector(toggleAutomaticQuoteSubstitution:)) { |
|
2798 NSMenuItem *menuItem = (NSMenuItem *)item; |
|
2799 if ([menuItem isKindOfClass:[NSMenuItem class]]) |
|
2800 [menuItem setState:[self isAutomaticQuoteSubstitutionEnabled] ? NSOnState : NSOffState]; |
|
2801 return [self _canEdit]; |
|
2802 } |
|
2803 if (action == @selector(toggleAutomaticLinkDetection:)) { |
|
2804 NSMenuItem *menuItem = (NSMenuItem *)item; |
|
2805 if ([menuItem isKindOfClass:[NSMenuItem class]]) |
|
2806 [menuItem setState:[self isAutomaticLinkDetectionEnabled] ? NSOnState : NSOffState]; |
|
2807 return [self _canEdit]; |
|
2808 } |
|
2809 if (action == @selector(toggleAutomaticDashSubstitution:)) { |
|
2810 NSMenuItem *menuItem = (NSMenuItem *)item; |
|
2811 if ([menuItem isKindOfClass:[NSMenuItem class]]) |
|
2812 [menuItem setState:[self isAutomaticDashSubstitutionEnabled] ? NSOnState : NSOffState]; |
|
2813 return [self _canEdit]; |
|
2814 } |
|
2815 if (action == @selector(toggleAutomaticTextReplacement:)) { |
|
2816 NSMenuItem *menuItem = (NSMenuItem *)item; |
|
2817 if ([menuItem isKindOfClass:[NSMenuItem class]]) |
|
2818 [menuItem setState:[self isAutomaticTextReplacementEnabled] ? NSOnState : NSOffState]; |
|
2819 return [self _canEdit]; |
|
2820 } |
|
2821 if (action == @selector(toggleAutomaticSpellingCorrection:)) { |
|
2822 NSMenuItem *menuItem = (NSMenuItem *)item; |
|
2823 if ([menuItem isKindOfClass:[NSMenuItem class]]) |
|
2824 [menuItem setState:[self isAutomaticSpellingCorrectionEnabled] ? NSOnState : NSOffState]; |
|
2825 return [self _canEdit]; |
|
2826 } |
|
2827 #endif |
|
2828 |
|
2829 Editor::Command command = [self coreCommandBySelector:action]; |
|
2830 if (command.isSupported()) { |
|
2831 NSMenuItem *menuItem = (NSMenuItem *)item; |
|
2832 if ([menuItem isKindOfClass:[NSMenuItem class]]) |
|
2833 [menuItem setState:kit(command.state())]; |
|
2834 return command.isEnabled(); |
|
2835 } |
|
2836 |
|
2837 return YES; |
|
2838 } |
|
2839 |
|
2840 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item |
|
2841 { |
|
2842 // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean |
|
2843 // assumes the WebVIew is non-nil. |
|
2844 if (![self _webView]) |
|
2845 return NO; |
|
2846 BOOL result = [self validateUserInterfaceItemWithoutDelegate:item]; |
|
2847 return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result); |
|
2848 } |
|
2849 |
|
2850 - (BOOL)acceptsFirstResponder |
|
2851 { |
|
2852 // Don't accept first responder when we first click on this view. |
|
2853 // We have to pass the event down through WebCore first to be sure we don't hit a subview. |
|
2854 // Do accept first responder at any other time, for example from keyboard events, |
|
2855 // or from calls back from WebCore once we begin mouse-down event handling. |
|
2856 NSEvent *event = [NSApp currentEvent]; |
|
2857 if ([event type] == NSLeftMouseDown |
|
2858 && !_private->handlingMouseDownEvent |
|
2859 && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) { |
|
2860 return NO; |
|
2861 } |
|
2862 return YES; |
|
2863 } |
|
2864 |
|
2865 - (BOOL)maintainsInactiveSelection |
|
2866 { |
|
2867 // This method helps to determine whether the WebHTMLView should maintain |
|
2868 // an inactive selection when it's not first responder. |
|
2869 // Traditionally, these views have not maintained such selections, |
|
2870 // clearing them when the view was not first responder. However, |
|
2871 // to fix bugs like this one: |
|
2872 // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even |
|
2873 // when they're not firstResponder" |
|
2874 // it was decided to add a switch to act more like an NSTextView. |
|
2875 |
|
2876 if ([[self _webView] maintainsInactiveSelection]) |
|
2877 return YES; |
|
2878 |
|
2879 // Predict the case where we are losing first responder status only to |
|
2880 // gain it back again. Want to keep the selection in that case. |
|
2881 id nextResponder = [[self window] _newFirstResponderAfterResigning]; |
|
2882 if ([nextResponder isKindOfClass:[NSScrollView class]]) { |
|
2883 id contentView = [nextResponder contentView]; |
|
2884 if (contentView) |
|
2885 nextResponder = contentView; |
|
2886 } |
|
2887 if ([nextResponder isKindOfClass:[NSClipView class]]) { |
|
2888 id documentView = [nextResponder documentView]; |
|
2889 if (documentView) |
|
2890 nextResponder = documentView; |
|
2891 } |
|
2892 if (nextResponder == self) |
|
2893 return YES; |
|
2894 |
|
2895 Frame* coreFrame = core([self _frame]); |
|
2896 bool selectionIsEditable = coreFrame && coreFrame->selection()->isContentEditable(); |
|
2897 bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]] |
|
2898 && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]]; |
|
2899 |
|
2900 return selectionIsEditable && nextResponderIsInWebView; |
|
2901 } |
|
2902 |
|
2903 - (void)addMouseMovedObserver |
|
2904 { |
|
2905 if (!_private->dataSource || ![self _isTopHTMLView] || _private->observingMouseMovedNotifications) |
|
2906 return; |
|
2907 |
|
2908 // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window. |
|
2909 if (!([[self window] isKeyWindow] |
|
2910 #if ENABLE(DASHBOARD_SUPPORT) |
|
2911 || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows] |
|
2912 #endif |
|
2913 )) |
|
2914 return; |
|
2915 |
|
2916 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:) |
|
2917 name:WKMouseMovedNotification() object:nil]; |
|
2918 [self _frameOrBoundsChanged]; |
|
2919 _private->observingMouseMovedNotifications = true; |
|
2920 } |
|
2921 |
|
2922 - (void)removeMouseMovedObserver |
|
2923 { |
|
2924 #if ENABLE(DASHBOARD_SUPPORT) |
|
2925 // Don't remove the observer if we're running the Dashboard. |
|
2926 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]) |
|
2927 return; |
|
2928 #endif |
|
2929 |
|
2930 [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0]; |
|
2931 [self _removeMouseMovedObserverUnconditionally]; |
|
2932 } |
|
2933 |
|
2934 - (void)addSuperviewObservers |
|
2935 { |
|
2936 if (_private->observingSuperviewNotifications) |
|
2937 return; |
|
2938 |
|
2939 NSView *superview = [self superview]; |
|
2940 if (!superview || ![self window]) |
|
2941 return; |
|
2942 |
|
2943 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; |
|
2944 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewFrameDidChangeNotification object:superview]; |
|
2945 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewBoundsDidChangeNotification object:superview]; |
|
2946 |
|
2947 // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged. |
|
2948 // It will check the current scroll against the previous layout's scroll. We need to |
|
2949 // do this here to catch the case where the WebView is laid out at one size, removed from its |
|
2950 // window, resized, and inserted into another window. Our frame/bounds changed notifications |
|
2951 // will not be sent in that situation, since we only watch for changes while in the view hierarchy. |
|
2952 [self _frameOrBoundsChanged]; |
|
2953 |
|
2954 _private->observingSuperviewNotifications = true; |
|
2955 } |
|
2956 |
|
2957 - (void)addWindowObservers |
|
2958 { |
|
2959 if (_private->observingWindowNotifications) |
|
2960 return; |
|
2961 |
|
2962 NSWindow *window = [self window]; |
|
2963 if (!window) |
|
2964 return; |
|
2965 |
|
2966 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; |
|
2967 [notificationCenter addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil]; |
|
2968 [notificationCenter addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil]; |
|
2969 [notificationCenter addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:window]; |
|
2970 |
|
2971 _private->observingWindowNotifications = true; |
|
2972 } |
|
2973 |
|
2974 - (void)viewWillMoveToSuperview:(NSView *)newSuperview |
|
2975 { |
|
2976 [self _removeSuperviewObservers]; |
|
2977 } |
|
2978 |
|
2979 - (void)viewDidMoveToSuperview |
|
2980 { |
|
2981 if ([self superview] != nil) |
|
2982 [self addSuperviewObservers]; |
|
2983 |
|
2984 #if USE(ACCELERATED_COMPOSITING) |
|
2985 if ([self superview] && [self _isUsingAcceleratedCompositing]) { |
|
2986 WebView *webView = [self _webView]; |
|
2987 if ([webView _postsAcceleratedCompositingNotifications]) |
|
2988 [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:webView userInfo:nil]; |
|
2989 } |
|
2990 #endif |
|
2991 } |
|
2992 |
|
2993 - (void)viewWillMoveToWindow:(NSWindow *)window |
|
2994 { |
|
2995 // Don't do anything if we aren't initialized. This happens |
|
2996 // when decoding a WebView. When WebViews are decoded their subviews |
|
2997 // are created by initWithCoder: and so won't be normally |
|
2998 // initialized. The stub views are discarded by WebView. |
|
2999 if (!_private) |
|
3000 return; |
|
3001 |
|
3002 // FIXME: Some of these calls may not work because this view may be already removed from it's superview. |
|
3003 [self _removeMouseMovedObserverUnconditionally]; |
|
3004 [self _removeWindowObservers]; |
|
3005 [self _removeSuperviewObservers]; |
|
3006 [self _cancelUpdateMouseoverTimer]; |
|
3007 |
|
3008 // FIXME: This accomplishes the same thing as the call to setCanStartMedia(false) in |
|
3009 // WebView. It would be nice to have a single mechanism instead of two. |
|
3010 [[self _pluginController] stopAllPlugins]; |
|
3011 } |
|
3012 |
|
3013 - (void)viewDidMoveToWindow |
|
3014 { |
|
3015 // Don't do anything if we aren't initialized. This happens |
|
3016 // when decoding a WebView. When WebViews are decoded their subviews |
|
3017 // are created by initWithCoder: and so won't be normally |
|
3018 // initialized. The stub views are discarded by WebView. |
|
3019 if (!_private || _private->closed) |
|
3020 return; |
|
3021 |
|
3022 [self _stopAutoscrollTimer]; |
|
3023 if ([self window]) { |
|
3024 _private->lastScrollPosition = [[self superview] bounds].origin; |
|
3025 [self addWindowObservers]; |
|
3026 [self addSuperviewObservers]; |
|
3027 [self addMouseMovedObserver]; |
|
3028 |
|
3029 // FIXME: This accomplishes the same thing as the call to setCanStartMedia(true) in |
|
3030 // WebView. It would be nice to have a single mechanism instead of two. |
|
3031 [[self _pluginController] startAllPlugins]; |
|
3032 |
|
3033 _private->lastScrollPosition = NSZeroPoint; |
|
3034 |
|
3035 #if USE(ACCELERATED_COMPOSITING) && !defined(BUILDING_ON_LEOPARD) |
|
3036 // We may have created the layer hosting view while outside the window. Update the scale factor |
|
3037 // now that we have a window to get it from. |
|
3038 if (_private->layerHostingView) { |
|
3039 CGFloat scaleFactor = [[self window] userSpaceScaleFactor]; |
|
3040 [[_private->layerHostingView layer] setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)]; |
|
3041 } |
|
3042 #endif |
|
3043 } |
|
3044 } |
|
3045 |
|
3046 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow |
|
3047 { |
|
3048 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow]; |
|
3049 } |
|
3050 |
|
3051 - (void)viewDidMoveToHostWindow |
|
3052 { |
|
3053 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil]; |
|
3054 } |
|
3055 |
|
3056 |
|
3057 - (void)addSubview:(NSView *)view |
|
3058 { |
|
3059 [super addSubview:view]; |
|
3060 |
|
3061 if ([WebPluginController isPlugInView:view]) |
|
3062 [[self _pluginController] addPlugin:view]; |
|
3063 } |
|
3064 |
|
3065 - (void)willRemoveSubview:(NSView *)subview |
|
3066 { |
|
3067 #ifndef NDEBUG |
|
3068 // Have to null-check _private, since this can be called via -dealloc when |
|
3069 // cleaning up the the layerHostingView. |
|
3070 if (_private && _private->enumeratingSubviews) |
|
3071 LOG(View, "A view of class %s was removed during subview enumeration for layout or printing mode change. We will still do layout or the printing mode change even though this view is no longer in the view hierarchy.", object_getClassName([subview class])); |
|
3072 #endif |
|
3073 |
|
3074 if ([WebPluginController isPlugInView:subview]) |
|
3075 [[self _pluginController] destroyPlugin:subview]; |
|
3076 |
|
3077 [super willRemoveSubview:subview]; |
|
3078 } |
|
3079 |
|
3080 - (void)reapplyStyles |
|
3081 { |
|
3082 if (!_private->needsToApplyStyles) |
|
3083 return; |
|
3084 |
|
3085 #ifdef LOG_TIMES |
|
3086 double start = CFAbsoluteTimeGetCurrent(); |
|
3087 #endif |
|
3088 |
|
3089 if (Frame* coreFrame = core([self _frame])) { |
|
3090 if (FrameView* coreView = coreFrame->view()) |
|
3091 coreView->setMediaType(_private->printing ? "print" : "screen"); |
|
3092 if (Document* document = coreFrame->document()) |
|
3093 document->setPrinting(_private->printing); |
|
3094 coreFrame->reapplyStyles(); |
|
3095 } |
|
3096 |
|
3097 #ifdef LOG_TIMES |
|
3098 double thisTime = CFAbsoluteTimeGetCurrent() - start; |
|
3099 LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime); |
|
3100 #endif |
|
3101 |
|
3102 _private->needsToApplyStyles = NO; |
|
3103 } |
|
3104 |
|
3105 // Do a layout, but set up a new fixed width for the purposes of doing printing layout. |
|
3106 // minPageWidth==0 implies a non-printing layout |
|
3107 - (void)layoutToMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)adjustViewSize |
|
3108 { |
|
3109 [self reapplyStyles]; |
|
3110 |
|
3111 if (![self _needsLayout]) |
|
3112 return; |
|
3113 |
|
3114 #ifdef LOG_TIMES |
|
3115 double start = CFAbsoluteTimeGetCurrent(); |
|
3116 #endif |
|
3117 |
|
3118 LOG(View, "%@ doing layout", self); |
|
3119 |
|
3120 Frame* coreFrame = core([self _frame]); |
|
3121 if (!coreFrame) |
|
3122 return; |
|
3123 |
|
3124 if (FrameView* coreView = coreFrame->view()) { |
|
3125 if (minPageWidth > 0.0) |
|
3126 coreView->forceLayoutWithPageWidthRange(minPageWidth, maxPageWidth, adjustViewSize); |
|
3127 else { |
|
3128 coreView->forceLayout(!adjustViewSize); |
|
3129 if (adjustViewSize) |
|
3130 coreView->adjustViewSize(); |
|
3131 } |
|
3132 } |
|
3133 |
|
3134 #ifdef LOG_TIMES |
|
3135 double thisTime = CFAbsoluteTimeGetCurrent() - start; |
|
3136 LOG(Timing, "%s layout seconds = %f", [self URL], thisTime); |
|
3137 #endif |
|
3138 } |
|
3139 |
|
3140 - (void)layout |
|
3141 { |
|
3142 [self layoutToMinimumPageWidth:0.0f maximumPageWidth:0.0f adjustingViewSize:NO]; |
|
3143 } |
|
3144 |
|
3145 // Deliver mouseup events to the DOM for button 2. |
|
3146 - (void)rightMouseUp:(NSEvent *)event |
|
3147 { |
|
3148 // There's a chance that if we run a nested event loop the event will be released. |
|
3149 // Retaining and then autoreleasing prevents that from causing a problem later here or |
|
3150 // inside AppKit code. |
|
3151 [[event retain] autorelease]; |
|
3152 |
|
3153 [super rightMouseUp:event]; |
|
3154 |
|
3155 if (Frame* coreframe = core([self _frame])) |
|
3156 coreframe->eventHandler()->mouseUp(event); |
|
3157 } |
|
3158 |
|
3159 - (NSMenu *)menuForEvent:(NSEvent *)event |
|
3160 { |
|
3161 // There's a chance that if we run a nested event loop the event will be released. |
|
3162 // Retaining and then autoreleasing prevents that from causing a problem later here or |
|
3163 // inside AppKit code. |
|
3164 [[event retain] autorelease]; |
|
3165 |
|
3166 [_private->completionController endRevertingChange:NO moveLeft:NO]; |
|
3167 |
|
3168 RefPtr<Frame> coreFrame = core([self _frame]); |
|
3169 if (!coreFrame) |
|
3170 return nil; |
|
3171 |
|
3172 Page* page = coreFrame->page(); |
|
3173 if (!page) |
|
3174 return nil; |
|
3175 |
|
3176 // Match behavior of other browsers by sending a mousedown event for right clicks. |
|
3177 _private->handlingMouseDownEvent = YES; |
|
3178 page->contextMenuController()->clearContextMenu(); |
|
3179 coreFrame->eventHandler()->mouseDown(event); |
|
3180 BOOL handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(event); |
|
3181 _private->handlingMouseDownEvent = NO; |
|
3182 |
|
3183 if (!handledEvent) |
|
3184 return nil; |
|
3185 |
|
3186 // Re-get page, since it might have gone away during event handling. |
|
3187 page = coreFrame->page(); |
|
3188 if (!page) |
|
3189 return nil; |
|
3190 |
|
3191 ContextMenu* coreMenu = page->contextMenuController()->contextMenu(); |
|
3192 if (!coreMenu) |
|
3193 return nil; |
|
3194 |
|
3195 NSArray* menuItems = coreMenu->platformDescription(); |
|
3196 if (!menuItems) |
|
3197 return nil; |
|
3198 |
|
3199 NSUInteger count = [menuItems count]; |
|
3200 if (!count) |
|
3201 return nil; |
|
3202 |
|
3203 NSMenu* menu = [[[NSMenu alloc] init] autorelease]; |
|
3204 for (NSUInteger i = 0; i < count; i++) |
|
3205 [menu addItem:[menuItems objectAtIndex:i]]; |
|
3206 return menu; |
|
3207 } |
|
3208 |
|
3209 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag |
|
3210 { |
|
3211 return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO]; |
|
3212 } |
|
3213 |
|
3214 - (void)clearFocus |
|
3215 { |
|
3216 Frame* coreFrame = core([self _frame]); |
|
3217 if (!coreFrame) |
|
3218 return; |
|
3219 Document* document = coreFrame->document(); |
|
3220 if (!document) |
|
3221 return; |
|
3222 |
|
3223 document->setFocusedNode(0); |
|
3224 } |
|
3225 |
|
3226 - (BOOL)isOpaque |
|
3227 { |
|
3228 return [[self _webView] drawsBackground]; |
|
3229 } |
|
3230 |
|
3231 #if !LOG_DISABLED |
|
3232 - (void)setNeedsDisplay:(BOOL)flag |
|
3233 { |
|
3234 LOG(View, "%@ setNeedsDisplay:%@", self, flag ? @"YES" : @"NO"); |
|
3235 [super setNeedsDisplay:flag]; |
|
3236 } |
|
3237 #endif |
|
3238 |
|
3239 #ifndef BUILDING_ON_TIGER |
|
3240 - (void)setNeedsDisplayInRect:(NSRect)invalidRect |
|
3241 { |
|
3242 if (_private->inScrollPositionChanged) { |
|
3243 // When scrolling, the dirty regions are adjusted for the scroll only |
|
3244 // after NSViewBoundsDidChangeNotification is sent. Translate the invalid |
|
3245 // rect to pre-scrolled coordinates in order to get the right dirty region |
|
3246 // after adjustment. See <rdar://problem/7678927>. |
|
3247 NSPoint origin = [[self superview] bounds].origin; |
|
3248 invalidRect.origin.x -= _private->lastScrollPosition.x - origin.x; |
|
3249 invalidRect.origin.y -= _private->lastScrollPosition.y - origin.y; |
|
3250 } |
|
3251 [super setNeedsDisplayInRect:invalidRect]; |
|
3252 } |
|
3253 #endif |
|
3254 |
|
3255 - (void)setNeedsLayout: (BOOL)flag |
|
3256 { |
|
3257 LOG(View, "%@ setNeedsLayout:%@", self, flag ? @"YES" : @"NO"); |
|
3258 if (!flag) |
|
3259 return; // There's no way to say you don't need a layout. |
|
3260 if (Frame* frame = core([self _frame])) { |
|
3261 if (frame->document() && frame->document()->inPageCache()) |
|
3262 return; |
|
3263 if (FrameView* view = frame->view()) |
|
3264 view->setNeedsLayout(); |
|
3265 } |
|
3266 } |
|
3267 |
|
3268 - (void)setNeedsToApplyStyles: (BOOL)flag |
|
3269 { |
|
3270 LOG(View, "%@ setNeedsToApplyStyles:%@", self, flag ? @"YES" : @"NO"); |
|
3271 _private->needsToApplyStyles = flag; |
|
3272 } |
|
3273 |
|
3274 - (void)drawSingleRect:(NSRect)rect |
|
3275 { |
|
3276 [NSGraphicsContext saveGraphicsState]; |
|
3277 NSRectClip(rect); |
|
3278 |
|
3279 ASSERT([[self superview] isKindOfClass:[WebClipView class]]); |
|
3280 |
|
3281 [(WebClipView *)[self superview] setAdditionalClip:rect]; |
|
3282 |
|
3283 @try { |
|
3284 if ([self _transparentBackground]) { |
|
3285 [[NSColor clearColor] set]; |
|
3286 NSRectFill (rect); |
|
3287 } |
|
3288 |
|
3289 [[self _frame] _drawRect:rect contentsOnly:YES]; |
|
3290 |
|
3291 WebView *webView = [self _webView]; |
|
3292 |
|
3293 // This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be |
|
3294 // called after the WebView has closed. If the client did not properly close the WebView and set the |
|
3295 // UIDelegate to nil, then the UIDelegate will be stale and this code will crash. |
|
3296 static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK); |
|
3297 if (version3OrLaterClient) |
|
3298 [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]]; |
|
3299 |
|
3300 if (WebNodeHighlight *currentHighlight = [webView currentNodeHighlight]) |
|
3301 [currentHighlight setNeedsUpdateInTargetViewRect:[self convertRect:rect toView:[currentHighlight targetView]]]; |
|
3302 |
|
3303 [(WebClipView *)[self superview] resetAdditionalClip]; |
|
3304 |
|
3305 [NSGraphicsContext restoreGraphicsState]; |
|
3306 } @catch (NSException *localException) { |
|
3307 [(WebClipView *)[self superview] resetAdditionalClip]; |
|
3308 [NSGraphicsContext restoreGraphicsState]; |
|
3309 LOG_ERROR("Exception caught while drawing: %@", localException); |
|
3310 [localException raise]; |
|
3311 } |
|
3312 } |
|
3313 |
|
3314 - (void)drawRect:(NSRect)rect |
|
3315 { |
|
3316 ASSERT_MAIN_THREAD(); |
|
3317 LOG(View, "%@ drawing", self); |
|
3318 |
|
3319 const NSRect *rects; |
|
3320 NSInteger count; |
|
3321 [self getRectsBeingDrawn:&rects count:&count]; |
|
3322 |
|
3323 BOOL subviewsWereSetAside = _private->subviewsSetAside; |
|
3324 if (subviewsWereSetAside) |
|
3325 [self _restoreSubviews]; |
|
3326 |
|
3327 #ifdef LOG_TIMES |
|
3328 double start = CFAbsoluteTimeGetCurrent(); |
|
3329 #endif |
|
3330 |
|
3331 WebView *webView = [self _webView]; |
|
3332 if ([webView _mustDrawUnionedRect:rect singleRects:rects count:count]) |
|
3333 [self drawSingleRect:rect]; |
|
3334 else |
|
3335 for (int i = 0; i < count; ++i) |
|
3336 [self drawSingleRect:rects[i]]; |
|
3337 |
|
3338 #ifdef LOG_TIMES |
|
3339 double thisTime = CFAbsoluteTimeGetCurrent() - start; |
|
3340 LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime); |
|
3341 #endif |
|
3342 |
|
3343 if (subviewsWereSetAside) |
|
3344 [self _setAsideSubviews]; |
|
3345 |
|
3346 #if USE(ACCELERATED_COMPOSITING) |
|
3347 // Only do the synchronization dance if we're drawing into the window, otherwise |
|
3348 // we risk disabling screen updates when no flush is pending. |
|
3349 if ([NSGraphicsContext currentContext] == [[self window] graphicsContext] && [webView _needsOneShotDrawingSynchronization]) { |
|
3350 // Disable screen updates to minimize the chances of the race between the CA |
|
3351 // display link and AppKit drawing causing flashes. |
|
3352 [[self window] disableScreenUpdatesUntilFlush]; |
|
3353 |
|
3354 // Make sure any layer changes that happened as a result of layout |
|
3355 // via -viewWillDraw are committed. |
|
3356 [CATransaction flush]; |
|
3357 [webView _setNeedsOneShotDrawingSynchronization:NO]; |
|
3358 } |
|
3359 #endif |
|
3360 } |
|
3361 |
|
3362 // Turn off the additional clip while computing our visibleRect. |
|
3363 - (NSRect)visibleRect |
|
3364 { |
|
3365 if (!([[self superview] isKindOfClass:[WebClipView class]])) |
|
3366 return [super visibleRect]; |
|
3367 |
|
3368 WebClipView *clipView = (WebClipView *)[self superview]; |
|
3369 |
|
3370 BOOL hasAdditionalClip = [clipView hasAdditionalClip]; |
|
3371 if (!hasAdditionalClip) { |
|
3372 return [super visibleRect]; |
|
3373 } |
|
3374 |
|
3375 NSRect additionalClip = [clipView additionalClip]; |
|
3376 [clipView resetAdditionalClip]; |
|
3377 NSRect visibleRect = [super visibleRect]; |
|
3378 [clipView setAdditionalClip:additionalClip]; |
|
3379 return visibleRect; |
|
3380 } |
|
3381 |
|
3382 - (BOOL)isFlipped |
|
3383 { |
|
3384 return YES; |
|
3385 } |
|
3386 |
|
3387 - (void)windowDidBecomeKey:(NSNotification *)notification |
|
3388 { |
|
3389 if (!pthread_main_np()) { |
|
3390 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO]; |
|
3391 return; |
|
3392 } |
|
3393 |
|
3394 NSWindow *keyWindow = [notification object]; |
|
3395 |
|
3396 if (keyWindow == [self window]) |
|
3397 [self addMouseMovedObserver]; |
|
3398 } |
|
3399 |
|
3400 - (void)windowDidResignKey:(NSNotification *)notification |
|
3401 { |
|
3402 if (!pthread_main_np()) { |
|
3403 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO]; |
|
3404 return; |
|
3405 } |
|
3406 |
|
3407 NSWindow *formerKeyWindow = [notification object]; |
|
3408 |
|
3409 if (formerKeyWindow == [self window]) |
|
3410 [self removeMouseMovedObserver]; |
|
3411 |
|
3412 if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) |
|
3413 [_private->completionController endRevertingChange:NO moveLeft:NO]; |
|
3414 } |
|
3415 |
|
3416 - (void)windowWillClose:(NSNotification *)notification |
|
3417 { |
|
3418 if (!pthread_main_np()) { |
|
3419 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO]; |
|
3420 return; |
|
3421 } |
|
3422 |
|
3423 [_private->completionController endRevertingChange:NO moveLeft:NO]; |
|
3424 [[self _pluginController] destroyAllPlugins]; |
|
3425 } |
|
3426 |
|
3427 - (void)scrollWheel:(NSEvent *)event |
|
3428 { |
|
3429 // There's a chance that responding to this event will run a nested event loop, and |
|
3430 // fetching a new event might release the old one. Retaining and then autoreleasing |
|
3431 // the current event prevents that from causing a problem inside WebKit or AppKit code. |
|
3432 [[event retain] autorelease]; |
|
3433 |
|
3434 Frame* frame = core([self _frame]); |
|
3435 if (!frame || !frame->eventHandler()->wheelEvent(event)) |
|
3436 [super scrollWheel:event]; |
|
3437 } |
|
3438 |
|
3439 - (BOOL)_isSelectionEvent:(NSEvent *)event |
|
3440 { |
|
3441 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; |
|
3442 return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue]; |
|
3443 } |
|
3444 |
|
3445 - (BOOL)_isScrollBarEvent:(NSEvent *)event |
|
3446 { |
|
3447 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; |
|
3448 return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsInScrollBarKey] boolValue]; |
|
3449 } |
|
3450 |
|
3451 - (BOOL)acceptsFirstMouse:(NSEvent *)event |
|
3452 { |
|
3453 // There's a chance that responding to this event will run a nested event loop, and |
|
3454 // fetching a new event might release the old one. Retaining and then autoreleasing |
|
3455 // the current event prevents that from causing a problem inside WebKit or AppKit code. |
|
3456 [[event retain] autorelease]; |
|
3457 |
|
3458 NSView *hitView = [self _hitViewForEvent:event]; |
|
3459 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil; |
|
3460 |
|
3461 #if ENABLE(DASHBOARD_SUPPORT) |
|
3462 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse]) |
|
3463 return YES; |
|
3464 #endif |
|
3465 |
|
3466 if (hitHTMLView) { |
|
3467 bool result = false; |
|
3468 if (Frame* coreFrame = core([hitHTMLView _frame])) { |
|
3469 coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]); |
|
3470 [hitHTMLView _setMouseDownEvent:event]; |
|
3471 if ([hitHTMLView _isSelectionEvent:event]) |
|
3472 result = coreFrame->eventHandler()->eventMayStartDrag(event); |
|
3473 else if ([hitHTMLView _isScrollBarEvent:event]) |
|
3474 result = true; |
|
3475 [hitHTMLView _setMouseDownEvent:nil]; |
|
3476 } |
|
3477 return result; |
|
3478 } |
|
3479 return [hitView acceptsFirstMouse:event]; |
|
3480 } |
|
3481 |
|
3482 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event |
|
3483 { |
|
3484 // There's a chance that responding to this event will run a nested event loop, and |
|
3485 // fetching a new event might release the old one. Retaining and then autoreleasing |
|
3486 // the current event prevents that from causing a problem inside WebKit or AppKit code. |
|
3487 [[event retain] autorelease]; |
|
3488 |
|
3489 NSView *hitView = [self _hitViewForEvent:event]; |
|
3490 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil; |
|
3491 if (hitHTMLView) { |
|
3492 bool result = false; |
|
3493 if ([hitHTMLView _isSelectionEvent:event]) { |
|
3494 if (Frame* coreFrame = core([hitHTMLView _frame])) { |
|
3495 [hitHTMLView _setMouseDownEvent:event]; |
|
3496 result = coreFrame->eventHandler()->eventMayStartDrag(event); |
|
3497 [hitHTMLView _setMouseDownEvent:nil]; |
|
3498 } |
|
3499 } |
|
3500 return result; |
|
3501 } |
|
3502 return [hitView shouldDelayWindowOrderingForEvent:event]; |
|
3503 } |
|
3504 |
|
3505 - (void)mouseDown:(NSEvent *)event |
|
3506 { |
|
3507 // There's a chance that responding to this event will run a nested event loop, and |
|
3508 // fetching a new event might release the old one. Retaining and then autoreleasing |
|
3509 // the current event prevents that from causing a problem inside WebKit or AppKit code. |
|
3510 [[event retain] autorelease]; |
|
3511 |
|
3512 RetainPtr<WebHTMLView> protector = self; |
|
3513 if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event]) |
|
3514 return; |
|
3515 |
|
3516 _private->handlingMouseDownEvent = YES; |
|
3517 |
|
3518 // Record the mouse down position so we can determine drag hysteresis. |
|
3519 [self _setMouseDownEvent:event]; |
|
3520 |
|
3521 NSInputManager *currentInputManager = [NSInputManager currentInputManager]; |
|
3522 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) |
|
3523 goto done; |
|
3524 |
|
3525 [_private->completionController endRevertingChange:NO moveLeft:NO]; |
|
3526 |
|
3527 // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here. |
|
3528 // We don't want to pass them along to KHTML a second time. |
|
3529 if (!([event modifierFlags] & NSControlKeyMask)) { |
|
3530 _private->ignoringMouseDraggedEvents = NO; |
|
3531 |
|
3532 // Don't do any mouseover while the mouse is down. |
|
3533 [self _cancelUpdateMouseoverTimer]; |
|
3534 |
|
3535 // Let WebCore get a chance to deal with the event. This will call back to us |
|
3536 // to start the autoscroll timer if appropriate. |
|
3537 if (Frame* coreframe = core([self _frame])) |
|
3538 coreframe->eventHandler()->mouseDown(event); |
|
3539 } |
|
3540 |
|
3541 done: |
|
3542 _private->handlingMouseDownEvent = NO; |
|
3543 } |
|
3544 |
|
3545 - (void)dragImage:(NSImage *)dragImage |
|
3546 at:(NSPoint)at |
|
3547 offset:(NSSize)offset |
|
3548 event:(NSEvent *)event |
|
3549 pasteboard:(NSPasteboard *)pasteboard |
|
3550 source:(id)source |
|
3551 slideBack:(BOOL)slideBack |
|
3552 { |
|
3553 ASSERT(self == [self _topHTMLView]); |
|
3554 [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack]; |
|
3555 } |
|
3556 |
|
3557 - (void)mouseDragged:(NSEvent *)event |
|
3558 { |
|
3559 // There's a chance that responding to this event will run a nested event loop, and |
|
3560 // fetching a new event might release the old one. Retaining and then autoreleasing |
|
3561 // the current event prevents that from causing a problem inside WebKit or AppKit code. |
|
3562 [[event retain] autorelease]; |
|
3563 |
|
3564 NSInputManager *currentInputManager = [NSInputManager currentInputManager]; |
|
3565 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) |
|
3566 return; |
|
3567 |
|
3568 [self retain]; |
|
3569 |
|
3570 if (!_private->ignoringMouseDraggedEvents) { |
|
3571 if (Frame* frame = core([self _frame])) { |
|
3572 if (Page* page = frame->page()) |
|
3573 page->mainFrame()->eventHandler()->mouseDragged(event); |
|
3574 } |
|
3575 } |
|
3576 |
|
3577 [self release]; |
|
3578 } |
|
3579 |
|
3580 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal |
|
3581 { |
|
3582 ASSERT(![self _webView] || [self _isTopHTMLView]); |
|
3583 |
|
3584 Page* page = core([self _webView]); |
|
3585 if (!page) |
|
3586 return NSDragOperationNone; |
|
3587 |
|
3588 return (NSDragOperation)page->dragController()->sourceDragOperation(); |
|
3589 } |
|
3590 |
|
3591 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation |
|
3592 { |
|
3593 ASSERT(![self _webView] || [self _isTopHTMLView]); |
|
3594 |
|
3595 NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint]; |
|
3596 NSPoint windowMouseLoc = windowImageLoc; |
|
3597 |
|
3598 if (Page* page = core([self _webView])) { |
|
3599 DragController* dragController = page->dragController(); |
|
3600 windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y()); |
|
3601 dragController->dragEnded(); |
|
3602 } |
|
3603 |
|
3604 [[self _frame] _dragSourceEndedAt:windowMouseLoc operation:operation]; |
|
3605 |
|
3606 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event. |
|
3607 _private->ignoringMouseDraggedEvents = YES; |
|
3608 |
|
3609 // Once the dragging machinery kicks in, we no longer get mouse drags or the up event. |
|
3610 // WebCore expects to get balanced down/up's, so we must fake up a mouseup. |
|
3611 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp |
|
3612 location:windowMouseLoc |
|
3613 modifierFlags:[[NSApp currentEvent] modifierFlags] |
|
3614 timestamp:[NSDate timeIntervalSinceReferenceDate] |
|
3615 windowNumber:[[self window] windowNumber] |
|
3616 context:[[NSApp currentEvent] context] |
|
3617 eventNumber:0 clickCount:0 pressure:0]; |
|
3618 [self mouseUp:fakeEvent]; // This will also update the mouseover state. |
|
3619 } |
|
3620 |
|
3621 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination |
|
3622 { |
|
3623 NSFileWrapper *wrapper = nil; |
|
3624 NSURL *draggingImageURL = nil; |
|
3625 |
|
3626 if (WebCore::CachedImage* tiffResource = [self promisedDragTIFFDataSource]) { |
|
3627 |
|
3628 SharedBuffer *buffer = static_cast<CachedResource*>(tiffResource)->data(); |
|
3629 if (!buffer) |
|
3630 goto noPromisedData; |
|
3631 |
|
3632 NSData *data = buffer->createNSData(); |
|
3633 NSURLResponse *response = tiffResource->response().nsURLResponse(); |
|
3634 draggingImageURL = [response URL]; |
|
3635 wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease]; |
|
3636 NSString* filename = [response suggestedFilename]; |
|
3637 NSString* trueExtension(tiffResource->image()->filenameExtension()); |
|
3638 if (!matchesExtensionOrEquivalent(filename, trueExtension)) |
|
3639 filename = [[filename stringByAppendingString:@"."] stringByAppendingString:trueExtension]; |
|
3640 [wrapper setPreferredFilename:filename]; |
|
3641 } |
|
3642 |
|
3643 noPromisedData: |
|
3644 |
|
3645 if (!wrapper) { |
|
3646 ASSERT(![self _webView] || [self _isTopHTMLView]); |
|
3647 Page* page = core([self _webView]); |
|
3648 |
|
3649 //If a load occurs midway through a drag, the view may be detached, which gives |
|
3650 //us no ability to get to the original Page, so we cannot access any drag state |
|
3651 //FIXME: is there a way to recover? |
|
3652 if (!page) |
|
3653 return nil; |
|
3654 |
|
3655 const KURL& imageURL = page->dragController()->draggingImageURL(); |
|
3656 ASSERT(!imageURL.isEmpty()); |
|
3657 draggingImageURL = imageURL; |
|
3658 |
|
3659 wrapper = [[self _dataSource] _fileWrapperForURL:draggingImageURL]; |
|
3660 } |
|
3661 |
|
3662 if (wrapper == nil) { |
|
3663 LOG_ERROR("Failed to create image file."); |
|
3664 return nil; |
|
3665 } |
|
3666 |
|
3667 // FIXME: Report an error if we fail to create a file. |
|
3668 NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]]; |
|
3669 path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path]; |
|
3670 if (![wrapper writeToFile:path atomically:NO updateFilenames:YES]) |
|
3671 LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]"); |
|
3672 |
|
3673 if (draggingImageURL) |
|
3674 [[NSFileManager defaultManager] _webkit_setMetadataURL:[draggingImageURL absoluteString] referrer:nil atPath:path]; |
|
3675 |
|
3676 return [NSArray arrayWithObject:[path lastPathComponent]]; |
|
3677 } |
|
3678 |
|
3679 - (void)mouseUp:(NSEvent *)event |
|
3680 { |
|
3681 // There's a chance that responding to this event will run a nested event loop, and |
|
3682 // fetching a new event might release the old one. Retaining and then autoreleasing |
|
3683 // the current event prevents that from causing a problem inside WebKit or AppKit code. |
|
3684 [[event retain] autorelease]; |
|
3685 |
|
3686 [self _setMouseDownEvent:nil]; |
|
3687 |
|
3688 NSInputManager *currentInputManager = [NSInputManager currentInputManager]; |
|
3689 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) |
|
3690 return; |
|
3691 |
|
3692 [self retain]; |
|
3693 |
|
3694 [self _stopAutoscrollTimer]; |
|
3695 if (Frame* frame = core([self _frame])) { |
|
3696 if (Page* page = frame->page()) |
|
3697 page->mainFrame()->eventHandler()->mouseUp(event); |
|
3698 } |
|
3699 [self _updateMouseoverWithFakeEvent]; |
|
3700 |
|
3701 [self release]; |
|
3702 } |
|
3703 |
|
3704 - (void)mouseMovedNotification:(NSNotification *)notification |
|
3705 { |
|
3706 [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]]; |
|
3707 } |
|
3708 |
|
3709 // returning YES from this method is the way we tell AppKit that it is ok for this view |
|
3710 // to be in the key loop even when "tab to all controls" is not on. |
|
3711 - (BOOL)needsPanelToBecomeKey |
|
3712 { |
|
3713 return YES; |
|
3714 } |
|
3715 |
|
3716 // Utility function to make sure we don't return anything through the NSTextInput |
|
3717 // API when an editable region is not currently focused. |
|
3718 static BOOL isTextInput(Frame* coreFrame) |
|
3719 { |
|
3720 return coreFrame && !coreFrame->selection()->isNone() && coreFrame->selection()->isContentEditable(); |
|
3721 } |
|
3722 |
|
3723 static BOOL isInPasswordField(Frame* coreFrame) |
|
3724 { |
|
3725 return coreFrame && coreFrame->selection()->isInPasswordField(); |
|
3726 } |
|
3727 |
|
3728 - (BOOL)becomeFirstResponder |
|
3729 { |
|
3730 NSSelectionDirection direction = NSDirectSelection; |
|
3731 if (![[self _webView] _isPerformingProgrammaticFocus]) |
|
3732 direction = [[self window] keyViewSelectionDirection]; |
|
3733 |
|
3734 [self _updateFontPanel]; |
|
3735 |
|
3736 Frame* frame = core([self _frame]); |
|
3737 if (!frame) |
|
3738 return YES; |
|
3739 |
|
3740 BOOL exposeInputContext = isTextInput(frame) && !isInPasswordField(frame); |
|
3741 if (exposeInputContext != _private->exposeInputContext) { |
|
3742 _private->exposeInputContext = exposeInputContext; |
|
3743 [NSApp updateWindows]; |
|
3744 } |
|
3745 |
|
3746 frame->editor()->setStartNewKillRingSequence(true); |
|
3747 |
|
3748 Page* page = frame->page(); |
|
3749 if (!page) |
|
3750 return YES; |
|
3751 |
|
3752 if (![[self _webView] _isPerformingProgrammaticFocus]) |
|
3753 page->focusController()->setFocusedFrame(frame); |
|
3754 |
|
3755 page->focusController()->setFocused(true); |
|
3756 |
|
3757 if (direction == NSDirectSelection) |
|
3758 return YES; |
|
3759 |
|
3760 if (Document* document = frame->document()) |
|
3761 document->setFocusedNode(0); |
|
3762 page->focusController()->setInitialFocus(direction == NSSelectingNext ? FocusDirectionForward : FocusDirectionBackward, |
|
3763 frame->eventHandler()->currentKeyboardEvent().get()); |
|
3764 return YES; |
|
3765 } |
|
3766 |
|
3767 - (BOOL)resignFirstResponder |
|
3768 { |
|
3769 BOOL resign = [super resignFirstResponder]; |
|
3770 if (resign) { |
|
3771 [_private->completionController endRevertingChange:NO moveLeft:NO]; |
|
3772 Frame* coreFrame = core([self _frame]); |
|
3773 if (!coreFrame) |
|
3774 return resign; |
|
3775 Page* page = coreFrame->page(); |
|
3776 if (!page) |
|
3777 return resign; |
|
3778 if (![self maintainsInactiveSelection]) { |
|
3779 [self deselectAll]; |
|
3780 if (![[self _webView] _isPerformingProgrammaticFocus]) |
|
3781 [self clearFocus]; |
|
3782 } |
|
3783 |
|
3784 id nextResponder = [[self window] _newFirstResponderAfterResigning]; |
|
3785 bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]] |
|
3786 && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]]; |
|
3787 if (!nextResponderIsInWebView) |
|
3788 page->focusController()->setFocused(false); |
|
3789 } |
|
3790 return resign; |
|
3791 } |
|
3792 |
|
3793 - (void)setDataSource:(WebDataSource *)dataSource |
|
3794 { |
|
3795 ASSERT(dataSource); |
|
3796 if (_private->dataSource != dataSource) { |
|
3797 ASSERT(!_private->closed); |
|
3798 BOOL hadDataSource = _private->dataSource != nil; |
|
3799 |
|
3800 [dataSource retain]; |
|
3801 [_private->dataSource release]; |
|
3802 _private->dataSource = dataSource; |
|
3803 [_private->pluginController setDataSource:dataSource]; |
|
3804 |
|
3805 if (!hadDataSource) |
|
3806 [self addMouseMovedObserver]; |
|
3807 } |
|
3808 } |
|
3809 |
|
3810 - (void)dataSourceUpdated:(WebDataSource *)dataSource |
|
3811 { |
|
3812 } |
|
3813 |
|
3814 // This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes |
|
3815 // key. WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme |
|
3816 // renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly). |
|
3817 - (void)updateCell:(NSCell*)cell |
|
3818 { |
|
3819 } |
|
3820 |
|
3821 // Does setNeedsDisplay:NO as a side effect when printing is ending. |
|
3822 // pageWidth != 0 implies we will relayout to a new width |
|
3823 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize |
|
3824 { |
|
3825 WebFrame *frame = [self _frame]; |
|
3826 NSArray *subframes = [frame childFrames]; |
|
3827 unsigned n = [subframes count]; |
|
3828 unsigned i; |
|
3829 for (i = 0; i != n; ++i) { |
|
3830 WebFrame *subframe = [subframes objectAtIndex:i]; |
|
3831 WebFrameView *frameView = [subframe frameView]; |
|
3832 if ([[subframe _dataSource] _isDocumentHTML]) { |
|
3833 [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:adjustViewSize]; |
|
3834 } |
|
3835 } |
|
3836 |
|
3837 if (printing || _private->printing) { |
|
3838 [_private->pageRects release]; |
|
3839 _private->pageRects = nil; |
|
3840 _private->printing = printing; |
|
3841 if (!printing) |
|
3842 _private->avoidingPrintOrphan = NO; |
|
3843 [self setNeedsToApplyStyles:YES]; |
|
3844 [self setNeedsLayout:YES]; |
|
3845 [self layoutToMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize]; |
|
3846 if (!printing) { |
|
3847 // Can't do this when starting printing or nested printing won't work, see 3491427. |
|
3848 [self setNeedsDisplay:NO]; |
|
3849 } |
|
3850 } |
|
3851 } |
|
3852 |
|
3853 - (BOOL)canPrintHeadersAndFooters |
|
3854 { |
|
3855 return YES; |
|
3856 } |
|
3857 |
|
3858 // This is needed for the case where the webview is embedded in the view that's being printed. |
|
3859 // It shouldn't be called when the webview is being printed directly. |
|
3860 - (void)adjustPageHeightNew:(CGFloat *)newBottom top:(CGFloat)oldTop bottom:(CGFloat)oldBottom limit:(CGFloat)bottomLimit |
|
3861 { |
|
3862 // This helps when we print as part of a larger print process. |
|
3863 // If the WebHTMLView itself is what we're printing, then we will never have to do this. |
|
3864 BOOL wasInPrintingMode = _private->printing; |
|
3865 if (!wasInPrintingMode) |
|
3866 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO]; |
|
3867 |
|
3868 *newBottom = [self _adjustedBottomOfPageWithTop:oldTop bottom:oldBottom limit:bottomLimit]; |
|
3869 |
|
3870 if (!wasInPrintingMode) { |
|
3871 NSPrintOperation *currenPrintOperation = [NSPrintOperation currentOperation]; |
|
3872 if (currenPrintOperation) |
|
3873 // delay _setPrinting:NO until back to main loop as this method may get called repeatedly |
|
3874 [self performSelector:@selector(_delayedEndPrintMode:) withObject:currenPrintOperation afterDelay:0]; |
|
3875 else |
|
3876 // not sure if this is actually ever invoked, it probably shouldn't be |
|
3877 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO]; |
|
3878 } |
|
3879 } |
|
3880 |
|
3881 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation |
|
3882 { |
|
3883 float viewWidth = NSWidth([self bounds]); |
|
3884 if (viewWidth < 1) { |
|
3885 LOG_ERROR("%@ has no width when printing", self); |
|
3886 return 1.0f; |
|
3887 } |
|
3888 |
|
3889 float userScaleFactor = [printOperation _web_pageSetupScaleFactor]; |
|
3890 float maxShrinkToFitScaleFactor = 1.0f / _WebHTMLViewPrintingMaximumShrinkFactor; |
|
3891 float shrinkToFitScaleFactor = [printOperation _web_availablePaperWidth] / viewWidth; |
|
3892 float shrinkToAvoidOrphan = _private->avoidingPrintOrphan ? (1.0f / PrintingOrphanShrinkAdjustment) : 1.0f; |
|
3893 return userScaleFactor * max(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor) * shrinkToAvoidOrphan; |
|
3894 } |
|
3895 |
|
3896 // FIXME 3491344: This is a secret AppKit-internal method that we need to override in order |
|
3897 // to get our shrink-to-fit to work with a custom pagination scheme. We can do this better |
|
3898 // if AppKit makes it SPI/API. |
|
3899 - (CGFloat)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation |
|
3900 { |
|
3901 return [self _scaleFactorForPrintOperation:printOperation]; |
|
3902 } |
|
3903 |
|
3904 // This is used for Carbon printing. At some point we might want to make this public API. |
|
3905 - (void)setPageWidthForPrinting:(float)pageWidth |
|
3906 { |
|
3907 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO]; |
|
3908 [self _setPrinting:YES minimumPageWidth:pageWidth maximumPageWidth:pageWidth adjustViewSize:YES]; |
|
3909 } |
|
3910 |
|
3911 - (void)_endPrintModeAndRestoreWindowAutodisplay |
|
3912 { |
|
3913 [self _endPrintMode]; |
|
3914 [[self window] setAutodisplay:YES]; |
|
3915 } |
|
3916 |
|
3917 - (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation |
|
3918 { |
|
3919 ASSERT_ARG(initiatingOperation, initiatingOperation != nil); |
|
3920 NSPrintOperation *currentOperation = [NSPrintOperation currentOperation]; |
|
3921 if (initiatingOperation == currentOperation) { |
|
3922 // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're |
|
3923 // being extra paranoid here since the printing code is so fragile. Delay the cleanup |
|
3924 // further. |
|
3925 ASSERT_NOT_REACHED(); |
|
3926 [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0]; |
|
3927 } else if ([currentOperation view] == self) { |
|
3928 // A new print job has started, but it is printing the same WebHTMLView again. We don't expect |
|
3929 // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so |
|
3930 // fragile. Do nothing, because we don't want to break the print job currently in progress, and |
|
3931 // the print job currently in progress is responsible for its own cleanup. |
|
3932 ASSERT_NOT_REACHED(); |
|
3933 } else { |
|
3934 // The print job that kicked off this delayed call has finished, and this view is not being |
|
3935 // printed again. We expect that no other print job has started. Since this delayed call wasn't |
|
3936 // cancelled, beginDocument and endDocument must not have been called, and we need to clean up |
|
3937 // the print mode here. |
|
3938 ASSERT(currentOperation == nil); |
|
3939 [self _endPrintModeAndRestoreWindowAutodisplay]; |
|
3940 } |
|
3941 } |
|
3942 |
|
3943 // Return the number of pages available for printing |
|
3944 - (BOOL)knowsPageRange:(NSRangePointer)range |
|
3945 { |
|
3946 // Must do this explicit display here, because otherwise the view might redisplay while the print |
|
3947 // sheet was up, using printer fonts (and looking different). |
|
3948 [self displayIfNeeded]; |
|
3949 [[self window] setAutodisplay:NO]; |
|
3950 |
|
3951 NSPrintOperation *printOperation = [NSPrintOperation currentOperation]; |
|
3952 if (![self _beginPrintModeWithPageWidth:[printOperation _web_availablePaperWidth] shrinkToFit:YES]) |
|
3953 return NO; |
|
3954 |
|
3955 // Certain types of errors, including invalid page ranges, can cause beginDocument and |
|
3956 // endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases |
|
3957 // we need to get out of print mode without relying on any more callbacks from the printing mechanism. |
|
3958 // If we get as far as beginDocument without trouble, then this delayed request will be cancelled. |
|
3959 // If not cancelled, this delayed call will be invoked in the next pass through the main event loop, |
|
3960 // which is after beginDocument and endDocument would be called. |
|
3961 [self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0]; |
|
3962 [[self _webView] _adjustPrintingMarginsForHeaderAndFooter]; |
|
3963 |
|
3964 // There is a theoretical chance that someone could do some drawing between here and endDocument, |
|
3965 // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because |
|
3966 // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari. |
|
3967 |
|
3968 range->location = 1; |
|
3969 float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation]; |
|
3970 float userScaleFactor = [printOperation _web_pageSetupScaleFactor]; |
|
3971 [_private->pageRects release]; |
|
3972 float fullPageHeight = floorf([printOperation _web_availablePaperHeight] / totalScaleFactor); |
|
3973 WebFrame *frame = [self _frame]; |
|
3974 NSArray *newPageRects = [frame _computePageRectsWithPrintWidthScaleFactor:userScaleFactor printHeight:fullPageHeight]; |
|
3975 |
|
3976 // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we |
|
3977 // hit that case we'll pass along a degenerate 1 pixel square to print. This will print |
|
3978 // a blank page (with correct-looking header and footer if that option is on), which matches |
|
3979 // the behavior of IE and Camino at least. |
|
3980 if ([newPageRects count] == 0) |
|
3981 newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]]; |
|
3982 else if ([newPageRects count] > 1) { |
|
3983 // If the last page is a short orphan, try adjusting the print height slightly to see if this will squeeze the |
|
3984 // content onto one fewer page. If it does, use the adjusted scale. If not, use the original scale. |
|
3985 float lastPageHeight = NSHeight([[newPageRects lastObject] rectValue]); |
|
3986 if (lastPageHeight/fullPageHeight < LastPrintedPageOrphanRatio) { |
|
3987 NSArray *adjustedPageRects = [frame _computePageRectsWithPrintWidthScaleFactor:userScaleFactor printHeight:fullPageHeight * PrintingOrphanShrinkAdjustment]; |
|
3988 // Use the adjusted rects only if the page count went down |
|
3989 if ([adjustedPageRects count] < [newPageRects count]) { |
|
3990 newPageRects = adjustedPageRects; |
|
3991 _private->avoidingPrintOrphan = YES; |
|
3992 } |
|
3993 } |
|
3994 } |
|
3995 |
|
3996 _private->pageRects = [newPageRects retain]; |
|
3997 |
|
3998 range->length = [_private->pageRects count]; |
|
3999 |
|
4000 return YES; |
|
4001 } |
|
4002 |
|
4003 // Return the drawing rectangle for a particular page number |
|
4004 - (NSRect)rectForPage:(NSInteger)page |
|
4005 { |
|
4006 return [[_private->pageRects objectAtIndex:page - 1] rectValue]; |
|
4007 } |
|
4008 |
|
4009 - (void)drawPageBorderWithSize:(NSSize)borderSize |
|
4010 { |
|
4011 ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize])); |
|
4012 [[self _webView] _drawHeaderAndFooter]; |
|
4013 } |
|
4014 |
|
4015 - (void)beginDocument |
|
4016 { |
|
4017 @try { |
|
4018 // From now on we'll get a chance to call _endPrintMode in either beginDocument or |
|
4019 // endDocument, so we can cancel the "just in case" pending call. |
|
4020 [NSObject cancelPreviousPerformRequestsWithTarget:self |
|
4021 selector:@selector(_delayedEndPrintMode:) |
|
4022 object:[NSPrintOperation currentOperation]]; |
|
4023 [super beginDocument]; |
|
4024 } @catch (NSException *localException) { |
|
4025 // Exception during [super beginDocument] means that endDocument will not get called, |
|
4026 // so we need to clean up our "print mode" here. |
|
4027 [self _endPrintModeAndRestoreWindowAutodisplay]; |
|
4028 } |
|
4029 } |
|
4030 |
|
4031 - (void)endDocument |
|
4032 { |
|
4033 [super endDocument]; |
|
4034 // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO |
|
4035 [self _endPrintModeAndRestoreWindowAutodisplay]; |
|
4036 } |
|
4037 |
|
4038 - (void)keyDown:(NSEvent *)event |
|
4039 { |
|
4040 // There's a chance that responding to this event will run a nested event loop, and |
|
4041 // fetching a new event might release the old one. Retaining and then autoreleasing |
|
4042 // the current event prevents that from causing a problem inside WebKit or AppKit code. |
|
4043 [[event retain] autorelease]; |
|
4044 |
|
4045 RetainPtr<WebHTMLView> selfProtector = self; |
|
4046 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event); |
|
4047 |
|
4048 BOOL callSuper = NO; |
|
4049 |
|
4050 [_private->keyDownEvent release]; |
|
4051 _private->keyDownEvent = [event retain]; |
|
4052 |
|
4053 BOOL completionPopupWasOpen = _private->completionController && [_private->completionController popupWindowIsOpen]; |
|
4054 Frame* coreFrame = core([self _frame]); |
|
4055 if (!eventWasSentToWebCore && coreFrame && coreFrame->eventHandler()->keyEvent(event)) { |
|
4056 // WebCore processed a key event, bail on any preexisting complete: UI |
|
4057 if (completionPopupWasOpen) |
|
4058 [_private->completionController endRevertingChange:YES moveLeft:NO]; |
|
4059 } else if (!_private->completionController || ![_private->completionController filterKeyDown:event]) { |
|
4060 // Not consumed by complete: popup window |
|
4061 [_private->completionController endRevertingChange:YES moveLeft:NO]; |
|
4062 callSuper = YES; |
|
4063 } |
|
4064 if (callSuper) |
|
4065 [super keyDown:event]; |
|
4066 else |
|
4067 [NSCursor setHiddenUntilMouseMoves:YES]; |
|
4068 } |
|
4069 |
|
4070 - (void)keyUp:(NSEvent *)event |
|
4071 { |
|
4072 // There's a chance that responding to this event will run a nested event loop, and |
|
4073 // fetching a new event might release the old one. Retaining and then autoreleasing |
|
4074 // the current event prevents that from causing a problem inside WebKit or AppKit code. |
|
4075 [[event retain] autorelease]; |
|
4076 |
|
4077 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event); |
|
4078 |
|
4079 RetainPtr<WebHTMLView> selfProtector = self; |
|
4080 Frame* coreFrame = core([self _frame]); |
|
4081 if (coreFrame && !eventWasSentToWebCore) |
|
4082 coreFrame->eventHandler()->keyEvent(event); |
|
4083 else |
|
4084 [super keyUp:event]; |
|
4085 } |
|
4086 |
|
4087 - (void)flagsChanged:(NSEvent *)event |
|
4088 { |
|
4089 // There's a chance that responding to this event will run a nested event loop, and |
|
4090 // fetching a new event might release the old one. Retaining and then autoreleasing |
|
4091 // the current event prevents that from causing a problem inside WebKit or AppKit code. |
|
4092 [[event retain] autorelease]; |
|
4093 |
|
4094 Frame* coreFrame = core([self _frame]); |
|
4095 if (coreFrame) |
|
4096 coreFrame->eventHandler()->capsLockStateMayHaveChanged(); |
|
4097 |
|
4098 RetainPtr<WebHTMLView> selfProtector = self; |
|
4099 |
|
4100 unsigned short keyCode = [event keyCode]; |
|
4101 |
|
4102 //Don't make an event from the num lock and function keys |
|
4103 if (coreFrame && keyCode != 0 && keyCode != 10 && keyCode != 63) { |
|
4104 coreFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(event)); |
|
4105 return; |
|
4106 } |
|
4107 |
|
4108 [super flagsChanged:event]; |
|
4109 } |
|
4110 |
|
4111 - (id)accessibilityAttributeValue:(NSString*)attributeName |
|
4112 { |
|
4113 if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) { |
|
4114 id accTree = [[self _frame] _accessibilityTree]; |
|
4115 if (accTree) |
|
4116 return [NSArray arrayWithObject:accTree]; |
|
4117 return nil; |
|
4118 } |
|
4119 return [super accessibilityAttributeValue:attributeName]; |
|
4120 } |
|
4121 |
|
4122 - (id)accessibilityFocusedUIElement |
|
4123 { |
|
4124 id accTree = [[self _frame] _accessibilityTree]; |
|
4125 if (accTree) |
|
4126 return [accTree accessibilityFocusedUIElement]; |
|
4127 return self; |
|
4128 } |
|
4129 |
|
4130 - (id)accessibilityHitTest:(NSPoint)point |
|
4131 { |
|
4132 id accTree = [[self _frame] _accessibilityTree]; |
|
4133 if (accTree) { |
|
4134 NSPoint windowCoord = [[self window] convertScreenToBase:point]; |
|
4135 return [accTree accessibilityHitTest:[self convertPoint:windowCoord fromView:nil]]; |
|
4136 } |
|
4137 return self; |
|
4138 } |
|
4139 |
|
4140 - (id)_accessibilityParentForSubview:(NSView *)subview |
|
4141 { |
|
4142 id accTree = [[self _frame] _accessibilityTree]; |
|
4143 if (!accTree) |
|
4144 return self; |
|
4145 id parent = [accTree _accessibilityParentForSubview:subview]; |
|
4146 if (!parent) |
|
4147 return self; |
|
4148 return parent; |
|
4149 } |
|
4150 |
|
4151 - (void)centerSelectionInVisibleArea:(id)sender |
|
4152 { |
|
4153 COMMAND_PROLOGUE |
|
4154 |
|
4155 if (Frame* coreFrame = core([self _frame])) |
|
4156 coreFrame->revealSelection(ScrollAlignment::alignCenterAlways); |
|
4157 } |
|
4158 |
|
4159 - (NSData *)_selectionStartFontAttributesAsRTF |
|
4160 { |
|
4161 Frame* coreFrame = core([self _frame]); |
|
4162 NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"x" |
|
4163 attributes:coreFrame ? coreFrame->fontAttributesForSelectionStart() : nil]; |
|
4164 NSData *data = [string RTFFromRange:NSMakeRange(0, [string length]) documentAttributes:nil]; |
|
4165 [string release]; |
|
4166 return data; |
|
4167 } |
|
4168 |
|
4169 - (NSDictionary *)_fontAttributesFromFontPasteboard |
|
4170 { |
|
4171 NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard]; |
|
4172 if (fontPasteboard == nil) |
|
4173 return nil; |
|
4174 NSData *data = [fontPasteboard dataForType:NSFontPboardType]; |
|
4175 if (data == nil || [data length] == 0) |
|
4176 return nil; |
|
4177 // NSTextView does something more efficient by parsing the attributes only, but that's not available in API. |
|
4178 NSAttributedString *string = [[[NSAttributedString alloc] initWithRTF:data documentAttributes:NULL] autorelease]; |
|
4179 if (string == nil || [string length] == 0) |
|
4180 return nil; |
|
4181 return [string fontAttributesInRange:NSMakeRange(0, 1)]; |
|
4182 } |
|
4183 |
|
4184 - (DOMCSSStyleDeclaration *)_emptyStyle |
|
4185 { |
|
4186 return [[[self _frame] DOMDocument] createCSSStyleDeclaration]; |
|
4187 } |
|
4188 |
|
4189 - (NSString *)_colorAsString:(NSColor *)color |
|
4190 { |
|
4191 NSColor *rgbColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace]; |
|
4192 // FIXME: If color is non-nil and rgbColor is nil, that means we got some kind |
|
4193 // of fancy color that can't be converted to RGB. Changing that to "transparent" |
|
4194 // might not be great, but it's probably OK. |
|
4195 if (rgbColor == nil) |
|
4196 return @"transparent"; |
|
4197 float r = [rgbColor redComponent]; |
|
4198 float g = [rgbColor greenComponent]; |
|
4199 float b = [rgbColor blueComponent]; |
|
4200 float a = [rgbColor alphaComponent]; |
|
4201 if (a == 0) |
|
4202 return @"transparent"; |
|
4203 if (r == 0 && g == 0 && b == 0 && a == 1) |
|
4204 return @"black"; |
|
4205 if (r == 1 && g == 1 && b == 1 && a == 1) |
|
4206 return @"white"; |
|
4207 // FIXME: Lots more named colors. Maybe we could use the table in WebCore? |
|
4208 if (a == 1) |
|
4209 return [NSString stringWithFormat:@"rgb(%.0f,%.0f,%.0f)", r * 255, g * 255, b * 255]; |
|
4210 return [NSString stringWithFormat:@"rgba(%.0f,%.0f,%.0f,%f)", r * 255, g * 255, b * 255, a]; |
|
4211 } |
|
4212 |
|
4213 - (NSString *)_shadowAsString:(NSShadow *)shadow |
|
4214 { |
|
4215 if (shadow == nil) |
|
4216 return @"none"; |
|
4217 NSSize offset = [shadow shadowOffset]; |
|
4218 float blurRadius = [shadow shadowBlurRadius]; |
|
4219 if (offset.width == 0 && offset.height == 0 && blurRadius == 0) |
|
4220 return @"none"; |
|
4221 NSColor *color = [shadow shadowColor]; |
|
4222 if (color == nil) |
|
4223 return @"none"; |
|
4224 // FIXME: Handle non-integral values here? |
|
4225 if (blurRadius == 0) |
|
4226 return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height]; |
|
4227 return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height, blurRadius]; |
|
4228 } |
|
4229 |
|
4230 - (DOMCSSStyleDeclaration *)_styleFromFontAttributes:(NSDictionary *)dictionary |
|
4231 { |
|
4232 DOMCSSStyleDeclaration *style = [self _emptyStyle]; |
|
4233 |
|
4234 NSColor *color = [dictionary objectForKey:NSBackgroundColorAttributeName]; |
|
4235 [style setBackgroundColor:[self _colorAsString:color]]; |
|
4236 |
|
4237 NSFont *font = [dictionary objectForKey:NSFontAttributeName]; |
|
4238 if (!font) { |
|
4239 [style setFontFamily:@"Helvetica"]; |
|
4240 [style setFontSize:@"12px"]; |
|
4241 [style setFontWeight:@"normal"]; |
|
4242 [style setFontStyle:@"normal"]; |
|
4243 } else { |
|
4244 NSFontManager *fm = [NSFontManager sharedFontManager]; |
|
4245 // FIXME: Need more sophisticated escaping code if we want to handle family names |
|
4246 // with characters like single quote or backslash in their names. |
|
4247 [style setFontFamily:[NSString stringWithFormat:@"'%@'", [font familyName]]]; |
|
4248 [style setFontSize:[NSString stringWithFormat:@"%0.fpx", [font pointSize]]]; |
|
4249 // FIXME: Map to the entire range of CSS weight values. |
|
4250 if ([fm weightOfFont:font] >= MIN_BOLD_WEIGHT) |
|
4251 [style setFontWeight:@"bold"]; |
|
4252 else |
|
4253 [style setFontWeight:@"normal"]; |
|
4254 if ([fm traitsOfFont:font] & NSItalicFontMask) |
|
4255 [style setFontStyle:@"italic"]; |
|
4256 else |
|
4257 [style setFontStyle:@"normal"]; |
|
4258 } |
|
4259 |
|
4260 color = [dictionary objectForKey:NSForegroundColorAttributeName]; |
|
4261 [style setColor:color ? [self _colorAsString:color] : (NSString *)@"black"]; |
|
4262 |
|
4263 NSShadow *shadow = [dictionary objectForKey:NSShadowAttributeName]; |
|
4264 [style setTextShadow:[self _shadowAsString:shadow]]; |
|
4265 |
|
4266 int strikethroughInt = [[dictionary objectForKey:NSStrikethroughStyleAttributeName] intValue]; |
|
4267 |
|
4268 int superscriptInt = [[dictionary objectForKey:NSSuperscriptAttributeName] intValue]; |
|
4269 if (superscriptInt > 0) |
|
4270 [style setVerticalAlign:@"super"]; |
|
4271 else if (superscriptInt < 0) |
|
4272 [style setVerticalAlign:@"sub"]; |
|
4273 else |
|
4274 [style setVerticalAlign:@"baseline"]; |
|
4275 int underlineInt = [[dictionary objectForKey:NSUnderlineStyleAttributeName] intValue]; |
|
4276 // FIXME: Underline wins here if we have both (see bug 3790443). |
|
4277 if (strikethroughInt == NSUnderlineStyleNone && underlineInt == NSUnderlineStyleNone) |
|
4278 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""]; |
|
4279 else if (underlineInt == NSUnderlineStyleNone) |
|
4280 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""]; |
|
4281 else |
|
4282 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""]; |
|
4283 |
|
4284 return style; |
|
4285 } |
|
4286 |
|
4287 - (void)_applyStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction |
|
4288 { |
|
4289 if (Frame* coreFrame = core([self _frame])) |
|
4290 coreFrame->editor()->applyStyleToSelection(core(style), undoAction); |
|
4291 } |
|
4292 |
|
4293 - (void)_applyParagraphStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction |
|
4294 { |
|
4295 if (Frame* coreFrame = core([self _frame])) |
|
4296 coreFrame->editor()->applyParagraphStyleToSelection(core(style), undoAction); |
|
4297 } |
|
4298 |
|
4299 - (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event |
|
4300 { |
|
4301 ASSERT([self _webView]); |
|
4302 if (![[[self _webView] preferences] respectStandardStyleKeyEquivalents]) |
|
4303 return NO; |
|
4304 |
|
4305 if (![self _canEdit]) |
|
4306 return NO; |
|
4307 |
|
4308 if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask) |
|
4309 return NO; |
|
4310 |
|
4311 NSString *string = [event characters]; |
|
4312 if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) { |
|
4313 [self executeCoreCommandByName:"ToggleBold"]; |
|
4314 return YES; |
|
4315 } |
|
4316 if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) { |
|
4317 [self executeCoreCommandByName:"ToggleItalic"]; |
|
4318 return YES; |
|
4319 } |
|
4320 |
|
4321 return NO; |
|
4322 } |
|
4323 |
|
4324 - (BOOL)performKeyEquivalent:(NSEvent *)event |
|
4325 { |
|
4326 // There's a chance that responding to this event will run a nested event loop, and |
|
4327 // fetching a new event might release the old one. Retaining and then autoreleasing |
|
4328 // the current event prevents that from causing a problem inside WebKit or AppKit code. |
|
4329 [[event retain] autorelease]; |
|
4330 |
|
4331 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event); |
|
4332 BOOL ret = NO; |
|
4333 |
|
4334 [_private->keyDownEvent release]; |
|
4335 _private->keyDownEvent = [event retain]; |
|
4336 |
|
4337 [self retain]; |
|
4338 |
|
4339 // Pass command-key combos through WebCore if there is a key binding available for |
|
4340 // this event. This lets web pages have a crack at intercepting command-modified keypresses. |
|
4341 // But don't do it if we have already handled the event. |
|
4342 // Pressing Esc results in a fake event being sent - don't pass it to WebCore. |
|
4343 if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder]) |
|
4344 if (Frame* frame = core([self _frame])) |
|
4345 ret = frame->eventHandler()->keyEvent(event); |
|
4346 |
|
4347 if (!ret) |
|
4348 ret = [self _handleStyleKeyEquivalent:event] || [super performKeyEquivalent:event]; |
|
4349 |
|
4350 [self release]; |
|
4351 |
|
4352 return ret; |
|
4353 } |
|
4354 |
|
4355 - (void)copyFont:(id)sender |
|
4356 { |
|
4357 COMMAND_PROLOGUE |
|
4358 |
|
4359 // Put RTF with font attributes on the pasteboard. |
|
4360 // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font. |
|
4361 NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard]; |
|
4362 [fontPasteboard declareTypes:[NSArray arrayWithObject:NSFontPboardType] owner:nil]; |
|
4363 [fontPasteboard setData:[self _selectionStartFontAttributesAsRTF] forType:NSFontPboardType]; |
|
4364 } |
|
4365 |
|
4366 - (void)pasteFont:(id)sender |
|
4367 { |
|
4368 COMMAND_PROLOGUE |
|
4369 |
|
4370 // Read RTF with font attributes from the pasteboard. |
|
4371 // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font. |
|
4372 [self _applyStyleToSelection:[self _styleFromFontAttributes:[self _fontAttributesFromFontPasteboard]] withUndoAction:EditActionPasteFont]; |
|
4373 } |
|
4374 |
|
4375 - (void)pasteAsRichText:(id)sender |
|
4376 { |
|
4377 COMMAND_PROLOGUE |
|
4378 |
|
4379 // Since rich text always beats plain text when both are on the pasteboard, it's not |
|
4380 // clear how this is different from plain old paste. |
|
4381 [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:NO]; |
|
4382 } |
|
4383 |
|
4384 - (NSFont *)_originalFontA |
|
4385 { |
|
4386 return [[NSFontManager sharedFontManager] fontWithFamily:@"Helvetica" traits:0 weight:STANDARD_WEIGHT size:10.0f]; |
|
4387 } |
|
4388 |
|
4389 - (NSFont *)_originalFontB |
|
4390 { |
|
4391 return [[NSFontManager sharedFontManager] fontWithFamily:@"Times" traits:NSFontItalicTrait weight:STANDARD_BOLD_WEIGHT size:12.0f]; |
|
4392 } |
|
4393 |
|
4394 - (void)_addToStyle:(DOMCSSStyleDeclaration *)style fontA:(NSFont *)a fontB:(NSFont *)b |
|
4395 { |
|
4396 // Since there's no way to directly ask NSFontManager what style change it's going to do |
|
4397 // we instead pass two "specimen" fonts to it and let it change them. We then deduce what |
|
4398 // style change it was doing by looking at what happened to each of the two fonts. |
|
4399 // So if it was making the text bold, both fonts will be bold after the fact. |
|
4400 |
|
4401 if (a == nil || b == nil) |
|
4402 return; |
|
4403 |
|
4404 NSFontManager *fm = [NSFontManager sharedFontManager]; |
|
4405 |
|
4406 NSFont *oa = [self _originalFontA]; |
|
4407 |
|
4408 NSString *aFamilyName = [a familyName]; |
|
4409 NSString *bFamilyName = [b familyName]; |
|
4410 |
|
4411 int aPointSize = (int)[a pointSize]; |
|
4412 int bPointSize = (int)[b pointSize]; |
|
4413 |
|
4414 int aWeight = [fm weightOfFont:a]; |
|
4415 int bWeight = [fm weightOfFont:b]; |
|
4416 |
|
4417 BOOL aIsItalic = ([fm traitsOfFont:a] & NSItalicFontMask) != 0; |
|
4418 BOOL bIsItalic = ([fm traitsOfFont:b] & NSItalicFontMask) != 0; |
|
4419 |
|
4420 BOOL aIsBold = aWeight > MIN_BOLD_WEIGHT; |
|
4421 |
|
4422 if ([aFamilyName isEqualToString:bFamilyName]) { |
|
4423 NSString *familyNameForCSS = aFamilyName; |
|
4424 |
|
4425 // The family name may not be specific enough to get us the font specified. |
|
4426 // In some cases, the only way to get exactly what we are looking for is to use |
|
4427 // the Postscript name. |
|
4428 |
|
4429 // Find the font the same way the rendering code would later if it encountered this CSS. |
|
4430 NSFontTraitMask traits = aIsItalic ? NSFontItalicTrait : 0; |
|
4431 int weight = aIsBold ? STANDARD_BOLD_WEIGHT : STANDARD_WEIGHT; |
|
4432 NSFont *foundFont = [WebFontCache fontWithFamily:aFamilyName traits:traits weight:weight size:aPointSize]; |
|
4433 |
|
4434 // If we don't find a font with the same Postscript name, then we'll have to use the |
|
4435 // Postscript name to make the CSS specific enough. |
|
4436 if (![[foundFont fontName] isEqualToString:[a fontName]]) |
|
4437 familyNameForCSS = [a fontName]; |
|
4438 |
|
4439 // FIXME: Need more sophisticated escaping code if we want to handle family names |
|
4440 // with characters like single quote or backslash in their names. |
|
4441 [style setFontFamily:[NSString stringWithFormat:@"'%@'", familyNameForCSS]]; |
|
4442 } |
|
4443 |
|
4444 int soa = (int)[oa pointSize]; |
|
4445 if (aPointSize == bPointSize) |
|
4446 [style setFontSize:[NSString stringWithFormat:@"%dpx", aPointSize]]; |
|
4447 else if (aPointSize < soa) |
|
4448 [style _setFontSizeDelta:@"-1px"]; |
|
4449 else if (aPointSize > soa) |
|
4450 [style _setFontSizeDelta:@"1px"]; |
|
4451 |
|
4452 // FIXME: Map to the entire range of CSS weight values. |
|
4453 if (aWeight == bWeight) |
|
4454 [style setFontWeight:aIsBold ? @"bold" : @"normal"]; |
|
4455 |
|
4456 if (aIsItalic == bIsItalic) |
|
4457 [style setFontStyle:aIsItalic ? @"italic" : @"normal"]; |
|
4458 } |
|
4459 |
|
4460 - (DOMCSSStyleDeclaration *)_styleFromFontManagerOperation |
|
4461 { |
|
4462 DOMCSSStyleDeclaration *style = [self _emptyStyle]; |
|
4463 |
|
4464 NSFontManager *fm = [NSFontManager sharedFontManager]; |
|
4465 |
|
4466 NSFont *oa = [self _originalFontA]; |
|
4467 NSFont *ob = [self _originalFontB]; |
|
4468 [self _addToStyle:style fontA:[fm convertFont:oa] fontB:[fm convertFont:ob]]; |
|
4469 |
|
4470 return style; |
|
4471 } |
|
4472 |
|
4473 - (void)changeFont:(id)sender |
|
4474 { |
|
4475 COMMAND_PROLOGUE |
|
4476 |
|
4477 [self _applyStyleToSelection:[self _styleFromFontManagerOperation] withUndoAction:EditActionSetFont]; |
|
4478 } |
|
4479 |
|
4480 - (DOMCSSStyleDeclaration *)_styleForAttributeChange:(id)sender |
|
4481 { |
|
4482 DOMCSSStyleDeclaration *style = [self _emptyStyle]; |
|
4483 |
|
4484 NSShadow *shadow = [[NSShadow alloc] init]; |
|
4485 [shadow setShadowOffset:NSMakeSize(1, 1)]; |
|
4486 |
|
4487 NSDictionary *oa = [NSDictionary dictionaryWithObjectsAndKeys: |
|
4488 [self _originalFontA], NSFontAttributeName, |
|
4489 nil]; |
|
4490 NSDictionary *ob = [NSDictionary dictionaryWithObjectsAndKeys: |
|
4491 [NSColor blackColor], NSBackgroundColorAttributeName, |
|
4492 [self _originalFontB], NSFontAttributeName, |
|
4493 [NSColor whiteColor], NSForegroundColorAttributeName, |
|
4494 shadow, NSShadowAttributeName, |
|
4495 [NSNumber numberWithInt:NSUnderlineStyleSingle], NSStrikethroughStyleAttributeName, |
|
4496 [NSNumber numberWithInt:1], NSSuperscriptAttributeName, |
|
4497 [NSNumber numberWithInt:NSUnderlineStyleSingle], NSUnderlineStyleAttributeName, |
|
4498 nil]; |
|
4499 |
|
4500 [shadow release]; |
|
4501 |
|
4502 #if 0 |
|
4503 |
|
4504 NSObliquenessAttributeName /* float; skew to be applied to glyphs, default 0: no skew */ |
|
4505 // font-style, but that is just an on-off switch |
|
4506 |
|
4507 NSExpansionAttributeName /* float; log of expansion factor to be applied to glyphs, default 0: no expansion */ |
|
4508 // font-stretch? |
|
4509 |
|
4510 NSKernAttributeName /* float, amount to modify default kerning, if 0, kerning off */ |
|
4511 // letter-spacing? probably not good enough |
|
4512 |
|
4513 NSUnderlineColorAttributeName /* NSColor, default nil: same as foreground color */ |
|
4514 NSStrikethroughColorAttributeName /* NSColor, default nil: same as foreground color */ |
|
4515 // text-decoration-color? |
|
4516 |
|
4517 NSLigatureAttributeName /* int, default 1: default ligatures, 0: no ligatures, 2: all ligatures */ |
|
4518 NSBaselineOffsetAttributeName /* float, in points; offset from baseline, default 0 */ |
|
4519 NSStrokeWidthAttributeName /* float, in percent of font point size, default 0: no stroke; positive for stroke alone, negative for stroke and fill (a typical value for outlined text would be 3.0) */ |
|
4520 NSStrokeColorAttributeName /* NSColor, default nil: same as foreground color */ |
|
4521 // need extensions? |
|
4522 |
|
4523 #endif |
|
4524 |
|
4525 NSDictionary *a = [sender convertAttributes:oa]; |
|
4526 NSDictionary *b = [sender convertAttributes:ob]; |
|
4527 |
|
4528 NSColor *ca = [a objectForKey:NSBackgroundColorAttributeName]; |
|
4529 NSColor *cb = [b objectForKey:NSBackgroundColorAttributeName]; |
|
4530 if (ca == cb) { |
|
4531 [style setBackgroundColor:[self _colorAsString:ca]]; |
|
4532 } |
|
4533 |
|
4534 [self _addToStyle:style fontA:[a objectForKey:NSFontAttributeName] fontB:[b objectForKey:NSFontAttributeName]]; |
|
4535 |
|
4536 ca = [a objectForKey:NSForegroundColorAttributeName]; |
|
4537 cb = [b objectForKey:NSForegroundColorAttributeName]; |
|
4538 if (ca == cb) { |
|
4539 [style setColor:[self _colorAsString:ca]]; |
|
4540 } |
|
4541 |
|
4542 NSShadow *sha = [a objectForKey:NSShadowAttributeName]; |
|
4543 if (sha) |
|
4544 [style setTextShadow:[self _shadowAsString:sha]]; |
|
4545 else if ([b objectForKey:NSShadowAttributeName] == nil) |
|
4546 [style setTextShadow:@"none"]; |
|
4547 |
|
4548 int sa = [[a objectForKey:NSStrikethroughStyleAttributeName] intValue]; |
|
4549 int sb = [[b objectForKey:NSStrikethroughStyleAttributeName] intValue]; |
|
4550 if (sa == sb) { |
|
4551 if (sa == NSUnderlineStyleNone) |
|
4552 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""]; |
|
4553 // we really mean "no line-through" rather than "none" |
|
4554 else |
|
4555 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""]; |
|
4556 // we really mean "add line-through" rather than "line-through" |
|
4557 } |
|
4558 |
|
4559 sa = [[a objectForKey:NSSuperscriptAttributeName] intValue]; |
|
4560 sb = [[b objectForKey:NSSuperscriptAttributeName] intValue]; |
|
4561 if (sa == sb) { |
|
4562 if (sa > 0) |
|
4563 [style setVerticalAlign:@"super"]; |
|
4564 else if (sa < 0) |
|
4565 [style setVerticalAlign:@"sub"]; |
|
4566 else |
|
4567 [style setVerticalAlign:@"baseline"]; |
|
4568 } |
|
4569 |
|
4570 int ua = [[a objectForKey:NSUnderlineStyleAttributeName] intValue]; |
|
4571 int ub = [[b objectForKey:NSUnderlineStyleAttributeName] intValue]; |
|
4572 if (ua == ub) { |
|
4573 if (ua == NSUnderlineStyleNone) |
|
4574 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""]; |
|
4575 // we really mean "no underline" rather than "none" |
|
4576 else |
|
4577 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""]; |
|
4578 // we really mean "add underline" rather than "underline" |
|
4579 } |
|
4580 |
|
4581 return style; |
|
4582 } |
|
4583 |
|
4584 - (void)changeAttributes:(id)sender |
|
4585 { |
|
4586 COMMAND_PROLOGUE |
|
4587 |
|
4588 [self _applyStyleToSelection:[self _styleForAttributeChange:sender] withUndoAction:EditActionChangeAttributes]; |
|
4589 } |
|
4590 |
|
4591 - (DOMCSSStyleDeclaration *)_styleFromColorPanelWithSelector:(SEL)selector |
|
4592 { |
|
4593 DOMCSSStyleDeclaration *style = [self _emptyStyle]; |
|
4594 |
|
4595 ASSERT([style respondsToSelector:selector]); |
|
4596 [style performSelector:selector withObject:[self _colorAsString:[[NSColorPanel sharedColorPanel] color]]]; |
|
4597 |
|
4598 return style; |
|
4599 } |
|
4600 |
|
4601 - (EditAction)_undoActionFromColorPanelWithSelector:(SEL)selector |
|
4602 { |
|
4603 if (selector == @selector(setBackgroundColor:)) |
|
4604 return EditActionSetBackgroundColor; |
|
4605 return EditActionSetColor; |
|
4606 } |
|
4607 |
|
4608 - (void)_changeCSSColorUsingSelector:(SEL)selector inRange:(DOMRange *)range |
|
4609 { |
|
4610 DOMCSSStyleDeclaration *style = [self _styleFromColorPanelWithSelector:selector]; |
|
4611 WebView *webView = [self _webView]; |
|
4612 if ([[webView _editingDelegateForwarder] webView:webView shouldApplyStyle:style toElementsInDOMRange:range]) |
|
4613 if (Frame* coreFrame = core([self _frame])) |
|
4614 coreFrame->editor()->applyStyle(core(style), [self _undoActionFromColorPanelWithSelector:selector]); |
|
4615 } |
|
4616 |
|
4617 - (void)changeDocumentBackgroundColor:(id)sender |
|
4618 { |
|
4619 COMMAND_PROLOGUE |
|
4620 |
|
4621 // Mimicking NSTextView, this method sets the background color for the |
|
4622 // entire document. There is no NSTextView API for setting the background |
|
4623 // color on the selected range only. Note that this method is currently |
|
4624 // never called from the UI (see comment in changeColor:). |
|
4625 // FIXME: this actually has no effect when called, probably due to 3654850. _documentRange seems |
|
4626 // to do the right thing because it works in startSpeaking:, and I know setBackgroundColor: does the |
|
4627 // right thing because I tested it with [self _selectedRange]. |
|
4628 // FIXME: This won't actually apply the style to the entire range here, because it ends up calling |
|
4629 // [frame _applyStyle:], which operates on the current selection. To make this work right, we'll |
|
4630 // need to save off the selection, temporarily set it to the entire range, make the change, then |
|
4631 // restore the old selection. |
|
4632 [self _changeCSSColorUsingSelector:@selector(setBackgroundColor:) inRange:[self _documentRange]]; |
|
4633 } |
|
4634 |
|
4635 - (void)changeColor:(id)sender |
|
4636 { |
|
4637 COMMAND_PROLOGUE |
|
4638 |
|
4639 // FIXME: in NSTextView, this method calls changeDocumentBackgroundColor: when a |
|
4640 // private call has earlier been made by [NSFontFontEffectsBox changeColor:], see 3674493. |
|
4641 // AppKit will have to be revised to allow this to work with anything that isn't an |
|
4642 // NSTextView. However, this might not be required for Tiger, since the background-color |
|
4643 // changing box in the font panel doesn't work in Mail (3674481), though it does in TextEdit. |
|
4644 [self _applyStyleToSelection:[self _styleFromColorPanelWithSelector:@selector(setColor:)] |
|
4645 withUndoAction:EditActionSetColor]; |
|
4646 } |
|
4647 |
|
4648 - (void)_changeWordCaseWithSelector:(SEL)selector |
|
4649 { |
|
4650 if (![self _canEdit]) |
|
4651 return; |
|
4652 |
|
4653 WebFrame *frame = [self _frame]; |
|
4654 [self selectWord:nil]; |
|
4655 NSString *word = [[frame _selectedString] performSelector:selector]; |
|
4656 // FIXME: Does this need a different action context other than "typed"? |
|
4657 if ([self _shouldReplaceSelectionWithText:word givenAction:WebViewInsertActionTyped]) |
|
4658 [frame _replaceSelectionWithText:word selectReplacement:NO smartReplace:NO]; |
|
4659 } |
|
4660 |
|
4661 - (void)uppercaseWord:(id)sender |
|
4662 { |
|
4663 COMMAND_PROLOGUE |
|
4664 |
|
4665 [self _changeWordCaseWithSelector:@selector(uppercaseString)]; |
|
4666 } |
|
4667 |
|
4668 - (void)lowercaseWord:(id)sender |
|
4669 { |
|
4670 COMMAND_PROLOGUE |
|
4671 |
|
4672 [self _changeWordCaseWithSelector:@selector(lowercaseString)]; |
|
4673 } |
|
4674 |
|
4675 - (void)capitalizeWord:(id)sender |
|
4676 { |
|
4677 COMMAND_PROLOGUE |
|
4678 |
|
4679 [self _changeWordCaseWithSelector:@selector(capitalizedString)]; |
|
4680 } |
|
4681 |
|
4682 - (void)complete:(id)sender |
|
4683 { |
|
4684 COMMAND_PROLOGUE |
|
4685 |
|
4686 if (![self _canEdit]) |
|
4687 return; |
|
4688 if (!_private->completionController) |
|
4689 _private->completionController = [[WebTextCompletionController alloc] initWithWebView:[self _webView] HTMLView:self]; |
|
4690 [_private->completionController doCompletion]; |
|
4691 } |
|
4692 |
|
4693 - (void)checkSpelling:(id)sender |
|
4694 { |
|
4695 COMMAND_PROLOGUE |
|
4696 |
|
4697 if (Frame* coreFrame = core([self _frame])) |
|
4698 coreFrame->editor()->advanceToNextMisspelling(); |
|
4699 } |
|
4700 |
|
4701 - (void)showGuessPanel:(id)sender |
|
4702 { |
|
4703 COMMAND_PROLOGUE |
|
4704 |
|
4705 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; |
|
4706 if (!checker) { |
|
4707 LOG_ERROR("No NSSpellChecker"); |
|
4708 return; |
|
4709 } |
|
4710 |
|
4711 NSPanel *spellingPanel = [checker spellingPanel]; |
|
4712 #ifndef BUILDING_ON_TIGER |
|
4713 // Post-Tiger, this menu item is a show/hide toggle, to match AppKit. Leave Tiger behavior alone |
|
4714 // to match rest of OS X. |
|
4715 if ([spellingPanel isVisible]) { |
|
4716 [spellingPanel orderOut:sender]; |
|
4717 return; |
|
4718 } |
|
4719 #endif |
|
4720 |
|
4721 if (Frame* coreFrame = core([self _frame])) |
|
4722 coreFrame->editor()->advanceToNextMisspelling(true); |
|
4723 [spellingPanel orderFront:sender]; |
|
4724 } |
|
4725 |
|
4726 - (void)_changeSpellingToWord:(NSString *)newWord |
|
4727 { |
|
4728 if (![self _canEdit]) |
|
4729 return; |
|
4730 |
|
4731 // Don't correct to empty string. (AppKit checked this, we might as well too.) |
|
4732 if (![NSSpellChecker sharedSpellChecker]) { |
|
4733 LOG_ERROR("No NSSpellChecker"); |
|
4734 return; |
|
4735 } |
|
4736 |
|
4737 if ([newWord isEqualToString:@""]) |
|
4738 return; |
|
4739 |
|
4740 if ([self _shouldReplaceSelectionWithText:newWord givenAction:WebViewInsertActionPasted]) |
|
4741 [[self _frame] _replaceSelectionWithText:newWord selectReplacement:YES smartReplace:NO]; |
|
4742 } |
|
4743 |
|
4744 - (void)changeSpelling:(id)sender |
|
4745 { |
|
4746 COMMAND_PROLOGUE |
|
4747 |
|
4748 [self _changeSpellingToWord:[[sender selectedCell] stringValue]]; |
|
4749 } |
|
4750 |
|
4751 - (void)performFindPanelAction:(id)sender |
|
4752 { |
|
4753 COMMAND_PROLOGUE |
|
4754 |
|
4755 // Implementing this will probably require copying all of NSFindPanel.h and .m. |
|
4756 // We need *almost* the same thing as AppKit, but not quite. |
|
4757 LOG_ERROR("unimplemented"); |
|
4758 } |
|
4759 |
|
4760 - (void)startSpeaking:(id)sender |
|
4761 { |
|
4762 COMMAND_PROLOGUE |
|
4763 |
|
4764 WebFrame *frame = [self _frame]; |
|
4765 DOMRange *range = [self _selectedRange]; |
|
4766 if (!range || [range collapsed]) |
|
4767 range = [self _documentRange]; |
|
4768 [NSApp speakString:[frame _stringForRange:range]]; |
|
4769 } |
|
4770 |
|
4771 - (void)stopSpeaking:(id)sender |
|
4772 { |
|
4773 COMMAND_PROLOGUE |
|
4774 |
|
4775 [NSApp stopSpeaking:sender]; |
|
4776 } |
|
4777 |
|
4778 - (void)toggleBaseWritingDirection:(id)sender |
|
4779 { |
|
4780 COMMAND_PROLOGUE |
|
4781 |
|
4782 if (![self _canEdit]) |
|
4783 return; |
|
4784 |
|
4785 Frame* coreFrame = core([self _frame]); |
|
4786 if (!coreFrame) |
|
4787 return; |
|
4788 |
|
4789 WritingDirection direction = RightToLeftWritingDirection; |
|
4790 switch (coreFrame->baseWritingDirectionForSelectionStart()) { |
|
4791 case NSWritingDirectionLeftToRight: |
|
4792 break; |
|
4793 case NSWritingDirectionRightToLeft: |
|
4794 direction = LeftToRightWritingDirection; |
|
4795 break; |
|
4796 // The writingDirectionForSelectionStart method will never return "natural". It |
|
4797 // will always return a concrete direction. So, keep the compiler happy, and assert not reached. |
|
4798 case NSWritingDirectionNatural: |
|
4799 ASSERT_NOT_REACHED(); |
|
4800 break; |
|
4801 } |
|
4802 |
|
4803 if (Frame* coreFrame = core([self _frame])) |
|
4804 coreFrame->editor()->setBaseWritingDirection(direction); |
|
4805 } |
|
4806 |
|
4807 - (void)changeBaseWritingDirection:(id)sender |
|
4808 { |
|
4809 COMMAND_PROLOGUE |
|
4810 |
|
4811 if (![self _canEdit]) |
|
4812 return; |
|
4813 |
|
4814 NSWritingDirection writingDirection = static_cast<NSWritingDirection>([sender tag]); |
|
4815 |
|
4816 // We disable the menu item that performs this action because we can't implement |
|
4817 // NSWritingDirectionNatural's behavior using CSS. |
|
4818 ASSERT(writingDirection != NSWritingDirectionNatural); |
|
4819 |
|
4820 if (Frame* coreFrame = core([self _frame])) |
|
4821 coreFrame->editor()->setBaseWritingDirection(writingDirection == NSWritingDirectionLeftToRight ? LeftToRightWritingDirection : RightToLeftWritingDirection); |
|
4822 } |
|
4823 |
|
4824 static BOOL writingDirectionKeyBindingsEnabled() |
|
4825 { |
|
4826 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
4827 return YES; |
|
4828 #else |
|
4829 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; |
|
4830 return [defaults boolForKey:@"NSAllowsBaseWritingDirectionKeyBindings"] || [defaults boolForKey:@"AppleTextDirection"]; |
|
4831 #endif |
|
4832 } |
|
4833 |
|
4834 - (void)_changeBaseWritingDirectionTo:(NSWritingDirection)direction |
|
4835 { |
|
4836 if (![self _canEdit]) |
|
4837 return; |
|
4838 |
|
4839 static BOOL bindingsEnabled = writingDirectionKeyBindingsEnabled(); |
|
4840 |
|
4841 if (!bindingsEnabled) { |
|
4842 NSBeep(); |
|
4843 return; |
|
4844 } |
|
4845 |
|
4846 if (Frame* coreFrame = core([self _frame])) |
|
4847 coreFrame->editor()->setBaseWritingDirection(direction == NSWritingDirectionLeftToRight ? LeftToRightWritingDirection : RightToLeftWritingDirection); |
|
4848 } |
|
4849 |
|
4850 - (void)makeBaseWritingDirectionLeftToRight:(id)sender |
|
4851 { |
|
4852 COMMAND_PROLOGUE |
|
4853 |
|
4854 [self _changeBaseWritingDirectionTo:NSWritingDirectionLeftToRight]; |
|
4855 } |
|
4856 |
|
4857 - (void)makeBaseWritingDirectionRightToLeft:(id)sender |
|
4858 { |
|
4859 COMMAND_PROLOGUE |
|
4860 |
|
4861 [self _changeBaseWritingDirectionTo:NSWritingDirectionRightToLeft]; |
|
4862 } |
|
4863 |
|
4864 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) |
|
4865 - (void)changeBaseWritingDirectionToLTR:(id)sender |
|
4866 { |
|
4867 [self makeBaseWritingDirectionLeftToRight:sender]; |
|
4868 } |
|
4869 |
|
4870 - (void)changeBaseWritingDirectionToRTL:(id)sender |
|
4871 { |
|
4872 [self makeBaseWritingDirectionRightToLeft:sender]; |
|
4873 } |
|
4874 #endif |
|
4875 |
|
4876 - (void)makeBaseWritingDirectionNatural:(id)sender |
|
4877 { |
|
4878 LOG_ERROR("Sent from %@.", sender); |
|
4879 } |
|
4880 |
|
4881 #if 0 |
|
4882 |
|
4883 // CSS does not have a way to specify an outline font, which may make this difficult to implement. |
|
4884 // Maybe a special case of text-shadow? |
|
4885 - (void)outline:(id)sender; |
|
4886 |
|
4887 // This is part of table support, which may be in NSTextView for Tiger. |
|
4888 // It's probably simple to do the equivalent thing for WebKit. |
|
4889 - (void)insertTable:(id)sender; |
|
4890 |
|
4891 // This could be important. |
|
4892 - (void)toggleTraditionalCharacterShape:(id)sender; |
|
4893 |
|
4894 // I'm not sure what the equivalents of these in the web world are. |
|
4895 - (void)insertLineSeparator:(id)sender; |
|
4896 - (void)insertPageBreak:(id)sender; |
|
4897 |
|
4898 // These methods are not implemented in NSTextView yet at the time of this writing. |
|
4899 - (void)changeCaseOfLetter:(id)sender; |
|
4900 - (void)transposeWords:(id)sender; |
|
4901 |
|
4902 #endif |
|
4903 |
|
4904 #ifndef BUILDING_ON_TIGER |
|
4905 |
|
4906 // Override this so that AppKit will send us arrow keys as key down events so we can |
|
4907 // support them via the key bindings mechanism. |
|
4908 - (BOOL)_wantsKeyDownForEvent:(NSEvent *)event |
|
4909 { |
|
4910 bool haveWebCoreFrame = core([self _frame]); |
|
4911 |
|
4912 // If we have a frame, our keyDown method will handle key bindings after sending |
|
4913 // the event through the DOM, so ask AppKit not to do its early special key binding |
|
4914 // mapping. If we don't have a frame, just let things work the normal way without |
|
4915 // a keyDown. |
|
4916 return haveWebCoreFrame; |
|
4917 } |
|
4918 |
|
4919 #else |
|
4920 |
|
4921 // Super-hack alert. |
|
4922 // All this code accomplishes the same thing as the _wantsKeyDownForEvent method above. |
|
4923 |
|
4924 // Returns a selector only if called while: |
|
4925 // 1) first responder is self |
|
4926 // 2) handling a key down event |
|
4927 // 3) not yet inside keyDown: method |
|
4928 // 4) key is an arrow key |
|
4929 // The selector is the one that gets sent by -[NSWindow _processKeyboardUIKey] for this key. |
|
4930 - (SEL)_arrowKeyDownEventSelectorIfPreprocessing |
|
4931 { |
|
4932 NSWindow *w = [self window]; |
|
4933 if ([w firstResponder] != self) |
|
4934 return NULL; |
|
4935 NSEvent *e = [w currentEvent]; |
|
4936 if ([e type] != NSKeyDown) |
|
4937 return NULL; |
|
4938 if (e == _private->keyDownEvent) |
|
4939 return NULL; |
|
4940 NSString *s = [e charactersIgnoringModifiers]; |
|
4941 if ([s length] == 0) |
|
4942 return NULL; |
|
4943 switch ([s characterAtIndex:0]) { |
|
4944 case NSDownArrowFunctionKey: |
|
4945 return @selector(moveDown:); |
|
4946 case NSLeftArrowFunctionKey: |
|
4947 return @selector(moveLeft:); |
|
4948 case NSRightArrowFunctionKey: |
|
4949 return @selector(moveRight:); |
|
4950 case NSUpArrowFunctionKey: |
|
4951 return @selector(moveUp:); |
|
4952 default: |
|
4953 return NULL; |
|
4954 } |
|
4955 } |
|
4956 |
|
4957 // Returns NO instead of YES if called on the selector that the |
|
4958 // _arrowKeyDownEventSelectorIfPreprocessing method returns. |
|
4959 // This should only happen inside -[NSWindow _processKeyboardUIKey], |
|
4960 // and together with the change below should cause that method |
|
4961 // to return NO rather than handling the key. |
|
4962 // Also set a 1-shot flag for the nextResponder check below. |
|
4963 - (BOOL)respondsToSelector:(SEL)selector |
|
4964 { |
|
4965 if (![super respondsToSelector:selector]) |
|
4966 return NO; |
|
4967 SEL arrowKeySelector = [self _arrowKeyDownEventSelectorIfPreprocessing]; |
|
4968 if (selector != arrowKeySelector) |
|
4969 return YES; |
|
4970 _private->nextResponderDisabledOnce = YES; |
|
4971 return NO; |
|
4972 } |
|
4973 |
|
4974 // Returns nil instead of the next responder if called when the |
|
4975 // one-shot flag is set, and _arrowKeyDownEventSelectorIfPreprocessing |
|
4976 // returns something other than NULL. This should only happen inside |
|
4977 // -[NSWindow _processKeyboardUIKey] and together with the change above |
|
4978 // should cause that method to return NO rather than handling the key. |
|
4979 - (NSResponder *)nextResponder |
|
4980 { |
|
4981 BOOL disabled = _private->nextResponderDisabledOnce; |
|
4982 _private->nextResponderDisabledOnce = NO; |
|
4983 if (disabled && [self _arrowKeyDownEventSelectorIfPreprocessing] != NULL) |
|
4984 return nil; |
|
4985 return [super nextResponder]; |
|
4986 } |
|
4987 |
|
4988 #endif |
|
4989 |
|
4990 - (void)_updateControlTints |
|
4991 { |
|
4992 Frame* frame = core([self _frame]); |
|
4993 if (!frame) |
|
4994 return; |
|
4995 FrameView* view = frame->view(); |
|
4996 if (!view) |
|
4997 return; |
|
4998 view->updateControlTints(); |
|
4999 } |
|
5000 |
|
5001 // Despite its name, this is called at different times than windowDidBecomeKey is. |
|
5002 // It takes into account all the other factors that determine when NSCell draws |
|
5003 // with different tints, so it's the right call to use for control tints. We'd prefer |
|
5004 // to do this with API. <rdar://problem/5136760> |
|
5005 - (void)_windowChangedKeyState |
|
5006 { |
|
5007 if (pthread_main_np()) |
|
5008 [self _updateControlTints]; |
|
5009 else |
|
5010 [self performSelectorOnMainThread:@selector(_updateControlTints) withObject:nil waitUntilDone:NO]; |
|
5011 |
|
5012 [super _windowChangedKeyState]; |
|
5013 } |
|
5014 |
|
5015 - (void)otherMouseDown:(NSEvent *)event |
|
5016 { |
|
5017 if ([event buttonNumber] == 2) |
|
5018 [self mouseDown:event]; |
|
5019 else |
|
5020 [super otherMouseDown:event]; |
|
5021 } |
|
5022 |
|
5023 - (void)otherMouseDragged:(NSEvent *)event |
|
5024 { |
|
5025 if ([event buttonNumber] == 2) |
|
5026 [self mouseDragged:event]; |
|
5027 else |
|
5028 [super otherMouseDragged:event]; |
|
5029 } |
|
5030 |
|
5031 - (void)otherMouseUp:(NSEvent *)event |
|
5032 { |
|
5033 if ([event buttonNumber] == 2) |
|
5034 [self mouseUp:event]; |
|
5035 else |
|
5036 [super otherMouseUp:event]; |
|
5037 } |
|
5038 |
|
5039 @end |
|
5040 |
|
5041 @implementation NSArray (WebHTMLView) |
|
5042 |
|
5043 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object |
|
5044 { |
|
5045 #if ENABLE(NETSCAPE_PLUGIN_API) |
|
5046 NSEnumerator *enumerator = [self objectEnumerator]; |
|
5047 WebNetscapePluginView *view; |
|
5048 while ((view = [enumerator nextObject]) != nil) |
|
5049 if ([view isKindOfClass:[WebBaseNetscapePluginView class]]) |
|
5050 [view performSelector:selector withObject:object]; |
|
5051 #endif |
|
5052 } |
|
5053 |
|
5054 @end |
|
5055 |
|
5056 @implementation WebHTMLView (WebInternal) |
|
5057 |
|
5058 - (void)_selectionChanged |
|
5059 { |
|
5060 [self _updateSelectionForInputManager]; |
|
5061 [self _updateFontPanel]; |
|
5062 if (Frame* coreFrame = core([self _frame])) |
|
5063 coreFrame->editor()->setStartNewKillRingSequence(true); |
|
5064 } |
|
5065 |
|
5066 - (void)_updateFontPanel |
|
5067 { |
|
5068 // FIXME: NSTextView bails out if becoming or resigning first responder, for which it has ivar flags. Not |
|
5069 // sure if we need to do something similar. |
|
5070 |
|
5071 if (![self _canEdit]) |
|
5072 return; |
|
5073 |
|
5074 NSWindow *window = [self window]; |
|
5075 // FIXME: is this first-responder check correct? What happens if a subframe is editable and is first responder? |
|
5076 if (![window isKeyWindow] || [window firstResponder] != self) |
|
5077 return; |
|
5078 |
|
5079 bool multipleFonts = false; |
|
5080 NSFont *font = nil; |
|
5081 if (Frame* coreFrame = core([self _frame])) { |
|
5082 if (const SimpleFontData* fd = coreFrame->editor()->fontForSelection(multipleFonts)) |
|
5083 font = fd->getNSFont(); |
|
5084 } |
|
5085 |
|
5086 // FIXME: for now, return a bogus font that distinguishes the empty selection from the non-empty |
|
5087 // selection. We should be able to remove this once the rest of this code works properly. |
|
5088 if (font == nil) |
|
5089 font = [self _hasSelection] ? [NSFont menuFontOfSize:23] : [NSFont toolTipsFontOfSize:17]; |
|
5090 ASSERT(font != nil); |
|
5091 |
|
5092 [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:multipleFonts]; |
|
5093 |
|
5094 // FIXME: we don't keep track of selected attributes, or set them on the font panel. This |
|
5095 // appears to have no effect on the UI. E.g., underlined text in Mail or TextEdit is |
|
5096 // not reflected in the font panel. Maybe someday this will change. |
|
5097 } |
|
5098 |
|
5099 - (BOOL)_canSmartCopyOrDelete |
|
5100 { |
|
5101 if (![[self _webView] smartInsertDeleteEnabled]) |
|
5102 return NO; |
|
5103 Frame* coreFrame = core([self _frame]); |
|
5104 return coreFrame && coreFrame->selectionGranularity() == WordGranularity; |
|
5105 } |
|
5106 |
|
5107 - (NSEvent *)_mouseDownEvent |
|
5108 { |
|
5109 return _private->mouseDownEvent; |
|
5110 } |
|
5111 |
|
5112 - (id<WebHTMLHighlighter>)_highlighterForType:(NSString*)type |
|
5113 { |
|
5114 return [_private->highlighters objectForKey:type]; |
|
5115 } |
|
5116 |
|
5117 - (WebFrame *)_frame |
|
5118 { |
|
5119 return [_private->dataSource webFrame]; |
|
5120 } |
|
5121 |
|
5122 - (void)paste:(id)sender |
|
5123 { |
|
5124 COMMAND_PROLOGUE |
|
5125 |
|
5126 RetainPtr<WebHTMLView> selfProtector = self; |
|
5127 RefPtr<Frame> coreFrame = core([self _frame]); |
|
5128 if (!coreFrame) |
|
5129 return; |
|
5130 if (coreFrame->editor()->tryDHTMLPaste()) |
|
5131 return; // DHTML did the whole operation |
|
5132 if (!coreFrame->editor()->canPaste()) |
|
5133 return; |
|
5134 if (coreFrame->selection()->isContentRichlyEditable()) |
|
5135 [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:YES]; |
|
5136 else |
|
5137 coreFrame->editor()->pasteAsPlainText(); |
|
5138 } |
|
5139 |
|
5140 - (void)closeIfNotCurrentView |
|
5141 { |
|
5142 if ([[[self _frame] frameView] documentView] != self) |
|
5143 [self close]; |
|
5144 } |
|
5145 |
|
5146 - (DOMDocumentFragment*)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard |
|
5147 { |
|
5148 return [self _documentFragmentFromPasteboard:pasteboard inContext:nil allowPlainText:NO]; |
|
5149 } |
|
5150 |
|
5151 #ifndef BUILDING_ON_TIGER |
|
5152 |
|
5153 - (BOOL)isGrammarCheckingEnabled |
|
5154 { |
|
5155 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because |
|
5156 // the AppKit code checks the first responder. |
|
5157 return [[self _webView] isGrammarCheckingEnabled]; |
|
5158 } |
|
5159 |
|
5160 - (void)setGrammarCheckingEnabled:(BOOL)flag |
|
5161 { |
|
5162 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because |
|
5163 // the AppKit code checks the first responder. |
|
5164 [[self _webView] setGrammarCheckingEnabled:flag]; |
|
5165 } |
|
5166 |
|
5167 - (void)toggleGrammarChecking:(id)sender |
|
5168 { |
|
5169 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because |
|
5170 // the AppKit code checks the first responder. |
|
5171 [[self _webView] toggleGrammarChecking:sender]; |
|
5172 } |
|
5173 |
|
5174 |
|
5175 static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point) |
|
5176 { |
|
5177 NSArray *screens = [NSScreen screens]; |
|
5178 |
|
5179 if ([screens count] == 0) { |
|
5180 // You could theoretically get here if running with no monitor, in which case it doesn't matter |
|
5181 // much where the "on-screen" point is. |
|
5182 return CGPointMake(point.x, point.y); |
|
5183 } |
|
5184 |
|
5185 // Flip the y coordinate from the top of the menu bar screen -- see 4636390 |
|
5186 return CGPointMake(point.x, NSMaxY([[screens objectAtIndex:0] frame]) - point.y); |
|
5187 } |
|
5188 |
|
5189 #endif |
|
5190 |
|
5191 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
5192 |
|
5193 - (void)orderFrontSubstitutionsPanel:(id)sender |
|
5194 { |
|
5195 COMMAND_PROLOGUE |
|
5196 |
|
5197 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; |
|
5198 if (!checker) { |
|
5199 LOG_ERROR("No NSSpellChecker"); |
|
5200 return; |
|
5201 } |
|
5202 |
|
5203 NSPanel *substitutionsPanel = [checker substitutionsPanel]; |
|
5204 if ([substitutionsPanel isVisible]) { |
|
5205 [substitutionsPanel orderOut:sender]; |
|
5206 return; |
|
5207 } |
|
5208 [substitutionsPanel orderFront:sender]; |
|
5209 } |
|
5210 |
|
5211 // FIXME 4799134: WebView is the bottleneck for this logic, but we must implement these methods here because |
|
5212 // the AppKit code checks the first responder. |
|
5213 |
|
5214 - (BOOL)smartInsertDeleteEnabled |
|
5215 { |
|
5216 return [[self _webView] smartInsertDeleteEnabled]; |
|
5217 } |
|
5218 |
|
5219 - (void)setSmartInsertDeleteEnabled:(BOOL)flag |
|
5220 { |
|
5221 [[self _webView] setSmartInsertDeleteEnabled:flag]; |
|
5222 } |
|
5223 |
|
5224 - (void)toggleSmartInsertDelete:(id)sender |
|
5225 { |
|
5226 [[self _webView] toggleSmartInsertDelete:sender]; |
|
5227 } |
|
5228 |
|
5229 - (BOOL)isAutomaticQuoteSubstitutionEnabled |
|
5230 { |
|
5231 return [[self _webView] isAutomaticQuoteSubstitutionEnabled]; |
|
5232 } |
|
5233 |
|
5234 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag |
|
5235 { |
|
5236 [[self _webView] setAutomaticQuoteSubstitutionEnabled:flag]; |
|
5237 } |
|
5238 |
|
5239 - (void)toggleAutomaticQuoteSubstitution:(id)sender |
|
5240 { |
|
5241 [[self _webView] toggleAutomaticQuoteSubstitution:sender]; |
|
5242 } |
|
5243 |
|
5244 - (BOOL)isAutomaticLinkDetectionEnabled |
|
5245 { |
|
5246 return [[self _webView] isAutomaticLinkDetectionEnabled]; |
|
5247 } |
|
5248 |
|
5249 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag |
|
5250 { |
|
5251 [[self _webView] setAutomaticLinkDetectionEnabled:flag]; |
|
5252 } |
|
5253 |
|
5254 - (void)toggleAutomaticLinkDetection:(id)sender |
|
5255 { |
|
5256 [[self _webView] toggleAutomaticLinkDetection:sender]; |
|
5257 } |
|
5258 |
|
5259 - (BOOL)isAutomaticDashSubstitutionEnabled |
|
5260 { |
|
5261 return [[self _webView] isAutomaticDashSubstitutionEnabled]; |
|
5262 } |
|
5263 |
|
5264 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag |
|
5265 { |
|
5266 [[self _webView] setAutomaticDashSubstitutionEnabled:flag]; |
|
5267 } |
|
5268 |
|
5269 - (void)toggleAutomaticDashSubstitution:(id)sender |
|
5270 { |
|
5271 [[self _webView] toggleAutomaticDashSubstitution:sender]; |
|
5272 } |
|
5273 |
|
5274 - (BOOL)isAutomaticTextReplacementEnabled |
|
5275 { |
|
5276 return [[self _webView] isAutomaticTextReplacementEnabled]; |
|
5277 } |
|
5278 |
|
5279 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag |
|
5280 { |
|
5281 [[self _webView] setAutomaticTextReplacementEnabled:flag]; |
|
5282 } |
|
5283 |
|
5284 - (void)toggleAutomaticTextReplacement:(id)sender |
|
5285 { |
|
5286 [[self _webView] toggleAutomaticTextReplacement:sender]; |
|
5287 } |
|
5288 |
|
5289 - (BOOL)isAutomaticSpellingCorrectionEnabled |
|
5290 { |
|
5291 return [[self _webView] isAutomaticSpellingCorrectionEnabled]; |
|
5292 } |
|
5293 |
|
5294 - (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag |
|
5295 { |
|
5296 [[self _webView] setAutomaticSpellingCorrectionEnabled:flag]; |
|
5297 } |
|
5298 |
|
5299 - (void)toggleAutomaticSpellingCorrection:(id)sender |
|
5300 { |
|
5301 [[self _webView] toggleAutomaticSpellingCorrection:sender]; |
|
5302 } |
|
5303 |
|
5304 #endif |
|
5305 |
|
5306 - (void)_lookUpInDictionaryFromMenu:(id)sender |
|
5307 { |
|
5308 // Dictionary API will accept a whitespace-only string and display UI as if it were real text, |
|
5309 // so bail out early to avoid that. |
|
5310 if ([[[self selectedString] _webkit_stringByTrimmingWhitespace] length] == 0) |
|
5311 return; |
|
5312 |
|
5313 NSAttributedString *attrString = [self selectedAttributedString]; |
|
5314 |
|
5315 Frame* coreFrame = core([self _frame]); |
|
5316 if (!coreFrame) |
|
5317 return; |
|
5318 |
|
5319 NSRect rect = coreFrame->selectionBounds(); |
|
5320 |
|
5321 #ifndef BUILDING_ON_TIGER |
|
5322 NSDictionary *attributes = [attrString fontAttributesInRange:NSMakeRange(0,1)]; |
|
5323 NSFont *font = [attributes objectForKey:NSFontAttributeName]; |
|
5324 if (font) |
|
5325 rect.origin.y += [font ascender]; |
|
5326 #endif |
|
5327 |
|
5328 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
5329 [self showDefinitionForAttributedString:attrString atPoint:rect.origin]; |
|
5330 return; |
|
5331 #endif |
|
5332 |
|
5333 // We soft link to get the function that displays the dictionary (either pop-up window or app) to avoid the performance |
|
5334 // penalty of linking to another framework. This function changed signature as well as framework between Tiger and Leopard, |
|
5335 // so the two cases are handled separately. |
|
5336 |
|
5337 #ifdef BUILDING_ON_TIGER |
|
5338 typedef OSStatus (*ServiceWindowShowFunction)(id inWordString, NSRect inWordBoundary, UInt16 inLineDirection); |
|
5339 const char *frameworkPath = "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/LangAnalysis.framework/LangAnalysis"; |
|
5340 const char *functionName = "DCMDictionaryServiceWindowShow"; |
|
5341 #else |
|
5342 typedef void (*ServiceWindowShowFunction)(id unusedDictionaryRef, id inWordString, CFRange selectionRange, id unusedFont, CGPoint textOrigin, Boolean verticalText, id unusedTransform); |
|
5343 const char *frameworkPath = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox"; |
|
5344 const char *functionName = "HIDictionaryWindowShow"; |
|
5345 #endif |
|
5346 |
|
5347 static bool lookedForFunction = false; |
|
5348 static ServiceWindowShowFunction dictionaryServiceWindowShow = NULL; |
|
5349 |
|
5350 if (!lookedForFunction) { |
|
5351 void* langAnalysisFramework = dlopen(frameworkPath, RTLD_LAZY); |
|
5352 ASSERT(langAnalysisFramework); |
|
5353 if (langAnalysisFramework) |
|
5354 dictionaryServiceWindowShow = (ServiceWindowShowFunction)dlsym(langAnalysisFramework, functionName); |
|
5355 lookedForFunction = true; |
|
5356 } |
|
5357 |
|
5358 ASSERT(dictionaryServiceWindowShow); |
|
5359 if (!dictionaryServiceWindowShow) { |
|
5360 NSLog(@"Couldn't find the %s function in %s", functionName, frameworkPath); |
|
5361 return; |
|
5362 } |
|
5363 |
|
5364 #ifdef BUILDING_ON_TIGER |
|
5365 // FIXME: must check for right-to-left here |
|
5366 NSWritingDirection writingDirection = NSWritingDirectionLeftToRight; |
|
5367 |
|
5368 // FIXME: the dictionary API expects the rect for the first line of selection. Passing |
|
5369 // the rect for the entire selection, as we do here, positions the pop-up window near |
|
5370 // the bottom of the selection rather than at the selected word. |
|
5371 rect = [self convertRect:rect toView:nil]; |
|
5372 rect.origin = [[self window] convertBaseToScreen:rect.origin]; |
|
5373 NSData *data = [attrString RTFFromRange:NSMakeRange(0, [attrString length]) documentAttributes:nil]; |
|
5374 dictionaryServiceWindowShow(data, rect, (writingDirection == NSWritingDirectionRightToLeft) ? 1 : 0); |
|
5375 #else |
|
5376 // The HIDictionaryWindowShow function requires the origin, in CG screen coordinates, of the first character of text in the selection. |
|
5377 // FIXME 4945808: We approximate this in a way that works well when a single word is selected, and less well in some other cases |
|
5378 // (but no worse than we did in Tiger) |
|
5379 NSPoint windowPoint = [self convertPoint:rect.origin toView:nil]; |
|
5380 NSPoint screenPoint = [[self window] convertBaseToScreen:windowPoint]; |
|
5381 |
|
5382 dictionaryServiceWindowShow(nil, attrString, CFRangeMake(0, [attrString length]), nil, |
|
5383 coreGraphicsScreenPointForAppKitScreenPoint(screenPoint), false, nil); |
|
5384 #endif |
|
5385 } |
|
5386 |
|
5387 - (void)_hoverFeedbackSuspendedChanged |
|
5388 { |
|
5389 [self _updateMouseoverWithFakeEvent]; |
|
5390 } |
|
5391 |
|
5392 - (BOOL)_interceptEditingKeyEvent:(KeyboardEvent*)event shouldSaveCommand:(BOOL)shouldSave |
|
5393 { |
|
5394 // Ask AppKit to process the key event -- it will call back with either insertText or doCommandBySelector. |
|
5395 WebHTMLViewInterpretKeyEventsParameters parameters; |
|
5396 parameters.eventWasHandled = false; |
|
5397 parameters.shouldSaveCommand = shouldSave; |
|
5398 // If we're intercepting the initial IM call we assume that the IM has consumed the event, |
|
5399 // and only change this assumption if one of the NSTextInput/Responder callbacks is used. |
|
5400 // We assume the IM will *not* consume hotkey sequences |
|
5401 parameters.consumedByIM = !event->metaKey() && shouldSave; |
|
5402 |
|
5403 if (const PlatformKeyboardEvent* platformEvent = event->keyEvent()) { |
|
5404 NSEvent *macEvent = platformEvent->macEvent(); |
|
5405 if ([macEvent type] == NSKeyDown && [_private->completionController filterKeyDown:macEvent]) |
|
5406 return true; |
|
5407 |
|
5408 if ([macEvent type] == NSFlagsChanged) |
|
5409 return false; |
|
5410 |
|
5411 parameters.event = event; |
|
5412 _private->interpretKeyEventsParameters = ¶meters; |
|
5413 _private->receivedNOOP = NO; |
|
5414 const Vector<KeypressCommand>& commands = event->keypressCommands(); |
|
5415 bool hasKeypressCommand = !commands.isEmpty(); |
|
5416 |
|
5417 // FIXME: interpretKeyEvents doesn't match application key equivalents (such as Cmd+A), |
|
5418 // and sends noop: for those. As a result, we don't handle those from within WebCore, |
|
5419 // but send a full sequence of DOM events, including an unneeded keypress. |
|
5420 if (parameters.shouldSaveCommand || !hasKeypressCommand) |
|
5421 [self interpretKeyEvents:[NSArray arrayWithObject:macEvent]]; |
|
5422 else { |
|
5423 size_t size = commands.size(); |
|
5424 // Are there commands that would just cause text insertion if executed via Editor? |
|
5425 // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore |
|
5426 // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated |
|
5427 // (e.g. Tab that inserts a Tab character, or Enter). |
|
5428 bool haveTextInsertionCommands = false; |
|
5429 for (size_t i = 0; i < size; ++i) { |
|
5430 if ([self coreCommandBySelector:NSSelectorFromString(commands[i].commandName)].isTextInsertion()) |
|
5431 haveTextInsertionCommands = true; |
|
5432 } |
|
5433 if (!haveTextInsertionCommands || platformEvent->type() == PlatformKeyboardEvent::Char) { |
|
5434 for (size_t i = 0; i < size; ++i) { |
|
5435 if (commands[i].commandName == "insertText:") |
|
5436 [self insertText:commands[i].text]; |
|
5437 else |
|
5438 [self doCommandBySelector:NSSelectorFromString(commands[i].commandName)]; |
|
5439 } |
|
5440 } |
|
5441 } |
|
5442 _private->interpretKeyEventsParameters = 0; |
|
5443 } |
|
5444 return (!_private->receivedNOOP && parameters.eventWasHandled) || parameters.consumedByIM; |
|
5445 } |
|
5446 |
|
5447 - (WebCore::CachedImage*)promisedDragTIFFDataSource |
|
5448 { |
|
5449 return _private->promisedDragTIFFDataSource; |
|
5450 } |
|
5451 |
|
5452 - (void)setPromisedDragTIFFDataSource:(WebCore::CachedImage*)source |
|
5453 { |
|
5454 if (source) |
|
5455 source->addClient(promisedDataClient()); |
|
5456 |
|
5457 if (_private->promisedDragTIFFDataSource) |
|
5458 _private->promisedDragTIFFDataSource->removeClient(promisedDataClient()); |
|
5459 _private->promisedDragTIFFDataSource = source; |
|
5460 } |
|
5461 |
|
5462 #undef COMMAND_PROLOGUE |
|
5463 |
|
5464 - (void)_layoutIfNeeded |
|
5465 { |
|
5466 ASSERT(!_private->subviewsSetAside); |
|
5467 |
|
5468 if (_private->needsToApplyStyles || [self _needsLayout]) |
|
5469 [self layout]; |
|
5470 } |
|
5471 |
|
5472 - (void)_web_layoutIfNeededRecursive |
|
5473 { |
|
5474 [self _layoutIfNeeded]; |
|
5475 |
|
5476 #ifndef NDEBUG |
|
5477 _private->enumeratingSubviews = YES; |
|
5478 #endif |
|
5479 |
|
5480 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init]; |
|
5481 |
|
5482 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews]; |
|
5483 |
|
5484 unsigned count = [descendantWebHTMLViews count]; |
|
5485 for (unsigned i = 0; i < count; ++i) |
|
5486 [[descendantWebHTMLViews objectAtIndex:i] _layoutIfNeeded]; |
|
5487 |
|
5488 [descendantWebHTMLViews release]; |
|
5489 |
|
5490 #ifndef NDEBUG |
|
5491 _private->enumeratingSubviews = NO; |
|
5492 #endif |
|
5493 } |
|
5494 |
|
5495 - (void) _destroyAllWebPlugins |
|
5496 { |
|
5497 [[self _pluginController] destroyAllPlugins]; |
|
5498 } |
|
5499 |
|
5500 - (BOOL)_needsLayout |
|
5501 { |
|
5502 return [[self _frame] _needsLayout]; |
|
5503 } |
|
5504 |
|
5505 #if USE(ACCELERATED_COMPOSITING) |
|
5506 - (void)attachRootLayer:(CALayer*)layer |
|
5507 { |
|
5508 if (!_private->layerHostingView) { |
|
5509 NSView* hostingView = [[NSView alloc] initWithFrame:[self bounds]]; |
|
5510 #if !defined(BUILDING_ON_LEOPARD) |
|
5511 [hostingView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; |
|
5512 #endif |
|
5513 [self addSubview:hostingView]; |
|
5514 [hostingView release]; |
|
5515 // hostingView is owned by being a subview of self |
|
5516 _private->layerHostingView = hostingView; |
|
5517 } |
|
5518 |
|
5519 // Make a container layer, which will get sized/positioned by AppKit and CA. |
|
5520 CALayer* viewLayer = [CALayer layer]; |
|
5521 |
|
5522 #if defined(BUILDING_ON_LEOPARD) |
|
5523 // Turn off default animations. |
|
5524 NSNull *nullValue = [NSNull null]; |
|
5525 NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys: |
|
5526 nullValue, @"anchorPoint", |
|
5527 nullValue, @"bounds", |
|
5528 nullValue, @"contents", |
|
5529 nullValue, @"contentsRect", |
|
5530 nullValue, @"opacity", |
|
5531 nullValue, @"position", |
|
5532 nullValue, @"sublayerTransform", |
|
5533 nullValue, @"sublayers", |
|
5534 nullValue, @"transform", |
|
5535 nil]; |
|
5536 [viewLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]]; |
|
5537 #endif |
|
5538 |
|
5539 #if !defined(BUILDING_ON_LEOPARD) |
|
5540 // If we aren't in the window yet, we'll use the screen's scale factor now, and reset the scale |
|
5541 // via -viewDidMoveToWindow. |
|
5542 CGFloat scaleFactor = [self window] ? [[self window] userSpaceScaleFactor] : [[NSScreen mainScreen] userSpaceScaleFactor]; |
|
5543 [viewLayer setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)]; |
|
5544 #endif |
|
5545 |
|
5546 [_private->layerHostingView setLayer:viewLayer]; |
|
5547 [_private->layerHostingView setWantsLayer:YES]; |
|
5548 |
|
5549 // Parent our root layer in the container layer |
|
5550 [viewLayer addSublayer:layer]; |
|
5551 |
|
5552 if ([[self _webView] _postsAcceleratedCompositingNotifications]) |
|
5553 [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:[self _webView] userInfo:nil]; |
|
5554 |
|
5555 #if defined(BUILDING_ON_LEOPARD) |
|
5556 [self _updateLayerHostingViewPosition]; |
|
5557 #endif |
|
5558 } |
|
5559 |
|
5560 - (void)detachRootLayer |
|
5561 { |
|
5562 if (_private->layerHostingView) { |
|
5563 [_private->layerHostingView setLayer:nil]; |
|
5564 [_private->layerHostingView setWantsLayer:NO]; |
|
5565 [_private->layerHostingView removeFromSuperview]; |
|
5566 _private->layerHostingView = nil; |
|
5567 } |
|
5568 } |
|
5569 |
|
5570 #if defined(BUILDING_ON_LEOPARD) |
|
5571 // This method is necessary on Leopard to work around <rdar://problem/7067892>. |
|
5572 - (void)_updateLayerHostingViewPosition |
|
5573 { |
|
5574 if (!_private->layerHostingView) |
|
5575 return; |
|
5576 |
|
5577 const CGFloat maxHeight = 2048; |
|
5578 NSRect layerViewFrame = [self bounds]; |
|
5579 |
|
5580 if (layerViewFrame.size.height > maxHeight) { |
|
5581 CGFloat documentHeight = layerViewFrame.size.height; |
|
5582 |
|
5583 // Clamp the size of the view to <= maxHeight to avoid the bug. |
|
5584 layerViewFrame.size.height = maxHeight; |
|
5585 NSRect visibleRect = [[self enclosingScrollView] documentVisibleRect]; |
|
5586 |
|
5587 // Place the top of the layer-hosting view at the top of the visibleRect. |
|
5588 CGFloat topOffset = NSMinY(visibleRect); |
|
5589 layerViewFrame.origin.y = topOffset; |
|
5590 |
|
5591 // Compensate for the moved view by adjusting the sublayer transform on the view's layer (using flipped coords). |
|
5592 CGFloat bottomOffset = documentHeight - layerViewFrame.size.height - topOffset; |
|
5593 [[_private->layerHostingView layer] setSublayerTransform:CATransform3DMakeTranslation(0, -bottomOffset, 0)]; |
|
5594 } |
|
5595 |
|
5596 [_private->layerHostingView _updateLayerGeometryFromView]; // Workaround for <rdar://problem/7071636> |
|
5597 [_private->layerHostingView setFrame:layerViewFrame]; |
|
5598 } |
|
5599 #endif // defined(BUILDING_ON_LEOPARD) |
|
5600 #endif // USE(ACCELERATED_COMPOSITING) |
|
5601 |
|
5602 @end |
|
5603 |
|
5604 @implementation WebHTMLView (WebNSTextInputSupport) |
|
5605 |
|
5606 - (NSArray *)validAttributesForMarkedText |
|
5607 { |
|
5608 static NSArray *validAttributes; |
|
5609 if (!validAttributes) { |
|
5610 validAttributes = [[NSArray alloc] initWithObjects: |
|
5611 NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, |
|
5612 NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil]; |
|
5613 // NSText also supports the following attributes, but it's |
|
5614 // hard to tell which are really required for text input to |
|
5615 // work well; I have not seen any input method make use of them yet. |
|
5616 // NSFontAttributeName, NSForegroundColorAttributeName, |
|
5617 // NSBackgroundColorAttributeName, NSLanguageAttributeName. |
|
5618 CFRetain(validAttributes); |
|
5619 } |
|
5620 LOG(TextInput, "validAttributesForMarkedText -> (...)"); |
|
5621 return validAttributes; |
|
5622 } |
|
5623 |
|
5624 - (NSTextInputContext *)inputContext |
|
5625 { |
|
5626 return _private->exposeInputContext ? [super inputContext] : nil; |
|
5627 } |
|
5628 |
|
5629 - (NSAttributedString *)textStorage |
|
5630 { |
|
5631 if (!_private->exposeInputContext) { |
|
5632 LOG(TextInput, "textStorage -> nil"); |
|
5633 return nil; |
|
5634 } |
|
5635 NSAttributedString *result = [self attributedSubstringFromRange:NSMakeRange(0, UINT_MAX)]; |
|
5636 |
|
5637 LOG(TextInput, "textStorage -> \"%@\"", result ? [result string] : @""); |
|
5638 |
|
5639 // We have to return an empty string rather than null to prevent TSM from calling -string |
|
5640 return result ? result : [[[NSAttributedString alloc] initWithString:@""] autorelease]; |
|
5641 } |
|
5642 |
|
5643 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint |
|
5644 { |
|
5645 NSWindow *window = [self window]; |
|
5646 WebFrame *frame = [self _frame]; |
|
5647 |
|
5648 if (window) |
|
5649 thePoint = [window convertScreenToBase:thePoint]; |
|
5650 thePoint = [self convertPoint:thePoint fromView:nil]; |
|
5651 |
|
5652 DOMRange *range = [frame _characterRangeAtPoint:thePoint]; |
|
5653 if (!range) { |
|
5654 LOG(TextInput, "characterIndexForPoint:(%f, %f) -> NSNotFound", thePoint.x, thePoint.y); |
|
5655 return NSNotFound; |
|
5656 } |
|
5657 |
|
5658 unsigned result = [frame _convertDOMRangeToNSRange:range].location; |
|
5659 LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result); |
|
5660 return result; |
|
5661 } |
|
5662 |
|
5663 - (NSRect)firstRectForCharacterRange:(NSRange)theRange |
|
5664 { |
|
5665 WebFrame *frame = [self _frame]; |
|
5666 |
|
5667 // Just to match NSTextView's behavior. Regression tests cannot detect this; |
|
5668 // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682 |
|
5669 // (type something; try ranges (1, -1) and (2, -1). |
|
5670 if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0)) |
|
5671 theRange.length = 0; |
|
5672 |
|
5673 DOMRange *range = [frame _convertNSRangeToDOMRange:theRange]; |
|
5674 if (!range) { |
|
5675 LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (0, 0, 0, 0)", theRange.location, theRange.length); |
|
5676 return NSMakeRect(0, 0, 0, 0); |
|
5677 } |
|
5678 |
|
5679 ASSERT([range startContainer]); |
|
5680 ASSERT([range endContainer]); |
|
5681 |
|
5682 NSRect resultRect = [frame _firstRectForDOMRange:range]; |
|
5683 resultRect = [self convertRect:resultRect toView:nil]; |
|
5684 |
|
5685 NSWindow *window = [self window]; |
|
5686 if (window) |
|
5687 resultRect.origin = [window convertBaseToScreen:resultRect.origin]; |
|
5688 |
|
5689 LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", theRange.location, theRange.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height); |
|
5690 return resultRect; |
|
5691 } |
|
5692 |
|
5693 - (NSRange)selectedRange |
|
5694 { |
|
5695 if (!isTextInput(core([self _frame]))) { |
|
5696 LOG(TextInput, "selectedRange -> (NSNotFound, 0)"); |
|
5697 return NSMakeRange(NSNotFound, 0); |
|
5698 } |
|
5699 NSRange result = [[self _frame] _selectedNSRange]; |
|
5700 |
|
5701 LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length); |
|
5702 return result; |
|
5703 } |
|
5704 |
|
5705 - (NSRange)markedRange |
|
5706 { |
|
5707 WebFrame *webFrame = [self _frame]; |
|
5708 Frame* coreFrame = core(webFrame); |
|
5709 if (!coreFrame) |
|
5710 return NSMakeRange(0, 0); |
|
5711 NSRange result = [webFrame _convertToNSRange:coreFrame->editor()->compositionRange().get()]; |
|
5712 |
|
5713 LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length); |
|
5714 return result; |
|
5715 } |
|
5716 |
|
5717 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange |
|
5718 { |
|
5719 WebFrame *frame = [self _frame]; |
|
5720 Frame* coreFrame = core(frame); |
|
5721 if (!isTextInput(coreFrame) || isInPasswordField(coreFrame)) { |
|
5722 LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length); |
|
5723 return nil; |
|
5724 } |
|
5725 DOMRange *domRange = [frame _convertNSRangeToDOMRange:nsRange]; |
|
5726 if (!domRange) { |
|
5727 LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length); |
|
5728 return nil; |
|
5729 } |
|
5730 |
|
5731 NSAttributedString *result = [NSAttributedString _web_attributedStringFromRange:core(domRange)]; |
|
5732 |
|
5733 // [NSAttributedString(WebKitExtras) _web_attributedStringFromRange:] insists on inserting a trailing |
|
5734 // whitespace at the end of the string which breaks the ATOK input method. <rdar://problem/5400551> |
|
5735 // To work around this we truncate the resultant string to the correct length. |
|
5736 if ([result length] > nsRange.length) { |
|
5737 ASSERT([result length] == nsRange.length + 1); |
|
5738 ASSERT([[result string] characterAtIndex:nsRange.length] == '\n' || [[result string] characterAtIndex:nsRange.length] == ' '); |
|
5739 result = [result attributedSubstringFromRange:NSMakeRange(0, nsRange.length)]; |
|
5740 } |
|
5741 LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", nsRange.location, nsRange.length, [result string]); |
|
5742 return result; |
|
5743 } |
|
5744 |
|
5745 // test for 10.4 because of <rdar://problem/4243463> |
|
5746 #ifdef BUILDING_ON_TIGER |
|
5747 - (long)conversationIdentifier |
|
5748 { |
|
5749 return (long)self; |
|
5750 } |
|
5751 #else |
|
5752 - (NSInteger)conversationIdentifier |
|
5753 { |
|
5754 return (NSInteger)self; |
|
5755 } |
|
5756 #endif |
|
5757 |
|
5758 - (BOOL)hasMarkedText |
|
5759 { |
|
5760 Frame* coreFrame = core([self _frame]); |
|
5761 BOOL result = coreFrame && coreFrame->editor()->hasComposition(); |
|
5762 LOG(TextInput, "hasMarkedText -> %u", result); |
|
5763 return result; |
|
5764 } |
|
5765 |
|
5766 - (void)unmarkText |
|
5767 { |
|
5768 LOG(TextInput, "unmarkText"); |
|
5769 |
|
5770 // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. |
|
5771 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters; |
|
5772 _private->interpretKeyEventsParameters = 0; |
|
5773 |
|
5774 if (parameters) { |
|
5775 parameters->eventWasHandled = YES; |
|
5776 parameters->consumedByIM = NO; |
|
5777 } |
|
5778 |
|
5779 if (Frame* coreFrame = core([self _frame])) |
|
5780 coreFrame->editor()->confirmComposition(); |
|
5781 } |
|
5782 |
|
5783 static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result) |
|
5784 { |
|
5785 int length = [[string string] length]; |
|
5786 |
|
5787 int i = 0; |
|
5788 while (i < length) { |
|
5789 NSRange range; |
|
5790 NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)]; |
|
5791 |
|
5792 if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) { |
|
5793 Color color = Color::black; |
|
5794 if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName]) |
|
5795 color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]); |
|
5796 result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1)); |
|
5797 } |
|
5798 |
|
5799 i = range.location + range.length; |
|
5800 } |
|
5801 } |
|
5802 |
|
5803 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange |
|
5804 { |
|
5805 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString |
|
5806 |
|
5807 LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length); |
|
5808 |
|
5809 // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. |
|
5810 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters; |
|
5811 _private->interpretKeyEventsParameters = 0; |
|
5812 |
|
5813 if (parameters) { |
|
5814 parameters->eventWasHandled = YES; |
|
5815 parameters->consumedByIM = NO; |
|
5816 } |
|
5817 |
|
5818 Frame* coreFrame = core([self _frame]); |
|
5819 if (!coreFrame) |
|
5820 return; |
|
5821 |
|
5822 if (![self _isEditable]) |
|
5823 return; |
|
5824 |
|
5825 Vector<CompositionUnderline> underlines; |
|
5826 NSString *text = string; |
|
5827 |
|
5828 if (isAttributedString) { |
|
5829 unsigned markedTextLength = [(NSString *)string length]; |
|
5830 NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:NULL inRange:NSMakeRange(0, markedTextLength)]; |
|
5831 LOG(TextInput, " ReplacementRange: %@", rangeString); |
|
5832 // The AppKit adds a 'secret' property to the string that contains the replacement range. |
|
5833 // The replacement range is the range of the the text that should be replaced with the new string. |
|
5834 if (rangeString) |
|
5835 [[self _frame] _selectNSRange:NSRangeFromString(rangeString)]; |
|
5836 |
|
5837 text = [string string]; |
|
5838 extractUnderlines(string, underlines); |
|
5839 } |
|
5840 |
|
5841 coreFrame->editor()->setComposition(text, underlines, newSelRange.location, NSMaxRange(newSelRange)); |
|
5842 } |
|
5843 |
|
5844 - (void)doCommandBySelector:(SEL)selector |
|
5845 { |
|
5846 LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector)); |
|
5847 |
|
5848 // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. |
|
5849 // The same call to interpretKeyEvents can do more than one command. |
|
5850 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters; |
|
5851 if (parameters) |
|
5852 parameters->consumedByIM = NO; |
|
5853 |
|
5854 if (selector == @selector(noop:)) { |
|
5855 _private->receivedNOOP = YES; |
|
5856 return; |
|
5857 } |
|
5858 |
|
5859 KeyboardEvent* event = parameters ? parameters->event : 0; |
|
5860 bool shouldSaveCommand = parameters && parameters->shouldSaveCommand; |
|
5861 |
|
5862 if (event && shouldSaveCommand) |
|
5863 event->keypressCommands().append(KeypressCommand(NSStringFromSelector(selector))); |
|
5864 else { |
|
5865 // Make sure that only direct calls to doCommandBySelector: see the parameters by setting to 0. |
|
5866 _private->interpretKeyEventsParameters = 0; |
|
5867 |
|
5868 bool eventWasHandled; |
|
5869 |
|
5870 WebView *webView = [self _webView]; |
|
5871 if ([[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector]) |
|
5872 eventWasHandled = true; |
|
5873 else { |
|
5874 Editor::Command command = [self coreCommandBySelector:selector]; |
|
5875 if (command.isSupported()) |
|
5876 eventWasHandled = command.execute(event); |
|
5877 else { |
|
5878 // If WebKit does not support this command, we need to pass the selector to super. |
|
5879 _private->selectorForDoCommandBySelector = selector; |
|
5880 |
|
5881 // The sink does two things: 1) Tells us if the responder went unhandled, and |
|
5882 // 2) prevents any NSBeep; we don't ever want to beep here. |
|
5883 WebResponderChainSink *sink = [[WebResponderChainSink alloc] initWithResponderChain:self]; |
|
5884 [super doCommandBySelector:selector]; |
|
5885 eventWasHandled = ![sink receivedUnhandledCommand]; |
|
5886 [sink detach]; |
|
5887 [sink release]; |
|
5888 |
|
5889 _private->selectorForDoCommandBySelector = 0; |
|
5890 } |
|
5891 } |
|
5892 |
|
5893 if (parameters) |
|
5894 parameters->eventWasHandled = eventWasHandled; |
|
5895 |
|
5896 // Restore the parameters so that other calls to doCommandBySelector: see them, |
|
5897 // and other commands can participate in setting the "eventWasHandled" flag. |
|
5898 _private->interpretKeyEventsParameters = parameters; |
|
5899 } |
|
5900 } |
|
5901 |
|
5902 - (void)insertText:(id)string |
|
5903 { |
|
5904 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString |
|
5905 |
|
5906 LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string); |
|
5907 |
|
5908 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters; |
|
5909 _private->interpretKeyEventsParameters = 0; |
|
5910 if (parameters) |
|
5911 parameters->consumedByIM = NO; |
|
5912 |
|
5913 // We don't support inserting an attributed string but input methods don't appear to require this. |
|
5914 RefPtr<Frame> coreFrame = core([self _frame]); |
|
5915 NSString *text; |
|
5916 bool isFromInputMethod = coreFrame && coreFrame->editor()->hasComposition(); |
|
5917 if (isAttributedString) { |
|
5918 text = [string string]; |
|
5919 // We deal with the NSTextInputReplacementRangeAttributeName attribute from NSAttributedString here |
|
5920 // simply because it is used by at least one Input Method -- it corresonds to the kEventParamTextInputSendReplaceRange |
|
5921 // event in TSM. This behaviour matches that of -[WebHTMLView setMarkedText:selectedRange:] when it receives an |
|
5922 // NSAttributedString |
|
5923 NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:NULL inRange:NSMakeRange(0, [text length])]; |
|
5924 LOG(TextInput, " ReplacementRange: %@", rangeString); |
|
5925 if (rangeString) { |
|
5926 [[self _frame] _selectNSRange:NSRangeFromString(rangeString)]; |
|
5927 isFromInputMethod = YES; |
|
5928 } |
|
5929 } else |
|
5930 text = string; |
|
5931 |
|
5932 bool eventHandled = false; |
|
5933 if ([text length]) { |
|
5934 KeyboardEvent* event = parameters ? parameters->event : 0; |
|
5935 |
|
5936 // insertText can be called from an input method or from normal key event processing |
|
5937 // If its from normal key event processing, we may need to save the action to perform it later. |
|
5938 // If its from an input method, then we should go ahead and insert the text now. |
|
5939 // We assume it's from the input method if we have marked text. |
|
5940 // FIXME: In theory, this could be wrong for some input methods, so we should try to find |
|
5941 // another way to determine if the call is from the input method |
|
5942 bool shouldSaveCommand = parameters && parameters->shouldSaveCommand; |
|
5943 if (event && shouldSaveCommand && !isFromInputMethod) { |
|
5944 event->keypressCommands().append(KeypressCommand("insertText:", text)); |
|
5945 _private->interpretKeyEventsParameters = parameters; |
|
5946 return; |
|
5947 } |
|
5948 |
|
5949 String eventText = text; |
|
5950 eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore |
|
5951 if (coreFrame && coreFrame->editor()->canEdit()) { |
|
5952 if (!coreFrame->editor()->hasComposition()) |
|
5953 eventHandled = coreFrame->editor()->insertText(eventText, event); |
|
5954 else { |
|
5955 eventHandled = true; |
|
5956 coreFrame->editor()->confirmComposition(eventText); |
|
5957 } |
|
5958 } |
|
5959 } |
|
5960 |
|
5961 if (!parameters) |
|
5962 return; |
|
5963 |
|
5964 if (isFromInputMethod) { |
|
5965 // Allow doCommandBySelector: to be called after insertText: by resetting interpretKeyEventsParameters |
|
5966 _private->interpretKeyEventsParameters = parameters; |
|
5967 parameters->consumedByIM = YES; |
|
5968 return; |
|
5969 } |
|
5970 |
|
5971 parameters->eventWasHandled = eventHandled; |
|
5972 } |
|
5973 |
|
5974 - (void)_updateSelectionForInputManager |
|
5975 { |
|
5976 Frame* coreFrame = core([self _frame]); |
|
5977 if (!coreFrame) |
|
5978 return; |
|
5979 |
|
5980 BOOL exposeInputContext = isTextInput(coreFrame) && !isInPasswordField(coreFrame); |
|
5981 if (exposeInputContext != _private->exposeInputContext) { |
|
5982 _private->exposeInputContext = exposeInputContext; |
|
5983 // Let AppKit cache a potentially changed input context. |
|
5984 // WebCore routinely sets the selection to None when editing, and IMs become unhappy when an input context suddenly turns nil, see bug 26009. |
|
5985 if (!coreFrame->selection()->isNone()) |
|
5986 [NSApp updateWindows]; |
|
5987 } |
|
5988 |
|
5989 if (!coreFrame->editor()->hasComposition()) |
|
5990 return; |
|
5991 |
|
5992 if (coreFrame->editor()->ignoreCompositionSelectionChange()) |
|
5993 return; |
|
5994 |
|
5995 unsigned start; |
|
5996 unsigned end; |
|
5997 if (coreFrame->editor()->getCompositionSelection(start, end)) |
|
5998 [[NSInputManager currentInputManager] markedTextSelectionChanged:NSMakeRange(start, end - start) client:self]; |
|
5999 else { |
|
6000 coreFrame->editor()->confirmCompositionWithoutDisturbingSelection(); |
|
6001 [[NSInputManager currentInputManager] markedTextAbandoned:self]; |
|
6002 } |
|
6003 } |
|
6004 |
|
6005 @end |
|
6006 |
|
6007 @implementation WebHTMLView (WebDocumentPrivateProtocols) |
|
6008 |
|
6009 - (NSRect)selectionRect |
|
6010 { |
|
6011 if ([self _hasSelection]) |
|
6012 return core([self _frame])->selectionBounds(); |
|
6013 return NSZeroRect; |
|
6014 } |
|
6015 |
|
6016 - (NSArray *)selectionTextRects |
|
6017 { |
|
6018 if (![self _hasSelection]) |
|
6019 return nil; |
|
6020 |
|
6021 Vector<FloatRect> list; |
|
6022 if (Frame* coreFrame = core([self _frame])) |
|
6023 coreFrame->selectionTextRects(list, Frame::RespectTransforms); |
|
6024 |
|
6025 unsigned size = list.size(); |
|
6026 NSMutableArray *result = [[[NSMutableArray alloc] initWithCapacity:size] autorelease]; |
|
6027 for (unsigned i = 0; i < size; ++i) |
|
6028 [result addObject:[NSValue valueWithRect:list[i]]]; |
|
6029 |
|
6030 return result; |
|
6031 } |
|
6032 |
|
6033 - (NSView *)selectionView |
|
6034 { |
|
6035 return self; |
|
6036 } |
|
6037 |
|
6038 - (NSImage *)selectionImageForcingBlackText:(BOOL)forceBlackText |
|
6039 { |
|
6040 if ([self _hasSelection]) |
|
6041 return core([self _frame])->selectionImage(forceBlackText); |
|
6042 return nil; |
|
6043 } |
|
6044 |
|
6045 - (NSRect)selectionImageRect |
|
6046 { |
|
6047 if ([self _hasSelection]) |
|
6048 return core([self _frame])->selectionBounds(); |
|
6049 return NSZeroRect; |
|
6050 } |
|
6051 |
|
6052 - (NSArray *)pasteboardTypesForSelection |
|
6053 { |
|
6054 if ([self _canSmartCopyOrDelete]) { |
|
6055 NSMutableArray *types = [[[[self class] _selectionPasteboardTypes] mutableCopy] autorelease]; |
|
6056 [types addObject:WebSmartPastePboardType]; |
|
6057 return types; |
|
6058 } else { |
|
6059 return [[self class] _selectionPasteboardTypes]; |
|
6060 } |
|
6061 } |
|
6062 |
|
6063 - (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard |
|
6064 { |
|
6065 [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:nil]; |
|
6066 } |
|
6067 |
|
6068 - (void)selectAll |
|
6069 { |
|
6070 Frame* coreFrame = core([self _frame]); |
|
6071 if (coreFrame) |
|
6072 coreFrame->selection()->selectAll(); |
|
6073 } |
|
6074 |
|
6075 - (void)deselectAll |
|
6076 { |
|
6077 Frame* coreFrame = core([self _frame]); |
|
6078 if (!coreFrame) |
|
6079 return; |
|
6080 coreFrame->selection()->clear(); |
|
6081 } |
|
6082 |
|
6083 - (NSString *)string |
|
6084 { |
|
6085 return [[self _frame] _stringForRange:[self _documentRange]]; |
|
6086 } |
|
6087 |
|
6088 - (NSAttributedString *)_attributeStringFromDOMRange:(DOMRange *)range |
|
6089 { |
|
6090 NSAttributedString *attributedString; |
|
6091 #if !LOG_DISABLED |
|
6092 double start = CFAbsoluteTimeGetCurrent(); |
|
6093 #endif |
|
6094 attributedString = [[[NSAttributedString alloc] _initWithDOMRange:range] autorelease]; |
|
6095 #if !LOG_DISABLED |
|
6096 double duration = CFAbsoluteTimeGetCurrent() - start; |
|
6097 LOG(Timing, "creating attributed string from selection took %f seconds.", duration); |
|
6098 #endif |
|
6099 return attributedString; |
|
6100 } |
|
6101 |
|
6102 - (NSAttributedString *)attributedString |
|
6103 { |
|
6104 DOMDocument *document = [[self _frame] DOMDocument]; |
|
6105 NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[document _documentRange]]; |
|
6106 if (!attributedString) { |
|
6107 Document* coreDocument = core(document); |
|
6108 attributedString = [NSAttributedString _web_attributedStringFromRange:Range::create(coreDocument, coreDocument, 0, coreDocument, coreDocument->childNodeCount()).get()]; |
|
6109 } |
|
6110 return attributedString; |
|
6111 } |
|
6112 |
|
6113 - (NSString *)selectedString |
|
6114 { |
|
6115 return [[self _frame] _selectedString]; |
|
6116 } |
|
6117 |
|
6118 - (NSAttributedString *)selectedAttributedString |
|
6119 { |
|
6120 NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[self _selectedRange]]; |
|
6121 if (!attributedString) { |
|
6122 Frame* coreFrame = core([self _frame]); |
|
6123 if (coreFrame) { |
|
6124 RefPtr<Range> range = coreFrame->selection()->selection().toNormalizedRange(); |
|
6125 attributedString = [NSAttributedString _web_attributedStringFromRange:range.get()]; |
|
6126 } |
|
6127 } |
|
6128 return attributedString; |
|
6129 } |
|
6130 |
|
6131 - (BOOL)supportsTextEncoding |
|
6132 { |
|
6133 return YES; |
|
6134 } |
|
6135 |
|
6136 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection |
|
6137 { |
|
6138 if (![string length]) |
|
6139 return NO; |
|
6140 Frame* coreFrame = core([self _frame]); |
|
6141 return coreFrame && coreFrame->findString(string, forward, caseFlag, wrapFlag, startInSelection); |
|
6142 } |
|
6143 |
|
6144 @end |
|
6145 |
|
6146 @implementation WebHTMLView (WebDocumentInternalProtocols) |
|
6147 |
|
6148 - (NSDictionary *)elementAtPoint:(NSPoint)point |
|
6149 { |
|
6150 return [self elementAtPoint:point allowShadowContent:NO]; |
|
6151 } |
|
6152 |
|
6153 - (NSDictionary *)elementAtPoint:(NSPoint)point allowShadowContent:(BOOL)allow |
|
6154 { |
|
6155 Frame* coreFrame = core([self _frame]); |
|
6156 if (!coreFrame) |
|
6157 return nil; |
|
6158 return [[[WebElementDictionary alloc] initWithHitTestResult:coreFrame->eventHandler()->hitTestResultAtPoint(IntPoint(point), allow)] autorelease]; |
|
6159 } |
|
6160 |
|
6161 - (NSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag limit:(NSUInteger)limit |
|
6162 { |
|
6163 Frame* coreFrame = core([self _frame]); |
|
6164 if (!coreFrame) |
|
6165 return 0; |
|
6166 return coreFrame->markAllMatchesForText(string, caseFlag, limit); |
|
6167 } |
|
6168 |
|
6169 - (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue |
|
6170 { |
|
6171 Frame* coreFrame = core([self _frame]); |
|
6172 if (!coreFrame) |
|
6173 return; |
|
6174 coreFrame->setMarkedTextMatchesAreHighlighted(newValue); |
|
6175 } |
|
6176 |
|
6177 - (BOOL)markedTextMatchesAreHighlighted |
|
6178 { |
|
6179 Frame* coreFrame = core([self _frame]); |
|
6180 return coreFrame && coreFrame->markedTextMatchesAreHighlighted(); |
|
6181 } |
|
6182 |
|
6183 - (void)unmarkAllTextMatches |
|
6184 { |
|
6185 Frame* coreFrame = core([self _frame]); |
|
6186 if (!coreFrame) |
|
6187 return; |
|
6188 Document* document = coreFrame->document(); |
|
6189 if (!document) |
|
6190 return; |
|
6191 document->removeMarkers(DocumentMarker::TextMatch); |
|
6192 } |
|
6193 |
|
6194 - (NSArray *)rectsForTextMatches |
|
6195 { |
|
6196 Frame* coreFrame = core([self _frame]); |
|
6197 if (!coreFrame) |
|
6198 return [NSArray array]; |
|
6199 Document* document = coreFrame->document(); |
|
6200 if (!document) |
|
6201 return [NSArray array]; |
|
6202 |
|
6203 Vector<IntRect> rects = document->renderedRectsForMarkers(DocumentMarker::TextMatch); |
|
6204 unsigned count = rects.size(); |
|
6205 NSMutableArray *result = [NSMutableArray arrayWithCapacity:count]; |
|
6206 for (unsigned index = 0; index < count; ++index) |
|
6207 [result addObject:[NSValue valueWithRect:rects[index]]]; |
|
6208 return result; |
|
6209 } |
|
6210 |
|
6211 @end |
|
6212 |
|
6213 // This is used by AppKit and is included here so that WebDataProtocolScheme is only defined once. |
|
6214 @implementation NSURL (WebDataURL) |
|
6215 |
|
6216 + (NSURL *)_web_uniqueWebDataURL |
|
6217 { |
|
6218 CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault); |
|
6219 NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef); |
|
6220 CFRelease(UUIDRef); |
|
6221 NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", WebDataProtocolScheme, UUIDString]]; |
|
6222 CFRelease(UUIDString); |
|
6223 return URL; |
|
6224 } |
|
6225 |
|
6226 @end |
|
6227 |
|
6228 @implementation WebResponderChainSink |
|
6229 |
|
6230 - (id)initWithResponderChain:(NSResponder *)chain |
|
6231 { |
|
6232 self = [super init]; |
|
6233 _lastResponderInChain = chain; |
|
6234 while (NSResponder *next = [_lastResponderInChain nextResponder]) |
|
6235 _lastResponderInChain = next; |
|
6236 [_lastResponderInChain setNextResponder:self]; |
|
6237 return self; |
|
6238 } |
|
6239 |
|
6240 - (void)detach |
|
6241 { |
|
6242 [_lastResponderInChain setNextResponder:nil]; |
|
6243 _lastResponderInChain = nil; |
|
6244 } |
|
6245 |
|
6246 - (BOOL)receivedUnhandledCommand |
|
6247 { |
|
6248 return _receivedUnhandledCommand; |
|
6249 } |
|
6250 |
|
6251 - (void)noResponderFor:(SEL)selector |
|
6252 { |
|
6253 _receivedUnhandledCommand = YES; |
|
6254 } |
|
6255 |
|
6256 - (void)doCommandBySelector:(SEL)selector |
|
6257 { |
|
6258 _receivedUnhandledCommand = YES; |
|
6259 } |
|
6260 |
|
6261 - (BOOL)tryToPerform:(SEL)action with:(id)object |
|
6262 { |
|
6263 _receivedUnhandledCommand = YES; |
|
6264 return YES; |
|
6265 } |
|
6266 |
|
6267 @end |