~ubuntu-branches/ubuntu/maverick/webkit/maverick

« back to all changes in this revision

Viewing changes to WebKit/WebView/WebHTMLView.mm

  • Committer: Bazaar Package Importer
  • Author(s): Mike Hommey
  • Date: 2007-08-19 15:54:12 UTC
  • Revision ID: james.westby@ubuntu.com-20070819155412-uxxg1h9plpghmtbi
Tags: upstream-0~svn25144
ImportĀ upstreamĀ versionĀ 0~svn25144

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
 
3
 *           (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 *
 
9
 * 1.  Redistributions of source code must retain the above copyright
 
10
 *     notice, this list of conditions and the following disclaimer. 
 
11
 * 2.  Redistributions in binary form must reproduce the above copyright
 
12
 *     notice, this list of conditions and the following disclaimer in the
 
13
 *     documentation and/or other materials provided with the distribution. 
 
14
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 
15
 *     its contributors may be used to endorse or promote products derived
 
16
 *     from this software without specific prior written permission. 
 
17
 *
 
18
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 
19
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 
21
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
27
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
28
 */
 
29
 
 
30
#import "WebHTMLView.h"
 
31
 
 
32
#import "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>
 
74
#import <dlfcn.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>
 
109
 
 
110
using namespace WebCore;
 
111
using namespace HTMLNames;
 
112
 
 
113
extern "C" {
 
114
 
 
115
// Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers.
 
116
 
 
117
extern NSString *NSMarkedClauseSegmentAttributeName; 
 
118
extern NSString *NSTextInputReplacementRangeAttributeName; 
 
119
 
 
120
// Kill ring calls. Would be better to use NSKillRing.h, but that's not available as API or SPI.
 
121
 
 
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);
 
130
 
 
131
}
 
132
 
 
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;
 
140
@end
 
141
 
 
142
@interface NSApplication (AppKitSecretsIKnowAbout)
 
143
- (void)speakString:(NSString *)string;
 
144
@end
 
145
 
 
146
@interface NSWindow (AppKitSecretsIKnowAbout)
 
147
- (id)_newFirstResponderAfterResigning;
 
148
- (void)_setForceActiveControls:(BOOL)flag;
 
149
@end
 
150
 
 
151
@interface NSAttributedString (AppKitSecretsIKnowAbout)
 
152
- (id)_initWithDOMRange:(DOMRange *)domRange;
 
153
- (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
 
154
@end
 
155
 
 
156
@interface NSSpellChecker (CurrentlyPrivateForTextView)
 
157
- (void)learnWord:(NSString *)word;
 
158
@end
 
159
 
 
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
 
166
 
 
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
 
172
 
 
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
 
177
 
 
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
 
182
 
 
183
#define AUTOSCROLL_INTERVAL             0.1f
 
184
 
 
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
 
190
 
 
191
#define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP        120.0f
 
192
#define MAX_DRAG_LABEL_WIDTH                    320.0f
 
193
 
 
194
#define DRAG_LINK_LABEL_FONT_SIZE   11.0f
 
195
#define DRAG_LINK_URL_FONT_SIZE   10.0f
 
196
 
 
197
// Any non-zero value will do, but using something recognizable might help us debug some day.
 
198
#define TRACKING_RECT_TAG 0xBADFACE
 
199
 
 
200
// FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
 
201
#define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
 
202
 
 
203
#define STANDARD_WEIGHT 5
 
204
#define MIN_BOLD_WEIGHT 9
 
205
#define STANDARD_BOLD_WEIGHT 10
 
206
 
 
207
// Fake URL scheme.
 
208
#define WebDataProtocolScheme @"webkit-fake-url"
 
209
 
 
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
 
213
@end
 
214
 
 
215
@implementation WebCoreScrollView
 
216
@end
 
217
 
 
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;
 
220
 
 
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;
 
223
 
 
224
static WebHTMLView *lastHitView;
 
225
 
 
226
// We need this to be able to safely reference the CachedImage for the promised drag data
 
227
static CachedResourceClient* promisedDataClient()
 
228
{
 
229
    static CachedResourceClient* staticCachedResourceClient = new CachedResourceClient;
 
230
    return staticCachedResourceClient;
 
231
}
 
232
 
 
233
@interface WebHTMLView (WebTextSizing) <_WebDocumentTextSizing>
 
234
@end
 
235
 
 
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;
 
261
@end
 
262
 
 
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;
 
265
@end
 
266
 
 
267
@interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
 
268
- (void)_updateSelectionForInputManager;
 
269
@end
 
270
 
 
271
@interface WebHTMLView (WebEditingStyleSupport)
 
272
- (DOMCSSStyleDeclaration *)_emptyStyle;
 
273
- (NSString *)_colorAsString:(NSColor *)color;
 
274
@end
 
275
 
 
276
@interface NSView (WebHTMLViewFileInternal)
 
277
- (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array;
 
278
@end
 
279
 
 
280
@interface NSMutableDictionary (WebHTMLViewFileInternal)
 
281
- (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
 
282
@end
 
283
 
 
284
// Handles the complete: text command
 
285
@interface WebTextCompleteController : NSObject {
 
286
@private
 
287
    WebHTMLView *_view;
 
288
    NSWindow *_popupWindow;
 
289
    NSTableView *_tableView;
 
290
    NSArray *_completions;
 
291
    NSString *_originalString;
 
292
    int prefixLength;
 
293
}
 
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;
 
300
@end
 
301
 
 
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
 
308
    BOOL consumedByIM;
 
309
};
 
310
 
 
311
@implementation WebHTMLViewPrivate
 
312
 
 
313
#ifndef BUILDING_ON_TIGER
 
314
+ (void)initialize
 
315
{
 
316
    WebCoreObjCFinalizeOnMainThread(self);
 
317
}
 
318
#endif
 
319
 
 
320
- (void)dealloc
 
321
{
 
322
    ASSERT(!autoscrollTimer);
 
323
    ASSERT(!autoscrollTriggerEvent);
 
324
    ASSERT(!updateActiveStateTimer);
 
325
    ASSERT(!updateMouseoverTimer);
 
326
    
 
327
    [mouseDownEvent release];
 
328
    [keyDownEvent release];
 
329
    [pluginController release];
 
330
    [toolTip release];
 
331
    [compController release];
 
332
    [firstResponderTextViewAtMouseDownTime release];
 
333
    [dataSource release];
 
334
    [highlighters release];
 
335
    if (promisedDragTIFFDataSource)
 
336
        promisedDragTIFFDataSource->deref(promisedDataClient());
 
337
 
 
338
    [super dealloc];
 
339
}
 
340
 
 
341
- (void)finalize
 
342
{
 
343
    ASSERT_MAIN_THREAD();
 
344
 
 
345
    if (promisedDragTIFFDataSource)
 
346
        promisedDragTIFFDataSource->deref(promisedDataClient());
 
347
 
 
348
    [super finalize];
 
349
}
 
350
 
 
351
- (void)clear
 
352
{
 
353
    [mouseDownEvent release];
 
354
    [keyDownEvent release];
 
355
    [pluginController release];
 
356
    [toolTip release];
 
357
    [compController release];
 
358
    [firstResponderTextViewAtMouseDownTime release];
 
359
    [dataSource release];
 
360
    [highlighters release];
 
361
    if (promisedDragTIFFDataSource)
 
362
        promisedDragTIFFDataSource->deref(promisedDataClient());
 
363
 
 
364
    mouseDownEvent = nil;
 
365
    keyDownEvent = nil;
 
366
    pluginController = nil;
 
367
    toolTip = nil;
 
368
    compController = nil;
 
369
    firstResponderTextViewAtMouseDownTime = nil;
 
370
    dataSource = nil;
 
371
    highlighters = nil;
 
372
    promisedDragTIFFDataSource = 0;
 
373
}
 
374
 
 
375
@end
 
376
 
 
377
@implementation WebHTMLView (WebHTMLViewFileInternal)
 
378
 
 
379
- (DOMRange *)_documentRange
 
380
{
 
381
    return [[[self _frame] DOMDocument] _documentRange];
 
382
}
 
383
 
 
384
- (BOOL)_imageExistsAtPaths:(NSArray *)paths
 
385
{
 
386
    NSEnumerator *enumerator = [paths objectEnumerator];
 
387
    NSString *path;
 
388
    
 
389
    while ((path = [enumerator nextObject]) != nil) {
 
390
        NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
 
391
        if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType))
 
392
            return YES;
 
393
    }
 
394
    
 
395
    return NO;
 
396
}
 
397
 
 
398
- (WebDataSource *)_dataSource
 
399
{
 
400
    return _private->dataSource;
 
401
}
 
402
 
 
403
- (WebFrameBridge *)_bridge
 
404
{
 
405
    return [_private->dataSource _bridge];
 
406
}
 
407
 
 
408
- (WebView *)_webView
 
409
{
 
410
    return [_private->dataSource _webView];
 
411
}
 
412
 
 
413
- (WebFrameView *)_frameView
 
414
{
 
415
    return [[_private->dataSource webFrame] frameView];
 
416
}
 
417
 
 
418
- (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
 
419
{
 
420
    DOMDocumentFragment *fragment;
 
421
    NSEnumerator *enumerator = [paths objectEnumerator];
 
422
    NSMutableArray *domNodes = [[NSMutableArray alloc] init];
 
423
    NSString *path;
 
424
    
 
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]];
 
430
    }
 
431
    
 
432
    fragment = [[self _bridge] documentFragmentWithNodesAsParagraphs:domNodes]; 
 
433
    
 
434
    [domNodes release];
 
435
    
 
436
    return [fragment firstChild] != nil ? fragment : nil;
 
437
}
 
438
 
 
439
+ (NSArray *)_excludedElementsForAttributedStringConversion
 
440
{
 
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.
 
445
            @"style",
 
446
            // Omit xml so the result is not XHTML.
 
447
            @"xml", 
 
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.
 
453
            @"object", nil];
 
454
        CFRetain(elements);
 
455
    }
 
456
    return elements;
 
457
}
 
458
 
 
459
static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
 
460
{
 
461
    CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
 
462
    NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
 
463
    CFRelease(UUIDRef);
 
464
    NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]];
 
465
    CFRelease(UUIDString);
 
466
 
 
467
    return URL;
 
468
}
 
469
 
 
470
- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
 
471
                                               inContext:(DOMRange *)context
 
472
                                          allowPlainText:(BOOL)allowPlainText
 
473
                                          chosePlainText:(BOOL *)chosePlainText
 
474
{
 
475
    NSArray *types = [pasteboard types];
 
476
    *chosePlainText = NO;
 
477
    DOMDocumentFragment *fragment = nil;
 
478
 
 
479
    if ([types containsObject:WebArchivePboardType] &&
 
480
        (fragment = [self _documentFragmentFromPasteboard:pasteboard 
 
481
                                                  forType:WebArchivePboardType
 
482
                                                inContext:context
 
483
                                             subresources:0]))
 
484
        return fragment;
 
485
                                           
 
486
    if ([types containsObject:NSFilenamesPboardType] &&
 
487
        (fragment = [self _documentFragmentFromPasteboard:pasteboard 
 
488
                                                  forType:NSFilenamesPboardType
 
489
                                                inContext:context
 
490
                                             subresources:0]))
 
491
        return fragment;
 
492
    
 
493
    if ([types containsObject:NSHTMLPboardType] &&
 
494
        (fragment = [self _documentFragmentFromPasteboard:pasteboard 
 
495
                                                  forType:NSHTMLPboardType
 
496
                                                inContext:context
 
497
                                             subresources:0]))
 
498
        return fragment;
 
499
    
 
500
    if ([types containsObject:NSRTFPboardType] &&
 
501
        (fragment = [self _documentFragmentFromPasteboard:pasteboard 
 
502
                                                  forType:NSRTFPboardType
 
503
                                                inContext:context
 
504
                                             subresources:0]))
 
505
        return fragment;
 
506
 
 
507
    if ([types containsObject:NSRTFDPboardType] &&
 
508
        (fragment = [self _documentFragmentFromPasteboard:pasteboard 
 
509
                                                  forType:NSRTFDPboardType
 
510
                                                inContext:context
 
511
                                             subresources:0]))
 
512
        return fragment;
 
513
 
 
514
    if ([types containsObject:NSTIFFPboardType] &&
 
515
        (fragment = [self _documentFragmentFromPasteboard:pasteboard 
 
516
                                                  forType:NSTIFFPboardType
 
517
                                                inContext:context
 
518
                                             subresources:0]))
 
519
        return fragment;
 
520
 
 
521
    if ([types containsObject:NSPICTPboardType] &&
 
522
        (fragment = [self _documentFragmentFromPasteboard:pasteboard 
 
523
                                                  forType:NSPICTPboardType
 
524
                                                inContext:context
 
525
                                             subresources:0]))
 
526
        return fragment;
 
527
    
 
528
    if ([types containsObject:NSURLPboardType] &&
 
529
        (fragment = [self _documentFragmentFromPasteboard:pasteboard 
 
530
                                                  forType:NSURLPboardType
 
531
                                                inContext:context
 
532
                                             subresources:0]))
 
533
        return fragment;
 
534
        
 
535
    if (allowPlainText && [types containsObject:NSStringPboardType] &&
 
536
        (fragment = [self _documentFragmentFromPasteboard:pasteboard
 
537
                                                  forType:NSStringPboardType
 
538
                                                inContext:context
 
539
                                             subresources:0])) {
 
540
        *chosePlainText = YES;
 
541
        return fragment;
 
542
    }
 
543
    
 
544
    return nil;
 
545
}
 
546
 
 
547
- (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
 
548
{
 
549
    NSArray *types = [pasteboard types];
 
550
    
 
551
    if ([types containsObject:NSStringPboardType])
 
552
        return [pasteboard stringForType:NSStringPboardType];
 
553
    
 
554
    NSAttributedString *attributedString = nil;
 
555
    NSString *string;
 
556
 
 
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];
 
565
    }
 
566
    
 
567
    if ([types containsObject:NSFilenamesPboardType]) {
 
568
        string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
 
569
        if (string != nil)
 
570
            return string;
 
571
    }
 
572
    
 
573
    NSURL *URL;
 
574
    
 
575
    if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
 
576
        string = [URL _web_userVisibleString];
 
577
        if ([string length] > 0)
 
578
            return string;
 
579
    }
 
580
    
 
581
    return nil;
 
582
}
 
583
 
 
584
- (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
 
585
{
 
586
    DOMRange *range = [self _selectedRange];
 
587
    BOOL chosePlainText;
 
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];
 
593
    }
 
594
}
 
595
 
 
596
- (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard
 
597
{
 
598
    NSString *text = [self _plainTextFromPasteboard:pasteboard];
 
599
    if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted])
 
600
        [[self _bridge] replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]];
 
601
}
 
602
 
 
603
- (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
 
604
{
 
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];
 
610
}
 
611
 
 
612
- (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
 
613
{
 
614
    WebView *webView = [self _webView];
 
615
    return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
 
616
}
 
617
 
 
618
- (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
 
619
{
 
620
    return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
 
621
}
 
622
 
 
623
// Calculate the vertical size of the view that fits on a single page
 
624
- (float)_calculatePrintHeight
 
625
{
 
626
    // Obtain the print info object for the current operation
 
627
    NSPrintInfo *pi = [[NSPrintOperation currentOperation] printInfo];
 
628
    
 
629
    // Calculate the page height in points
 
630
    NSSize paperSize = [pi paperSize];
 
631
    return paperSize.height - [pi topMargin] - [pi bottomMargin];
 
632
}
 
633
 
 
634
- (void)_updateTextSizeMultiplier
 
635
{
 
636
    [[self _bridge] setTextSizeMultiplier:[[self _webView] textSizeMultiplier]];    
 
637
}
 
638
 
 
639
- (DOMRange *)_selectedRange
 
640
{
 
641
    Frame* coreFrame = core([self _frame]);
 
642
    return coreFrame ? kit(coreFrame->selectionController()->toRange().get()) : nil;
 
643
}
 
644
 
 
645
- (BOOL)_shouldDeleteRange:(DOMRange *)range
 
646
{
 
647
    Frame* coreFrame = core([self _frame]);
 
648
    return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range));
 
649
}
 
650
 
 
651
- (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
 
652
{
 
653
    return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
 
654
}
 
655
 
 
656
- (NSView *)_hitViewForEvent:(NSEvent *)event
 
657
{
 
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;    
 
663
    return hitView;
 
664
}
 
665
 
 
666
- (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
 
667
{
 
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];
 
672
    }
 
673
    
 
674
    // Put the attributed string on the pasteboard (RTF/RTFD format).
 
675
    if ([types containsObject:NSRTFDPboardType]) {
 
676
        if (attributedString == nil) {
 
677
            attributedString = [self selectedAttributedString];
 
678
        }        
 
679
        NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
 
680
        [pasteboard setData:RTFDData forType:NSRTFDPboardType];
 
681
    }        
 
682
    if ([types containsObject:NSRTFPboardType]) {
 
683
        if (attributedString == nil) {
 
684
            attributedString = [self selectedAttributedString];
 
685
        }
 
686
        if ([attributedString containsAttachments]) {
 
687
            attributedString = [attributedString _web_attributedStringByStrippingAttachmentCharacters];
 
688
        }
 
689
        NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
 
690
        [pasteboard setData:RTFData forType:NSRTFPboardType];
 
691
    }
 
692
    
 
693
    // Put plain string on the pasteboard.
 
694
    if ([types containsObject:NSStringPboardType]) {
 
695
        // Map &nbsp; 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];
 
702
        [s release];
 
703
    }
 
704
    
 
705
    if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
 
706
        [pasteboard setData:nil forType:WebSmartPastePboardType];
 
707
    }
 
708
}
 
709
 
 
710
- (void)_setMouseDownEvent:(NSEvent *)event
 
711
{
 
712
    ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
 
713
 
 
714
    if (event == _private->mouseDownEvent)
 
715
        return;
 
716
 
 
717
    [event retain];
 
718
    [_private->mouseDownEvent release];
 
719
    _private->mouseDownEvent = event;
 
720
 
 
721
    [_private->firstResponderTextViewAtMouseDownTime release];
 
722
    
 
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.
 
727
    if (event) {
 
728
        NSResponder *firstResponder = [[self window] firstResponder];
 
729
        if ([firstResponder isKindOfClass:[NSTextView class]])
 
730
            _private->firstResponderTextViewAtMouseDownTime = [firstResponder retain];
 
731
        else
 
732
            _private->firstResponderTextViewAtMouseDownTime = nil;
 
733
    } else
 
734
        _private->firstResponderTextViewAtMouseDownTime = nil;
 
735
}
 
736
 
 
737
- (void)_cancelUpdateActiveStateTimer
 
738
{
 
739
    if (_private->updateActiveStateTimer) {
 
740
        CFRunLoopTimerInvalidate(_private->updateActiveStateTimer);
 
741
        CFRelease(_private->updateActiveStateTimer);
 
742
        _private->updateActiveStateTimer = NULL;
 
743
    }
 
744
}
 
745
 
 
746
- (void)_cancelUpdateMouseoverTimer
 
747
{
 
748
    if (_private->updateMouseoverTimer) {
 
749
        CFRunLoopTimerInvalidate(_private->updateMouseoverTimer);
 
750
        CFRelease(_private->updateMouseoverTimer);
 
751
        _private->updateMouseoverTimer = NULL;
 
752
    }
 
753
}
 
754
 
 
755
- (WebHTMLView *)_topHTMLView
 
756
{
 
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];
 
759
    ASSERT(view);
 
760
    ASSERT([view isKindOfClass:[WebHTMLView class]]);
 
761
    return view;
 
762
}
 
763
 
 
764
- (BOOL)_isTopHTMLView
 
765
{
 
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];
 
768
}
 
769
 
 
770
- (void)_web_setPrintingModeRecursive
 
