summaryrefslogtreecommitdiffstats
path: root/Source/WebKit/mac/WebView/WebHTMLView.mm
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit/mac/WebView/WebHTMLView.mm')
-rw-r--r--Source/WebKit/mac/WebView/WebHTMLView.mm176
1 files changed, 127 insertions, 49 deletions
diff --git a/Source/WebKit/mac/WebView/WebHTMLView.mm b/Source/WebKit/mac/WebView/WebHTMLView.mm
index e611e2b..fb1d794 100644
--- a/Source/WebKit/mac/WebView/WebHTMLView.mm
+++ b/Source/WebKit/mac/WebView/WebHTMLView.mm
@@ -53,7 +53,6 @@
#import "WebKitNSStringExtras.h"
#import "WebKitVersionChecks.h"
#import "WebLocalizableStringsInternal.h"
-#import "WebNSAttributedStringExtras.h"
#import "WebNSEventExtras.h"
#import "WebNSFileManagerExtras.h"
#import "WebNSImageExtras.h"
@@ -94,6 +93,7 @@
#import <WebCore/Frame.h>
#import <WebCore/FrameLoader.h>
#import <WebCore/FrameView.h>
+#import <WebCore/HTMLConverter.h>
#import <WebCore/HTMLNames.h>
#import <WebCore/HitTestResult.h>
#import <WebCore/Image.h>
@@ -112,6 +112,7 @@
#import <WebCore/Text.h>
#import <WebCore/WebCoreObjCExtras.h>
#import <WebCore/WebFontCache.h>
+#import <WebCore/WebNSAttributedStringExtras.h>
#import <WebCore/markup.h>
#import <WebKit/DOM.h>
#import <WebKit/DOMExtensions.h>
@@ -431,6 +432,7 @@ static CachedResourceClient* promisedDataClient()
@interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
- (void)_setPrinting:(BOOL)printing minimumPageLogicalWidth:(float)minPageWidth logicalHeight:(float)minPageHeight maximumPageLogicalWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize paginateScreenContent:(BOOL)paginateScreenContent;
+- (void)_updateSecureInputState;
@end
@class NSTextInputContext;
@@ -465,6 +467,7 @@ struct WebHTMLViewInterpretKeyEventsParameters {
bool eventInterpretationHadSideEffects;
bool shouldSaveCommands;
bool consumedByIM;
+ bool executingSavedKeypressCommands;
};
@interface WebHTMLViewPrivate : NSObject {
@@ -493,6 +496,11 @@ struct WebHTMLViewInterpretKeyEventsParameters {
// This state is saved each time selection changes, because computing it causes style recalc, which is not always safe to do.
BOOL exposeInputContext;
+ // Track whether the view has set a secure input state.
+ BOOL isInSecureInputState;
+
+ BOOL _forceUpdateSecureInputState;
+
NSPoint lastScrollPosition;
#ifndef BUILDING_ON_TIGER
BOOL inScrollPositionChanged;
@@ -1041,12 +1049,10 @@ static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
[pasteboard setData:RTFDData forType:NSRTFDPboardType];
}
if ([types containsObject:NSRTFPboardType]) {
- if (attributedString == nil) {
+ if (!attributedString)
attributedString = [self selectedAttributedString];
- }
- if ([attributedString containsAttachments]) {
- attributedString = [attributedString _web_attributedStringByStrippingAttachmentCharacters];
- }
+ if ([attributedString containsAttachments])
+ attributedString = attributedStringByStrippingAttachmentCharacters(attributedString);
NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
[pasteboard setData:RTFData forType:NSRTFPboardType];
}
@@ -1994,6 +2000,11 @@ static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info)
// remove tooltips before clearing _private so removeTrackingRect: will work correctly
[self removeAllToolTips];
+ if (_private->isInSecureInputState) {
+ DisableSecureEventInput();
+ _private->isInSecureInputState = NO;
+ }
+
[_private clear];
}
@@ -3459,8 +3470,10 @@ static void setMenuTargets(NSMenu* menu)
NSWindow *keyWindow = [notification object];
- if (keyWindow == [self window])
+ if (keyWindow == [self window]) {
[self addMouseMovedObserver];
+ [self _updateSecureInputState];
+ }
}
- (void)windowDidResignKey:(NSNotification *)notification
@@ -3475,8 +3488,10 @@ static void setMenuTargets(NSMenu* menu)
if (formerKeyWindow == [self window])
[self removeMouseMovedObserver];
- if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet])
+ if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
+ [self _updateSecureInputState];
[_private->completionController endRevertingChange:NO moveLeft:NO];
+ }
}
- (void)windowWillClose:(NSNotification *)notification
@@ -3809,6 +3824,10 @@ static BOOL isInPasswordField(Frame* coreFrame)
[NSApp updateWindows];
}
+ _private->_forceUpdateSecureInputState = YES;
+ [self _updateSecureInputState];
+ _private->_forceUpdateSecureInputState = NO;
+
frame->editor()->setStartNewKillRingSequence(true);
Page* page = frame->page();
@@ -3834,6 +3853,10 @@ static BOOL isInPasswordField(Frame* coreFrame)
{
BOOL resign = [super resignFirstResponder];
if (resign) {
+ if (_private->isInSecureInputState) {
+ DisableSecureEventInput();
+ _private->isInSecureInputState = NO;
+ }
[_private->completionController endRevertingChange:NO moveLeft:NO];
Frame* coreFrame = core([self _frame]);
if (!coreFrame)
@@ -4163,15 +4186,12 @@ static BOOL isInPasswordField(Frame* coreFrame)
// the current event prevents that from causing a problem inside WebKit or AppKit code.
[[event retain] autorelease];
- Frame* coreFrame = core([self _frame]);
- if (coreFrame)
- coreFrame->eventHandler()->capsLockStateMayHaveChanged();
-
RetainPtr<WebHTMLView> selfProtector = self;
+ Frame* coreFrame = core([self _frame]);
unsigned short keyCode = [event keyCode];
- //Don't make an event from the num lock and function keys
+ // Don't make an event from the num lock and function keys.
if (coreFrame && keyCode != 0 && keyCode != 10 && keyCode != 63) {
coreFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(event));
return;
@@ -4370,8 +4390,11 @@ static BOOL isInPasswordField(Frame* coreFrame)
- (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event
{
- ASSERT([self _webView]);
- if (![[[self _webView] preferences] respectStandardStyleKeyEquivalents])
+ WebView *webView = [self _webView];
+ if (!webView)
+ return NO;
+
+ if (![[webView preferences] respectStandardStyleKeyEquivalents])
return NO;
if (![self _canEdit])
@@ -5428,26 +5451,35 @@ static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
[self _updateMouseoverWithFakeEvent];
}
-- (void)_executeSavedEditingCommands
+- (void)_executeSavedKeypressCommands
{
WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
if (!parameters || parameters->event->keypressCommands().isEmpty())
return;
+ // We could be called again if the execution of one command triggers a call to selectedRange.
+ // In this case, the state is up to date, and we don't need to execute any more saved commands to return a result
+ if (parameters->executingSavedKeypressCommands)
+ return;
+
// Avoid an infinite loop that would occur if executing a command appended it to event->keypressCommands() again.
bool wasSavingCommands = parameters->shouldSaveCommands;
parameters->shouldSaveCommands = false;
-
+ parameters->executingSavedKeypressCommands = true;
+
const Vector<KeypressCommand>& commands = parameters->event->keypressCommands();
for (size_t i = 0; i < commands.size(); ++i) {
if (commands[i].commandName == "insertText:")
[self insertText:commands[i].text];
+ else if (commands[i].commandName == "noop:")
+ ; // Do nothing. This case can be removed once <rdar://problem/9025012> is fixed.
else
[self doCommandBySelector:NSSelectorFromString(commands[i].commandName)];
}
parameters->event->keypressCommands().clear();
parameters->shouldSaveCommands = wasSavingCommands;
+ parameters->executingSavedKeypressCommands = false;
}
- (BOOL)_interpretKeyEvent:(KeyboardEvent*)event savingCommands:(BOOL)savingCommands
@@ -5458,6 +5490,7 @@ static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
WebHTMLViewInterpretKeyEventsParameters parameters;
parameters.eventInterpretationHadSideEffects = false;
parameters.shouldSaveCommands = savingCommands;
+ parameters.executingSavedKeypressCommands = false;
// If we're intercepting the initial IM call we assume that the IM has consumed the event,
// and only change this assumption if one of the NSTextInput/Responder callbacks is used.
// We assume the IM will *not* consume hotkey sequences
@@ -5501,7 +5534,7 @@ static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
// If there are no text insertion commands, default keydown handler is the right time to execute the commands.
// Keypress (Char event) handler is the latest opportunity to execute.
if (!haveTextInsertionCommands || platformEvent->type() == PlatformKeyboardEvent::Char)
- [self _executeSavedEditingCommands];
+ [self _executeSavedKeypressCommands];
}
_private->interpretKeyEventsParameters = 0;
@@ -5746,7 +5779,7 @@ static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
{
- [self _executeSavedEditingCommands];
+ [self _executeSavedKeypressCommands];
NSWindow *window = [self window];
WebFrame *frame = [self _frame];
@@ -5768,7 +5801,7 @@ static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
- (NSRect)firstRectForCharacterRange:(NSRange)theRange
{
- [self _executeSavedEditingCommands];
+ [self _executeSavedKeypressCommands];
WebFrame *frame = [self _frame];
@@ -5800,7 +5833,7 @@ static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
- (NSRange)selectedRange
{
- [self _executeSavedEditingCommands];
+ [self _executeSavedKeypressCommands];
if (!isTextInput(core([self _frame]))) {
LOG(TextInput, "selectedRange -> (NSNotFound, 0)");
@@ -5814,7 +5847,7 @@ static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
- (NSRange)markedRange
{
- [self _executeSavedEditingCommands];
+ [self _executeSavedKeypressCommands];
WebFrame *webFrame = [self _frame];
Frame* coreFrame = core(webFrame);
@@ -5828,7 +5861,7 @@ static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
- (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange
{
- [self _executeSavedEditingCommands];
+ [self _executeSavedKeypressCommands];
WebFrame *frame = [self _frame];
Frame* coreFrame = core(frame);
@@ -5836,15 +5869,15 @@ static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
return nil;
}
- DOMRange *domRange = [frame _convertNSRangeToDOMRange:nsRange];
- if (!domRange) {
+ RefPtr<Range> range = [frame _convertToDOMRange:nsRange];
+ if (!range) {
LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
return nil;
}
- NSAttributedString *result = [NSAttributedString _web_attributedStringFromRange:core(domRange)];
+ NSAttributedString *result = [WebHTMLConverter editingAttributedStringFromRange:range.get()];
- // [NSAttributedString(WebKitExtras) _web_attributedStringFromRange:] insists on inserting a trailing
+ // [WebHTMLConverter editingAttributedStringFromRange:] insists on inserting a trailing
// whitespace at the end of the string which breaks the ATOK input method. <rdar://problem/5400551>
// To work around this we truncate the resultant string to the correct length.
if ([result length] > nsRange.length) {
@@ -5871,23 +5904,27 @@ static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
- (BOOL)hasMarkedText
{
- [self _executeSavedEditingCommands];
-
Frame* coreFrame = core([self _frame]);
BOOL result = coreFrame && coreFrame->editor()->hasComposition();
+
+ if (result) {
+ // A saved command can confirm a composition, but it cannot start a new one.
+ [self _executeSavedKeypressCommands];
+ result = coreFrame->editor()->hasComposition();
+ }
+
LOG(TextInput, "hasMarkedText -> %u", result);
return result;
}
- (void)unmarkText
{
- [self _executeSavedEditingCommands];
+ [self _executeSavedKeypressCommands];
LOG(TextInput, "unmarkText");
// Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
- _private->interpretKeyEventsParameters = 0;
if (parameters) {
parameters->eventInterpretationHadSideEffects = true;
@@ -5920,15 +5957,15 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde
- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
{
- [self _executeSavedEditingCommands];
+ [self _executeSavedKeypressCommands];
- BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString
+ BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
+ ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length);
// Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
- _private->interpretKeyEventsParameters = 0;
if (parameters) {
parameters->eventInterpretationHadSideEffects = true;
@@ -5943,20 +5980,25 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde
return;
Vector<CompositionUnderline> underlines;
- NSString *text = string;
+ NSString *text;
+ NSRange replacementRange = { NSNotFound, 0 };
if (isAttributedString) {
- unsigned markedTextLength = [(NSString *)string length];
- NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:0 inRange:NSMakeRange(0, markedTextLength)];
+ // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
+ text = [string string];
+ NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:0 inRange:NSMakeRange(0, [text length])];
LOG(TextInput, " ReplacementRange: %@", rangeString);
// The AppKit adds a 'secret' property to the string that contains the replacement range.
// The replacement range is the range of the the text that should be replaced with the new string.
if (rangeString)
- [[self _frame] _selectNSRange:NSRangeFromString(rangeString)];
+ replacementRange = NSRangeFromString(rangeString);
- text = [string string];
extractUnderlines(string, underlines);
- }
+ } else
+ text = string;
+
+ if (replacementRange.location != NSNotFound)
+ [[self _frame] _selectNSRange:replacementRange];
coreFrame->editor()->setComposition(text, underlines, newSelRange.location, NSMaxRange(newSelRange));
}
@@ -6018,7 +6060,8 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde
- (void)insertText:(id)string
{
- BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString
+ BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
+ ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
@@ -6026,20 +6069,19 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde
if (parameters)
parameters->consumedByIM = false;
- // We don't support inserting an attributed string but input methods don't appear to require this.
RefPtr<Frame> coreFrame = core([self _frame]);
NSString *text;
+ NSRange replacementRange = { NSNotFound, 0 };
bool isFromInputMethod = coreFrame && coreFrame->editor()->hasComposition();
+
if (isAttributedString) {
+ // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
+ // It does not look like any input methods ever use insertText: with attributes other than NSTextInputReplacementRangeAttributeName.
text = [string string];
- // We deal with the NSTextInputReplacementRangeAttributeName attribute from NSAttributedString here
- // simply because it is used by at least one Input Method -- it corresonds to the kEventParamTextInputSendReplaceRange
- // event in TSM. This behavior matches that of -[WebHTMLView setMarkedText:selectedRange:] when it receives an
- // NSAttributedString
NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:0 inRange:NSMakeRange(0, [text length])];
LOG(TextInput, " ReplacementRange: %@", rangeString);
if (rangeString) {
- [[self _frame] _selectNSRange:NSRangeFromString(rangeString)];
+ replacementRange = NSRangeFromString(rangeString);
isFromInputMethod = true;
}
} else
@@ -6062,6 +6104,9 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde
if (!coreFrame || !coreFrame->editor()->canEdit())
return;
+ if (replacementRange.location != NSNotFound)
+ [[self _frame] _selectNSRange:replacementRange];
+
bool eventHandled = false;
String eventText = text;
eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
@@ -6078,6 +6123,37 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde
parameters->eventInterpretationHadSideEffects |= eventHandled;
}
+- (void)_updateSecureInputState
+{
+ if (![[self window] isKeyWindow] || ([[self window] firstResponder] != self && !_private->_forceUpdateSecureInputState)) {
+ if (_private->isInSecureInputState) {
+ DisableSecureEventInput();
+ _private->isInSecureInputState = NO;
+ }
+ return;
+ }
+
+ Frame* coreFrame = core([self _frame]);
+ if (!coreFrame)
+ return;
+
+ if (isInPasswordField(coreFrame)) {
+ if (!_private->isInSecureInputState)
+ EnableSecureEventInput();
+ _private->isInSecureInputState = YES;
+ // WebKit substitutes nil for input context when in password field, which corresponds to null TSMDocument. So, there is
+ // no need to call TSMGetActiveDocument(), which may return an incorrect result when selection hasn't been yet updated
+ // after focusing a node.
+ static CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
+ TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag, sizeof(CFArrayRef), &inputSources);
+ } else {
+ if (_private->isInSecureInputState)
+ DisableSecureEventInput();
+ _private->isInSecureInputState = NO;
+ TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
+ }
+}
+
- (void)_updateSelectionForInputManager
{
Frame* coreFrame = core([self _frame]);
@@ -6093,6 +6169,8 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde
[NSApp updateWindows];
}
+ [self _updateSecureInputState];
+
if (!coreFrame->editor()->hasComposition())
return;
@@ -6214,7 +6292,7 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde
NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[document _documentRange]];
if (!attributedString) {
Document* coreDocument = core(document);
- attributedString = [NSAttributedString _web_attributedStringFromRange:Range::create(coreDocument, coreDocument, 0, coreDocument, coreDocument->childNodeCount()).get()];
+ attributedString = [WebHTMLConverter editingAttributedStringFromRange:Range::create(coreDocument, coreDocument, 0, coreDocument, coreDocument->childNodeCount()).get()];
}
return attributedString;
}
@@ -6231,7 +6309,7 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde
Frame* coreFrame = core([self _frame]);
if (coreFrame) {
RefPtr<Range> range = coreFrame->selection()->selection().toNormalizedRange();
- attributedString = [NSAttributedString _web_attributedStringFromRange:range.get()];
+ attributedString = [WebHTMLConverter editingAttributedStringFromRange:range.get()];
}
}
return attributedString;