Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / ios / chrome / browser / find_in_page / js_findinpage_manager.mm
blob87edcc03d638389510d3a8dd2b7671ec5e369b6e
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "ios/chrome/browser/find_in_page/js_findinpage_manager.h"
7 #include <string>
9 #import "base/ios/weak_nsobject.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/string_escape.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "base/values.h"
16 #import "ios/chrome/browser/find_in_page/find_in_page_model.h"
18 namespace {
20 // Global variable defined in find_in_page.js that can be used for testing
21 // whether JavaScript bas heen loaded.
22 NSString* const kFindInPageBeacon = @"window.__gCrWeb.findInPage";
24 // Initializes Find In Page JavaScript with the width and height of the window.
25 NSString* const kFindInPageInit = @"window.__gCrWeb.findInPage && "
26                                    "window.__gCrWeb.findInPage.init(%.f, %.f);";
28 // This will only do verbatim matches.
29 // The timeout of 100ms is hardcoded into this string so we don't have
30 // to spend any time at runtime to format this constant into another constant.
31 NSString* const kFindInPageVerbatim =
32     @"window.__gCrWeb.findInPage && "
33      "window.__gCrWeb.findInPage.highlightWord(%@, false, 100.0);";
35 // The timeout of 100ms is hardcoded into this string so we don't have
36 // to spend any time at runtime to format this constant into another constant.
37 NSString* const kFindInPagePump =
38     @"window.__gCrWeb.findInPage && "
39      "window.__gCrWeb.findInPage.pumpSearch(100.0);";
41 NSString* const kFindInPagePrev = @"window.__gCrWeb.findInPage && "
42                                    "window.__gCrWeb.findInPage.goPrev();";
44 NSString* const kFindInPageNext = @"window.__gCrWeb.findInPage && "
45                                    "window.__gCrWeb.findInPage.goNext();";
47 NSString* const kFindInPageDisable = @"window.__gCrWeb.findInPage && "
48                                       "window.__gCrWeb.findInPage.disable();";
50 NSString* const kFindInPagePending = @"[false]";
52 const FindInPageEntry kFindInPageEntryZero = {{0.0, 0.0}, 0};
54 }  // namespace
56 @interface JsFindinpageManager ()
57 // Update find in page model with results, return true if fip completes or
58 // false if still pending and requires pumping. If |point| is not nil, it will
59 // contain the scroll position upon return.
60 - (BOOL)processFindInPageResult:(NSString*)result
61                  scrollPosition:(CGPoint*)point;
62 // Updates find in page model with results. Calls |completionHandler| with the
63 // the result of the processing and the new scroll position if successfull. If
64 // |completionHandler| is called with NO, further pumping is required.
65 // |completionHandler| cannot be nil.
66 - (void)processFindInPagePumpResult:(NSString*)result
67                   completionHandler:(void (^)(BOOL, CGPoint))completionHandler;
68 // Helper functions to extract FindInPageEntry from JSON.
69 - (FindInPageEntry)findInPageEntryForJson:(NSString*)jsonStr;
70 - (FindInPageEntry)entryForListValue:(base::ListValue*)position;
71 // Executes |script| which is a piece of JavaScript to move to the next or
72 // previous element in the page and executes |completionHandler| after moving
73 // with the new scroll position passed in.
74 - (void)moveHighlightByEvaluatingJavaScript:(NSString*)script
75                           completionHandler:
76                               (void (^)(CGPoint))completionHandler;
77 // Updates the current match index and its found position in the model.
78 - (void)updateIndex:(NSInteger)index atPoint:(CGPoint)point;
79 @end
81 @implementation JsFindinpageManager
83 - (FindInPageModel*)findInPageModel {
84   if (!findInPageModel_)
85     findInPageModel_.reset([[FindInPageModel alloc] init]);
86   return findInPageModel_.get();
89 - (void)setWidth:(CGFloat)width height:(CGFloat)height {
90   NSString* javaScript =
91       [NSString stringWithFormat:kFindInPageInit, width, height];
92   [self evaluate:javaScript stringResultHandler:nil];
95 - (void)findString:(NSString*)query
96     completionHandler:(void (^)(BOOL, CGPoint))completionHandler {
97   DCHECK(completionHandler);
98   // Save the query in the model before searching.
99   [findInPageModel_ updateQuery:query matches:0];
101   // Escape |query| before passing to js.
102   std::string escapedJson;
103   base::EscapeJSONString(base::SysNSStringToUTF16(query), true, &escapedJson);
104   NSString* jsonQuery =
105       [NSString stringWithFormat:kFindInPageVerbatim,
106                                  base::SysUTF8ToNSString(escapedJson.c_str())];
107   base::WeakNSObject<JsFindinpageManager> weakSelf(self);
108   [self evaluate:jsonQuery
109       stringResultHandler:^(NSString* result, NSError* error) {
110         [weakSelf processFindInPagePumpResult:result
111                             completionHandler:completionHandler];
112       }];
115 - (void)pumpWithCompletionHandler:(void (^)(BOOL, CGPoint))completionHandler {
116   DCHECK(completionHandler);
117   base::WeakNSObject<JsFindinpageManager> weakSelf(self);
118   [self evaluate:kFindInPagePump
119       stringResultHandler:^(NSString* result, NSError* error) {
120         // TODO(shreyasv): What to do here if this returns an NSError in the
121         // WKWebView version.
122         [weakSelf processFindInPagePumpResult:result
123                             completionHandler:completionHandler];
124       }];
127 - (void)nextMatchWithCompletionHandler:(void (^)(CGPoint))completionHandler {
128   [self moveHighlightByEvaluatingJavaScript:kFindInPageNext
129                           completionHandler:completionHandler];
132 - (void)previousMatchWithCompletionHandler:
133         (void (^)(CGPoint))completionHandler {
134   [self moveHighlightByEvaluatingJavaScript:kFindInPagePrev
135                           completionHandler:completionHandler];
138 - (void)moveHighlightByEvaluatingJavaScript:(NSString*)script
139                           completionHandler:
140                               (void (^)(CGPoint))completionHandler {
141   base::WeakNSObject<JsFindinpageManager> weakSelf(self);
142   [self evaluate:script
143       stringResultHandler:^(NSString* result, NSError* error) {
144         base::WeakNSObject<JsFindinpageManager> strongSelf([weakSelf retain]);
145         if (!strongSelf)
146           return;
147         DCHECK(!error);
148         FindInPageEntry entry = kFindInPageEntryZero;
149         if (![result isEqualToString:kFindInPagePending])
150           entry = [strongSelf findInPageEntryForJson:result];
151         CGPoint newPoint = entry.point;
152         [strongSelf updateIndex:entry.index atPoint:newPoint];
153         if (completionHandler)
154           completionHandler(newPoint);
155       }];
158 - (void)disableWithCompletionHandler:(ProceduralBlock)completionHandler {
159   DCHECK(completionHandler);
160   base::WeakNSObject<FindInPageModel> weakFindInPageModel(findInPageModel_);
161   [self evaluate:kFindInPageDisable
162       stringResultHandler:^(NSString* result, NSError* error) {
163         [weakFindInPageModel setEnabled:NO];
164         completionHandler();
165       }];
168 #pragma mark -
169 #pragma mark FindInPageEntry
171 - (BOOL)processFindInPageResult:(NSString*)result
172                  scrollPosition:(CGPoint*)point {
173   if (!result)
174     return NO;
176   // Parse JSONs.
177   std::string json([result UTF8String]);
178   scoped_ptr<base::Value> root(base::JSONReader::Read(json, false));
179   if (!root.get())
180     return YES;
181   if (!root->IsType(base::Value::TYPE_LIST))
182     return YES;
184   base::ListValue* resultList = static_cast<base::ListValue*>(root.get());
185   DCHECK(resultList);
186   if (resultList) {
187     if (resultList->GetSize() == 2) {
188       int numHighlighted = 0;
189       if (resultList->GetInteger(0, &numHighlighted)) {
190         if (numHighlighted > 0) {
191           base::ListValue* position;
192           if (resultList->GetList(1, &position)) {
193             [findInPageModel_ updateQuery:nil matches:numHighlighted];
194             // Scroll to first match.
195             FindInPageEntry entry = [self entryForListValue:position];
196             [findInPageModel_ updateIndex:entry.index atPoint:entry.point];
197             if (point)
198               *point = entry.point;
199           }
200         }
201       }
202     }
203   }
204   return YES;
207 - (void)processFindInPagePumpResult:(NSString*)result
208                   completionHandler:(void (^)(BOOL, CGPoint))completionHandler {
209   CGPoint point = CGPointZero;
210   if ([result isEqualToString:kFindInPagePending]) {
211     completionHandler(NO, point);
212   }
213   // TODO(shreyasv): Inline this call from the logic from the above function
214   // and remove the above function.
215   BOOL processFIPResult =
216       [self processFindInPageResult:result scrollPosition:&point];
217   completionHandler(processFIPResult, point);
220 - (void)updateIndex:(NSInteger)index atPoint:(CGPoint)point {
221   [findInPageModel_ updateIndex:index atPoint:point];
224 - (FindInPageEntry)findInPageEntryForJson:(NSString*)jsonStr {
225   std::string json([jsonStr UTF8String]);
226   scoped_ptr<base::Value> root(base::JSONReader::Read(json, false));
227   if (!root.get())
228     return kFindInPageEntryZero;
230   if (!root->IsType(base::Value::TYPE_LIST))
231     return kFindInPageEntryZero;
233   base::ListValue* position = static_cast<base::ListValue*>(root.get());
234   return [self entryForListValue:position];
237 - (FindInPageEntry)entryForListValue:(base::ListValue*)position {
238   if (!position)
239     return kFindInPageEntryZero;
241   // Position should always be of length 3, from [index,x,y].
242   DCHECK(position->GetSize() == 3);
243   if (position->GetSize() != 3)
244     return kFindInPageEntryZero;
246   // The array position comes from the JSON string [index, x, y], which
247   // represents the index of the currently found string, and the x and y
248   // position necessary to center that string.  Pull out that data into a
249   // FindInPageEntry struct.
250   int index;
251   double x = 0, y = 0;
252   position->GetInteger(0, &index);
253   position->GetDouble(1, &x);
254   position->GetDouble(2, &y);
255   FindInPageEntry entry;
256   entry.index = index;
257   entry.point.x = x;
258   entry.point.y = y;
259   return entry;
262 #pragma mark -
263 #pragma mark ProtectedMethods
265 - (NSString*)scriptPath {
266   return @"find_in_page";
269 - (NSString*)presenceBeacon {
270   return kFindInPageBeacon;
273 @end