771
{
 
772
    [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
 
773
 
 
774
#ifndef NDEBUG
 
775
    _private->enumeratingSubviews = YES;
 
776
#endif
 
777
 
 
778
    NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
 
779
 
 
780
    [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
 
781
 
 
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];
 
785
 
 
786
    [descendantWebHTMLViews release];
 
787
 
 
788
#ifndef NDEBUG
 
789
    _private->enumeratingSubviews = NO;
 
790
#endif
 
791
}
 
792
 
 
793
- (void)_web_clearPrintingModeRecursive
 
794
{
 
795
    [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
 
796
 
 
797
#ifndef NDEBUG
 
798
    _private->enumeratingSubviews = YES;
 
799
#endif
 
800
 
 
801
    NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
 
802
 
 
803
    [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
 
804
 
 
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];
 
808
 
 
809
    [descendantWebHTMLViews release];
 
810
 
 
811
#ifndef NDEBUG
 
812
    _private->enumeratingSubviews = NO;
 
813
#endif
 
814
}
 
815
 
 
816
- (void)_web_setPrintingModeRecursiveAndAdjustViewSize
 
817
{
 
818
    [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
 
819
 
 
820
#ifndef NDEBUG
 
821
    _private->enumeratingSubviews = YES;
 
822
#endif
 
823
 
 
824
    NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
 
825
 
 
826
    [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
 
827
 
 
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];
 
831
 
 
832
    [descendantWebHTMLViews release];
 
833
 
 
834
#ifndef NDEBUG
 
835
    _private->enumeratingSubviews = NO;
 
836
#endif
 
837
}
 
838
 
 
839
- (void)_layoutIfNeeded
 
840
{
 
841
    ASSERT(!_private->subviewsSetAside);
 
842
 
 
843
    if ([[self _bridge] needsLayout])
 
844
        _private->needsLayout = YES;
 
845
    if (_private->needsToApplyStyles || _private->needsLayout)
 
846
        [self layout];
 
847
}
 
848
 
 
849
- (void)_web_layoutIfNeededRecursive
 
850
{
 
851
    [self _layoutIfNeeded];
 
852
 
 
853
#ifndef NDEBUG
 
854
    _private->enumeratingSubviews = YES;
 
855
#endif
 
856
 
 
857
    NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
 
858
 
 
859
    [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
 
860
 
 
861
    unsigned count = [descendantWebHTMLViews count];
 
862
    for (unsigned i = 0; i < count; ++i)
 
863
        [[descendantWebHTMLViews objectAtIndex:i] _layoutIfNeeded];
 
864
 
 
865
    [descendantWebHTMLViews release];
 
866
 
 
867
#ifndef NDEBUG
 
868
    _private->enumeratingSubviews = NO;
 
869
#endif
 
870
}
 
871
 
 
872
@end
 
873
 
 
874
@implementation WebHTMLView (WebPrivate)
 
875
 
 
876
+ (NSArray *)supportedMIMETypes
 
877
{
 
878
    return [WebHTMLRepresentation supportedMIMETypes];
 
879
}
 
880
 
 
881
+ (NSArray *)supportedImageMIMETypes
 
882
{
 
883
    return [WebHTMLRepresentation supportedImageMIMETypes];
 
884
}
 
885
 
 
886
+ (NSArray *)supportedNonImageMIMETypes
 
887
{
 
888
    return [WebHTMLRepresentation supportedNonImageMIMETypes];
 
889
}
 
890
 
 
891
+ (NSArray *)unsupportedTextMIMETypes
 
892
{
 
893
    return [NSArray arrayWithObjects:
 
894
        @"text/calendar",       // iCal
 
895
        @"text/x-calendar",
 
896
        @"text/x-vcalendar",
 
897
        @"text/vcalendar",
 
898
        @"text/vcard",          // vCard
 
899
        @"text/x-vcard",
 
900
        @"text/directory",
 
901
        @"text/ldif",           // Netscape Address Book
 
902
        @"text/qif",            // Quicken
 
903
        @"text/x-qif",
 
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
 
907
        nil];
 
908
}
 
909
 
 
910
+ (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
 
911
{
 
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];
 
919
 
 
920
    // Pretend it's a mouse move.
 
921
    [[NSNotificationCenter defaultCenter]
 
922
        postNotificationName:WKMouseMovedNotification() object:self
 
923
        userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
 
924
}
 
925
 
 
926
- (void)_updateMouseoverWithFakeEvent
 
927
{
 
928
    [self _cancelUpdateMouseoverTimer];
 
929
    
 
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];
 
937
    
 
938
    [self _updateMouseoverWithEvent:fakeEvent];
 
939
}
 
940
 
 
941
static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info)
 
942
{
 
943
    WebHTMLView *view = (WebHTMLView *)info;
 
944
    
 
945
    [view _updateMouseoverWithFakeEvent];
 
946
}
 
947
 
 
948
- (void)_frameOrBoundsChanged
 
949
{
 
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];
 
954
    }
 
955
 
 
956
    NSPoint origin = [[self superview] bounds].origin;
 
957
    if (!NSEqualPoints(_private->lastScrollPosition, origin)) {
 
958
        [[self _bridge] sendScrollEvent];
 
959
        [_private->compController endRevertingChange:NO moveLeft:NO];
 
960
        
 
961
        WebView *webView = [self _webView];
 
962
        [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
 
963
    }
 
964
    _private->lastScrollPosition = origin;
 
965
 
 
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);
 
971
    }
 
972
}
 
973
 
 
974
- (void)_setAsideSubviews
 
975
{
 
976
    ASSERT(!_private->subviewsSetAside);
 
977
    ASSERT(_private->savedSubviews == nil);
 
978
    _private->savedSubviews = _subviews;
 
979
    _subviews = nil;
 
980
    _private->subviewsSetAside = YES;
 
981
 }
 
982
 
 
983
 - (void)_restoreSubviews
 
984
 {
 
985
    ASSERT(_private->subviewsSetAside);
 
986
    ASSERT(_subviews == nil);
 
987
    _subviews = _private->savedSubviews;
 
988
    _private->savedSubviews = nil;
 
989
    _private->subviewsSetAside = NO;
 
990
}
 
991
 
 
992
#ifndef NDEBUG
 
993
 
 
994
- (void)didAddSubview:(NSView *)subview
 
995
{
 
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]));
 
998
}
 
999
 
 
1000
- (void)willRemoveSubview:(NSView *)subview
 
1001
{
 
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]));
 
1004
}
 
1005
 
 
1006
#endif
 
1007
 
 
1008
#ifdef BUILDING_ON_TIGER
 
1009
 
 
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
 
1016
{
 
1017
    if (![[self _webView] drawsBackground])
 
1018
        [self _web_layoutIfNeededRecursive];
 
1019
    [super _propagateDirtyRectsToOpaqueAncestors];
 
1020
}
 
1021
 
 
1022
#else
 
1023
 
 
1024
- (void)viewWillDraw
 
1025
{
 
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];
 
1032
}
 
1033
 
 
1034
#endif
 
1035
 
 
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
 
1038
{
 
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) {
 
1044
        if (isPrinting)
 
1045
            [self _web_setPrintingModeRecursive];
 
1046
        else
 
1047
            [self _web_clearPrintingModeRecursive];
 
1048
    }
 
1049
 
 
1050
#ifdef BUILDING_ON_TIGER
 
1051
 
 
1052
    // Because Tiger does not have viewWillDraw we need to do layout here.
 
1053
    [self _web_layoutIfNeededRecursive];
 
1054
    [_subviews makeObjectsPerformSelector:@selector(_propagateDirtyRectsToOpaqueAncestors)];
 
1055
 
 
1056
#endif
 
1057
 
 
1058
    [self _setAsideSubviews];
 
1059
    [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView];
 
1060
    [self _restoreSubviews];
 
1061
 
 
1062
    if (wasInPrintingMode != isPrinting) {
 
1063
        if (wasInPrintingMode)
 
1064
            [self _web_setPrintingModeRecursive];
 
1065
        else
 
1066
            [self _web_clearPrintingModeRecursive];
 
1067
    }
 
1068
}
 
1069
 
 
1070
// Don't let AppKit even draw subviews. We take care of that.
 
1071
- (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
 
1072
{
 
1073
    BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
 
1074
 
 
1075
    BOOL wasInPrintingMode = _private->printing;
 
1076
    BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
 
1077
 
 
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) {
 
1082
            if (isPrinting)
 
1083
                [self _web_setPrintingModeRecursive];
 
1084
            else
 
1085
                [self _web_clearPrintingModeRecursive];
 
1086
        }
 
1087
 
 
1088
#ifdef BUILDING_ON_TIGER
 
1089
 
 
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];
 
1094
 
 
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];
 
1101
 
 
1102
#endif
 
1103
 
 
1104
        [self _setAsideSubviews];
 
1105
    }
 
1106
 
 
1107
    [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
 
1108
 
 
1109
    if (needToSetAsideSubviews) {
 
1110
        if (wasInPrintingMode != isPrinting) {
 
1111
            if (wasInPrintingMode)
 
1112
                [self _web_setPrintingModeRecursive];
 
1113
            else
 
1114
                [self _web_clearPrintingModeRecursive];
 
1115
        }
 
1116
 
 
1117
        [self _restoreSubviews];
 
1118
    }
 
1119
}
 
1120
 
 
1121
- (BOOL)_insideAnotherHTMLView
 
1122
{
 
1123
    return self != [self _topHTMLView];
 
1124
}
 
1125
 
 
1126
- (void)scrollPoint:(NSPoint)point
 
1127
{
 
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];
 
1137
            return;
 
1138
        }
 
1139
        const unichar pageDown = NSPageDownFunctionKey;
 
1140
        if ([[event characters] rangeOfString:[NSString stringWithCharacters:&pageDown length:1]].length == 1) {
 
1141
            [self tryToPerform:@selector(scrollPageDown:) with:nil];
 
1142
            return;
 
1143
        }
 
1144
    }
 
1145
    
 
1146
    [super scrollPoint:point];
 
1147
}
 
1148
 
 
1149
- (NSView *)hitTest:(NSPoint)point
 
1150
{
 
1151
    // WebHTMLView objects handle all events for objects inside them.
 
1152
    // To get those events, we prevent hit testing from AppKit.
 
1153
 
 
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.
 
1174
 
 
1175
    BOOL captureHitsOnSubviews;
 
1176
    if (forceNSViewHitTest)
 
1177
        captureHitsOnSubviews = NO;
 
1178
    else if (forceWebHTMLViewHitTest)
 
1179
        captureHitsOnSubviews = YES;
 
1180
    else {
 
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);
 
1186
    }
 
1187
 
 
1188
    if (!captureHitsOnSubviews)
 
1189
        return [super hitTest:point];
 
1190
    if ([[self superview] mouse:point inRect:[self frame]])
 
1191
        return self;
 
1192
    return nil;
 
1193
}
 
1194
 
 
1195
- (void)_clearLastHitViewIfSelf
 
1196
{
 
1197
    if (lastHitView == self)
 
1198
        lastHitView = nil;
 
1199
}
 
1200
 
 
1201
- (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
 
1202
{
 
1203
    ASSERT(_private->trackingRectOwner == nil);
 
1204
    _private->trackingRectOwner = owner;
 
1205
    _private->trackingRectUserData = data;
 
1206
    return TRACKING_RECT_TAG;
 
1207
}
 
1208
 
 
1209
- (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
 
1210
{
 
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;
 
1216
}
 
1217
 
 
1218
- (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
 
1219
{
 
1220
    ASSERT(count == 1);
 
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;
 
1226
}
 
1227
 
 
1228
- (void)removeTrackingRect:(NSTrackingRectTag)tag
 
1229
{
 
1230
    if (tag == 0)
 
1231
        return;
 
1232
    
 
1233
    if (_private && (tag == TRACKING_RECT_TAG)) {
 
1234
        _private->trackingRectOwner = nil;
 
1235
        return;
 
1236
    }
 
1237
    
 
1238
    if (_private && (tag == _private->lastToolTipTag)) {
 
1239
        [super removeTrackingRect:tag];
 
1240
        _private->lastToolTipTag = 0;
 
1241
        return;
 
1242
    }
 
1243
    
 
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();
 
1247
}
 
1248
 
 
1249
- (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
 
1250
{
 
1251
    int i;
 
1252
    for (i = 0; i < count; ++i) {
 
1253
        int tag = tags[i];
 
1254
        if (tag == 0)
 
1255
            continue;
 
1256
        ASSERT(tag == TRACKING_RECT_TAG);
 
1257
        if (_private != nil) {
 
1258
            _private->trackingRectOwner = nil;
 
1259
        }
 
1260
    }
 
1261
}
 
1262
 
 
1263
- (void)_sendToolTipMouseExited
 
1264
{
 
1265
    // Nothing matters except window, trackingNumber, and userData.
 
1266
    NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
 
1267
        location:NSMakePoint(0, 0)
 
1268
        modifierFlags:0
 
1269
        timestamp:0
 
1270
        windowNumber:[[self window] windowNumber]
 
1271
        context:NULL
 
1272
        eventNumber:0
 
1273
        trackingNumber:TRACKING_RECT_TAG
 
1274
        userData:_private->trackingRectUserData];
 
1275
    [_private->trackingRectOwner mouseExited:fakeEvent];
 
1276
}
 
1277
 
 
1278
- (void)_sendToolTipMouseEntered
 
1279
{
 
1280
    // Nothing matters except window, trackingNumber, and userData.
 
1281
    NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
 
1282
        location:NSMakePoint(0, 0)
 
1283
        modifierFlags:0
 
1284
        timestamp:0
 
1285
        windowNumber:[[self window] windowNumber]
 
1286
        context:NULL
 
1287
        eventNumber:0
 
1288
        trackingNumber:TRACKING_RECT_TAG
 
1289
        userData:_private->trackingRectUserData];
 
1290
    [_private->trackingRectOwner mouseEntered:fakeEvent];
 
1291
}
 
1292
 
 
1293
- (void)_setToolTip:(NSString *)string
 
1294
{
 
1295
    NSString *toolTip = [string length] == 0 ? nil : string;
 
1296
    NSString *oldToolTip = _private->toolTip;
 
1297
    if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
 
1298
        return;
 
1299
    }
 
1300
    if (oldToolTip) {
 
1301
        [self _sendToolTipMouseExited];
 
1302
        [oldToolTip release];
 
1303
    }
 
1304
    _private->toolTip = [toolTip copy];
 
1305
    if (toolTip) {
 
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];
 
1311
    }
 
1312
}
 
1313
 
 
1314
- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
 
1315
{
 
1316
    return [[_private->toolTip copy] autorelease];
 
1317
}
 
1318
 
 
1319
- (void)_updateMouseoverWithEvent:(NSEvent *)event
 
1320
{
 
1321
    if (_private->closed)
 
1322
        return;
 
1323
 
 
1324
    NSView *contentView = [[event window] contentView];
 
1325
    NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
 
1326
    
 
1327
    forceWebHTMLViewHitTest = YES;
 
1328
    NSView *hitView = [contentView hitTest:locationForHitTest];
 
1329
    forceWebHTMLViewHitTest = NO;
 
1330
    
 
1331
    WebHTMLView *view = nil;
 
1332
    if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended])
 
1333
        view = (WebHTMLView *)hitView;    
 
1334
 
 
1335
    if (view)
 
1336
        [view retain];
 
1337
 
 
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;
 
1345
 
 
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);
 
1355
    }
 
1356
 
 
1357
    lastHitView = view;
 
1358
 
 
1359
    if (view) {
 
1360
        if (Frame* coreFrame = core([view _frame]))
 
1361
            coreFrame->eventHandler()->mouseMoved(event);
 
1362
 
 
1363
        [view release];
 
1364
    }
 
1365
}
 
1366
 
 
1367
// keep in sync with WebPasteboardHelper::insertablePasteboardTypes
 
1368
+ (NSArray *)_insertablePasteboardTypes
 
1369
{
 
1370
    static NSArray *types = nil;
 
1371
    if (!types) {
 
1372
        types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType,
 
1373
            NSFilenamesPboardType, NSTIFFPboardType, NSPICTPboardType, NSURLPboardType, 
 
1374
            NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, nil];
 
1375
        CFRetain(types);
 
1376
    }
 
1377
    return types;
 
1378
}
 
1379
 
 
1380
+ (NSArray *)_selectionPasteboardTypes
 
1381
{
 
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];
 
1384
}
 
1385
 
 
1386
- (NSImage *)_dragImageForURL:(NSString*)urlString withLabel:(NSString*)label
 
1387
{
 
1388
    BOOL drawURLString = YES;
 
1389
    BOOL clipURLString = NO, clipLabelString = NO;
 
1390
    
 
1391
    if (!label) {
 
1392
        drawURLString = NO;
 
1393
        label = urlString;
 
1394
    }
 
1395
    
 
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];
 
1399
    NSSize labelSize;
 
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;
 
1405
    }
 
1406
    
 
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;
 
1417
        } else {
 
1418
            imageSize.width = MAX(labelSize.width + DRAG_LABEL_BORDER_X * 2.0f, urlStringSize.width + DRAG_LABEL_BORDER_X * 2.0f);
 
1419
        }
 
1420
    }
 
1421
    NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
 
1422
    [dragImage lockFocus];
 
1423
    
 
1424
    [[NSColor colorWithCalibratedRed: 0.7f green: 0.7f blue: 0.7f alpha: 0.8f] set];
 
1425
    
 
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)];
 
1432
    
 
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)];
 
1436
    [path fill];
 
1437
    
 
1438
    NSColor *topColor = [NSColor colorWithCalibratedWhite:0.0f alpha:0.75f];
 
1439
    NSColor *bottomColor = [NSColor colorWithCalibratedWhite:1.0f alpha:0.5f];
 
1440
    if (drawURLString) {
 
1441
        if (clipURLString)
 
1442
            urlString = [WebStringTruncator centerTruncateString: urlString toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:urlFont];
 
1443
        
 
1444
        [urlString _web_drawDoubledAtPoint:NSMakePoint(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y - [urlFont descender]) 
 
1445
                              withTopColor:topColor bottomColor:bottomColor font:urlFont];
 
1446
    }
 
1447
    
 
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];
 
1452
    
 
1453
    [dragImage unlockFocus];
 
1454
    
 
1455
    return dragImage;
 
1456
}
 
1457
 
 
1458
- (NSImage *)_dragImageForLinkElement:(NSDictionary *)element
 
1459
{
 
1460
    NSURL *linkURL = [element objectForKey: WebElementLinkURLKey];
 
1461
    
 
1462
    NSString *label = [element objectForKey: WebElementLinkLabelKey];
 
1463
    NSString *urlString = [linkURL _web_userVisibleString];
 
1464
    return [self _dragImageForURL:urlString withLabel:label];
 
1465
}
 
1466
 
 
1467
- (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
 
1468
{
 
1469
    [self setPromisedDragTIFFDataSource:0];
 
1470
}
 
1471
 
 
1472
- (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
 
1473
{
 
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]];
 
1477
        [archive release];
 
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];
 
1482
    }
 
1483
}
 
1484
 
 
1485
- (void)_handleAutoscrollForMouseDragged:(NSEvent *)event 
 
1486
 
1487
    [self autoscroll:event]; 
 
1488
    [self _startAutoscrollTimer:event]; 
 
1489
 
1490
 
 
1491
- (WebPluginController *)_pluginController
 
1492
{
 
1493
    return _private->pluginController;
 
1494
}
 
1495
 
 
1496
- (void)_layoutForPrinting
 
1497
{
 
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];
 
1504
}
 
1505
 
 
1506
- (void)_startAutoscrollTimer: (NSEvent *)triggerEvent
 
1507
{
 
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];
 
1512
    }
 
1513
}
 
1514
 
 
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
 
1518
{
 
1519
    return [self selectionRect];
 
1520
}
 
1521
 
 
1522
- (void)_stopAutoscrollTimer
 
1523
{
 
1524
    NSTimer *timer = _private->autoscrollTimer;
 
1525
    _private->autoscrollTimer = nil;
 
1526
    [_private->autoscrollTriggerEvent release];
 
1527
    _private->autoscrollTriggerEvent = nil;
 
1528
    [timer invalidate];
 
1529
    [timer release];
 
1530
}
 
1531
 
 
1532
- (void)_autoscroll
 
1533
{
 
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);   
 
1537
    if (!isStillDown){
 
1538
        [self _stopAutoscrollTimer];
 
1539
        return;
 
1540
    }
 
1541
 
 
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];
 
1550
}
 
1551
 
 
1552
- (BOOL)_canEdit
 
1553
{
 
1554
    Frame* coreFrame = core([self _frame]);
 
1555
    return coreFrame && coreFrame->editor()->canEdit();
 
1556
}
 
1557
 
 
1558
- (BOOL)_canEditRichly
 
1559
{
 
1560
    Frame* coreFrame = core([self _frame]);
 
1561
    return coreFrame && coreFrame->editor()->canEditRichly();
 
1562
}
 
1563
 
 
1564
- (BOOL)_canAlterCurrentSelection
 
1565
{
 
1566
    return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
 
1567
}
 
1568
 
 
1569
- (BOOL)_hasSelection
 
1570
{
 
1571
    Frame* coreFrame = core([self _frame]);
 
1572
    return coreFrame && coreFrame->selectionController()->isRange();
 
1573
}
 
1574
 
 
1575
- (BOOL)_hasSelectionOrInsertionPoint
 
1576
{
 
1577
    Frame* coreFrame = core([self _frame]);
 
1578
    return coreFrame && coreFrame->selectionController()->isCaretOrRange();
 
1579
}
 
1580
 
 
1581
- (BOOL)_hasInsertionPoint
 
1582
{
 
1583
    Frame* coreFrame = core([self _frame]);
 
1584
    return coreFrame && coreFrame->selectionController()->isCaret();
 
1585
}
 
1586
 
 
1587
- (BOOL)_isEditable
 
1588
{
 
1589
    Frame* coreFrame = core([self _frame]);
 
1590
    return coreFrame && coreFrame->selectionController()->isContentEditable();
 
1591
}
 
1592
 
 
1593
- (BOOL)_transparentBackground
 
1594
{
 
1595
    return _private->transparentBackground;
 
1596
}
 
1597
 
 
1598
- (void)_setTransparentBackground:(BOOL)f
 
1599
{
 
1600
    _private->transparentBackground = f;
 
1601
}
 
1602
 
 
1603
- (NSImage *)_selectionDraggingImage
 
1604
{
 
1605
    if ([self _hasSelection]) {
 
1606
        NSImage *dragImage = core([self _frame])->selectionImage();
 
1607
        [dragImage _web_dissolveToFraction:WebDragImageAlpha];
 
1608
        return dragImage;
 
1609
    }
 
1610
    return nil;
 
1611
}
 
1612
 
 
1613
- (NSRect)_selectionDraggingRect
 
1614
{
 
1615
    // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
 
1616
    return [self selectionRect];
 
1617
}
 
1618
 
 
1619
- (DOMNode *)_insertOrderedList
 
1620
{
 
1621
    Frame* coreFrame = core([self _frame]);
 
1622
    return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil;
 
1623
}
 
1624
 
 
1625
- (DOMNode *)_insertUnorderedList
 
1626
{
 
1627
    Frame* coreFrame = core([self _frame]);
 
1628
    return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil;
 
1629
}
 
1630
 
 
1631
- (BOOL)_canIncreaseSelectionListLevel
 
1632
{
 
1633
    Frame* coreFrame = core([self _frame]);
 
1634
    return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel();
 
1635
}
 
1636
 
 
1637
- (BOOL)_canDecreaseSelectionListLevel
 
1638
{
 
1639
    Frame* coreFrame = core([self _frame]);
 
1640
    return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel();
 
1641
}
 
1642
 
 
1643
- (DOMNode *)_increaseSelectionListLevel
 
1644
{
 
1645
    Frame* coreFrame = core([self _frame]);
 
1646
    return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil;
 
1647
}
 
1648
 
 
1649
- (DOMNode *)_increaseSelectionListLevelOrdered
 
1650
{
 
1651
    Frame* coreFrame = core([self _frame]);
 
1652
    return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil;
 
1653
}
 
1654
 
 
1655
- (DOMNode *)_increaseSelectionListLevelUnordered
 
