2
* Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
3
* (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
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.
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.
30
#import "WebHTMLView.h"
32
#import "DOMNodeInternal.h"
33
#import "DOMRangeInternal.h"
34
#import "WebArchive.h"
35
#import "WebArchiver.h"
36
#import "WebBaseNetscapePluginViewInternal.h"
37
#import "WebClipView.h"
38
#import "WebDOMOperationsPrivate.h"
39
#import "WebDataSourceInternal.h"
40
#import "WebDefaultUIDelegate.h"
41
#import "WebDocumentInternal.h"
42
#import "WebEditingDelegate.h"
43
#import "WebElementDictionary.h"
44
#import "WebFrameBridge.h"
45
#import "WebFrameInternal.h"
46
#import "WebFramePrivate.h"
47
#import "WebFrameViewInternal.h"
48
#import "WebHTMLRepresentationPrivate.h"
49
#import "WebHTMLViewInternal.h"
50
#import "WebKitLogging.h"
51
#import "WebKitNSStringExtras.h"
52
#import "WebKitPluginContainerView.h"
53
#import "WebKitVersionChecks.h"
54
#import "WebLocalizableStrings.h"
55
#import "WebNSAttributedStringExtras.h"
56
#import "WebNSEventExtras.h"
57
#import "WebNSFileManagerExtras.h"
58
#import "WebNSImageExtras.h"
59
#import "WebNSObjectExtras.h"
60
#import "WebNSPasteboardExtras.h"
61
#import "WebNSPrintOperationExtras.h"
62
#import "WebNSURLExtras.h"
63
#import "WebNSViewExtras.h"
64
#import "WebNetscapePluginEmbeddedView.h"
65
#import "WebPluginController.h"
66
#import "WebPreferences.h"
67
#import "WebPreferencesPrivate.h"
68
#import "WebResourcePrivate.h"
69
#import "WebStringTruncator.h"
70
#import "WebUIDelegatePrivate.h"
71
#import "WebViewInternal.h"
72
#import <AppKit/NSAccessibility.h>
73
#import <ApplicationServices/ApplicationServices.h>
75
#import <WebCore/CachedImage.h>
76
#import <WebCore/CachedResourceClient.h>
77
#import <WebCore/ContextMenu.h>
78
#import <WebCore/ContextMenuController.h>
79
#import <WebCore/Document.h>
80
#import <WebCore/Editor.h>
81
#import <WebCore/EditorDeleteAction.h>
82
#import <WebCore/Element.h>
83
#import <WebCore/EventHandler.h>
84
#import <WebCore/EventNames.h>
85
#import <WebCore/ExceptionHandlers.h>
86
#import <WebCore/DragController.h>
87
#import <WebCore/FloatRect.h>
88
#import <WebCore/FocusController.h>
89
#import <WebCore/Frame.h>
90
#import <WebCore/FrameLoader.h>
91
#import <WebCore/FrameView.h>
92
#import <WebCore/HitTestResult.h>
93
#import <WebCore/HTMLNames.h>
94
#import <WebCore/Image.h>
95
#import <WebCore/KeyboardEvent.h>
96
#import <WebCore/MIMETypeRegistry.h>
97
#import <WebCore/Page.h>
98
#import <WebCore/PlatformKeyboardEvent.h>
99
#import <WebCore/PlatformMouseEvent.h>
100
#import <WebCore/Range.h>
101
#import <WebCore/SelectionController.h>
102
#import <WebCore/SharedBuffer.h>
103
#import <WebCore/WebCoreObjCExtras.h>
104
#import <WebCore/WebCoreTextRenderer.h>
105
#import <WebKit/DOM.h>
106
#import <WebKit/DOMExtensions.h>
107
#import <WebKit/DOMPrivate.h>
108
#import <WebKitSystemInterface.h>
110
using namespace WebCore;
111
using namespace HTMLNames;
115
// Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers.
117
extern NSString *NSMarkedClauseSegmentAttributeName;
118
extern NSString *NSTextInputReplacementRangeAttributeName;
120
// Kill ring calls. Would be better to use NSKillRing.h, but that's not available as API or SPI.
122
void _NSInitializeKillRing(void);
123
void _NSAppendToKillRing(NSString *);
124
void _NSPrependToKillRing(NSString *);
125
NSString *_NSYankFromKillRing(void);
126
NSString *_NSYankPreviousFromKillRing(void);
127
void _NSNewKillRingSequence(void);
128
void _NSSetKillRingToYankedState(void);
129
void _NSResetKillRingOperationFlag(void);
133
@interface NSView (AppKitSecretsIKnowAbout)
134
- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
135
- (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
136
- (NSRect)_dirtyRect;
137
- (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
138
- (void)_propagateDirtyRectsToOpaqueAncestors;
139
- (void)_windowChangedKeyState;
142
@interface NSApplication (AppKitSecretsIKnowAbout)
143
- (void)speakString:(NSString *)string;
146
@interface NSWindow (AppKitSecretsIKnowAbout)
147
- (id)_newFirstResponderAfterResigning;
148
- (void)_setForceActiveControls:(BOOL)flag;
151
@interface NSAttributedString (AppKitSecretsIKnowAbout)
152
- (id)_initWithDOMRange:(DOMRange *)domRange;
153
- (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
156
@interface NSSpellChecker (CurrentlyPrivateForTextView)
157
- (void)learnWord:(NSString *)word;
160
// By imaging to a width a little wider than the available pixels,
161
// thin pages will be scaled down a little, matching the way they
162
// print in IE and Camino. This lets them use fewer sheets than they
163
// would otherwise, which is presumably why other browsers do this.
164
// Wide pages will be scaled down more than this.
165
#define PrintingMinimumShrinkFactor 1.25f
167
// This number determines how small we are willing to reduce the page content
168
// in order to accommodate the widest line. If the page would have to be
169
// reduced smaller to make the widest line fit, we just clip instead (this
170
// behavior matches MacIE and Mozilla, at least)
171
#define PrintingMaximumShrinkFactor 2.0f
173
// This number determines how short the last printed page of a multi-page print session
174
// can be before we try to shrink the scale in order to reduce the number of pages, and
175
// thus eliminate the orphan.
176
#define LastPrintedPageOrphanRatio 0.1f
178
// This number determines the amount the scale factor is adjusted to try to eliminate orphans.
179
// It has no direct mathematical relationship to LastPrintedPageOrphanRatio, due to variable
180
// numbers of pages, logic to avoid breaking elements, and CSS-supplied hard page breaks.
181
#define PrintingOrphanShrinkAdjustment 1.1f
183
#define AUTOSCROLL_INTERVAL 0.1f
185
#define DRAG_LABEL_BORDER_X 4.0f
186
//Keep border_y in synch with DragController::LinkDragBorderInset
187
#define DRAG_LABEL_BORDER_Y 2.0f
188
#define DRAG_LABEL_RADIUS 5.0f
189
#define DRAG_LABEL_BORDER_Y_OFFSET 2.0f
191
#define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP 120.0f
192
#define MAX_DRAG_LABEL_WIDTH 320.0f
194
#define DRAG_LINK_LABEL_FONT_SIZE 11.0f
195
#define DRAG_LINK_URL_FONT_SIZE 10.0f
197
// Any non-zero value will do, but using something recognizable might help us debug some day.
198
#define TRACKING_RECT_TAG 0xBADFACE
200
// FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
201
#define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
203
#define STANDARD_WEIGHT 5
204
#define MIN_BOLD_WEIGHT 9
205
#define STANDARD_BOLD_WEIGHT 10
208
#define WebDataProtocolScheme @"webkit-fake-url"
210
// <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present
211
// in some NIB files, so NSUnarchiver must be still able to look up this now-unused class.
212
@interface WebCoreScrollView : NSScrollView
215
@implementation WebCoreScrollView
218
// if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
219
static BOOL forceNSViewHitTest;
221
// 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])
222
static BOOL forceWebHTMLViewHitTest;
224
static WebHTMLView *lastHitView;
226
// We need this to be able to safely reference the CachedImage for the promised drag data
227
static CachedResourceClient* promisedDataClient()
229
static CachedResourceClient* staticCachedResourceClient = new CachedResourceClient;
230
return staticCachedResourceClient;
233
@interface WebHTMLView (WebTextSizing) <_WebDocumentTextSizing>
236
@interface WebHTMLView (WebHTMLViewFileInternal)
237
- (BOOL)_imageExistsAtPaths:(NSArray *)paths;
238
- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText chosePlainText:(BOOL *)chosePlainText;
239
- (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
240
- (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
241
- (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
242
- (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
243
- (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
244
- (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
245
- (float)_calculatePrintHeight;
246
- (void)_updateTextSizeMultiplier;
247
- (DOMRange *)_selectedRange;
248
- (BOOL)_shouldDeleteRange:(DOMRange *)range;
249
- (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard;
250
- (NSView *)_hitViewForEvent:(NSEvent *)event;
251
- (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString;
252
- (DOMRange *)_documentRange;
253
- (WebFrameBridge *)_bridge;
254
- (void)_setMouseDownEvent:(NSEvent *)event;
255
- (WebHTMLView *)_topHTMLView;
256
- (BOOL)_isTopHTMLView;
257
- (void)_web_setPrintingModeRecursive;
258
- (void)_web_setPrintingModeRecursiveAndAdjustViewSize;
259
- (void)_web_clearPrintingModeRecursive;
260
- (void)_web_layoutIfNeededRecursive;
263
@interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
264
- (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize;
267
@interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
268
- (void)_updateSelectionForInputManager;
271
@interface WebHTMLView (WebEditingStyleSupport)
272
- (DOMCSSStyleDeclaration *)_emptyStyle;
273
- (NSString *)_colorAsString:(NSColor *)color;
276
@interface NSView (WebHTMLViewFileInternal)
277
- (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array;
280
@interface NSMutableDictionary (WebHTMLViewFileInternal)
281
- (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
284
// Handles the complete: text command
285
@interface WebTextCompleteController : NSObject {
288
NSWindow *_popupWindow;
289
NSTableView *_tableView;
290
NSArray *_completions;
291
NSString *_originalString;
294
- (id)initWithHTMLView:(WebHTMLView *)view;
295
- (void)doCompletion;
296
- (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft;
297
- (BOOL)popupWindowIsOpen;
298
- (BOOL)filterKeyDown:(NSEvent *)event;
299
- (void)_reflectSelection;
302
struct WebHTMLViewInterpretKeyEventsParameters {
303
KeyboardEvent* event;
304
BOOL eventWasHandled;
305
BOOL shouldSaveCommand;
306
// The Input Method may consume an event and not tell us, in
307
// which case we should not bubble the event up the DOM
311
@implementation WebHTMLViewPrivate
313
#ifndef BUILDING_ON_TIGER
316
WebCoreObjCFinalizeOnMainThread(self);
322
ASSERT(!autoscrollTimer);
323
ASSERT(!autoscrollTriggerEvent);
324
ASSERT(!updateActiveStateTimer);
325
ASSERT(!updateMouseoverTimer);
327
[mouseDownEvent release];
328
[keyDownEvent release];
329
[pluginController release];
331
[compController release];
332
[firstResponderTextViewAtMouseDownTime release];
333
[dataSource release];
334
[highlighters release];
335
if (promisedDragTIFFDataSource)
336
promisedDragTIFFDataSource->deref(promisedDataClient());
343
ASSERT_MAIN_THREAD();
345
if (promisedDragTIFFDataSource)
346
promisedDragTIFFDataSource->deref(promisedDataClient());
353
[mouseDownEvent release];
354
[keyDownEvent release];
355
[pluginController release];
357
[compController release];
358
[firstResponderTextViewAtMouseDownTime release];
359
[dataSource release];
360
[highlighters release];
361
if (promisedDragTIFFDataSource)
362
promisedDragTIFFDataSource->deref(promisedDataClient());
364
mouseDownEvent = nil;
366
pluginController = nil;
368
compController = nil;
369
firstResponderTextViewAtMouseDownTime = nil;
372
promisedDragTIFFDataSource = 0;
377
@implementation WebHTMLView (WebHTMLViewFileInternal)
379
- (DOMRange *)_documentRange
381
return [[[self _frame] DOMDocument] _documentRange];
384
- (BOOL)_imageExistsAtPaths:(NSArray *)paths
386
NSEnumerator *enumerator = [paths objectEnumerator];
389
while ((path = [enumerator nextObject]) != nil) {
390
NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
391
if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType))
398
- (WebDataSource *)_dataSource
400
return _private->dataSource;
403
- (WebFrameBridge *)_bridge
405
return [_private->dataSource _bridge];
408
- (WebView *)_webView
410
return [_private->dataSource _webView];
413
- (WebFrameView *)_frameView
415
return [[_private->dataSource webFrame] frameView];
418
- (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
420
DOMDocumentFragment *fragment;
421
NSEnumerator *enumerator = [paths objectEnumerator];
422
NSMutableArray *domNodes = [[NSMutableArray alloc] init];
425
while ((path = [enumerator nextObject]) != nil) {
426
// Non-image file types; _web_userVisibleString is appropriate here because this will
427
// be pasted as visible text.
428
NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString];
429
[domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]];
432
fragment = [[self _bridge] documentFragmentWithNodesAsParagraphs:domNodes];
436
return [fragment firstChild] != nil ? fragment : nil;
439
+ (NSArray *)_excludedElementsForAttributedStringConversion
441
static NSArray *elements = nil;
442
if (elements == nil) {
443
elements = [[NSArray alloc] initWithObjects:
444
// Omit style since we want style to be inline so the fragment can be easily inserted.
446
// Omit xml so the result is not XHTML.
448
// Omit tags that will get stripped when converted to a fragment anyway.
449
@"doctype", @"html", @"head", @"body",
450
// Omit deprecated tags.
451
@"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
452
// Omit object so no file attachments are part of the fragment.
459
static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
461
CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
462
NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
464
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]];
465
CFRelease(UUIDString);
470
- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
471
inContext:(DOMRange *)context
472
allowPlainText:(BOOL)allowPlainText
473
chosePlainText:(BOOL *)chosePlainText
475
NSArray *types = [pasteboard types];
476
*chosePlainText = NO;
477
DOMDocumentFragment *fragment = nil;
479
if ([types containsObject:WebArchivePboardType] &&
480
(fragment = [self _documentFragmentFromPasteboard:pasteboard
481
forType:WebArchivePboardType
486
if ([types containsObject:NSFilenamesPboardType] &&
487
(fragment = [self _documentFragmentFromPasteboard:pasteboard
488
forType:NSFilenamesPboardType
493
if ([types containsObject:NSHTMLPboardType] &&
494
(fragment = [self _documentFragmentFromPasteboard:pasteboard
495
forType:NSHTMLPboardType
500
if ([types containsObject:NSRTFPboardType] &&
501
(fragment = [self _documentFragmentFromPasteboard:pasteboard
502
forType:NSRTFPboardType
507
if ([types containsObject:NSRTFDPboardType] &&
508
(fragment = [self _documentFragmentFromPasteboard:pasteboard
509
forType:NSRTFDPboardType
514
if ([types containsObject:NSTIFFPboardType] &&
515
(fragment = [self _documentFragmentFromPasteboard:pasteboard
516
forType:NSTIFFPboardType
521
if ([types containsObject:NSPICTPboardType] &&
522
(fragment = [self _documentFragmentFromPasteboard:pasteboard
523
forType:NSPICTPboardType
528
if ([types containsObject:NSURLPboardType] &&
529
(fragment = [self _documentFragmentFromPasteboard:pasteboard
530
forType:NSURLPboardType
535
if (allowPlainText && [types containsObject:NSStringPboardType] &&
536
(fragment = [self _documentFragmentFromPasteboard:pasteboard
537
forType:NSStringPboardType
540
*chosePlainText = YES;
547
- (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
549
NSArray *types = [pasteboard types];
551
if ([types containsObject:NSStringPboardType])
552
return [pasteboard stringForType:NSStringPboardType];
554
NSAttributedString *attributedString = nil;
557
if ([types containsObject:NSRTFDPboardType])
558
attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
559
if (attributedString == nil && [types containsObject:NSRTFPboardType])
560
attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
561
if (attributedString != nil) {
562
string = [[attributedString string] copy];
563
[attributedString release];
564
return [string autorelease];
567
if ([types containsObject:NSFilenamesPboardType]) {
568
string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
575
if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
576
string = [URL _web_userVisibleString];
577
if ([string length] > 0)
584
- (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
586
DOMRange *range = [self _selectedRange];
588
DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard
589
inContext:range allowPlainText:allowPlainText chosePlainText:&chosePlainText];
590
WebFrameBridge *bridge = [self _bridge];
591
if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:[self _selectedRange] givenAction:WebViewInsertActionPasted]) {
592
[bridge replaceSelectionWithFragment:fragment selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard] matchStyle:chosePlainText];
596
- (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard
598
NSString *text = [self _plainTextFromPasteboard:pasteboard];
599
if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted])
600
[[self _bridge] replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]];
603
- (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
605
WebView *webView = [self _webView];
606
DOMNode *child = [fragment firstChild];
607
if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]])
608
return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
609
return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
612
- (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
614
WebView *webView = [self _webView];
615
return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
618
- (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
620
return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
623
// Calculate the vertical size of the view that fits on a single page
624
- (float)_calculatePrintHeight
626
// Obtain the print info object for the current operation
627
NSPrintInfo *pi = [[NSPrintOperation currentOperation] printInfo];
629
// Calculate the page height in points
630
NSSize paperSize = [pi paperSize];
631
return paperSize.height - [pi topMargin] - [pi bottomMargin];
634
- (void)_updateTextSizeMultiplier
636
[[self _bridge] setTextSizeMultiplier:[[self _webView] textSizeMultiplier]];
639
- (DOMRange *)_selectedRange
641
Frame* coreFrame = core([self _frame]);
642
return coreFrame ? kit(coreFrame->selectionController()->toRange().get()) : nil;
645
- (BOOL)_shouldDeleteRange:(DOMRange *)range
647
Frame* coreFrame = core([self _frame]);
648
return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range));
651
- (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
653
return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
656
- (NSView *)_hitViewForEvent:(NSEvent *)event
658
// Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView.
659
// Callers of this method, however, want to query the deepest view instead.
660
forceNSViewHitTest = YES;
661
NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
662
forceNSViewHitTest = NO;
666
- (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
668
// Put HTML on the pasteboard.
669
if ([types containsObject:WebArchivePboardType]) {
670
WebArchive *archive = [WebArchiver archiveSelectionInFrame:[self _frame]];
671
[pasteboard setData:[archive data] forType:WebArchivePboardType];
674
// Put the attributed string on the pasteboard (RTF/RTFD format).
675
if ([types containsObject:NSRTFDPboardType]) {
676
if (attributedString == nil) {
677
attributedString = [self selectedAttributedString];
679
NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
680
[pasteboard setData:RTFDData forType:NSRTFDPboardType];
682
if ([types containsObject:NSRTFPboardType]) {
683
if (attributedString == nil) {
684
attributedString = [self selectedAttributedString];
686
if ([attributedString containsAttachments]) {
687
attributedString = [attributedString _web_attributedStringByStrippingAttachmentCharacters];
689
NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
690
[pasteboard setData:RTFData forType:NSRTFPboardType];
693
// Put plain string on the pasteboard.
694
if ([types containsObject:NSStringPboardType]) {
695
// Map to a plain old space because this is better for source code, other browsers do it,
696
// and because HTML forces you to do this any time you want two spaces in a row.
697
NSMutableString *s = [[self selectedString] mutableCopy];
698
const unichar NonBreakingSpaceCharacter = 0xA0;
699
NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
700
[s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
701
[pasteboard setString:s forType:NSStringPboardType];
705
if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
706
[pasteboard setData:nil forType:WebSmartPastePboardType];
710
- (void)_setMouseDownEvent:(NSEvent *)event
712
ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
714
if (event == _private->mouseDownEvent)
718
[_private->mouseDownEvent release];
719
_private->mouseDownEvent = event;
721
[_private->firstResponderTextViewAtMouseDownTime release];
723
// The only code that checks this ivar only cares about NSTextViews. The code used to be more general,
724
// but it caused reference cycles leading to world leaks (see 4557386). We should be able to eliminate
725
// firstResponderTextViewAtMouseDownTime entirely when all the form controls are native widgets, because
726
// the only caller (in WebCore) will be unnecessary.
728
NSResponder *firstResponder = [[self window] firstResponder];
729
if ([firstResponder isKindOfClass:[NSTextView class]])
730
_private->firstResponderTextViewAtMouseDownTime = [firstResponder retain];
732
_private->firstResponderTextViewAtMouseDownTime = nil;
734
_private->firstResponderTextViewAtMouseDownTime = nil;
737
- (void)_cancelUpdateActiveStateTimer
739
if (_private->updateActiveStateTimer) {
740
CFRunLoopTimerInvalidate(_private->updateActiveStateTimer);
741
CFRelease(_private->updateActiveStateTimer);
742
_private->updateActiveStateTimer = NULL;
746
- (void)_cancelUpdateMouseoverTimer
748
if (_private->updateMouseoverTimer) {
749
CFRunLoopTimerInvalidate(_private->updateMouseoverTimer);
750
CFRelease(_private->updateMouseoverTimer);
751
_private->updateMouseoverTimer = NULL;
755
- (WebHTMLView *)_topHTMLView
757
// FIXME: this can fail if the dataSource is nil, which happens when the WebView is tearing down from the window closing.
758
WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView];
760
ASSERT([view isKindOfClass:[WebHTMLView class]]);
764
- (BOOL)_isTopHTMLView
766
// FIXME: this should be a cached boolean that doesn't rely on _topHTMLView since that can fail (see _topHTMLView).
767
return self == [self _topHTMLView];
770
- (void)_web_setPrintingModeRecursive
772
[self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
775
_private->enumeratingSubviews = YES;
778
NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
780
[self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
782
unsigned count = [descendantWebHTMLViews count];
783
for (unsigned i = 0; i < count; ++i)
784
[[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
786
[descendantWebHTMLViews release];
789
_private->enumeratingSubviews = NO;
793
- (void)_web_clearPrintingModeRecursive
795
[self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
798
_private->enumeratingSubviews = YES;
801
NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
803
[self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
805
unsigned count = [descendantWebHTMLViews count];
806
for (unsigned i = 0; i < count; ++i)
807
[[descendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
809
[descendantWebHTMLViews release];
812
_private->enumeratingSubviews = NO;
816
- (void)_web_setPrintingModeRecursiveAndAdjustViewSize
818
[self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
821
_private->enumeratingSubviews = YES;
824
NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
826
[self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
828
unsigned count = [descendantWebHTMLViews count];
829
for (unsigned i = 0; i < count; ++i)
830
[[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
832
[descendantWebHTMLViews release];
835
_private->enumeratingSubviews = NO;
839
- (void)_layoutIfNeeded
841
ASSERT(!_private->subviewsSetAside);
843
if ([[self _bridge] needsLayout])
844
_private->needsLayout = YES;
845
if (_private->needsToApplyStyles || _private->needsLayout)
849
- (void)_web_layoutIfNeededRecursive
851
[self _layoutIfNeeded];
854
_private->enumeratingSubviews = YES;
857
NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
859
[self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
861
unsigned count = [descendantWebHTMLViews count];
862
for (unsigned i = 0; i < count; ++i)
863
[[descendantWebHTMLViews objectAtIndex:i] _layoutIfNeeded];
865
[descendantWebHTMLViews release];
868
_private->enumeratingSubviews = NO;
874
@implementation WebHTMLView (WebPrivate)
876
+ (NSArray *)supportedMIMETypes
878
return [WebHTMLRepresentation supportedMIMETypes];
881
+ (NSArray *)supportedImageMIMETypes
883
return [WebHTMLRepresentation supportedImageMIMETypes];
886
+ (NSArray *)supportedNonImageMIMETypes
888
return [WebHTMLRepresentation supportedNonImageMIMETypes];
891
+ (NSArray *)unsupportedTextMIMETypes
893
return [NSArray arrayWithObjects:
894
@"text/calendar", // iCal
898
@"text/vcard", // vCard
901
@"text/ldif", // Netscape Address Book
902
@"text/qif", // Quicken
904
@"text/x-csv", // CSV (for Address Book and Microsoft Outlook)
905
@"text/x-vcf", // vCard type used in Sun affinity app
906
@"text/rtf", // Rich Text Format
910
+ (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
912
NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
913
location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
914
modifierFlags:[flagsChangedEvent modifierFlags]
915
timestamp:[flagsChangedEvent timestamp]
916
windowNumber:[flagsChangedEvent windowNumber]
917
context:[flagsChangedEvent context]
918
eventNumber:0 clickCount:0 pressure:0];
920
// Pretend it's a mouse move.
921
[[NSNotificationCenter defaultCenter]
922
postNotificationName:WKMouseMovedNotification() object:self
923
userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
926
- (void)_updateMouseoverWithFakeEvent
928
[self _cancelUpdateMouseoverTimer];
930
NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
931
location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
932
modifierFlags:[[NSApp currentEvent] modifierFlags]
933
timestamp:[NSDate timeIntervalSinceReferenceDate]
934
windowNumber:[[self window] windowNumber]
935
context:[[NSApp currentEvent] context]
936
eventNumber:0 clickCount:0 pressure:0];
938
[self _updateMouseoverWithEvent:fakeEvent];
941
static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info)
943
WebHTMLView *view = (WebHTMLView *)info;
945
[view _updateMouseoverWithFakeEvent];
948
- (void)_frameOrBoundsChanged
950
if (!NSEqualSizes(_private->lastLayoutSize, [(NSClipView *)[self superview] documentVisibleRect].size)) {
951
[self setNeedsLayout:YES];
952
[self setNeedsDisplay:YES];
953
[_private->compController endRevertingChange:NO moveLeft:NO];
956
NSPoint origin = [[self superview] bounds].origin;
957
if (!NSEqualPoints(_private->lastScrollPosition, origin)) {
958
[[self _bridge] sendScrollEvent];
959
[_private->compController endRevertingChange:NO moveLeft:NO];
961
WebView *webView = [self _webView];
962
[[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
964
_private->lastScrollPosition = origin;
966
if (!_private->updateMouseoverTimer) {
967
CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
968
_private->updateMouseoverTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0, 0, 0,
969
_updateMouseoverTimerCallback, &context);
970
CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateMouseoverTimer, kCFRunLoopDefaultMode);
974
- (void)_setAsideSubviews
976
ASSERT(!_private->subviewsSetAside);
977
ASSERT(_private->savedSubviews == nil);
978
_private->savedSubviews = _subviews;
980
_private->subviewsSetAside = YES;
983
- (void)_restoreSubviews
985
ASSERT(_private->subviewsSetAside);
986
ASSERT(_subviews == nil);
987
_subviews = _private->savedSubviews;
988
_private->savedSubviews = nil;
989
_private->subviewsSetAside = NO;
994
- (void)didAddSubview:(NSView *)subview
996
if (_private->enumeratingSubviews)
997
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]));
1000
- (void)willRemoveSubview:(NSView *)subview
1002
if (_private->enumeratingSubviews)
1003
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]));
1008
#ifdef BUILDING_ON_TIGER
1010
// This is called when we are about to draw, but before our dirty rect is propagated to our ancestors.
1011
// That's the perfect time to do a layout, except that ideally we'd want to be sure that we're dirty
1012
// before doing it. As a compromise, when we're opaque we do the layout only when actually asked to
1013
// draw, but when we're transparent we do the layout at this stage so views behind us know that they
1014
// need to be redrawn (in case the layout causes some things to get dirtied).
1015
- (void)_propagateDirtyRectsToOpaqueAncestors
1017
if (![[self _webView] drawsBackground])
1018
[self _web_layoutIfNeededRecursive];
1019
[super _propagateDirtyRectsToOpaqueAncestors];
1024
- (void)viewWillDraw
1026
// On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView
1027
// So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed
1028
// once the FIXME in _isTopHTMLView is fixed.
1029
if (_private->dataSource && [self _isTopHTMLView])
1030
[self _web_layoutIfNeededRecursive];
1031
[super viewWillDraw];
1036
// Don't let AppKit even draw subviews. We take care of that.
1037
- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
1039
// This helps when we print as part of a larger print process.
1040
// If the WebHTMLView itself is what we're printing, then we will never have to do this.
1041
BOOL wasInPrintingMode = _private->printing;
1042
BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1043
if (wasInPrintingMode != isPrinting) {
1045
[self _web_setPrintingModeRecursive];
1047
[self _web_clearPrintingModeRecursive];
1050
#ifdef BUILDING_ON_TIGER
1052
// Because Tiger does not have viewWillDraw we need to do layout here.
1053
[self _web_layoutIfNeededRecursive];
1054
[_subviews makeObjectsPerformSelector:@selector(_propagateDirtyRectsToOpaqueAncestors)];
1058
[self _setAsideSubviews];
1059
[super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView];
1060
[self _restoreSubviews];
1062
if (wasInPrintingMode != isPrinting) {
1063
if (wasInPrintingMode)
1064
[self _web_setPrintingModeRecursive];
1066
[self _web_clearPrintingModeRecursive];
1070
// Don't let AppKit even draw subviews. We take care of that.
1071
- (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
1073
BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
1075
BOOL wasInPrintingMode = _private->printing;
1076
BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1078
if (needToSetAsideSubviews) {
1079
// This helps when we print as part of a larger print process.
1080
// If the WebHTMLView itself is what we're printing, then we will never have to do this.
1081
if (wasInPrintingMode != isPrinting) {
1083
[self _web_setPrintingModeRecursive];
1085
[self _web_clearPrintingModeRecursive];
1088
#ifdef BUILDING_ON_TIGER
1090
// Because Tiger does not have viewWillDraw we need to do layout here.
1091
NSRect boundsBeforeLayout = [self bounds];
1092
if (!NSIsEmptyRect(visRect))
1093
[self _web_layoutIfNeededRecursive];
1095
// If layout changes the view's bounds, then we need to recompute the visRect.
1096
// That's because the visRect passed to us was based on the bounds at the time
1097
// we were called. This method is only displayed to draw "all", so it's safe
1098
// to just call visibleRect to compute the entire rectangle.
1099
if (!NSEqualRects(boundsBeforeLayout, [self bounds]))
1100
visRect = [self visibleRect];
1104
[self _setAsideSubviews];
1107
[super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
1109
if (needToSetAsideSubviews) {
1110
if (wasInPrintingMode != isPrinting) {
1111
if (wasInPrintingMode)
1112
[self _web_setPrintingModeRecursive];
1114
[self _web_clearPrintingModeRecursive];
1117
[self _restoreSubviews];
1121
- (BOOL)_insideAnotherHTMLView
1123
return self != [self _topHTMLView];
1126
- (void)scrollPoint:(NSPoint)point
1128
// Since we can't subclass NSTextView to do what we want, we have to second guess it here.
1129
// If we get called during the handling of a key down event, we assume the call came from
1130
// NSTextView, and ignore it and use our own code to decide how to page up and page down
1131
// We are smarter about how far to scroll, and we have "superview scrolling" logic.
1132
NSEvent *event = [[self window] currentEvent];
1133
if ([event type] == NSKeyDown) {
1134
const unichar pageUp = NSPageUpFunctionKey;
1135
if ([[event characters] rangeOfString:[NSString stringWithCharacters:&pageUp length:1]].length == 1) {
1136
[self tryToPerform:@selector(scrollPageUp:) with:nil];
1139
const unichar pageDown = NSPageDownFunctionKey;
1140
if ([[event characters] rangeOfString:[NSString stringWithCharacters:&pageDown length:1]].length == 1) {
1141
[self tryToPerform:@selector(scrollPageDown:) with:nil];
1146
[super scrollPoint:point];
1149
- (NSView *)hitTest:(NSPoint)point
1151
// WebHTMLView objects handle all events for objects inside them.
1152
// To get those events, we prevent hit testing from AppKit.
1154
// But there are three exceptions to this:
1155
// 1) For right mouse clicks and control clicks we don't yet have an implementation
1156
// that works for nested views, so we let the hit testing go through the
1157
// standard NSView code path (needs to be fixed, see bug 4361618).
1158
// 2) Java depends on doing a hit test inside it's mouse moved handling,
1159
// so we let the hit testing go through the standard NSView code path
1160
// when the current event is a mouse move (except when we are calling
1161
// from _updateMouseoverWithEvent, so we have to use a global,
1162
// forceWebHTMLViewHitTest, for that)
1163
// 3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
1164
// both need to figure out which view to check with inside the WebHTMLView.
1165
// They use a global to change the behavior of hitTest: so they can get the
1166
// right view. The global is forceNSViewHitTest and the method they use to
1167
// do the hit testing is _hitViewForEvent:. (But this does not work correctly
1168
// when there is HTML overlapping the view, see bug 4361626)
1169
// 4) NSAccessibilityHitTest relies on this for checking the cursor position.
1170
// Our check for that is whether the event is NSFlagsChanged. This works
1171
// for VoiceOver's cntl-opt-f5 command (move focus to item under cursor)
1172
// and Dictionary's cmd-cntl-D (open dictionary popup for item under cursor).
1173
// This is of course a hack.
1175
BOOL captureHitsOnSubviews;
1176
if (forceNSViewHitTest)
1177
captureHitsOnSubviews = NO;
1178
else if (forceWebHTMLViewHitTest)
1179
captureHitsOnSubviews = YES;
1181
NSEvent *event = [[self window] currentEvent];
1182
captureHitsOnSubviews = !([event type] == NSMouseMoved
1183
|| [event type] == NSRightMouseDown
1184
|| ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
1185
|| [event type] == NSFlagsChanged);
1188
if (!captureHitsOnSubviews)
1189
return [super hitTest:point];
1190
if ([[self superview] mouse:point inRect:[self frame]])
1195
- (void)_clearLastHitViewIfSelf
1197
if (lastHitView == self)
1201
- (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1203
ASSERT(_private->trackingRectOwner == nil);
1204
_private->trackingRectOwner = owner;
1205
_private->trackingRectUserData = data;
1206
return TRACKING_RECT_TAG;
1209
- (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1211
ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1212
ASSERT(_private->trackingRectOwner == nil);
1213
_private->trackingRectOwner = owner;
1214
_private->trackingRectUserData = data;
1215
return TRACKING_RECT_TAG;
1218
- (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1221
ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1222
ASSERT(_private->trackingRectOwner == nil);
1223
_private->trackingRectOwner = owner;
1224
_private->trackingRectUserData = userDataList[0];
1225
trackingNums[0] = TRACKING_RECT_TAG;
1228
- (void)removeTrackingRect:(NSTrackingRectTag)tag
1233
if (_private && (tag == TRACKING_RECT_TAG)) {
1234
_private->trackingRectOwner = nil;
1238
if (_private && (tag == _private->lastToolTipTag)) {
1239
[super removeTrackingRect:tag];
1240
_private->lastToolTipTag = 0;
1244
// If any other tracking rect is being removed, we don't know how it was created
1245
// and it's possible there's a leak involved (see 3500217)
1246
ASSERT_NOT_REACHED();
1249
- (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1252
for (i = 0; i < count; ++i) {
1256
ASSERT(tag == TRACKING_RECT_TAG);
1257
if (_private != nil) {
1258
_private->trackingRectOwner = nil;
1263
- (void)_sendToolTipMouseExited
1265
// Nothing matters except window, trackingNumber, and userData.
1266
NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1267
location:NSMakePoint(0, 0)
1270
windowNumber:[[self window] windowNumber]
1273
trackingNumber:TRACKING_RECT_TAG
1274
userData:_private->trackingRectUserData];
1275
[_private->trackingRectOwner mouseExited:fakeEvent];
1278
- (void)_sendToolTipMouseEntered
1280
// Nothing matters except window, trackingNumber, and userData.
1281
NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1282
location:NSMakePoint(0, 0)
1285
windowNumber:[[self window] windowNumber]
1288
trackingNumber:TRACKING_RECT_TAG
1289
userData:_private->trackingRectUserData];
1290
[_private->trackingRectOwner mouseEntered:fakeEvent];
1293
- (void)_setToolTip:(NSString *)string
1295
NSString *toolTip = [string length] == 0 ? nil : string;
1296
NSString *oldToolTip = _private->toolTip;
1297
if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
1301
[self _sendToolTipMouseExited];
1302
[oldToolTip release];
1304
_private->toolTip = [toolTip copy];
1306
// See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1307
[self removeAllToolTips];
1308
NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1309
_private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1310
[self _sendToolTipMouseEntered];
1314
- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1316
return [[_private->toolTip copy] autorelease];
1319
- (void)_updateMouseoverWithEvent:(NSEvent *)event
1321
if (_private->closed)
1324
NSView *contentView = [[event window] contentView];
1325
NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
1327
forceWebHTMLViewHitTest = YES;
1328
NSView *hitView = [contentView hitTest:locationForHitTest];
1329
forceWebHTMLViewHitTest = NO;
1331
WebHTMLView *view = nil;
1332
if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended])
1333
view = (WebHTMLView *)hitView;
1338
if (lastHitView != view && lastHitView && [lastHitView _frame]) {
1339
// If we are moving out of a view (or frame), let's pretend the mouse moved
1340
// all the way out of that view. But we have to account for scrolling, because
1341
// khtml doesn't understand our clipping.
1342
NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
1343
float yScroll = visibleRect.origin.y;
1344
float xScroll = visibleRect.origin.x;
1346
event = [NSEvent mouseEventWithType:NSMouseMoved
1347
location:NSMakePoint(-1 - xScroll, -1 - yScroll )
1348
modifierFlags:[[NSApp currentEvent] modifierFlags]
1349
timestamp:[NSDate timeIntervalSinceReferenceDate]
1350
windowNumber:[[view window] windowNumber]
1351
context:[[NSApp currentEvent] context]
1352
eventNumber:0 clickCount:0 pressure:0];
1353
if (Frame* lastHitCoreFrame = core([lastHitView _frame]))
1354
lastHitCoreFrame->eventHandler()->mouseMoved(event);
1360
if (Frame* coreFrame = core([view _frame]))
1361
coreFrame->eventHandler()->mouseMoved(event);
1367
// keep in sync with WebPasteboardHelper::insertablePasteboardTypes
1368
+ (NSArray *)_insertablePasteboardTypes
1370
static NSArray *types = nil;
1372
types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType,
1373
NSFilenamesPboardType, NSTIFFPboardType, NSPICTPboardType, NSURLPboardType,
1374
NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, nil];
1380
+ (NSArray *)_selectionPasteboardTypes
1382
// FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
1383
return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
1386
- (NSImage *)_dragImageForURL:(NSString*)urlString withLabel:(NSString*)label
1388
BOOL drawURLString = YES;
1389
BOOL clipURLString = NO, clipLabelString = NO;
1396
NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DRAG_LINK_LABEL_FONT_SIZE]
1397
toHaveTrait:NSBoldFontMask];
1398
NSFont *urlFont = [NSFont systemFontOfSize: DRAG_LINK_URL_FONT_SIZE];
1400
labelSize.width = [label _web_widthWithFont: labelFont];
1401
labelSize.height = [labelFont ascender] - [labelFont descender];
1402
if (labelSize.width > MAX_DRAG_LABEL_WIDTH){
1403
labelSize.width = MAX_DRAG_LABEL_WIDTH;
1404
clipLabelString = YES;
1407
NSSize imageSize, urlStringSize;
1408
imageSize.width = labelSize.width + DRAG_LABEL_BORDER_X * 2.0f;
1409
imageSize.height = labelSize.height + DRAG_LABEL_BORDER_Y * 2.0f;
1410
if (drawURLString) {
1411
urlStringSize.width = [urlString _web_widthWithFont: urlFont];
1412
urlStringSize.height = [urlFont ascender] - [urlFont descender];
1413
imageSize.height += urlStringSize.height;
1414
if (urlStringSize.width > MAX_DRAG_LABEL_WIDTH) {
1415
imageSize.width = MAX(MAX_DRAG_LABEL_WIDTH + DRAG_LABEL_BORDER_X * 2.0f, MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP);
1416
clipURLString = YES;
1418
imageSize.width = MAX(labelSize.width + DRAG_LABEL_BORDER_X * 2.0f, urlStringSize.width + DRAG_LABEL_BORDER_X * 2.0f);
1421
NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
1422
[dragImage lockFocus];
1424
[[NSColor colorWithCalibratedRed: 0.7f green: 0.7f blue: 0.7f alpha: 0.8f] set];
1426
// Drag a rectangle with rounded corners/
1427
NSBezierPath *path = [NSBezierPath bezierPath];
1428
[path appendBezierPathWithOvalInRect: NSMakeRect(0.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1429
[path appendBezierPathWithOvalInRect: NSMakeRect(0, imageSize.height - DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1430
[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)];
1431
[path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1433
[path appendBezierPathWithRect: NSMakeRect(DRAG_LABEL_RADIUS, 0.0f, imageSize.width - DRAG_LABEL_RADIUS * 2.0f, imageSize.height)];
1434
[path appendBezierPathWithRect: NSMakeRect(0.0f, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 10.0f, imageSize.height - 2.0f * DRAG_LABEL_RADIUS)];
1435
[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)];
1438
NSColor *topColor = [NSColor colorWithCalibratedWhite:0.0f alpha:0.75f];
1439
NSColor *bottomColor = [NSColor colorWithCalibratedWhite:1.0f alpha:0.5f];
1440
if (drawURLString) {
1442
urlString = [WebStringTruncator centerTruncateString: urlString toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:urlFont];
1444
[urlString _web_drawDoubledAtPoint:NSMakePoint(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y - [urlFont descender])
1445
withTopColor:topColor bottomColor:bottomColor font:urlFont];
1448
if (clipLabelString)
1449
label = [WebStringTruncator rightTruncateString: label toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:labelFont];
1450
[label _web_drawDoubledAtPoint:NSMakePoint (DRAG_LABEL_BORDER_X, imageSize.height - DRAG_LABEL_BORDER_Y_OFFSET - [labelFont pointSize])
1451
withTopColor:topColor bottomColor:bottomColor font:labelFont];
1453
[dragImage unlockFocus];
1458
- (NSImage *)_dragImageForLinkElement:(NSDictionary *)element
1460
NSURL *linkURL = [element objectForKey: WebElementLinkURLKey];
1462
NSString *label = [element objectForKey: WebElementLinkLabelKey];
1463
NSString *urlString = [linkURL _web_userVisibleString];
1464
return [self _dragImageForURL:urlString withLabel:label];
1467
- (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
1469
[self setPromisedDragTIFFDataSource:0];
1472
- (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
1474
if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) {
1475
WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1476
[pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]];
1478
} else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) {
1479
if (Image* image = [self promisedDragTIFFDataSource]->image())
1480
[pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType];
1481
[self setPromisedDragTIFFDataSource:0];
1485
- (void)_handleAutoscrollForMouseDragged:(NSEvent *)event
1487
[self autoscroll:event];
1488
[self _startAutoscrollTimer:event];
1491
- (WebPluginController *)_pluginController
1493
return _private->pluginController;
1496
- (void)_layoutForPrinting
1498
// Set printing mode temporarily so we can adjust the size of the view. This will allow
1499
// AppKit's pagination code to use the correct height for the page content. Leaving printing
1500
// mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just
1501
// turn it off again after adjusting the size.
1502
[self _web_setPrintingModeRecursiveAndAdjustViewSize];
1503
[self _web_clearPrintingModeRecursive];
1506
- (void)_startAutoscrollTimer: (NSEvent *)triggerEvent
1508
if (_private->autoscrollTimer == nil) {
1509
_private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1510
target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1511
_private->autoscrollTriggerEvent = [triggerEvent retain];
1515
// FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1516
// We can't remove this yet because it's still in use by Mail.
1517
- (NSRect)_selectionRect
1519
return [self selectionRect];
1522
- (void)_stopAutoscrollTimer
1524
NSTimer *timer = _private->autoscrollTimer;
1525
_private->autoscrollTimer = nil;
1526
[_private->autoscrollTriggerEvent release];
1527
_private->autoscrollTriggerEvent = nil;
1534
// Guarantee that the autoscroll timer is invalidated, even if we don't receive
1535
// a mouse up event.
1536
BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);
1538
[self _stopAutoscrollTimer];
1542
NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1543
location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1544
modifierFlags:[[NSApp currentEvent] modifierFlags]
1545
timestamp:[NSDate timeIntervalSinceReferenceDate]
1546
windowNumber:[[self window] windowNumber]
1547
context:[[NSApp currentEvent] context]
1548
eventNumber:0 clickCount:0 pressure:0];
1549
[self mouseDragged:fakeEvent];
1554
Frame* coreFrame = core([self _frame]);
1555
return coreFrame && coreFrame->editor()->canEdit();
1558
- (BOOL)_canEditRichly
1560
Frame* coreFrame = core([self _frame]);
1561
return coreFrame && coreFrame->editor()->canEditRichly();
1564
- (BOOL)_canAlterCurrentSelection
1566
return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1569
- (BOOL)_hasSelection
1571
Frame* coreFrame = core([self _frame]);
1572
return coreFrame && coreFrame->selectionController()->isRange();
1575
- (BOOL)_hasSelectionOrInsertionPoint
1577
Frame* coreFrame = core([self _frame]);
1578
return coreFrame && coreFrame->selectionController()->isCaretOrRange();
1581
- (BOOL)_hasInsertionPoint
1583
Frame* coreFrame = core([self _frame]);
1584
return coreFrame && coreFrame->selectionController()->isCaret();
1589
Frame* coreFrame = core([self _frame]);
1590
return coreFrame && coreFrame->selectionController()->isContentEditable();
1593
- (BOOL)_transparentBackground
1595
return _private->transparentBackground;
1598
- (void)_setTransparentBackground:(BOOL)f
1600
_private->transparentBackground = f;
1603
- (NSImage *)_selectionDraggingImage
1605
if ([self _hasSelection]) {
1606
NSImage *dragImage = core([self _frame])->selectionImage();
1607
[dragImage _web_dissolveToFraction:WebDragImageAlpha];
1613
- (NSRect)_selectionDraggingRect
1615
// Mail currently calls this method. We can eliminate it when Mail no longer calls it.
1616
return [self selectionRect];
1619
- (DOMNode *)_insertOrderedList
1621
Frame* coreFrame = core([self _frame]);
1622
return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil;
1625
- (DOMNode *)_insertUnorderedList
1627
Frame* coreFrame = core([self _frame]);
1628
return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil;
1631
- (BOOL)_canIncreaseSelectionListLevel
1633
Frame* coreFrame = core([self _frame]);
1634
return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel();
1637
- (BOOL)_canDecreaseSelectionListLevel
1639
Frame* coreFrame = core([self _frame]);
1640
return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel();
1643
- (DOMNode *)_increaseSelectionListLevel
1645
Frame* coreFrame = core([self _frame]);
1646
return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil;
1649
- (DOMNode *)_increaseSelectionListLevelOrdered
1651
Frame* coreFrame = core([self _frame]);
1652
return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil;
1655
- (DOMNode *)_increaseSelectionListLevelUnordered
1657
Frame* coreFrame = core([self _frame]);
1658
return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil;
1661
- (void)_decreaseSelectionListLevel
1663
Frame* coreFrame = core([self _frame]);
1665
coreFrame->editor()->decreaseSelectionListLevel();
1668
- (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
1670
if (!_private->highlighters)
1671
_private->highlighters = [[NSMutableDictionary alloc] init];
1672
[_private->highlighters setObject:highlighter forKey:type];
1675
- (void)_removeHighlighterOfType:(NSString*)type
1677
[_private->highlighters removeObjectForKey:type];
1680
- (BOOL)_web_firstResponderCausesFocusDisplay
1682
return [[self window] firstResponder] == self || [[self window] firstResponder] == [self _frameView];
1685
- (void)_updateActiveState
1687
[self _cancelUpdateActiveStateTimer];
1689
// This method does the job of updating the view based on the view's firstResponder-ness and
1690
// the window key-ness of the window containing this view. This involves four kinds of
1691
// drawing updates right now.
1693
// The four display attributes are as follows:
1695
// 1. The background color used to draw behind selected content (active | inactive color)
1696
// 2. Caret blinking (blinks | does not blink)
1697
// 3. The drawing of a focus ring around links in web pages.
1698
// 4. Changing the tint of controls from clear to aqua/graphite and vice versa
1700
// Also, this is responsible for letting the bridge know if the window has gained or lost focus
1701
// so we can send focus and blur events.
1703
Frame* frame = core([self _frame]);
1707
Page* page = frame->page();
1711
NSWindow *window = [self window];
1712
BOOL windowIsKey = [window isKeyWindow];
1713
BOOL windowOrSheetIsKey = windowIsKey || [[window attachedSheet] isKeyWindow];
1715
BOOL isActive = !_private->resigningFirstResponder && windowIsKey && [self _web_firstResponderCausesFocusDisplay];
1716
frame->setIsActive(isActive);
1718
Frame* focusedFrame = page->focusController()->focusedOrMainFrame();
1719
frame->setWindowHasFocus(frame == focusedFrame && windowOrSheetIsKey);
1722
- (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
1724
ASSERT([self _hasSelection]);
1725
NSArray *types = [self pasteboardTypesForSelection];
1727
// Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
1728
NSAttributedString *attributedString = [self selectedAttributedString];
1729
NSMutableArray *mutableTypes = nil;
1730
if (![attributedString containsAttachments]) {
1731
mutableTypes = [types mutableCopy];
1732
[mutableTypes removeObject:NSRTFDPboardType];
1733
types = mutableTypes;
1736
[pasteboard declareTypes:types owner:[self _topHTMLView]];
1737
[self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
1738
[mutableTypes release];
1743
// Check for a nil _private here incase we were created with initWithCoder. In that case, the WebView is just throwing
1744
// out the archived WebHTMLView and recreating a new one if needed. So close dosen't need to do anything in that case.
1745
if (!_private || _private->closed)
1747
[self _clearLastHitViewIfSelf];
1748
// FIXME: This is slow; should remove individual observers instead.
1749
[[NSNotificationCenter defaultCenter] removeObserver:self];
1750
[_private->pluginController destroyAllPlugins];
1751
[_private->pluginController setDataSource:nil];
1752
// remove tooltips before clearing _private so removeTrackingRect: will work correctly
1753
[self removeAllToolTips];
1755
_private->closed = YES;
1756
Page* page = core([self _webView]);
1758
page->dragController()->setDraggingImageURL(KURL());
1761
- (BOOL)_hasHTMLDocument
1763
Frame* coreFrame = core([self _frame]);
1766
Document* document = coreFrame->document();
1767
return document && document->isHTMLDocument();
1770
- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
1771
forType:(NSString *)pboardType
1772
inContext:(DOMRange *)context
1773
subresources:(NSArray **)subresources
1775
if (pboardType == WebArchivePboardType) {
1776
WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1778
*subresources = [archive subresources];
1779
DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
1783
if (pboardType == NSFilenamesPboardType)
1784
return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
1786
if (pboardType == NSHTMLPboardType) {
1787
NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
1788
// This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
1789
if ([HTMLString hasPrefix:@"Version:"]) {
1790
NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
1791
if (range.location != NSNotFound)
1792
HTMLString = [HTMLString substringFromIndex:range.location];
1794
if ([HTMLString length] == 0)
1797
return [[self _bridge] documentFragmentWithMarkupString:HTMLString baseURLString:nil];
1800
// The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369.
1801
// If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found".
1802
// FIXME: Remove this once bug 5052369 is fixed.
1803
if ([self _hasHTMLDocument] && pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType) {
1804
NSAttributedString *string = nil;
1805
if (pboardType == NSRTFDPboardType)
1806
string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
1808
string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
1812
NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
1813
[[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
1814
self, @"WebResourceHandler", nil];
1817
BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks];
1818
if (!wasDeferringCallbacks)
1819
[[self _webView] setDefersCallbacks:YES];
1821
DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length])
1822
document:[[self _frame] DOMDocument]
1823
documentAttributes:documentAttributes
1828
NSEnumerator *e = [s objectEnumerator];
1830
while ((r = [e nextObject]))
1831
[[self _dataSource] addSubresource:r];
1833
if (!wasDeferringCallbacks)
1834
[[self _webView] setDefersCallbacks:NO];
1836
[documentAttributes release];
1840
if (pboardType == NSTIFFPboardType) {
1841
WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
1842
URL:uniqueURLWithRelativePart(@"image.tiff")
1843
MIMEType:@"image/tiff"
1844
textEncodingName:nil
1846
DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
1850
if (pboardType == NSPICTPboardType) {
1851
WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType]
1852
URL:uniqueURLWithRelativePart(@"image.pict")
1853
MIMEType:@"image/pict"
1854
textEncodingName:nil
1856
DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
1860
if (pboardType == NSURLPboardType) {
1861
NSURL *URL = [NSURL URLFromPasteboard:pasteboard];
1862
DOMDocument* document = [[self _frame] DOMDocument];
1866
DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"];
1867
NSString *URLString = [URL _web_originalDataAsString];
1868
if ([URLString length] == 0)
1870
NSString *URLTitleString = [pasteboard stringForType:WebURLNamePboardType];
1871
DOMText *text = [document createTextNode:URLTitleString];
1872
[anchor setHref:URLString];
1873
[anchor appendChild:text];
1874
DOMDocumentFragment *fragment = [document createDocumentFragment];
1875
[fragment appendChild:anchor];
1878
if (pboardType == NSStringPboardType)
1879
return [[self _bridge] documentFragmentWithText:[pasteboard stringForType:NSStringPboardType]
1887
@implementation NSView (WebHTMLViewFileInternal)
1889
- (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
1891
unsigned count = [_subviews count];
1892
for (unsigned i = 0; i < count; ++i) {
1893
NSView *child = [_subviews objectAtIndex:i];
1894
if ([child isKindOfClass:[WebHTMLView class]])
1895
[array addObject:child];
1896
[child _web_addDescendantWebHTMLViewsToArray:array];
1902
@implementation NSMutableDictionary (WebHTMLViewFileInternal)
1904
- (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
1906
if (object == nil) {
1907
[self removeObjectForKey:key];
1909
[self setObject:object forKey:key];
1915
#ifdef BUILDING_ON_TIGER
1917
// The following is a workaround for
1918
// <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears
1919
// The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:.
1920
// Since the category will be searched before the real class, we'll prevent the flag from being
1921
// set on the tool tip panel.
1923
@interface NSToolTipPanel : NSPanel
1926
@interface NSToolTipPanel (WebHTMLViewFileInternal)
1929
@implementation NSToolTipPanel (WebHTMLViewFileInternal)
1931
- (void)setAcceptsMouseMovedEvents:(BOOL)flag
1933
// Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
1940
@interface NSArray (WebHTMLView)
1941
- (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object;
1944
@implementation WebHTMLView
1948
[NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes]
1949
returnTypes:[[self class] _insertablePasteboardTypes]];
1950
_NSInitializeKillRing();
1951
#ifndef BUILDING_ON_TIGER
1952
WebCoreObjCFinalizeOnMainThread(self);
1956
- (id)initWithFrame:(NSRect)frame
1958
self = [super initWithFrame:frame];
1962
[self setFocusRingType:NSFocusRingTypeNone];
1964
// Make all drawing go through us instead of subviews.
1965
[self _setDrawsOwnDescendants:YES];
1967
_private = [[WebHTMLViewPrivate alloc] init];
1969
_private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
1970
_private->needsLayout = YES;
1977
// We can't assert that close has already been called because
1978
// this view can be removed from it's superview, even though
1979
// it could be needed later, so close if needed.
1988
ASSERT_MAIN_THREAD();
1989
// We can't assert that close has already been called because
1990
// this view can be removed from it's superview, even though
1991
// it could be needed later, so close if needed.
1996
// Returns YES if the delegate returns YES (so we should do no more work).
1997
- (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector
1999
BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector;
2000
_private->selectorForDoCommandBySelector = 0;
2001
if (callerAlreadyCalledDelegate)
2004
WebView *webView = [self _webView];
2005
id editingDelegate = [webView editingDelegate];
2006
if (![editingDelegate respondsToSelector:@selector(webView:doCommandBySelector:)])
2009
return [editingDelegate webView:webView doCommandBySelector:selector];
2012
- (void)callWebCoreCommand:(SEL)selector
2014
if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
2017
Frame* coreFrame = core([self _frame]);
2021
// Capitalize the first letter of the selector, since we use capitalized command
2022
// names in the Editor object (why?). And remove the trailing colon.
2023
const char* selectorName = sel_getName(selector);
2024
size_t selectorNameLength = strlen(selectorName);
2025
ASSERT(selectorNameLength >= 2);
2026
ASSERT(selectorName[selectorNameLength - 1] == ':');
2027
Vector<char, 256> commandName(selectorNameLength - 1 + 1);
2028
commandName[0] = toupper(selectorName[0]);
2029
memcpy(&commandName[1], &selectorName[1], selectorNameLength - 2);
2030
commandName[selectorNameLength - 1] = 0;
2032
coreFrame->editor()->execCommand(commandName.data());
2035
// These commands are forwarded to the Editor object in WebCore.
2036
// Ideally we'd do this for all editing commands; more of the code
2037
// should be moved from here to there, and more commands should be
2038
// added to this list.
2040
#define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self callWebCoreCommand:_cmd]; }
2042
WEBCORE_COMMAND(deleteWordBackward)
2043
WEBCORE_COMMAND(deleteWordForward)
2044
WEBCORE_COMMAND(insertBacktab)
2045
WEBCORE_COMMAND(insertLineBreak)
2046
WEBCORE_COMMAND(insertNewline)
2047
WEBCORE_COMMAND(insertTab)
2048
WEBCORE_COMMAND(moveBackward)
2049
WEBCORE_COMMAND(moveBackwardAndModifySelection)
2050
WEBCORE_COMMAND(moveDown)
2051
WEBCORE_COMMAND(moveDownAndModifySelection)
2052
WEBCORE_COMMAND(moveForward)
2053
WEBCORE_COMMAND(moveForwardAndModifySelection)
2054
WEBCORE_COMMAND(moveLeft)
2055
WEBCORE_COMMAND(moveLeftAndModifySelection)
2056
WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
2057
WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
2058
WEBCORE_COMMAND(moveRight)
2059
WEBCORE_COMMAND(moveRightAndModifySelection)
2060
WEBCORE_COMMAND(moveToBeginningOfDocument)
2061
WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
2062
WEBCORE_COMMAND(moveToBeginningOfLine)
2063
WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
2064
WEBCORE_COMMAND(moveToBeginningOfParagraph)
2065
WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
2066
WEBCORE_COMMAND(moveToBeginningOfSentence)
2067
WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
2068
WEBCORE_COMMAND(moveToEndOfDocument)
2069
WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
2070
WEBCORE_COMMAND(moveToEndOfLine)
2071
WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
2072
WEBCORE_COMMAND(moveToEndOfParagraph)
2073
WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
2074
WEBCORE_COMMAND(moveToEndOfSentence)
2075
WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
2076
WEBCORE_COMMAND(moveUp)
2077
WEBCORE_COMMAND(moveUpAndModifySelection)
2078
WEBCORE_COMMAND(moveWordBackward)
2079
WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
2080
WEBCORE_COMMAND(moveWordForward)
2081
WEBCORE_COMMAND(moveWordForwardAndModifySelection)
2082
WEBCORE_COMMAND(moveWordLeft)
2083
WEBCORE_COMMAND(moveWordLeftAndModifySelection)
2084
WEBCORE_COMMAND(moveWordRight)
2085
WEBCORE_COMMAND(moveWordRightAndModifySelection)
2087
#undef WEBCORE_COMMAND
2089
#define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return;
2091
- (IBAction)takeFindStringFromSelection:(id)sender
2095
if (![self _hasSelection]) {
2100
[NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
2103
- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
2105
[pasteboard declareTypes:types owner:[self _topHTMLView]];
2106
[self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2110
- (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
2112
Frame* coreFrame = core([self _frame]);
2115
if (coreFrame->selectionController()->isContentRichlyEditable())
2116
[self _pasteWithPasteboard:pasteboard allowPlainText:YES];
2118
[self _pasteAsPlainTextWithPasteboard:pasteboard];
2122
- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2124
if (sendType != nil && [[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]) {
2126
} else if (returnType != nil && [[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) {
2129
return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2132
- (void)selectAll:(id)sender
2139
// jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2140
// was using the old jumpToSelection selector in its menu. Newer versions of Safari will us the
2141
// selector centerSelectionInVisibleArea. We'll leave this old selector in place for two reasons:
2142
// (1) compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2143
// might be using the jumpToSelection: selector, and we don't want to break them.
2144
- (void)jumpToSelection:(id)sender
2148
if (Frame* coreFrame = core([self _frame]))
2149
coreFrame->revealSelection(RenderLayer::gAlignCenterAlways);
2152
- (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
2154
SEL action = [item action];
2155
Frame* frame = core([self _frame]);
2157
if (Document* doc = frame->document()) {
2158
if (doc->isPluginDocument())
2161
if (doc->isImageDocument()) {
2162
if (action == @selector(copy:))
2163
return frame->loader()->isComplete();
2169
if (action == @selector(changeSpelling:)
2170
|| action == @selector(_changeSpellingFromMenu:)
2171
|| action == @selector(checkSpelling:)
2172
|| action == @selector(complete:)
2173
|| action == @selector(deleteBackward:)
2174
|| action == @selector(deleteBackwardByDecomposingPreviousCharacter:)
2175
|| action == @selector(deleteForward:)
2176
|| action == @selector(deleteToBeginningOfLine:)
2177
|| action == @selector(deleteToBeginningOfParagraph:)
2178
|| action == @selector(deleteToEndOfLine:)
2179
|| action == @selector(deleteToEndOfParagraph:)
2180
|| action == @selector(deleteToMark:)
2181
|| action == @selector(deleteWordBackward:)
2182
|| action == @selector(deleteWordForward:)
2183
|| action == @selector(insertBacktab:)
2184
|| action == @selector(insertLineBreak:)
2185
|| action == @selector(insertNewline:)
2186
|| action == @selector(insertNewlineIgnoringFieldEditor:)
2187
|| action == @selector(insertParagraphSeparator:)
2188
|| action == @selector(insertTab:)
2189
|| action == @selector(insertTabIgnoringFieldEditor:)
2190
|| action == @selector(moveBackward:)
2191
|| action == @selector(moveBackwardAndModifySelection:)
2192
|| action == @selector(moveDown:)
2193
|| action == @selector(moveDownAndModifySelection:)
2194
|| action == @selector(moveForward:)
2195
|| action == @selector(moveForwardAndModifySelection:)
2196
|| action == @selector(moveLeft:)
2197
|| action == @selector(moveLeftAndModifySelection:)
2198
|| action == @selector(moveParagraphBackwardAndModifySelection:)
2199
|| action == @selector(moveParagraphForwardAndModifySelection:)
2200
|| action == @selector(moveRight:)
2201
|| action == @selector(moveRightAndModifySelection:)
2202
|| action == @selector(moveToBeginningOfDocument:)
2203
|| action == @selector(moveToBeginningOfDocumentAndModifySelection:)
2204
|| action == @selector(moveToBeginningOfSentence:)
2205
|| action == @selector(moveToBeginningOfSentenceAndModifySelection:)
2206
|| action == @selector(moveToBeginningOfLine:)
2207
|| action == @selector(moveToBeginningOfLineAndModifySelection:)
2208
|| action == @selector(moveToBeginningOfParagraph:)
2209
|| action == @selector(moveToBeginningOfParagraphAndModifySelection:)
2210
|| action == @selector(moveToEndOfDocument:)
2211
|| action == @selector(moveToEndOfDocumentAndModifySelection:)
2212
|| action == @selector(moveToEndOfSentence:)
2213
|| action == @selector(moveToEndOfSentenceAndModifySelection:)
2214
|| action == @selector(moveToEndOfLine:)
2215
|| action == @selector(moveToEndOfLineAndModifySelection:)
2216
|| action == @selector(moveToEndOfParagraph:)
2217
|| action == @selector(moveToEndOfParagraphAndModifySelection:)
2218
|| action == @selector(moveUp:)
2219
|| action == @selector(moveUpAndModifySelection:)
2220
|| action == @selector(moveWordBackward:)
2221
|| action == @selector(moveWordBackwardAndModifySelection:)
2222
|| action == @selector(moveWordForward:)
2223
|| action == @selector(moveWordForwardAndModifySelection:)
2224
|| action == @selector(moveWordLeft:)
2225
|| action == @selector(moveWordLeftAndModifySelection:)
2226
|| action == @selector(moveWordRight:)
2227
|| action == @selector(moveWordRightAndModifySelection:)
2228
|| action == @selector(pageDown:)
2229
|| action == @selector(pageDownAndModifySelection:)
2230
|| action == @selector(pageUp:)
2231
|| action == @selector(pageUpAndModifySelection:)
2232
|| action == @selector(pasteFont:)
2233
|| action == @selector(transpose:)
2234
|| action == @selector(yank:)
2235
|| action == @selector(yankAndSelect:))
2236
return [self _canEdit];
2238
if (action == @selector(showGuessPanel:)) {
2239
#ifndef BUILDING_ON_TIGER
2240
// Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior.
2241
NSMenuItem *menuItem = (NSMenuItem *)item;
2242
if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2243
BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
2244
[menuItem setTitle:panelShowing ? UI_STRING("Hide Spelling and Grammar", "menu item title") : UI_STRING("Show Spelling and Grammar", "menu item title")];
2247
return [self _canEdit];
2250
if (action == @selector(changeBaseWritingDirection:)) {
2251
NSMenuItem *menuItem = (NSMenuItem *)item;
2252
if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2253
NSWritingDirection writingDirection = static_cast<NSWritingDirection>([item tag]);
2254
if (writingDirection == NSWritingDirectionNatural) {
2255
[menuItem setState:NO];
2258
DOMCSSStyleDeclaration* style = [self _emptyStyle];
2259
[style setDirection:writingDirection == NSWritingDirectionLeftToRight ? @"LTR" : @"RTL"];
2260
[menuItem setState:[[self _bridge] selectionHasStyle:style]];
2262
return [self _canEdit];
2265
if (action == @selector(toggleBaseWritingDirection:)) {
2266
NSMenuItem *menuItem = (NSMenuItem *)item;
2267
if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2268
DOMCSSStyleDeclaration* rtl = [self _emptyStyle];
2269
[rtl setDirection:@"RTL"];
2270
// Take control of the title of the menu item, instead of just checking/unchecking it because otherwise
2271
// we don't know what the check would mean.
2272
[menuItem setTitle:[[self _bridge] selectionHasStyle:rtl] ? UI_STRING("Left to Right", "Left to Right context menu item") : UI_STRING("Right to Left", "Right to Left context menu item")];
2274
return [self _canEdit];
2277
if (action == @selector(alignCenter:)
2278
|| action == @selector(alignLeft:)
2279
|| action == @selector(alignJustified:)
2280
|| action == @selector(alignRight:)
2281
|| action == @selector(changeAttributes:)
2282
|| action == @selector(changeColor:)
2283
|| action == @selector(changeFont:)
2284
|| action == @selector(indent:)
2285
|| action == @selector(outdent:))
2286
return [self _canEditRichly];
2288
if (action == @selector(capitalizeWord:)
2289
|| action == @selector(lowercaseWord:)
2290
|| action == @selector(uppercaseWord:))
2291
return [self _hasSelection] && [self _isEditable];
2293
if (action == @selector(centerSelectionInVisibleArea:)
2294
|| action == @selector(jumpToSelection:)
2295
|| action == @selector(copyFont:)
2296
|| action == @selector(setMark:))
2297
return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2299
if (action == @selector(changeDocumentBackgroundColor:))
2300
return [[self _webView] isEditable] && [self _canEditRichly];
2302
if (action == @selector(copy:))
2303
return (frame && frame->editor()->canDHTMLCopy()) || frame->editor()->canCopy();
2305
if (action == @selector(cut:))
2306
return (frame && frame->editor()->canDHTMLCut()) || frame->editor()->canCut();
2308
if (action == @selector(delete:))
2309
return (frame && frame->editor()->canDelete());
2311
if (action == @selector(_ignoreSpellingFromMenu:)
2312
|| action == @selector(_learnSpellingFromMenu:)
2313
|| action == @selector(takeFindStringFromSelection:))
2314
return [self _hasSelection];
2316
if (action == @selector(paste:) || action == @selector(pasteAsPlainText:))
2317
return (frame && frame->editor()->canDHTMLPaste()) || frame->editor()->canPaste();
2319
if (action == @selector(pasteAsRichText:))
2320
return frame && (frame->editor()->canDHTMLPaste()
2321
|| (frame->editor()->canPaste() && frame->selectionController()->isContentRichlyEditable()));
2323
if (action == @selector(performFindPanelAction:))
2324
// FIXME: Not yet implemented.
2327
if (action == @selector(selectToMark:)
2328
|| action == @selector(swapWithMark:))
2329
return [self _hasSelectionOrInsertionPoint] && [[self _bridge] markDOMRange] != nil;
2331
if (action == @selector(subscript:)) {
2332
NSMenuItem *menuItem = (NSMenuItem *)item;
2333
if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2334
DOMCSSStyleDeclaration *style = [self _emptyStyle];
2335
[style setVerticalAlign:@"sub"];
2336
[menuItem setState:[[self _bridge] selectionHasStyle:style]];
2338
return [self _canEditRichly];
2341
if (action == @selector(superscript:)) {
2342
NSMenuItem *menuItem = (NSMenuItem *)item;
2343
if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2344
DOMCSSStyleDeclaration *style = [self _emptyStyle];
2345
[style setVerticalAlign:@"super"];
2346
[menuItem setState:[[self _bridge] selectionHasStyle:style]];
2348
return [self _canEditRichly];
2351
if (action == @selector(underline:)) {
2352
NSMenuItem *menuItem = (NSMenuItem *)item;
2353
if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2354
DOMCSSStyleDeclaration *style = [self _emptyStyle];
2355
[style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
2356
[menuItem setState:[[self _bridge] selectionHasStyle:style]];
2358
return [self _canEditRichly];
2361
if (action == @selector(unscript:)) {
2362
NSMenuItem *menuItem = (NSMenuItem *)item;
2363
if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2364
DOMCSSStyleDeclaration *style = [self _emptyStyle];
2365
[style setVerticalAlign:@"baseline"];
2366
[menuItem setState:[[self _bridge] selectionHasStyle:style]];
2368
return [self _canEditRichly];
2371
if (action == @selector(_lookUpInDictionaryFromMenu:)) {
2372
return [self _hasSelection];
2375
#ifndef BUILDING_ON_TIGER
2376
if (action == @selector(toggleGrammarChecking:)) {
2377
// FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate
2378
// the selector here because we implement it here, and we must implement it here because the AppKit
2379
// code checks the first responder.
2380
BOOL checkMark = [self isGrammarCheckingEnabled];
2381
if ([(NSObject *)item isKindOfClass:[NSMenuItem class]]) {
2382
NSMenuItem *menuItem = (NSMenuItem *)item;
2383
[menuItem setState:checkMark ? NSOnState : NSOffState];
2392
- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2394
BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
2396
id ud = [[self _webView] UIDelegate];
2397
if (ud && [ud respondsToSelector:@selector(webView:validateUserInterfaceItem:defaultValidation:)])
2398
return [ud webView:[self _webView] validateUserInterfaceItem:item defaultValidation:result];
2403
- (BOOL)acceptsFirstResponder
2405
// Don't accept first responder when we first click on this view.
2406
// We have to pass the event down through WebCore first to be sure we don't hit a subview.
2407
// Do accept first responder at any other time, for example from keyboard events,
2408
// or from calls back from WebCore once we begin mouse-down event handling.
2409
NSEvent *event = [NSApp currentEvent];
2410
if ([event type] == NSLeftMouseDown
2411
&& !_private->handlingMouseDownEvent
2412
&& NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2418
- (BOOL)maintainsInactiveSelection
2420
// This method helps to determine whether the WebHTMLView should maintain
2421
// an inactive selection when it's not first responder.
2422
// Traditionally, these views have not maintained such selections,
2423
// clearing them when the view was not first responder. However,
2424
// to fix bugs like this one:
2425
// <rdar://problem/3672088>: "Editable WebViews should maintain a selection even
2426
// when they're not firstResponder"
2427
// it was decided to add a switch to act more like an NSTextView.
2429
if ([[self _webView] maintainsInactiveSelection])
2432
// Predict the case where we are losing first responder status only to
2433
// gain it back again. Want to keep the selection in that case.
2434
id nextResponder = [[self window] _newFirstResponderAfterResigning];
2435
if ([nextResponder isKindOfClass:[NSScrollView class]]) {
2436
id contentView = [nextResponder contentView];
2438
nextResponder = contentView;
2440
if ([nextResponder isKindOfClass:[NSClipView class]]) {
2441
id documentView = [nextResponder documentView];
2443
nextResponder = documentView;
2445
if (nextResponder == self)
2448
Frame* coreFrame = core([self _frame]);
2449
bool selectionIsEditable = coreFrame && coreFrame->selectionController()->isContentEditable();
2450
bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
2451
&& [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
2453
return selectionIsEditable && nextResponderIsInWebView;
2456
- (void)addMouseMovedObserver
2458
if (!_private->dataSource || ![self _isTopHTMLView])
2461
// Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
2462
if (!([[self window] isKeyWindow] || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]))
2465
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
2466
name:WKMouseMovedNotification() object:nil];
2467
[self _frameOrBoundsChanged];
2470
- (void)removeMouseMovedObserverUnconditionally
2472
[[NSNotificationCenter defaultCenter] removeObserver:self
2473
name:WKMouseMovedNotification() object:nil];
2476
- (void)removeMouseMovedObserver
2478
// Don't remove the observer if we're running the Dashboard.
2479
if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
2482
[[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
2483
[self removeMouseMovedObserverUnconditionally];
2486
- (void)addSuperviewObservers
2488
// We watch the bounds of our superview, so that we can do a layout when the size
2489
// of the superview changes. This is different from other scrollable things that don't
2490
// need this kind of thing because their layout doesn't change.
2492
// We need to pay attention to both height and width because our "layout" has to change
2493
// to extend the background the full height of the space and because some elements have
2494
// sizes that are based on the total size of the view.
2496
NSView *superview = [self superview];
2497
if (superview && [self window]) {
2498
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged)
2499
name:NSViewFrameDidChangeNotification object:superview];
2500
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged)
2501
name:NSViewBoundsDidChangeNotification object:superview];
2503
// In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
2504
// It will check the current size/scroll against the previous layout's size/scroll. We need to
2505
// do this here to catch the case where the WebView is laid out at one size, removed from its
2506
// window, resized, and inserted into another window. Our frame/bounds changed notifications
2507
// will not be sent in that situation, since we only watch for changes while in the view hierarchy.
2508
[self _frameOrBoundsChanged];
2512
- (void)removeSuperviewObservers
2514
NSView *superview = [self superview];
2515
if (superview && [self window]) {
2516
[[NSNotificationCenter defaultCenter] removeObserver:self
2517
name:NSViewFrameDidChangeNotification object:superview];
2518
[[NSNotificationCenter defaultCenter] removeObserver:self
2519
name:NSViewBoundsDidChangeNotification object:superview];
2523
- (void)addWindowObservers
2525
NSWindow *window = [self window];
2527
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeKey:)
2528
name:NSWindowDidBecomeKeyNotification object:nil];
2529
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResignKey:)
2530
name:NSWindowDidResignKeyNotification object:nil];
2531
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowWillClose:)
2532
name:NSWindowWillCloseNotification object:window];
2536
- (void)removeWindowObservers
2538
NSWindow *window = [self window];
2540
[[NSNotificationCenter defaultCenter] removeObserver:self
2541
name:NSWindowDidBecomeKeyNotification object:nil];
2542
[[NSNotificationCenter defaultCenter] removeObserver:self
2543
name:NSWindowDidResignKeyNotification object:nil];
2544
[[NSNotificationCenter defaultCenter] removeObserver:self
2545
name:NSWindowWillCloseNotification object:window];
2549
- (void)viewWillMoveToSuperview:(NSView *)newSuperview
2551
[self removeSuperviewObservers];
2554
- (void)viewDidMoveToSuperview
2556
// Do this here in case the text size multiplier changed when a non-HTML
2557
// view was installed.
2558
if ([self superview] != nil) {
2559
[self _updateTextSizeMultiplier];
2560
[self addSuperviewObservers];
2564
static void _updateActiveStateTimerCallback(CFRunLoopTimerRef timer, void *info)
2566
WebHTMLView *view = (WebHTMLView *)info;
2567
[view _updateActiveState];
2570
- (void)viewWillMoveToWindow:(NSWindow *)window
2572
// Don't do anything if we aren't initialized. This happens
2573
// when decoding a WebView. When WebViews are decoded their subviews
2574
// are created by initWithCoder: and so won't be normally
2575
// initialized. The stub views are discarded by WebView.
2579
// FIXME: Some of these calls may not work because this view may be already removed from it's superview.
2580
[self removeMouseMovedObserverUnconditionally];
2581
[self removeWindowObservers];
2582
[self removeSuperviewObservers];
2583
[self _cancelUpdateMouseoverTimer];
2584
[self _cancelUpdateActiveStateTimer];
2586
[[self _pluginController] stopAllPlugins];
2589
- (void)viewDidMoveToWindow
2591
// Don't do anything if we aren't initialized. This happens
2592
// when decoding a WebView. When WebViews are decoded their subviews
2593
// are created by initWithCoder: and so won't be normally
2594
// initialized. The stub views are discarded by WebView.
2598
[self _stopAutoscrollTimer];
2599
if ([self window]) {
2600
_private->lastScrollPosition = [[self superview] bounds].origin;
2601
[self addWindowObservers];
2602
[self addSuperviewObservers];
2603
[self addMouseMovedObserver];
2605
// Schedule this update, rather than making the call right now.
2606
// The reason is that placing the caret in the just-installed view requires
2607
// the HTML/XML document to be available on the WebCore side, but it is not
2608
// at the time this code is running. However, it will be there on the next
2609
// crank of the run loop. Doing this helps to make a blinking caret appear
2610
// in a new, empty window "automatic".
2611
if (!_private->updateActiveStateTimer) {
2612
CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
2613
_private->updateActiveStateTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0, 0, 0,
2614
_updateActiveStateTimerCallback, &context);
2615
CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateActiveStateTimer, kCFRunLoopDefaultMode);
2618
[[self _pluginController] startAllPlugins];
2620
_private->lastScrollPosition = NSZeroPoint;
2624
- (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
2626
[[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
2629
- (void)viewDidMoveToHostWindow
2631
[[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
2635
- (void)addSubview:(NSView *)view
2637
[super addSubview:view];
2639
if ([WebPluginController isPlugInView:view])
2640
[[self _pluginController] addPlugin:view];
2643
- (void)willRemoveSubview:(NSView *)subview
2645
if ([WebPluginController isPlugInView:subview])
2646
[[self _pluginController] destroyPlugin:subview];
2648
[super willRemoveSubview:subview];
2651
- (void)reapplyStyles
2653
if (!_private->needsToApplyStyles) {
2658
double start = CFAbsoluteTimeGetCurrent();
2661
[[self _bridge] reapplyStylesForDeviceType:
2662
_private->printing ? WebCoreDevicePrinter : WebCoreDeviceScreen];
2665
double thisTime = CFAbsoluteTimeGetCurrent() - start;
2666
LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
2669
_private->needsToApplyStyles = NO;
2672
// Do a layout, but set up a new fixed width for the purposes of doing printing layout.
2673
// minPageWidth==0 implies a non-printing layout
2674
- (void)layoutToMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)adjustViewSize
2676
[self reapplyStyles];
2678
if (!_private->needsLayout && ![[self _bridge] needsLayout])
2682
double start = CFAbsoluteTimeGetCurrent();
2685
LOG(View, "%@ doing layout", self);
2687
if (minPageWidth > 0.0) {
2688
[[self _bridge] forceLayoutWithMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
2690
[[self _bridge] forceLayoutAdjustingViewSize:adjustViewSize];
2692
_private->needsLayout = NO;
2694
if (!_private->printing) {
2695
// get size of the containing dynamic scrollview, so
2696
// appearance and disappearance of scrollbars will not show up
2698
NSSize newLayoutFrameSize = [[[self superview] superview] frame].size;
2699
if (_private->laidOutAtLeastOnce && !NSEqualSizes(_private->lastLayoutFrameSize, newLayoutFrameSize)) {
2700
[[self _bridge] sendResizeEvent];
2701
if ([[self _bridge] needsLayout])
2702
[[self _bridge] forceLayoutAdjustingViewSize:NO];
2704
_private->laidOutAtLeastOnce = YES;
2705
_private->lastLayoutSize = [(NSClipView *)[self superview] documentVisibleRect].size;
2706
_private->lastLayoutFrameSize = newLayoutFrameSize;
2710
double thisTime = CFAbsoluteTimeGetCurrent() - start;
2711
LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
2717
[self layoutToMinimumPageWidth:0.0f maximumPageWidth:0.0f adjustingViewSize:NO];
2720
- (NSMenu *)menuForEvent:(NSEvent *)event
2722
[_private->compController endRevertingChange:NO moveLeft:NO];
2724
_private->handlingMouseDownEvent = YES;
2725
BOOL handledEvent = NO;
2726
Frame* coreFrame = core([self _frame]);
2729
_private->handlingMouseDownEvent = NO;
2733
Page* page = coreFrame->page();
2737
page->contextMenuController()->clearContextMenu();
2738
handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(PlatformMouseEvent(event));
2739
_private->handlingMouseDownEvent = NO;
2744
ContextMenu* coreMenu = page->contextMenuController()->contextMenu();
2748
NSArray* menuItems = coreMenu->platformDescription();
2750
if (menuItems && [menuItems count] > 0) {
2751
menu = [[[NSMenu alloc] init] autorelease];
2752
for (unsigned i = 0; i < [menuItems count]; i++)
2753
[menu addItem:[menuItems objectAtIndex:i]];
2759
- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
2761
return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
2766
Frame* coreFrame = core([self _frame]);
2769
Document* document = coreFrame->document();
2773
document->setFocusedNode(0);
2778
return [[self _webView] drawsBackground];
2781
- (void)setNeedsDisplay:(BOOL)flag
2783
LOG(View, "%@ flag = %d", self, (int)flag);
2784
[super setNeedsDisplay: flag];
2787
- (void)setNeedsLayout: (BOOL)flag
2789
LOG(View, "%@ flag = %d", self, (int)flag);
2790
_private->needsLayout = flag;
2794
- (void)setNeedsToApplyStyles: (BOOL)flag
2796
LOG(View, "%@ flag = %d", self, (int)flag);
2797
_private->needsToApplyStyles = flag;
2800
- (void)drawSingleRect:(NSRect)rect
2802
[NSGraphicsContext saveGraphicsState];
2805
ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
2807
[(WebClipView *)[self superview] setAdditionalClip:rect];
2810
if ([self _transparentBackground]) {
2811
[[NSColor clearColor] set];
2815
[[self _bridge] drawRect:rect];
2817
// This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be
2818
// called after the WebView has closed. If the client did not properly close the WebView and set the
2819
// UIDelegate to nil, then the UIDelegate will be stale and this code will crash.
2820
static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK);
2821
if (version3OrLaterClient) {
2822
WebView *webView = [self _webView];
2823
[[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
2826
[(WebClipView *)[self superview] resetAdditionalClip];
2828
[NSGraphicsContext restoreGraphicsState];
2830
[(WebClipView *)[self superview] resetAdditionalClip];
2831
[NSGraphicsContext restoreGraphicsState];
2832
LOG_ERROR("Exception caught while drawing: %@", localException);
2833
[localException raise];
2837
- (void)drawRect:(NSRect)rect
2839
ASSERT_MAIN_THREAD();
2840
LOG(View, "%@ drawing", self);
2842
const NSRect *rects;
2844
[self getRectsBeingDrawn:&rects count:&count];
2846
BOOL subviewsWereSetAside = _private->subviewsSetAside;
2847
if (subviewsWereSetAside)
2848
[self _restoreSubviews];
2851
double start = CFAbsoluteTimeGetCurrent();
2854
// If count == 0 here, use the rect passed in for drawing. This is a workaround for:
2855
// <rdar://problem/3908282> REGRESSION (Mail): No drag image dragging selected text in Blot and Mail
2856
// The reason for the workaround is that this method is called explicitly from the code
2857
// to generate a drag image, and at that time, getRectsBeingDrawn:count: will return a zero count.
2858
const int cRectThreshold = 10;
2859
const float cWastedSpaceThreshold = 0.75f;
2860
BOOL useUnionedRect = (count <= 1) || (count > cRectThreshold);
2861
if (!useUnionedRect) {
2862
// Attempt to guess whether or not we should use the unioned rect or the individual rects.
2863
// We do this by computing the percentage of "wasted space" in the union. If that wasted space
2864
// is too large, then we will do individual rect painting instead.
2865
float unionPixels = (rect.size.width * rect.size.height);
2866
float singlePixels = 0;
2867
for (int i = 0; i < count; ++i)
2868
singlePixels += rects[i].size.width * rects[i].size.height;
2869
float wastedSpace = 1 - (singlePixels / unionPixels);
2870
if (wastedSpace <= cWastedSpaceThreshold)
2871
useUnionedRect = YES;
2875
[self drawSingleRect:rect];
2877
for (int i = 0; i < count; ++i)
2878
[self drawSingleRect:rects[i]];
2881
double thisTime = CFAbsoluteTimeGetCurrent() - start;
2882
LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
2885
if (subviewsWereSetAside)
2886
[self _setAsideSubviews];
2889
// Turn off the additional clip while computing our visibleRect.
2890
- (NSRect)visibleRect
2892
if (!([[self superview] isKindOfClass:[WebClipView class]]))
2893
return [super visibleRect];
2895
WebClipView *clipView = (WebClipView *)[self superview];
2897
BOOL hasAdditionalClip = [clipView hasAdditionalClip];
2898
if (!hasAdditionalClip) {
2899
return [super visibleRect];
2902
NSRect additionalClip = [clipView additionalClip];
2903
[clipView resetAdditionalClip];
2904
NSRect visibleRect = [super visibleRect];
2905
[clipView setAdditionalClip:additionalClip];
2914
- (void)windowDidBecomeKey:(NSNotification *)notification
2916
NSWindow *keyWindow = [notification object];
2918
if (keyWindow == [self window])
2919
[self addMouseMovedObserver];
2921
if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
2922
[self _updateActiveState];
2925
- (void)windowDidResignKey:(NSNotification *)notification
2927
NSWindow *formerKeyWindow = [notification object];
2929
if (formerKeyWindow == [self window])
2930
[self removeMouseMovedObserver];
2932
if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
2933
[self _updateActiveState];
2934
[_private->compController endRevertingChange:NO moveLeft:NO];
2938
- (void)windowWillClose:(NSNotification *)notification
2940
[_private->compController endRevertingChange:NO moveLeft:NO];
2941
[[self _pluginController] destroyAllPlugins];
2944
- (void)scrollWheel:(NSEvent *)event
2947
Frame* frame = core([self _frame]);
2948
if (!frame || !frame->eventHandler()->wheelEvent(event))
2949
[[self nextResponder] scrollWheel:event];
2953
- (BOOL)_isSelectionEvent:(NSEvent *)event
2955
NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2956
return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
2959
- (BOOL)acceptsFirstMouse:(NSEvent *)event
2961
NSView *hitView = [self _hitViewForEvent:event];
2962
WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
2964
if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
2968
bool result = false;
2969
if (Frame* coreFrame = core([hitHTMLView _frame])) {
2970
coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]);
2971
[hitHTMLView _setMouseDownEvent:event];
2972
if ([hitHTMLView _isSelectionEvent:event])
2973
result = coreFrame->eventHandler()->eventMayStartDrag(event);
2974
[hitHTMLView _setMouseDownEvent:nil];
2978
return [hitView acceptsFirstMouse:event];
2981
- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
2983
NSView *hitView = [self _hitViewForEvent:event];
2984
WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
2986
bool result = false;
2987
if ([hitHTMLView _isSelectionEvent:event])
2988
if (Frame* coreFrame = core([hitHTMLView _frame])) {
2989
[hitHTMLView _setMouseDownEvent:event];
2990
result = coreFrame->eventHandler()->eventMayStartDrag(event);
2991
[hitHTMLView _setMouseDownEvent:nil];
2995
return [hitView shouldDelayWindowOrderingForEvent:event];
2998
- (void)mouseDown:(NSEvent *)event
3002
_private->handlingMouseDownEvent = YES;
3004
// Record the mouse down position so we can determine drag hysteresis.
3005
[self _setMouseDownEvent:event];
3007
NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3008
if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3011
[_private->compController endRevertingChange:NO moveLeft:NO];
3013
// If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
3014
// We don't want to pass them along to KHTML a second time.
3015
if (!([event modifierFlags] & NSControlKeyMask)) {
3016
_private->ignoringMouseDraggedEvents = NO;
3018
// Don't do any mouseover while the mouse is down.
3019
[self _cancelUpdateMouseoverTimer];
3021
// Let WebCore get a chance to deal with the event. This will call back to us
3022
// to start the autoscroll timer if appropriate.
3023
if (Frame* coreframe = core([self _frame]))
3024
coreframe->eventHandler()->mouseDown(event);
3028
[_private->firstResponderTextViewAtMouseDownTime release];
3029
_private->firstResponderTextViewAtMouseDownTime = nil;
3031
_private->handlingMouseDownEvent = NO;
3036
- (void)dragImage:(NSImage *)dragImage
3038
offset:(NSSize)offset
3039
event:(NSEvent *)event
3040
pasteboard:(NSPasteboard *)pasteboard
3042
slideBack:(BOOL)slideBack
3044
ASSERT(self == [self _topHTMLView]);
3045
[super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
3048
- (void)mouseDragged:(NSEvent *)event
3050
NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3051
if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3056
if (!_private->ignoringMouseDraggedEvents)
3057
if (Frame* coreframe = core([self _frame]))
3058
coreframe->eventHandler()->mouseDragged(event);
3063
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
3065
ASSERT(![self _webView] || [self _isTopHTMLView]);
3067
Page *page = core([self _webView]);
3070
return NSDragOperationNone;
3072
if (page->dragController()->dragOperation() == DragOperationNone)
3073
return NSDragOperationGeneric | NSDragOperationCopy;
3075
return (NSDragOperation)page->dragController()->dragOperation();
3078
- (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenLoc
3080
ASSERT(![self _webView] || [self _isTopHTMLView]);
3082
NSPoint windowImageLoc = [[self window] convertScreenToBase:screenLoc];
3083
NSPoint windowMouseLoc = windowImageLoc;
3085
if (Page* page = core([self _webView])) {
3086
DragController* dragController = page->dragController();
3087
NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3090
[[self _bridge] dragSourceMovedTo:windowMouseLoc];
3093
- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
3095
ASSERT(![self _webView] || [self _isTopHTMLView]);
3097
NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
3098
NSPoint windowMouseLoc = windowImageLoc;
3100
if (Page* page = core([self _webView])) {
3101
DragController* dragController = page->dragController();
3102
windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3103
dragController->dragEnded();
3106
[[self _bridge] dragSourceEndedAt:windowMouseLoc operation:operation];
3108
// Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
3109
_private->ignoringMouseDraggedEvents = YES;
3111
// Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
3112
// WebCore expects to get balanced down/up's, so we must fake up a mouseup.
3113
NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
3114
location:windowMouseLoc
3115
modifierFlags:[[NSApp currentEvent] modifierFlags]
3116
timestamp:[NSDate timeIntervalSinceReferenceDate]
3117
windowNumber:[[self window] windowNumber]
3118
context:[[NSApp currentEvent] context]
3119
eventNumber:0 clickCount:0 pressure:0];
3120
[self mouseUp:fakeEvent]; // This will also update the mouseover state.
3123
- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
3125
NSFileWrapper *wrapper = nil;
3127
if (WebCore::CachedResource* tiffResource = [self promisedDragTIFFDataSource]) {
3129
SharedBuffer *buffer = tiffResource->data();
3131
goto noPromisedData;
3133
NSData *data = buffer->createNSData();
3134
NSURLResponse *response = tiffResource->response().nsURLResponse();
3136
wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
3137
[wrapper setPreferredFilename:[response suggestedFilename]];
3143
ASSERT(![self _webView] || [self _isTopHTMLView]);
3144
Page* page = core([self _webView]);
3146
//If a load occurs midway through a drag, the view may be detached, which gives
3147
//us no ability to get to the original Page, so we cannot access any drag state
3148
//FIXME: is there a way to recover?
3152
KURL imageURL = page->dragController()->draggingImageURL();
3153
ASSERT(!imageURL.isEmpty());
3155
wrapper = [[self _dataSource] _fileWrapperForURL:imageURL.getNSURL()];
3158
if (wrapper == nil) {
3159
LOG_ERROR("Failed to create image file.");
3163
// FIXME: Report an error if we fail to create a file.
3164
NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
3165
path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
3166
if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
3167
LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
3169
return [NSArray arrayWithObject:[path lastPathComponent]];
3172
- (void)mouseUp:(NSEvent *)event
3174
[self _setMouseDownEvent:nil];
3176
NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3177
if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3182
[self _stopAutoscrollTimer];
3183
if (Frame* coreframe = core([self _frame]))
3184
coreframe->eventHandler()->mouseUp(event);
3185
[self _updateMouseoverWithFakeEvent];
3190
- (void)mouseMovedNotification:(NSNotification *)notification
3192
[self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
3195
// returning YES from this method is the way we tell AppKit that it is ok for this view
3196
// to be in the key loop even when "tab to all controls" is not on.
3197
- (BOOL)needsPanelToBecomeKey
3202
- (BOOL)becomeFirstResponder
3204
NSSelectionDirection direction = NSDirectSelection;
3205
if (![[self _webView] _isPerformingProgrammaticFocus] && !_private->willBecomeFirstResponderForNodeFocus)
3206
direction = [[self window] keyViewSelectionDirection];
3207
_private->willBecomeFirstResponderForNodeFocus = NO;
3209
[self _updateActiveState];
3210
[self _updateFontPanel];
3212
Frame* frame = core([self _frame]);
3216
frame->editor()->setStartNewKillRingSequence(true);
3218
if (direction == NSDirectSelection)
3221
Page* page = frame->page();
3225
page->focusController()->setFocusedFrame(frame);
3226
if (Document* document = frame->document())
3227
document->setFocusedNode(0);
3228
page->focusController()->setInitialFocus(frame->eventHandler()->currentKeyboardEvent().get());
3232
- (BOOL)resignFirstResponder
3234
BOOL resign = [super resignFirstResponder];
3236
[_private->compController endRevertingChange:NO moveLeft:NO];
3237
_private->resigningFirstResponder = YES;
3238
if (![self maintainsInactiveSelection]) {
3240
if (![[self _webView] _isPerformingProgrammaticFocus])
3243
[self _updateActiveState];
3244
_private->resigningFirstResponder = NO;
3245
_private->willBecomeFirstResponderForNodeFocus = NO;
3250
- (void)setDataSource:(WebDataSource *)dataSource
3253
if (_private->dataSource != dataSource) {
3254
ASSERT(!_private->dataSource);
3255
ASSERT(!_private->closed);
3256
_private->dataSource = [dataSource retain];
3257
[_private->pluginController setDataSource:dataSource];
3258
[self addMouseMovedObserver];
3262
- (void)dataSourceUpdated:(WebDataSource *)dataSource
3266
// This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes
3267
// key. WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme
3268
// renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly).
3269
- (void)updateCell:(NSCell*)cell
3273
// Does setNeedsDisplay:NO as a side effect when printing is ending.
3274
// pageWidth != 0 implies we will relayout to a new width
3275
- (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize
3277
WebFrame *frame = [self _frame];
3278
NSArray *subframes = [frame childFrames];
3279
unsigned n = [subframes count];
3281
for (i = 0; i != n; ++i) {
3282
WebFrame *subframe = [subframes objectAtIndex:i];
3283
WebFrameView *frameView = [subframe frameView];
3284
if ([[subframe _dataSource] _isDocumentHTML]) {
3285
[(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:adjustViewSize];
3289
if (printing != _private->printing) {
3290
[_private->pageRects release];
3291
_private->pageRects = nil;
3292
_private->printing = printing;
3294
_private->avoidingPrintOrphan = NO;
3295
[self setNeedsToApplyStyles:YES];
3296
[self setNeedsLayout:YES];
3297
[self layoutToMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
3299
// Can't do this when starting printing or nested printing won't work, see 3491427.
3300
[self setNeedsDisplay:NO];
3305
- (BOOL)canPrintHeadersAndFooters
3310
// This is needed for the case where the webview is embedded in the view that's being printed.
3311
// It shouldn't be called when the webview is being printed directly.
3312
- (void)adjustPageHeightNew:(float *)newBottom top:(float)oldTop bottom:(float)oldBottom limit:(float)bottomLimit
3314
// This helps when we print as part of a larger print process.
3315
// If the WebHTMLView itself is what we're printing, then we will never have to do this.
3316
BOOL wasInPrintingMode = _private->printing;
3317
if (!wasInPrintingMode)
3318
[self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3320
[[self _bridge] adjustPageHeightNew:newBottom top:oldTop bottom:oldBottom limit:bottomLimit];
3322
if (!wasInPrintingMode) {
3323
NSPrintOperation *currenPrintOperation = [NSPrintOperation currentOperation];
3324
if (currenPrintOperation)
3325
// delay _setPrinting:NO until back to main loop as this method may get called repeatedly
3326
[self performSelector:@selector(_delayedEndPrintMode:) withObject:currenPrintOperation afterDelay:0];
3328
// not sure if this is actually ever invoked, it probably shouldn't be
3329
[self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3333
- (float)_availablePaperWidthForPrintOperation:(NSPrintOperation *)printOperation
3335
NSPrintInfo *printInfo = [printOperation printInfo];
3336
return [printInfo paperSize].width - [printInfo leftMargin] - [printInfo rightMargin];
3339
- (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3341
float viewWidth = NSWidth([self bounds]);
3342
if (viewWidth < 1) {
3343
LOG_ERROR("%@ has no width when printing", self);
3347
float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3348
float maxShrinkToFitScaleFactor = 1.0f / PrintingMaximumShrinkFactor;
3349
float shrinkToFitScaleFactor = [self _availablePaperWidthForPrintOperation:printOperation]/viewWidth;
3350
float shrinkToAvoidOrphan = _private->avoidingPrintOrphan ? (1.0f / PrintingOrphanShrinkAdjustment) : 1.0f;
3351
return userScaleFactor * MAX(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor) * shrinkToAvoidOrphan;
3354
// FIXME 3491344: This is a secret AppKit-internal method that we need to override in order
3355
// to get our shrink-to-fit to work with a custom pagination scheme. We can do this better
3356
// if AppKit makes it SPI/API.
3357
- (float)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3359
return [self _scaleFactorForPrintOperation:printOperation];
3362
// This is used for Carbon printing. At some point we might want to make this public API.
3363
- (void)setPageWidthForPrinting:(float)pageWidth
3365
[self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3366
[self _setPrinting:YES minimumPageWidth:pageWidth maximumPageWidth:pageWidth adjustViewSize:YES];
3369
- (void)_endPrintMode
3371
[self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
3372
[[self window] setAutodisplay:YES];
3375
- (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation
3377
ASSERT_ARG(initiatingOperation, initiatingOperation != nil);
3378
NSPrintOperation *currentOperation = [NSPrintOperation currentOperation];
3379
if (initiatingOperation == currentOperation) {
3380
// The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're
3381
// being extra paranoid here since the printing code is so fragile. Delay the cleanup
3383
ASSERT_NOT_REACHED();
3384
[self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0];
3385
} else if ([currentOperation view] == self) {
3386
// A new print job has started, but it is printing the same WebHTMLView again. We don't expect
3387
// this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so
3388
// fragile. Do nothing, because we don't want to break the print job currently in progress, and
3389
// the print job currently in progress is responsible for its own cleanup.
3390
ASSERT_NOT_REACHED();
3392
// The print job that kicked off this delayed call has finished, and this view is not being
3393
// printed again. We expect that no other print job has started. Since this delayed call wasn't
3394
// cancelled, beginDocument and endDocument must not have been called, and we need to clean up
3395
// the print mode here.
3396
ASSERT(currentOperation == nil);
3397
[self _endPrintMode];
3401
// Return the number of pages available for printing
3402
- (BOOL)knowsPageRange:(NSRangePointer)range
3404
// Must do this explicit display here, because otherwise the view might redisplay while the print
3405
// sheet was up, using printer fonts (and looking different).
3406
[self displayIfNeeded];
3407
[[self window] setAutodisplay:NO];
3409
// If we are a frameset just print with the layout we have onscreen, otherwise relayout
3410
// according to the paper size
3411
float minLayoutWidth = 0.0f;
3412
float maxLayoutWidth = 0.0f;
3413
Frame* frame = core([self _frame]);
3416
if (!frame->isFrameSet()) {
3417
float paperWidth = [self _availablePaperWidthForPrintOperation:[NSPrintOperation currentOperation]];
3418
minLayoutWidth = paperWidth * PrintingMinimumShrinkFactor;
3419
maxLayoutWidth = paperWidth * PrintingMaximumShrinkFactor;
3421
[self _setPrinting:YES minimumPageWidth:minLayoutWidth maximumPageWidth:maxLayoutWidth adjustViewSize:YES]; // will relayout
3422
NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
3423
// Certain types of errors, including invalid page ranges, can cause beginDocument and
3424
// endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases
3425
// we need to get out of print mode without relying on any more callbacks from the printing mechanism.
3426
// If we get as far as beginDocument without trouble, then this delayed request will be cancelled.
3427
// If not cancelled, this delayed call will be invoked in the next pass through the main event loop,
3428
// which is after beginDocument and endDocument would be called.
3429
[self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0];
3430
[[self _webView] _adjustPrintingMarginsForHeaderAndFooter];
3432
// There is a theoretical chance that someone could do some drawing between here and endDocument,
3433
// if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
3434
// you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
3436
range->location = 1;
3437
float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation];
3438
float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3439
[_private->pageRects release];
3440
float fullPageHeight = floorf([self _calculatePrintHeight]/totalScaleFactor);
3441
NSArray *newPageRects = [[self _bridge] computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3442
printHeight:fullPageHeight];
3444
// AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we
3445
// hit that case we'll pass along a degenerate 1 pixel square to print. This will print
3446
// a blank page (with correct-looking header and footer if that option is on), which matches
3447
// the behavior of IE and Camino at least.
3448
if ([newPageRects count] == 0)
3449
newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]];
3450
else if ([newPageRects count] > 1) {
3451
// If the last page is a short orphan, try adjusting the print height slightly to see if this will squeeze the
3452
// content onto one fewer page. If it does, use the adjusted scale. If not, use the original scale.
3453
float lastPageHeight = NSHeight([[newPageRects lastObject] rectValue]);
3454
if (lastPageHeight/fullPageHeight < LastPrintedPageOrphanRatio) {
3455
NSArray *adjustedPageRects = [[self _bridge] computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3456
printHeight:fullPageHeight*PrintingOrphanShrinkAdjustment];
3457
// Use the adjusted rects only if the page count went down
3458
if ([adjustedPageRects count] < [newPageRects count]) {
3459
newPageRects = adjustedPageRects;
3460
_private->avoidingPrintOrphan = YES;
3465
_private->pageRects = [newPageRects retain];
3467
range->length = [_private->pageRects count];
3472
// Return the drawing rectangle for a particular page number
3473
- (NSRect)rectForPage:(int)page
3475
return [[_private->pageRects objectAtIndex:page - 1] rectValue];
3478
- (void)drawPageBorderWithSize:(NSSize)borderSize
3480
ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));
3481
[[self _webView] _drawHeaderAndFooter];
3484
- (void)beginDocument
3487
// From now on we'll get a chance to call _endPrintMode in either beginDocument or
3488
// endDocument, so we can cancel the "just in case" pending call.
3489
[NSObject cancelPreviousPerformRequestsWithTarget:self
3490
selector:@selector(_delayedEndPrintMode:)
3491
object:[NSPrintOperation currentOperation]];
3492
[super beginDocument];
3494
// Exception during [super beginDocument] means that endDocument will not get called,
3495
// so we need to clean up our "print mode" here.
3496
[self _endPrintMode];
3502
[super endDocument];
3503
// Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO
3504
[self _endPrintMode];
3507
- (void)keyDown:(NSEvent *)event
3509
RetainPtr<WebHTMLView> selfProtector = self;
3510
BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
3512
BOOL callSuper = NO;
3514
[_private->keyDownEvent release];
3515
_private->keyDownEvent = [event retain];
3517
BOOL completionPopupWasOpen = _private->compController && [_private->compController popupWindowIsOpen];
3518
if (!eventWasSentToWebCore && core([self _frame])->eventHandler()->keyEvent(event)) {
3519
// WebCore processed a key event, bail on any preexisting complete: UI
3520
if (completionPopupWasOpen)
3521
[_private->compController endRevertingChange:YES moveLeft:NO];
3522
} else if (!_private->compController || ![_private->compController filterKeyDown:event]) {
3523
// Not consumed by complete: popup window
3524
[_private->compController endRevertingChange:YES moveLeft:NO];
3528
[super keyDown:event];
3530
[NSCursor setHiddenUntilMouseMoves:YES];
3533
- (void)keyUp:(NSEvent *)event
3535
BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
3538
if (eventWasSentToWebCore || !core([self _frame])->eventHandler()->keyEvent(event))
3539
[super keyUp:event];
3543
- (id)accessibilityAttributeValue:(NSString*)attributeName
3545
if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
3546
id accTree = [[self _bridge] accessibilityTree];
3548
return [NSArray arrayWithObject:accTree];
3551
return [super accessibilityAttributeValue:attributeName];
3554
- (id)accessibilityFocusedUIElement
3556
id accTree = [[self _bridge] accessibilityTree];
3558
return [accTree accessibilityFocusedUIElement];
3562
- (id)accessibilityHitTest:(NSPoint)point
3564
id accTree = [[self _bridge] accessibilityTree];
3566
NSPoint windowCoord = [[self window] convertScreenToBase:point];
3567
return [accTree accessibilityHitTest:[self convertPoint:windowCoord fromView:nil]];
3572
- (id)_accessibilityParentForSubview:(NSView *)subview
3574
id accTree = [[self _bridge] accessibilityTree];
3577
id parent = [accTree _accessibilityParentForSubview:subview];
3583
- (void)centerSelectionInVisibleArea:(id)sender
3587
if (Frame* coreFrame = core([self _frame]))
3588
coreFrame->revealSelection(RenderLayer::gAlignCenterAlways);
3591
- (void)pageUp:(id)sender
3595
WebFrameView *frameView = [self _frameView];
3598
if ([self _canAlterCurrentSelection])
3599
[[self _bridge] alterCurrentSelection:SelectionController::MOVE verticalDistance:-[frameView _verticalPageScrollDistance]];
3602
- (void)pageDown:(id)sender
3606
WebFrameView *frameView = [self _frameView];
3609
if ([self _canAlterCurrentSelection])
3610
[[self _bridge] alterCurrentSelection:SelectionController::MOVE verticalDistance:[frameView _verticalPageScrollDistance]];
3613
- (void)pageUpAndModifySelection:(id)sender
3617
WebFrameView *frameView = [self _frameView];
3620
if ([self _canAlterCurrentSelection])
3621
[[self _bridge] alterCurrentSelection:SelectionController::EXTEND verticalDistance:-[frameView _verticalPageScrollDistance]];
3624
- (void)pageDownAndModifySelection:(id)sender
3628
WebFrameView *frameView = [self _frameView];
3629
if (frameView == nil)
3631
if ([self _canAlterCurrentSelection])
3632
[[self _bridge] alterCurrentSelection:SelectionController::EXTEND verticalDistance:[frameView _verticalPageScrollDistance]];
3635
- (void)_expandSelectionToGranularity:(TextGranularity)granularity
3637
if (![self _canAlterCurrentSelection])
3640
Frame* coreFrame = core([self _frame]);
3641
if (!coreFrame || !coreFrame->selectionController()->isCaretOrRange())
3644
// NOTE: The enums *must* match the very similar ones declared in SelectionController.h
3645
Selection selection(coreFrame->selectionController()->selection());
3646
selection.expandUsingGranularity(static_cast<TextGranularity>(granularity));
3648
RefPtr<Range> range = selection.toRange();
3649
DOMRange *domRange = kit(range.get());
3651
if ([domRange collapsed])
3654
EAffinity affinity = coreFrame->selectionController()->affinity();
3655
WebView *webView = [self _webView];
3656
if ([[webView _editingDelegateForwarder] webView:webView shouldChangeSelectedDOMRange:[self _selectedRange] toDOMRange:domRange affinity:kit(affinity) stillSelecting:NO]) {
3657
ExceptionCode ec = 0;
3658
coreFrame->selectionController()->setSelectedRange(range.get(), affinity, true, ec);
3662
- (void)selectParagraph:(id)sender
3666
[self _expandSelectionToGranularity:ParagraphGranularity];
3669
- (void)selectLine:(id)sender
3673
[self _expandSelectionToGranularity:LineGranularity];
3676
- (void)selectSentence:(id)sender
3680
[self _expandSelectionToGranularity:SentenceGranularity];
3683
- (void)selectWord:(id)sender
3687
[self _expandSelectionToGranularity:WordGranularity];
3690
- (void)delete:(id)sender
3694
if (Frame* coreFrame = core([self _frame]))
3695
coreFrame->editor()->performDelete();
3698
- (NSData *)_selectionStartFontAttributesAsRTF
3700
NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"x"
3701
attributes:core([self _frame])->fontAttributesForSelectionStart()];
3702
NSData *data = [string RTFFromRange:NSMakeRange(0, [string length]) documentAttributes:nil];
3707
- (NSDictionary *)_fontAttributesFromFontPasteboard
3709
NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
3710
if (fontPasteboard == nil)
3712
NSData *data = [fontPasteboard dataForType:NSFontPboardType];
3713
if (data == nil || [data length] == 0)
3715
// NSTextView does something more efficient by parsing the attributes only, but that's not available in API.
3716
NSAttributedString *string = [[[NSAttributedString alloc] initWithRTF:data documentAttributes:NULL] autorelease];
3717
if (string == nil || [string length] == 0)
3719
return [string fontAttributesInRange:NSMakeRange(0, 1)];
3722
- (DOMCSSStyleDeclaration *)_emptyStyle
3724
return [[[self _frame] DOMDocument] createCSSStyleDeclaration];
3727
- (NSString *)_colorAsString:(NSColor *)color
3729
NSColor *rgbColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
3730
// FIXME: If color is non-nil and rgbColor is nil, that means we got some kind
3731
// of fancy color that can't be converted to RGB. Changing that to "transparent"
3732
// might not be great, but it's probably OK.
3733
if (rgbColor == nil)
3734
return @"transparent";
3735
float r = [rgbColor redComponent];
3736
float g = [rgbColor greenComponent];
3737
float b = [rgbColor blueComponent];
3738
float a = [rgbColor alphaComponent];
3740
return @"transparent";
3741
if (r == 0 && g == 0 && b == 0 && a == 1)
3743
if (r == 1 && g == 1 && b == 1 && a == 1)
3745
// FIXME: Lots more named colors. Maybe we could use the table in WebCore?
3747
return [NSString stringWithFormat:@"rgb(%.0f,%.0f,%.0f)", r * 255, g * 255, b * 255];
3748
return [NSString stringWithFormat:@"rgba(%.0f,%.0f,%.0f,%f)", r * 255, g * 255, b * 255, a];
3751
- (NSString *)_shadowAsString:(NSShadow *)shadow
3755
NSSize offset = [shadow shadowOffset];
3756
float blurRadius = [shadow shadowBlurRadius];
3757
if (offset.width == 0 && offset.height == 0 && blurRadius == 0)
3759
NSColor *color = [shadow shadowColor];
3762
// FIXME: Handle non-integral values here?
3763
if (blurRadius == 0)
3764
return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height];
3765
return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height, blurRadius];
3768
- (DOMCSSStyleDeclaration *)_styleFromFontAttributes:(NSDictionary *)dictionary
3770
DOMCSSStyleDeclaration *style = [self _emptyStyle];
3772
NSColor *color = [dictionary objectForKey:NSBackgroundColorAttributeName];
3773
[style setBackgroundColor:[self _colorAsString:color]];
3775
NSFont *font = [dictionary objectForKey:NSFontAttributeName];
3777
[style setFontFamily:@"Helvetica"];
3778
[style setFontSize:@"12px"];
3779
[style setFontWeight:@"normal"];
3780
[style setFontStyle:@"normal"];
3782
NSFontManager *fm = [NSFontManager sharedFontManager];
3783
// FIXME: Need more sophisticated escaping code if we want to handle family names
3784
// with characters like single quote or backslash in their names.
3785
[style setFontFamily:[NSString stringWithFormat:@"'%@'", [font familyName]]];
3786
[style setFontSize:[NSString stringWithFormat:@"%0.fpx", [font pointSize]]];
3787
if ([fm weightOfFont:font] >= MIN_BOLD_WEIGHT)
3788
[style setFontWeight:@"bold"];
3790
[style setFontWeight:@"normal"];
3791
if (([fm traitsOfFont:font] & NSItalicFontMask) != 0)
3792
[style setFontStyle:@"italic"];
3794
[style setFontStyle:@"normal"];
3797
color = [dictionary objectForKey:NSForegroundColorAttributeName];
3798
[style setColor:color ? [self _colorAsString:color] : (NSString *)@"black"];
3800
NSShadow *shadow = [dictionary objectForKey:NSShadowAttributeName];
3801
[style setTextShadow:[self _shadowAsString:shadow]];
3803
int strikethroughInt = [[dictionary objectForKey:NSStrikethroughStyleAttributeName] intValue];
3805
int superscriptInt = [[dictionary objectForKey:NSSuperscriptAttributeName] intValue];
3806
if (superscriptInt > 0)
3807
[style setVerticalAlign:@"super"];
3808
else if (superscriptInt < 0)
3809
[style setVerticalAlign:@"sub"];
3811
[style setVerticalAlign:@"baseline"];
3812
int underlineInt = [[dictionary objectForKey:NSUnderlineStyleAttributeName] intValue];
3813
// FIXME: Underline wins here if we have both (see bug 3790443).
3814
if (strikethroughInt == NSUnderlineStyleNone && underlineInt == NSUnderlineStyleNone)
3815
[style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
3816
else if (underlineInt == NSUnderlineStyleNone)
3817
[style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""];
3819
[style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
3824
- (void)_applyStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
3826
if (Frame* coreFrame = core([self _frame]))
3827
coreFrame->editor()->applyStyleToSelection(core(style), undoAction);
3830
- (void)_applyParagraphStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
3832
if (Frame* coreFrame = core([self _frame]))
3833
coreFrame->editor()->applyParagraphStyleToSelection(core(style), undoAction);
3838
if (Frame* coreFrame = core([self _frame]))
3839
coreFrame->editor()->execCommand("ToggleBold");
3842
- (void)_toggleItalic
3844
if (Frame* coreFrame = core([self _frame]))
3845
coreFrame->editor()->execCommand("ToggleItalic");
3848
- (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event
3850
ASSERT([self _webView]);
3851
if (![[[self _webView] preferences] respectStandardStyleKeyEquivalents])
3854
if (![self _canEdit])
3857
if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask)
3860
NSString *string = [event characters];
3861
if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) {
3865
if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) {
3866
[self _toggleItalic];
3873
- (BOOL)performKeyEquivalent:(NSEvent *)event
3875
if ([self _handleStyleKeyEquivalent:event])
3878
BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
3881
[_private->keyDownEvent release];
3882
_private->keyDownEvent = [event retain];
3886
// Pass command-key combos through WebCore if there is a key binding available for
3887
// this event. This lets web pages have a crack at intercepting command-modified keypresses.
3888
// But don't do it if we have already handled the event.
3889
// Pressing Esc results in a fake event being sent - don't pass it to WebCore.
3890
if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder])
3891
if (Frame* frame = core([self _frame]))
3892
ret = frame->eventHandler()->keyEvent(event);
3895
ret = [super performKeyEquivalent:event];
3902
- (void)copyFont:(id)sender
3906
// Put RTF with font attributes on the pasteboard.
3907
// Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
3908
NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
3909
[fontPasteboard declareTypes:[NSArray arrayWithObject:NSFontPboardType] owner:nil];
3910
[fontPasteboard setData:[self _selectionStartFontAttributesAsRTF] forType:NSFontPboardType];
3913
- (void)pasteFont:(id)sender
3917
// Read RTF with font attributes from the pasteboard.
3918
// Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
3919
[self _applyStyleToSelection:[self _styleFromFontAttributes:[self _fontAttributesFromFontPasteboard]] withUndoAction:EditActionPasteFont];
3922
- (void)pasteAsRichText:(id)sender
3926
// Since rich text always beats plain text when both are on the pasteboard, it's not
3927
// clear how this is different from plain old paste.
3928
[self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:NO];
3931
- (NSFont *)_originalFontA
3933
return [[NSFontManager sharedFontManager] fontWithFamily:@"Helvetica" traits:0 weight:STANDARD_WEIGHT size:10.0f];
3936
- (NSFont *)_originalFontB
3938
return [[NSFontManager sharedFontManager] fontWithFamily:@"Times" traits:(NSBoldFontMask | NSItalicFontMask) weight:STANDARD_BOLD_WEIGHT size:12.0f];
3941
- (void)_addToStyle:(DOMCSSStyleDeclaration *)style fontA:(NSFont *)a fontB:(NSFont *)b
3943
// Since there's no way to directly ask NSFontManager what style change it's going to do
3944
// we instead pass two "specimen" fonts to it and let it change them. We then deduce what
3945
// style change it was doing by looking at what happened to each of the two fonts.
3946
// So if it was making the text bold, both fonts will be bold after the fact.
3948
if (a == nil || b == nil)
3951
NSFontManager *fm = [NSFontManager sharedFontManager];
3953
NSFont *oa = [self _originalFontA];
3955
NSString *aFamilyName = [a familyName];
3956
NSString *bFamilyName = [b familyName];
3958
int aPointSize = (int)[a pointSize];
3959
int bPointSize = (int)[b pointSize];
3961
int aWeight = [fm weightOfFont:a];
3962
int bWeight = [fm weightOfFont:b];
3964
BOOL aIsBold = aWeight >= MIN_BOLD_WEIGHT;
3966
BOOL aIsItalic = ([fm traitsOfFont:a] & NSItalicFontMask) != 0;
3967
BOOL bIsItalic = ([fm traitsOfFont:b] & NSItalicFontMask) != 0;
3969
if ([aFamilyName isEqualToString:bFamilyName]) {
3970
NSString *familyNameForCSS = aFamilyName;
3972
// The family name may not be specific enough to get us the font specified.
3973
// In some cases, the only way to get exactly what we are looking for is to use
3974
// the Postscript name.
3976
// Find the font the same way the rendering code would later if it encountered this CSS.
3977
NSFontTraitMask traits = 0;
3979
traits |= NSBoldFontMask;
3981
traits |= NSItalicFontMask;
3982
NSFont *foundFont = WebCoreFindFont(aFamilyName, traits, aPointSize);
3984
// If we don't find a font with the same Postscript name, then we'll have to use the
3985
// Postscript name to make the CSS specific enough.
3986
if (![[foundFont fontName] isEqualToString:[a fontName]]) {
3987
familyNameForCSS = [a fontName];
3990
// FIXME: Need more sophisticated escaping code if we want to handle family names
3991
// with characters like single quote or backslash in their names.
3992
[style setFontFamily:[NSString stringWithFormat:@"'%@'", familyNameForCSS]];
3995
int soa = (int)[oa pointSize];
3996
if (aPointSize == bPointSize)
3997
[style setFontSize:[NSString stringWithFormat:@"%dpx", aPointSize]];
3998
else if (aPointSize < soa)
3999
[style _setFontSizeDelta:@"-1px"];
4000
else if (aPointSize > soa)
4001
[style _setFontSizeDelta:@"1px"];
4003
if (aWeight == bWeight)
4004
[style setFontWeight:aIsBold ? @"bold" : @"normal"];
4006
if (aIsItalic == bIsItalic)
4007
[style setFontStyle:aIsItalic ? @"italic" : @"normal"];
4010
- (DOMCSSStyleDeclaration *)_styleFromFontManagerOperation
4012
DOMCSSStyleDeclaration *style = [self _emptyStyle];
4014
NSFontManager *fm = [NSFontManager sharedFontManager];
4016
NSFont *oa = [self _originalFontA];
4017
NSFont *ob = [self _originalFontB];
4018
[self _addToStyle:style fontA:[fm convertFont:oa] fontB:[fm convertFont:ob]];
4023
- (void)changeFont:(id)sender
4027
[self _applyStyleToSelection:[self _styleFromFontManagerOperation] withUndoAction:EditActionSetFont];
4030
- (DOMCSSStyleDeclaration *)_styleForAttributeChange:(id)sender
4032
DOMCSSStyleDeclaration *style = [self _emptyStyle];
4034
NSShadow *shadow = [[NSShadow alloc] init];
4035
[shadow setShadowOffset:NSMakeSize(1, 1)];
4037
NSDictionary *oa = [NSDictionary dictionaryWithObjectsAndKeys:
4038
[self _originalFontA], NSFontAttributeName,
4040
NSDictionary *ob = [NSDictionary dictionaryWithObjectsAndKeys:
4041
[NSColor blackColor], NSBackgroundColorAttributeName,
4042
[self _originalFontB], NSFontAttributeName,
4043
[NSColor whiteColor], NSForegroundColorAttributeName,
4044
shadow, NSShadowAttributeName,
4045
[NSNumber numberWithInt:NSUnderlineStyleSingle], NSStrikethroughStyleAttributeName,
4046
[NSNumber numberWithInt:1], NSSuperscriptAttributeName,
4047
[NSNumber numberWithInt:NSUnderlineStyleSingle], NSUnderlineStyleAttributeName,
4054
NSObliquenessAttributeName /* float; skew to be applied to glyphs, default 0: no skew */
4055
// font-style, but that is just an on-off switch
4057
NSExpansionAttributeName /* float; log of expansion factor to be applied to glyphs, default 0: no expansion */
4060
NSKernAttributeName /* float, amount to modify default kerning, if 0, kerning off */
4061
// letter-spacing? probably not good enough
4063
NSUnderlineColorAttributeName /* NSColor, default nil: same as foreground color */
4064
NSStrikethroughColorAttributeName /* NSColor, default nil: same as foreground color */
4065
// text-decoration-color?
4067
NSLigatureAttributeName /* int, default 1: default ligatures, 0: no ligatures, 2: all ligatures */
4068
NSBaselineOffsetAttributeName /* float, in points; offset from baseline, default 0 */
4069
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) */
4070
NSStrokeColorAttributeName /* NSColor, default nil: same as foreground color */
4075
NSDictionary *a = [sender convertAttributes:oa];
4076
NSDictionary *b = [sender convertAttributes:ob];
4078
NSColor *ca = [a objectForKey:NSBackgroundColorAttributeName];
4079
NSColor *cb = [b objectForKey:NSBackgroundColorAttributeName];
4081
[style setBackgroundColor:[self _colorAsString:ca]];
4084
[self _addToStyle:style fontA:[a objectForKey:NSFontAttributeName] fontB:[b objectForKey:NSFontAttributeName]];
4086
ca = [a objectForKey:NSForegroundColorAttributeName];
4087
cb = [b objectForKey:NSForegroundColorAttributeName];
4089
[style setColor:[self _colorAsString:ca]];
4092
NSShadow *sha = [a objectForKey:NSShadowAttributeName];
4094
[style setTextShadow:[self _shadowAsString:sha]];
4095
else if ([b objectForKey:NSShadowAttributeName] == nil)
4096
[style setTextShadow:@"none"];
4098
int sa = [[a objectForKey:NSStrikethroughStyleAttributeName] intValue];
4099
int sb = [[b objectForKey:NSStrikethroughStyleAttributeName] intValue];
4101
if (sa == NSUnderlineStyleNone)
4102
[style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
4103
// we really mean "no line-through" rather than "none"
4105
[style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""];
4106
// we really mean "add line-through" rather than "line-through"
4109
sa = [[a objectForKey:NSSuperscriptAttributeName] intValue];
4110
sb = [[b objectForKey:NSSuperscriptAttributeName] intValue];
4113
[style setVerticalAlign:@"super"];
4115
[style setVerticalAlign:@"sub"];
4117
[style setVerticalAlign:@"baseline"];
4120
int ua = [[a objectForKey:NSUnderlineStyleAttributeName] intValue];
4121
int ub = [[b objectForKey:NSUnderlineStyleAttributeName] intValue];
4123
if (ua == NSUnderlineStyleNone)
4124
[style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
4125
// we really mean "no underline" rather than "none"
4127
[style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
4128
// we really mean "add underline" rather than "underline"
4134
- (void)changeAttributes:(id)sender
4138
[self _applyStyleToSelection:[self _styleForAttributeChange:sender] withUndoAction:EditActionChangeAttributes];
4141
- (DOMCSSStyleDeclaration *)_styleFromColorPanelWithSelector:(SEL)selector
4143
DOMCSSStyleDeclaration *style = [self _emptyStyle];
4145
ASSERT([style respondsToSelector:selector]);
4146
[style performSelector:selector withObject:[self _colorAsString:[[NSColorPanel sharedColorPanel] color]]];
4151
- (EditAction)_undoActionFromColorPanelWithSelector:(SEL)selector
4153
if (selector == @selector(setBackgroundColor:))
4154
return EditActionSetBackgroundColor;
4155
return EditActionSetColor;
4158
- (void)_changeCSSColorUsingSelector:(SEL)selector inRange:(DOMRange *)range
4160
DOMCSSStyleDeclaration *style = [self _styleFromColorPanelWithSelector:selector];
4161
WebView *webView = [self _webView];
4162
if ([[webView _editingDelegateForwarder] webView:webView shouldApplyStyle:style toElementsInDOMRange:range])
4163
core([self _frame])->editor()->applyStyle(core(style), [self _undoActionFromColorPanelWithSelector:selector]);
4166
- (void)changeDocumentBackgroundColor:(id)sender
4170
// Mimicking NSTextView, this method sets the background color for the
4171
// entire document. There is no NSTextView API for setting the background
4172
// color on the selected range only. Note that this method is currently
4173
// never called from the UI (see comment in changeColor:).
4174
// FIXME: this actually has no effect when called, probably due to 3654850. _documentRange seems
4175
// to do the right thing because it works in startSpeaking:, and I know setBackgroundColor: does the
4176
// right thing because I tested it with [self _selectedRange].
4177
// FIXME: This won't actually apply the style to the entire range here, because it ends up calling
4178
// [bridge applyStyle:], which operates on the current selection. To make this work right, we'll
4179
// need to save off the selection, temporarily set it to the entire range, make the change, then
4180
// restore the old selection.
4181
[self _changeCSSColorUsingSelector:@selector(setBackgroundColor:) inRange:[self _documentRange]];
4184
- (void)changeColor:(id)sender
4188
// FIXME: in NSTextView, this method calls changeDocumentBackgroundColor: when a
4189
// private call has earlier been made by [NSFontFontEffectsBox changeColor:], see 3674493.
4190
// AppKit will have to be revised to allow this to work with anything that isn't an
4191
// NSTextView. However, this might not be required for Tiger, since the background-color
4192
// changing box in the font panel doesn't work in Mail (3674481), though it does in TextEdit.
4193
[self _applyStyleToSelection:[self _styleFromColorPanelWithSelector:@selector(setColor:)]
4194
withUndoAction:EditActionSetColor];
4197
- (void)_alignSelectionUsingCSSValue:(NSString *)CSSAlignmentValue withUndoAction:(EditAction)undoAction
4199
if (![self _canEditRichly])
4202
DOMCSSStyleDeclaration *style = [self _emptyStyle];
4203
[style setTextAlign:CSSAlignmentValue];
4204
[self _applyStyleToSelection:style withUndoAction:undoAction];
4207
- (void)alignCenter:(id)sender
4211
[self _alignSelectionUsingCSSValue:@"center" withUndoAction:EditActionCenter];
4214
- (void)alignJustified:(id)sender
4218
[self _alignSelectionUsingCSSValue:@"justify" withUndoAction:EditActionJustify];
4221
- (void)alignLeft:(id)sender
4225
[self _alignSelectionUsingCSSValue:@"left" withUndoAction:EditActionAlignLeft];
4228
- (void)alignRight:(id)sender
4232
[self _alignSelectionUsingCSSValue:@"right" withUndoAction:EditActionAlignRight];
4235
- (void)insertParagraphSeparator:(id)sender
4239
if (Frame* coreFrame = core([self _frame]))
4240
coreFrame->editor()->execCommand("InsertNewline");
4243
- (void)_changeWordCaseWithSelector:(SEL)selector
4245
if (![self _canEdit])
4248
WebFrameBridge *bridge = [self _bridge];
4249
[self selectWord:nil];
4250
NSString *word = [[bridge selectedString] performSelector:selector];
4251
// FIXME: Does this need a different action context other than "typed"?
4252
if ([self _shouldReplaceSelectionWithText:word givenAction:WebViewInsertActionTyped])
4253
[bridge replaceSelectionWithText:word selectReplacement:NO smartReplace:NO];
4256
- (void)uppercaseWord:(id)sender
4260
[self _changeWordCaseWithSelector:@selector(uppercaseString)];
4263
- (void)lowercaseWord:(id)sender
4267
[self _changeWordCaseWithSelector:@selector(lowercaseString)];
4270
- (void)capitalizeWord:(id)sender
4274
[self _changeWordCaseWithSelector:@selector(capitalizedString)];
4277
- (void)deleteForward:(id)sender
4281
if (![self _isEditable])
4283
Frame* coreFrame = core([self _frame]);
4285
coreFrame->editor()->deleteWithDirection(SelectionController::FORWARD, CharacterGranularity, false, true);
4288
- (void)deleteBackward:(id)sender
4292
if (![self _isEditable])
4294
Frame* coreFrame = core([self _frame]);
4296
coreFrame->editor()->deleteWithDirection(SelectionController::BACKWARD, CharacterGranularity, false, true);
4299
- (void)deleteBackwardByDecomposingPreviousCharacter:(id)sender
4303
LOG_ERROR("unimplemented, doing deleteBackward instead");
4305
if (![self _isEditable])
4307
Frame* coreFrame = core([self _frame]);
4309
coreFrame->editor()->deleteWithDirection(SelectionController::BACKWARD, CharacterGranularity, false, true);
4312
- (void)deleteToBeginningOfLine:(id)sender
4316
Frame* coreFrame = core([self _frame]);
4318
coreFrame->editor()->deleteWithDirection(SelectionController::BACKWARD, LineBoundary, true, false);
4321
- (void)deleteToEndOfLine:(id)sender
4325
// To match NSTextView, this command should delete the newline at the end of
4326
// a paragraph if you are at the end of a paragraph (like deleteToEndOfParagraph does below).
4327
Frame* coreFrame = core([self _frame]);
4329
if (!coreFrame->editor()->deleteWithDirection(SelectionController::FORWARD, LineBoundary, true, false))
4330
coreFrame->editor()->deleteWithDirection(SelectionController::FORWARD, CharacterGranularity, true, false);
4335
- (void)deleteToBeginningOfParagraph:(id)sender
4339
Frame* coreFrame = core([self _frame]);
4341
coreFrame->editor()->deleteWithDirection(SelectionController::BACKWARD, ParagraphBoundary, true, false);
4344
- (void)deleteToEndOfParagraph:(id)sender
4348
// Despite the name of the method, this should delete the newline if the caret is at the end of a paragraph.
4349
// If deletion to the end of the paragraph fails, we delete one character forward, which will delete the newline.
4350
Frame* coreFrame = core([self _frame]);
4352
if (!coreFrame->editor()->deleteWithDirection(SelectionController::FORWARD, ParagraphBoundary, true, false))
4353
coreFrame->editor()->deleteWithDirection(SelectionController::FORWARD, CharacterGranularity, true, false);
4357
- (void)complete:(id)sender
4361
if (![self _canEdit])
4363
if (!_private->compController)
4364
_private->compController = [[WebTextCompleteController alloc] initWithHTMLView:self];
4365
[_private->compController doCompletion];
4368
- (void)checkSpelling:(id)sender
4372
NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
4374
LOG_ERROR("No NSSpellChecker");
4378
core([self _frame])->editor()->advanceToNextMisspelling();
4381
- (void)showGuessPanel:(id)sender
4385
NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
4387
LOG_ERROR("No NSSpellChecker");
4391
NSPanel *spellingPanel = [checker spellingPanel];
4392
#ifndef BUILDING_ON_TIGER
4393
// Post-Tiger, this menu item is a show/hide toggle, to match AppKit. Leave Tiger behavior alone
4394
// to match rest of OS X.
4395
if ([spellingPanel isVisible]) {
4396
[spellingPanel orderOut:sender];
4401
core([self _frame])->editor()->advanceToNextMisspelling(true);
4402
[spellingPanel orderFront:sender];
4405
- (void)_changeSpellingToWord:(NSString *)newWord
4407
if (![self _canEdit])
4410
// Don't correct to empty string. (AppKit checked this, we might as well too.)
4411
if (![NSSpellChecker sharedSpellChecker]) {
4412
LOG_ERROR("No NSSpellChecker");
4416
if ([newWord isEqualToString:@""])
4419
if ([self _shouldReplaceSelectionWithText:newWord givenAction:WebViewInsertActionPasted])
4420
[[self _bridge] replaceSelectionWithText:newWord selectReplacement:YES smartReplace:NO];
4423
- (void)changeSpelling:(id)sender
4427
[self _changeSpellingToWord:[[sender selectedCell] stringValue]];
4430
- (void)ignoreSpelling:(id)sender
4434
NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
4436
LOG_ERROR("No NSSpellChecker");
4440
NSString *stringToIgnore = [sender stringValue];
4441
unsigned int length = [stringToIgnore length];
4442
if (stringToIgnore && length > 0) {
4443
[checker ignoreWord:stringToIgnore inSpellDocumentWithTag:[[self _webView] spellCheckerDocumentTag]];
4444
// FIXME: Need to clear misspelling marker if the currently selected word is the one we are to ignore?
4448
- (void)performFindPanelAction:(id)sender
4452
// Implementing this will probably require copying all of NSFindPanel.h and .m.
4453
// We need *almost* the same thing as AppKit, but not quite.
4454
LOG_ERROR("unimplemented");
4457
- (void)startSpeaking:(id)sender
4461
WebFrameBridge *bridge = [self _bridge];
4462
DOMRange *range = [self _selectedRange];
4463
if (!range || [range collapsed])
4464
range = [self _documentRange];
4465
[NSApp speakString:[bridge stringForRange:range]];
4468
- (void)stopSpeaking:(id)sender
4472
[NSApp stopSpeaking:sender];
4475
- (void)insertNewlineIgnoringFieldEditor:(id)sender
4479
if (Frame* coreFrame = core([self _frame]))
4480
coreFrame->editor()->execCommand("InsertNewline");
4483
- (void)insertTabIgnoringFieldEditor:(id)sender
4487
if (Frame* coreFrame = core([self _frame]))
4488
coreFrame->editor()->execCommand("InsertTab");
4491
- (void)subscript:(id)sender
4495
DOMCSSStyleDeclaration *style = [self _emptyStyle];
4496
[style setVerticalAlign:@"sub"];
4497
[self _applyStyleToSelection:style withUndoAction:EditActionSubscript];
4500
- (void)superscript:(id)sender
4504
DOMCSSStyleDeclaration *style = [self _emptyStyle];
4505
[style setVerticalAlign:@"super"];
4506
[self _applyStyleToSelection:style withUndoAction:EditActionSuperscript];
4509
- (void)unscript:(id)sender
4513
DOMCSSStyleDeclaration *style = [self _emptyStyle];
4514
[style setVerticalAlign:@"baseline"];
4515
[self _applyStyleToSelection:style withUndoAction:EditActionUnscript];
4518
- (void)underline:(id)sender
4522
Frame* coreFrame = core([self _frame]);
4525
// Despite the name, this method is actually supposed to toggle underline.
4526
// FIXME: This currently clears overline, line-through, and blink as an unwanted side effect.
4527
DOMCSSStyleDeclaration *style = [self _emptyStyle];
4528
[style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
4529
if (coreFrame->editor()->selectionStartHasStyle(core(style)))
4530
[style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
4531
[self _applyStyleToSelection:style withUndoAction:EditActionUnderline];
4534
- (void)yank:(id)sender
4538
if (![self _canEdit])
4541
NSString* yankee = _NSYankFromKillRing();
4543
if (Frame* coreFrame = core([self _frame]))
4544
coreFrame->editor()->insertTextWithoutSendingTextEvent(yankee, false);
4546
_NSSetKillRingToYankedState();
4549
- (void)yankAndSelect:(id)sender
4553
if (![self _canEdit])
4556
NSString* yankee = _NSYankFromKillRing();
4558
if (Frame* coreFrame = core([self _frame]))
4559
coreFrame->editor()->insertTextWithoutSendingTextEvent(yankee, true);
4561
_NSSetKillRingToYankedState();
4564
- (void)setMark:(id)sender
4568
[[self _bridge] setMarkDOMRange:[self _selectedRange]];
4571
static DOMRange *unionDOMRanges(DOMRange *a, DOMRange *b)
4575
DOMRange *s = [a compareBoundaryPoints:DOM_START_TO_START sourceRange:b] <= 0 ? a : b;
4576
DOMRange *e = [a compareBoundaryPoints:DOM_END_TO_END sourceRange:b] <= 0 ? b : a;
4577
DOMRange *r = [[[a startContainer] ownerDocument] createRange];
4578
[r setStart:[s startContainer] offset:[s startOffset]];
4579
[r setEnd:[e endContainer] offset:[e endOffset]];
4583
- (void)deleteToMark:(id)sender
4587
if (![self _canEdit])
4590
DOMRange *mark = [[self _bridge] markDOMRange];
4592
if (Frame* coreFrame = core([self _frame]))
4593
coreFrame->editor()->performDelete();
4595
DOMRange *selection = [self _selectedRange];
4598
r = unionDOMRanges(mark, selection);
4602
Frame* coreFrame = core([self _frame]);
4604
coreFrame->editor()->deleteRange([r _range], true, true, false, deleteSelectionAction, CharacterGranularity);
4607
[[self _bridge] setMarkDOMRange:[self _selectedRange]];
4610
- (void)selectToMark:(id)sender
4614
WebFrameBridge *bridge = [self _bridge];
4615
DOMRange *mark = [bridge markDOMRange];
4616
DOMRange *selection = [self _selectedRange];
4617
Frame* coreFrame = core([self _frame]);
4618
if (!mark || !selection || !coreFrame) {
4622
ExceptionCode ec = 0;
4623
coreFrame->selectionController()->setSelectedRange(core(unionDOMRanges(mark, [self _selectedRange])), DOWNSTREAM, true, ec);
4626
- (void)swapWithMark:(id)sender
4630
WebFrameBridge *bridge = [self _bridge];
4631
DOMRange *mark = [bridge markDOMRange];
4632
DOMRange *selection = [self _selectedRange];
4633
Frame* coreFrame = core([self _frame]);
4634
if (!mark || !selection || !coreFrame) {
4639
ExceptionCode ec = 0;
4640
coreFrame->selectionController()->setSelectedRange(core(mark), DOWNSTREAM, true, ec);
4642
[bridge setMarkDOMRange:selection];
4645
- (void)transpose:(id)sender
4649
if (![self _canEdit])
4652
WebFrameBridge *bridge = [self _bridge];
4653
DOMRange *r = [bridge rangeOfCharactersAroundCaret];
4656
NSString *characters = [bridge stringForRange:r];
4657
if ([characters length] != 2)
4659
NSString *transposed = [[characters substringFromIndex:1] stringByAppendingString:[characters substringToIndex:1]];
4660
WebView *webView = [self _webView];
4661
if (![[webView _editingDelegateForwarder] webView:webView shouldChangeSelectedDOMRange:[self _selectedRange]
4662
toDOMRange:r affinity:NSSelectionAffinityDownstream stillSelecting:NO])
4665
Frame* coreFrame = core([self _frame]);
4669
ExceptionCode ec = 0;
4670
coreFrame->selectionController()->setSelectedRange(core(r), DOWNSTREAM, true, ec);
4671
if ([self _shouldReplaceSelectionWithText:transposed givenAction:WebViewInsertActionTyped])
4672
[bridge replaceSelectionWithText:transposed selectReplacement:NO smartReplace:NO];
4675
- (void)toggleBaseWritingDirection:(id)sender
4679
if (![self _canEdit])
4682
NSString *direction = @"RTL";
4683
switch ([[self _bridge] baseWritingDirectionForSelectionStart]) {
4684
case NSWritingDirectionLeftToRight:
4686
case NSWritingDirectionRightToLeft:
4689
// The writingDirectionForSelectionStart method will never return "natural". It
4690
// will always return a concrete direction. So, keep the compiler happy, and assert not reached.
4691
case NSWritingDirectionNatural:
4692
ASSERT_NOT_REACHED();
4696
DOMCSSStyleDeclaration *style = [self _emptyStyle];
4697
[style setDirection:direction];
4698
[self _applyParagraphStyleToSelection:style withUndoAction:EditActionSetWritingDirection];
4701
- (void)changeBaseWritingDirection:(id)sender
4705
if (![self _canEdit])
4708
NSWritingDirection writingDirection = static_cast<NSWritingDirection>([sender tag]);
4710
// We disable the menu item that performs this action because we can't implement
4711
// NSWritingDirectionNatural's behavior using CSS.
4712
ASSERT(writingDirection != NSWritingDirectionNatural);
4714
DOMCSSStyleDeclaration *style = [self _emptyStyle];
4715
[style setDirection:writingDirection == NSWritingDirectionLeftToRight ? @"LTR" : @"RTL"];
4716
[self _applyParagraphStyleToSelection:style withUndoAction:EditActionSetWritingDirection];
4719
- (void)indent:(id)sender
4723
core([self _frame])->editor()->indent();
4726
- (void)outdent:(id)sender
4730
core([self _frame])->editor()->outdent();
4735
// CSS does not have a way to specify an outline font, which may make this difficult to implement.
4736
// Maybe a special case of text-shadow?
4737
- (void)outline:(id)sender;
4739
// This is part of table support, which may be in NSTextView for Tiger.
4740
// It's probably simple to do the equivalent thing for WebKit.
4741
- (void)insertTable:(id)sender;
4743
// This could be important.
4744
- (void)toggleTraditionalCharacterShape:(id)sender;
4746
// I'm not sure what the equivalents of these in the web world are.
4747
- (void)insertLineSeparator:(id)sender;
4748
- (void)insertPageBreak:(id)sender;
4750
// These methods are not implemented in NSTextView yet at the time of this writing.
4751
- (void)changeCaseOfLetter:(id)sender;
4752
- (void)transposeWords:(id)sender;
4756
// Super-hack alert.
4757
// Workaround for bug 3789278.
4759
// Returns a selector only if called while:
4760
// 1) first responder is self
4761
// 2) handling a key down event
4762
// 3) not yet inside keyDown: method
4763
// 4) key is an arrow key
4764
// The selector is the one that gets sent by -[NSWindow _processKeyboardUIKey] for this key.
4765
- (SEL)_arrowKeyDownEventSelectorIfPreprocessing
4767
NSWindow *w = [self window];
4768
if ([w firstResponder] != self)
4770
NSEvent *e = [w currentEvent];
4771
if ([e type] != NSKeyDown)
4773
if (e == _private->keyDownEvent)
4775
NSString *s = [e charactersIgnoringModifiers];
4776
if ([s length] == 0)
4778
switch ([s characterAtIndex:0]) {
4779
case NSDownArrowFunctionKey:
4780
return @selector(moveDown:);
4781
case NSLeftArrowFunctionKey:
4782
return @selector(moveLeft:);
4783
case NSRightArrowFunctionKey:
4784
return @selector(moveRight:);
4785
case NSUpArrowFunctionKey:
4786
return @selector(moveUp:);
4792
// Returns NO instead of YES if called on the selector that the
4793
// _arrowKeyDownEventSelectorIfPreprocessing method returns.
4794
// This should only happen inside -[NSWindow _processKeyboardUIKey],
4795
// and together with the change below should cause that method
4796
// to return NO rather than handling the key.
4797
// Also set a 1-shot flag for the nextResponder check below.
4798
- (BOOL)respondsToSelector:(SEL)selector
4800
if (![super respondsToSelector:selector])
4802
SEL arrowKeySelector = [self _arrowKeyDownEventSelectorIfPreprocessing];
4803
if (selector != arrowKeySelector)
4805
_private->nextResponderDisabledOnce = YES;
4809
// Returns nil instead of the next responder if called when the
4810
// one-shot flag is set, and _arrowKeyDownEventSelectorIfPreprocessing
4811
// returns something other than NULL. This should only happen inside
4812
// -[NSWindow _processKeyboardUIKey] and together with the change above
4813
// should cause that method to return NO rather than handling the key.
4814
- (NSResponder *)nextResponder
4816
BOOL disabled = _private->nextResponderDisabledOnce;
4817
_private->nextResponderDisabledOnce = NO;
4818
if (disabled && [self _arrowKeyDownEventSelectorIfPreprocessing] != NULL)
4820
return [super nextResponder];
4823
// Despite its name, this is called at different times than windowDidBecomeKey is.
4824
// It takes into account all the other factors that determine when NSCell draws
4825
// with different tints, so it's the right call to use for control tints. We'd prefer
4826
// to do this with API. <rdar://problem/5136760>
4827
- (void)_windowChangedKeyState
4829
if (Frame* frame = core([self _frame]))
4830
if (FrameView* view = frame->view())
4831
view->updateControlTints();
4832
[super _windowChangedKeyState];
4837
@implementation WebHTMLView (WebTextSizing)
4839
- (IBAction)_makeTextSmaller:(id)sender
4841
[self _updateTextSizeMultiplier];
4844
- (IBAction)_makeTextLarger:(id)sender
4846
[self _updateTextSizeMultiplier];
4849
- (IBAction)_makeTextStandardSize:(id)sender
4851
[self _updateTextSizeMultiplier];
4854
- (BOOL)_tracksCommonSizeFactor
4859
- (void)_textSizeMultiplierChanged
4861
[self _updateTextSizeMultiplier];
4864
// never sent because we track the common size factor
4865
- (BOOL)_canMakeTextSmaller
4867
ASSERT_NOT_REACHED();
4871
- (BOOL)_canMakeTextLarger
4873
ASSERT_NOT_REACHED();
4877
- (BOOL)_canMakeTextStandardSize
4879
ASSERT_NOT_REACHED();
4885
@implementation NSArray (WebHTMLView)
4887
- (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object
4890
NSEnumerator *enumerator = [self objectEnumerator];
4891
WebNetscapePluginEmbeddedView *view;
4892
while ((view = [enumerator nextObject]) != nil)
4893
if ([view isKindOfClass:[WebNetscapePluginEmbeddedView class]])
4894
[view performSelector:selector withObject:object];
4900
@implementation WebHTMLView (WebInternal)
4902
- (void)_selectionChanged
4904
[self _updateSelectionForInputManager];
4905
[self _updateFontPanel];
4906
if (core([self _frame]))
4907
core([self _frame])->editor()->setStartNewKillRingSequence(true);
4910
- (void)_updateFontPanel
4912
// FIXME: NSTextView bails out if becoming or resigning first responder, for which it has ivar flags. Not
4913
// sure if we need to do something similar.
4915
if (![self _canEdit])
4918
NSWindow *window = [self window];
4919
// FIXME: is this first-responder check correct? What happens if a subframe is editable and is first responder?
4920
if ([NSApp keyWindow] != window || [window firstResponder] != self)
4924
NSFont *font = [[self _bridge] fontForSelection:&multiple];
4926
// FIXME: for now, return a bogus font that distinguishes the empty selection from the non-empty
4927
// selection. We should be able to remove this once the rest of this code works properly.
4929
font = [self _hasSelection] ? [NSFont menuFontOfSize:23] : [NSFont toolTipsFontOfSize:17];
4930
ASSERT(font != nil);
4932
NSFontManager *fm = [NSFontManager sharedFontManager];
4933
[fm setSelectedFont:font isMultiple:multiple];
4935
// FIXME: we don't keep track of selected attributes, or set them on the font panel. This
4936
// appears to have no effect on the UI. E.g., underlined text in Mail or TextEdit is
4937
// not reflected in the font panel. Maybe someday this will change.
4940
- (BOOL)_canSmartCopyOrDelete
4942
return [[self _webView] smartInsertDeleteEnabled] && [[self _bridge] selectionGranularity] == WordGranularity;
4945
- (DOMRange *)_smartDeleteRangeForProposedRange:(DOMRange *)proposedRange
4947
if (proposedRange == nil || [self _canSmartCopyOrDelete] == NO)
4950
return [[self _bridge] smartDeleteRangeForProposedRange:proposedRange];
4953
- (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
4955
if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) {
4957
*beforeString = nil;
4963
[[self _bridge] smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString];
4966
- (BOOL)_textViewWasFirstResponderAtMouseDownTime:(NSTextView *)textView
4968
return textView == _private->firstResponderTextViewAtMouseDownTime;
4972
- (NSEvent *)_mouseDownEvent
4974
return _private->mouseDownEvent;
4978
- (void)_pauseNullEventsForAllNetscapePlugins
4980
NSArray *subviews = [self subviews];
4981
unsigned int subviewCount = [subviews count];
4982
unsigned int subviewIndex;
4984
for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
4985
NSView *subview = [subviews objectAtIndex:subviewIndex];
4986
if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
4987
[(WebBaseNetscapePluginView *)subview stopNullEvents];
4993
- (void)_resumeNullEventsForAllNetscapePlugins
4995
NSArray *subviews = [self subviews];
4996
unsigned int subviewCount = [subviews count];
4997
unsigned int subviewIndex;
4999
for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
5000
NSView *subview = [subviews objectAtIndex:subviewIndex];
5001
if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
5002
[(WebBaseNetscapePluginView *)subview restartNullEvents];
5007
- (void)_willMakeFirstResponderForNodeFocus
5009
_private->willBecomeFirstResponderForNodeFocus = YES;
5012
- (id<WebHTMLHighlighter>)_highlighterForType:(NSString*)type
5014
return [_private->highlighters objectForKey:type];
5017
- (WebFrame *)_frame
5019
return [_private->dataSource webFrame];
5022
- (void)copy:(id)sender
5026
if (Frame* coreFrame = core([self _frame]))
5027
coreFrame->editor()->copy();
5030
- (void)cut:(id)sender
5034
if (Frame* coreFrame = core([self _frame]))
5035
coreFrame->editor()->cut();
5038
- (void)paste:(id)sender
5042
Frame* coreFrame = core([self _frame]);
5043
if (coreFrame && coreFrame->editor()->tryDHTMLPaste())
5044
return; // DHTML did the whole operation
5045
if (!coreFrame->editor()->canPaste())
5047
if (coreFrame && coreFrame->selectionController()->isContentRichlyEditable())
5048
[self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:YES];
5050
coreFrame->editor()->pasteAsPlainText();
5053
- (void)pasteAsPlainText:(id)sender
5057
if (![self _canEdit])
5059
[self _pasteAsPlainTextWithPasteboard:[NSPasteboard generalPasteboard]];
5062
- (void)closeIfNotCurrentView
5064
if ([[[self _frame] frameView] documentView] != self)
5068
- (DOMDocumentFragment*)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
5071
return [self _documentFragmentFromPasteboard:pasteboard inContext:nil allowPlainText:NO chosePlainText:&discard];
5074
#ifndef BUILDING_ON_TIGER
5076
- (BOOL)isGrammarCheckingEnabled
5078
// FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because
5079
// the AppKit code checks the first responder.
5080
return [[self _webView] isGrammarCheckingEnabled];
5083
- (void)setGrammarCheckingEnabled:(BOOL)flag
5085
// FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because
5086
// the AppKit code checks the first responder.
5087
[[self _webView] setGrammarCheckingEnabled:flag];
5090
- (void)toggleGrammarChecking:(id)sender
5092
// FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because
5093
// the AppKit code checks the first responder.
5094
[[self _webView] toggleGrammarChecking:sender];
5098
static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
5100
NSArray *screens = [NSScreen screens];
5102
if ([screens count] == 0) {
5103
// You could theoretically get here if running with no monitor, in which case it doesn't matter
5104
// much where the "on-screen" point is.
5105
return CGPointMake(point.x, point.y);
5108
// Flip the y coordinate from the top of the menu bar screen -- see 4636390
5109
return CGPointMake(point.x, NSMaxY([[screens objectAtIndex:0] frame]) - point.y);
5114
- (void)_lookUpInDictionaryFromMenu:(id)sender
5116
// Dictionary API will accept a whitespace-only string and display UI as if it were real text,
5117
// so bail out early to avoid that.
5118
if ([[[self selectedString] _webkit_stringByTrimmingWhitespace] length] == 0)
5121
// We soft link to get the function that displays the dictionary (either pop-up window or app) to avoid the performance
5122
// penalty of linking to another framework. This function changed signature as well as framework between Tiger and Leopard,
5123
// so the two cases are handled separately.
5125
#ifdef BUILDING_ON_TIGER
5126
typedef OSStatus (*ServiceWindowShowFunction)(id inWordString, NSRect inWordBoundary, UInt16 inLineDirection);
5127
const char *frameworkPath = "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/LangAnalysis.framework/LangAnalysis";
5128
const char *functionName = "DCMDictionaryServiceWindowShow";
5130
typedef void (*ServiceWindowShowFunction)(id unusedDictionaryRef, id inWordString, CFRange selectionRange, id unusedFont, CGPoint textOrigin, Boolean verticalText, id unusedTransform);
5131
const char *frameworkPath = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox";
5132
const char *functionName = "HIDictionaryWindowShow";
5135
static bool lookedForFunction = false;
5136
static ServiceWindowShowFunction dictionaryServiceWindowShow = NULL;
5138
if (!lookedForFunction) {
5139
void* langAnalysisFramework = dlopen(frameworkPath, RTLD_LAZY);
5140
ASSERT(langAnalysisFramework);
5141
if (langAnalysisFramework)
5142
dictionaryServiceWindowShow = (ServiceWindowShowFunction)dlsym(langAnalysisFramework, functionName);
5143
lookedForFunction = true;
5146
ASSERT(dictionaryServiceWindowShow);
5147
if (!dictionaryServiceWindowShow) {
5148
NSLog(@"Couldn't find the %s function in %s", functionName, frameworkPath);
5152
NSAttributedString *attrString = [self selectedAttributedString];
5154
#ifdef BUILDING_ON_TIGER
5155
// FIXME: must check for right-to-left here
5156
NSWritingDirection writingDirection = NSWritingDirectionLeftToRight;
5158
// FIXME: the dictionary API expects the rect for the first line of selection. Passing
5159
// the rect for the entire selection, as we do here, positions the pop-up window near
5160
// the bottom of the selection rather than at the selected word.
5161
NSRect rect = [self convertRect:core([self _frame])->selectionRect() toView:nil];
5162
rect.origin = [[self window] convertBaseToScreen:rect.origin];
5163
NSData *data = [attrString RTFFromRange:NSMakeRange(0, [attrString length]) documentAttributes:nil];
5164
dictionaryServiceWindowShow(data, rect, (writingDirection == NSWritingDirectionRightToLeft) ? 1 : 0);
5166
// The HIDictionaryWindowShow function requires the origin, in CG screen coordinates, of the first character of text in the selection.
5167
// FIXME 4945808: We approximate this in a way that works well when a single word is selected, and less well in some other cases
5168
// (but no worse than we did in Tiger)
5169
NSRect rect = core([self _frame])->selectionRect();
5171
NSDictionary *attributes = [attrString fontAttributesInRange:NSMakeRange(0,1)];
5172
NSFont *font = [attributes objectForKey:NSFontAttributeName];
5174
rect.origin.y += [font ascender];
5176
NSPoint windowPoint = [self convertPoint:rect.origin toView:nil];
5177
NSPoint screenPoint = [[self window] convertBaseToScreen:windowPoint];
5179
dictionaryServiceWindowShow(nil, attrString, CFRangeMake(0, [attrString length]), nil,
5180
coreGraphicsScreenPointForAppKitScreenPoint(screenPoint), false, nil);
5184
- (void)_hoverFeedbackSuspendedChanged
5186
[self _updateMouseoverWithFakeEvent];
5189
- (BOOL)_interceptEditingKeyEvent:(KeyboardEvent*)event shouldSaveCommand:(BOOL)shouldSave
5191
// Ask AppKit to process the key event -- it will call back with either insertText or doCommandBySelector.
5192
WebHTMLViewInterpretKeyEventsParameters parameters;
5193
parameters.eventWasHandled = false;
5194
parameters.shouldSaveCommand = shouldSave;
5195
// If we're intercepting the initial IM call we assume that the IM has consumed the event,
5196
// and only change this assumption if one of the NSTextInput/Responder callbacks is used.
5197
// We assume the IM will *not* consume hotkey sequences
5198
parameters.consumedByIM = !event->metaKey() && shouldSave;
5200
if (const PlatformKeyboardEvent* platformEvent = event->keyEvent()) {
5201
NSEvent *macEvent = platformEvent->macEvent();
5202
if ([macEvent type] == NSKeyDown && [_private->compController filterKeyDown:macEvent])
5204
parameters.event = event;
5205
_private->interpretKeyEventsParameters = ¶meters;
5206
_private->receivedNOOP = NO;
5207
KeypressCommand command = event->keypressCommand();
5208
bool hasKeypressCommand = !command.commandNames.isEmpty() || !command.text.isEmpty();
5210
if (parameters.shouldSaveCommand || !hasKeypressCommand)
5211
[self interpretKeyEvents:[NSArray arrayWithObject:macEvent]];
5213
if (!command.text.isEmpty())
5214
[self insertText:command.text];
5216
size_t size = command.commandNames.size();
5217
for (size_t i = 0; i < size; ++i)
5218
[self doCommandBySelector:NSSelectorFromString(command.commandNames[i])];
5221
_private->interpretKeyEventsParameters = 0;
5223
return (!_private->receivedNOOP && parameters.eventWasHandled) || parameters.consumedByIM;
5226
- (WebCore::CachedImage*)promisedDragTIFFDataSource
5228
return _private->promisedDragTIFFDataSource;
5231
- (void)setPromisedDragTIFFDataSource:(WebCore::CachedImage*)source
5234
source->ref(promisedDataClient());
5236
if (_private->promisedDragTIFFDataSource)
5237
_private->promisedDragTIFFDataSource->deref(promisedDataClient());
5238
_private->promisedDragTIFFDataSource = source;
5241
#undef COMMAND_PROLOGUE
5245
@implementation WebHTMLView (WebNSTextInputSupport)
5247
- (NSArray *)validAttributesForMarkedText
5249
static NSArray *validAttributes;
5250
if (!validAttributes) {
5251
validAttributes = [[NSArray alloc] initWithObjects:
5252
NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName,
5253
NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil];
5254
// NSText also supports the following attributes, but it's
5255
// hard to tell which are really required for text input to
5256
// work well; I have not seen any input method make use of them yet.
5257
// NSFontAttributeName, NSForegroundColorAttributeName,
5258
// NSBackgroundColorAttributeName, NSLanguageAttributeName.
5259
CFRetain(validAttributes);
5261
LOG(TextInput, "validAttributesForMarkedText -> (...)");
5262
return validAttributes;
5265
// Utility function to make sure we don't return anything through the NSTextInput
5266
// API when an editable region is not currently focused.
5267
static BOOL isTextInput(Frame* coreFrame)
5269
return coreFrame && !coreFrame->selectionController()->isNone() && coreFrame->selectionController()->isContentEditable();
5272
- (NSAttributedString *)textStorage
5274
if (!isTextInput(core([self _frame]))) {
5275
LOG(TextInput, "textStorage -> nil");
5278
NSAttributedString *result = [self attributedSubstringFromRange:NSMakeRange(0, UINT_MAX)];
5280
LOG(TextInput, "textStorage -> \"%s\"", result ? [[result string] UTF8String] : "");
5282
// We have to return an empty string rather than null to prevent TSM from calling -string
5283
return result ? result : [[[NSAttributedString alloc] initWithString:@""] autorelease];
5286
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
5288
NSWindow *window = [self window];
5289
WebFrameBridge *bridge = [self _bridge];
5292
thePoint = [window convertScreenToBase:thePoint];
5293
thePoint = [self convertPoint:thePoint fromView:nil];
5295
DOMRange *range = [bridge characterRangeAtPoint:thePoint];
5297
LOG(TextInput, "characterIndexForPoint:(%f, %f) -> NSNotFound", thePoint.x, thePoint.y);
5301
unsigned result = [bridge convertDOMRangeToNSRange:range].location;
5302
LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result);
5306
- (NSRect)firstRectForCharacterRange:(NSRange)theRange
5308
WebFrameBridge *bridge = [self _bridge];
5310
// Just to match NSTextView's behavior. Regression tests cannot detect this;
5311
// to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
5312
// (type something; try ranges (1, -1) and (2, -1).
5313
if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
5314
theRange.length = 0;
5316
DOMRange *range = [bridge convertNSRangeToDOMRange:theRange];
5318
LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (0, 0, 0, 0)", theRange.location, theRange.length);
5319
return NSMakeRect(0, 0, 0, 0);
5322
ASSERT([range startContainer]);
5323
ASSERT([range endContainer]);
5325
NSRect resultRect = [bridge firstRectForDOMRange:range];
5326
resultRect = [self convertRect:resultRect toView:nil];
5328
NSWindow *window = [self window];
5330
resultRect.origin = [window convertBaseToScreen:resultRect.origin];
5332
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);
5336
- (NSRange)selectedRange
5338
if (!isTextInput(core([self _frame]))) {
5339
LOG(TextInput, "selectedRange -> (NSNotFound, 0)");
5340
return NSMakeRange(NSNotFound, 0);
5342
NSRange result = [[self _bridge] selectedNSRange];
5344
LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length);
5348
- (NSRange)markedRange
5350
if (![self hasMarkedText]) {
5351
LOG(TextInput, "markedRange -> (NSNotFound, 0)");
5352
return NSMakeRange(NSNotFound, 0);
5354
NSRange result = [[self _bridge] markedTextNSRange];
5356
LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length);
5360
- (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange
5362
if (!isTextInput(core([self _frame]))) {
5363
LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
5366
WebFrameBridge *bridge = [self _bridge];
5367
DOMRange *domRange = [bridge convertNSRangeToDOMRange:nsRange];
5369
LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
5373
NSAttributedString *result = [NSAttributedString _web_attributedStringFromRange:core(domRange)];
5375
// [NSAttributedString(WebKitExtras) _web_attributedStringFromRange:] insists on inserting a trailing
5376
// whitespace at the end of the string which breaks the ATOK input method. <rdar://problem/5400551>
5377
// To work around this we truncate the resultant string to the correct length.
5378
if ([result length] > nsRange.length) {
5379
ASSERT([result length] == nsRange.length + 1);
5380
ASSERT([[result string] characterAtIndex:nsRange.length] == '\n' || [[result string] characterAtIndex:nsRange.length] == ' ');
5381
result = [result attributedSubstringFromRange:NSMakeRange(0, nsRange.length)];
5383
LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%s\"", nsRange.location, nsRange.length, [[result string] UTF8String]);
5387
// test for 10.4 because of <rdar://problem/4243463>
5388
#ifdef BUILDING_ON_TIGER
5389
- (long)conversationIdentifier
5394
- (NSInteger)conversationIdentifier
5396
return (NSInteger)self;
5400
- (BOOL)hasMarkedText
5402
BOOL result = [[self _bridge] markedTextDOMRange] != nil;
5404
LOG(TextInput, "hasMarkedText -> %u", result);
5410
LOG(TextInput, "unmarkText");
5412
// Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
5413
WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5414
_private->interpretKeyEventsParameters = 0;
5417
parameters->eventWasHandled = YES;
5418
parameters->consumedByIM = NO;
5421
if (Frame* coreFrame = core([self _frame]))
5422
coreFrame->editor()->unmarkText();
5425
- (void)_extractAttributes:(NSArray **)a ranges:(NSArray **)r fromAttributedString:(NSAttributedString *)string
5427
int length = [[string string] length];
5429
NSMutableArray *attributes = [NSMutableArray array];
5430
NSMutableArray *ranges = [NSMutableArray array];
5431
while (i < length) {
5432
NSRange effectiveRange;
5433
NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&effectiveRange inRange:NSMakeRange(i,length - i)];
5434
[attributes addObject:attrs];
5435
[ranges addObject:[NSValue valueWithRange:effectiveRange]];
5436
i = effectiveRange.location + effectiveRange.length;
5442
- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
5444
BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString
5446
LOG(TextInput, "setMarkedText:\"%s\" selectedRange:(%u, %u)", isAttributedString ? [[string string] UTF8String] : [string UTF8String], newSelRange.location, newSelRange.length);
5448
// Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
5449
WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5450
_private->interpretKeyEventsParameters = 0;
5453
parameters->eventWasHandled = YES;
5454
parameters->consumedByIM = NO;
5457
Frame* coreFrame = core([self _frame]);
5461
WebFrameBridge *bridge = [self _bridge];
5463
if (![self _isEditable])
5466
if (isAttributedString) {
5467
unsigned markedTextLength = [(NSString *)string length];
5468
NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:NULL inRange:NSMakeRange(0, markedTextLength)];
5469
LOG(TextInput, " ReplacementRange: %s", [rangeString UTF8String]);
5470
// The AppKit adds a 'secret' property to the string that contains the replacement
5471
// range. The replacement range is the range of the the text that should be replaced
5472
// with the new string.
5474
[[self _bridge] selectNSRange:NSRangeFromString(rangeString)];
5477
coreFrame->editor()->setIgnoreMarkedTextSelectionChange(true);
5479
// if we had marked text already, we need to make sure to replace
5480
// that, instead of the selection/caret
5481
coreFrame->editor()->selectMarkedText();
5483
NSString *text = string;
5484
NSArray *attributes = nil;
5485
NSArray *ranges = nil;
5486
if (isAttributedString) {
5487
text = [string string];
5488
[self _extractAttributes:&attributes ranges:&ranges fromAttributedString:string];
5491
coreFrame->editor()->replaceMarkedText(text);
5492
[bridge setMarkedTextDOMRange:[self _selectedRange] customAttributes:attributes ranges:ranges];
5493
if ([self hasMarkedText])
5494
coreFrame->selectRangeInMarkedText(newSelRange.location, newSelRange.length);
5496
coreFrame->editor()->setIgnoreMarkedTextSelectionChange(false);
5499
- (void)doCommandBySelector:(SEL)selector
5501
LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
5503
// Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
5504
// The same call to interpretKeyEvents can do more than one command.
5505
WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5507
parameters->consumedByIM = NO;
5509
if (selector == @selector(noop:)) {
5510
_private->receivedNOOP = YES;
5514
KeyboardEvent* event = parameters ? parameters->event : 0;
5515
bool shouldSaveCommand = parameters && parameters->shouldSaveCommand;
5517
if (event && shouldSaveCommand) {
5518
KeypressCommand command = event->keypressCommand();
5519
command.commandNames.append(NSStringFromSelector(selector));
5520
event->setKeypressCommand(command);
5522
// Make sure that only direct calls to doCommandBySelector: see the parameters by setting to 0.
5523
_private->interpretKeyEventsParameters = 0;
5525
bool eventWasHandled = true;
5527
WebView *webView = [self _webView];
5528
Frame* coreFrame = core([self _frame]);
5529
if (![[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector] && coreFrame) {
5530
if (selector == @selector(insertNewline:) || selector == @selector(insertParagraphSeparator:) || selector == @selector(insertNewlineIgnoringFieldEditor:))
5531
eventWasHandled = coreFrame->editor()->execCommand("InsertNewline", event);
5532
else if (selector == @selector(insertLineBreak:))
5533
eventWasHandled = coreFrame->editor()->execCommand("InsertLineBreak", event);
5534
else if (selector == @selector(insertTab:) || selector == @selector(insertTabIgnoringFieldEditor:))
5535
eventWasHandled = coreFrame->editor()->execCommand("InsertTab", event);
5536
else if (selector == @selector(insertBacktab:))
5537
eventWasHandled = coreFrame->editor()->execCommand("InsertBacktab", event);
5539
_private->selectorForDoCommandBySelector = selector;
5540
[super doCommandBySelector:selector];
5541
_private->selectorForDoCommandBySelector = 0;
5546
parameters->eventWasHandled = eventWasHandled;
5548
// Restore the parameters so that other calls to doCommandBySelector: see them,
5549
// and other commands can participate in setting the "eventWasHandled" flag.
5550
_private->interpretKeyEventsParameters = parameters;
5554
- (void)insertText:(id)string
5556
BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString
5558
LOG(TextInput, "insertText:\"%s\"", isAttributedString ? [[string string] UTF8String] : [string UTF8String]);
5560
WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5561
_private->interpretKeyEventsParameters = 0;
5563
parameters->consumedByIM = NO;
5565
// We don't support inserting an attributed string but input methods don't appear to require this.
5567
bool isFromInputMethod = [self hasMarkedText];
5568
if (isAttributedString) {
5569
text = [string string];
5570
// We deal with the NSTextInputReplacementRangeAttributeName attribute from NSAttributedString here
5571
// simply because it is used by at least one Input Method -- it corresonds to the kEventParamTextInputSendReplaceRange
5572
// event in TSM. This behaviour matches that of -[WebHTMLView setMarkedText:selectedRange:] when it receives an
5573
// NSAttributedString
5574
NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:NULL inRange:NSMakeRange(0, [text length])];
5575
LOG(TextInput, " ReplacementRange: %s", [rangeString UTF8String]);
5577
[[self _bridge] selectNSRange:NSRangeFromString(rangeString)];
5578
isFromInputMethod = YES;
5583
bool eventHandled = false;
5584
if ([text length]) {
5585
KeyboardEvent* event = parameters ? parameters->event : 0;
5587
// insertText can be called from an input method or from normal key event processing
5588
// If its from normal key event processing, we may need to save the action to perform it later.
5589
// If its from an input method, then we should go ahead and insert the text now.
5590
// We assume it's from the input method if we have marked text.
5591
// FIXME: In theory, this could be wrong for some input methods, so we should try to find
5592
// another way to determine if the call is from the input method
5593
bool shouldSaveCommand = parameters && parameters->shouldSaveCommand;
5594
if (event && shouldSaveCommand && !isFromInputMethod) {
5595
KeypressCommand command;
5596
command.text = text;
5597
event->setKeypressCommand(command);
5601
Frame* coreFrame = core([self _frame]);
5602
String eventText = text;
5603
eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
5604
eventHandled = coreFrame && coreFrame->editor()->insertText(eventText, event);
5610
if (isFromInputMethod) {
5611
// Allow doCommandBySelector: to be called after insertText: by resetting interpretKeyEventsParameters
5612
_private->interpretKeyEventsParameters = parameters;
5613
parameters->consumedByIM = YES;
5617
parameters->eventWasHandled = eventHandled;
5620
- (BOOL)_selectionIsInsideMarkedText
5622
WebFrameBridge *bridge = [self _bridge];
5623
DOMRange *selection = [self _selectedRange];
5624
DOMRange *markedTextRange = [bridge markedTextDOMRange];
5626
ASSERT([markedTextRange startContainer] == [markedTextRange endContainer]);
5628
if ([selection startContainer] != [markedTextRange startContainer])
5631
if ([selection endContainer] != [markedTextRange startContainer])
5634
if ([selection startOffset] < [markedTextRange startOffset])
5637
if ([selection endOffset] > [markedTextRange endOffset])
5643
- (void)_updateSelectionForInputManager
5645
if (![self hasMarkedText])
5648
Frame* coreFrame = core([self _frame]);
5652
if (coreFrame->editor()->ignoreMarkedTextSelectionChange())
5655
if ([self _selectionIsInsideMarkedText]) {
5656
DOMRange *selection = [self _selectedRange];
5657
DOMRange *markedTextDOMRange = [[self _bridge] markedTextDOMRange];
5659
unsigned markedSelectionStart = [selection startOffset] - [markedTextDOMRange startOffset];
5660
unsigned markedSelectionLength = [selection endOffset] - [selection startOffset];
5661
NSRange newSelectionRange = NSMakeRange(markedSelectionStart, markedSelectionLength);
5663
[[NSInputManager currentInputManager] markedTextSelectionChanged:newSelectionRange client:self];
5666
[[NSInputManager currentInputManager] markedTextAbandoned:self];
5673
This class runs the show for handing the complete: NSTextView operation. It counts on its HTML view
5674
to call endRevertingChange: whenever the current completion needs to be aborted.
5676
The class is in one of two modes: PopupWindow showing, or not. It is shown when a completion yields
5677
more than one match. If a completion yields one or zero matches, it is not shown, and **there is no
5678
state carried across to the next completion**.
5681
@implementation WebTextCompleteController
5683
- (id)initWithHTMLView:(WebHTMLView *)view
5685
self = [super init];
5694
[_popupWindow release];
5695
[_completions release];
5696
[_originalString release];
5701
- (void)_insertMatch:(NSString *)match
5703
// FIXME: 3769654 - We should preserve case of string being inserted, even in prefix (but then also be
5704
// able to revert that). Mimic NSText.
5705
WebFrameBridge *bridge = [_view _bridge];
5706
NSString *newText = [match substringFromIndex:prefixLength];
5707
[bridge replaceSelectionWithText:newText selectReplacement:YES smartReplace:NO];
5710
// mostly lifted from NSTextView_KeyBinding.m
5713
NSRect scrollFrame = NSMakeRect(0, 0, 100, 100);
5714
NSRect tableFrame = NSZeroRect;
5715
tableFrame.size = [NSScrollView contentSizeForFrameSize:scrollFrame.size hasHorizontalScroller:NO hasVerticalScroller:YES borderType:NSNoBorder];
5716
// Added cast to work around problem with multiple Foundation initWithIdentifier: methods with different parameter types.
5717
NSTableColumn *column = [(NSTableColumn *)[NSTableColumn alloc] initWithIdentifier:[NSNumber numberWithInt:0]];
5718
[column setWidth:tableFrame.size.width];
5719
[column setEditable:NO];
5721
_tableView = [[NSTableView alloc] initWithFrame:tableFrame];
5722
[_tableView setAutoresizingMask:NSViewWidthSizable];
5723
[_tableView addTableColumn:column];
5725
[_tableView setDrawsGrid:NO];
5726
[_tableView setCornerView:nil];
5727
[_tableView setHeaderView:nil];
5728
[_tableView setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle];
5729
[_tableView setDelegate:self];
5730
[_tableView setDataSource:self];
5731
[_tableView setTarget:self];
5732
[_tableView setDoubleAction:@selector(tableAction:)];
5734
NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame:scrollFrame];
5735
[scrollView setBorderType:NSNoBorder];
5736
[scrollView setHasVerticalScroller:YES];
5737
[scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
5738
[scrollView setDocumentView:_tableView];
5739
[_tableView release];
5741
_popupWindow = [[NSWindow alloc] initWithContentRect:scrollFrame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
5742
[_popupWindow setAlphaValue:0.88f];
5743
[_popupWindow setContentView:scrollView];
5744
[scrollView release];
5745
[_popupWindow setHasShadow:YES];
5746
[_popupWindow setOneShot:YES];
5747
[_popupWindow _setForceActiveControls:YES];
5748
[_popupWindow setReleasedWhenClosed:NO];
5751
// mostly lifted from NSTextView_KeyBinding.m
5752
- (void)_placePopupWindow:(NSPoint)topLeft
5754
int numberToShow = [_completions count];
5755
if (numberToShow > 20) {
5760
NSPoint wordStart = topLeft;
5761
windowFrame.origin = [[_view window] convertBaseToScreen:[_view convertPoint:wordStart toView:nil]];
5762
windowFrame.size.height = numberToShow * [_tableView rowHeight] + (numberToShow + 1) * [_tableView intercellSpacing].height;
5763
windowFrame.origin.y -= windowFrame.size.height;
5764
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont systemFontOfSize:12.0f], NSFontAttributeName, nil];
5765
float maxWidth = 0.0f;
5768
for (i = 0; i < numberToShow; i++) {
5769
float width = ceilf([[_completions objectAtIndex:i] sizeWithAttributes:attributes].width);
5770
if (width > maxWidth) {
5775
windowFrame.size.width = 100;
5776
if (maxIndex >= 0) {
5777
maxWidth = ceilf([NSScrollView frameSizeForContentSize:NSMakeSize(maxWidth, 100.0f) hasHorizontalScroller:NO hasVerticalScroller:YES borderType:NSNoBorder].width);
5778
maxWidth = ceilf([NSWindow frameRectForContentRect:NSMakeRect(0.0f, 0.0f, maxWidth, 100.0f) styleMask:NSBorderlessWindowMask].size.width);
5780
windowFrame.size.width = MAX(maxWidth, windowFrame.size.width);
5781
maxWidth = MIN(400.0f, windowFrame.size.width);
5783
[_popupWindow setFrame:windowFrame display:NO];
5785
[_tableView reloadData];
5786
[_tableView selectRow:0 byExtendingSelection:NO];
5787
[_tableView scrollRowToVisible:0];
5788
[self _reflectSelection];
5789
[_popupWindow setLevel:NSPopUpMenuWindowLevel];
5790
[_popupWindow orderFront:nil];
5791
[[_view window] addChildWindow:_popupWindow ordered:NSWindowAbove];
5794
- (void)doCompletion
5796
if (!_popupWindow) {
5797
NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
5799
LOG_ERROR("No NSSpellChecker");
5803
// Get preceeding word stem
5804
WebFrameBridge *bridge = [_view _bridge];
5805
DOMRange *selection = kit(core([_view _frame])->selectionController()->toRange().get());
5806
DOMRange *wholeWord = [bridge rangeByAlteringCurrentSelection:SelectionController::EXTEND
5807
direction:SelectionController::BACKWARD granularity:WordGranularity];
5808
DOMRange *prefix = [wholeWord cloneRange];
5809
[prefix setEnd:[selection startContainer] offset:[selection startOffset]];
5811
// Reject some NOP cases
5812
if ([prefix collapsed]) {
5816
NSString *prefixStr = [bridge stringForRange:prefix];
5817
NSString *trimmedPrefix = [prefixStr stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
5818
if ([trimmedPrefix length] == 0) {
5822
prefixLength = [prefixStr length];
5825
[_completions release];
5826
_completions = [checker completionsForPartialWordRange:NSMakeRange(0, [prefixStr length]) inString:prefixStr language:nil inSpellDocumentWithTag:[[_view _webView] spellCheckerDocumentTag]];
5827
[_completions retain];
5829
if (!_completions || [_completions count] == 0) {
5831
} else if ([_completions count] == 1) {
5832
[self _insertMatch:[_completions objectAtIndex:0]];
5834
ASSERT(!_originalString); // this should only be set IFF we have a popup window
5835
_originalString = [[bridge stringForRange:selection] retain];
5837
NSRect wordRect = [bridge caretRectAtNode:[wholeWord startContainer] offset:[wholeWord startOffset] affinity:NSSelectionAffinityDownstream];
5838
// +1 to be under the word, not the caret
5839
// FIXME - 3769652 - Wrong positioning for right to left languages. We should line up the upper
5840
// right corner with the caret instead of upper left, and the +1 would be a -1.
5841
NSPoint wordLowerLeft = { NSMinX(wordRect)+1, NSMaxY(wordRect) };
5842
[self _placePopupWindow:wordLowerLeft];
5845
[self endRevertingChange:YES moveLeft:NO];
5849
- (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft
5853
[[_view window] removeChildWindow:_popupWindow];
5854
[_popupWindow orderOut:self];
5855
// Must autorelease because event tracking code may be on the stack touching UI
5856
[_popupWindow autorelease];
5860
WebFrameBridge *bridge = [_view _bridge];
5861
[bridge replaceSelectionWithText:_originalString selectReplacement:YES smartReplace:NO];
5862
} else if ([_view _hasSelection]) {
5864
[_view moveBackward:nil];
5866
[_view moveForward:nil];
5868
[_originalString release];
5869
_originalString = nil;
5871
// else there is no state to abort if the window was not up
5874
- (BOOL)popupWindowIsOpen
5876
return _popupWindow != nil;
5879
// WebHTMLView gives us a crack at key events it sees. Return whether we consumed the event.
5880
// The features for the various keys mimic NSTextView.
5881
- (BOOL)filterKeyDown:(NSEvent *)event
5884
NSString *string = [event charactersIgnoringModifiers];
5885
unichar c = [string characterAtIndex:0];
5886
if (c == NSUpArrowFunctionKey) {
5887
int selectedRow = [_tableView selectedRow];
5888
if (0 < selectedRow) {
5889
[_tableView selectRow:selectedRow-1 byExtendingSelection:NO];
5890
[_tableView scrollRowToVisible:selectedRow-1];
5893
} else if (c == NSDownArrowFunctionKey) {
5894
int selectedRow = [_tableView selectedRow];
5895
if (selectedRow < (int)[_completions count]-1) {
5896
[_tableView selectRow:selectedRow+1 byExtendingSelection:NO];
5897
[_tableView scrollRowToVisible:selectedRow+1];
5900
} else if (c == NSRightArrowFunctionKey || c == '\n' || c == '\r' || c == '\t') {
5901
[self endRevertingChange:NO moveLeft:NO];
5903
} else if (c == NSLeftArrowFunctionKey) {
5904
[self endRevertingChange:NO moveLeft:YES];
5906
} else if (c == 0x1b || c == NSF5FunctionKey) {
5907
[self endRevertingChange:YES moveLeft:NO];
5909
} else if (c == ' ' || ispunct(c)) {
5910
[self endRevertingChange:NO moveLeft:NO];
5911
return NO; // let the char get inserted
5917
- (void)_reflectSelection
5919
int selectedRow = [_tableView selectedRow];
5920
ASSERT(selectedRow >= 0 && selectedRow < (int)[_completions count]);
5921
[self _insertMatch:[_completions objectAtIndex:selectedRow]];
5924
- (void)tableAction:(id)sender
5926
[self _reflectSelection];
5927
[self endRevertingChange:NO moveLeft:NO];
5930
- (int)numberOfRowsInTableView:(NSTableView *)tableView
5932
return [_completions count];
5935
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
5937
return [_completions objectAtIndex:row];
5940
- (void)tableViewSelectionDidChange:(NSNotification *)notification
5942
[self _reflectSelection];
5947
@implementation WebHTMLView (WebDocumentPrivateProtocols)
5949
- (NSRect)selectionRect
5951
if ([self _hasSelection])
5952
return core([self _frame])->selectionRect();
5956
- (NSArray *)selectionTextRects
5958
if (![self _hasSelection])
5961
Vector<FloatRect> list;
5962
core([self _frame])->selectionTextRects(list);
5964
unsigned size = list.size();
5965
NSMutableArray *result = [[[NSMutableArray alloc] initWithCapacity:size] autorelease];
5966
for (unsigned i = 0; i < size; ++i)
5967
[result addObject:[NSValue valueWithRect:list[i]]];
5972
- (NSView *)selectionView
5977
- (NSImage *)selectionImageForcingBlackText:(BOOL)forceBlackText
5979
if ([self _hasSelection])
5980
return core([self _frame])->selectionImage(forceBlackText);
5984
- (NSImage *)selectionImageForcingWhiteText:(BOOL)forceWhiteText
5986
// NOTE: this method is obsolete and doesn't behave as its name suggests.
5987
// See comment in WebDocumentPrivate.h.
5988
return [self selectionImageForcingBlackText:forceWhiteText];
5991
- (NSRect)selectionImageRect
5993
if ([self _hasSelection])
5994
return core([self _frame])->selectionRect();
5998
- (NSArray *)pasteboardTypesForSelection
6000
if ([self _canSmartCopyOrDelete]) {
6001
NSMutableArray *types = [[[[self class] _selectionPasteboardTypes] mutableCopy] autorelease];
6002
[types addObject:WebSmartPastePboardType];
6005
return [[self class] _selectionPasteboardTypes];
6009
- (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
6011
[self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:nil];
6016
Frame* coreFrame = core([self _frame]);
6018
coreFrame->selectionController()->selectAll();
6023
Frame* coreFrame = core([self _frame]);
6026
coreFrame->selectionController()->clear();
6029
- (NSString *)string
6031
return [[self _bridge] stringForRange:[self _documentRange]];
6034
- (NSAttributedString *)_attributeStringFromDOMRange:(DOMRange *)range
6036
NSAttributedString *attributedString;
6038
double start = CFAbsoluteTimeGetCurrent();
6040
attributedString = [[[NSAttributedString alloc] _initWithDOMRange:range] autorelease];
6042
double duration = CFAbsoluteTimeGetCurrent() - start;
6043
LOG(Timing, "creating attributed string from selection took %f seconds.", duration);
6045
return attributedString;
6048
- (NSAttributedString *)attributedString
6050
DOMDocument *document = [[self _frame] DOMDocument];
6051
NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[document _documentRange]];
6052
if (!attributedString) {
6053
Document* coreDocument = core(document);
6054
Range range(coreDocument, coreDocument, 0, 0, 0);
6055
attributedString = [NSAttributedString _web_attributedStringFromRange:&range];
6057
return attributedString;
6060
- (NSString *)selectedString
6062
return [[self _bridge] selectedString];
6065
- (NSAttributedString *)selectedAttributedString
6067
NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[self _selectedRange]];
6068
if (!attributedString) {
6069
Frame* coreFrame = core([self _frame]);
6071
RefPtr<Range> range = coreFrame->selectionController()->selection().toRange();
6072
attributedString = [NSAttributedString _web_attributedStringFromRange:range.get()];
6075
return attributedString;
6078
- (BOOL)supportsTextEncoding
6083
- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection
6085
if (![string length])
6088
return [[self _bridge] searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:startInSelection];
6093
@implementation WebHTMLView (WebDocumentInternalProtocols)
6095
- (NSDictionary *)elementAtPoint:(NSPoint)point
6097
return [self elementAtPoint:point allowShadowContent:NO];
6100
- (NSDictionary *)elementAtPoint:(NSPoint)point allowShadowContent:(BOOL)allow;
6102
Frame* coreframe = core([self _frame]);
6104
return [[[WebElementDictionary alloc] initWithHitTestResult:coreframe->eventHandler()->hitTestResultAtPoint(IntPoint(point), allow)] autorelease];
6108
- (NSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag limit:(NSUInteger)limit
6110
return [[self _bridge] markAllMatchesForText:string caseSensitive:caseFlag limit:limit];
6113
- (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue
6115
[[self _bridge] setMarkedTextMatchesAreHighlighted:newValue];
6118
- (BOOL)markedTextMatchesAreHighlighted
6120
return [[self _bridge] markedTextMatchesAreHighlighted];
6123
- (void)unmarkAllTextMatches
6125
return [[self _bridge] unmarkAllTextMatches];
6128
- (NSArray *)rectsForTextMatches
6130
return [[self _bridge] rectsForTextMatches];
6135
// This is used by AppKit and is included here so that WebDataProtocolScheme is only defined once.
6136
@implementation NSURL (WebDataURL)
6138
+ (NSURL *)_web_uniqueWebDataURL
6140
CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
6141
NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
6143
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", WebDataProtocolScheme, UUIDString]];
6144
CFRelease(UUIDString);