blob: 02737fc82a07a0b54767ea700615aaa5dca3baa1 [file] [log] [blame]
// 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.
#import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
#import <WebKit/WebKit.h>
#include "base/memory/ptr_util.h"
#import "ios/web/js_messaging/crw_wk_script_message_router.h"
#import "ios/web/js_messaging/page_script_util.h"
#import "ios/web/public/js_messaging/java_script_feature.h"
#include "ios/web/public/test/fakes/fake_browser_state.h"
#import "ios/web/public/test/fakes/fake_web_client.h"
#include "ios/web/public/test/scoped_testing_web_client.h"
#import "ios/web/public/web_client.h"
#import "ios/web/test/fakes/fake_wk_configuration_provider_observer.h"
#include "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#include "testing/platform_test.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Returns the WKUserScript from |user_scripts| which contains |script_string|
// or null if no such script is found.
WKUserScript* FindWKUserScriptContaining(NSArray<WKUserScript*>* user_scripts,
NSString* script_string) {
for (WKUserScript* user_script in user_scripts) {
if ([user_script.source containsString:script_string]) {
return user_script;
}
}
return nil;
}
} // namespace
namespace web {
namespace {
class WKWebViewConfigurationProviderTest : public PlatformTest {
public:
WKWebViewConfigurationProviderTest()
: web_client_(std::make_unique<FakeWebClient>()) {}
protected:
// Returns WKWebViewConfigurationProvider associated with |browser_state_|.
WKWebViewConfigurationProvider& GetProvider() {
return GetProvider(&browser_state_);
}
// Returns WKWebViewConfigurationProvider for given |browser_state|.
WKWebViewConfigurationProvider& GetProvider(
BrowserState* browser_state) const {
return WKWebViewConfigurationProvider::FromBrowserState(browser_state);
}
FakeWebClient* GetWebClient() {
return static_cast<FakeWebClient*>(web_client_.Get());
}
// BrowserState required for WKWebViewConfigurationProvider creation.
web::ScopedTestingWebClient web_client_;
FakeBrowserState browser_state_;
};
// Tests that each WKWebViewConfigurationProvider has own, non-nil
// configuration and configurations returned by the same provider will always
// have the same process pool.
TEST_F(WKWebViewConfigurationProviderTest, ConfigurationOwnerhip) {
// Configuration is not nil.
WKWebViewConfigurationProvider& provider = GetProvider(&browser_state_);
ASSERT_TRUE(provider.GetWebViewConfiguration());
// Same non-nil WKProcessPool for the same provider.
ASSERT_TRUE(provider.GetWebViewConfiguration().processPool);
EXPECT_EQ(provider.GetWebViewConfiguration().processPool,
provider.GetWebViewConfiguration().processPool);
// Different WKProcessPools for different providers.
FakeBrowserState other_browser_state;
WKWebViewConfigurationProvider& other_provider =
GetProvider(&other_browser_state);
EXPECT_NE(provider.GetWebViewConfiguration().processPool,
other_provider.GetWebViewConfiguration().processPool);
}
// Tests Non-OffTheRecord configuration.
TEST_F(WKWebViewConfigurationProviderTest, NoneOffTheRecordConfiguration) {
browser_state_.SetOffTheRecord(false);
WKWebViewConfigurationProvider& provider = GetProvider(&browser_state_);
EXPECT_TRUE(provider.GetWebViewConfiguration().websiteDataStore.persistent);
}
// Tests OffTheRecord configuration.
TEST_F(WKWebViewConfigurationProviderTest, OffTheRecordConfiguration) {
browser_state_.SetOffTheRecord(true);
WKWebViewConfigurationProvider& provider = GetProvider(&browser_state_);
WKWebViewConfiguration* config = provider.GetWebViewConfiguration();
ASSERT_TRUE(config);
EXPECT_FALSE(config.websiteDataStore.persistent);
}
// Tests that internal configuration object can not be changed by clients.
TEST_F(WKWebViewConfigurationProviderTest, ConfigurationProtection) {
WKWebViewConfigurationProvider& provider = GetProvider(&browser_state_);
WKWebViewConfiguration* config = provider.GetWebViewConfiguration();
WKProcessPool* pool = [config processPool];
WKPreferences* prefs = [config preferences];
WKUserContentController* userContentController =
[config userContentController];
// Change the properties of returned configuration object.
FakeBrowserState other_browser_state;
WKWebViewConfiguration* other_wk_web_view_configuration =
GetProvider(&other_browser_state).GetWebViewConfiguration();
ASSERT_TRUE(other_wk_web_view_configuration);
config.processPool = other_wk_web_view_configuration.processPool;
config.preferences = other_wk_web_view_configuration.preferences;
config.userContentController =
other_wk_web_view_configuration.userContentController;
// Make sure that the properties of internal configuration were not changed.
EXPECT_TRUE(provider.GetWebViewConfiguration().processPool);
EXPECT_EQ(pool, provider.GetWebViewConfiguration().processPool);
EXPECT_TRUE(provider.GetWebViewConfiguration().preferences);
EXPECT_EQ(prefs, provider.GetWebViewConfiguration().preferences);
EXPECT_TRUE(provider.GetWebViewConfiguration().userContentController);
EXPECT_EQ(userContentController,
provider.GetWebViewConfiguration().userContentController);
}
// Tests that script message router is bound to correct user content controller.
TEST_F(WKWebViewConfigurationProviderTest, ScriptMessageRouter) {
ASSERT_TRUE(GetProvider().GetWebViewConfiguration().userContentController);
EXPECT_EQ(GetProvider().GetWebViewConfiguration().userContentController,
GetProvider().GetScriptMessageRouter().userContentController);
}
// Tests that both configuration and script message router are deallocated after
// |Purge| call.
TEST_F(WKWebViewConfigurationProviderTest, Purge) {
__weak id config;
__weak id router;
@autoreleasepool { // Make sure that resulting copy is deallocated.
id strong_config = GetProvider().GetWebViewConfiguration();
config = strong_config;
router = GetProvider().GetScriptMessageRouter();
ASSERT_TRUE(config);
ASSERT_TRUE(router);
}
// No configuration and router after |Purge| call.
GetProvider().Purge();
EXPECT_FALSE(config);
EXPECT_FALSE(router);
}
// Tests that configuration's userContentController has only one script with the
// same content as web::GetDocumentStartScriptForMainFrame() returns.
TEST_F(WKWebViewConfigurationProviderTest, UserScript) {
WKUserContentController* user_content_controller =
GetProvider().GetWebViewConfiguration().userContentController;
WKUserScript* early_all_user_script = FindWKUserScriptContaining(
user_content_controller.userScripts,
GetDocumentStartScriptForAllFrames(&browser_state_));
ASSERT_TRUE(early_all_user_script);
EXPECT_FALSE(early_all_user_script.isForMainFrameOnly);
WKUserScript* main_frame_script = FindWKUserScriptContaining(
user_content_controller.userScripts,
GetDocumentStartScriptForMainFrame(&browser_state_));
ASSERT_TRUE(main_frame_script);
EXPECT_TRUE(main_frame_script.isForMainFrameOnly);
WKUserScript* late_all_frames_script = FindWKUserScriptContaining(
user_content_controller.userScripts,
GetDocumentEndScriptForAllFrames(&browser_state_));
ASSERT_TRUE(late_all_frames_script);
EXPECT_FALSE(late_all_frames_script.isForMainFrameOnly);
}
// Tests that configuration's userContentController has different scripts after
// the scripts are updated.
TEST_F(WKWebViewConfigurationProviderTest, UpdateScripts) {
FakeWebClient* client = GetWebClient();
client->SetEarlyPageScript(@"var test = 4;");
WKUserContentController* user_content_controller =
GetProvider().GetWebViewConfiguration().userContentController;
NSString* initial_main_frame_script =
GetDocumentStartScriptForMainFrame(&browser_state_);
WKUserScript* initial_script = FindWKUserScriptContaining(
user_content_controller.userScripts, initial_main_frame_script);
EXPECT_TRUE(initial_script);
client->SetEarlyPageScript(@"var test = 3;");
GetProvider().UpdateScripts();
NSString* updated_main_frame_script =
GetDocumentStartScriptForMainFrame(&browser_state_);
WKUserScript* updated_script = FindWKUserScriptContaining(
user_content_controller.userScripts, updated_main_frame_script);
EXPECT_TRUE(updated_script);
EXPECT_NE(updated_main_frame_script, initial_main_frame_script);
EXPECT_NE(initial_script.source, updated_script.source);
EXPECT_LT(
0U,
[updated_script.source rangeOfString:updated_main_frame_script].length);
EXPECT_EQ(
0U,
[initial_script.source rangeOfString:updated_main_frame_script].length);
}
// Tests that configuration's userContentController has additional scripts
// injected for JavaScriptFeatures configured through the WebClient.
TEST_F(WKWebViewConfigurationProviderTest, JavaScriptFeatureInjection) {
FakeWebClient* client = GetWebClient();
WKUserContentController* user_content_controller =
GetProvider().GetWebViewConfiguration().userContentController;
unsigned long original_script_count =
[user_content_controller.userScripts count];
std::vector<const web::JavaScriptFeature::FeatureScript> feature_scripts = {
web::JavaScriptFeature::FeatureScript::CreateWithFilename(
"all_frames_web_bundle",
web::JavaScriptFeature::FeatureScript::InjectionTime::kDocumentStart,
web::JavaScriptFeature::FeatureScript::TargetFrames::kAllFrames)};
std::unique_ptr<web::JavaScriptFeature> feature =
std::make_unique<web::JavaScriptFeature>(
web::JavaScriptFeature::ContentWorld::kPageContentWorld,
feature_scripts);
client->SetJavaScriptFeatures({feature.get()});
GetProvider().UpdateScripts();
EXPECT_GT([user_content_controller.userScripts count], original_script_count);
}
// Tests that observers methods are correctly triggered when observing the
// WKWebViewConfigurationProvider
TEST_F(WKWebViewConfigurationProviderTest, Observers) {
auto browser_state = std::make_unique<FakeBrowserState>();
WKWebViewConfigurationProvider* provider = &GetProvider(browser_state.get());
FakeWKConfigurationProviderObserver observer(provider);
EXPECT_FALSE(observer.GetLastCreatedWKConfiguration());
WKWebViewConfiguration* config = provider->GetWebViewConfiguration();
EXPECT_NSEQ(config.preferences,
observer.GetLastCreatedWKConfiguration().preferences);
observer.ResetLastCreatedWKConfig();
config = provider->GetWebViewConfiguration();
EXPECT_FALSE(observer.GetLastCreatedWKConfiguration());
}
// Tests that if -[ResetWithWebViewConfiguration:] copies and applies Chrome's
// initialization logic to the |config| that passed into that method
TEST_F(WKWebViewConfigurationProviderTest, ResetConfiguration) {
auto browser_state = std::make_unique<FakeBrowserState>();
WKWebViewConfigurationProvider* provider = &GetProvider(browser_state.get());
FakeWKConfigurationProviderObserver observer(provider);
ASSERT_FALSE(observer.GetLastCreatedWKConfiguration());
WKWebViewConfiguration* config = [[WKWebViewConfiguration alloc] init];
config.allowsInlineMediaPlayback = NO;
provider->ResetWithWebViewConfiguration(config);
WKWebViewConfiguration* actual = observer.GetLastCreatedWKConfiguration();
ASSERT_TRUE(actual);
// To check the configuration inside is reset.
EXPECT_EQ(config.preferences, actual.preferences);
// To check Chrome's initialization logic has been applied to |actual|,
// where the |actual.allowsInlineMediaPlayback| should be overwriten by YES.
EXPECT_EQ(NO, config.allowsInlineMediaPlayback);
EXPECT_EQ(YES, actual.allowsInlineMediaPlayback);
// Compares the POINTERS to make sure the |config| has been shallow cloned
// inside the |provider|.
EXPECT_NE(config, actual);
}
TEST_F(WKWebViewConfigurationProviderTest, GetContentRuleListProvider) {
auto browser_state = std::make_unique<FakeBrowserState>();
WKWebViewConfigurationProvider& provider = GetProvider(browser_state.get());
EXPECT_NE(nil, provider.GetContentRuleListProvider());
}
} // namespace
} // namespace web