1656
{
 
1657
    Frame* coreFrame = core([self _frame]);
 
1658
    return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil;
 
1659
}
 
1660
 
 
1661
- (void)_decreaseSelectionListLevel
 
1662
{
 
1663
    Frame* coreFrame = core([self _frame]);
 
1664
    if (coreFrame)
 
1665
        coreFrame->editor()->decreaseSelectionListLevel();
 
1666
}
 
1667
 
 
1668
- (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
 
1669
{
 
1670
    if (!_private->highlighters)
 
1671
        _private->highlighters = [[NSMutableDictionary alloc] init];
 
1672
    [_private->highlighters setObject:highlighter forKey:type];
 
1673
}
 
1674
 
 
1675
- (void)_removeHighlighterOfType:(NSString*)type
 
1676
{
 
1677
    [_private->highlighters removeObjectForKey:type];
 
1678
}
 
1679
 
 
1680
- (BOOL)_web_firstResponderCausesFocusDisplay
 
1681
{
 
1682
    return [[self window] firstResponder] == self || [[self window] firstResponder] == [self _frameView];
 
1683
}
 
1684
 
 
1685
- (void)_updateActiveState
 
1686
{
 
1687
    [self _cancelUpdateActiveStateTimer];
 
1688
 
 
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. 
 
1692
    // 
 
1693
    // The four display attributes are as follows:
 
1694
    // 
 
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
 
1699
    //
 
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.
 
1702
 
 
1703
    Frame* frame = core([self _frame]);
 
1704
    if (!frame)
 
1705
        return;
 
1706
    
 
1707
    Page* page = frame->page();
 
1708
    if (!page)
 
1709
        return;
 
1710
 
 
1711
    NSWindow *window = [self window];
 
1712
    BOOL windowIsKey = [window isKeyWindow];
 
1713
    BOOL windowOrSheetIsKey = windowIsKey || [[window attachedSheet] isKeyWindow];
 
1714
 
 
1715
    BOOL isActive = !_private->resigningFirstResponder && windowIsKey && [self _web_firstResponderCausesFocusDisplay];
 
1716
    frame->setIsActive(isActive);            
 
1717
 
 
1718
    Frame* focusedFrame = page->focusController()->focusedOrMainFrame();
 
1719
    frame->setWindowHasFocus(frame == focusedFrame && windowOrSheetIsKey);
 
1720
}
 
1721
 
 
1722
- (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
 
1723
{
 
1724
    ASSERT([self _hasSelection]);
 
1725
    NSArray *types = [self pasteboardTypesForSelection];
 
1726
 
 
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;
 
1734
    }
 
1735
 
 
1736
    [pasteboard declareTypes:types owner:[self _topHTMLView]];
 
1737
    [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
 
1738
    [mutableTypes release];
 
1739
}
 
1740
 
 
1741
- (void)close
 
1742
{
 
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)
 
1746
        return;
 
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];
 
1754
    [_private clear];
 
1755
    _private->closed = YES;
 
1756
    Page* page = core([self _webView]);
 
1757
    if (page)
 
1758
        page->dragController()->setDraggingImageURL(KURL());
 
1759
}
 
1760
 
 
1761
- (BOOL)_hasHTMLDocument
 
1762
{
 
1763
    Frame* coreFrame = core([self _frame]);
 
1764
    if (!coreFrame)
 
1765
        return NO;
 
1766
    Document* document = coreFrame->document();
 
1767
    return document && document->isHTMLDocument();
 
1768
}
 
1769
 
 
1770
- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
 
1771
                                                 forType:(NSString *)pboardType
 
1772
                                               inContext:(DOMRange *)context
 
1773
                                            subresources:(NSArray **)subresources
 
1774
{
 
1775
    if (pboardType == WebArchivePboardType) {
 
1776
        WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
 
1777
        if (subresources)
 
1778
            *subresources = [archive subresources];
 
1779
        DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
 
1780
        [archive release];
 
1781
        return fragment;
 
1782
    }
 
1783
    if (pboardType == NSFilenamesPboardType)
 
1784
        return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
 
1785
        
 
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];
 
1793
        }
 
1794
        if ([HTMLString length] == 0)
 
1795
            return nil;
 
1796
        
 
1797
        return [[self _bridge] documentFragmentWithMarkupString:HTMLString baseURLString:nil];
 
1798
    }
 
1799
 
 
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];
 
1807
        if (string == nil)
 
1808
            string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
 
1809
        if (string == nil)
 
1810
            return nil;
 
1811
            
 
1812
        NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
 
1813
            [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
 
1814
            self, @"WebResourceHandler", nil];
 
1815
        NSArray *s;
 
1816
        
 
1817
        BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks];
 
1818
        if (!wasDeferringCallbacks)
 
1819
            [[self _webView] setDefersCallbacks:YES];
 
1820
            
 
1821
        DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length]) 
 
1822
                                                          document:[[self _frame] DOMDocument] 
 
1823
                                                documentAttributes:documentAttributes
 
1824
                                                      subresources:&s];
 
1825
        if (subresources)
 
1826
            *subresources = s;
 
1827
        
 
1828
        NSEnumerator *e = [s objectEnumerator];
 
1829
        WebResource *r;
 
1830
        while ((r = [e nextObject]))
 
1831
            [[self _dataSource] addSubresource:r];
 
1832
        
 
1833
        if (!wasDeferringCallbacks)
 
1834
            [[self _webView] setDefersCallbacks:NO];
 
1835
        
 
1836
        [documentAttributes release];
 
1837
        [string release];
 
1838
        return fragment;
 
1839
    }
 
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
 
1845
                                                        frameName:nil];
 
1846
        DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
 
1847
        [resource release];
 
1848
        return fragment;
 
1849
    }
 
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
 
1855
                                                        frameName:nil];
 
1856
        DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
 
1857
        [resource release];
 
1858
        return fragment;
 
1859
    }
 
1860
    if (pboardType == NSURLPboardType) {
 
1861
        NSURL *URL = [NSURL URLFromPasteboard:pasteboard];
 
1862
        DOMDocument* document = [[self _frame] DOMDocument];
 
1863
        ASSERT(document);
 
1864
        if (!document)
 
1865
            return nil;
 
1866
        DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"];
 
1867
        NSString *URLString = [URL _web_originalDataAsString];
 
1868
        if ([URLString length] == 0)
 
1869
            return nil;
 
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];
 
1876
        return fragment;
 
1877
    }
 
1878
    if (pboardType == NSStringPboardType)
 
1879
        return [[self _bridge] documentFragmentWithText:[pasteboard stringForType:NSStringPboardType]
 
1880
                                              inContext:context];
 
1881
                                              
 
1882
    return nil;
 
1883
}
 
1884
 
 
1885
@end
 
1886
 
 
1887
@implementation NSView (WebHTMLViewFileInternal)
 
1888
 
 
1889
- (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
 
1890
{
 
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];
 
1897
    }
 
1898
}
 
1899
 
 
1900
@end
 
1901
 
 
1902
@implementation NSMutableDictionary (WebHTMLViewFileInternal)
 
1903
 
 
1904
- (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
 
1905
{
 
1906
    if (object == nil) {
 
1907
        [self removeObjectForKey:key];
 
1908
    } else {
 
1909
        [self setObject:object forKey:key];
 
1910
    }
 
1911
}
 
1912
 
 
1913
@end
 
1914
 
 
1915
#ifdef BUILDING_ON_TIGER
 
1916
 
 
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.
 
1922
 
 
1923
@interface NSToolTipPanel : NSPanel
 
1924
@end
 
1925
 
 
1926
@interface NSToolTipPanel (WebHTMLViewFileInternal)
 
1927
@end
 
1928
 
 
1929
@implementation NSToolTipPanel (WebHTMLViewFileInternal)
 
1930
 
 
1931
- (void)setAcceptsMouseMovedEvents:(BOOL)flag
 
1932
{
 
1933
    // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
 
1934
}
 
1935
 
 
1936
@end
 
1937
 
 
1938
#endif
 
1939
 
 
1940
@interface NSArray (WebHTMLView)
 
1941
- (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object;
 
1942
@end
 
1943
 
 
1944
@implementation WebHTMLView
 
1945
 
 
1946
+ (void)initialize
 
1947
{
 
1948
    [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes] 
 
1949
                             returnTypes:[[self class] _insertablePasteboardTypes]];
 
1950
    _NSInitializeKillRing();
 
1951
#ifndef BUILDING_ON_TIGER
 
1952
    WebCoreObjCFinalizeOnMainThread(self);
 
1953
#endif
 
1954
}
 
1955
 
 
1956
- (id)initWithFrame:(NSRect)frame
 
1957
{
 
1958
    self = [super initWithFrame:frame];
 
1959
    if (!self)
 
1960
        return nil;
 
1961
    
 
1962
    [self setFocusRingType:NSFocusRingTypeNone];
 
1963
    
 
1964
    // Make all drawing go through us instead of subviews.
 
1965
    [self _setDrawsOwnDescendants:YES];
 
1966
    
 
1967
    _private = [[WebHTMLViewPrivate alloc] init];
 
1968
 
 
1969
    _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
 
1970
    _private->needsLayout = YES;
 
1971
    
 
1972
    return self;
 
1973
}
 
1974
 
 
1975
- (void)dealloc
 
1976
{
 
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.
 
1980
    [self close];
 
1981
    [_private release];
 
1982
    _private = nil;
 
1983
    [super dealloc];
 
1984
}
 
1985
 
 
1986
- (void)finalize
 
1987
{
 
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.
 
1992
    [self close];
 
1993
    [super finalize];
 
1994
}
 
1995
 
 
1996
// Returns YES if the delegate returns YES (so we should do no more work).
 
1997
- (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector
 
1998
{
 
1999
    BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector;
 
2000
    _private->selectorForDoCommandBySelector = 0;
 
2001
    if (callerAlreadyCalledDelegate)
 
2002
        return NO;
 
2003
 
 
2004
    WebView *webView = [self _webView];
 
2005
    id editingDelegate = [webView editingDelegate];
 
2006
    if (![editingDelegate respondsToSelector:@selector(webView:doCommandBySelector:)])
 
2007
        return NO;
 
2008
 
 
2009
    return [editingDelegate webView:webView doCommandBySelector:selector];
 
2010
}
 
2011
 
 
2012
- (void)callWebCoreCommand:(SEL)selector
 
2013
{
 
2014
    if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
 
2015
        return;
 
2016
 
 
2017
    Frame* coreFrame = core([self _frame]);
 
2018
    if (!coreFrame)
 
2019
        return;
 
2020
 
 
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;
 
2031
 
 
2032
    coreFrame->editor()->execCommand(commandName.data());
 
2033
}
 
2034
 
 
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.
 
2039
 
 
2040
#define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self callWebCoreCommand:_cmd]; }
 
2041
 
 
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)
 
2086
 
 
2087
#undef WEBCORE_COMMAND
 
2088
 
 
2089
#define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return;
 
2090
 
 
2091
- (IBAction)takeFindStringFromSelection:(id)sender
 
2092
{
 
2093
    COMMAND_PROLOGUE
 
2094
 
 
2095
    if (![self _hasSelection]) {
 
2096
        NSBeep();
 
2097
        return;
 
2098
    }
 
2099
 
 
2100
    [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
 
2101
}
 
2102
 
 
2103
- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
 
2104
{
 
2105
    [pasteboard declareTypes:types owner:[self _topHTMLView]];
 
2106
    [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
 
2107
    return YES;
 
2108
}
 
2109
 
 
2110
- (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
 
2111
{
 
2112
    Frame* coreFrame = core([self _frame]);
 
2113
    if (!coreFrame)
 
2114
        return NO;
 
2115
    if (coreFrame->selectionController()->isContentRichlyEditable())
 
2116
        [self _pasteWithPasteboard:pasteboard allowPlainText:YES];
 
2117
    else
 
2118
        [self _pasteAsPlainTextWithPasteboard:pasteboard];
 
2119
    return YES;
 
2120
}
 
2121
 
 
2122
- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
 
2123
{
 
2124
    if (sendType != nil && [[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]) {
 
2125
        return self;
 
2126
    } else if (returnType != nil && [[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) {
 
2127
        return self;
 
2128
    }
 
2129
    return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
 
2130
}
 
2131
 
 
2132
- (void)selectAll:(id)sender
 
2133
{
 
2134
    COMMAND_PROLOGUE
 
2135
 
 
2136
    [self selectAll];
 
2137
}
 
2138
 
 
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
 
2145
{
 
2146
    COMMAND_PROLOGUE
 
2147
 
 
2148
    if (Frame* coreFrame = core([self _frame]))
 
2149
        coreFrame->revealSelection(RenderLayer::gAlignCenterAlways);
 
2150
}
 
2151
 
 
2152
- (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
 
2153
{
 
2154
    SEL action = [item action];
 
2155
    Frame* frame = core([self _frame]);
 
2156
 
 
2157
    if (Document* doc = frame->document()) {
 
2158
        if (doc->isPluginDocument())
 
2159
            return NO;
 
2160
        
 
2161
        if (doc->isImageDocument()) {            
 
2162
            if (action == @selector(copy:))
 
2163
                return frame->loader()->isComplete();
 
2164
        
 
2165
            return NO;
 
2166
        }
 
2167
    }
 
2168
    
 
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];
 
2237
    
 
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")];
 
2245
        }
 
2246
#endif
 
2247
        return [self _canEdit];
 
2248
    }
 
2249
    
 
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];
 
2256
                return NO;
 
2257
            }
 
2258
            DOMCSSStyleDeclaration* style = [self _emptyStyle];
 
2259
            [style setDirection:writingDirection == NSWritingDirectionLeftToRight ? @"LTR" : @"RTL"];
 
2260
            [menuItem setState:[[self _bridge] selectionHasStyle:style]];
 
2261
        }
 
2262
        return [self _canEdit];
 
2263
    }
 
2264
    
 
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")];
 
2273
        }
 
2274
        return [self _canEdit];
 
2275
    } 
 
2276
    
 
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];
 
2287
    
 
2288
    if (action == @selector(capitalizeWord:)
 
2289
               || action == @selector(lowercaseWord:)
 
2290
               || action == @selector(uppercaseWord:))
 
2291
        return [self _hasSelection] && [self _isEditable];
 
2292
    
 
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]);
 
2298
    
 
2299
    if (action == @selector(changeDocumentBackgroundColor:))
 
2300
        return [[self _webView] isEditable] && [self _canEditRichly];
 
2301
    
 
2302
    if (action == @selector(copy:))
 
2303
        return (frame && frame->editor()->canDHTMLCopy()) || frame->editor()->canCopy();
 
2304
    
 
2305
    if (action == @selector(cut:))
 
2306
        return (frame && frame->editor()->canDHTMLCut()) || frame->editor()->canCut();
 
2307
    
 
2308
    if (action == @selector(delete:))
 
2309
        return (frame && frame->editor()->canDelete());
 
2310
    
 
2311
    if (action == @selector(_ignoreSpellingFromMenu:)
 
2312
            || action == @selector(_learnSpellingFromMenu:)
 
2313
            || action == @selector(takeFindStringFromSelection:))
 
2314
        return [self _hasSelection];
 
2315
    
 
2316
    if (action == @selector(paste:) || action == @selector(pasteAsPlainText:))
 
2317
        return (frame && frame->editor()->canDHTMLPaste()) || frame->editor()->canPaste();
 
2318
    
 
2319
    if (action == @selector(pasteAsRichText:))
 
2320
        return frame && (frame->editor()->canDHTMLPaste()
 
2321
            || (frame->editor()->canPaste() && frame->selectionController()->isContentRichlyEditable()));
 
2322
    
 
2323
    if (action == @selector(performFindPanelAction:))
 
2324
        // FIXME: Not yet implemented.
 
2325
        return NO;
 
2326
    
 
2327
    if (action == @selector(selectToMark:)
 
2328
            || action == @selector(swapWithMark:))
 
2329
        return [self _hasSelectionOrInsertionPoint] && [[self _bridge] markDOMRange] != nil;
 
2330
    
 
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]];
 
2337
        }
 
2338
        return [self _canEditRichly];
 
2339
    } 
 
2340
    
 
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]];
 
2347
        }
 
2348
        return [self _canEditRichly];
 
2349
    } 
 
2350
    
 
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]];
 
2357
        }
 
2358
        return [self _canEditRichly];
 
2359
    } 
 
2360
    
 
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]];
 
2367
        }
 
2368
        return [self _canEditRichly];
 
2369
    } 
 
2370
    
 
2371
    if (action == @selector(_lookUpInDictionaryFromMenu:)) {
 
2372
        return [self _hasSelection];
 
2373
    } 
 
2374
    
 
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];
 
2384
        }
 
2385
        return YES;
 
2386
    }
 
2387
#endif
 
2388
    
 
2389
    return YES;
 
2390
}
 
2391
 
 
2392
- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
 
2393
{
 
2394
    BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
 
2395
 
 
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];
 
2399
 
 
2400
    return result;
 
2401
}
 
2402
 
 
2403
- (BOOL)acceptsFirstResponder
 
2404
{
 
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])) {
 
2413
        return NO;
 
2414
    }
 
2415
    return YES;
 
2416
}
 
2417
 
 
2418
- (BOOL)maintainsInactiveSelection
 
2419
{
 
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.
 
2428
 
 
2429
    if ([[self _webView] maintainsInactiveSelection])
 
2430
        return YES;
 
2431
 
 
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];
 
2437
        if (contentView)
 
2438
            nextResponder = contentView;
 
2439
    }
 
2440
    if ([nextResponder isKindOfClass:[NSClipView class]]) {
 
2441
        id documentView = [nextResponder documentView];
 
2442
        if (documentView)
 
2443
            nextResponder = documentView;
 
2444
    }
 
2445
    if (nextResponder == self)
 
2446
        return YES;
 
2447
 
 
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]];
 
2452
 
 
2453
    return selectionIsEditable && nextResponderIsInWebView;
 
2454
}
 
2455
 
 
2456
- (void)addMouseMovedObserver
 
2457
{
 
2458
    if (!_private->dataSource || ![self _isTopHTMLView])
 
2459
        return;
 
2460
 
 
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]))
 
2463
        return;
 
2464
 
 
2465
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
 
2466
        name:WKMouseMovedNotification() object:nil];
 
2467
    [self _frameOrBoundsChanged];
 
2468
}
 
2469
 
 
2470
- (void)removeMouseMovedObserverUnconditionally
 
2471
{
 
2472
    [[NSNotificationCenter defaultCenter] removeObserver:self
 
2473
        name:WKMouseMovedNotification() object:nil];
 
2474
}
 
2475
 
 
2476
- (void)removeMouseMovedObserver
 
2477
{
 
2478
    // Don't remove the observer if we're running the Dashboard.
 
2479
    if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
 
2480
        return;
 
2481
 
 
2482
    [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
 
2483
    [self removeMouseMovedObserverUnconditionally];
 
2484
}
 
2485
 
 
2486
- (void)addSuperviewObservers
 
2487
{
 
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.
 
2491
    
 
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.
 
2495
    
 
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];
 
2502
 
 
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];
 
2509
    }
 
2510
}
 
2511
 
 
2512
- (void)removeSuperviewObservers
 
2513
{
 
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];
 
2520
    }
 
2521
}
 
2522
 
 
2523
- (void)addWindowObservers
 
2524
{
 
2525
    NSWindow *window = [self window];
 
2526
    if (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];
 
2533
    }
 
2534
}
 
2535
 
 
2536
- (void)removeWindowObservers
 
2537
{
 
2538
    NSWindow *window = [self window];
 
2539
    if (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];
 
2546
    }
 
2547
}
 
2548
 
 
2549
- (void)viewWillMoveToSuperview:(NSView *)newSuperview
 
2550
{
 
2551
    [self removeSuperviewObservers];
 
2552
}
 
2553
 
 
2554
- (void)viewDidMoveToSuperview
 
2555
{
 
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];
 
2561
    }
 
2562
}
 
2563
 
 
2564
static void _updateActiveStateTimerCallback(CFRunLoopTimerRef timer, void *info)
 
2565
{
 
2566
    WebHTMLView *view = (WebHTMLView *)info;
 
2567
    [view _updateActiveState];
 
2568
}
 
2569
 
 
2570
- (void)viewWillMoveToWindow:(NSWindow *)window
 
2571
{
 
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.
 
2576
    if (!_private)
 
2577
        return;
 
2578
 
 
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];
 
2585
    
 
2586
    [[self _pluginController] stopAllPlugins];
 
2587
}
 
2588
 
 
2589
- (void)viewDidMoveToWindow
 
2590
{
 
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.
 
2595
    if (!_private)
 
2596
        return;
 
2597
        
 
2598
    [self _stopAutoscrollTimer];
 
2599
    if ([self window]) {
 
2600
        _private->lastScrollPosition = [[self superview] bounds].origin;
 
2601
        [self addWindowObservers];
 
2602
        [self addSuperviewObservers];
 
2603
        [self addMouseMovedObserver];
 
2604
 
 
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);
 
2616
        }
 
2617
        
 
2618
        [[self _pluginController] startAllPlugins];
 
2619
 
 
2620
        _private->lastScrollPosition = NSZeroPoint;
 
2621
    }
 
2622
}
 
