From 628e793a2d4f986192db1ada5621ed50fe4eb16f Mon Sep 17 00:00:00 2001 From: droger Date: Fri, 12 Dec 2014 06:37:42 -0800 Subject: [PATCH] Upstream JavaScript injection for iOS Review URL: https://codereview.chromium.org/790803002 Cr-Commit-Position: refs/heads/master@{#308072} --- ios/web/ios_web.gyp | 17 +++ .../public/test/crw_test_js_injection_receiver.h | 15 ++ .../public/test/crw_test_js_injection_receiver.mm | 69 ++++++++++ ios/web/public/web_state/js/crw_js_base_manager.h | 16 +++ .../web_state/js/crw_js_injection_evaluator.h | 38 +++++ .../public/web_state/js/crw_js_injection_manager.h | 92 +++++++++++++ .../web_state/js/crw_js_injection_receiver.h | 31 +++++ .../public/web_state/js/crw_js_message_manager.h | 15 ++ ios/web/web_state/js/crw_js_base_manager.mm | 20 +++ ios/web/web_state/js/crw_js_common_manager.h | 17 +++ ios/web/web_state/js/crw_js_common_manager.mm | 26 ++++ ios/web/web_state/js/crw_js_injection_manager.mm | 153 +++++++++++++++++++++ ios/web/web_state/js/crw_js_injection_receiver.mm | 77 +++++++++++ .../web_state/js/crw_js_message_dynamic_manager.h | 15 ++ .../web_state/js/crw_js_message_dynamic_manager.mm | 29 ++++ ios/web/web_state/js/crw_js_message_manager.mm | 25 ++++ ios/web/web_view_util.h | 26 ++++ ios/web/web_view_util.mm | 36 +++++ 18 files changed, 717 insertions(+) create mode 100644 ios/web/public/test/crw_test_js_injection_receiver.h create mode 100644 ios/web/public/test/crw_test_js_injection_receiver.mm create mode 100644 ios/web/public/web_state/js/crw_js_base_manager.h create mode 100644 ios/web/public/web_state/js/crw_js_injection_evaluator.h create mode 100644 ios/web/public/web_state/js/crw_js_injection_manager.h create mode 100644 ios/web/public/web_state/js/crw_js_injection_receiver.h create mode 100644 ios/web/public/web_state/js/crw_js_message_manager.h create mode 100644 ios/web/web_state/js/crw_js_base_manager.mm create mode 100644 ios/web/web_state/js/crw_js_common_manager.h create mode 100644 ios/web/web_state/js/crw_js_common_manager.mm create mode 100644 ios/web/web_state/js/crw_js_injection_manager.mm create mode 100644 ios/web/web_state/js/crw_js_injection_receiver.mm create mode 100644 ios/web/web_state/js/crw_js_message_dynamic_manager.h create mode 100644 ios/web/web_state/js/crw_js_message_dynamic_manager.mm create mode 100644 ios/web/web_state/js/crw_js_message_manager.mm create mode 100644 ios/web/web_view_util.h create mode 100644 ios/web/web_view_util.mm diff --git a/ios/web/ios_web.gyp b/ios/web/ios_web.gyp index 276d86008b51..4ff80d8599d8 100644 --- a/ios/web/ios_web.gyp +++ b/ios/web/ios_web.gyp @@ -38,11 +38,26 @@ 'public/string_util.h', 'public/user_agent.h', 'public/user_agent.mm', + 'public/web_state/js/crw_js_base_manager.h', + 'public/web_state/js/crw_js_injection_evaluator.h', + 'public/web_state/js/crw_js_injection_manager.h', + 'public/web_state/js/crw_js_injection_receiver.h', + 'public/web_state/js/crw_js_message_manager.h', 'public/web_state/web_state_observer.h', 'public/web_thread.h', 'string_util.cc', + 'web_state/js/crw_js_base_manager.mm', + 'web_state/js/crw_js_common_manager.h', + 'web_state/js/crw_js_common_manager.mm', + 'web_state/js/crw_js_injection_manager.mm', + 'web_state/js/crw_js_injection_receiver.mm', + 'web_state/js/crw_js_message_dynamic_manager.h', + 'web_state/js/crw_js_message_dynamic_manager.mm', + 'web_state/js/crw_js_message_manager.mm', 'web_state/web_state_observer.cc', 'web_thread.cc', + 'web_view_util.h', + 'web_view_util.mm', ], }, { @@ -93,6 +108,8 @@ '../..', ], 'sources': [ + 'public/test/crw_test_js_injection_receiver.h', + 'public/test/crw_test_js_injection_receiver.mm', 'public/test/test_browser_state.cc', 'public/test/test_browser_state.h', 'public/test/test_web_state.cc', diff --git a/ios/web/public/test/crw_test_js_injection_receiver.h b/ios/web/public/test/crw_test_js_injection_receiver.h new file mode 100644 index 000000000000..e5296632aa48 --- /dev/null +++ b/ios/web/public/test/crw_test_js_injection_receiver.h @@ -0,0 +1,15 @@ +// Copyright 2013 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. + +#ifndef IOS_WEB_PUBLIC_TEST_CRW_TEST_JS_INJECTION_RECEIVER_H_ +#define IOS_WEB_PUBLIC_TEST_CRW_TEST_JS_INJECTION_RECEIVER_H_ + +#import "ios/web/public/web_state/js/crw_js_injection_receiver.h" + +// TestInjectionReceiver is used for tests. +// It uses a bare UIWebView as backend for javascript evaluation. +@interface CRWTestJSInjectionReceiver : CRWJSInjectionReceiver +@end + +#endif // IOS_WEB_PUBLIC_TEST_CRW_TEST_JS_INJECTION_RECEIVER_H_ diff --git a/ios/web/public/test/crw_test_js_injection_receiver.mm b/ios/web/public/test/crw_test_js_injection_receiver.mm new file mode 100644 index 000000000000..3d6d55d8dd6d --- /dev/null +++ b/ios/web/public/test/crw_test_js_injection_receiver.mm @@ -0,0 +1,69 @@ +// Copyright 2013 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. + +#import "ios/web/public/test/crw_test_js_injection_receiver.h" + +#import + +#import "base/mac/scoped_nsobject.h" +#import "ios/web/public/web_state/js/crw_js_injection_evaluator.h" + +@interface CRWTestUIWebViewEvaluator : NSObject { + base::scoped_nsobject _webView; +} +@end + +@implementation CRWTestUIWebViewEvaluator + +- (id) init { + if (self = [super init]) + _webView.reset([[UIWebView alloc] init]); + return self; +} + +#pragma mark - +#pragma mark CRWJSInjectionEvaluatorMethods + +- (void)evaluateJavaScript:(NSString*)script + stringResultHandler:(web::JavaScriptCompletion)handler { + dispatch_async(dispatch_get_main_queue(), ^{ + // TODO(shreyasv): Change to weaknsobject once weaknsobject is moved to + // ios/base. + NSString* result = + [_webView stringByEvaluatingJavaScriptFromString:script]; + if (handler) + handler(result, nil); + }); +} + +- (BOOL)scriptHasBeenInjectedForClass:(Class)jsInjectionManagerClass + presenceBeacon:(NSString*)beacon { + NSString* result = [_webView stringByEvaluatingJavaScriptFromString: + [NSString stringWithFormat:@"typeof %@", beacon]]; + return [result isEqualToString:@"object"]; +} + +- (void)injectScript:(NSString*)script + forClass:(Class)jsInjectionManagerClass { + [_webView stringByEvaluatingJavaScriptFromString:script]; +} + +@end + +@interface CRWTestJSInjectionReceiver () { + base::scoped_nsobject evaluator_; +} +@end + +@implementation CRWTestJSInjectionReceiver + +- (id)init { + base::scoped_nsobject evaluator( + [[CRWTestUIWebViewEvaluator alloc] init]); + if (self = [super initWithEvaluator:evaluator]) + evaluator_.swap(evaluator); + return self; +} + +@end diff --git a/ios/web/public/web_state/js/crw_js_base_manager.h b/ios/web/public/web_state/js/crw_js_base_manager.h new file mode 100644 index 000000000000..4bddfaf422eb --- /dev/null +++ b/ios/web/public/web_state/js/crw_js_base_manager.h @@ -0,0 +1,16 @@ +// Copyright 2013 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. + +#ifndef IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_BASE_MANAGER_H_ +#define IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_BASE_MANAGER_H_ + +#import "ios/web/public/web_state/js/crw_js_injection_manager.h" + +@class CRWJSInjectionReceiver; + +// Loads JavaScript file base.js which contains basic JavaScript for the app. +@interface CRWJSBaseManager : CRWJSInjectionManager +@end + +#endif // IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_BASE_MANAGER_H_ diff --git a/ios/web/public/web_state/js/crw_js_injection_evaluator.h b/ios/web/public/web_state/js/crw_js_injection_evaluator.h new file mode 100644 index 000000000000..724f1bdfa061 --- /dev/null +++ b/ios/web/public/web_state/js/crw_js_injection_evaluator.h @@ -0,0 +1,38 @@ +// Copyright 2012 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. + +#ifndef IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_INJECTION_EVALUATOR_H_ +#define IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_INJECTION_EVALUATOR_H_ + +#import + +// The type of the completion handler block that is called from +// |evaluateJavaScript:completionHandler| +namespace web { +typedef void (^JavaScriptCompletion)(NSString*, NSError*); +} + +@protocol CRWJSInjectionEvaluator + +// Evaluates the supplied JavaScript in the WebView. Calls |completionHandler| +// with results of the evaluation (which may be nil if the implementing object +// has no way to run the evaluation or the evaluation returns a nil value) +// or an NSError if there is an error. The |completionHandler| can be nil. +- (void)evaluateJavaScript:(NSString*)script + stringResultHandler:(web::JavaScriptCompletion)handler; + +// Checks to see if the script for a class has been injected into the +// current page already, given the class and the script's presence +// beacon (a JS object that should exist iff the script has been injected). +- (BOOL)scriptHasBeenInjectedForClass:(Class)jsInjectionManagerClass + presenceBeacon:(NSString*)beacon; + +// Injects the given script into the current page on behalf of +// |jsInjectionManagerClass|. This should only be used for injecting +// the manager's script, and not for evaluating arbitrary JavaScript. +- (void)injectScript:(NSString*)script forClass:(Class)jsInjectionManagerClass; + +@end + +#endif // IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_INJECTION_EVALUATOR_H_ diff --git a/ios/web/public/web_state/js/crw_js_injection_manager.h b/ios/web/public/web_state/js/crw_js_injection_manager.h new file mode 100644 index 000000000000..b78411ce4804 --- /dev/null +++ b/ios/web/public/web_state/js/crw_js_injection_manager.h @@ -0,0 +1,92 @@ +// Copyright 2012 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. + +#ifndef IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_INJECTION_MANAGER_H_ +#define IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_INJECTION_MANAGER_H_ + +#import + +#import "ios/web/public/web_state/js/crw_js_injection_evaluator.h" + +@class CRWJSInjectionReceiver; + +// This class defines the abstract interface for managing JavaScript +// Injection into a UIWebView. +@interface CRWJSInjectionManager : NSObject + +// Designated initializer. Initializes the object with the |receiver|. +- (id)initWithReceiver:(CRWJSInjectionReceiver*)receiver; + +// The array of |CRWJSInjectionManager| this class depends on. Default to be +// empty array. Note circular dependency is not allowed, which will cause stack +// overflow in dependency related computation such as -allDependencies. +- (NSArray*)directDependencies; + +// Returns a list of all the CRWJSInjectionManagers required by this manager, +// that is, this manager and the managers it directly or indirectly depends on. +// The list is ordered in such a way that any CRWJSInjectionManager in the list +// only depends on those appear before it in the list. +- (NSArray*)allDependencies; + +// Returns whether JavaScript has already been injected into the receiver. +- (BOOL)hasBeenInjected; + +// Injects JavaScript at |self.scriptPath| into the receiver object if it is +// missing. It also injects the dependencies' JavaScript if they are missing. +- (void)inject; + +// Returns an autoreleased string that is the JavaScript to be injected into +// the receiver object including any JavaScript for all specified dependencies. +- (NSString*)injectionContentIncludingDependencies; + +// Evaluates the provided JavaScript expression, slightly deferred. Designed for +// scripts where the chance of crwebinvoke:// being triggered indirectly is +// high, and that aren't required to return a value. +- (void)deferredEvaluate:(NSString*)script; + +// Evaluate the provided JavaScript asynchronously calling completionHandler +// after execution. The |completionHandler| can be nil. +- (void)evaluate:(NSString*)script + stringResultHandler:(web::JavaScriptCompletion)completionHandler; + +@end + +@interface CRWJSInjectionManager (ProtectedMethods) + +// The injection receiver used to evaluate JavaScript. +- (CRWJSInjectionReceiver*)receiver; + +// Path for the resource in the application bundle of type "js" that needs to +// injected for this manager. +// Subclasses must override this method to return the path to the JavaScript +// that needs to be injected. +- (NSString*)scriptPath; + +// The JavaScript function that returns the JavaScript constant of undefined +// if the JavaScript has not been injected. Default to be nil. Subclasses +// should override this if their script should only be injected into a page +// once. +- (NSString*)presenceBeacon; + +// Returns the content that should be injected. This is called every time +// injection content is needed; by default is uses a cached copy of +// staticInjectionContent. +// Subclasses should override this only if the content needs to be dynamic +// rather than cached, otherwise they should override staticInjectionContent. +- (NSString*)injectionContent; + +// Returns an autoreleased string that is the JavaScript to be injected into +// the receiver object. By default this returns the contents of the script file; +// subclasses can override this if they need to get a static script from some +// other source. +// The return value from this method will be cached; if dynamic script content +// is necessary, override injectionContent instead. +- (NSString*)staticInjectionContent; + +// Injects dependencies if they are missing. +- (void)injectDependenciesIfMissing; + +@end + +#endif // IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_INJECTION_MANAGER_H_ diff --git a/ios/web/public/web_state/js/crw_js_injection_receiver.h b/ios/web/public/web_state/js/crw_js_injection_receiver.h new file mode 100644 index 000000000000..f8fdfc5835e8 --- /dev/null +++ b/ios/web/public/web_state/js/crw_js_injection_receiver.h @@ -0,0 +1,31 @@ +// Copyright 2012 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. + +#ifndef IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_INJECTION_RECEIVER_H_ +#define IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_INJECTION_RECEIVER_H_ + +#import + +#import "ios/web/public/web_state/js/crw_js_injection_evaluator.h" + +@class CRWJSInjectionManager; + +// CRWJSInjectionReceiver injects JavaScript into a web view. +@interface CRWJSInjectionReceiver : NSObject + +// Init with JavaScript evaluator. +- (id)initWithEvaluator:(id)evaluator; + +// Returns an instance of |jsInjectionManagerClass|. Instances of the classes +// it depends on are created if needed. +- (CRWJSInjectionManager*)instanceOfClass:(Class)jsInjectionManagerClass; + +@end + +@interface CRWJSInjectionReceiver (Testing) +// Returns a dictionary of instantiated managers keyed by class. +- (NSDictionary*)managers; +@end + +#endif // IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_INJECTION_RECEIVER_H_ diff --git a/ios/web/public/web_state/js/crw_js_message_manager.h b/ios/web/public/web_state/js/crw_js_message_manager.h new file mode 100644 index 000000000000..85fe836d0a71 --- /dev/null +++ b/ios/web/public/web_state/js/crw_js_message_manager.h @@ -0,0 +1,15 @@ +// Copyright 2014 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. + +#ifndef IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_MESSAGE_MANAGER_H_ +#define IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_MESSAGE_MANAGER_H_ + +#import "ios/web/public/web_state/js/crw_js_injection_manager.h" + +// Loads the JavaScript file message.js which contains methods allowing other +// scripts to message the main application. +@interface CRWJSMessageManager : CRWJSInjectionManager +@end + +#endif // IOS_WEB_PUBLIC_WEB_STATE_JS_CRW_JS_MESSAGE_MANAGER_H_ diff --git a/ios/web/web_state/js/crw_js_base_manager.mm b/ios/web/web_state/js/crw_js_base_manager.mm new file mode 100644 index 000000000000..29507bcc963d --- /dev/null +++ b/ios/web/web_state/js/crw_js_base_manager.mm @@ -0,0 +1,20 @@ +// Copyright 2013 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. + +#import "ios/web/public/web_state/js/crw_js_base_manager.h" + +@implementation CRWJSBaseManager + +#pragma mark - +#pragma mark ProtectedMethods + +- (NSString*)scriptPath { + return @"base"; +} + +- (NSString*)presenceBeacon { + return @"__gCrWeb"; +} + +@end diff --git a/ios/web/web_state/js/crw_js_common_manager.h b/ios/web/web_state/js/crw_js_common_manager.h new file mode 100644 index 000000000000..4b147719bdc2 --- /dev/null +++ b/ios/web/web_state/js/crw_js_common_manager.h @@ -0,0 +1,17 @@ +// Copyright 2013 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. + +#ifndef IOS_WEB_WEB_STATE_JS_CRW_JS_COMMON_MANAGER_H_ +#define IOS_WEB_WEB_STATE_JS_CRW_JS_COMMON_MANAGER_H_ + +#import "ios/web/public/web_state/js/crw_js_injection_manager.h" + +@class CRWJSInjectionReceiver; + +// Loads JavaScript file common.js which contains common JavaScript methods that +// can be called by other CRWJSInjectionManagers. +@interface CRWJSCommonManager : CRWJSInjectionManager +@end + +#endif // IOS_WEB_WEB_STATE_JS_CRW_JS_COMMON_MANAGER_H_ diff --git a/ios/web/web_state/js/crw_js_common_manager.mm b/ios/web/web_state/js/crw_js_common_manager.mm new file mode 100644 index 000000000000..82839d0592d1 --- /dev/null +++ b/ios/web/web_state/js/crw_js_common_manager.mm @@ -0,0 +1,26 @@ +// Copyright 2013 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. + +#import "ios/web/web_state/js/crw_js_common_manager.h" + +#import "ios/web/public/web_state/js/crw_js_base_manager.h" + +@implementation CRWJSCommonManager + +#pragma mark - +#pragma mark ProtectedMethods + +- (NSString*)scriptPath { + return @"common"; +} + +- (NSString*)presenceBeacon { + return @"__gCrWeb.common"; +} + +- (NSArray*)directDependencies { + return @[ [CRWJSBaseManager class] ]; +} + +@end diff --git a/ios/web/web_state/js/crw_js_injection_manager.mm b/ios/web/web_state/js/crw_js_injection_manager.mm new file mode 100644 index 000000000000..86b4139bb1d0 --- /dev/null +++ b/ios/web/web_state/js/crw_js_injection_manager.mm @@ -0,0 +1,153 @@ +// Copyright 2012 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. + +#import "ios/web/public/web_state/js/crw_js_injection_manager.h" + +#import + +#include "base/logging.h" +#include "base/mac/bundle_locations.h" +#import "base/mac/scoped_nsobject.h" +#include "base/strings/sys_string_conversions.h" +#import "ios/web/public/web_state/js/crw_js_injection_receiver.h" + +namespace { +// Template with presence beacon check to prevent re-injection of JavaScript. +NSString* const kPresenceCheckTemplate = @"if (typeof %@ !== 'object') { %@ }"; +} + +@implementation CRWJSInjectionManager { + // JS to inject into the page. This may be nil if it has been purged due to + // low memory. + base::scoped_nsobject _injectObject; + // An object the can receive JavaScript injection. + CRWJSInjectionReceiver* _receiver; // Weak. +} + +- (id)initWithReceiver:(CRWJSInjectionReceiver*)receiver { + DCHECK(receiver); + self = [super init]; + if (self) { + _receiver = receiver; + // Register for low-memory warnings. + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(lowMemoryWarning:) + name:UIApplicationDidReceiveMemoryWarningNotification + object:nil]; + } + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +- (BOOL)hasBeenInjected { + DCHECK(self.presenceBeacon); + return [_receiver scriptHasBeenInjectedForClass:[self class] + presenceBeacon:self.presenceBeacon]; +} + +- (void)inject { + if ([self hasBeenInjected]) + return; + [self injectDependenciesIfMissing]; + [_receiver injectScript:[self injectionContent] forClass:[self class]]; + DCHECK([self hasBeenInjected]); +} + +- (NSString*)injectionContentIncludingDependencies { + NSArray* allDependencies = [self allDependencies]; + NSMutableArray* scripts = + [NSMutableArray arrayWithCapacity:allDependencies.count]; + for (CRWJSInjectionManager* dependency in allDependencies) { + [scripts addObject:[dependency injectionContent]]; + } + NSString* script = [scripts componentsJoinedByString:@""]; + NSString* beacon = [self presenceBeacon]; + return [NSString stringWithFormat:kPresenceCheckTemplate, beacon, script]; +} + +- (void)lowMemoryWarning:(NSNotification*)notify { + _injectObject.reset(); +} + +- (void)deferredEvaluate:(NSString*)script { + NSString* deferredScript = [NSString + stringWithFormat:@"window.setTimeout(function() {%@}, 0)", script]; + [self evaluate:deferredScript stringResultHandler:nil]; +} + +- (void)evaluate:(NSString*)script + stringResultHandler:(web::JavaScriptCompletion)completionHandler { + [_receiver evaluateJavaScript:script stringResultHandler:completionHandler]; +} + +- (NSArray*)directDependencies { + return @[]; +} + +- (NSArray*)allDependencies { + NSMutableArray* allDendencies = [NSMutableArray array]; + for (Class dependencyClass in [self directDependencies]) { + CRWJSInjectionManager* dependency = + [_receiver instanceOfClass:dependencyClass]; + NSArray* list = [dependency allDependencies]; + for (CRWJSInjectionManager* manager in list) { + if (![allDendencies containsObject:manager]) + [allDendencies addObject:manager]; + } + } + [allDendencies addObject:self]; + return allDendencies; +} + +#pragma mark - +#pragma mark ProtectedMethods + +- (CRWJSInjectionReceiver*)receiver { + return _receiver; +} + +- (NSString*)scriptPath { + NOTREACHED(); + return nil; +} + +- (NSString*)presenceBeacon { + return nil; +} + +- (NSString*)injectionContent { + if (!_injectObject) + _injectObject.reset([[self staticInjectionContent] copy]); + return _injectObject.get(); +} + +- (NSString*)staticInjectionContent { + DCHECK(self.scriptPath); + NSString* path = [base::mac::FrameworkBundle() pathForResource:self.scriptPath + ofType:@"js"]; + DCHECK(path) << "Script file not found: " + << base::SysNSStringToUTF8(self.scriptPath) << ".js"; + NSError* error = nil; + NSString* content = [NSString stringWithContentsOfFile:path + encoding:NSUTF8StringEncoding + error:&error]; + DCHECK(!error) << "Error fetching script: " << [error.description UTF8String]; + DCHECK(content); + return content; +} + +- (void)injectDependenciesIfMissing { + for (Class dependencyClass in [self directDependencies]) { + CRWJSInjectionManager* dependency = + [_receiver instanceOfClass:dependencyClass]; + [dependency inject]; + } +} + +@end diff --git a/ios/web/web_state/js/crw_js_injection_receiver.mm b/ios/web/web_state/js/crw_js_injection_receiver.mm new file mode 100644 index 000000000000..745fab8d14be --- /dev/null +++ b/ios/web/web_state/js/crw_js_injection_receiver.mm @@ -0,0 +1,77 @@ +// Copyright 2012 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. + +#import "ios/web/public/web_state/js/crw_js_injection_receiver.h" + +#include "base/logging.h" +#import "base/mac/scoped_nsobject.h" +#import "ios/web/public/web_state/js/crw_js_injection_evaluator.h" +#import "ios/web/public/web_state/js/crw_js_injection_manager.h" + +@implementation CRWJSInjectionReceiver { + // Used to evaluate JavaScripts. + id _evaluator; + + // Map from a CRWJSInjectionManager class to its instance created for this + // receiver. + base::scoped_nsobject _managers; +} + +- (id)init { + NOTREACHED(); + return [super init]; +} + +- (id)initWithEvaluator:(id)evaluator { + DCHECK(evaluator); + self = [super init]; + if (self) { + _evaluator = evaluator; + _managers.reset([[NSMutableDictionary alloc] init]); + } + return self; +} + +#pragma mark - +#pragma mark CRWJSInjectionEvaluatorMethods + +- (void)evaluateJavaScript:(NSString*)script + stringResultHandler:(web::JavaScriptCompletion)handler { + [_evaluator evaluateJavaScript:script stringResultHandler:handler]; +} + +- (BOOL)scriptHasBeenInjectedForClass:(Class)jsInjectionManagerClass + presenceBeacon:(NSString*)beacon { + return [_evaluator scriptHasBeenInjectedForClass:jsInjectionManagerClass + presenceBeacon:beacon]; +} + +- (void)injectScript:(NSString*)script forClass:(Class)jsInjectionManagerClass { + [_evaluator injectScript:script forClass:jsInjectionManagerClass]; +} + +- (CRWJSInjectionManager*)instanceOfClass:(Class)jsInjectionManagerClass { + DCHECK(_managers); + CRWJSInjectionManager* manager = + [_managers objectForKey:jsInjectionManagerClass]; + if (!manager) { + base::scoped_nsobject newManager( + [[jsInjectionManagerClass alloc] initWithReceiver:self]); + [_managers setObject:newManager forKey:jsInjectionManagerClass]; + manager = newManager; + } + DCHECK(manager); + for (Class depedencyClass in [manager directDependencies]) { + [self instanceOfClass:depedencyClass]; + } + return manager; +} + +@end + +@implementation CRWJSInjectionReceiver (Testing) +- (NSDictionary*)managers { + return _managers.get(); +} +@end diff --git a/ios/web/web_state/js/crw_js_message_dynamic_manager.h b/ios/web/web_state/js/crw_js_message_dynamic_manager.h new file mode 100644 index 000000000000..0943079d80b8 --- /dev/null +++ b/ios/web/web_state/js/crw_js_message_dynamic_manager.h @@ -0,0 +1,15 @@ +// Copyright 2014 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. + +#ifndef IOS_WEB_WEB_STATE_JS_CRW_JS_MESSAGE_DYNAMIC_MANAGER_H_ +#define IOS_WEB_WEB_STATE_JS_CRW_JS_MESSAGE_DYNAMIC_MANAGER_H_ + +#import "ios/web/public/web_state/js/crw_js_injection_manager.h" + +// Manager for the injection of the UI/WKWebView specific parts of JavaScript +// messaging into the web view. +@interface CRWJSMessageDynamicManager : CRWJSInjectionManager +@end + +#endif // IOS_WEB_WEB_STATE_JS_CRW_JS_MESSAGE_DYNAMIC_MANAGER_H_ diff --git a/ios/web/web_state/js/crw_js_message_dynamic_manager.mm b/ios/web/web_state/js/crw_js_message_dynamic_manager.mm new file mode 100644 index 000000000000..50059f0ce074 --- /dev/null +++ b/ios/web/web_state/js/crw_js_message_dynamic_manager.mm @@ -0,0 +1,29 @@ +// Copyright 2014 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. + +#import "ios/web/web_state/js/crw_js_message_dynamic_manager.h" + +#import "ios/web/web_state/js/crw_js_common_manager.h" +#include "ios/web/web_view_util.h" + +@implementation CRWJSMessageDynamicManager + +- (NSString*)scriptPath { + if (web::IsWKWebViewEnabled()) + return @"message_dynamic_wk"; + return @"message_dynamic_ui"; +} + +- (NSString*)presenceBeacon { + return @"__gCrWeb.message_dynamic"; +} + +- (NSArray*)directDependencies { + return @[ + // The 'base' manager is omitted deliberately; see . + [CRWJSCommonManager class], + ]; +} + +@end diff --git a/ios/web/web_state/js/crw_js_message_manager.mm b/ios/web/web_state/js/crw_js_message_manager.mm new file mode 100644 index 000000000000..59fd57d91568 --- /dev/null +++ b/ios/web/web_state/js/crw_js_message_manager.mm @@ -0,0 +1,25 @@ +// Copyright 2014 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. + +#import "ios/web/public/web_state/js/crw_js_message_manager.h" + +#import "ios/web/web_state/js/crw_js_common_manager.h" +#import "ios/web/web_state/js/crw_js_message_dynamic_manager.h" + +@implementation CRWJSMessageManager + +- (NSString*)scriptPath { + return @"message"; +} + +- (NSString*)presenceBeacon { + return @"__gCrWeb.message"; +} + +- (NSArray*)directDependencies { + // The 'base' manager is omitted deliberately; see . + return @[ [CRWJSCommonManager class], [CRWJSMessageDynamicManager class] ]; +} + +@end diff --git a/ios/web/web_view_util.h b/ios/web/web_view_util.h new file mode 100644 index 000000000000..5b58ad80d36f --- /dev/null +++ b/ios/web/web_view_util.h @@ -0,0 +1,26 @@ +// Copyright 2014 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. + +#ifndef IOS_WEB_WEB_VIEW_UTIL_H_ +#define IOS_WEB_WEB_VIEW_UTIL_H_ + +namespace web { + +// Returns true if WKWebView is being used instead of UIWebView. +// TODO(stuartmorgan): Eliminate this global flag in favor of a per-web-view +// flag. +bool IsWKWebViewEnabled(); + +// If |flag| is true, causes IsWKWebViewEnabled to return false, even if +// WKWebView is enabled using the compile time flag. Should only be called from +// ScopedUIWebViewEnforcer for use in unit tests that need to test UIWebView +// while WKWebView is enabled. +void SetForceUIWebView(bool flag); + +// Returns true if use of UIWebView is to be enforced. +bool GetForceUIWebView(); + +} // web + +#endif // IOS_WEB_WEB_VIEW_UTIL_H_ diff --git a/ios/web/web_view_util.mm b/ios/web/web_view_util.mm new file mode 100644 index 000000000000..d98330633880 --- /dev/null +++ b/ios/web/web_view_util.mm @@ -0,0 +1,36 @@ +// Copyright 2014 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/web_view_util.h" + +#include "base/ios/ios_util.h" + +namespace { + +// If true, UIWebView is always used even if WKWebView is available. +bool g_force_ui_web_view = false; + +} // namespace + +namespace web { + +bool IsWKWebViewEnabled() { +#if defined(ENABLE_WKWEBVIEW) + // Eventually this may be a dynamic flag, but for now it's purely a + // compile-time option. + return !g_force_ui_web_view && base::ios::IsRunningOnIOS8OrLater(); +#else + return false; +#endif +} + +void SetForceUIWebView(bool flag) { + g_force_ui_web_view = flag; +} + +bool GetForceUIWebView() { + return g_force_ui_web_view; +} + +} // namespace web -- 2.11.4.GIT