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

Unified 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, 8 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 side-by-side diff with in-line comments
Download patch
Index: ios/web/webui/crw_web_ui_page_builder.mm
diff --git a/ios/web/webui/crw_web_ui_page_builder.mm b/ios/web/webui/crw_web_ui_page_builder.mm
new file mode 100644
index 0000000000000000000000000000000000000000..33a4e91b4796bff2c387cbca71a11e48b57d0520
--- /dev/null
+++ b/ios/web/webui/crw_web_ui_page_builder.mm
@@ -0,0 +1,317 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/web/webui/crw_web_ui_page_builder.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/ios/weak_nsobject.h"
+#include "base/logging.h"
+#include "base/mac/bundle_locations.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/strings/sys_string_conversions.h"
+
+namespace {
+// Prefix for script tags. Used to locate JavaScript subresources.
+NSString* const kJSTagPrefix = @"<script src=\"";
+// Suffix for script tags. Used to locate JavaScript subresources.
+NSString* const kJSTagSuffix = @"\"></script>";
+// Prefix for stylesheet tags. Used to locate CSS subresources.
+NSString* const kCSSTagPrefix = @"<link rel=\"stylesheet\" href=\"";
+// Suffix for stylesheet tags. Used to locate CSS subresources.
+NSString* const kCSSTagSuffix = @"\">";
+// Template for creating inlined JavaScript tags.
+NSString* const kWebUIScriptTextTemplate = @"<script>%@</script>";
+// Template for creating inlined CSS tags.
+NSString* const kWebUIStyleTextTemplate = @"<style>%@</style>";
+// URL placeholder for WebUI messaging JavaScript.
+NSString* const kWebUICoreJSURL = @"chrome://resources/js/ios/core.js";
+} // namespace
+
+@interface CRWWebUIPageBuilder ()
+
+// Builds WebUI page for URL with HTML as default resource.
+- (void)buildWebUIPageForHTML:(NSString*)HTML
+ webUIURL:(const GURL&)URL
+ completionHandler:(web::WebUIPageCompletion)completionHandler;
+
+// Loads |resourceURL| by invoking _delegate.
+- (void)fetchResourceWithURL:(const GURL&)resourceURL
+ completionHandler:(web::WebUIDelegateCompletion)handler;
+
+// Loads |resourceURLs| by invoking _delegate. Members of resourceURLs must be
+// valid subresource URLs. |completionHandler| is called upon load of each
+// resource.
+- (void)fetchSubresourcesWithURLs:(const std::vector<GURL>&)resourceURLs
+ completionHandler:(web::WebUIDelegateCompletion)handler;
+
+// Looks for substrings surrounded by |prefix| and |suffix| in resource and
+// returns them as an NSSet.
+// For example, if prefix is "<a href='", suffix is "'>", and |resource|
+// includes substrings "<a href='http://www.apple.com'>" and
+// "<a href='chrome.html'>", return value will contain strings
+// "http://www.apple.com" and "chrome.html".
+- (NSSet*)URLStringsFromResource:(NSString*)resource
+ prefix:(NSString*)prefix
+ suffix:(NSString*)suffix;
+
+// Returns URL strings for subresources found in |HTML|. URLs are found by
+// searching for tags of the format <link rel="stylesheet" href="URLString">
+// and <script src="URLString">.
+- (NSSet*)URLStringsFromHTML:(NSString*)HTML;
+
+// Returns URL strings for subresources found in |CSS|. URLs are found by
+// searching for statements of the format @import(URLString).
+- (NSSet*)URLStringsFromCSS:(NSString*)CSS;
+
+// YES if subresourceURL is a valid subresource URL. Valid subresource URLs
+// include files with extension ".js" and ".css".
+- (BOOL)isValidSubresourceURL:(const GURL&)subresourceURL;
+
+// YES if subresourceURL is for a CSS resource.
+- (BOOL)isCSSSubresourceURL:(const GURL&)subresourceURL;
+
+// Prepends "<link rel="stylesheet" href="|URL|">" to the HTML link tag with
+// href=|sourceURL| in |HTML|.
+- (void)addCSSTagToHTML:(NSMutableString*)HTML
+ forURL:(const GURL&)URL
+ sourceURL:(const GURL&)sourceURL;
+
+// Flattens HTML with provided map of URLs to resource strings.
+- (void)flattenHTML:(NSMutableString*)HTML
+ withSubresources:(std::map<GURL, std::string>)subresources;
+
+// Returns JavaScript needed for bridging WebUI chrome.send() messages to
+// the core.js defined message delivery system.
+- (NSString*)webUIJavaScript;
+
+@end
+
+@implementation CRWWebUIPageBuilder {
+ // Delegate for requesting resources.
+ base::WeakNSProtocol<id<CRWWebUIPageBuilderDelegate>> _delegate;
+}
+
+#pragma mark - Public Methods
+
+- (instancetype)init {
+ NOTREACHED();
+ return self;
+}
+
+- (instancetype)initWithDelegate:(id<CRWWebUIPageBuilderDelegate>)delegate {
+ if (self = [super init]) {
+ _delegate.reset(delegate);
+ }
+ return self;
+}
+
+- (void)buildWebUIPageForURL:(const GURL&)webUIURL
+ completionHandler:(web::WebUIPageCompletion)completionHandler {
+ [self fetchResourceWithURL:webUIURL
+ completionHandler:^(NSString* webUIHTML, const GURL& URL) {
+ [self buildWebUIPageForHTML:webUIHTML
+ webUIURL:URL
+ completionHandler:completionHandler];
+ }];
+}
+
+#pragma mark - Private Methods
+
+- (void)buildWebUIPageForHTML:(NSString*)HTML
+ webUIURL:(const GURL&)pageURL
+ completionHandler:(web::WebUIPageCompletion)completionHandler {
+ __block base::scoped_nsobject<NSMutableString> webUIHTML([HTML mutableCopy]);
+ NSSet* subresourceURLStrings = [self URLStringsFromHTML:webUIHTML];
+ __block NSUInteger pendingSubresourceCount = [subresourceURLStrings count];
+ if (!pendingSubresourceCount) {
+ completionHandler(webUIHTML);
+ return;
+ }
+ __block std::map<GURL, std::string> subresources;
+ base::WeakNSObject<CRWWebUIPageBuilder> weakSelf(self);
+ // Completion handler for subresource loads.
+ __block web::WebUIDelegateCompletion subresourceHandler = nil;
+ subresourceHandler = [[^(NSString* subresource, const GURL& subresourceURL) {
+ // Import statements in CSS resources are also loaded.
+ if ([self isCSSSubresourceURL:subresourceURL]) {
+ NSSet* URLStrings = [weakSelf URLStringsFromCSS:subresource];
+ for (NSString* URLString in URLStrings) {
+ GURL URL(subresourceURL.Resolve(base::SysNSStringToUTF8(URLString)));
+ [weakSelf addCSSTagToHTML:webUIHTML
+ forURL:URL
+ sourceURL:subresourceURL];
+ pendingSubresourceCount++;
+ [weakSelf fetchResourceWithURL:URL
+ completionHandler:subresourceHandler];
+ }
+ }
+ subresources[subresourceURL] = base::SysNSStringToUTF8(subresource);
+ pendingSubresourceCount--;
+ // When subresource loading is complete, flatten the default resource
+ // and invoke the completion handler.
+ if (!pendingSubresourceCount) {
+ [weakSelf flattenHTML:webUIHTML withSubresources:subresources];
+ completionHandler(webUIHTML);
+ }
+ } copy] autorelease];
+
+ for (NSString* URLString in subresourceURLStrings) {
+ // chrome://resources/js/ios/core.js is skipped because it is
+ // retrieved via webUIJavaScript rather than the net stack.
+ if ([URLString isEqualToString:kWebUICoreJSURL]) {
+ pendingSubresourceCount--;
+ if (!pendingSubresourceCount) {
+ [weakSelf flattenHTML:webUIHTML withSubresources:subresources];
+ completionHandler(webUIHTML);
+ }
+ continue;
+ }
+ GURL URL(pageURL.Resolve(base::SysNSStringToUTF8(URLString)));
+ // If the resolved URL is different from URLString, replace URLString in
+ // webUIHTML so that it will be located appropriately when flattening
+ // occurs.
+ if (URL.spec() != base::SysNSStringToUTF8(URLString)) {
+ [webUIHTML replaceOccurrencesOfString:URLString
+ withString:base::SysUTF8ToNSString(URL.spec())
+ options:0
+ range:NSMakeRange(0, [HTML length])];
+ }
+ [self fetchResourceWithURL:URL completionHandler:subresourceHandler];
+ }
+}
+
+- (void)fetchResourceWithURL:(const GURL&)resourceURL
+ completionHandler:(web::WebUIDelegateCompletion)handler {
+ [_delegate webUIPageBuilder:self
+ fetchResourceWithURL:resourceURL
+ completionHandler:handler];
+}
+
+- (void)fetchSubresourcesWithURLs:(const std::vector<GURL>&)resourceURLs
+ completionHandler:(web::WebUIDelegateCompletion)handler {
+ for (const GURL& URL : resourceURLs) {
+ DCHECK([self isValidSubresourceURL:URL]);
+ [self fetchResourceWithURL:URL completionHandler:handler];
+ }
+}
+
+- (NSSet*)URLStringsFromResource:(NSString*)resource
+ prefix:(NSString*)prefix
+ suffix:(NSString*)suffix {
+ DCHECK(resource);
+ DCHECK(prefix);
+ DCHECK(suffix);
+ NSMutableSet* URLStrings = [NSMutableSet set];
+ NSError* error = nil;
+ NSString* tagPattern = [NSString
+ stringWithFormat:@"%@(.*?)%@",
+ [NSRegularExpression escapedPatternForString:prefix],
+ [NSRegularExpression escapedPatternForString:suffix]];
+ NSRegularExpression* tagExpression = [NSRegularExpression
+ regularExpressionWithPattern:tagPattern
+ options:NSRegularExpressionCaseInsensitive
+ error:&error];
+ if (error) {
+ DLOG(WARNING) << "Error: " << error.description.UTF8String;
+ }
+ NSArray* matches =
+ [tagExpression matchesInString:resource
+ options:0
+ range:NSMakeRange(0, [resource length])];
+ for (NSTextCheckingResult* match in matches) {
+ NSRange matchRange = [match rangeAtIndex:1];
+ DCHECK(matchRange.length);
+ NSString* URLString = [resource substringWithRange:matchRange];
+ [URLStrings addObject:URLString];
+ }
+ return URLStrings;
+}
+
+- (NSSet*)URLStringsFromHTML:(NSString*)HTML {
+ NSSet* JS = [self URLStringsFromResource:HTML
+ prefix:kJSTagPrefix
+ suffix:kJSTagSuffix];
+ NSSet* CSS = [self URLStringsFromResource:HTML
+ prefix:kCSSTagPrefix
+ suffix:kCSSTagSuffix];
+ return [JS setByAddingObjectsFromSet:CSS];
+}
+
+- (NSSet*)URLStringsFromCSS:(NSString*)CSS {
+ NSString* prefix = @"@import url(";
+ NSString* suffix = @");";
+ return [self URLStringsFromResource:CSS prefix:prefix suffix:suffix];
+}
+
+- (BOOL)isValidSubresourceURL:(const GURL&)subresourceURL {
+ base::FilePath resourcePath(subresourceURL.ExtractFileName());
+ std::string extension = resourcePath.Extension();
+ return extension == ".css" || extension == ".js";
+}
+
+- (BOOL)isCSSSubresourceURL:(const GURL&)subresourceURL {
+ base::FilePath resourcePath(subresourceURL.ExtractFileName());
+ std::string extension = resourcePath.Extension();
+ return extension == ".css";
+}
+
+- (void)addCSSTagToHTML:(NSMutableString*)HTML
+ forURL:(const GURL&)URL
+ sourceURL:(const GURL&)sourceURL {
+ NSString* URLString = base::SysUTF8ToNSString(URL.spec());
+ NSString* sourceURLString = base::SysUTF8ToNSString(sourceURL.spec());
+ NSString* sourceTag =
+ [NSString stringWithFormat:@"%@%@%@", kCSSTagPrefix, sourceURLString,
+ kCSSTagSuffix];
+ NSString* extendedTag = [[NSString
+ stringWithFormat:@"%@%@%@", kCSSTagPrefix, URLString, kCSSTagSuffix]
+ stringByAppendingString:sourceTag];
+ [HTML replaceOccurrencesOfString:sourceTag
+ withString:extendedTag
+ options:0
+ range:NSMakeRange(0, [HTML length])];
+}
+
+- (void)flattenHTML:(NSMutableString*)HTML
+ withSubresources:(std::map<GURL, std::string>)subresources {
+ // Add core.js script to resources.
+ GURL webUIJSURL("chrome://resources/js/ios/core.js");
+ subresources[webUIJSURL] = base::SysNSStringToUTF8([self webUIJavaScript]);
+ for (auto it = subresources.begin(); it != subresources.end(); it++) {
+ NSString* linkTemplate = @"";
+ NSString* textTemplate = @"";
+ if ([self isCSSSubresourceURL:it->first]) {
+ linkTemplate =
+ [NSString stringWithFormat:@"%@%%@%@", kCSSTagPrefix, kCSSTagSuffix];
+ textTemplate = kWebUIStyleTextTemplate;
+ } else { // JavaScript.
+ linkTemplate =
+ [NSString stringWithFormat:@"%@%%@%@", kJSTagPrefix, kJSTagSuffix];
+ textTemplate = kWebUIScriptTextTemplate;
+ }
+ NSString* resourceURLString = base::SysUTF8ToNSString(it->first.spec());
+ NSString* linkTag =
+ [NSString stringWithFormat:linkTemplate, resourceURLString];
+ NSString* resource = base::SysUTF8ToNSString(it->second);
+ NSString* textTag = [NSString stringWithFormat:textTemplate, resource];
+ [HTML replaceOccurrencesOfString:linkTag
+ withString:textTag
+ options:0
+ range:NSMakeRange(0, [HTML length])];
+ }
+}
+
+- (NSString*)webUIJavaScript {
+ NSBundle* bundle = base::mac::FrameworkBundle();
+ NSString* path = [bundle pathForResource:@"web_ui" ofType:@"js"];
+ DCHECK(path) << "web_ui.js file not found";
+ return [NSString stringWithContentsOfFile:path
+ encoding:NSUTF8StringEncoding
+ error:nil];
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698