2623
 
 
2624
- (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
 
2625
{
 
2626
    [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
 
2627
}
 
2628
 
 
2629
- (void)viewDidMoveToHostWindow
 
2630
{
 
2631
    [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
 
2632
}
 
2633
 
 
2634
 
 
2635
- (void)addSubview:(NSView *)view
 
2636
{
 
2637
    [super addSubview:view];
 
2638
 
 
2639
    if ([WebPluginController isPlugInView:view])
 
2640
        [[self _pluginController] addPlugin:view];
 
2641
}
 
2642
 
 
2643
- (void)willRemoveSubview:(NSView *)subview
 
2644
{
 
2645
    if ([WebPluginController isPlugInView:subview])
 
2646
        [[self _pluginController] destroyPlugin:subview];
 
2647
 
 
2648
    [super willRemoveSubview:subview];
 
2649
}
 
2650
 
 
2651
- (void)reapplyStyles
 
2652
{
 
2653
    if (!_private->needsToApplyStyles) {
 
2654
        return;
 
2655
    }
 
2656
    
 
2657
#ifdef _KWQ_TIMING        
 
2658
    double start = CFAbsoluteTimeGetCurrent();
 
2659
#endif
 
2660
 
 
2661
    [[self _bridge] reapplyStylesForDeviceType:
 
2662
        _private->printing ? WebCoreDevicePrinter : WebCoreDeviceScreen];
 
2663
    
 
2664
#ifdef _KWQ_TIMING        
 
2665
    double thisTime = CFAbsoluteTimeGetCurrent() - start;
 
2666
    LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
 
2667
#endif
 
2668
 
 
2669
    _private->needsToApplyStyles = NO;
 
2670
}
 
2671
 
 
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
 
2675
{
 
2676
    [self reapplyStyles];
 
2677
    
 
2678
    if (!_private->needsLayout && ![[self _bridge] needsLayout])
 
2679
        return;
 
2680
 
 
2681
#ifdef _KWQ_TIMING        
 
2682
    double start = CFAbsoluteTimeGetCurrent();
 
2683
#endif
 
2684
 
 
2685
    LOG(View, "%@ doing layout", self);
 
2686
 
 
2687
    if (minPageWidth > 0.0) {
 
2688
        [[self _bridge] forceLayoutWithMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
 
2689
    } else {
 
2690
        [[self _bridge] forceLayoutAdjustingViewSize:adjustViewSize];
 
2691
    }
 
2692
    _private->needsLayout = NO;
 
2693
    
 
2694
    if (!_private->printing) {
 
2695
        // get size of the containing dynamic scrollview, so
 
2696
        // appearance and disappearance of scrollbars will not show up
 
2697
        // as a size change
 
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];
 
2703
        }
 
2704
        _private->laidOutAtLeastOnce = YES;
 
2705
        _private->lastLayoutSize = [(NSClipView *)[self superview] documentVisibleRect].size;
 
2706
        _private->lastLayoutFrameSize = newLayoutFrameSize;
 
2707
    }
 
2708
 
 
2709
#ifdef _KWQ_TIMING        
 
2710
    double thisTime = CFAbsoluteTimeGetCurrent() - start;
 
2711
    LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
 
2712
#endif
 
2713
}
 
2714
 
 
2715
- (void)layout
 
2716
{
 
2717
    [self layoutToMinimumPageWidth:0.0f maximumPageWidth:0.0f adjustingViewSize:NO];
 
2718
}
 
2719
 
 
2720
- (NSMenu *)menuForEvent:(NSEvent *)event
 
2721
{
 
2722
    [_private->compController endRevertingChange:NO moveLeft:NO];
 
2723
 
 
2724
    _private->handlingMouseDownEvent = YES;
 
2725
    BOOL handledEvent = NO;
 
2726
    Frame* coreFrame = core([self _frame]);
 
2727
 
 
2728
    if (!coreFrame) {
 
2729
        _private->handlingMouseDownEvent = NO;
 
2730
        return nil;
 
2731
    }
 
2732
 
 
2733
    Page* page = coreFrame->page();
 
2734
    if (!page)
 
2735
        return nil;
 
2736
 
 
2737
    page->contextMenuController()->clearContextMenu();
 
2738
    handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(PlatformMouseEvent(event));
 
2739
    _private->handlingMouseDownEvent = NO;
 
2740
 
 
2741
    if (!handledEvent)
 
2742
        return nil;
 
2743
 
 
2744
    ContextMenu* coreMenu = page->contextMenuController()->contextMenu();
 
2745
    if (!coreMenu)
 
2746
        return nil;
 
2747
 
 
2748
    NSArray* menuItems = coreMenu->platformDescription();
 
2749
    NSMenu* menu = nil;
 
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]];
 
2754
    }
 
2755
 
 
2756
    return menu;
 
2757
}
 
2758
 
 
2759
- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
 
2760
{
 
2761
    return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
 
2762
}
 
2763
 
 
2764
- (void)clearFocus
 
2765
{
 
2766
    Frame* coreFrame = core([self _frame]);
 
2767
    if (!coreFrame)
 
2768
        return;
 
2769
    Document* document = coreFrame->document();
 
2770
    if (!document)
 
2771
        return;
 
2772
    
 
2773
    document->setFocusedNode(0);
 
2774
}
 
2775
 
 
2776
- (BOOL)isOpaque
 
2777
{
 
2778
    return [[self _webView] drawsBackground];
 
2779
}
 
2780
 
 
2781
- (void)setNeedsDisplay:(BOOL)flag
 
2782
{
 
2783
    LOG(View, "%@ flag = %d", self, (int)flag);
 
2784
    [super setNeedsDisplay: flag];
 
2785
}
 
2786
 
 
2787
- (void)setNeedsLayout: (BOOL)flag
 
2788
{
 
2789
    LOG(View, "%@ flag = %d", self, (int)flag);
 
2790
    _private->needsLayout = flag;
 
2791
}
 
2792
 
 
2793
 
 
2794
- (void)setNeedsToApplyStyles: (BOOL)flag
 
2795
{
 
2796
    LOG(View, "%@ flag = %d", self, (int)flag);
 
2797
    _private->needsToApplyStyles = flag;
 
2798
}
 
2799
 
 
2800
- (void)drawSingleRect:(NSRect)rect
 
2801
{
 
2802
    [NSGraphicsContext saveGraphicsState];
 
2803
    NSRectClip(rect);
 
2804
        
 
2805
    ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
 
2806
 
 
2807
    [(WebClipView *)[self superview] setAdditionalClip:rect];
 
2808
 
 
2809
    NS_DURING {
 
2810
        if ([self _transparentBackground]) {
 
2811
            [[NSColor clearColor] set];
 
2812
            NSRectFill (rect);
 
2813
        }
 
2814
 
 
2815
        [[self _bridge] drawRect:rect];
 
2816
 
 
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]];
 
2824
        }
 
2825
 
 
2826
        [(WebClipView *)[self superview] resetAdditionalClip];
 
2827
 
 
2828
        [NSGraphicsContext restoreGraphicsState];
 
2829
    } NS_HANDLER {
 
2830
        [(WebClipView *)[self superview] resetAdditionalClip];
 
2831
        [NSGraphicsContext restoreGraphicsState];
 
2832
        LOG_ERROR("Exception caught while drawing: %@", localException);
 
2833
        [localException raise];
 
2834
    } NS_ENDHANDLER
 
2835
}
 
2836
 
 
2837
- (void)drawRect:(NSRect)rect
 
2838
{
 
2839
    ASSERT_MAIN_THREAD();
 
2840
    LOG(View, "%@ drawing", self);
 
2841
 
 
2842
    const NSRect *rects;
 
2843
    NSInteger count;
 
2844
    [self getRectsBeingDrawn:&rects count:&count];
 
2845
 
 
2846
    BOOL subviewsWereSetAside = _private->subviewsSetAside;
 
2847
    if (subviewsWereSetAside)
 
2848
        [self _restoreSubviews];
 
2849
 
 
2850
#ifdef _KWQ_TIMING
 
2851
    double start = CFAbsoluteTimeGetCurrent();
 
2852
#endif
 
2853
 
 
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;
 
2872
    }
 
2873
    
 
2874
    if (useUnionedRect)
 
2875
        [self drawSingleRect:rect];
 
2876
    else
 
2877
        for (int i = 0; i < count; ++i)
 
2878
            [self drawSingleRect:rects[i]];
 
2879
 
 
2880
#ifdef _KWQ_TIMING
 
2881
    double thisTime = CFAbsoluteTimeGetCurrent() - start;
 
2882
    LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
 
2883
#endif
 
2884
 
 
2885
    if (subviewsWereSetAside)
 
2886
        [self _setAsideSubviews];
 
2887
}
 
2888
 
 
2889
// Turn off the additional clip while computing our visibleRect.
 
2890
- (NSRect)visibleRect
 
2891
{
 
2892
    if (!([[self superview] isKindOfClass:[WebClipView class]]))
 
2893
        return [super visibleRect];
 
2894
        
 
2895
    WebClipView *clipView = (WebClipView *)[self superview];
 
2896
 
 
2897
    BOOL hasAdditionalClip = [clipView hasAdditionalClip];
 
2898
    if (!hasAdditionalClip) {
 
2899
        return [super visibleRect];
 
2900
    }
 
2901
    
 
2902
    NSRect additionalClip = [clipView additionalClip];
 
2903
    [clipView resetAdditionalClip];
 
2904
    NSRect visibleRect = [super visibleRect];
 
2905
    [clipView setAdditionalClip:additionalClip];
 
2906
    return visibleRect;
 
2907
}
 
2908
 
 
2909
- (BOOL)isFlipped 
 
2910
{
 
2911
    return YES;
 
2912
}
 
2913
 
 
2914
- (void)windowDidBecomeKey:(NSNotification *)notification
 
2915
{
 
2916
    NSWindow *keyWindow = [notification object];
 
2917
 
 
2918
    if (keyWindow == [self window])
 
2919
        [self addMouseMovedObserver];
 
2920
 
 
2921
    if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
 
2922
        [self _updateActiveState];
 
2923
}
 
2924
 
 
2925
- (void)windowDidResignKey:(NSNotification *)notification
 
2926
{
 
2927
    NSWindow *formerKeyWindow = [notification object];
 
2928
 
 
2929
    if (formerKeyWindow == [self window])
 
2930
        [self removeMouseMovedObserver];
 
2931
 
 
2932
    if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
 
2933
        [self _updateActiveState];
 
2934
        [_private->compController endRevertingChange:NO moveLeft:NO];
 
2935
    }
 
2936
}
 
2937
 
 
2938
- (void)windowWillClose:(NSNotification *)notification
 
2939
{
 
2940
    [_private->compController endRevertingChange:NO moveLeft:NO];
 
2941
    [[self _pluginController] destroyAllPlugins];
 
2942
}
 
2943
 
 
2944
- (void)scrollWheel:(NSEvent *)event
 
2945
{
 
2946
    [self retain];
 
2947
    Frame* frame = core([self _frame]);
 
2948
    if (!frame || !frame->eventHandler()->wheelEvent(event))
 
2949
        [[self nextResponder] scrollWheel:event];    
 
2950
    [self release];
 
2951
}
 
2952
 
 
2953
- (BOOL)_isSelectionEvent:(NSEvent *)event
 
2954
{
 
2955
    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
 
2956
    return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
 
2957
}
 
2958
 
 
2959
- (BOOL)acceptsFirstMouse:(NSEvent *)event
 
2960
{
 
2961
    NSView *hitView = [self _hitViewForEvent:event];
 
2962
    WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
 
2963
    
 
2964
    if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
 
2965
        return YES;
 
2966
    
 
2967
    if (hitHTMLView) {
 
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];
 
2975
        }
 
2976
        return result;
 
2977
    }
 
2978
    return [hitView acceptsFirstMouse:event];
 
2979
}
 
2980
 
 
2981
- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
 
2982
{
 
2983
    NSView *hitView = [self _hitViewForEvent:event];
 
2984
    WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
 
2985
    if (hitHTMLView) {
 
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];
 
2992
            }
 
2993
        return result;
 
2994
    }
 
2995
    return [hitView shouldDelayWindowOrderingForEvent:event];
 
2996
}
 
2997
 
 
2998
- (void)mouseDown:(NSEvent *)event
 
2999
{
 
3000
    [self retain];
 
3001
 
 
3002
    _private->handlingMouseDownEvent = YES;
 
3003
 
 
3004
    // Record the mouse down position so we can determine drag hysteresis.
 
3005
    [self _setMouseDownEvent:event];
 
3006
 
 
3007
    NSInputManager *currentInputManager = [NSInputManager currentInputManager];
 
3008
    if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
 
3009
        goto done;
 
3010
 
 
3011
    [_private->compController endRevertingChange:NO moveLeft:NO];
 
3012
 
 
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;
 
3017
 
 
3018
        // Don't do any mouseover while the mouse is down.
 
3019
        [self _cancelUpdateMouseoverTimer];
 
3020
 
 
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);
 
3025
    }
 
3026
 
 
3027
done:
 
3028
    [_private->firstResponderTextViewAtMouseDownTime release];
 
3029
    _private->firstResponderTextViewAtMouseDownTime = nil;
 
3030
 
 
3031
    _private->handlingMouseDownEvent = NO;
 
3032
    
 
3033
    [self release];
 
3034
}
 
3035
 
 
3036
- (void)dragImage:(NSImage *)dragImage
 
3037
               at:(NSPoint)at
 
3038
           offset:(NSSize)offset
 
3039
            event:(NSEvent *)event
 
3040
       pasteboard:(NSPasteboard *)pasteboard
 
3041
           source:(id)source
 
3042
        slideBack:(BOOL)slideBack
 
3043
{
 
3044
    ASSERT(self == [self _topHTMLView]);
 
3045
    [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
 
3046
}
 
3047
 
 
3048
- (void)mouseDragged:(NSEvent *)event
 
3049
{
 
3050
    NSInputManager *currentInputManager = [NSInputManager currentInputManager];
 
3051
    if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
 
3052
        return;
 
3053
 
 
3054
    [self retain];
 
3055
 
 
3056
    if (!_private->ignoringMouseDraggedEvents)
 
3057
        if (Frame* coreframe = core([self _frame]))
 
3058
            coreframe->eventHandler()->mouseDragged(event);
 
3059
 
 
3060
    [self release];
 
3061
}
 
3062
 
 
3063
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
 
3064
{
 
3065
    ASSERT(![self _webView] || [self _isTopHTMLView]);
 
3066
    
 
3067
    Page *page = core([self _webView]);
 
3068
    
 
3069
    if (!page)
 
3070
        return NSDragOperationNone;
 
3071
    
 
3072
    if (page->dragController()->dragOperation() == DragOperationNone)
 
3073
        return NSDragOperationGeneric | NSDragOperationCopy;
 
3074
    
 
3075
    return (NSDragOperation)page->dragController()->dragOperation();
 
3076
}
 
3077
 
 
3078
- (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenLoc
 
3079
{
 
3080
    ASSERT(![self _webView] || [self _isTopHTMLView]);
 
3081
    
 
3082
    NSPoint windowImageLoc = [[self window] convertScreenToBase:screenLoc];
 
3083
    NSPoint windowMouseLoc = windowImageLoc;
 
3084
    
 
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());
 
3088
    }
 
3089
    
 
3090
    [[self _bridge] dragSourceMovedTo:windowMouseLoc];
 
3091
}
 
3092
 
 
3093
- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
 
3094
{
 
3095
    ASSERT(![self _webView] || [self _isTopHTMLView]);
 
3096
    
 
3097
    NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
 
3098
    NSPoint windowMouseLoc = windowImageLoc;
 
3099
    
 
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();
 
3104
    }
 
3105
    
 
3106
    [[self _bridge] dragSourceEndedAt:windowMouseLoc operation:operation];
 
3107
    
 
3108
    // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
 
3109
    _private->ignoringMouseDraggedEvents = YES;
 
3110
    
 
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.
 
3121
}
 
3122
 
 
3123
- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
 
3124
{
 
3125
    NSFileWrapper *wrapper = nil;
 
3126
    
 
3127
    if (WebCore::CachedResource* tiffResource = [self promisedDragTIFFDataSource]) {
 
3128
        
 
3129
        SharedBuffer *buffer = tiffResource->data();
 
3130
        if (!buffer)
 
3131
            goto noPromisedData;
 
3132
        
 
3133
        NSData *data = buffer->createNSData();
 
3134
        NSURLResponse *response = tiffResource->response().nsURLResponse();
 
3135
        
 
3136
        wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
 
3137
        [wrapper setPreferredFilename:[response suggestedFilename]];
 
3138
    }
 
3139
    
 
3140
noPromisedData:
 
3141
    
 
3142
    if (!wrapper) {
 
3143
        ASSERT(![self _webView] || [self _isTopHTMLView]);
 
3144
        Page* page = core([self _webView]);
 
3145
        
 
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?
 
3149
        if (!page) 
 
3150
            return nil; 
 
3151
        
 
3152
        KURL imageURL = page->dragController()->draggingImageURL();
 
3153
        ASSERT(!imageURL.isEmpty());
 
3154
        
 
3155
        wrapper = [[self _dataSource] _fileWrapperForURL:imageURL.getNSURL()];
 
3156
    }
 
3157
    
 
3158
    if (wrapper == nil) {
 
3159
        LOG_ERROR("Failed to create image file.");
 
3160
        return nil;
 
3161
    }
 
3162
 
 
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:]");
 
3168
 
 
3169
    return [NSArray arrayWithObject:[path lastPathComponent]];
 
3170
}
 
3171
 
 
3172
- (void)mouseUp:(NSEvent *)event
 
3173
{
 
3174
    [self _setMouseDownEvent:nil];
 
3175
 
 
3176
    NSInputManager *currentInputManager = [NSInputManager currentInputManager];
 
3177
    if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
 
3178
        return;
 
3179
 
 
3180
    [self retain];
 
3181
 
 
3182
    [self _stopAutoscrollTimer];
 
3183
    if (Frame* coreframe = core([self _frame]))
 
3184
        coreframe->eventHandler()->mouseUp(event);
 
3185
    [self _updateMouseoverWithFakeEvent];
 
3186
 
 
3187
    [self release];
 
3188
}
 
3189
 
 
3190
- (void)mouseMovedNotification:(NSNotification *)notification
 
3191
{
 
3192
    [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
 
3193
}
 
3194
 
 
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
 
3198
{
 
3199
    return YES;
 
3200
}
 
3201
 
 
3202
- (BOOL)becomeFirstResponder
 
3203
{
 
3204
    NSSelectionDirection direction = NSDirectSelection;
 
3205
    if (![[self _webView] _isPerformingProgrammaticFocus] && !_private->willBecomeFirstResponderForNodeFocus)
 
3206
        direction = [[self window] keyViewSelectionDirection];
 
3207
    _private->willBecomeFirstResponderForNodeFocus = NO;
 
3208
 
 
3209
    [self _updateActiveState];
 
3210
    [self _updateFontPanel];
 
3211
    
 
3212
    Frame* frame = core([self _frame]);
 
3213
    if (!frame)
 
3214
        return YES;
 
3215
    
 
3216
    frame->editor()->setStartNewKillRingSequence(true);
 
3217
 
 
3218
    if (direction == NSDirectSelection)
 
3219
        return YES;
 
3220
 
 
3221
    Page* page = frame->page();
 
3222
    if (!page)
 
3223
        return YES;
 
3224
 
 
3225
    page->focusController()->setFocusedFrame(frame);
 
3226
    if (Document* document = frame->document())
 
3227
        document->setFocusedNode(0);
 
3228
    page->focusController()->setInitialFocus(frame->eventHandler()->currentKeyboardEvent().get());
 
3229
    return YES;
 
3230
}
 
3231
 
 
3232
- (BOOL)resignFirstResponder
 
3233
{
 
3234
    BOOL resign = [super resignFirstResponder];
 
3235
    if (resign) {
 
3236
        [_private->compController endRevertingChange:NO moveLeft:NO];
 
3237
        _private->resigningFirstResponder = YES;
 
3238
        if (![self maintainsInactiveSelection]) { 
 
3239
            [self deselectAll];
 
3240
            if (![[self _webView] _isPerformingProgrammaticFocus])
 
3241
                [self clearFocus];
 
3242
        }
 
3243
        [self _updateActiveState];
 
3244
        _private->resigningFirstResponder = NO;
 
3245
        _private->willBecomeFirstResponderForNodeFocus = NO;
 
3246
    }
 
3247
    return resign;
 
3248
}
 
3249
 
 
3250
- (void)setDataSource:(WebDataSource *)dataSource 
 
3251
{
 
3252
    ASSERT(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];
 
3259
    }
 
3260
}
 
3261
 
 
3262
- (void)dataSourceUpdated:(WebDataSource *)dataSource
 
3263
{
 
3264
}
 
3265
 
 
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
 
3270
{
 
3271
}
 
3272
 
 
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
 
3276
{
 
3277
    WebFrame *frame = [self _frame];
 
3278
    NSArray *subframes = [frame childFrames];
 
3279
    unsigned n = [subframes count];
 
3280
    unsigned i;
 
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];
 
3286
        }
 
3287
    }
 
3288
 
 
3289
    if (printing != _private->printing) {
 
3290
        [_private->pageRects release];
 
3291
        _private->pageRects = nil;
 
3292
        _private->printing = printing;
 
3293
        if (!printing)
 
3294
            _private->avoidingPrintOrphan = NO;
 
3295
        [self setNeedsToApplyStyles:YES];
 
3296
        [self setNeedsLayout:YES];
 
3297
        [self layoutToMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
 
3298
        if (!printing) {
 
3299
            // Can't do this when starting printing or nested printing won't work, see 3491427.
 
3300
            [self setNeedsDisplay:NO];
 
3301
        }
 
3302
    }
 
3303
}
 
3304
 
 
3305
- (BOOL)canPrintHeadersAndFooters
 
3306
{
 
3307
    return YES;
 
3308
}
 
3309
 
 
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
 
3313
{
 
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];
 
3319
 
 
3320
    [[self _bridge] adjustPageHeightNew:newBottom top:oldTop bottom:oldBottom limit:bottomLimit];
 
3321
    
 
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];
 
3327
        else
 
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];
 
3330
    }
 
3331
}
 
3332
 
 
3333
- (float)_availablePaperWidthForPrintOperation:(NSPrintOperation *)printOperation
 
3334
{
 
3335
    NSPrintInfo *printInfo = [printOperation printInfo];
 
3336
    return [printInfo paperSize].width - [printInfo leftMargin] - [printInfo rightMargin];
 
3337
}
 
