java_script_feature.mm 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // Copyright 2020 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. #include "ios/web/public/js_messaging/java_script_feature.h"
  5. #import <Foundation/Foundation.h>
  6. #include "base/bind.h"
  7. #import "base/strings/sys_string_conversions.h"
  8. #include "base/time/time.h"
  9. #import "ios/web/js_messaging/java_script_content_world.h"
  10. #import "ios/web/js_messaging/java_script_feature_manager.h"
  11. #include "ios/web/js_messaging/page_script_util.h"
  12. #include "ios/web/js_messaging/web_frame_internal.h"
  13. #import "ios/web/public/js_messaging/web_frame.h"
  14. #if !defined(__has_feature) || !__has_feature(objc_arc)
  15. #error "This file requires ARC support."
  16. #endif
  17. namespace {
  18. // Returns a JavaScript safe string based on |script_filename|. This is used as
  19. // a unique identifier for a given script and passed to
  20. // |MakeScriptInjectableOnce| which ensures JS isn't executed multiple times due
  21. // to duplicate injection.
  22. NSString* InjectionTokenForScript(NSString* script_filename) {
  23. NSMutableCharacterSet* validCharacters =
  24. [NSMutableCharacterSet alphanumericCharacterSet];
  25. [validCharacters addCharactersInString:@"$_"];
  26. NSCharacterSet* invalidCharacters = validCharacters.invertedSet;
  27. NSString* token =
  28. [script_filename stringByTrimmingCharactersInSet:invalidCharacters];
  29. DCHECK_GT(token.length, 0ul);
  30. return token;
  31. }
  32. } // namespace
  33. namespace web {
  34. #pragma mark - JavaScriptFeature::FeatureScript
  35. JavaScriptFeature::FeatureScript
  36. JavaScriptFeature::FeatureScript::CreateWithFilename(
  37. const std::string& filename,
  38. InjectionTime injection_time,
  39. TargetFrames target_frames,
  40. ReinjectionBehavior reinjection_behavior,
  41. const PlaceholderReplacementsCallback& replacements_callback) {
  42. return JavaScriptFeature::FeatureScript(filename, injection_time,
  43. target_frames, reinjection_behavior,
  44. replacements_callback);
  45. }
  46. JavaScriptFeature::FeatureScript::FeatureScript(
  47. const std::string& filename,
  48. InjectionTime injection_time,
  49. TargetFrames target_frames,
  50. ReinjectionBehavior reinjection_behavior,
  51. const PlaceholderReplacementsCallback& replacements_callback)
  52. : script_filename_(filename),
  53. injection_time_(injection_time),
  54. target_frames_(target_frames),
  55. reinjection_behavior_(reinjection_behavior),
  56. replacements_callback_(replacements_callback) {}
  57. JavaScriptFeature::FeatureScript::FeatureScript(const FeatureScript&) = default;
  58. JavaScriptFeature::FeatureScript& JavaScriptFeature::FeatureScript::operator=(
  59. const FeatureScript&) = default;
  60. JavaScriptFeature::FeatureScript::FeatureScript(FeatureScript&&) = default;
  61. JavaScriptFeature::FeatureScript& JavaScriptFeature::FeatureScript::operator=(
  62. FeatureScript&&) = default;
  63. JavaScriptFeature::FeatureScript::~FeatureScript() = default;
  64. NSString* JavaScriptFeature::FeatureScript::GetScriptString() const {
  65. NSString* script_filename = base::SysUTF8ToNSString(script_filename_);
  66. if (reinjection_behavior_ ==
  67. ReinjectionBehavior::kReinjectOnDocumentRecreation) {
  68. return ReplacePlaceholders(GetPageScript(script_filename));
  69. }
  70. // WKUserScript instances will automatically be re-injected by WebKit when the
  71. // document is re-created, even though the JavaScript context will not be
  72. // re-created. So the script needs to be wrapped in |MakeScriptInjectableOnce|
  73. // so that is is not re-injected.
  74. return MakeScriptInjectableOnce(
  75. InjectionTokenForScript(script_filename),
  76. ReplacePlaceholders(GetPageScript(script_filename)));
  77. }
  78. NSString* JavaScriptFeature::FeatureScript::ReplacePlaceholders(
  79. NSString* script) const {
  80. if (replacements_callback_.is_null())
  81. return script;
  82. PlaceholderReplacements replacements = replacements_callback_.Run();
  83. if (!replacements)
  84. return script;
  85. for (NSString* key in replacements) {
  86. script = [script stringByReplacingOccurrencesOfString:key
  87. withString:replacements[key]];
  88. }
  89. return script;
  90. }
  91. #pragma mark - JavaScriptFeature
  92. JavaScriptFeature::JavaScriptFeature(ContentWorld supported_world)
  93. : supported_world_(supported_world), weak_factory_(this) {}
  94. JavaScriptFeature::JavaScriptFeature(
  95. ContentWorld supported_world,
  96. std::vector<const FeatureScript> feature_scripts)
  97. : supported_world_(supported_world),
  98. scripts_(feature_scripts),
  99. weak_factory_(this) {}
  100. JavaScriptFeature::JavaScriptFeature(
  101. ContentWorld supported_world,
  102. std::vector<const FeatureScript> feature_scripts,
  103. std::vector<const JavaScriptFeature*> dependent_features)
  104. : supported_world_(supported_world),
  105. scripts_(feature_scripts),
  106. dependent_features_(dependent_features),
  107. weak_factory_(this) {}
  108. JavaScriptFeature::~JavaScriptFeature() = default;
  109. JavaScriptFeature::ContentWorld JavaScriptFeature::GetSupportedContentWorld()
  110. const {
  111. return supported_world_;
  112. }
  113. const std::vector<const JavaScriptFeature::FeatureScript>
  114. JavaScriptFeature::GetScripts() const {
  115. return scripts_;
  116. }
  117. const std::vector<const JavaScriptFeature*>
  118. JavaScriptFeature::GetDependentFeatures() const {
  119. return dependent_features_;
  120. }
  121. absl::optional<std::string> JavaScriptFeature::GetScriptMessageHandlerName()
  122. const {
  123. return absl::nullopt;
  124. }
  125. absl::optional<JavaScriptFeature::ScriptMessageHandler>
  126. JavaScriptFeature::GetScriptMessageHandler() const {
  127. if (!GetScriptMessageHandlerName()) {
  128. return absl::nullopt;
  129. }
  130. return base::BindRepeating(&JavaScriptFeature::ScriptMessageReceived,
  131. weak_factory_.GetWeakPtr());
  132. }
  133. void JavaScriptFeature::ScriptMessageReceived(WebState* web_state,
  134. const ScriptMessage& message) {}
  135. bool JavaScriptFeature::CallJavaScriptFunction(
  136. WebFrame* web_frame,
  137. const std::string& function_name,
  138. const std::vector<base::Value>& parameters) {
  139. DCHECK(web_frame);
  140. JavaScriptFeatureManager* feature_manager =
  141. JavaScriptFeatureManager::FromBrowserState(web_frame->GetBrowserState());
  142. DCHECK(feature_manager);
  143. JavaScriptContentWorld* content_world =
  144. feature_manager->GetContentWorldForFeature(this);
  145. DCHECK(content_world);
  146. return web_frame->GetWebFrameInternal()->CallJavaScriptFunctionInContentWorld(
  147. function_name, parameters, content_world);
  148. }
  149. bool JavaScriptFeature::CallJavaScriptFunction(
  150. WebFrame* web_frame,
  151. const std::string& function_name,
  152. const std::vector<base::Value>& parameters,
  153. base::OnceCallback<void(const base::Value*)> callback,
  154. base::TimeDelta timeout) {
  155. DCHECK(web_frame);
  156. JavaScriptFeatureManager* feature_manager =
  157. JavaScriptFeatureManager::FromBrowserState(web_frame->GetBrowserState());
  158. DCHECK(feature_manager);
  159. JavaScriptContentWorld* content_world =
  160. feature_manager->GetContentWorldForFeature(this);
  161. DCHECK(content_world);
  162. return web_frame->GetWebFrameInternal()->CallJavaScriptFunctionInContentWorld(
  163. function_name, parameters, content_world, std::move(callback), timeout);
  164. }
  165. } // namespace web