Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(212)

Side by Side Diff: ios/web/webui/crw_web_ui_page_builder.mm

Issue 1110213002: Upstream most of the iOS WebUI support in ios/web/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "ios/web/webui/crw_web_ui_page_builder.h"
6
7 #include <map>
8 #include <string>
9 #include <vector>
10
11 #include "base/ios/weak_nsobject.h"
12 #include "base/logging.h"
13 #include "base/mac/bundle_locations.h"
14 #include "base/mac/scoped_nsobject.h"
15 #include "base/strings/sys_string_conversions.h"
16
17 namespace {
18 // Prefix for script tags. Used to locate JavaScript subresources.
19 NSString* const kJSTagPrefix = @"<script src=\"";
20 // Suffix for script tags. Used to locate JavaScript subresources.
21 NSString* const kJSTagSuffix = @"\"></script>";
22 // Prefix for stylesheet tags. Used to locate CSS subresources.
23 NSString* const kCSSTagPrefix = @"<link rel=\"stylesheet\" href=\"";
24 // Suffix for stylesheet tags. Used to locate CSS subresources.
25 NSString* const kCSSTagSuffix = @"\">";
26 // Template for creating inlined JavaScript tags.
27 NSString* const kWebUIScriptTextTemplate = @"<script>%@</script>";
28 // Template for creating inlined CSS tags.
29 NSString* const kWebUIStyleTextTemplate = @"<style>%@</style>";
30 // URL placeholder for WebUI messaging JavaScript.
31 NSString* const kWebUICoreJSURL = @"chrome://resources/js/ios/core.js";
32 } // namespace
33
34 @interface CRWWebUIPageBuilder ()
35
36 // Builds WebUI page for URL with HTML as default resource.
37 - (void)buildWebUIPageForHTML:(NSString*)HTML
38 webUIURL:(const GURL&)URL
39 completionHandler:(web::WebUIPageCompletion)completionHandler;
40
41 // Loads |resourceURL| by invoking _delegate.
42 - (void)fetchResourceWithURL:(const GURL&)resourceURL
43 completionHandler:(web::WebUIDelegateCompletion)handler;
44
45 // Loads |resourceURLs| by invoking _delegate. Members of resourceURLs must be
46 // valid subresource URLs. |completionHandler| is called upon load of each
47 // resource.
48 - (void)fetchSubresourcesWithURLs:(const std::vector<GURL>&)resourceURLs
49 completionHandler:(web::WebUIDelegateCompletion)handler;
50
51 // Looks for substrings surrounded by |prefix| and |suffix| in resource and
52 // returns them as an NSSet.
53 // For example, if prefix is "<a href='", suffix is "'>", and |resource|
54 // includes substrings "<a href='http://www.apple.com'>" and
55 // "<a href='chrome.html'>", return value will contain strings
56 // "http://www.apple.com" and "chrome.html".
57 - (NSSet*)URLStringsFromResource:(NSString*)resource
58 prefix:(NSString*)prefix
59 suffix:(NSString*)suffix;
60
61 // Returns URL strings for subresources found in |HTML|. URLs are found by
62 // searching for tags of the format <link rel="stylesheet" href="URLString">
63 // and <script src="URLString">.
64 - (NSSet*)URLStringsFromHTML:(NSString*)HTML;
65
66 // Returns URL strings for subresources found in |CSS|. URLs are found by
67 // searching for statements of the format @import(URLString).
68 - (NSSet*)URLStringsFromCSS:(NSString*)CSS;
69
70 // YES if subresourceURL is a valid subresource URL. Valid subresource URLs
71 // include files with extension ".js" and ".css".
72 - (BOOL)isValidSubresourceURL:(const GURL&)subresourceURL;
73
74 // YES if subresourceURL is for a CSS resource.
75 - (BOOL)isCSSSubresourceURL:(const GURL&)subresourceURL;
76
77 // Prepends "<link rel="stylesheet" href="|URL|">" to the HTML link tag with
78 // href=|sourceURL| in |HTML|.
79 - (void)addCSSTagToHTML:(NSMutableString*)HTML
80 forURL:(const GURL&)URL
81 sourceURL:(const GURL&)sourceURL;
82
83 // Flattens HTML with provided map of URLs to resource strings.
84 - (void)flattenHTML:(NSMutableString*)HTML
85 withSubresources:(std::map<GURL, std::string>)subresources;
86
87 // Returns JavaScript needed for bridging WebUI chrome.send() messages to
88 // the core.js defined message delivery system.
89 - (NSString*)webUIJavaScript;
90
91 @end
92
93 @implementation CRWWebUIPageBuilder {
94 // Delegate for requesting resources.
95 base::WeakNSProtocol<id<CRWWebUIPageBuilderDelegate>> _delegate;
96 }
97
98 #pragma mark - Public Methods
99
100 - (instancetype)init {
101 NOTREACHED();
102 return self;
103 }
104
105 - (instancetype)initWithDelegate:(id<CRWWebUIPageBuilderDelegate>)delegate {
106 if (self = [super init]) {
107 _delegate.reset(delegate);
108 }
109 return self;
110 }
111
112 - (void)buildWebUIPageForURL:(const GURL&)webUIURL
113 completionHandler:(web::WebUIPageCompletion)completionHandler {
114 [self fetchResourceWithURL:webUIURL
115 completionHandler:^(NSString* webUIHTML, const GURL& URL) {
116 [self buildWebUIPageForHTML:webUIHTML
117 webUIURL:URL
118 completionHandler:completionHandler];
119 }];
120 }
121
122 #pragma mark - Private Methods
123
124 - (void)buildWebUIPageForHTML:(NSString*)HTML
125 webUIURL:(const GURL&)pageURL
126 completionHandler:(web::WebUIPageCompletion)completionHandler {
127 __block base::scoped_nsobject<NSMutableString> webUIHTML([HTML mutableCopy]);
128 NSSet* subresourceURLStrings = [self URLStringsFromHTML:webUIHTML];
129 __block NSUInteger pendingSubresourceCount = [subresourceURLStrings count];
130 if (!pendingSubresourceCount) {
131 completionHandler(webUIHTML);
132 return;
133 }
134 __block std::map<GURL, std::string> subresources;
135 base::WeakNSObject<CRWWebUIPageBuilder> weakSelf(self);
136 // Completion handler for subresource loads.
137 __block web::WebUIDelegateCompletion subresourceHandler = nil;
138 subresourceHandler = [[^(NSString* subresource, const GURL& subresourceURL) {
139 // Import statements in CSS resources are also loaded.
140 if ([self isCSSSubresourceURL:subresourceURL]) {
141 NSSet* URLStrings = [weakSelf URLStringsFromCSS:subresource];
142 for (NSString* URLString in URLStrings) {
143 GURL URL(subresourceURL.Resolve(base::SysNSStringToUTF8(URLString)));
144 [weakSelf addCSSTagToHTML:webUIHTML
145 forURL:URL
146 sourceURL:subresourceURL];
147 pendingSubresourceCount++;
148 [weakSelf fetchResourceWithURL:URL
149 completionHandler:subresourceHandler];
150 }
151 }
152 subresources[subresourceURL] = base::SysNSStringToUTF8(subresource);
153 pendingSubresourceCount--;
154 // When subresource loading is complete, flatten the default resource
155 // and invoke the completion handler.
156 if (!pendingSubresourceCount) {
157 [weakSelf flattenHTML:webUIHTML withSubresources:subresources];
158 completionHandler(webUIHTML);
159 }
160 } copy] autorelease];
161
162 for (NSString* URLString in subresourceURLStrings) {
163 // chrome://resources/js/ios/core.js is skipped because it is
164 // retrieved via webUIJavaScript rather than the net stack.
165 if ([URLString isEqualToString:kWebUICoreJSURL]) {
166 pendingSubresourceCount--;
167 if (!pendingSubresourceCount) {
168 [weakSelf flattenHTML:webUIHTML withSubresources:subresources];
169 completionHandler(webUIHTML);
170 }
171 continue;
172 }
173 GURL URL(pageURL.Resolve(base::SysNSStringToUTF8(URLString)));
174 // If the resolved URL is different from URLString, replace URLString in
175 // webUIHTML so that it will be located appropriately when flattening
176 // occurs.
177 if (URL.spec() != base::SysNSStringToUTF8(URLString)) {
178 [webUIHTML replaceOccurrencesOfString:URLString
179 withString:base::SysUTF8ToNSString(URL.spec())
180 options:0
181 range:NSMakeRange(0, [HTML length])];
182 }
183 [self fetchResourceWithURL:URL completionHandler:subresourceHandler];
184 }
185 }
186
187 - (void)fetchResourceWithURL:(const GURL&)resourceURL
188 completionHandler:(web::WebUIDelegateCompletion)handler {
189 [_delegate webUIPageBuilder:self
190 fetchResourceWithURL:resourceURL
191 completionHandler:handler];
192 }
193
194 - (void)fetchSubresourcesWithURLs:(const std::vector<GURL>&)resourceURLs
195 completionHandler:(web::WebUIDelegateCompletion)handler {
196 for (const GURL& URL : resourceURLs) {
197 DCHECK([self isValidSubresourceURL:URL]);
198 [self fetchResourceWithURL:URL completionHandler:handler];
199 }
200 }
201
202 - (NSSet*)URLStringsFromResource:(NSString*)resource
203 prefix:(NSString*)prefix
204 suffix:(NSString*)suffix {
205 DCHECK(resource);
206 DCHECK(prefix);
207 DCHECK(suffix);
208 NSMutableSet* URLStrings = [NSMutableSet set];
209 NSError* error = nil;
210 NSString* tagPattern = [NSString
211 stringWithFormat:@"%@(.*?)%@",
212 [NSRegularExpression escapedPatternForString:prefix],
213 [NSRegularExpression escapedPatternForString:suffix]];
214 NSRegularExpression* tagExpression = [NSRegularExpression
215 regularExpressionWithPattern:tagPattern
216 options:NSRegularExpressionCaseInsensitive
217 error:&error];
218 if (error) {
219 DLOG(WARNING) << "Error: " << error.description.UTF8String;
220 }
221 NSArray* matches =
222 [tagExpression matchesInString:resource
223 options:0
224 range:NSMakeRange(0, [resource length])];
225 for (NSTextCheckingResult* match in matches) {
226 NSRange matchRange = [match rangeAtIndex:1];
227 DCHECK(matchRange.length);
228 NSString* URLString = [resource substringWithRange:matchRange];
229 [URLStrings addObject:URLString];
230 }
231 return URLStrings;
232 }
233
234 - (NSSet*)URLStringsFromHTML:(NSString*)HTML {
235 NSSet* JS = [self URLStringsFromResource:HTML
236 prefix:kJSTagPrefix
237 suffix:kJSTagSuffix];
238 NSSet* CSS = [self URLStringsFromResource:HTML
239 prefix:kCSSTagPrefix
240 suffix:kCSSTagSuffix];
241 return [JS setByAddingObjectsFromSet:CSS];
242 }
243
244 - (NSSet*)URLStringsFromCSS:(NSString*)CSS {
245 NSString* prefix = @"@import url(";
246 NSString* suffix = @");";
247 return [self URLStringsFromResource:CSS prefix:prefix suffix:suffix];
248 }
249
250 - (BOOL)isValidSubresourceURL:(const GURL&)subresourceURL {
251 base::FilePath resourcePath(subresourceURL.ExtractFileName());
252 std::string extension = resourcePath.Extension();
253 return extension == ".css" || extension == ".js";
254 }
255
256 - (BOOL)isCSSSubresourceURL:(const GURL&)subresourceURL {
257 base::FilePath resourcePath(subresourceURL.ExtractFileName());
258 std::string extension = resourcePath.Extension();
259 return extension == ".css";
260 }
261
262 - (void)addCSSTagToHTML:(NSMutableString*)HTML
263 forURL:(const GURL&)URL
264 sourceURL:(const GURL&)sourceURL {
265 NSString* URLString = base::SysUTF8ToNSString(URL.spec());
266 NSString* sourceURLString = base::SysUTF8ToNSString(sourceURL.spec());
267 NSString* sourceTag =
268 [NSString stringWithFormat:@"%@%@%@", kCSSTagPrefix, sourceURLString,
269 kCSSTagSuffix];
270 NSString* extendedTag = [[NSString
271 stringWithFormat:@"%@%@%@", kCSSTagPrefix, URLString, kCSSTagSuffix]
272 stringByAppendingString:sourceTag];
273 [HTML replaceOccurrencesOfString:sourceTag
274 withString:extendedTag
275 options:0
276 range:NSMakeRange(0, [HTML length])];
277 }
278
279 - (void)flattenHTML:(NSMutableString*)HTML
280 withSubresources:(std::map<GURL, std::string>)subresources {
281 // Add core.js script to resources.
282 GURL webUIJSURL("chrome://resources/js/ios/core.js");
283 subresources[webUIJSURL] = base::SysNSStringToUTF8([self webUIJavaScript]);
284 for (auto it = subresources.begin(); it != subresources.end(); it++) {
285 NSString* linkTemplate = @"";
286 NSString* textTemplate = @"";
287 if ([self isCSSSubresourceURL:it->first]) {
288 linkTemplate =
289 [NSString stringWithFormat:@"%@%%@%@", kCSSTagPrefix, kCSSTagSuffix];
290 textTemplate = kWebUIStyleTextTemplate;
291 } else { // JavaScript.
292 linkTemplate =
293 [NSString stringWithFormat:@"%@%%@%@", kJSTagPrefix, kJSTagSuffix];
294 textTemplate = kWebUIScriptTextTemplate;
295 }
296 NSString* resourceURLString = base::SysUTF8ToNSString(it->first.spec());
297 NSString* linkTag =
298 [NSString stringWithFormat:linkTemplate, resourceURLString];
299 NSString* resource = base::SysUTF8ToNSString(it->second);
300 NSString* textTag = [NSString stringWithFormat:textTemplate, resource];
301 [HTML replaceOccurrencesOfString:linkTag
302 withString:textTag
303 options:0
304 range:NSMakeRange(0, [HTML length])];
305 }
306 }
307
308 - (NSString*)webUIJavaScript {
309 NSBundle* bundle = base::mac::FrameworkBundle();
310 NSString* path = [bundle pathForResource:@"web_ui" ofType:@"js"];
311 DCHECK(path) << "web_ui.js file not found";
312 return [NSString stringWithContentsOfFile:path
313 encoding:NSUTF8StringEncoding
314 error:nil];
315 }
316
317 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698