3338
 
 
3339
- (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
 
3340
{
 
3341
    float viewWidth = NSWidth([self bounds]);
 
3342
    if (viewWidth < 1) {
 
3343
        LOG_ERROR("%@ has no width when printing", self);
 
3344
        return 1.0f;
 
3345
    }
 
3346
 
 
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;
 
3352
}
 
3353
 
 
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 
 
3358
{
 
3359
    return [self _scaleFactorForPrintOperation:printOperation];
 
3360
}
 
3361
 
 
3362
// This is used for Carbon printing. At some point we might want to make this public API.
 
3363
- (void)setPageWidthForPrinting:(float)pageWidth
 
3364
{
 
3365
    [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
 
3366
    [self _setPrinting:YES minimumPageWidth:pageWidth maximumPageWidth:pageWidth adjustViewSize:YES];
 
3367
}
 
3368
 
 
3369
- (void)_endPrintMode
 
3370
{
 
3371
    [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
 
3372
    [[self window] setAutodisplay:YES];
 
3373
}
 
3374
 
 
3375
- (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation
 
3376
{
 
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
 
3382
        // further.
 
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();
 
3391
    } else {
 
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];
 
3398
    }
 
3399
}
 
3400
 
 
3401
// Return the number of pages available for printing
 
3402
- (BOOL)knowsPageRange:(NSRangePointer)range
 
3403
{
 
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];
 
3408
    
 
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]);
 
3414
    if (!frame)
 
3415
        return NO;
 
3416
    if (!frame->isFrameSet()) {
 
3417
        float paperWidth = [self _availablePaperWidthForPrintOperation:[NSPrintOperation currentOperation]];
 
3418
        minLayoutWidth = paperWidth * PrintingMinimumShrinkFactor;
 
3419
        maxLayoutWidth = paperWidth * PrintingMaximumShrinkFactor;
 
3420
    }
 
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];
 
3431
    
 
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.
 
3435
 
 
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];
 
3443
    
 
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;
 
3461
            }
 
3462
        }
 
3463
    }
 
3464
    
 
3465
    _private->pageRects = [newPageRects retain];
 
3466
    
 
3467
    range->length = [_private->pageRects count];
 
3468
    
 
3469
    return YES;
 
3470
}
 
3471
 
 
3472
// Return the drawing rectangle for a particular page number
 
3473
- (NSRect)rectForPage:(int)page
 
3474
{
 
3475
    return [[_private->pageRects objectAtIndex:page - 1] rectValue];
 
3476
}
 
3477
 
 
3478
- (void)drawPageBorderWithSize:(NSSize)borderSize
 
3479
{
 
3480
    ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));    
 
3481
    [[self _webView] _drawHeaderAndFooter];
 
3482
}
 
3483
 
 
3484
- (void)beginDocument
 
3485
{
 
3486
    NS_DURING
 
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];
 
3493
    NS_HANDLER
 
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];
 
3497
    NS_ENDHANDLER
 
3498
}
 
3499
 
 
3500
- (void)endDocument
 
3501
{
 
3502
    [super endDocument];
 
3503
    // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO 
 
3504
    [self _endPrintMode];
 
3505
}
 
3506
 
 
3507
- (void)keyDown:(NSEvent *)event
 
3508
{
 
3509
    RetainPtr<WebHTMLView> selfProtector = self;
 
3510
    BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
 
3511
 
 
3512
    BOOL callSuper = NO;
 
3513
 
 
3514
    [_private->keyDownEvent release];
 
3515
    _private->keyDownEvent = [event retain];
 
3516
 
 
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];
 
3525
        callSuper = YES;
 
3526
    }
 
3527
    if (callSuper)
 
3528
        [super keyDown:event];
 
3529
    else
 
3530
        [NSCursor setHiddenUntilMouseMoves:YES];
 
3531
}
 
3532
 
 
3533
- (void)keyUp:(NSEvent *)event
 
3534
{
 
3535
    BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
 
3536
 
 
3537
    [self retain];
 
3538
    if (eventWasSentToWebCore || !core([self _frame])->eventHandler()->keyEvent(event))
 
3539
        [super keyUp:event];    
 
3540
    [self release];
 
3541
}
 
3542
 
 
3543
- (id)accessibilityAttributeValue:(NSString*)attributeName
 
3544
{
 
3545
    if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
 
3546
        id accTree = [[self _bridge] accessibilityTree];
 
3547
        if (accTree)
 
3548
            return [NSArray arrayWithObject:accTree];
 
3549
        return nil;
 
3550
    }
 
3551
    return [super accessibilityAttributeValue:attributeName];
 
3552
}
 
3553
 
 
3554
- (id)accessibilityFocusedUIElement
 
3555
{
 
3556
    id accTree = [[self _bridge] accessibilityTree];
 
3557
    if (accTree)
 
3558
        return [accTree accessibilityFocusedUIElement];
 
3559
    return self;
 
3560
}
 
3561
 
 
3562
- (id)accessibilityHitTest:(NSPoint)point
 
3563
{
 
3564
    id accTree = [[self _bridge] accessibilityTree];
 
3565
    if (accTree) {
 
3566
        NSPoint windowCoord = [[self window] convertScreenToBase:point];
 
3567
        return [accTree accessibilityHitTest:[self convertPoint:windowCoord fromView:nil]];
 
3568
    }
 
3569
    return self;
 
3570
}
 
3571
 
 
3572
- (id)_accessibilityParentForSubview:(NSView *)subview
 
3573
{
 
3574
    id accTree = [[self _bridge] accessibilityTree];
 
3575
    if (!accTree)
 
3576
        return self;
 
3577
    id parent = [accTree _accessibilityParentForSubview:subview];
 
3578
    if (!parent)
 
3579
        return self;
 
3580
    return parent;
 
3581
}
 
3582
 
 
3583
- (void)centerSelectionInVisibleArea:(id)sender
 
3584
{
 
3585
    COMMAND_PROLOGUE
 
3586
 
 
3587
    if (Frame* coreFrame = core([self _frame]))
 
3588
        coreFrame->revealSelection(RenderLayer::gAlignCenterAlways);
 
3589
}
 
3590
 
 
3591
- (void)pageUp:(id)sender
 
3592
{
 
3593
    COMMAND_PROLOGUE
 
3594
 
 
3595
    WebFrameView *frameView = [self _frameView];
 
3596
    if (!frameView)
 
3597
        return;
 
3598
    if ([self _canAlterCurrentSelection])
 
3599
        [[self _bridge] alterCurrentSelection:SelectionController::MOVE verticalDistance:-[frameView _verticalPageScrollDistance]];
 
3600
}
 
3601
 
 
3602
- (void)pageDown:(id)sender
 
3603
{
 
3604
    COMMAND_PROLOGUE
 
3605
 
 
3606
    WebFrameView *frameView = [self _frameView];
 
3607
    if (!frameView)
 
3608
        return;
 
3609
    if ([self _canAlterCurrentSelection])
 
3610
        [[self _bridge] alterCurrentSelection:SelectionController::MOVE verticalDistance:[frameView _verticalPageScrollDistance]];
 
3611
}
 
3612
 
 
3613
- (void)pageUpAndModifySelection:(id)sender
 
3614
{
 
3615
    COMMAND_PROLOGUE
 
3616
 
 
3617
    WebFrameView *frameView = [self _frameView];
 
3618
    if (!frameView)
 
3619
        return;
 
3620
    if ([self _canAlterCurrentSelection])
 
3621
        [[self _bridge] alterCurrentSelection:SelectionController::EXTEND verticalDistance:-[frameView _verticalPageScrollDistance]];
 
3622
}
 
3623
 
 
3624
- (void)pageDownAndModifySelection:(id)sender
 
3625
{
 
3626
    COMMAND_PROLOGUE
 
3627
 
 
3628
    WebFrameView *frameView = [self _frameView];
 
3629
    if (frameView == nil)
 
3630
        return;
 
3631
    if ([self _canAlterCurrentSelection])
 
3632
        [[self _bridge] alterCurrentSelection:SelectionController::EXTEND verticalDistance:[frameView _verticalPageScrollDistance]];
 
3633
}
 
3634
 
 
3635
- (void)_expandSelectionToGranularity:(TextGranularity)granularity
 
3636
{
 
3637
    if (![self _canAlterCurrentSelection])
 
3638
        return;
 
3639
    
 
3640
    Frame* coreFrame = core([self _frame]);
 
3641
    if (!coreFrame || !coreFrame->selectionController()->isCaretOrRange())
 
3642
        return;
 
3643
 
 
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));
 
3647
    
 
3648
    RefPtr<Range> range = selection.toRange();
 
3649
    DOMRange *domRange = kit(range.get());
 
3650
    
 
3651
    if ([domRange collapsed])
 
3652
        return;
 
3653
 
 
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);
 
3659
    }
 
3660
}
 
3661
 
 
3662
- (void)selectParagraph:(id)sender
 
3663
{
 
3664
    COMMAND_PROLOGUE
 
3665
 
 
3666
    [self _expandSelectionToGranularity:ParagraphGranularity];
 
3667
}
 
3668
 
 
3669
- (void)selectLine:(id)sender
 
3670
{
 
3671
    COMMAND_PROLOGUE
 
3672
 
 
3673
    [self _expandSelectionToGranularity:LineGranularity];
 
3674
}
 
3675
 
 
3676
- (void)selectSentence:(id)sender
 
3677
{
 
3678
    COMMAND_PROLOGUE
 
3679
 
 
3680
    [self _expandSelectionToGranularity:SentenceGranularity];
 
3681
}
 
3682
 
 
3683
- (void)selectWord:(id)sender
 
3684
{
 
3685
    COMMAND_PROLOGUE
 
3686
 
 
3687
    [self _expandSelectionToGranularity:WordGranularity];
 
3688
}
 
3689
 
 
3690
- (void)delete:(id)sender
 
3691
{
 
3692
    COMMAND_PROLOGUE
 
3693
 
 
3694
    if (Frame* coreFrame = core([self _frame]))
 
3695
        coreFrame->editor()->performDelete();
 
3696
}
 
3697
 
 
3698
- (NSData *)_selectionStartFontAttributesAsRTF
 
3699
{
 
3700
    NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"x"
 
3701
        attributes:core([self _frame])->fontAttributesForSelectionStart()];
 
3702
    NSData *data = [string RTFFromRange:NSMakeRange(0, [string length]) documentAttributes:nil];
 
3703
    [string release];
 
3704
    return data;
 
3705
}
 
3706
 
 
3707
- (NSDictionary *)_fontAttributesFromFontPasteboard
 
3708
{
 
3709
    NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
 
3710
    if (fontPasteboard == nil)
 
3711
        return nil;
 
3712
    NSData *data = [fontPasteboard dataForType:NSFontPboardType];
 
3713
    if (data == nil || [data length] == 0)
 
3714
        return nil;
 
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)
 
3718
        return nil;
 
3719
    return [string fontAttributesInRange:NSMakeRange(0, 1)];
 
3720
}
 
3721
 
 
3722
- (DOMCSSStyleDeclaration *)_emptyStyle
 
3723
{
 
3724
    return [[[self _frame] DOMDocument] createCSSStyleDeclaration];
 
3725
}
 
3726
 
 
3727
- (NSString *)_colorAsString:(NSColor *)color
 
3728
{
 
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];
 
3739
    if (a == 0)
 
3740
        return @"transparent";
 
3741
    if (r == 0 && g == 0 && b == 0 && a == 1)
 
3742
        return @"black";
 
3743
    if (r == 1 && g == 1 && b == 1 && a == 1)
 
3744
        return @"white";
 
3745
    // FIXME: Lots more named colors. Maybe we could use the table in WebCore?
 
3746
    if (a == 1)
 
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];
 
3749
}
 
3750
 
 
3751
- (NSString *)_shadowAsString:(NSShadow *)shadow
 
3752
{
 
3753
    if (shadow == nil)
 
3754
        return @"none";
 
3755
    NSSize offset = [shadow shadowOffset];
 
3756
    float blurRadius = [shadow shadowBlurRadius];
 
3757
    if (offset.width == 0 && offset.height == 0 && blurRadius == 0)
 
3758
        return @"none";
 
3759
    NSColor *color = [shadow shadowColor];
 
3760
    if (color == nil)
 
3761
        return @"none";
 
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];
 
3766
}
 
3767
 
 
3768
- (DOMCSSStyleDeclaration *)_styleFromFontAttributes:(NSDictionary *)dictionary
 
3769
{
 
3770
    DOMCSSStyleDeclaration *style = [self _emptyStyle];
 
3771
 
 
3772
    NSColor *color = [dictionary objectForKey:NSBackgroundColorAttributeName];
 
3773
    [style setBackgroundColor:[self _colorAsString:color]];
 
3774
 
 
3775
    NSFont *font = [dictionary objectForKey:NSFontAttributeName];
 
3776
    if (font == nil) {
 
3777
        [style setFontFamily:@"Helvetica"];
 
3778
        [style setFontSize:@"12px"];
 
3779
        [style setFontWeight:@"normal"];
 
3780
        [style setFontStyle:@"normal"];
 
3781
    } else {
 
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"];
 
3789
        else
 
3790
            [style setFontWeight:@"normal"];
 
3791
        if (([fm traitsOfFont:font] & NSItalicFontMask) != 0)
 
3792
            [style setFontStyle:@"italic"];
 
3793
        else
 
3794
            [style setFontStyle:@"normal"];
 
3795
    }
 
3796
 
 
3797
    color = [dictionary objectForKey:NSForegroundColorAttributeName];
 
3798
    [style setColor:color ? [self _colorAsString:color] : (NSString *)@"black"];
 
3799
 
 
3800
    NSShadow *shadow = [dictionary objectForKey:NSShadowAttributeName];
 
3801
    [style setTextShadow:[self _shadowAsString:shadow]];
 
3802
 
 
3803
    int strikethroughInt = [[dictionary objectForKey:NSStrikethroughStyleAttributeName] intValue];
 
3804
 
 
3805
    int superscriptInt = [[dictionary objectForKey:NSSuperscriptAttributeName] intValue];
 
3806
    if (superscriptInt > 0)
 
3807
        [style setVerticalAlign:@"super"];
 
3808
    else if (superscriptInt < 0)
 
3809
        [style setVerticalAlign:@"sub"];
 
3810
    else
 
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:@""];
 
3818
    else
 
3819
        [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
 
3820
 
 
3821
    return style;
 
3822
}
 
3823
 
 
3824
- (void)_applyStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
 
3825
{
 
3826
    if (Frame* coreFrame = core([self _frame]))
 
3827
        coreFrame->editor()->applyStyleToSelection(core(style), undoAction);
 
3828
}
 
3829
 
 
3830
- (void)_applyParagraphStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
 
3831
{
 
3832
    if (Frame* coreFrame = core([self _frame]))
 
3833
        coreFrame->editor()->applyParagraphStyleToSelection(core(style), undoAction);
 
3834
}
 
3835
 
 
3836
- (void)_toggleBold
 
3837
{
 
3838
    if (Frame* coreFrame = core([self _frame]))
 
3839
        coreFrame->editor()->execCommand("ToggleBold");
 
3840
}
 
3841
 
 
3842
- (void)_toggleItalic
 
3843
{
 
3844
    if (Frame* coreFrame = core([self _frame]))
 
3845
        coreFrame->editor()->execCommand("ToggleItalic");
 
3846
}
 
3847
 
 
3848
- (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event
 
3849
{
 
3850
    ASSERT([self _webView]);
 
3851
    if (![[[self _webView] preferences] respectStandardStyleKeyEquivalents])
 
3852
        return NO;
 
3853
    
 
3854
    if (![self _canEdit])
 
3855
        return NO;
 
3856
    
 
3857
    if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask)
 
3858
        return NO;
 
3859
    
 
3860
    NSString *string = [event characters];
 
3861
    if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) {
 
3862
        [self _toggleBold];
 
3863
        return YES;
 
3864
    }
 
3865
    if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) {
 
3866
        [self _toggleItalic];
 
3867
        return YES;
 
3868
    }
 
3869
    
 
3870
    return NO;
 
3871
}
 
3872
 
 
3873
- (BOOL)performKeyEquivalent:(NSEvent *)event
 
3874
{
 
3875
    if ([self _handleStyleKeyEquivalent:event])
 
3876
        return YES;
 
3877
    
 
3878
    BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
 
3879
    BOOL ret = NO;
 
3880
 
 
3881
    [_private->keyDownEvent release];
 
3882
    _private->keyDownEvent = [event retain];
 
3883
    
 
3884
    [self retain];
 
3885
 
 
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);
 
3893
 
 
3894
    if (!ret)
 
3895
        ret = [super performKeyEquivalent:event];
 
3896
 
 
3897
    [self release];
 
3898
    
 
3899
    return ret;
 
3900
}
 
3901
 
 
3902
- (void)copyFont:(id)sender
 
3903
{
 
3904
    COMMAND_PROLOGUE
 
3905
 
 
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];
 
3911
}
 
3912
 
 
3913
- (void)pasteFont:(id)sender
 
3914
{
 
3915
    COMMAND_PROLOGUE
 
3916
 
 
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];
 
3920
}
 
3921
 
 
3922
- (void)pasteAsRichText:(id)sender
 
3923
{
 
3924
    COMMAND_PROLOGUE
 
3925
 
 
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];
 
3929
}
 
3930
 
 
3931
- (NSFont *)_originalFontA
 
3932
{
 
3933
    return [[NSFontManager sharedFontManager] fontWithFamily:@"Helvetica" traits:0 weight:STANDARD_WEIGHT size:10.0f];
 
3934
}
 
3935
 
 
3936
- (NSFont *)_originalFontB
 
3937
{
 
3938
    return [[NSFontManager sharedFontManager] fontWithFamily:@"Times" traits:(NSBoldFontMask | NSItalicFontMask) weight:STANDARD_BOLD_WEIGHT size:12.0f];
 
3939
}
 
3940
 
 
3941
- (void)_addToStyle:(DOMCSSStyleDeclaration *)style fontA:(NSFont *)a fontB:(NSFont *)b
 
3942
{
 
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.
 
3947
 
 
3948
    if (a == nil || b == nil)
 
3949
        return;
 
3950
 
 
3951
    NSFontManager *fm = [NSFontManager sharedFontManager];
 
3952
 
 
3953
    NSFont *oa = [self _originalFontA];
 
3954
 
 
3955
    NSString *aFamilyName = [a familyName];
 
3956
    NSString *bFamilyName = [b familyName];
 
3957
 
 
3958
    int aPointSize = (int)[a pointSize];
 
3959
    int bPointSize = (int)[b pointSize];
 
3960
 
 
3961
    int aWeight = [fm weightOfFont:a];
 
3962
    int bWeight = [fm weightOfFont:b];
 
3963
 
 
3964
    BOOL aIsBold = aWeight >= MIN_BOLD_WEIGHT;
 
3965
 
 
3966
    BOOL aIsItalic = ([fm traitsOfFont:a] & NSItalicFontMask) != 0;
 
3967
    BOOL bIsItalic = ([fm traitsOfFont:b] & NSItalicFontMask) != 0;
 
3968
 
 
3969
    if ([aFamilyName isEqualToString:bFamilyName]) {
 
3970
        NSString *familyNameForCSS = aFamilyName;
 
3971
 
 
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.
 
3975
        
 
3976
        // Find the font the same way the rendering code would later if it encountered this CSS.
 
3977
        NSFontTraitMask traits = 0;
 
3978
        if (aIsBold)
 
3979
            traits |= NSBoldFontMask;
 
3980
        if (aIsItalic)
 
3981
            traits |= NSItalicFontMask;
 
3982
        NSFont *foundFont = WebCoreFindFont(aFamilyName, traits, aPointSize);
 
3983
 
 
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];
 
3988
        }
 
3989
 
 
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]];
 
3993
    }
 
3994
 
 
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"];
 
4002
 
 
4003
    if (aWeight == bWeight)
 
4004
        [style setFontWeight:aIsBold ? @"bold" : @"normal"];
 
4005
 
 
4006
    if (aIsItalic == bIsItalic)
 
4007
        [style setFontStyle:aIsItalic ? @"italic" :  @"normal"];
 
4008
}
 
4009
 
 
4010
- (DOMCSSStyleDeclaration *)_styleFromFontManagerOperation
 
4011
{
 
4012
    DOMCSSStyleDeclaration *style = [self _emptyStyle];
 
4013
 
 
4014
    NSFontManager *fm = [NSFontManager sharedFontManager];
 
4015
 
 
4016
    NSFont *oa = [self _originalFontA];
 
4017
    NSFont *ob = [self _originalFontB];    
 
4018
    [self _addToStyle:style fontA:[fm convertFont:oa] fontB:[fm convertFont:ob]];
 
4019
 
 
4020
    return style;
 
4021
}
 
4022
 
 
4023
- (void)changeFont:(id)sender
 
4024
{
 
4025
    COMMAND_PROLOGUE
 
4026
 
 
4027
    [self _applyStyleToSelection:[self _styleFromFontManagerOperation] withUndoAction:EditActionSetFont];
 
4028
}
 
4029
 
 
4030
- (DOMCSSStyleDeclaration *)_styleForAttributeChange:(id)sender
 
4031
{
 
4032
    DOMCSSStyleDeclaration *style = [self _emptyStyle];
 
4033
 
 
4034
    NSShadow *shadow = [[NSShadow alloc] init];
 
4035
    [shadow setShadowOffset:NSMakeSize(1, 1)];
 
4036
 
 
4037
    NSDictionary *oa = [NSDictionary dictionaryWithObjectsAndKeys:
 
4038
        [self _originalFontA], NSFontAttributeName,
 
4039
        nil];
 
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,
 
4048
        nil];
 
4049
 
 
4050
    [shadow release];
 
4051
 
 
4052
#if 0
 
4053
 
 
4054
NSObliquenessAttributeName        /* float; skew to be applied to glyphs, default 0: no skew */
 
4055
    // font-style, but that is just an on-off switch
 
4056
 
 
4057
NSExpansionAttributeName          /* float; log of expansion factor to be applied to glyphs, default 0: no expansion */
 
4058
    // font-stretch?
 
4059
 
 
4060
NSKernAttributeName               /* float, amount to modify default kerning, if 0, kerning off */
 
4061
    // letter-spacing? probably not good enough
 
4062
 
 
4063
NSUnderlineColorAttributeName     /* NSColor, default nil: same as foreground color */
 
4064
NSStrikethroughColorAttributeName /* NSColor, default nil: same as foreground color */
 
