WebEditorClient.mm 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. /*
  2. * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
  3. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
  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. #import "WebEditorClient.h"
  30. #import "DOMCSSStyleDeclarationInternal.h"
  31. #import "DOMDocumentFragmentInternal.h"
  32. #import "DOMHTMLElementInternal.h"
  33. #import "DOMHTMLInputElementInternal.h"
  34. #import "DOMHTMLTextAreaElementInternal.h"
  35. #import "DOMNodeInternal.h"
  36. #import "DOMRangeInternal.h"
  37. #import "WebArchive.h"
  38. #import "WebDataSourceInternal.h"
  39. #import "WebDelegateImplementationCaching.h"
  40. #import "WebDocument.h"
  41. #import "WebEditingDelegatePrivate.h"
  42. #import "WebFormDelegate.h"
  43. #import "WebFrameInternal.h"
  44. #import "WebHTMLView.h"
  45. #import "WebHTMLViewInternal.h"
  46. #import "WebKitLogging.h"
  47. #import "WebKitVersionChecks.h"
  48. #import "WebLocalizableStringsInternal.h"
  49. #import "WebNSURLExtras.h"
  50. #import "WebResourceInternal.h"
  51. #import "WebViewInternal.h"
  52. #import <WebCore/ArchiveResource.h>
  53. #import <WebCore/Document.h>
  54. #import <WebCore/DocumentFragment.h>
  55. #import <WebCore/HTMLInputElement.h>
  56. #import <WebCore/HTMLNames.h>
  57. #import <WebCore/HTMLTextAreaElement.h>
  58. #import <WebCore/KeyboardEvent.h>
  59. #import <WebCore/LegacyWebArchive.h>
  60. #import <WebCore/Page.h>
  61. #import <WebCore/PlatformKeyboardEvent.h>
  62. #import <WebCore/RunLoop.h>
  63. #import <WebCore/Settings.h>
  64. #import <WebCore/SpellChecker.h>
  65. #import <WebCore/StylePropertySet.h>
  66. #import <WebCore/UndoStep.h>
  67. #import <WebCore/UserTypingGestureIndicator.h>
  68. #import <WebCore/WebCoreObjCExtras.h>
  69. #import <runtime/InitializeThreading.h>
  70. #import <wtf/MainThread.h>
  71. #import <wtf/PassRefPtr.h>
  72. #import <wtf/text/WTFString.h>
  73. using namespace WebCore;
  74. using namespace HTMLNames;
  75. #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
  76. @interface NSSpellChecker (WebNSSpellCheckerDetails)
  77. - (NSString *)languageForWordRange:(NSRange)range inString:(NSString *)string orthography:(NSOrthography *)orthography;
  78. @end
  79. #endif
  80. @interface NSAttributedString (WebNSAttributedStringDetails)
  81. - (id)_initWithDOMRange:(DOMRange*)range;
  82. - (DOMDocumentFragment*)_documentFromRange:(NSRange)range document:(DOMDocument*)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
  83. @end
  84. static WebViewInsertAction kit(EditorInsertAction coreAction)
  85. {
  86. return static_cast<WebViewInsertAction>(coreAction);
  87. }
  88. static const int InvalidCorrectionPanelTag = 0;
  89. @interface WebUndoStep : NSObject
  90. {
  91. RefPtr<UndoStep> m_step;
  92. }
  93. + (WebUndoStep *)stepWithUndoStep:(PassRefPtr<UndoStep>)step;
  94. - (UndoStep *)step;
  95. @end
  96. @implementation WebUndoStep
  97. + (void)initialize
  98. {
  99. JSC::initializeThreading();
  100. WTF::initializeMainThreadToProcessMainThread();
  101. WebCore::RunLoop::initializeMainRunLoop();
  102. WebCoreObjCFinalizeOnMainThread(self);
  103. }
  104. - (id)initWithUndoStep:(PassRefPtr<UndoStep>)step
  105. {
  106. ASSERT(step);
  107. self = [super init];
  108. if (!self)
  109. return nil;
  110. m_step = step;
  111. return self;
  112. }
  113. - (void)dealloc
  114. {
  115. if (WebCoreObjCScheduleDeallocateOnMainThread([WebUndoStep class], self))
  116. return;
  117. [super dealloc];
  118. }
  119. - (void)finalize
  120. {
  121. ASSERT_MAIN_THREAD();
  122. [super finalize];
  123. }
  124. + (WebUndoStep *)stepWithUndoStep:(PassRefPtr<UndoStep>)step
  125. {
  126. return [[[WebUndoStep alloc] initWithUndoStep:step] autorelease];
  127. }
  128. - (UndoStep *)step
  129. {
  130. return m_step.get();
  131. }
  132. @end
  133. @interface WebEditorUndoTarget : NSObject
  134. {
  135. }
  136. - (void)undoEditing:(id)arg;
  137. - (void)redoEditing:(id)arg;
  138. @end
  139. @implementation WebEditorUndoTarget
  140. - (void)undoEditing:(id)arg
  141. {
  142. ASSERT([arg isKindOfClass:[WebUndoStep class]]);
  143. [arg step]->unapply();
  144. }
  145. - (void)redoEditing:(id)arg
  146. {
  147. ASSERT([arg isKindOfClass:[WebUndoStep class]]);
  148. [arg step]->reapply();
  149. }
  150. @end
  151. void WebEditorClient::pageDestroyed()
  152. {
  153. delete this;
  154. }
  155. WebEditorClient::WebEditorClient(WebView *webView)
  156. : m_webView(webView)
  157. , m_undoTarget([[[WebEditorUndoTarget alloc] init] autorelease])
  158. , m_haveUndoRedoOperations(false)
  159. {
  160. }
  161. WebEditorClient::~WebEditorClient()
  162. {
  163. }
  164. bool WebEditorClient::isContinuousSpellCheckingEnabled()
  165. {
  166. return [m_webView isContinuousSpellCheckingEnabled];
  167. }
  168. void WebEditorClient::toggleContinuousSpellChecking()
  169. {
  170. [m_webView toggleContinuousSpellChecking:nil];
  171. }
  172. bool WebEditorClient::isGrammarCheckingEnabled()
  173. {
  174. return [m_webView isGrammarCheckingEnabled];
  175. }
  176. void WebEditorClient::toggleGrammarChecking()
  177. {
  178. [m_webView toggleGrammarChecking:nil];
  179. }
  180. int WebEditorClient::spellCheckerDocumentTag()
  181. {
  182. return [m_webView spellCheckerDocumentTag];
  183. }
  184. bool WebEditorClient::shouldDeleteRange(Range* range)
  185. {
  186. return [[m_webView _editingDelegateForwarder] webView:m_webView
  187. shouldDeleteDOMRange:kit(range)];
  188. }
  189. #if ENABLE(DELETION_UI)
  190. bool WebEditorClient::shouldShowDeleteInterface(HTMLElement* element)
  191. {
  192. return [[m_webView _editingDelegateForwarder] webView:m_webView
  193. shouldShowDeleteInterfaceForElement:kit(element)];
  194. }
  195. #endif
  196. bool WebEditorClient::smartInsertDeleteEnabled()
  197. {
  198. Page* page = [m_webView page];
  199. if (!page)
  200. return false;
  201. return page->settings()->smartInsertDeleteEnabled();
  202. }
  203. bool WebEditorClient::isSelectTrailingWhitespaceEnabled()
  204. {
  205. Page* page = [m_webView page];
  206. if (!page)
  207. return false;
  208. return page->settings()->selectTrailingWhitespaceEnabled();
  209. }
  210. bool WebEditorClient::shouldApplyStyle(StylePropertySet* style, Range* range)
  211. {
  212. RefPtr<MutableStylePropertySet> mutableStyle = style->isMutable() ? static_cast<MutableStylePropertySet*>(style) : style->mutableCopy();
  213. return [[m_webView _editingDelegateForwarder] webView:m_webView
  214. shouldApplyStyle:kit(mutableStyle->ensureCSSStyleDeclaration()) toElementsInDOMRange:kit(range)];
  215. }
  216. bool WebEditorClient::shouldMoveRangeAfterDelete(Range* range, Range* rangeToBeReplaced)
  217. {
  218. return [[m_webView _editingDelegateForwarder] webView:m_webView
  219. shouldMoveRangeAfterDelete:kit(range) replacingRange:kit(rangeToBeReplaced)];
  220. }
  221. bool WebEditorClient::shouldBeginEditing(Range* range)
  222. {
  223. return [[m_webView _editingDelegateForwarder] webView:m_webView
  224. shouldBeginEditingInDOMRange:kit(range)];
  225. return false;
  226. }
  227. bool WebEditorClient::shouldEndEditing(Range* range)
  228. {
  229. return [[m_webView _editingDelegateForwarder] webView:m_webView
  230. shouldEndEditingInDOMRange:kit(range)];
  231. }
  232. bool WebEditorClient::shouldInsertText(const String& text, Range* range, EditorInsertAction action)
  233. {
  234. WebView* webView = m_webView;
  235. return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:kit(range) givenAction:kit(action)];
  236. }
  237. bool WebEditorClient::shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity selectionAffinity, bool stillSelecting)
  238. {
  239. return [m_webView _shouldChangeSelectedDOMRange:kit(fromRange) toDOMRange:kit(toRange) affinity:kit(selectionAffinity) stillSelecting:stillSelecting];
  240. }
  241. void WebEditorClient::didBeginEditing()
  242. {
  243. [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidBeginEditingNotification object:m_webView];
  244. }
  245. void WebEditorClient::respondToChangedContents()
  246. {
  247. NSView <WebDocumentView> *view = [[[m_webView selectedFrame] frameView] documentView];
  248. if ([view isKindOfClass:[WebHTMLView class]])
  249. [(WebHTMLView *)view _updateFontPanel];
  250. [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidChangeNotification object:m_webView];
  251. }
  252. void WebEditorClient::respondToChangedSelection(Frame* frame)
  253. {
  254. NSView<WebDocumentView> *documentView = [[kit(frame) frameView] documentView];
  255. if ([documentView isKindOfClass:[WebHTMLView class]])
  256. [(WebHTMLView *)documentView _selectionChanged];
  257. // FIXME: This quirk is needed due to <rdar://problem/5009625> - We can phase it out once Aperture can adopt the new behavior on their end
  258. if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_APERTURE_QUIRK) && [[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.apple.Aperture"])
  259. return;
  260. [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidChangeSelectionNotification object:m_webView];
  261. }
  262. void WebEditorClient::didEndEditing()
  263. {
  264. [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidEndEditingNotification object:m_webView];
  265. }
  266. void WebEditorClient::didWriteSelectionToPasteboard()
  267. {
  268. [[m_webView _editingDelegateForwarder] webView:m_webView didWriteSelectionToPasteboard:[NSPasteboard generalPasteboard]];
  269. }
  270. void WebEditorClient::willWriteSelectionToPasteboard(WebCore::Range*)
  271. {
  272. // Not implemented WebKit, only WebKit2.
  273. }
  274. void WebEditorClient::getClientPasteboardDataForRange(WebCore::Range*, Vector<String>& pasteboardTypes, Vector<RefPtr<WebCore::SharedBuffer> >& pasteboardData)
  275. {
  276. // Not implemented WebKit, only WebKit2.
  277. }
  278. void WebEditorClient::didSetSelectionTypesForPasteboard()
  279. {
  280. [[m_webView _editingDelegateForwarder] webView:m_webView didSetSelectionTypesForPasteboard:[NSPasteboard generalPasteboard]];
  281. }
  282. NSString *WebEditorClient::userVisibleString(NSURL *URL)
  283. {
  284. return [URL _web_userVisibleString];
  285. }
  286. NSURL *WebEditorClient::canonicalizeURL(NSURL *URL)
  287. {
  288. return [URL _webkit_canonicalize];
  289. }
  290. NSURL *WebEditorClient::canonicalizeURLString(NSString *URLString)
  291. {
  292. NSURL *URL = nil;
  293. if ([URLString _webkit_looksLikeAbsoluteURL])
  294. URL = [[NSURL _web_URLWithUserTypedString:URLString] _webkit_canonicalize];
  295. return URL;
  296. }
  297. static NSArray *createExcludedElementsForAttributedStringConversion()
  298. {
  299. NSArray *elements = [[NSArray alloc] initWithObjects:
  300. // Omit style since we want style to be inline so the fragment can be easily inserted.
  301. @"style",
  302. // Omit xml so the result is not XHTML.
  303. @"xml",
  304. // Omit tags that will get stripped when converted to a fragment anyway.
  305. @"doctype", @"html", @"head", @"body",
  306. // Omit deprecated tags.
  307. @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
  308. // Omit object so no file attachments are part of the fragment.
  309. @"object", nil];
  310. CFRetain(elements);
  311. return elements;
  312. }
  313. DocumentFragment* WebEditorClient::documentFragmentFromAttributedString(NSAttributedString *string, Vector<RefPtr<ArchiveResource> >& resources)
  314. {
  315. static NSArray *excludedElements = createExcludedElementsForAttributedStringConversion();
  316. NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys: excludedElements, NSExcludedElementsDocumentAttribute,
  317. nil, @"WebResourceHandler", nil];
  318. NSArray *subResources;
  319. DOMDocumentFragment* fragment = [string _documentFromRange:NSMakeRange(0, [string length])
  320. document:[[m_webView mainFrame] DOMDocument]
  321. documentAttributes:dictionary
  322. subresources:&subResources];
  323. for (WebResource* resource in subResources)
  324. resources.append([resource _coreResource]);
  325. [dictionary release];
  326. return core(fragment);
  327. }
  328. void WebEditorClient::setInsertionPasteboard(const String& pasteboardName)
  329. {
  330. NSPasteboard *pasteboard = pasteboardName.isEmpty() ? nil : [NSPasteboard pasteboardWithName:pasteboardName];
  331. [m_webView _setInsertionPasteboard:pasteboard];
  332. }
  333. #if USE(APPKIT)
  334. void WebEditorClient::uppercaseWord()
  335. {
  336. [m_webView uppercaseWord:nil];
  337. }
  338. void WebEditorClient::lowercaseWord()
  339. {
  340. [m_webView lowercaseWord:nil];
  341. }
  342. void WebEditorClient::capitalizeWord()
  343. {
  344. [m_webView capitalizeWord:nil];
  345. }
  346. #endif
  347. #if USE(AUTOMATIC_TEXT_REPLACEMENT)
  348. void WebEditorClient::showSubstitutionsPanel(bool show)
  349. {
  350. NSPanel *spellingPanel = [[NSSpellChecker sharedSpellChecker] substitutionsPanel];
  351. if (show)
  352. [spellingPanel orderFront:nil];
  353. else
  354. [spellingPanel orderOut:nil];
  355. }
  356. bool WebEditorClient::substitutionsPanelIsShowing()
  357. {
  358. return [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
  359. }
  360. void WebEditorClient::toggleSmartInsertDelete()
  361. {
  362. [m_webView toggleSmartInsertDelete:nil];
  363. }
  364. bool WebEditorClient::isAutomaticQuoteSubstitutionEnabled()
  365. {
  366. return [m_webView isAutomaticQuoteSubstitutionEnabled];
  367. }
  368. void WebEditorClient::toggleAutomaticQuoteSubstitution()
  369. {
  370. [m_webView toggleAutomaticQuoteSubstitution:nil];
  371. }
  372. bool WebEditorClient::isAutomaticLinkDetectionEnabled()
  373. {
  374. return [m_webView isAutomaticLinkDetectionEnabled];
  375. }
  376. void WebEditorClient::toggleAutomaticLinkDetection()
  377. {
  378. [m_webView toggleAutomaticLinkDetection:nil];
  379. }
  380. bool WebEditorClient::isAutomaticDashSubstitutionEnabled()
  381. {
  382. return [m_webView isAutomaticDashSubstitutionEnabled];
  383. }
  384. void WebEditorClient::toggleAutomaticDashSubstitution()
  385. {
  386. [m_webView toggleAutomaticDashSubstitution:nil];
  387. }
  388. bool WebEditorClient::isAutomaticTextReplacementEnabled()
  389. {
  390. return [m_webView isAutomaticTextReplacementEnabled];
  391. }
  392. void WebEditorClient::toggleAutomaticTextReplacement()
  393. {
  394. [m_webView toggleAutomaticTextReplacement:nil];
  395. }
  396. bool WebEditorClient::isAutomaticSpellingCorrectionEnabled()
  397. {
  398. return [m_webView isAutomaticSpellingCorrectionEnabled];
  399. }
  400. void WebEditorClient::toggleAutomaticSpellingCorrection()
  401. {
  402. [m_webView toggleAutomaticSpellingCorrection:nil];
  403. }
  404. #endif // USE(AUTOMATIC_TEXT_REPLACEMENT)
  405. bool WebEditorClient::shouldInsertNode(Node *node, Range* replacingRange, EditorInsertAction givenAction)
  406. {
  407. return [[m_webView _editingDelegateForwarder] webView:m_webView shouldInsertNode:kit(node) replacingDOMRange:kit(replacingRange) givenAction:(WebViewInsertAction)givenAction];
  408. }
  409. static NSString* undoNameForEditAction(EditAction editAction)
  410. {
  411. switch (editAction) {
  412. case EditActionUnspecified: return nil;
  413. case EditActionSetColor: return UI_STRING_KEY_INTERNAL("Set Color", "Set Color (Undo action name)", "Undo action name");
  414. case EditActionSetBackgroundColor: return UI_STRING_KEY_INTERNAL("Set Background Color", "Set Background Color (Undo action name)", "Undo action name");
  415. case EditActionTurnOffKerning: return UI_STRING_KEY_INTERNAL("Turn Off Kerning", "Turn Off Kerning (Undo action name)", "Undo action name");
  416. case EditActionTightenKerning: return UI_STRING_KEY_INTERNAL("Tighten Kerning", "Tighten Kerning (Undo action name)", "Undo action name");
  417. case EditActionLoosenKerning: return UI_STRING_KEY_INTERNAL("Loosen Kerning", "Loosen Kerning (Undo action name)", "Undo action name");
  418. case EditActionUseStandardKerning: return UI_STRING_KEY_INTERNAL("Use Standard Kerning", "Use Standard Kerning (Undo action name)", "Undo action name");
  419. case EditActionTurnOffLigatures: return UI_STRING_KEY_INTERNAL("Turn Off Ligatures", "Turn Off Ligatures (Undo action name)", "Undo action name");
  420. case EditActionUseStandardLigatures: return UI_STRING_KEY_INTERNAL("Use Standard Ligatures", "Use Standard Ligatures (Undo action name)", "Undo action name");
  421. case EditActionUseAllLigatures: return UI_STRING_KEY_INTERNAL("Use All Ligatures", "Use All Ligatures (Undo action name)", "Undo action name");
  422. case EditActionRaiseBaseline: return UI_STRING_KEY_INTERNAL("Raise Baseline", "Raise Baseline (Undo action name)", "Undo action name");
  423. case EditActionLowerBaseline: return UI_STRING_KEY_INTERNAL("Lower Baseline", "Lower Baseline (Undo action name)", "Undo action name");
  424. case EditActionSetTraditionalCharacterShape: return UI_STRING_KEY_INTERNAL("Set Traditional Character Shape", "Set Traditional Character Shape (Undo action name)", "Undo action name");
  425. case EditActionSetFont: return UI_STRING_KEY_INTERNAL("Set Font", "Set Font (Undo action name)", "Undo action name");
  426. case EditActionChangeAttributes: return UI_STRING_KEY_INTERNAL("Change Attributes", "Change Attributes (Undo action name)", "Undo action name");
  427. case EditActionAlignLeft: return UI_STRING_KEY_INTERNAL("Align Left", "Align Left (Undo action name)", "Undo action name");
  428. case EditActionAlignRight: return UI_STRING_KEY_INTERNAL("Align Right", "Align Right (Undo action name)", "Undo action name");
  429. case EditActionCenter: return UI_STRING_KEY_INTERNAL("Center", "Center (Undo action name)", "Undo action name");
  430. case EditActionJustify: return UI_STRING_KEY_INTERNAL("Justify", "Justify (Undo action name)", "Undo action name");
  431. case EditActionSetWritingDirection: return UI_STRING_KEY_INTERNAL("Set Writing Direction", "Set Writing Direction (Undo action name)", "Undo action name");
  432. case EditActionSubscript: return UI_STRING_KEY_INTERNAL("Subscript", "Subscript (Undo action name)", "Undo action name");
  433. case EditActionSuperscript: return UI_STRING_KEY_INTERNAL("Superscript", "Superscript (Undo action name)", "Undo action name");
  434. case EditActionUnderline: return UI_STRING_KEY_INTERNAL("Underline", "Underline (Undo action name)", "Undo action name");
  435. case EditActionOutline: return UI_STRING_KEY_INTERNAL("Outline", "Outline (Undo action name)", "Undo action name");
  436. case EditActionUnscript: return UI_STRING_KEY_INTERNAL("Unscript", "Unscript (Undo action name)", "Undo action name");
  437. case EditActionDrag: return UI_STRING_KEY_INTERNAL("Drag", "Drag (Undo action name)", "Undo action name");
  438. case EditActionCut: return UI_STRING_KEY_INTERNAL("Cut", "Cut (Undo action name)", "Undo action name");
  439. case EditActionPaste: return UI_STRING_KEY_INTERNAL("Paste", "Paste (Undo action name)", "Undo action name");
  440. case EditActionPasteFont: return UI_STRING_KEY_INTERNAL("Paste Font", "Paste Font (Undo action name)", "Undo action name");
  441. case EditActionPasteRuler: return UI_STRING_KEY_INTERNAL("Paste Ruler", "Paste Ruler (Undo action name)", "Undo action name");
  442. case EditActionTyping: return UI_STRING_KEY_INTERNAL("Typing", "Typing (Undo action name)", "Undo action name");
  443. case EditActionCreateLink: return UI_STRING_KEY_INTERNAL("Create Link", "Create Link (Undo action name)", "Undo action name");
  444. case EditActionUnlink: return UI_STRING_KEY_INTERNAL("Unlink", "Unlink (Undo action name)", "Undo action name");
  445. case EditActionInsertList: return UI_STRING_KEY_INTERNAL("Insert List", "Insert List (Undo action name)", "Undo action name");
  446. case EditActionFormatBlock: return UI_STRING_KEY_INTERNAL("Formatting", "Format Block (Undo action name)", "Undo action name");
  447. case EditActionIndent: return UI_STRING_KEY_INTERNAL("Indent", "Indent (Undo action name)", "Undo action name");
  448. case EditActionOutdent: return UI_STRING_KEY_INTERNAL("Outdent", "Outdent (Undo action name)", "Undo action name");
  449. case EditActionBold: return UI_STRING_KEY_INTERNAL("Bold", "Bold (Undo action name)", "Undo action name");
  450. case EditActionItalics: return UI_STRING_KEY_INTERNAL("Italics", "Italics (Undo action name)", "Undo action name");
  451. }
  452. return nil;
  453. }
  454. void WebEditorClient::registerUndoOrRedoStep(PassRefPtr<UndoStep> step, bool isRedo)
  455. {
  456. ASSERT(step);
  457. NSUndoManager *undoManager = [m_webView undoManager];
  458. NSString *actionName = undoNameForEditAction(step->editingAction());
  459. WebUndoStep *webEntry = [WebUndoStep stepWithUndoStep:step];
  460. [undoManager registerUndoWithTarget:m_undoTarget.get() selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:webEntry];
  461. if (actionName)
  462. [undoManager setActionName:actionName];
  463. m_haveUndoRedoOperations = YES;
  464. }
  465. void WebEditorClient::registerUndoStep(PassRefPtr<UndoStep> cmd)
  466. {
  467. registerUndoOrRedoStep(cmd, false);
  468. }
  469. void WebEditorClient::registerRedoStep(PassRefPtr<UndoStep> cmd)
  470. {
  471. registerUndoOrRedoStep(cmd, true);
  472. }
  473. void WebEditorClient::clearUndoRedoOperations()
  474. {
  475. if (m_haveUndoRedoOperations) {
  476. // workaround for <rdar://problem/4645507> NSUndoManager dies
  477. // with uncaught exception when undo items cleared while
  478. // groups are open
  479. NSUndoManager *undoManager = [m_webView undoManager];
  480. int groupingLevel = [undoManager groupingLevel];
  481. for (int i = 0; i < groupingLevel; ++i)
  482. [undoManager endUndoGrouping];
  483. [undoManager removeAllActionsWithTarget:m_undoTarget.get()];
  484. for (int i = 0; i < groupingLevel; ++i)
  485. [undoManager beginUndoGrouping];
  486. m_haveUndoRedoOperations = NO;
  487. }
  488. }
  489. bool WebEditorClient::canCopyCut(Frame*, bool defaultValue) const
  490. {
  491. return defaultValue;
  492. }
  493. bool WebEditorClient::canPaste(Frame*, bool defaultValue) const
  494. {
  495. return defaultValue;
  496. }
  497. bool WebEditorClient::canUndo() const
  498. {
  499. return [[m_webView undoManager] canUndo];
  500. }
  501. bool WebEditorClient::canRedo() const
  502. {
  503. return [[m_webView undoManager] canRedo];
  504. }
  505. void WebEditorClient::undo()
  506. {
  507. if (canUndo())
  508. [[m_webView undoManager] undo];
  509. }
  510. void WebEditorClient::redo()
  511. {
  512. if (canRedo())
  513. [[m_webView undoManager] redo];
  514. }
  515. void WebEditorClient::handleKeyboardEvent(KeyboardEvent* event)
  516. {
  517. Frame* frame = event->target()->toNode()->document()->frame();
  518. WebHTMLView *webHTMLView = [[kit(frame) frameView] documentView];
  519. if ([webHTMLView _interpretKeyEvent:event savingCommands:NO])
  520. event->setDefaultHandled();
  521. }
  522. void WebEditorClient::handleInputMethodKeydown(KeyboardEvent* event)
  523. {
  524. Frame* frame = event->target()->toNode()->document()->frame();
  525. WebHTMLView *webHTMLView = [[kit(frame) frameView] documentView];
  526. if ([webHTMLView _interpretKeyEvent:event savingCommands:YES])
  527. event->setDefaultHandled();
  528. }
  529. #define FormDelegateLog(ctrl) LOG(FormDelegate, "control=%@", ctrl)
  530. void WebEditorClient::textFieldDidBeginEditing(Element* element)
  531. {
  532. if (!element->hasTagName(inputTag))
  533. return;
  534. DOMHTMLInputElement* inputElement = kit(static_cast<HTMLInputElement*>(element));
  535. FormDelegateLog(inputElement);
  536. CallFormDelegate(m_webView, @selector(textFieldDidBeginEditing:inFrame:), inputElement, kit(element->document()->frame()));
  537. }
  538. void WebEditorClient::textFieldDidEndEditing(Element* element)
  539. {
  540. if (!element->hasTagName(inputTag))
  541. return;
  542. DOMHTMLInputElement* inputElement = kit(static_cast<HTMLInputElement*>(element));
  543. FormDelegateLog(inputElement);
  544. CallFormDelegate(m_webView, @selector(textFieldDidEndEditing:inFrame:), inputElement, kit(element->document()->frame()));
  545. }
  546. void WebEditorClient::textDidChangeInTextField(Element* element)
  547. {
  548. if (!element->hasTagName(inputTag))
  549. return;
  550. if (!UserTypingGestureIndicator::processingUserTypingGesture() || UserTypingGestureIndicator::focusedElementAtGestureStart() != element)
  551. return;
  552. DOMHTMLInputElement* inputElement = kit(static_cast<HTMLInputElement*>(element));
  553. FormDelegateLog(inputElement);
  554. CallFormDelegate(m_webView, @selector(textDidChangeInTextField:inFrame:), inputElement, kit(element->document()->frame()));
  555. }
  556. static SEL selectorForKeyEvent(KeyboardEvent* event)
  557. {
  558. // FIXME: This helper function is for the auto-fill code so we can pass a selector to the form delegate.
  559. // Eventually, we should move all of the auto-fill code down to WebKit and remove the need for this function by
  560. // not relying on the selector in the new implementation.
  561. // The key identifiers are from <http://www.w3.org/TR/DOM-Level-3-Events/keyset.html#KeySet-Set>
  562. const String& key = event->keyIdentifier();
  563. if (key == "Up")
  564. return @selector(moveUp:);
  565. if (key == "Down")
  566. return @selector(moveDown:);
  567. if (key == "U+001B")
  568. return @selector(cancel:);
  569. if (key == "U+0009") {
  570. if (event->shiftKey())
  571. return @selector(insertBacktab:);
  572. return @selector(insertTab:);
  573. }
  574. if (key == "Enter")
  575. return @selector(insertNewline:);
  576. return 0;
  577. }
  578. bool WebEditorClient::doTextFieldCommandFromEvent(Element* element, KeyboardEvent* event)
  579. {
  580. if (!element->hasTagName(inputTag))
  581. return NO;
  582. DOMHTMLInputElement* inputElement = kit(static_cast<HTMLInputElement*>(element));
  583. FormDelegateLog(inputElement);
  584. if (SEL commandSelector = selectorForKeyEvent(event))
  585. return CallFormDelegateReturningBoolean(NO, m_webView, @selector(textField:doCommandBySelector:inFrame:), inputElement, commandSelector, kit(element->document()->frame()));
  586. return NO;
  587. }
  588. void WebEditorClient::textWillBeDeletedInTextField(Element* element)
  589. {
  590. if (!element->hasTagName(inputTag))
  591. return;
  592. DOMHTMLInputElement* inputElement = kit(static_cast<HTMLInputElement*>(element));
  593. FormDelegateLog(inputElement);
  594. // We're using the deleteBackward selector for all deletion operations since the autofill code treats all deletions the same way.
  595. CallFormDelegateReturningBoolean(NO, m_webView, @selector(textField:doCommandBySelector:inFrame:), inputElement, @selector(deleteBackward:), kit(element->document()->frame()));
  596. }
  597. void WebEditorClient::textDidChangeInTextArea(Element* element)
  598. {
  599. if (!element->hasTagName(textareaTag))
  600. return;
  601. DOMHTMLTextAreaElement* textAreaElement = kit(static_cast<HTMLTextAreaElement*>(element));
  602. FormDelegateLog(textAreaElement);
  603. CallFormDelegate(m_webView, @selector(textDidChangeInTextArea:inFrame:), textAreaElement, kit(element->document()->frame()));
  604. }
  605. bool WebEditorClient::shouldEraseMarkersAfterChangeSelection(TextCheckingType type) const
  606. {
  607. // This prevents erasing spelling markers on OS X Lion or later to match AppKit on these Mac OS X versions.
  608. #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
  609. return type != TextCheckingTypeSpelling;
  610. #else
  611. return true;
  612. #endif
  613. }
  614. void WebEditorClient::ignoreWordInSpellDocument(const String& text)
  615. {
  616. [[NSSpellChecker sharedSpellChecker] ignoreWord:text
  617. inSpellDocumentWithTag:spellCheckerDocumentTag()];
  618. }
  619. void WebEditorClient::learnWord(const String& text)
  620. {
  621. [[NSSpellChecker sharedSpellChecker] learnWord:text];
  622. }
  623. void WebEditorClient::checkSpellingOfString(const UChar* text, int length, int* misspellingLocation, int* misspellingLength)
  624. {
  625. NSString* textString = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(text) length:length freeWhenDone:NO];
  626. NSRange range = [[NSSpellChecker sharedSpellChecker] checkSpellingOfString:textString startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:spellCheckerDocumentTag() wordCount:NULL];
  627. [textString release];
  628. if (misspellingLocation) {
  629. // WebCore expects -1 to represent "not found"
  630. if (range.location == NSNotFound)
  631. *misspellingLocation = -1;
  632. else
  633. *misspellingLocation = range.location;
  634. }
  635. if (misspellingLength)
  636. *misspellingLength = range.length;
  637. }
  638. String WebEditorClient::getAutoCorrectSuggestionForMisspelledWord(const String& inputWord)
  639. {
  640. // This method can be implemented using customized algorithms for the particular browser.
  641. // Currently, it computes an empty string.
  642. return String();
  643. }
  644. void WebEditorClient::checkGrammarOfString(const UChar* text, int length, Vector<GrammarDetail>& details, int* badGrammarLocation, int* badGrammarLength)
  645. {
  646. NSArray *grammarDetails;
  647. NSString* textString = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(text) length:length freeWhenDone:NO];
  648. NSRange range = [[NSSpellChecker sharedSpellChecker] checkGrammarOfString:textString startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:spellCheckerDocumentTag() details:&grammarDetails];
  649. [textString release];
  650. if (badGrammarLocation)
  651. // WebCore expects -1 to represent "not found"
  652. *badGrammarLocation = (range.location == NSNotFound) ? -1 : static_cast<int>(range.location);
  653. if (badGrammarLength)
  654. *badGrammarLength = range.length;
  655. for (NSDictionary *detail in grammarDetails) {
  656. ASSERT(detail);
  657. GrammarDetail grammarDetail;
  658. NSValue *detailRangeAsNSValue = [detail objectForKey:NSGrammarRange];
  659. ASSERT(detailRangeAsNSValue);
  660. NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
  661. ASSERT(detailNSRange.location != NSNotFound);
  662. ASSERT(detailNSRange.length > 0);
  663. grammarDetail.location = detailNSRange.location;
  664. grammarDetail.length = detailNSRange.length;
  665. grammarDetail.userDescription = [detail objectForKey:NSGrammarUserDescription];
  666. NSArray *guesses = [detail objectForKey:NSGrammarCorrections];
  667. for (NSString *guess in guesses)
  668. grammarDetail.guesses.append(String(guess));
  669. details.append(grammarDetail);
  670. }
  671. }
  672. static Vector<TextCheckingResult> core(NSArray *incomingResults, TextCheckingTypeMask checkingTypes)
  673. {
  674. Vector<TextCheckingResult> results;
  675. for (NSTextCheckingResult *incomingResult in incomingResults) {
  676. NSRange resultRange = [incomingResult range];
  677. NSTextCheckingType resultType = [incomingResult resultType];
  678. ASSERT(resultRange.location != NSNotFound);
  679. ASSERT(resultRange.length > 0);
  680. if (NSTextCheckingTypeSpelling == resultType && 0 != (checkingTypes & NSTextCheckingTypeSpelling)) {
  681. TextCheckingResult result;
  682. result.type = TextCheckingTypeSpelling;
  683. result.location = resultRange.location;
  684. result.length = resultRange.length;
  685. results.append(result);
  686. } else if (NSTextCheckingTypeGrammar == resultType && 0 != (checkingTypes & NSTextCheckingTypeGrammar)) {
  687. TextCheckingResult result;
  688. NSArray *details = [incomingResult grammarDetails];
  689. result.type = TextCheckingTypeGrammar;
  690. result.location = resultRange.location;
  691. result.length = resultRange.length;
  692. for (NSDictionary *incomingDetail in details) {
  693. ASSERT(incomingDetail);
  694. GrammarDetail detail;
  695. NSValue *detailRangeAsNSValue = [incomingDetail objectForKey:NSGrammarRange];
  696. ASSERT(detailRangeAsNSValue);
  697. NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
  698. ASSERT(detailNSRange.location != NSNotFound);
  699. ASSERT(detailNSRange.length > 0);
  700. detail.location = detailNSRange.location;
  701. detail.length = detailNSRange.length;
  702. detail.userDescription = [incomingDetail objectForKey:NSGrammarUserDescription];
  703. NSArray *guesses = [incomingDetail objectForKey:NSGrammarCorrections];
  704. for (NSString *guess in guesses)
  705. detail.guesses.append(String(guess));
  706. result.details.append(detail);
  707. }
  708. results.append(result);
  709. } else if (NSTextCheckingTypeLink == resultType && 0 != (checkingTypes & NSTextCheckingTypeLink)) {
  710. TextCheckingResult result;
  711. result.type = TextCheckingTypeLink;
  712. result.location = resultRange.location;
  713. result.length = resultRange.length;
  714. result.replacement = [[incomingResult URL] absoluteString];
  715. results.append(result);
  716. } else if (NSTextCheckingTypeQuote == resultType && 0 != (checkingTypes & NSTextCheckingTypeQuote)) {
  717. TextCheckingResult result;
  718. result.type = TextCheckingTypeQuote;
  719. result.location = resultRange.location;
  720. result.length = resultRange.length;
  721. result.replacement = [incomingResult replacementString];
  722. results.append(result);
  723. } else if (NSTextCheckingTypeDash == resultType && 0 != (checkingTypes & NSTextCheckingTypeDash)) {
  724. TextCheckingResult result;
  725. result.type = TextCheckingTypeDash;
  726. result.location = resultRange.location;
  727. result.length = resultRange.length;
  728. result.replacement = [incomingResult replacementString];
  729. results.append(result);
  730. } else if (NSTextCheckingTypeReplacement == resultType && 0 != (checkingTypes & NSTextCheckingTypeReplacement)) {
  731. TextCheckingResult result;
  732. result.type = TextCheckingTypeReplacement;
  733. result.location = resultRange.location;
  734. result.length = resultRange.length;
  735. result.replacement = [incomingResult replacementString];
  736. results.append(result);
  737. } else if (NSTextCheckingTypeCorrection == resultType && 0 != (checkingTypes & NSTextCheckingTypeCorrection)) {
  738. TextCheckingResult result;
  739. result.type = TextCheckingTypeCorrection;
  740. result.location = resultRange.location;
  741. result.length = resultRange.length;
  742. result.replacement = [incomingResult replacementString];
  743. results.append(result);
  744. }
  745. }
  746. return results;
  747. }
  748. void WebEditorClient::checkTextOfParagraph(const UChar* text, int length, TextCheckingTypeMask checkingTypes, Vector<TextCheckingResult>& results)
  749. {
  750. NSString *textString = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(text) length:length freeWhenDone:NO];
  751. NSArray *incomingResults = [[NSSpellChecker sharedSpellChecker] checkString:textString range:NSMakeRange(0, [textString length]) types:(checkingTypes|NSTextCheckingTypeOrthography) options:nil inSpellDocumentWithTag:spellCheckerDocumentTag() orthography:NULL wordCount:NULL];
  752. [textString release];
  753. results = core(incomingResults, checkingTypes);
  754. }
  755. void WebEditorClient::updateSpellingUIWithGrammarString(const String& badGrammarPhrase, const GrammarDetail& grammarDetail)
  756. {
  757. NSMutableArray* corrections = [NSMutableArray array];
  758. for (unsigned i = 0; i < grammarDetail.guesses.size(); i++) {
  759. NSString* guess = grammarDetail.guesses[i];
  760. [corrections addObject:guess];
  761. }
  762. NSRange grammarRange = NSMakeRange(grammarDetail.location, grammarDetail.length);
  763. NSString* grammarUserDescription = grammarDetail.userDescription;
  764. NSDictionary* grammarDetailDict = [NSDictionary dictionaryWithObjectsAndKeys:[NSValue valueWithRange:grammarRange], NSGrammarRange, grammarUserDescription, NSGrammarUserDescription, corrections, NSGrammarCorrections, nil];
  765. [[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithGrammarString:badGrammarPhrase detail:grammarDetailDict];
  766. }
  767. void WebEditorClient::updateSpellingUIWithMisspelledWord(const String& misspelledWord)
  768. {
  769. [[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithMisspelledWord:misspelledWord];
  770. }
  771. void WebEditorClient::showSpellingUI(bool show)
  772. {
  773. NSPanel *spellingPanel = [[NSSpellChecker sharedSpellChecker] spellingPanel];
  774. if (show)
  775. [spellingPanel orderFront:nil];
  776. else
  777. [spellingPanel orderOut:nil];
  778. }
  779. bool WebEditorClient::spellingUIIsShowing()
  780. {
  781. return [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
  782. }
  783. void WebEditorClient::getGuessesForWord(const String& word, const String& context, Vector<String>& guesses) {
  784. guesses.clear();
  785. #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
  786. NSString* language = nil;
  787. NSOrthography* orthography = nil;
  788. NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
  789. if (context.length()) {
  790. [checker checkString:context range:NSMakeRange(0, context.length()) types:NSTextCheckingTypeOrthography options:0 inSpellDocumentWithTag:spellCheckerDocumentTag() orthography:&orthography wordCount:0];
  791. language = [checker languageForWordRange:NSMakeRange(0, context.length()) inString:context orthography:orthography];
  792. }
  793. NSArray* stringsArray = [checker guessesForWordRange:NSMakeRange(0, word.length()) inString:word language:language inSpellDocumentWithTag:spellCheckerDocumentTag()];
  794. #else
  795. NSArray* stringsArray = [[NSSpellChecker sharedSpellChecker] guessesForWord:word];
  796. #endif
  797. unsigned count = [stringsArray count];
  798. if (count > 0) {
  799. NSEnumerator* enumerator = [stringsArray objectEnumerator];
  800. NSString* string;
  801. while ((string = [enumerator nextObject]) != nil)
  802. guesses.append(string);
  803. }
  804. }
  805. void WebEditorClient::willSetInputMethodState()
  806. {
  807. }
  808. void WebEditorClient::setInputMethodState(bool)
  809. {
  810. }
  811. @interface WebEditorSpellCheckResponder : NSObject
  812. {
  813. WebEditorClient* _client;
  814. int _sequence;
  815. RetainPtr<NSArray> _results;
  816. }
  817. - (id)initWithClient:(WebEditorClient*)client sequence:(int)sequence results:(NSArray*)results;
  818. - (void)perform;
  819. @end
  820. @implementation WebEditorSpellCheckResponder
  821. - (id)initWithClient:(WebEditorClient*)client sequence:(int)sequence results:(NSArray*)results
  822. {
  823. self = [super init];
  824. if (!self)
  825. return nil;
  826. _client = client;
  827. _sequence = sequence;
  828. _results = results;
  829. return self;
  830. }
  831. - (void)perform
  832. {
  833. _client->didCheckSucceed(_sequence, _results.get());
  834. }
  835. @end
  836. void WebEditorClient::didCheckSucceed(int sequence, NSArray* results)
  837. {
  838. ASSERT_UNUSED(sequence, sequence == m_textCheckingRequest->data().sequence());
  839. m_textCheckingRequest->didSucceed(core(results, m_textCheckingRequest->data().mask()));
  840. m_textCheckingRequest.clear();
  841. }
  842. void WebEditorClient::requestCheckingOfString(PassRefPtr<WebCore::TextCheckingRequest> request)
  843. {
  844. ASSERT(!m_textCheckingRequest);
  845. m_textCheckingRequest = request;
  846. int sequence = m_textCheckingRequest->data().sequence();
  847. NSRange range = NSMakeRange(0, m_textCheckingRequest->data().text().length());
  848. NSRunLoop* currentLoop = [NSRunLoop currentRunLoop];
  849. [[NSSpellChecker sharedSpellChecker] requestCheckingOfString:m_textCheckingRequest->data().text() range:range types:NSTextCheckingAllSystemTypes options:0 inSpellDocumentWithTag:0
  850. completionHandler:^(NSInteger, NSArray* results, NSOrthography*, NSInteger) {
  851. [currentLoop performSelector:@selector(perform)
  852. target:[[[WebEditorSpellCheckResponder alloc] initWithClient:this sequence:sequence results:results] autorelease]
  853. argument:nil order:0 modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
  854. }];
  855. }