Add ICU message format support
[chromium-blink-merge.git] / ios / web / webui / crw_web_ui_manager.mm
blob515dd2ec5340d985fbb59e27653702eb25d6bc87
1 // Copyright 2015 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/web/webui/crw_web_ui_manager.h"
7 #include "base/mac/bind_objc_block.h"
8 #include "base/mac/scoped_nsobject.h"
9 #include "base/memory/scoped_vector.h"
10 #import "base/strings/sys_string_conversions.h"
11 #include "base/values.h"
12 #import "ios/web/net/request_group_util.h"
13 #include "ios/web/public/browser_state.h"
14 #include "ios/web/public/web_client.h"
15 #import "ios/web/public/web_state/web_state_observer_bridge.h"
16 #include "ios/web/web_state/web_state_impl.h"
17 #import "ios/web/webui/crw_web_ui_page_builder.h"
18 #include "ios/web/webui/url_fetcher_block_adapter.h"
19 #import "net/base/mac/url_conversions.h"
21 namespace {
22 // Prefix for history.requestFavicon JavaScript message.
23 const char kScriptCommandPrefix[] = "webui";
26 @interface CRWWebUIManager () <CRWWebUIPageBuilderDelegate>
28 // Current web state.
29 @property(nonatomic, readonly) web::WebStateImpl* webState;
31 // Composes WebUI page for webUIURL and invokes completionHandler with the
32 // result.
33 - (void)loadWebUIPageForURL:(const GURL&)webUIURL
34           completionHandler:(void (^)(NSString*))completionHandler;
36 // Retrieves resource for URL and invokes completionHandler with the result.
37 - (void)fetchResourceWithURL:(const GURL&)URL
38            completionHandler:(void (^)(NSData*))completionHandler;
40 // Handles JavaScript message from the WebUI page.
41 - (BOOL)handleWebUIJSMessage:(const base::DictionaryValue&)message;
43 // Removes favicon callback from web state.
44 - (void)resetWebState;
46 // Removes fetcher from vector of active fetchers.
47 - (void)removeFetcher:(web::URLFetcherBlockAdapter*)fetcher;
49 @end
51 @implementation CRWWebUIManager {
52   // Set of live WebUI fetchers for retrieving data.
53   ScopedVector<web::URLFetcherBlockAdapter> _fetchers;
54   // Bridge to observe the web state from Objective-C.
55   scoped_ptr<web::WebStateObserverBridge> _webStateObserverBridge;
56   // Weak WebStateImpl this CRWWebUIManager is associated with.
57   web::WebStateImpl* _webState;
60 - (instancetype)init {
61   NOTREACHED();
62   return self;
65 - (instancetype)initWithWebState:(web::WebStateImpl*)webState {
66   if (self = [super init]) {
67     _webState = webState;
68     _webStateObserverBridge.reset(
69         new web::WebStateObserverBridge(webState, self));
70     base::WeakNSObject<CRWWebUIManager> weakSelf(self);
71     _webState->AddScriptCommandCallback(
72         base::BindBlock(
73             ^bool(const base::DictionaryValue& message, const GURL&, bool) {
74               return [weakSelf handleWebUIJSMessage:message];
75             }),
76         kScriptCommandPrefix);
77   }
78   return self;
81 - (void)dealloc {
82   [self resetWebState];
83   [super dealloc];
86 #pragma mark - CRWWebStateObserver Methods
88 - (void)webState:(web::WebState*)webState
89     didStartProvisionalNavigationForURL:(const GURL&)URL {
90   DCHECK(webState == _webState);
91   GURL navigationURL(URL);
92   // Add request group ID to the URL, if not present. Request group ID may
93   // already be added if restoring state to a WebUI page.
94   GURL requestURL =
95       web::ExtractRequestGroupIDFromURL(net::NSURLWithGURL(URL))
96           ? URL
97           : net::GURLWithNSURL(web::AddRequestGroupIDToURL(
98                 net::NSURLWithGURL(URL), _webState->GetRequestGroupID()));
99   base::WeakNSObject<CRWWebUIManager> weakSelf(self);
100   [self loadWebUIPageForURL:requestURL
101           completionHandler:^(NSString* HTML) {
102             web::WebStateImpl* webState = [weakSelf webState];
103             if (webState) {
104               webState->LoadWebUIHtml(base::SysNSStringToUTF16(HTML),
105                                       navigationURL);
106             }
107           }];
110 - (void)webStateDestroyed:(web::WebState*)webState {
111   [self resetWebState];
114 #pragma mark - CRWWebUIPageBuilderDelegate Methods
116 - (void)webUIPageBuilder:(CRWWebUIPageBuilder*)webUIPageBuilder
117     fetchResourceWithURL:(const GURL&)resourceURL
118        completionHandler:(web::WebUIDelegateCompletion)completionHandler {
119   GURL URL(resourceURL);
120   [self fetchResourceWithURL:URL
121            completionHandler:^(NSData* data) {
122              base::scoped_nsobject<NSString> resource(
123                  [[NSString alloc] initWithData:data
124                                        encoding:NSUTF8StringEncoding]);
125              completionHandler(resource, URL);
126            }];
129 #pragma mark - Private Methods
131 - (void)loadWebUIPageForURL:(const GURL&)webUIURL
132           completionHandler:(void (^)(NSString*))handler {
133   base::scoped_nsobject<CRWWebUIPageBuilder> pageBuilder(
134       [[CRWWebUIPageBuilder alloc] initWithDelegate:self]);
135   [pageBuilder buildWebUIPageForURL:webUIURL completionHandler:handler];
138 - (void)fetchResourceWithURL:(const GURL&)URL
139            completionHandler:(void (^)(NSData*))completionHandler {
140   base::WeakNSObject<CRWWebUIManager> weakSelf(self);
141   web::URLFetcherBlockAdapterCompletion fetcherCompletion =
142       ^(NSData* data, web::URLFetcherBlockAdapter* fetcher) {
143         completionHandler(data);
144         [weakSelf removeFetcher:fetcher];
145       };
147   _fetchers.push_back(
148       [self fetcherForURL:URL completionHandler:fetcherCompletion].Pass());
149   _fetchers.back()->Start();
152 - (BOOL)handleWebUIJSMessage:(const base::DictionaryValue&)message {
153   std::string command;
154   if (!message.GetString("message", &command) ||
155       command != "webui.requestFavicon") {
156     DLOG(WARNING) << "Unexpected message received" << command;
157     return NO;
158   }
159   const base::ListValue* arguments = nullptr;
160   if (!message.GetList("arguments", &arguments)) {
161     DLOG(WARNING) << "JS message parameter not found: arguments";
162     return NO;
163   }
164   std::string favicon;
165   if (!arguments->GetString(0, &favicon)) {
166     DLOG(WARNING) << "JS message parameter not found: Favicon URL";
167     return NO;
168   }
169   GURL faviconURL(favicon);
170   DCHECK(faviconURL.is_valid());
171   // Retrieve favicon resource and set favicon background image via JavaScript.
172   base::WeakNSObject<CRWWebUIManager> weakSelf(self);
173   void (^faviconHandler)(NSData*) = ^void(NSData* data) {
174     NSString* base64EncodedResource = [data base64EncodedStringWithOptions:0];
175     NSString* dataURLString = [NSString
176         stringWithFormat:@"data:image/png;base64,%@", base64EncodedResource];
177     NSString* faviconURLString = base::SysUTF8ToNSString(faviconURL.spec());
178     NSString* script =
179         [NSString stringWithFormat:@"chrome.setFaviconBackground('%@', '%@');",
180                                    faviconURLString, dataURLString];
181     [weakSelf webState]->ExecuteJavaScriptAsync(
182         base::SysNSStringToUTF16(script));
183   };
184   [self fetchResourceWithURL:faviconURL completionHandler:faviconHandler];
185   return YES;
188 - (void)resetWebState {
189   if (_webState) {
190     _webState->RemoveScriptCommandCallback(kScriptCommandPrefix);
191   }
192   _webState = nullptr;
195 - (web::WebStateImpl*)webState {
196   return _webState;
199 - (void)removeFetcher:(web::URLFetcherBlockAdapter*)fetcher {
200   _fetchers.erase(std::find(_fetchers.begin(), _fetchers.end(), fetcher));
203 #pragma mark - Testing-Only Methods
205 - (scoped_ptr<web::URLFetcherBlockAdapter>)
206         fetcherForURL:(const GURL&)URL
207     completionHandler:(web::URLFetcherBlockAdapterCompletion)handler {
208   return scoped_ptr<web::URLFetcherBlockAdapter>(
209       new web::URLFetcherBlockAdapter(
210           URL, _webState->GetBrowserState()->GetRequestContext(), handler));
213 @end