4065
    // text-decoration-color?
 
4066
 
 
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 */
 
4071
    // need extensions?
 
4072
 
 
4073
#endif
 
4074
    
 
4075
    NSDictionary *a = [sender convertAttributes:oa];
 
4076
    NSDictionary *b = [sender convertAttributes:ob];
 
4077
 
 
4078
    NSColor *ca = [a objectForKey:NSBackgroundColorAttributeName];
 
4079
    NSColor *cb = [b objectForKey:NSBackgroundColorAttributeName];
 
4080
    if (ca == cb) {
 
4081
        [style setBackgroundColor:[self _colorAsString:ca]];
 
4082
    }
 
4083
 
 
4084
    [self _addToStyle:style fontA:[a objectForKey:NSFontAttributeName] fontB:[b objectForKey:NSFontAttributeName]];
 
4085
 
 
4086
    ca = [a objectForKey:NSForegroundColorAttributeName];
 
4087
    cb = [b objectForKey:NSForegroundColorAttributeName];
 
4088
    if (ca == cb) {
 
4089
        [style setColor:[self _colorAsString:ca]];
 
4090
    }
 
4091
 
 
4092
    NSShadow *sha = [a objectForKey:NSShadowAttributeName];
 
4093
    if (sha)
 
4094
        [style setTextShadow:[self _shadowAsString:sha]];
 
4095
    else if ([b objectForKey:NSShadowAttributeName] == nil)
 
4096
        [style setTextShadow:@"none"];
 
4097
 
 
4098
    int sa = [[a objectForKey:NSStrikethroughStyleAttributeName] intValue];
 
4099
    int sb = [[b objectForKey:NSStrikethroughStyleAttributeName] intValue];
 
4100
    if (sa == sb) {
 
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"
 
4104
        else
 
4105
            [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""];
 
4106
            // we really mean "add line-through" rather than "line-through"
 
4107
    }
 
4108
 
 
4109
    sa = [[a objectForKey:NSSuperscriptAttributeName] intValue];
 
4110
    sb = [[b objectForKey:NSSuperscriptAttributeName] intValue];
 
4111
    if (sa == sb) {
 
4112
        if (sa > 0)
 
4113
            [style setVerticalAlign:@"super"];
 
4114
        else if (sa < 0)
 
4115
            [style setVerticalAlign:@"sub"];
 
4116
        else
 
4117
            [style setVerticalAlign:@"baseline"];
 
4118
    }
 
4119
 
 
4120
    int ua = [[a objectForKey:NSUnderlineStyleAttributeName] intValue];
 
4121
    int ub = [[b objectForKey:NSUnderlineStyleAttributeName] intValue];
 
4122
    if (ua == ub) {
 
4123
        if (ua == NSUnderlineStyleNone)
 
4124
            [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
 
4125
            // we really mean "no underline" rather than "none"
 
4126
        else
 
4127
            [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
 
4128
            // we really mean "add underline" rather than "underline"
 
4129
    }
 
4130
 
 
4131
    return style;
 
4132
}
 
4133
 
 
4134
- (void)changeAttributes:(id)sender
 
4135
{
 
4136
    COMMAND_PROLOGUE
 
4137
 
 
4138
    [self _applyStyleToSelection:[self _styleForAttributeChange:sender] withUndoAction:EditActionChangeAttributes];
 
4139
}
 
4140
 
 
4141
- (DOMCSSStyleDeclaration *)_styleFromColorPanelWithSelector:(SEL)selector
 
4142
{
 
4143
    DOMCSSStyleDeclaration *style = [self _emptyStyle];
 
4144
 
 
4145
    ASSERT([style respondsToSelector:selector]);
 
4146
    [style performSelector:selector withObject:[self _colorAsString:[[NSColorPanel sharedColorPanel] color]]];
 
4147
    
 
4148
    return style;
 
4149
}
 
4150
 
 
4151
- (EditAction)_undoActionFromColorPanelWithSelector:(SEL)selector
 
4152
{
 
4153
    if (selector == @selector(setBackgroundColor:))
 
4154
        return EditActionSetBackgroundColor;    
 
4155
    return EditActionSetColor;
 
4156
}
 
4157
 
 
4158
- (void)_changeCSSColorUsingSelector:(SEL)selector inRange:(DOMRange *)range
 
4159
{
 
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]);
 
4164
}
 
4165
 
 
4166
- (void)changeDocumentBackgroundColor:(id)sender
 
4167
{
 
4168
    COMMAND_PROLOGUE
 
4169
 
 
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]];
 
4182
}
 
4183
 
 
4184
- (void)changeColor:(id)sender
 
4185
{
 
4186
    COMMAND_PROLOGUE
 
4187
 
 
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];
 
4195
}
 
4196
 
 
4197
- (void)_alignSelectionUsingCSSValue:(NSString *)CSSAlignmentValue withUndoAction:(EditAction)undoAction
 
4198
{
 
4199
    if (![self _canEditRichly])
 
4200
        return;
 
4201
        
 
4202
    DOMCSSStyleDeclaration *style = [self _emptyStyle];
 
4203
    [style setTextAlign:CSSAlignmentValue];
 
4204
    [self _applyStyleToSelection:style withUndoAction:undoAction];
 
4205
}
 
4206
 
 
4207
- (void)alignCenter:(id)sender
 
4208
{
 
4209
    COMMAND_PROLOGUE
 
4210
 
 
4211
    [self _alignSelectionUsingCSSValue:@"center" withUndoAction:EditActionCenter];
 
4212
}
 
4213
 
 
4214
- (void)alignJustified:(id)sender
 
4215
{
 
4216
    COMMAND_PROLOGUE
 
4217
 
 
4218
    [self _alignSelectionUsingCSSValue:@"justify" withUndoAction:EditActionJustify];
 
4219
}
 
4220
 
 
4221
- (void)alignLeft:(id)sender
 
4222
{
 
4223
    COMMAND_PROLOGUE
 
4224
 
 
4225
    [self _alignSelectionUsingCSSValue:@"left" withUndoAction:EditActionAlignLeft];
 
4226
}
 
4227
 
 
4228
- (void)alignRight:(id)sender
 
4229
{
 
4230
    COMMAND_PROLOGUE
 
4231
 
 
4232
    [self _alignSelectionUsingCSSValue:@"right" withUndoAction:EditActionAlignRight];
 
4233
}
 
4234
 
 
4235
- (void)insertParagraphSeparator:(id)sender
 
4236
{
 
4237
    COMMAND_PROLOGUE
 
4238
 
 
4239
    if (Frame* coreFrame = core([self _frame]))
 
4240
        coreFrame->editor()->execCommand("InsertNewline");
 
4241
}
 
4242
 
 
4243
- (void)_changeWordCaseWithSelector:(SEL)selector
 
4244
{
 
4245
    if (![self _canEdit])
 
4246
        return;
 
4247
 
 
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];
 
4254
}
 
4255
 
 
4256
- (void)uppercaseWord:(id)sender
 
4257
{
 
4258
    COMMAND_PROLOGUE
 
4259
 
 
4260
    [self _changeWordCaseWithSelector:@selector(uppercaseString)];
 
4261
}
 
4262
 
 
4263
- (void)lowercaseWord:(id)sender
 
4264
{
 
4265
    COMMAND_PROLOGUE
 
4266
 
 
4267
    [self _changeWordCaseWithSelector:@selector(lowercaseString)];
 
4268
}
 
4269
 
 
4270
- (void)capitalizeWord:(id)sender
 
4271
{
 
4272
    COMMAND_PROLOGUE
 
4273
 
 
4274
    [self _changeWordCaseWithSelector:@selector(capitalizedString)];
 
4275
}
 
4276
 
 
4277
- (void)deleteForward:(id)sender
 
4278
{
 
4279
    COMMAND_PROLOGUE
 
4280
 
 
4281
    if (![self _isEditable])
 
4282
        return;
 
4283
    Frame* coreFrame = core([self _frame]);
 
4284
    if (coreFrame)
 
4285
        coreFrame->editor()->deleteWithDirection(SelectionController::FORWARD, CharacterGranularity, false, true);
 
4286
}
 
4287
 
 
4288
- (void)deleteBackward:(id)sender
 
4289
{
 
4290
    COMMAND_PROLOGUE
 
4291
 
 
4292
    if (![self _isEditable])
 
4293
        return;
 
4294
    Frame* coreFrame = core([self _frame]);
 
4295
    if (coreFrame)
 
4296
        coreFrame->editor()->deleteWithDirection(SelectionController::BACKWARD, CharacterGranularity, false, true);
 
4297
}
 
4298
 
 
4299
- (void)deleteBackwardByDecomposingPreviousCharacter:(id)sender
 
4300
{
 
4301
    COMMAND_PROLOGUE
 
4302
 
 
4303
    LOG_ERROR("unimplemented, doing deleteBackward instead");
 
4304
 
 
4305
    if (![self _isEditable])
 
4306
        return;
 
4307
    Frame* coreFrame = core([self _frame]);
 
4308
    if (coreFrame)
 
4309
        coreFrame->editor()->deleteWithDirection(SelectionController::BACKWARD, CharacterGranularity, false, true);
 
4310
}
 
4311
 
 
4312
- (void)deleteToBeginningOfLine:(id)sender
 
4313
{
 
4314
    COMMAND_PROLOGUE
 
4315
 
 
4316
    Frame* coreFrame = core([self _frame]);
 
4317
    if (coreFrame)
 
4318
        coreFrame->editor()->deleteWithDirection(SelectionController::BACKWARD, LineBoundary, true, false);
 
4319
}
 
4320
 
 
4321
- (void)deleteToEndOfLine:(id)sender
 
4322
{
 
4323
    COMMAND_PROLOGUE
 
4324
 
 
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]);
 
4328
    if (coreFrame) {
 
4329
        if (!coreFrame->editor()->deleteWithDirection(SelectionController::FORWARD, LineBoundary, true, false))
 
4330
            coreFrame->editor()->deleteWithDirection(SelectionController::FORWARD, CharacterGranularity, true, false);
 
4331
    }
 
4332
    
 
4333
}
 
4334
 
 
4335
- (void)deleteToBeginningOfParagraph:(id)sender
 
4336
{
 
4337
    COMMAND_PROLOGUE
 
4338
 
 
4339
    Frame* coreFrame = core([self _frame]);
 
4340
    if (coreFrame)
 
4341
        coreFrame->editor()->deleteWithDirection(SelectionController::BACKWARD, ParagraphBoundary, true, false);
 
4342
}
 
4343
 
 
4344
- (void)deleteToEndOfParagraph:(id)sender
 
4345
{
 
4346
    COMMAND_PROLOGUE
 
4347
 
 
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]);
 
4351
    if (coreFrame) {
 
4352
        if (!coreFrame->editor()->deleteWithDirection(SelectionController::FORWARD, ParagraphBoundary, true, false))
 
4353
            coreFrame->editor()->deleteWithDirection(SelectionController::FORWARD, CharacterGranularity, true, false);
 
4354
    }
 
4355
}
 
4356
 
 
4357
- (void)complete:(id)sender
 
4358
{
 
4359
    COMMAND_PROLOGUE
 
4360
 
 
4361
    if (![self _canEdit])
 
4362
        return;
 
4363
    if (!_private->compController)
 
4364
        _private->compController = [[WebTextCompleteController alloc] initWithHTMLView:self];
 
4365
    [_private->compController doCompletion];
 
4366
}
 
4367
 
 
4368
- (void)checkSpelling:(id)sender
 
4369
{
 
4370
    COMMAND_PROLOGUE
 
4371
 
 
4372
    NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
 
4373
    if (!checker) {
 
4374
        LOG_ERROR("No NSSpellChecker");
 
4375
        return;
 
4376
    }
 
4377
    
 
4378
    core([self _frame])->editor()->advanceToNextMisspelling();
 
4379
}
 
4380
 
 
4381
- (void)showGuessPanel:(id)sender
 
4382
{
 
4383
    COMMAND_PROLOGUE
 
4384
 
 
4385
    NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
 
4386
    if (!checker) {
 
4387
        LOG_ERROR("No NSSpellChecker");
 
4388
        return;
 
4389
    }
 
4390
    
 
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];
 
4397
        return;
 
4398
    }
 
4399
#endif
 
4400
    
 
4401
    core([self _frame])->editor()->advanceToNextMisspelling(true);
 
4402
    [spellingPanel orderFront:sender];
 
4403
}
 
4404
 
 
4405
- (void)_changeSpellingToWord:(NSString *)newWord
 
4406
{
 
4407
    if (![self _canEdit])
 
4408
        return;
 
4409
 
 
4410
    // Don't correct to empty string.  (AppKit checked this, we might as well too.)
 
4411
    if (![NSSpellChecker sharedSpellChecker]) {
 
4412
        LOG_ERROR("No NSSpellChecker");
 
4413
        return;
 
4414
    }
 
4415
    
 
4416
    if ([newWord isEqualToString:@""])
 
4417
        return;
 
4418
 
 
4419
    if ([self _shouldReplaceSelectionWithText:newWord givenAction:WebViewInsertActionPasted])
 
4420
        [[self _bridge] replaceSelectionWithText:newWord selectReplacement:YES smartReplace:NO];
 
4421
}
 
4422
 
 
4423
- (void)changeSpelling:(id)sender
 
4424
{
 
4425
    COMMAND_PROLOGUE
 
4426
 
 
4427
    [self _changeSpellingToWord:[[sender selectedCell] stringValue]];
 
4428
}
 
4429
 
 
4430
- (void)ignoreSpelling:(id)sender
 
4431
{
 
4432
    COMMAND_PROLOGUE
 
4433
 
 
4434
    NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
 
4435
    if (!checker) {
 
4436
        LOG_ERROR("No NSSpellChecker");
 
4437
        return;
 
4438
    }
 
4439
    
 
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?
 
4445
    }
 
4446
}
 
4447
 
 
4448
- (void)performFindPanelAction:(id)sender
 
4449
{
 
4450
    COMMAND_PROLOGUE
 
4451
 
 
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");
 
4455
}
 
4456
 
 
4457
- (void)startSpeaking:(id)sender
 
4458
{
 
4459
    COMMAND_PROLOGUE
 
4460
 
 
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]];
 
4466
}
 
4467
 
 
4468
- (void)stopSpeaking:(id)sender
 
4469
{
 
4470
    COMMAND_PROLOGUE
 
4471
 
 
4472
    [NSApp stopSpeaking:sender];
 
4473
}
 
4474
 
 
4475
- (void)insertNewlineIgnoringFieldEditor:(id)sender
 
4476
{
 
4477
    COMMAND_PROLOGUE
 
4478
 
 
4479
    if (Frame* coreFrame = core([self _frame]))
 
4480
        coreFrame->editor()->execCommand("InsertNewline");
 
4481
}
 
4482
 
 
4483
- (void)insertTabIgnoringFieldEditor:(id)sender
 
4484
{
 
4485
    COMMAND_PROLOGUE
 
4486
 
 
4487
    if (Frame* coreFrame = core([self _frame]))
 
4488
        coreFrame->editor()->execCommand("InsertTab");
 
4489
}
 
4490
 
 
4491
- (void)subscript:(id)sender
 
4492
{
 
4493
    COMMAND_PROLOGUE
 
4494
 
 
4495
    DOMCSSStyleDeclaration *style = [self _emptyStyle];
 
4496
    [style setVerticalAlign:@"sub"];
 
4497
    [self _applyStyleToSelection:style withUndoAction:EditActionSubscript];
 
4498
}
 
4499
 
 
4500
- (void)superscript:(id)sender
 
4501
{
 
4502
    COMMAND_PROLOGUE
 
4503
 
 
4504
    DOMCSSStyleDeclaration *style = [self _emptyStyle];
 
4505
    [style setVerticalAlign:@"super"];
 
4506
    [self _applyStyleToSelection:style withUndoAction:EditActionSuperscript];
 
4507
}
 
4508
 
 
4509
- (void)unscript:(id)sender
 
4510
{
 
4511
    COMMAND_PROLOGUE
 
4512
 
 
4513
    DOMCSSStyleDeclaration *style = [self _emptyStyle];
 
4514
    [style setVerticalAlign:@"baseline"];
 
4515
    [self _applyStyleToSelection:style withUndoAction:EditActionUnscript];
 
4516
}
 
4517
 
 
4518
- (void)underline:(id)sender
 
4519
{
 
4520
    COMMAND_PROLOGUE
 
4521
 
 
4522
    Frame* coreFrame = core([self _frame]);
 
4523
    if (!coreFrame)
 
4524
        return;
 
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];
 
4532
}
 
4533
 
 
4534
- (void)yank:(id)sender
 
4535
{
 
4536
    COMMAND_PROLOGUE
 
4537
 
 
4538
    if (![self _canEdit])
 
4539
        return;
 
4540
        
 
4541
    NSString* yankee = _NSYankFromKillRing();
 
4542
 
 
4543
    if (Frame* coreFrame = core([self _frame]))
 
4544
        coreFrame->editor()->insertTextWithoutSendingTextEvent(yankee, false);
 
4545
 
 
4546
    _NSSetKillRingToYankedState();
 
4547
}
 
4548
 
 
4549
- (void)yankAndSelect:(id)sender
 
4550
{
 
4551
    COMMAND_PROLOGUE
 
4552
 
 
4553
    if (![self _canEdit])
 
4554
        return;
 
4555
 
 
4556
    NSString* yankee = _NSYankFromKillRing();
 
4557
    
 
4558
    if (Frame* coreFrame = core([self _frame]))
 
4559
        coreFrame->editor()->insertTextWithoutSendingTextEvent(yankee, true);
 
4560
        
 
4561
    _NSSetKillRingToYankedState();
 
4562
}
 
4563
 
 
4564
- (void)setMark:(id)sender
 
4565
{
 
4566
    COMMAND_PROLOGUE
 
4567
 
 
4568
    [[self _bridge] setMarkDOMRange:[self _selectedRange]];
 
4569
}
 
4570
 
 
4571
static DOMRange *unionDOMRanges(DOMRange *a, DOMRange *b)
 
4572
{
 
4573
    ASSERT(a);
 
4574
    ASSERT(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]];
 
4580
    return r;
 
4581
}
 
4582
 
 
4583
- (void)deleteToMark:(id)sender
 
4584
{
 
4585
    COMMAND_PROLOGUE
 
4586
 
 
4587
    if (![self _canEdit])
 
4588
        return;
 
4589
 
 
4590
    DOMRange *mark = [[self _bridge] markDOMRange];
 
4591
    if (mark == nil) {
 
4592
        if (Frame* coreFrame = core([self _frame]))
 
4593
            coreFrame->editor()->performDelete();
 
4594
    } else {
 
4595
        DOMRange *selection = [self _selectedRange];
 
4596
        DOMRange *r;
 
4597
        NS_DURING
 
4598
            r = unionDOMRanges(mark, selection);
 
4599
        NS_HANDLER
 
4600
            r = selection;
 
4601
        NS_ENDHANDLER
 
4602
        Frame* coreFrame = core([self _frame]);
 
4603
        if (coreFrame)
 
4604
            coreFrame->editor()->deleteRange([r _range], true, true, false, deleteSelectionAction, CharacterGranularity);
 
4605
 
 
4606
    }
 
4607
    [[self _bridge] setMarkDOMRange:[self _selectedRange]];
 
4608
}
 
4609
 
 
4610
- (void)selectToMark:(id)sender
 
4611
{
 
4612
    COMMAND_PROLOGUE
 
4613
 
 
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) {
 
4619
        NSBeep();
 
4620
        return;
 
4621
    }
 
4622
    ExceptionCode ec = 0;
 
4623
    coreFrame->selectionController()->setSelectedRange(core(unionDOMRanges(mark, [self _selectedRange])), DOWNSTREAM, true, ec);
 
4624
}
 
4625
 
 
4626
- (void)swapWithMark:(id)sender
 
4627
{
 
4628
    COMMAND_PROLOGUE
 
4629
 
 
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) {
 
4635
        NSBeep();
 
4636
        return;
 
4637
    }
 
4638
 
 
4639
    ExceptionCode ec = 0;
 
4640
    coreFrame->selectionController()->setSelectedRange(core(mark), DOWNSTREAM, true, ec);
 
4641
    if (ec == 0)
 
4642
        [bridge setMarkDOMRange:selection];
 
4643
}
 
4644
 
 
4645
- (void)transpose:(id)sender
 
4646
{
 
4647
    COMMAND_PROLOGUE
 
4648
 
 
4649
    if (![self _canEdit])
 
4650
        return;
 
4651
 
 
4652
    WebFrameBridge *bridge = [self _bridge];
 
4653
    DOMRange *r = [bridge rangeOfCharactersAroundCaret];
 
4654
    if (!r)
 
4655
        return;
 
4656
    NSString *characters = [bridge stringForRange:r];
 
4657
    if ([characters length] != 2)
 
4658
        return;
 
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])
 
4663
        return;
 
4664
 
 
4665
    Frame* coreFrame = core([self _frame]);
 
4666
    if (!coreFrame)
 
4667
        return;
 
4668
 
 
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];
 
4673
}
 
4674
 
 
4675
- (void)toggleBaseWritingDirection:(id)sender
 
4676
{
 
4677
    COMMAND_PROLOGUE
 
4678
 
 
4679
    if (![self _canEdit])
 
4680
        return;
 
4681
    
 
4682
    NSString *direction = @"RTL";
 
4683
    switch ([[self _bridge] baseWritingDirectionForSelectionStart]) {
 
4684
        case NSWritingDirectionLeftToRight:
 
4685
            break;
 
4686
        case NSWritingDirectionRightToLeft:
 
4687
            direction = @"LTR";
 
4688
            break;
 
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();
 
4693
            break;
 
4694
    }
 
4695
 
 
4696
    DOMCSSStyleDeclaration *style = [self _emptyStyle];
 
4697
    [style setDirection:direction];
 
4698
    [self _applyParagraphStyleToSelection:style withUndoAction:EditActionSetWritingDirection];
 
4699
}
 
4700
 
 
4701
- (void)changeBaseWritingDirection:(id)sender
 
4702
{
 
4703
    COMMAND_PROLOGUE
 
4704
 
 
4705
    if (![self _canEdit])
 
4706
        return;
 
4707
    
 
4708
    NSWritingDirection writingDirection = static_cast<NSWritingDirection>([sender tag]);
 
4709
    
 
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);
 
4713
 
 
4714
    DOMCSSStyleDeclaration *style = [self _emptyStyle];
 
4715
    [style setDirection:writingDirection == NSWritingDirectionLeftToRight ? @"LTR" : @"RTL"];
 
4716
    [self _applyParagraphStyleToSelection:style withUndoAction:EditActionSetWritingDirection];
 
4717
}
 
4718
 
 
4719
- (void)indent:(id)sender
 
4720
{
 
4721
    COMMAND_PROLOGUE
 
4722
 
 
4723
    core([self _frame])->editor()->indent();
 
4724
}
 
4725
 
 
4726
- (void)outdent:(id)sender
 
4727
{
 
4728
    COMMAND_PROLOGUE
 
4729
 
 
4730
    core([self _frame])->editor()->outdent();
 
4731
}
 
4732
 
 
4733
#if 0
 
4734
 
 
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;
 
4738
 
 
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;
 
4742
 
 
4743
// This could be important.
 
4744
- (void)toggleTraditionalCharacterShape:(id)sender;
 
4745
 
 
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;
 
4749
 
 
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;
 
4753
 
 
4754
#endif
 
4755
 
 
4756
// Super-hack alert.
 
4757
// Workaround for bug 3789278.
 
4758
 
 
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
 
4766
{
 
4767
    NSWindow *w = [self window];
 
4768
    if ([w firstResponder] != self)
 
4769
        return NULL;
 
4770
    NSEvent *e = [w currentEvent];
 
4771
    if ([e type] != NSKeyDown)
 
4772
        return NULL;
 
4773
    if (e == _private->keyDownEvent)
 
4774
        return NULL;
 
4775
    NSString *s = [e charactersIgnoringModifiers];
 
4776
    if ([s length] == 0)
 
4777
        return NULL;
 
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:);
 
4787
        default:
 
4788
            return NULL;
 
4789
    }
 
4790
}
 
4791
 
 
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
 
4799
{
 
4800
    if (![super respondsToSelector:selector])
 
4801
        return NO;
 
4802
    SEL arrowKeySelector = [self _arrowKeyDownEventSelectorIfPreprocessing];
 
4803
    if (selector != arrowKeySelector)
 
4804
        return YES;
 
4805
    _private->nextResponderDisabledOnce = YES;
 
4806
    return NO;
 
4807
}
 
4808
 
 
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
 
4815
{
 
4816
    BOOL disabled = _private->nextResponderDisabledOnce;
 
4817
    _private->nextResponderDisabledOnce = NO;
 
4818
    if (disabled && [self _arrowKeyDownEventSelectorIfPreprocessing] != NULL)
 
4819
        return nil;
 
4820
    return [super nextResponder];
 
4821
}
 
4822
 
 
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
 
4828
{
 
4829
    if (Frame* frame = core([self _frame]))
 
4830
        if (FrameView* view = frame->view())
 
4831
            view->updateControlTints();
 
4832
    [super _windowChangedKeyState];
 
4833
}
 
4834
 
 
4835
@end
 
4836
 
 
4837
@implementation WebHTMLView (WebTextSizing)
 
4838
 
 
4839
- (IBAction)_makeTextSmaller:(id)sender
 
4840
{
 
4841
    [self _updateTextSizeMultiplier];
 
4842
}
 
4843
 
 
4844
- (IBAction)_makeTextLarger:(id)sender
 
4845
{
 
4846
    [self _updateTextSizeMultiplier];
 
4847
}
 
4848
 
 
4849
- (IBAction)_makeTextStandardSize:(id)sender
 
4850
{
 
4851
    [self _updateTextSizeMultiplier];
 
4852
}
 
4853
 
 
4854
- (BOOL)_tracksCommonSizeFactor
 
4855
{
 
4856
    return YES;
 
4857
}
 
4858
 
 
4859
- (void)_textSizeMultiplierChanged
 
4860
{
 
4861
    [self _updateTextSizeMultiplier];
 
4862
}
 
4863
 
 
4864
// never sent because we track the common size factor
 
4865
- (BOOL)_canMakeTextSmaller
 
4866
{
 
4867
    ASSERT_NOT_REACHED();
 
4868
    return NO;
 
4869
}
 
4870
 
 
4871
- (BOOL)_canMakeTextLarger
 
4872
{
 
4873
    ASSERT_NOT_REACHED();
 
4874
    return NO;
 
4875
}
 
4876
 
 
4877
- (BOOL)_canMakeTextStandardSize
 
4878
{
 
4879
    ASSERT_NOT_REACHED();
 
4880
    return NO;
 
4881
}
 
4882
 
 
4883
@end
 
4884
 
 
4885
@implementation NSArray (WebHTMLView)
 
4886
 
 
4887
- (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object
 
4888
{
 
4889
#ifndef __LP64__
 
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];
 
4895
#endif
 
4896
}
 
4897
 
 
4898
@end
 
4899
 
 
4900
@implementation WebHTMLView (WebInternal)
 
4901
 
 
4902
- (void)_selectionChanged
 
4903
{
 
4904
    [self _updateSelectionForInputManager];
 
4905
    [self _updateFontPanel];
 
4906
    if (core([self _frame]))
 
4907
        core([self _frame])->editor()->setStartNewKillRingSequence(true);
 
4908
}
 
4909
 
 
4910
- (void)_updateFontPanel
 
4911
{
 
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.
 
4914
    
 
4915
    if (![self _canEdit])
 
4916
        return;
 
4917
    
 
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)
 
4921
        return;
 
4922
    
 
4923
    BOOL multiple = NO;
 
4924
    NSFont *font = [[self _bridge] fontForSelection:&multiple];
 
4925
 
 
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.
 
4928
    if (font == nil)
 
4929
        font = [self _hasSelection] ? [NSFont menuFontOfSize:23] : [NSFont toolTipsFontOfSize:17];
 
4930
    ASSERT(font != nil);
 
4931
 
 
4932
    NSFontManager *fm = [NSFontManager sharedFontManager];
 
4933
    [fm setSelectedFont:font isMultiple:multiple];
 
4934
 
 
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.
 
4938
}
 
4939
 
 
4940
- (BOOL)_canSmartCopyOrDelete
 
4941
{
 
4942
    return [[self _webView] smartInsertDeleteEnabled] && [[self _bridge] selectionGranularity] == WordGranularity;
 
4943
}
 
4944
 
 
4945
- (DOMRange *)_smartDeleteRangeForProposedRange:(DOMRange *)proposedRange
 
4946
{
 
4947
    if (proposedRange == nil || [self _canSmartCopyOrDelete] == NO)
 
4948
        return nil;
 
4949
    
 
4950
    return [[self _bridge] smartDeleteRangeForProposedRange:proposedRange];
 
4951
}
 
4952
 
 
4953
- (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
 
4954
{
 
4955
    if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) {
 
4956
        if (beforeString)
 
4957
            *beforeString = nil;
 
4958
        if (afterString)
 
4959
            *afterString = nil;
 
4960
        return;
 
4961
    }
 
4962
    
 
4963
    [[self _bridge] smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString];
 
4964
}
 
4965
 
 
4966
- (BOOL)_textViewWasFirstResponderAtMouseDownTime:(NSTextView *)textView
 
4967
{
 
4968
    return textView == _private->firstResponderTextViewAtMouseDownTime;
 
4969
}
 
4970
 
 
4971
 
 
4972
- (NSEvent *)_mouseDownEvent
 
4973
{
 
4974
    return _private->mouseDownEvent;
 
4975
}
 
4976
 
 
4977
#ifndef __LP64__
 
4978
- (void)_pauseNullEventsForAllNetscapePlugins
 
4979
{
 
4980
    NSArray *subviews = [self subviews];
 
4981
    unsigned int subviewCount = [subviews count];
 
4982
    unsigned int subviewIndex;
 
4983
    
 
4984
    for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
 
4985
        NSView *subview = [subviews objectAtIndex:subviewIndex];
 
4986
        if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
 
4987
            [(WebBaseNetscapePluginView *)subview stopNullEvents];
 
4988
    }
 
4989
}
 
4990
#endif
 
4991
 
 
4992
#ifndef __LP64__
 
4993
- (void)_resumeNullEventsForAllNetscapePlugins
 
4994
{
 
4995
    NSArray *subviews = [self subviews];
 
4996
    unsigned int subviewCount = [subviews count];
 
4997
    unsigned int subviewIndex;
 
4998
    
 
4999
    for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
 
5000
        NSView *subview = [subviews objectAtIndex:subviewIndex];
 
5001
        if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
 
5002
            [(WebBaseNetscapePluginView *)subview restartNullEvents];
 
5003
    }
 
5004
}
 
5005
#endif
 
5006
 
 
5007
- (void)_willMakeFirstResponderForNodeFocus
 
5008
{
 
5009
    _private->willBecomeFirstResponderForNodeFocus = YES;
 
5010
}
 
5011
 
 
5012
- (id<WebHTMLHighlighter>)_highlighterForType:(NSString*)type
 
5013
{
 
5014
    return [_private->highlighters objectForKey:type];
 
5015
}
 
5016
 
 
5017
- (WebFrame *)_frame
 
5018
{
 
5019
    return [_private->dataSource webFrame];
 
5020
}
 
5021
 
 
5022
- (void)copy:(id)sender
 
5023
{
 
5024
    COMMAND_PROLOGUE
 
5025
 
 
5026
    if (Frame* coreFrame = core([self _frame]))
 
5027
        coreFrame->editor()->copy();
 
5028
}
 
5029
 
 
5030
- (void)cut:(id)sender
 
5031
{
 
5032
    COMMAND_PROLOGUE
 
5033
 
 
5034
    if (Frame* coreFrame = core([self _frame]))
 
5035
        coreFrame->editor()->cut();
 
5036
}
 
5037
 
 
5038
- (void)paste:(id)sender
 
5039
{
 
5040
    COMMAND_PROLOGUE
 
5041
 
 
5042
    Frame* coreFrame = core([self _frame]);
 
5043
    if (coreFrame && coreFrame->editor()->tryDHTMLPaste())
 
5044
        return; // DHTML did the whole operation
 
5045
    if (!coreFrame->editor()->canPaste())
 
5046
        return;
 
5047
    if (coreFrame && coreFrame->selectionController()->isContentRichlyEditable())
 
5048
        [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:YES];
 
5049
    else
 
5050
        coreFrame->editor()->pasteAsPlainText();
 
5051
}
 
5052
 
 
5053
- (void)pasteAsPlainText:(id)sender
 
5054
{
 
5055
    COMMAND_PROLOGUE
 
5056
 
 
5057
    if (![self _canEdit])
 
5058
        return;
 
5059
    [self _pasteAsPlainTextWithPasteboard:[NSPasteboard generalPasteboard]];
 
5060
}
 
5061
 
 
5062
- (void)closeIfNotCurrentView
 
5063
{
 
5064
    if ([[[self _frame] frameView] documentView] != self)
 
5065
        [self close];
 
5066
}
 
5067
 
 
5068
- (DOMDocumentFragment*)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
 
5069
{
 
5070
    BOOL discard;
 
5071
    return [self _documentFragmentFromPasteboard:pasteboard inContext:nil allowPlainText:NO chosePlainText:&discard];
 
5072
}
 
5073
 
 
5074
#ifndef BUILDING_ON_TIGER
 
5075
 
 
5076
- (BOOL)isGrammarCheckingEnabled
 
5077
{
 
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];
 
5081
}
 
5082
 
 
5083
- (void)setGrammarCheckingEnabled:(BOOL)flag
 
5084
{
 
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];
 
5088
}
 
5089
 
 
5090
- (void)toggleGrammarChecking:(id)sender
 
5091
{
 
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];
 
5095
}
 
5096
 
 
5097
 
 
5098
static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
 
5099
{
 
5100
    NSArray *screens = [NSScreen screens];
 
5101
    
 
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);
 
5106
    }
 
5107
    
 
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);
 
5110
}
 
5111
 
 
5112
#endif
 
5113
 
 
5114
- (void)_lookUpInDictionaryFromMenu:(id)sender
 
5115
{
 
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)
 
5119
        return;
 
5120
 
 
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.
 
5124
 
 
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";
 
5129
#else
 
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";
 
5133
#endif
 
5134
 
 
5135
    static bool lookedForFunction = false;
 
5136
    static ServiceWindowShowFunction dictionaryServiceWindowShow = NULL;
 
5137
 
 
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;
 
5144
    }
 
5145
 
 
5146
    ASSERT(dictionaryServiceWindowShow);
 
5147
    if (!dictionaryServiceWindowShow) {
 
5148
        NSLog(@"Couldn't find the %s function in %s", functionName, frameworkPath); 
 
5149
        return;
 
5150
    }
 
5151
 
 
5152
    NSAttributedString *attrString = [self selectedAttributedString];
 
5153
 
 
5154
#ifdef BUILDING_ON_TIGER
 
5155
    // FIXME: must check for right-to-left here
 
5156
    NSWritingDirection writingDirection = NSWritingDirectionLeftToRight;
 
5157
 
 
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);
 
5165
#else
 
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();
 
5170
 
 
5171
    NSDictionary *attributes = [attrString fontAttributesInRange:NSMakeRange(0,1)];
 
5172
    NSFont *font = [attributes objectForKey:NSFontAttributeName];
 
5173
    if (font)
 
5174
        rect.origin.y += [font ascender];
 
5175
 
 
5176
    NSPoint windowPoint = [self convertPoint:rect.origin toView:nil];
 
5177
    NSPoint screenPoint = [[self window] convertBaseToScreen:windowPoint];
 
5178
 
 
5179
    dictionaryServiceWindowShow(nil, attrString, CFRangeMake(0, [attrString length]), nil, 
 
5180
                                coreGraphicsScreenPointForAppKitScreenPoint(screenPoint), false, nil);
 
5181
#endif    
 
5182
}
 
5183
 
 
5184
- (void)_hoverFeedbackSuspendedChanged
 
5185
{
 
5186
    [self _updateMouseoverWithFakeEvent];
 
5187
}
 
5188
 
 
5189
- (BOOL)_interceptEditingKeyEvent:(KeyboardEvent*)event shouldSaveCommand:(BOOL)shouldSave
 
5190
{
 
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;
 
5199
        
 
5200
    if (const PlatformKeyboardEvent* platformEvent = event->keyEvent()) {
 
5201
        NSEvent *macEvent = platformEvent->macEvent();
 
5202
        if ([macEvent type] == NSKeyDown && [_private->compController filterKeyDown:macEvent])
 
5203
            return true;
 
5204
        parameters.event = event;
 
5205
        _private->interpretKeyEventsParameters = &parameters;
 
5206
        _private->receivedNOOP = NO;
 
5207
        KeypressCommand command = event->keypressCommand();
 
5208
        bool hasKeypressCommand = !command.commandNames.isEmpty() || !command.text.isEmpty();
 
5209
 
 
5210
        if (parameters.shouldSaveCommand || !hasKeypressCommand)
 
5211
            [self interpretKeyEvents:[NSArray arrayWithObject:macEvent]];
 
5212
        else {
 
5213
            if (!command.text.isEmpty())
 
5214
                [self insertText:command.text];
 
5215
            else {
 
5216
                size_t size = command.commandNames.size();
 
5217
                for (size_t i = 0; i < size; ++i)
 
5218
                    [self doCommandBySelector:NSSelectorFromString(command.commandNames[i])];
 
5219
            }
 
5220
        }
 
5221
        _private->interpretKeyEventsParameters = 0;
 
5222
    }
 
5223
    return (!_private->receivedNOOP && parameters.eventWasHandled) || parameters.consumedByIM;
 
5224
}
 
5225
 
 
5226
- (WebCore::CachedImage*)promisedDragTIFFDataSource 
 
5227
{
 
5228
    return _private->promisedDragTIFFDataSource;
 
5229
}
 
5230
 
 
5231
- (void)setPromisedDragTIFFDataSource:(WebCore::CachedImage*)source
 
5232
{
 
5233
    if (source)
 
5234
        source->ref(promisedDataClient());
 
5235
    
 
5236
    if (_private->promisedDragTIFFDataSource)
 
5237
        _private->promisedDragTIFFDataSource->deref(promisedDataClient());
 
5238
    _private->promisedDragTIFFDataSource = source;
 
5239
}
 
5240
 
 
5241
#undef COMMAND_PROLOGUE
 
5242
 
 
5243
@end
 
5244
 
 
5245
@implementation WebHTMLView (WebNSTextInputSupport)
 
5246
 
 
5247
- (NSArray *)validAttributesForMarkedText
 
5248
{
 
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);
 
5260
    }
 
5261
    LOG(TextInput, "validAttributesForMarkedText -> (...)");
 
5262
    return validAttributes;
 
5263
}
 
5264
 
 
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)
 
5268
{
 
5269
    return coreFrame && !coreFrame->selectionController()->isNone() && coreFrame->selectionController()->isContentEditable();
 
5270
}
 
5271
 
 
5272
- (NSAttributedString *)textStorage
 
5273
{
 
5274
    if (!isTextInput(core([self _frame]))) {
 
5275
        LOG(TextInput, "textStorage -> nil");
 
5276
        return nil;
 
5277
    }
 
5278
    NSAttributedString *result = [self attributedSubstringFromRange:NSMakeRange(0, UINT_MAX)];
 
5279
    
 
5280
    LOG(TextInput, "textStorage -> \"%s\"", result ? [[result string] UTF8String] : "");
 
5281
    
 
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];
 
5284
}
 
5285
 
 
5286
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
 
5287
{
 
5288
    NSWindow *window = [self window];
 
5289
    WebFrameBridge *bridge = [self _bridge];
 
5290
 
 
5291
    if (window)
 
5292
        thePoint = [window convertScreenToBase:thePoint];
 
5293
    thePoint = [self convertPoint:thePoint fromView:nil];
 
5294
 
 
5295
    DOMRange *range = [bridge characterRangeAtPoint:thePoint];
 
5296
    if (!range) {
 
5297
        LOG(TextInput, "characterIndexForPoint:(%f, %f) -> NSNotFound", thePoint.x, thePoint.y);
 
5298
        return NSNotFound;
 
5299
    }
 
5300
    
 
5301
    unsigned result = [bridge convertDOMRangeToNSRange:range].location;
 
5302
    LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result);
 
5303
    return result;
 
5304
}
 
5305
 
 
5306
- (NSRect)firstRectForCharacterRange:(NSRange)theRange
 
5307
{    
 
5308
    WebFrameBridge *bridge = [self _bridge];
 
5309
    
 
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;
 
5315
    
 
5316
    DOMRange *range = [bridge convertNSRangeToDOMRange:theRange];
 
5317
    if (!range) {
 
5318
        LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (0, 0, 0, 0)", theRange.location, theRange.length);
 
5319
        return NSMakeRect(0, 0, 0, 0);
 
5320
    }
 
5321
    
 
5322
    ASSERT([range startContainer]);
 
5323
    ASSERT([range endContainer]);
 
5324
    
 
5325
    NSRect resultRect = [bridge firstRectForDOMRange:range];
 
5326
    resultRect = [self convertRect:resultRect toView:nil];
 
5327
 
 
5328
    NSWindow *window = [self window];
 
5329
    if (window)
 
5330
        resultRect.origin = [window convertBaseToScreen:resultRect.origin];
 
5331
    
 
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);
 
5333
    return resultRect;
 
5334
}
 
5335
 
 
5336
- (NSRange)selectedRange
 
5337
{
 
5338
    if (!isTextInput(core([self _frame]))) {
 
5339
        LOG(TextInput, "selectedRange -> (NSNotFound, 0)");
 
5340
        return NSMakeRange(NSNotFound, 0);
 
5341
    }
 
5342
    NSRange result = [[self _bridge] selectedNSRange];
 
5343
 
 
5344
    LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length);
 
5345
    return result;
 
5346
}
 
5347
 
 
5348
- (NSRange)markedRange
 
5349
{
 
5350
    if (![self hasMarkedText]) {
 
5351
        LOG(TextInput, "markedRange -> (NSNotFound, 0)");
 
5352
        return NSMakeRange(NSNotFound, 0);
 
5353
    }
 
5354
    NSRange result = [[self _bridge] markedTextNSRange];
 
5355
 
 
5356
    LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length);
 
5357
    return result;
 
5358
}
 
5359
 
 
5360
- (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange
 
5361
{
 
5362
    if (!isTextInput(core([self _frame]))) {
 
5363
        LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
 
5364
        return nil;
 
5365
    }
 
5366
    WebFrameBridge *bridge = [self _bridge];
 
5367
    DOMRange *domRange = [bridge convertNSRangeToDOMRange:nsRange];
 
5368
    if (!domRange) {
 
5369
        LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
 
5370
        return nil;
 
5371
    }
 
5372
 
 
5373
    NSAttributedString *result = [NSAttributedString _web_attributedStringFromRange:core(domRange)];
 
5374
    
 
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)];
 
5382
    }
 
5383
    LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%s\"", nsRange.location, nsRange.length, [[result string] UTF8String]);
 
5384
    return result;
 
5385
}
 
5386
 
 
5387
// test for 10.4 because of <rdar://problem/4243463>
 
5388
#ifdef BUILDING_ON_TIGER
 
5389
- (long)conversationIdentifier
 
5390
{
 
5391
    return (long)self;
 
5392
}
 
5393
#else
 
5394
- (NSInteger)conversationIdentifier
 
5395
{
 
5396
    return (NSInteger)self;
 
5397
}
 
5398
#endif
 
5399
 
 
5400
- (BOOL)hasMarkedText
 
5401
{
 
5402
    BOOL result = [[self _bridge] markedTextDOMRange] != nil;
 
5403
 
 
5404
    LOG(TextInput, "hasMarkedText -> %u", result);
 
5405
    return result;
 
5406
}
 
5407
 
 
5408
- (void)unmarkText
 
5409
{
 
5410
    LOG(TextInput, "unmarkText");
 
5411
 
 
5412
    // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
 
5413
    WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
 
5414
    _private->interpretKeyEventsParameters = 0;
 
5415
 
 
5416
    if (parameters) {
 
5417
        parameters->eventWasHandled = YES;
 
5418
        parameters->consumedByIM = NO;
 
5419
    }
 
5420
    
 
5421
    if (Frame* coreFrame = core([self _frame]))
 
5422
        coreFrame->editor()->unmarkText();
 
5423
}
 
5424
 
 
5425
- (void)_extractAttributes:(NSArray **)a ranges:(NSArray **)r fromAttributedString:(NSAttributedString *)string
 
5426
{
 
5427
    int length = [[string string] length];
 
5428
    int i = 0;
 
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;
 
5437
    }
 
5438
    *a = attributes;
 
5439
    *r = ranges;
 
5440
}
 
5441
 
 
5442
- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
 
5443
{
 
5444
    BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString
 
5445
 
 
5446
    LOG(TextInput, "setMarkedText:\"%s\" selectedRange:(%u, %u)", isAttributedString ? [[string string] UTF8String] : [string UTF8String], newSelRange.location, newSelRange.length);
 
5447
 
 
5448
    // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
 
5449
    WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
 
5450
    _private->interpretKeyEventsParameters = 0;
 
5451
 
 
5452
    if (parameters) {
 
5453
        parameters->eventWasHandled = YES;
 
5454
        parameters->consumedByIM = NO;
 
5455
    }
 
5456
    
 
5457
    Frame* coreFrame = core([self _frame]);
 
5458
    if (!coreFrame)
 
5459
        return;
 
5460
 
 
5461
    WebFrameBridge *bridge = [self _bridge];
 
5462
 
 
5463
    if (![self _isEditable])
 
5464
        return;
 
5465
 
 
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.
 
5473
        if (rangeString)
 
5474
            [[self _bridge] selectNSRange:NSRangeFromString(rangeString)];
 
5475
    }
 
5476
 
 
5477
    coreFrame->editor()->setIgnoreMarkedTextSelectionChange(true);
 
5478
 
 
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();
 
5482
 
 
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];
 
5489
    }
 
5490
 
 
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);
 
5495
 
 
5496
    coreFrame->editor()->setIgnoreMarkedTextSelectionChange(false);
 
5497
}
 
5498
 
 
5499
- (void)doCommandBySelector:(SEL)selector
 
5500
{
 
5501
    LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
 
5502
 
 
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;
 
5506
    if (parameters)
 
5507
        parameters->consumedByIM = NO;
 
5508
 
 
5509
    if (selector == @selector(noop:)) {
 
5510
        _private->receivedNOOP = YES;
 
5511
        return;
 
5512
    }
 
5513
 
 
5514
    KeyboardEvent* event = parameters ? parameters->event : 0;
 
5515
    bool shouldSaveCommand = parameters && parameters->shouldSaveCommand;
 
5516
 
 
5517
    if (event && shouldSaveCommand) {
 
5518
        KeypressCommand command = event->keypressCommand();
 
5519
        command.commandNames.append(NSStringFromSelector(selector));
 
5520
        event->setKeypressCommand(command);
 
5521
    } else {
 
5522
        // Make sure that only direct calls to doCommandBySelector: see the parameters by setting to 0.
 
5523
        _private->interpretKeyEventsParameters = 0;
 
5524
 
 
5525
        bool eventWasHandled = true;
 
5526
 
 
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);
 
5538
            else {
 
5539
                _private->selectorForDoCommandBySelector = selector;
 
5540
                [super doCommandBySelector:selector];
 
5541
                _private->selectorForDoCommandBySelector = 0;
 
5542
            }
 
5543
        }
 
5544
 
 
5545
        if (parameters)
 
5546
            parameters->eventWasHandled = eventWasHandled;
 
5547
 
 
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;
 
5551
    }
 
5552
}
 
5553
 
 
5554
- (void)insertText:(id)string
 
5555
{
 
5556
    BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString
 
5557
 
 
5558
    LOG(TextInput, "insertText:\"%s\"", isAttributedString ? [[string string] UTF8String] : [string UTF8String]);
 
5559
 
 
5560
    WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
 
5561
    _private->interpretKeyEventsParameters = 0;
 
5562
    if (parameters)
 
5563
        parameters->consumedByIM = NO;
 
5564
 
 
5565
    // We don't support inserting an attributed string but input methods don't appear to require this.
 
5566
    NSString *text;
 
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]);
 
5576
        if (rangeString) {
 
5577
            [[self _bridge] selectNSRange:NSRangeFromString(rangeString)];
 
5578
            isFromInputMethod = YES;
 
5579
        }
 
5580
    } else
 
5581
        text = string;
 
5582
 
 
5583
    bool eventHandled = false;
 
5584
    if ([text length]) {
 
5585
        KeyboardEvent* event = parameters ? parameters->event : 0;
 
5586
 
 
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);
 
5598
            return;
 
5599
        }
 
5600
        
 
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);
 
5605
    }
 
5606
    
 
5607
    if (!parameters)
 
5608
        return;
 
5609
    
 
5610
    if (isFromInputMethod) {
 
5611
        // Allow doCommandBySelector: to be called after insertText: by resetting interpretKeyEventsParameters
 
5612
        _private->interpretKeyEventsParameters = parameters;
 
5613
        parameters->consumedByIM = YES;
 
5614
        return;
 
5615
    }
 
5616
    
 
5617
    parameters->eventWasHandled = eventHandled;
 
5618
}
 
5619
 
 
5620
- (BOOL)_selectionIsInsideMarkedText
 
5621
{
 
5622
    WebFrameBridge *bridge = [self _bridge];
 
5623
    DOMRange *selection = [self _selectedRange];
 
5624
    DOMRange *markedTextRange = [bridge markedTextDOMRange];
 
5625
 
 
5626
    ASSERT([markedTextRange startContainer] == [markedTextRange endContainer]);
 
5627
 
 
5628
    if ([selection startContainer] != [markedTextRange startContainer]) 
 
5629
        return NO;
 
5630
 
 
5631
    if ([selection endContainer] != [markedTextRange startContainer])
 
5632
        return NO;
 
5633
 
 
5634
    if ([selection startOffset] < [markedTextRange startOffset])
 
5635
        return NO;
 
5636
 
 
5637
    if ([selection endOffset] > [markedTextRange endOffset])
 
5638
        return NO;
 
5639
 
 
5640
    return YES;
 
5641
}
 
5642
 
 
5643
- (void)_updateSelectionForInputManager
 
5644
{
 
5645
    if (![self hasMarkedText])
 
5646
        return;
 
5647
 
 
5648
    Frame* coreFrame = core([self _frame]);
 
5649
    if (!coreFrame)
 
5650
        return;
 
5651
 
 
5652
    if (coreFrame->editor()->ignoreMarkedTextSelectionChange())
 
5653
        return;
 
5654
 
 
5655
    if ([self _selectionIsInsideMarkedText]) {
 
5656
        DOMRange *selection = [self _selectedRange];
 
5657
        DOMRange *markedTextDOMRange = [[self _bridge] markedTextDOMRange];
 
5658
 
 
5659
        unsigned markedSelectionStart = [selection startOffset] - [markedTextDOMRange startOffset];
 
5660
        unsigned markedSelectionLength = [selection endOffset] - [selection startOffset];
 
5661
        NSRange newSelectionRange = NSMakeRange(markedSelectionStart, markedSelectionLength);
 
5662
 
 
5663
        [[NSInputManager currentInputManager] markedTextSelectionChanged:newSelectionRange client:self];
 
5664
    } else {
 
5665
        [self unmarkText];
 
5666
        [[NSInputManager currentInputManager] markedTextAbandoned:self];
 
5667
    }
 
5668
}
 
5669
 
 
5670
@end
 
5671
 
 
5672
/*
 
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.
 
5675
 
 
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**.
 
5679
 */
 
5680
 
 
5681
@implementation WebTextCompleteController
 
5682
 
 
5683
- (id)initWithHTMLView:(WebHTMLView *)view
 
5684
{
 
5685
    self = [super init];
 
5686
    if (!self)
 
5687
        return nil;
 
5688
    _view = view;
 
5689
    return self;
 
5690
}
 
5691
 
 
5692
- (void)dealloc
 
5693
{
 
5694
    [_popupWindow release];
 
5695
    [_completions release];
 
5696
    [_originalString release];
 
5697
    
 
5698
    [super dealloc];
 
5699
}
 
5700
 
 
5701
- (void)_insertMatch:(NSString *)match
 
5702
{
 
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];
 
5708
}
 
5709
 
 
5710
// mostly lifted from NSTextView_KeyBinding.m
 
5711
- (void)_buildUI
 
5712
{
 
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];
 
5720
    
 
5721
    _tableView = [[NSTableView alloc] initWithFrame:tableFrame];
 
5722
    [_tableView setAutoresizingMask:NSViewWidthSizable];
 
5723
    [_tableView addTableColumn:column];
 
5724
    [column release];
 
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:)];
 
5733
    
 
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];
 
5740
    
 
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];
 
5749
}
 
5750
 
 
5751
// mostly lifted from NSTextView_KeyBinding.m
 
5752
- (void)_placePopupWindow:(NSPoint)topLeft
 
5753
{
 
5754
    int numberToShow = [_completions count];
 
5755
    if (numberToShow > 20) {
 
5756
        numberToShow = 20;
 
5757
    }
 
5758
 
 
5759
    NSRect windowFrame;
 
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;
 
5766
    int maxIndex = -1;
 
5767
    int i;
 
5768
    for (i = 0; i < numberToShow; i++) {
 
5769
        float width = ceilf([[_completions objectAtIndex:i] sizeWithAttributes:attributes].width);
 
5770
        if (width > maxWidth) {
 
5771
            maxWidth = width;
 
5772
            maxIndex = i;
 
5773
        }
 
5774
    }
 
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);
 
5779
        maxWidth += 5.0f;
 
5780
        windowFrame.size.width = MAX(maxWidth, windowFrame.size.width);
 
5781
        maxWidth = MIN(400.0f, windowFrame.size.width);
 
5782
    }
 
5783
    [_popupWindow setFrame:windowFrame display:NO];
 
5784
    
 
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];
 
5792
}
 
5793
 
 
5794
- (void)doCompletion
 
5795
{
 
5796
    if (!_popupWindow) {
 
5797
        NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
 
5798
        if (!checker) {
 
5799
            LOG_ERROR("No NSSpellChecker");
 
5800
            return;
 
5801
        }
 
5802
 
 
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]];
 
5810
 
 
5811
        // Reject some NOP cases
 
5812
        if ([prefix collapsed]) {
 
5813
            NSBeep();
 
5814
            return;
 
5815
        }
 
5816
        NSString *prefixStr = [bridge stringForRange:prefix];
 
5817
        NSString *trimmedPrefix = [prefixStr stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
 
5818
        if ([trimmedPrefix length] == 0) {
 
5819
            NSBeep();
 
5820
            return;
 
5821
        }
 
5822
        prefixLength = [prefixStr length];
 
5823
 
 
5824
        // Lookup matches
 
5825
        [_completions release];
 
5826
        _completions = [checker completionsForPartialWordRange:NSMakeRange(0, [prefixStr length]) inString:prefixStr language:nil inSpellDocumentWithTag:[[_view _webView] spellCheckerDocumentTag]];
 
5827
        [_completions retain];
 
5828
    
 
5829
        if (!_completions || [_completions count] == 0) {
 
5830
            NSBeep();
 
5831
        } else if ([_completions count] == 1) {
 
5832
            [self _insertMatch:[_completions objectAtIndex:0]];
 
5833
        } else {
 
5834
            ASSERT(!_originalString);       // this should only be set IFF we have a popup window
 
5835
            _originalString = [[bridge stringForRange:selection] retain];
 
5836
            [self _buildUI];
 
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];
 
5843
        }
 
5844
    } else {
 
5845
        [self endRevertingChange:YES moveLeft:NO];
 
5846
    }
 
5847
}
 
5848
 
 
5849
- (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft
 
5850
{
 
5851
    if (_popupWindow) {
 
5852
        // tear down UI
 
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];
 
5857
        _popupWindow = nil;
 
5858
 
 
5859
        if (revertChange) {
 
5860
            WebFrameBridge *bridge = [_view _bridge];
 
5861
            [bridge replaceSelectionWithText:_originalString selectReplacement:YES smartReplace:NO];
 
5862
        } else if ([_view _hasSelection]) {
 
5863
            if (goLeft)
 
5864
                [_view moveBackward:nil];
 
5865
            else
 
5866
                [_view moveForward:nil];
 
5867
        }
 
5868
        [_originalString release];
 
5869
        _originalString = nil;
 
5870
    }
 
5871
    // else there is no state to abort if the window was not up
 
5872
}
 
5873
 
 
5874
- (BOOL)popupWindowIsOpen
 
5875
{
 
5876
    return _popupWindow != nil;
 
5877
}
 
5878
 
 
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
 
5882
{
 
5883
    if (_popupWindow) {
 
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];
 
5891
            }
 
5892
            return YES;
 
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];
 
5898
            }
 
5899
            return YES;
 
5900
        } else if (c == NSRightArrowFunctionKey || c == '\n' || c == '\r' || c == '\t') {
 
5901
            [self endRevertingChange:NO moveLeft:NO];
 
5902
            return YES;
 
5903
        } else if (c == NSLeftArrowFunctionKey) {
 
5904
            [self endRevertingChange:NO moveLeft:YES];
 
5905
            return YES;
 
5906
        } else if (c == 0x1b || c == NSF5FunctionKey) {
 
5907
            [self endRevertingChange:YES moveLeft:NO];
 
5908
            return YES;
 
5909
        } else if (c == ' ' || ispunct(c)) {
 
5910
            [self endRevertingChange:NO moveLeft:NO];
 
5911
            return NO;  // let the char get inserted
 
5912
        }
 
5913
    }
 
5914
    return NO;
 
5915
}
 
5916
 
 
5917
- (void)_reflectSelection
 
5918
{
 
5919
    int selectedRow = [_tableView selectedRow];
 
5920
    ASSERT(selectedRow >= 0 && selectedRow < (int)[_completions count]);
 
5921
    [self _insertMatch:[_completions objectAtIndex:selectedRow]];
 
5922
}
 
5923
 
 
5924
- (void)tableAction:(id)sender
 
5925
{
 
5926
    [self _reflectSelection];
 
5927
    [self endRevertingChange:NO moveLeft:NO];
 
5928
}
 
5929
 
 
5930
- (int)numberOfRowsInTableView:(NSTableView *)tableView
 
5931
{
 
5932
    return [_completions count];
 
5933
}
 
5934
 
 
5935
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
 
5936
{
 
5937
    return [_completions objectAtIndex:row];
 
5938
}
 
5939
 
 
5940
- (void)tableViewSelectionDidChange:(NSNotification *)notification
 
5941
{
 
5942
    [self _reflectSelection];
 
5943
}
 
5944
 
 
5945
@end
 
5946
 
 
5947
@implementation WebHTMLView (WebDocumentPrivateProtocols)
 
5948
 
 
5949
- (NSRect)selectionRect
 
5950
{
 
5951
    if ([self _hasSelection])
 
5952
        return core([self _frame])->selectionRect();
 
5953
    return NSZeroRect;
 
5954
}
 
5955
 
 
5956
- (NSArray *)selectionTextRects
 
5957
{
 
5958
    if (![self _hasSelection])
 
5959
        return nil;
 
5960
    
 
5961
    Vector<FloatRect> list;
 
5962
    core([self _frame])->selectionTextRects(list);
 
5963
 
 
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]]];
 
5968
    
 
5969
    return result;
 
5970
}
 
5971
 
 
5972
- (NSView *)selectionView
 
5973
{
 
5974
    return self;
 
5975
}
 
5976
 
 
5977
- (NSImage *)selectionImageForcingBlackText:(BOOL)forceBlackText
 
5978
{
 
5979
    if ([self _hasSelection])
 
5980
        return core([self _frame])->selectionImage(forceBlackText);
 
5981
    return nil;
 
5982
}
 
5983
 
 
5984
- (NSImage *)selectionImageForcingWhiteText:(BOOL)forceWhiteText
 
5985
{
 
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];
 
5989
}
 
5990
 
 
5991
- (NSRect)selectionImageRect
 
5992
{
 
5993
    if ([self _hasSelection])
 
5994
        return core([self _frame])->selectionRect();
 
5995
    return NSZeroRect;
 
5996
}
 
5997
 
 
5998
- (NSArray *)pasteboardTypesForSelection
 
5999
{
 
6000
    if ([self _canSmartCopyOrDelete]) {
 
6001
        NSMutableArray *types = [[[[self class] _selectionPasteboardTypes] mutableCopy] autorelease];
 
6002
        [types addObject:WebSmartPastePboardType];
 
6003
        return types;
 
6004
    } else {
 
6005
        return [[self class] _selectionPasteboardTypes];
 
6006
    }
 
6007
}
 
6008
 
 
6009
- (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
 
6010
{
 
6011
    [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:nil];
 
6012
}
 
6013
 
 
6014
- (void)selectAll
 
6015
{
 
6016
    Frame* coreFrame = core([self _frame]);
 
6017
    if (coreFrame)
 
6018
        coreFrame->selectionController()->selectAll();
 
6019
}
 
6020
 
 
6021
- (void)deselectAll
 
6022
{
 
6023
    Frame* coreFrame = core([self _frame]);
 
6024
    if (!coreFrame)
 
6025
        return;
 
6026
    coreFrame->selectionController()->clear();
 
6027
}
 
6028
 
 
6029
- (NSString *)string
 
6030
{
 
6031
    return [[self _bridge] stringForRange:[self _documentRange]];
 
6032
}
 
6033
 
 
6034
- (NSAttributedString *)_attributeStringFromDOMRange:(DOMRange *)range
 
6035
{
 
6036
    NSAttributedString *attributedString;
 
6037
#if !LOG_DISABLED        
 
6038
    double start = CFAbsoluteTimeGetCurrent();
 
6039
#endif    
 
6040
    attributedString = [[[NSAttributedString alloc] _initWithDOMRange:range] autorelease];
 
6041
#if !LOG_DISABLED
 
6042
    double duration = CFAbsoluteTimeGetCurrent() - start;
 
6043
    LOG(Timing, "creating attributed string from selection took %f seconds.", duration);
 
6044
#endif
 
6045
    return attributedString;
 
6046
}
 
6047
 
 
6048
- (NSAttributedString *)attributedString
 
6049
{
 
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];
 
6056
    }
 
6057
    return attributedString;
 
6058
}
 
6059
 
 
6060
- (NSString *)selectedString
 
6061
{
 
6062
    return [[self _bridge] selectedString];
 
6063
}
 
6064
 
 
6065
- (NSAttributedString *)selectedAttributedString
 
6066
{
 
6067
    NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[self _selectedRange]];
 
6068
    if (!attributedString) {
 
6069
        Frame* coreFrame = core([self _frame]);
 
6070
        if (coreFrame) {
 
6071
            RefPtr<Range> range = coreFrame->selectionController()->selection().toRange();
 
6072
            attributedString = [NSAttributedString _web_attributedStringFromRange:range.get()];
 
6073
        }
 
6074
    }
 
6075
    return attributedString;
 
6076
}
 
6077
 
 
6078
- (BOOL)supportsTextEncoding
 
6079
{
 
6080
    return YES;
 
6081
}
 
6082
 
 
6083
- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection
 
6084
{
 
6085
    if (![string length])
 
6086
        return NO;
 
6087
    
 
6088
    return [[self _bridge] searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:startInSelection];
 
6089
}
 
6090
 
 
6091
@end
 
6092
 
 
6093
@implementation WebHTMLView (WebDocumentInternalProtocols)
 
6094
 
 
6095
- (NSDictionary *)elementAtPoint:(NSPoint)point
 
6096
{
 
6097
    return [self elementAtPoint:point allowShadowContent:NO];
 
6098
}
 
6099
 
 
6100
- (NSDictionary *)elementAtPoint:(NSPoint)point allowShadowContent:(BOOL)allow;
 
6101
{
 
6102
    Frame* coreframe = core([self _frame]);
 
6103
    if (coreframe) 
 
6104
        return [[[WebElementDictionary alloc] initWithHitTestResult:coreframe->eventHandler()->hitTestResultAtPoint(IntPoint(point), allow)] autorelease];
 
6105
    return nil;
 
6106
}
 
6107
 
 
6108
- (NSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag limit:(NSUInteger)limit
 
6109
{
 
6110
    return [[self _bridge] markAllMatchesForText:string caseSensitive:caseFlag limit:limit];
 
6111
}
 
6112
 
 
6113
- (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue
 
6114
{
 
6115
    [[self _bridge] setMarkedTextMatchesAreHighlighted:newValue];
 
6116
}
 
6117
 
 
6118
- (BOOL)markedTextMatchesAreHighlighted
 
6119
{
 
6120
    return [[self _bridge] markedTextMatchesAreHighlighted];
 
6121
}
 
6122
 
 
6123
- (void)unmarkAllTextMatches
 
6124
{
 
6125
    return [[self _bridge] unmarkAllTextMatches];
 
6126
}
 
6127
 
 
6128
- (NSArray *)rectsForTextMatches
 
6129
{
 
6130
    return [[self _bridge] rectsForTextMatches];
 
6131
}
 
6132
 
 
6133
@end
 
6134
 
 
6135
// This is used by AppKit and is included here so that WebDataProtocolScheme is only defined once.
 
6136
@implementation NSURL (WebDataURL)
 
6137
 
 
6138
+ (NSURL *)_web_uniqueWebDataURL
 
6139
{
 
6140
    CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
 
6141
    NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
 
6142
    CFRelease(UUIDRef);
 
6143
    NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", WebDataProtocolScheme, UUIDString]];
 
6144
    CFRelease(UUIDString);
 
6145
    return URL;
 
6146
}
 
6147
 
 
6148
@end