diff --git a/BUILD.gn b/BUILD.gn index 72893af1..f9c6907 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -897,6 +897,7 @@ data_deps = [ ":layout_test_data_mojo_bindings", "//content/shell:content_shell", + "//mojo/public/interfaces/bindings/tests:test_associated_interfaces", "//mojo/public/interfaces/bindings/tests:test_interfaces", "//third_party/WebKit/public:blink_devtools_frontend_resources_files", "//third_party/mesa:osmesa",
diff --git a/DEPS b/DEPS index 88c054a5..382871a 100644 --- a/DEPS +++ b/DEPS
@@ -44,7 +44,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': '36a60914e44cddc57bc81aee75cd6bb56fa8a51d', + 'v8_revision': '0512f9ad8a5fda58196ce7bd9f5ab1233507d7e2', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -64,7 +64,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '350d2d904a3e6bd1e96542c5e223d301d9bdbe6a', + 'pdfium_revision': '25694831670ef6172b1b9b71359a6c192e26da20', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling openmax_dl # and whatever else without interference from each other. @@ -96,7 +96,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': 'b5b525957fba7bd8fce6908b02df49f0d7e0992c', + 'catapult_revision': '4d4343808af0ecf365e4a789add07c30d17f6b0e', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other.
diff --git a/ash/public/cpp/shell_window_ids.h b/ash/public/cpp/shell_window_ids.h index 5f3ce13..ab5a168 100644 --- a/ash/public/cpp/shell_window_ids.h +++ b/ash/public/cpp/shell_window_ids.h
@@ -113,7 +113,7 @@ // The topmost container, used for power off animation. kShellWindowId_PowerButtonAnimationContainer, - kShellWindowId_Min = kShellWindowId_NonLockScreenContainersContainer, + kShellWindowId_Min = kShellWindowId_ScreenRotationContainer, kShellWindowId_Max = kShellWindowId_PowerButtonAnimationContainer, };
diff --git a/chrome/VERSION b/chrome/VERSION index 9174e1f..a4ae5445 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=60 MINOR=0 -BUILD=3078 +BUILD=3079 PATCH=0
diff --git a/chrome/browser/resources/print_preview/previewarea/preview_area.js b/chrome/browser/resources/print_preview/previewarea/preview_area.js index 522995c..a8f754e 100644 --- a/chrome/browser/resources/print_preview/previewarea/preview_area.js +++ b/chrome/browser/resources/print_preview/previewarea/preview_area.js
@@ -484,11 +484,7 @@ * @private */ createPlugin_: function(srcUrl) { - if (this.plugin_) { - console.warn('Pdf preview plugin already created'); - return; - } - + assert(!this.plugin_); this.plugin_ = /** @type {print_preview.PDFPlugin} */( PDFCreateOutOfProcessPlugin(srcUrl)); this.plugin_.setKeyEventCallback(this.keyEventCallback_); @@ -503,15 +499,9 @@ this.getChildElement('.preview-area-plugin-wrapper'). appendChild(/** @type {Node} */(this.plugin_)); - - var pageNumbers = - this.printTicketStore_.pageRange.getPageNumberSet().asArray(); - var grayscale = !this.printTicketStore_.color.getValue(); this.plugin_.setLoadCallback(this.onPluginLoad_.bind(this)); this.plugin_.setViewportChangedCallback( this.onPreviewVisualStateChange_.bind(this)); - this.plugin_.resetPrintPreviewMode(srcUrl, grayscale, pageNumbers, - this.documentInfo_.isModifiable); }, /** @@ -569,14 +559,13 @@ this.isPluginReloaded_ = false; if (!this.plugin_) { this.createPlugin_(event.previewUrl); - } else { - var grayscale = !this.printTicketStore_.color.getValue(); - var pageNumbers = - this.printTicketStore_.pageRange.getPageNumberSet().asArray(); - var url = event.previewUrl; - this.plugin_.resetPrintPreviewMode(url, grayscale, pageNumbers, - this.documentInfo_.isModifiable); } + this.plugin_.resetPrintPreviewMode( + event.previewUrl, + !this.printTicketStore_.color.getValue(), + this.printTicketStore_.pageRange.getPageNumberSet().asArray(), + this.documentInfo_.isModifiable); + cr.dispatchSimpleEvent( this, PreviewArea.EventType.PREVIEW_GENERATION_IN_PROGRESS); },
diff --git a/chrome/browser/resources/settings/certificate_manager_page/certificate_entry.html b/chrome/browser/resources/settings/certificate_manager_page/certificate_entry.html index 31cb15c..0b44004 100644 --- a/chrome/browser/resources/settings/certificate_manager_page/certificate_entry.html +++ b/chrome/browser/resources/settings/certificate_manager_page/certificate_entry.html
@@ -1,6 +1,5 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html"> <link rel="import" href="certificates_browser_proxy.html"> <link rel="import" href="../settings_shared_css.html"> <link rel="import" href="certificate_subentry.html"> @@ -15,16 +14,14 @@ </cr-expand-button> </div> <template is="dom-if" if="[[expanded_]]"> - <iron-collapse opened="[[expanded_]]" no-animation> - <div class="list-frame"> - <template is="dom-repeat" items="[[model.subnodes]]"> - <settings-certificate-subentry model="[[item]]" - certificate-type="[[certificateType]]" - is-last$="[[isLast_(index, model)]]"> - </settings-certificate-subentry> - </template> - </div> - <iron-collapse> + <div class="list-frame"> + <template is="dom-repeat" items="[[model.subnodes]]"> + <settings-certificate-subentry model="[[item]]" + certificate-type="[[certificateType]]" + is-last$="[[isLast_(index, model)]]"> + </settings-certificate-subentry> + </template> + </div> </template> </template> <script src="certificate_entry.js"></script>
diff --git a/chrome/browser/resources/settings/settings_page/compiled_resources2.gyp b/chrome/browser/resources/settings/settings_page/compiled_resources2.gyp index f702f75..b008722 100644 --- a/chrome/browser/resources/settings/settings_page/compiled_resources2.gyp +++ b/chrome/browser/resources/settings/settings_page/compiled_resources2.gyp
@@ -19,7 +19,10 @@ 'dependencies': [ '../compiled_resources2.gyp:route', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', + '<(DEPTH)/ui/webui/resources/js/cr/compiled_resources2.gyp:ui', + '<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:focus_outline_manager', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], },
diff --git a/chrome/browser/resources/settings/settings_page/settings_animated_pages.html b/chrome/browser/resources/settings/settings_page/settings_animated_pages.html index 42e1956..720893f 100644 --- a/chrome/browser/resources/settings/settings_page/settings_animated_pages.html +++ b/chrome/browser/resources/settings/settings_page/settings_animated_pages.html
@@ -1,4 +1,7 @@ <link rel="import" href="chrome://resources/html/assert.html"> +<link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://resources/html/cr/ui.html"> +<link rel="import" href="chrome://resources/html/cr/ui/focus_outline_manager.html"> <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout.html"> <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/animations/fade-in-animation.html">
diff --git a/chrome/browser/resources/settings/settings_page/settings_animated_pages.js b/chrome/browser/resources/settings/settings_page/settings_animated_pages.js index 3da7e7a..b50d4fc 100644 --- a/chrome/browser/resources/settings/settings_page/settings_animated_pages.js +++ b/chrome/browser/resources/settings/settings_page/settings_animated_pages.js
@@ -51,6 +51,11 @@ this.lightDomChanged_.bind(this)); }, + /** @override */ + attached: function() { + this.outline_ = cr.ui.FocusOutlineManager.forDocument(document); + }, + /** * @param {!Event} e * @private @@ -82,7 +87,19 @@ // the currentRouteChanged callback. Using 'iron-select' listener which // fires after the animation has finished allows focus() to work as // expected. - this.querySelector(selector).focus(); + var toFocus = this.querySelector(selector); + var suppressInk = !this.outline_.visible; + var origNoInk; + + if (suppressInk) { + origNoInk = toFocus.noink; + toFocus.noink = true; + } + + toFocus.focus(); + + if (suppressInk) + toFocus.noink = origNoInk; } },
diff --git a/components/certificate_transparency/single_tree_tracker.cc b/components/certificate_transparency/single_tree_tracker.cc index f3d151e..99fe13f 100644 --- a/components/certificate_transparency/single_tree_tracker.cc +++ b/components/certificate_transparency/single_tree_tracker.cc
@@ -25,7 +25,6 @@ #include "net/log/net_log.h" using net::SHA256HashValue; -using net::ct::LogEntry; using net::ct::MerkleAuditProof; using net::ct::MerkleTreeLeaf; using net::ct::SignedCertificateTimestamp; @@ -33,10 +32,6 @@ // Overview of the process for auditing CT log entries // -// A CT log entry, represented by net::ct::LogEntry, is made up of the -// end-entity certificate and the Signed Certificate Timestamp associated with -// it. -// // In this file, obsered CT log entries are audited for inclusion in the CT log. // A pre-requirement for auditing a log entry is having a Signed Tree Head (STH) // from that log that is 24 hours (MMD period) after the timestamp in the SCT.
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py index 179d0ed..029b7420 100644 --- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py +++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -223,9 +223,6 @@ self.Fail('conformance2/glsl3/vector-dynamic-indexing-swizzled-lvalue.html', ['mac'], bug=709351) self.Fail('conformance2/rendering/' + - 'blitframebuffer-resolve-to-back-buffer.html', - ['mac'], bug=699566) - self.Fail('conformance2/rendering/' + 'framebuffer-completeness-unaffected.html', ['mac'], bug=630800) self.Fail('deqp/functional/gles3/fbocompleteness.html',
diff --git a/device/BUILD.gn b/device/BUILD.gn index 1542a87..0b508417 100644 --- a/device/BUILD.gn +++ b/device/BUILD.gn
@@ -74,8 +74,6 @@ "sensors/sensor_manager_android_unittest.cc", "sensors/sensor_manager_chromeos_unittest.cc", "test/run_all_unittests.cc", - "u2f/u2f_apdu_unittest.cc", - "u2f/u2f_message_unittest.cc", ] deps = [ @@ -135,13 +133,20 @@ "hid/test_report_descriptors.cc", "hid/test_report_descriptors.h", "serial/serial_io_handler_posix_unittest.cc", + "u2f/u2f_apdu_unittest.cc", "u2f/u2f_hid_device_unittest.cc", + "u2f/u2f_message_unittest.cc", + "u2f/u2f_register_unittest.cc", + "u2f/u2f_request_unittest.cc", + "u2f/u2f_sign_unittest.cc", ] deps += [ "//device/hid", "//device/hid:mocks", "//device/serial", "//device/serial:test_support", + "//device/u2f", + "//device/u2f:mocks", ] } @@ -171,7 +176,6 @@ deps += [ "//device/base", "//device/base:mocks", - "//device/u2f", "//device/usb", "//device/usb:test_support", "//device/usb/mojo",
diff --git a/device/geolocation/wifi_data_provider_common.cc b/device/geolocation/wifi_data_provider_common.cc index 729efb60..527de27 100644 --- a/device/geolocation/wifi_data_provider_common.cc +++ b/device/geolocation/wifi_data_provider_common.cc
@@ -13,7 +13,7 @@ namespace device { base::string16 MacAddressAsString16(const uint8_t mac_as_int[6]) { - // mac_as_int is big-endian. Write in byte chunks. + // |mac_as_int| is big-endian. Write in byte chunks. // Format is XX-XX-XX-XX-XX-XX. static const char* const kMacFormatString = "%02x-%02x-%02x-%02x-%02x-%02x"; return base::ASCIIToUTF16(base::StringPrintf( @@ -27,17 +27,17 @@ WifiDataProviderCommon::~WifiDataProviderCommon() {} void WifiDataProviderCommon::StartDataProvider() { - DCHECK(wlan_api_ == NULL); + DCHECK(!wlan_api_); wlan_api_.reset(NewWlanApi()); - if (wlan_api_ == NULL) { + if (!wlan_api_) { // Error! Can't do scans, so don't try and schedule one. is_first_scan_complete_ = true; return; } - DCHECK(polling_policy_ == NULL); + DCHECK(!polling_policy_); polling_policy_.reset(NewPollingPolicy()); - DCHECK(polling_policy_ != NULL); + DCHECK(polling_policy_); // Perform first scan ASAP regardless of the polling policy. If this scan // fails we'll retry at a rate in line with the polling policy.
diff --git a/device/geolocation/wifi_data_provider_common.h b/device/geolocation/wifi_data_provider_common.h index 68f9c4c..a569af1e 100644 --- a/device/geolocation/wifi_data_provider_common.h +++ b/device/geolocation/wifi_data_provider_common.h
@@ -50,9 +50,10 @@ protected: ~WifiDataProviderCommon() override; + // TODO(mcasas): change return types and possibly names of these two methods, + // see https://crbug.com/714348. // Returns ownership. virtual WlanApiInterface* NewWlanApi() = 0; - // Returns ownership. virtual WifiPollingPolicy* NewPollingPolicy() = 0;
diff --git a/device/geolocation/wifi_data_provider_common_unittest.cc b/device/geolocation/wifi_data_provider_common_unittest.cc index eb3257e2..4e837b79 100644 --- a/device/geolocation/wifi_data_provider_common_unittest.cc +++ b/device/geolocation/wifi_data_provider_common_unittest.cc
@@ -17,33 +17,28 @@ #include "testing/gtest/include/gtest/gtest.h" using testing::_; +using testing::AnyNumber; using testing::AtLeast; -using testing::DoDefault; +using testing::DoAll; using testing::Invoke; +using testing::InvokeWithoutArgs; using testing::Return; +using testing::SetArgPointee; +using testing::WithArgs; namespace device { class MockWlanApi : public WifiDataProviderCommon::WlanApiInterface { public: - MockWlanApi() : calls_(0), bool_return_(true) { - ANNOTATE_BENIGN_RACE(&calls_, "This is a test-only data race on a counter"); + MockWlanApi() { ON_CALL(*this, GetAccessPointData(_)) - .WillByDefault(Invoke(this, &MockWlanApi::GetAccessPointDataInternal)); + .WillByDefault(DoAll(SetArgPointee<0>(data_out_), Return(true))); } MOCK_METHOD1(GetAccessPointData, bool(WifiData::AccessPointDataSet* data)); - int calls_; - bool bool_return_; - WifiData::AccessPointDataSet data_out_; - private: - bool GetAccessPointDataInternal(WifiData::AccessPointDataSet* data) { - ++calls_; - *data = data_out_; - return bool_return_; - } + WifiData::AccessPointDataSet data_out_; }; class MockPollingPolicy : public WifiPollingPolicy { @@ -51,32 +46,29 @@ MockPollingPolicy() { ON_CALL(*this, PollingInterval()).WillByDefault(Return(1)); ON_CALL(*this, NoWifiInterval()).WillByDefault(Return(1)); + // We are not interested in calls to UpdatePollingInterval() method. + EXPECT_CALL(*this, UpdatePollingInterval(_)).Times(AnyNumber()); } + // WifiPollingPolicy implementation. + MOCK_METHOD1(UpdatePollingInterval, void(bool)); MOCK_METHOD0(PollingInterval, int()); MOCK_METHOD0(NoWifiInterval, int()); - - virtual void UpdatePollingInterval(bool) {} }; class WifiDataProviderCommonWithMock : public WifiDataProviderCommon { public: WifiDataProviderCommonWithMock() - : new_wlan_api_(new MockWlanApi), - new_polling_policy_(new MockPollingPolicy) {} + : wlan_api_(new MockWlanApi), polling_policy_(new MockPollingPolicy) {} // WifiDataProviderCommon - WlanApiInterface* NewWlanApi() override { - CHECK(new_wlan_api_ != NULL); - return new_wlan_api_.release(); - } + WlanApiInterface* NewWlanApi() override { return wlan_api_.release(); } WifiPollingPolicy* NewPollingPolicy() override { - CHECK(new_polling_policy_ != NULL); - return new_polling_policy_.release(); + return polling_policy_.release(); } - std::unique_ptr<MockWlanApi> new_wlan_api_; - std::unique_ptr<MockPollingPolicy> new_polling_policy_; + std::unique_ptr<MockWlanApi> wlan_api_; + std::unique_ptr<MockPollingPolicy> polling_policy_; private: ~WifiDataProviderCommonWithMock() override {} @@ -84,119 +76,93 @@ DISALLOW_COPY_AND_ASSIGN(WifiDataProviderCommonWithMock); }; -WifiDataProvider* CreateWifiDataProviderCommonWithMock() { - return new WifiDataProviderCommonWithMock; -} - // Main test fixture class GeolocationWifiDataProviderCommonTest : public testing::Test { public: GeolocationWifiDataProviderCommonTest() - : main_task_runner_(base::ThreadTaskRunnerHandle::Get()), - wifi_data_callback_( - base::Bind(&GeolocationWifiDataProviderCommonTest::OnWifiDataUpdate, - base::Unretained(this))) {} + : wifi_data_callback_(base::Bind(&base::DoNothing)), + provider_(new WifiDataProviderCommonWithMock), + wlan_api_(provider_->wlan_api_.get()), + polling_policy_(provider_->polling_policy_.get()) {} void SetUp() override { - provider_ = new WifiDataProviderCommonWithMock; - wlan_api_ = provider_->new_wlan_api_.get(); - polling_policy_ = provider_->new_polling_policy_.get(); provider_->AddCallback(&wifi_data_callback_); } void TearDown() override { provider_->RemoveCallback(&wifi_data_callback_); provider_->StopDataProvider(); - provider_ = NULL; - } - - void OnWifiDataUpdate() { - // Callbacks must run on the originating thread. - EXPECT_TRUE(main_task_runner_->BelongsToCurrentThread()); - run_loop_->Quit(); - } - - void RunLoop() { - run_loop_.reset(new base::RunLoop()); - run_loop_->Run(); } protected: - base::MessageLoopForUI message_loop_; - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; - std::unique_ptr<base::RunLoop> run_loop_; + const base::MessageLoopForUI message_loop_; WifiDataProviderManager::WifiDataUpdateCallback wifi_data_callback_; - scoped_refptr<WifiDataProviderCommonWithMock> provider_; - MockWlanApi* wlan_api_; - MockPollingPolicy* polling_policy_; + const scoped_refptr<WifiDataProviderCommonWithMock> provider_; + + MockWlanApi* const wlan_api_; + MockPollingPolicy* const polling_policy_; }; TEST_F(GeolocationWifiDataProviderCommonTest, CreateDestroy) { // Test fixture members were SetUp correctly. - EXPECT_TRUE(main_task_runner_->BelongsToCurrentThread()); - EXPECT_TRUE(NULL != provider_.get()); - EXPECT_TRUE(NULL != wlan_api_); -} - -TEST_F(GeolocationWifiDataProviderCommonTest, RunNormal) { - EXPECT_CALL(*wlan_api_, GetAccessPointData(_)).Times(AtLeast(1)); - EXPECT_CALL(*polling_policy_, PollingInterval()).Times(AtLeast(1)); - provider_->StartDataProvider(); - RunLoop(); - SUCCEED(); + EXPECT_TRUE(provider_); + EXPECT_TRUE(wlan_api_); + EXPECT_TRUE(polling_policy_); } TEST_F(GeolocationWifiDataProviderCommonTest, NoWifi) { + base::RunLoop run_loop; EXPECT_CALL(*polling_policy_, NoWifiInterval()).Times(AtLeast(1)); - EXPECT_CALL(*wlan_api_, GetAccessPointData(_)).WillRepeatedly(Return(false)); + EXPECT_CALL(*wlan_api_, GetAccessPointData(_)) + .WillOnce(InvokeWithoutArgs([&run_loop]() { + run_loop.Quit(); + return false; + })); + provider_->StartDataProvider(); - RunLoop(); + run_loop.Run(); } TEST_F(GeolocationWifiDataProviderCommonTest, IntermittentWifi) { + base::RunLoop run_loop; EXPECT_CALL(*polling_policy_, PollingInterval()).Times(AtLeast(1)); EXPECT_CALL(*polling_policy_, NoWifiInterval()).Times(1); EXPECT_CALL(*wlan_api_, GetAccessPointData(_)) .WillOnce(Return(true)) - .WillOnce(Return(false)) - .WillRepeatedly(DoDefault()); - - AccessPointData single_access_point; - single_access_point.channel = 2; - single_access_point.mac_address = 3; - single_access_point.radio_signal_strength = 4; - single_access_point.signal_to_noise = 5; - single_access_point.ssid = base::ASCIIToUTF16("foossid"); - wlan_api_->data_out_.insert(single_access_point); + .WillOnce(InvokeWithoutArgs([&run_loop]() { + run_loop.Quit(); + return false; + })); provider_->StartDataProvider(); - RunLoop(); - RunLoop(); + run_loop.Run(); } -#if defined(OS_MACOSX) -#define MAYBE_DoAnEmptyScan DISABLED_DoAnEmptyScan -#else -#define MAYBE_DoAnEmptyScan DoAnEmptyScan -#endif -TEST_F(GeolocationWifiDataProviderCommonTest, MAYBE_DoAnEmptyScan) { - EXPECT_CALL(*wlan_api_, GetAccessPointData(_)).Times(AtLeast(1)); +// This test runs StartDataProvider() and expects that GetAccessPointData() is +// called. The retrieved WifiData is expected to be empty. +TEST_F(GeolocationWifiDataProviderCommonTest, DoAnEmptyScan) { + base::RunLoop run_loop; + EXPECT_CALL(*polling_policy_, PollingInterval()).Times(AtLeast(1)); + EXPECT_CALL(*wlan_api_, GetAccessPointData(_)) + .WillOnce(InvokeWithoutArgs([&run_loop]() { + run_loop.Quit(); + return true; + })); + provider_->StartDataProvider(); - RunLoop(); - EXPECT_EQ(wlan_api_->calls_, 1); + run_loop.Run(); + WifiData data; EXPECT_TRUE(provider_->GetData(&data)); - EXPECT_EQ(0, static_cast<int>(data.access_point_data.size())); + EXPECT_TRUE(data.access_point_data.empty()); } -#if defined(OS_MACOSX) -#define MAYBE_DoScanWithResults DISABLED_DoScanWithResults -#else -#define MAYBE_DoScanWithResults DoScanWithResults -#endif -TEST_F(GeolocationWifiDataProviderCommonTest, MAYBE_DoScanWithResults) { - EXPECT_CALL(*wlan_api_, GetAccessPointData(_)).Times(AtLeast(1)); +// This test runs StartDataProvider() and expects that GetAccessPointData() is +// called. Some mock WifiData is returned then and expected to be retrieved. +TEST_F(GeolocationWifiDataProviderCommonTest, DoScanWithResults) { + base::RunLoop run_loop; + EXPECT_CALL(*polling_policy_, PollingInterval()).Times(AtLeast(1)); AccessPointData single_access_point; single_access_point.channel = 2; @@ -204,24 +170,24 @@ single_access_point.radio_signal_strength = 4; single_access_point.signal_to_noise = 5; single_access_point.ssid = base::ASCIIToUTF16("foossid"); - wlan_api_->data_out_.insert(single_access_point); + + WifiData::AccessPointDataSet data_out({single_access_point}); + + EXPECT_CALL(*wlan_api_, GetAccessPointData(_)) + .WillOnce(WithArgs<0>( + Invoke([&data_out, &run_loop](WifiData::AccessPointDataSet* data) { + *data = data_out; + run_loop.Quit(); + return true; + }))); provider_->StartDataProvider(); - RunLoop(); - EXPECT_EQ(wlan_api_->calls_, 1); + run_loop.Run(); + WifiData data; EXPECT_TRUE(provider_->GetData(&data)); - EXPECT_EQ(1, static_cast<int>(data.access_point_data.size())); + ASSERT_EQ(1u, data.access_point_data.size()); EXPECT_EQ(single_access_point.ssid, data.access_point_data.begin()->ssid); } -TEST_F(GeolocationWifiDataProviderCommonTest, RegisterUnregister) { - WifiDataProviderManager::SetFactoryForTesting( - CreateWifiDataProviderCommonWithMock); - WifiDataProviderManager::Register(&wifi_data_callback_); - RunLoop(); - WifiDataProviderManager::Unregister(&wifi_data_callback_); - WifiDataProviderManager::ResetFactoryForTesting(); -} - } // namespace device
diff --git a/device/u2f/BUILD.gn b/device/u2f/BUILD.gn index 91fd6a2..0fcfe4b 100644 --- a/device/u2f/BUILD.gn +++ b/device/u2f/BUILD.gn
@@ -19,6 +19,13 @@ "u2f_message.h", "u2f_packet.cc", "u2f_packet.h", + "u2f_register.cc", + "u2f_register.h", + "u2f_request.cc", + "u2f_request.h", + "u2f_return_code.h", + "u2f_sign.cc", + "u2f_sign.h", ] deps = [ @@ -30,6 +37,21 @@ ] } +source_set("mocks") { + testonly = true + + sources = [ + "mock_u2f_device.cc", + "mock_u2f_device.h", + ] + + deps = [ + ":u2f", + "//base", + "//testing/gmock", + ] +} + fuzzer_test("u2f_apdu_fuzzer") { sources = [ "u2f_apdu_fuzzer.cc",
diff --git a/device/u2f/mock_u2f_device.cc b/device/u2f/mock_u2f_device.cc new file mode 100644 index 0000000..4f8bdc4 --- /dev/null +++ b/device/u2f/mock_u2f_device.cc
@@ -0,0 +1,53 @@ +// Copyright 2017 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 "mock_u2f_device.h" + +namespace device { + +MockU2fDevice::MockU2fDevice() {} + +MockU2fDevice::~MockU2fDevice() {} + +void MockU2fDevice::DeviceTransact(std::unique_ptr<U2fApduCommand> command, + const DeviceCallback& cb) { + DeviceTransactPtr(command.get(), cb); +} + +// static +void MockU2fDevice::NotSatisfied(U2fApduCommand* cmd, + const DeviceCallback& cb) { + cb.Run(true, base::MakeUnique<U2fApduResponse>( + std::vector<uint8_t>(), + U2fApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED)); +} + +// static +void MockU2fDevice::WrongData(U2fApduCommand* cmd, const DeviceCallback& cb) { + cb.Run(true, + base::MakeUnique<U2fApduResponse>( + std::vector<uint8_t>(), U2fApduResponse::Status::SW_WRONG_DATA)); +} + +// static +void MockU2fDevice::NoErrorSign(U2fApduCommand* cmd, const DeviceCallback& cb) { + cb.Run(true, base::MakeUnique<U2fApduResponse>( + std::vector<uint8_t>({kSign}), + U2fApduResponse::Status::SW_NO_ERROR)); +} + +// static +void MockU2fDevice::NoErrorRegister(U2fApduCommand* cmd, + const DeviceCallback& cb) { + cb.Run(true, base::MakeUnique<U2fApduResponse>( + std::vector<uint8_t>({kRegister}), + U2fApduResponse::Status::SW_NO_ERROR)); +} + +// static +void MockU2fDevice::WinkDoNothing(const WinkCallback& cb) { + cb.Run(); +} + +} // namespace device
diff --git a/device/u2f/mock_u2f_device.h b/device/u2f/mock_u2f_device.h new file mode 100644 index 0000000..f4e65da --- /dev/null +++ b/device/u2f/mock_u2f_device.h
@@ -0,0 +1,43 @@ +// Copyright 2017 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 DEVICE_U2F_MOCK_U2F_DEVICE_H_ +#define DEVICE_U2F_MOCK_U2F_DEVICE_H_ + +#include <vector> + +#include "base/memory/ptr_util.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "u2f_apdu_command.h" +#include "u2f_device.h" + +namespace device { + +class MockU2fDevice : public U2fDevice { + public: + static constexpr uint8_t kSign = 0x1; + static constexpr uint8_t kRegister = 0x5; + + MockU2fDevice(); + ~MockU2fDevice() override; + + MOCK_METHOD1(TryWink, void(const WinkCallback& cb)); + MOCK_METHOD0(GetId, std::string(void)); + // GMock cannot mock a method taking a std::unique_ptr<T> + MOCK_METHOD2(DeviceTransactPtr, + void(U2fApduCommand* command, const DeviceCallback& cb)); + void DeviceTransact(std::unique_ptr<U2fApduCommand> command, + const DeviceCallback& cb) override; + static void TransactNoError(std::unique_ptr<U2fApduCommand> command, + const DeviceCallback& cb); + static void NotSatisfied(U2fApduCommand* cmd, const DeviceCallback& cb); + static void WrongData(U2fApduCommand* cmd, const DeviceCallback& cb); + static void NoErrorSign(U2fApduCommand* cmd, const DeviceCallback& cb); + static void NoErrorRegister(U2fApduCommand* cmd, const DeviceCallback& cb); + static void WinkDoNothing(const WinkCallback& cb); +}; + +} // namespace device + +#endif // DEVICE_U2F_MOCK_U2F_DEVICE_H_
diff --git a/device/u2f/u2f_apdu_response.h b/device/u2f/u2f_apdu_response.h index 44bf7d6..2e5d74e 100644 --- a/device/u2f/u2f_apdu_response.h +++ b/device/u2f/u2f_apdu_response.h
@@ -23,6 +23,7 @@ SW_NO_ERROR = 0x9000, SW_CONDITIONS_NOT_SATISFIED = 0x6985, SW_WRONG_DATA = 0x6A80, + SW_WRONG_LENGTH = 0x6700, }; U2fApduResponse(std::vector<uint8_t> message, Status response_status);
diff --git a/device/u2f/u2f_device.cc b/device/u2f/u2f_device.cc index dc9e5ec..20575cf 100644 --- a/device/u2f/u2f_device.cc +++ b/device/u2f/u2f_device.cc
@@ -20,7 +20,7 @@ std::unique_ptr<U2fApduCommand> register_cmd = U2fApduCommand::CreateRegister(app_param, challenge_param); if (!register_cmd) { - callback.Run(ReturnCode::INVALID_PARAMS, std::vector<uint8_t>()); + callback.Run(U2fReturnCode::INVALID_PARAMS, std::vector<uint8_t>()); return; } DeviceTransact(std::move(register_cmd), @@ -35,7 +35,7 @@ std::unique_ptr<U2fApduCommand> sign_cmd = U2fApduCommand::CreateSign(app_param, challenge_param, key_handle); if (!sign_cmd) { - callback.Run(ReturnCode::INVALID_PARAMS, std::vector<uint8_t>()); + callback.Run(U2fReturnCode::INVALID_PARAMS, std::vector<uint8_t>()); return; } DeviceTransact(std::move(sign_cmd), @@ -59,22 +59,22 @@ bool success, std::unique_ptr<U2fApduResponse> register_response) { if (!success || !register_response) { - callback.Run(ReturnCode::FAILURE, std::vector<uint8_t>()); + callback.Run(U2fReturnCode::FAILURE, std::vector<uint8_t>()); return; } switch (register_response->status()) { case U2fApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED: - callback.Run(ReturnCode::CONDITIONS_NOT_SATISFIED, + callback.Run(U2fReturnCode::CONDITIONS_NOT_SATISFIED, std::vector<uint8_t>()); break; case U2fApduResponse::Status::SW_NO_ERROR: - callback.Run(ReturnCode::SUCCESS, register_response->data()); + callback.Run(U2fReturnCode::SUCCESS, register_response->data()); break; case U2fApduResponse::Status::SW_WRONG_DATA: - callback.Run(ReturnCode::INVALID_PARAMS, std::vector<uint8_t>()); + callback.Run(U2fReturnCode::INVALID_PARAMS, std::vector<uint8_t>()); break; default: - callback.Run(ReturnCode::FAILURE, std::vector<uint8_t>()); + callback.Run(U2fReturnCode::FAILURE, std::vector<uint8_t>()); break; } } @@ -83,22 +83,21 @@ bool success, std::unique_ptr<U2fApduResponse> sign_response) { if (!success || !sign_response) { - callback.Run(ReturnCode::FAILURE, std::vector<uint8_t>()); + callback.Run(U2fReturnCode::FAILURE, std::vector<uint8_t>()); return; } switch (sign_response->status()) { case U2fApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED: - callback.Run(ReturnCode::CONDITIONS_NOT_SATISFIED, + callback.Run(U2fReturnCode::CONDITIONS_NOT_SATISFIED, std::vector<uint8_t>()); break; case U2fApduResponse::Status::SW_NO_ERROR: - callback.Run(ReturnCode::SUCCESS, sign_response->data()); + callback.Run(U2fReturnCode::SUCCESS, sign_response->data()); break; case U2fApduResponse::Status::SW_WRONG_DATA: - callback.Run(ReturnCode::INVALID_PARAMS, std::vector<uint8_t>()); - break; + case U2fApduResponse::Status::SW_WRONG_LENGTH: default: - callback.Run(ReturnCode::FAILURE, std::vector<uint8_t>()); + callback.Run(U2fReturnCode::INVALID_PARAMS, std::vector<uint8_t>()); break; } }
diff --git a/device/u2f/u2f_device.h b/device/u2f/u2f_device.h index 06b9d2f..77c8133 100644 --- a/device/u2f/u2f_device.h +++ b/device/u2f/u2f_device.h
@@ -10,6 +10,7 @@ #include "base/callback.h" #include "base/memory/weak_ptr.h" #include "u2f_apdu_response.h" +#include "u2f_return_code.h" namespace device { @@ -23,15 +24,9 @@ U2F_V2, UNKNOWN, }; - enum class ReturnCode : uint8_t { - SUCCESS, - FAILURE, - INVALID_PARAMS, - CONDITIONS_NOT_SATISFIED, - }; using MessageCallback = - base::Callback<void(ReturnCode, std::vector<uint8_t>)>; + base::Callback<void(U2fReturnCode, std::vector<uint8_t>)>; using VersionCallback = base::Callback<void(bool success, ProtocolVersion version)>; using DeviceCallback =
diff --git a/device/u2f/u2f_register.cc b/device/u2f/u2f_register.cc new file mode 100644 index 0000000..0f43e260 --- /dev/null +++ b/device/u2f/u2f_register.cc
@@ -0,0 +1,61 @@ +// Copyright 2017 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 "u2f_register.h" + +#include "base/memory/ptr_util.h" + +namespace device { + +U2fRegister::U2fRegister(const std::vector<uint8_t>& challenge_hash, + const std::vector<uint8_t>& app_param, + const ResponseCallback& cb) + : U2fRequest(cb), + challenge_hash_(challenge_hash), + app_param_(app_param), + weak_factory_(this) {} + +U2fRegister::~U2fRegister() {} + +// static +std::unique_ptr<U2fRequest> U2fRegister::TryRegistration( + const std::vector<uint8_t>& challenge_hash, + const std::vector<uint8_t>& app_param, + const ResponseCallback& cb) { + std::unique_ptr<U2fRequest> request = + base::MakeUnique<U2fRegister>(challenge_hash, app_param, cb); + request->Start(); + return request; +} + +void U2fRegister::TryDevice() { + DCHECK(current_device_); + + current_device_->Register( + app_param_, challenge_hash_, + base::Bind(&U2fRegister::OnTryDevice, weak_factory_.GetWeakPtr())); +} + +void U2fRegister::OnTryDevice(U2fReturnCode return_code, + std::vector<uint8_t> response_data) { + switch (return_code) { + case U2fReturnCode::SUCCESS: + state_ = State::COMPLETE; + cb_.Run(return_code, response_data); + break; + case U2fReturnCode::CONDITIONS_NOT_SATISFIED: + // Waiting for user touch, move on and try this device later + state_ = State::IDLE; + Transition(); + break; + default: + state_ = State::IDLE; + // An error has occured, quit trying this device + current_device_ = nullptr; + Transition(); + break; + } +} + +} // namespace device
diff --git a/device/u2f/u2f_register.h b/device/u2f/u2f_register.h new file mode 100644 index 0000000..466127ed --- /dev/null +++ b/device/u2f/u2f_register.h
@@ -0,0 +1,37 @@ +// Copyright 2017 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 DEVICE_U2F_U2F_REGISTER_H_ +#define DEVICE_U2F_U2F_REGISTER_H_ + +#include <vector> + +#include "u2f_request.h" + +namespace device { + +class U2fRegister : public U2fRequest { + public: + U2fRegister(const std::vector<uint8_t>& challenge_hash, + const std::vector<uint8_t>& app_param, + const ResponseCallback& cb); + ~U2fRegister() override; + + static std::unique_ptr<U2fRequest> TryRegistration( + const std::vector<uint8_t>& challenge_hash, + const std::vector<uint8_t>& app_param, + const ResponseCallback& cb); + + private: + void TryDevice() override; + void OnTryDevice(U2fReturnCode, std::vector<uint8_t>); + + std::vector<uint8_t> challenge_hash_; + std::vector<uint8_t> app_param_; + base::WeakPtrFactory<U2fRegister> weak_factory_; +}; + +} // namespace device + +#endif // DEVICE_U2F_U2F_REGISTER_H_
diff --git a/device/u2f/u2f_register_unittest.cc b/device/u2f/u2f_register_unittest.cc new file mode 100644 index 0000000..2d0d2d4 --- /dev/null +++ b/device/u2f/u2f_register_unittest.cc
@@ -0,0 +1,130 @@ +// Copyright 2017 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 <list> + +#include "base/run_loop.h" +#include "base/test/test_io_thread.h" +#include "device/base/mock_device_client.h" +#include "device/hid/mock_hid_service.h" +#include "device/test/test_device_client.h" +#include "mock_u2f_device.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "u2f_register.h" + +namespace device { + +class U2fRegisterTest : public testing::Test { + public: + U2fRegisterTest() : io_thread_(base::TestIOThread::kAutoStart) {} + + void SetUp() override { + MockHidService* hid_service = device_client_.hid_service(); + hid_service->FirstEnumerationComplete(); + } + + protected: + base::MessageLoopForUI message_loop_; + base::TestIOThread io_thread_; + device::MockDeviceClient device_client_; +}; + +class TestRegisterCallback { + public: + TestRegisterCallback() + : callback_(base::Bind(&TestRegisterCallback::ReceivedCallback, + base::Unretained(this))) {} + ~TestRegisterCallback() {} + + void ReceivedCallback(U2fReturnCode status_code, + std::vector<uint8_t> response) { + response_ = std::make_pair(status_code, response); + closure_.Run(); + } + + std::pair<U2fReturnCode, std::vector<uint8_t>>& WaitForCallback() { + closure_ = run_loop_.QuitClosure(); + run_loop_.Run(); + return response_; + } + + const U2fRequest::ResponseCallback& callback() { return callback_; } + + private: + std::pair<U2fReturnCode, std::vector<uint8_t>> response_; + base::Closure closure_; + U2fRequest::ResponseCallback callback_; + base::RunLoop run_loop_; +}; + +TEST_F(U2fRegisterTest, TestRegisterSuccess) { + std::unique_ptr<MockU2fDevice> device(new MockU2fDevice()); + EXPECT_CALL(*device.get(), DeviceTransactPtr(testing::_, testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister)); + EXPECT_CALL(*device.get(), TryWink(testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); + TestRegisterCallback cb; + std::unique_ptr<U2fRequest> request = U2fRegister::TryRegistration( + std::vector<uint8_t>(32), std::vector<uint8_t>(32), cb.callback()); + request->Start(); + request->AddDeviceForTesting(std::move(device)); + std::pair<U2fReturnCode, std::vector<uint8_t>>& response = + cb.WaitForCallback(); + EXPECT_EQ(U2fReturnCode::SUCCESS, response.first); + ASSERT_LT(static_cast<size_t>(0), response.second.size()); + EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kRegister), response.second[0]); +} + +TEST_F(U2fRegisterTest, TestDelayedSuccess) { + std::unique_ptr<MockU2fDevice> device(new MockU2fDevice()); + + // Go through the state machine twice before success + EXPECT_CALL(*device.get(), DeviceTransactPtr(testing::_, testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::NotSatisfied)) + .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister)); + EXPECT_CALL(*device.get(), TryWink(testing::_)) + .Times(2) + .WillRepeatedly(testing::Invoke(MockU2fDevice::WinkDoNothing)); + TestRegisterCallback cb; + + std::unique_ptr<U2fRequest> request = U2fRegister::TryRegistration( + std::vector<uint8_t>(32), std::vector<uint8_t>(32), cb.callback()); + request->Start(); + request->AddDeviceForTesting(std::move(device)); + std::pair<U2fReturnCode, std::vector<uint8_t>>& response = + cb.WaitForCallback(); + EXPECT_EQ(U2fReturnCode::SUCCESS, response.first); + ASSERT_LT(static_cast<size_t>(0), response.second.size()); + EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kRegister), response.second[0]); +} + +TEST_F(U2fRegisterTest, TestMultipleDevices) { + // Second device will have a successful touch + std::unique_ptr<MockU2fDevice> device0(new MockU2fDevice()); + std::unique_ptr<MockU2fDevice> device1(new MockU2fDevice()); + + EXPECT_CALL(*device0.get(), DeviceTransactPtr(testing::_, testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::NotSatisfied)); + // One wink per device + EXPECT_CALL(*device0.get(), TryWink(testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); + EXPECT_CALL(*device1.get(), DeviceTransactPtr(testing::_, testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister)); + EXPECT_CALL(*device1.get(), TryWink(testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); + + TestRegisterCallback cb; + std::unique_ptr<U2fRequest> request = U2fRegister::TryRegistration( + std::vector<uint8_t>(32), std::vector<uint8_t>(32), cb.callback()); + request->Start(); + request->AddDeviceForTesting(std::move(device0)); + request->AddDeviceForTesting(std::move(device1)); + std::pair<U2fReturnCode, std::vector<uint8_t>>& response = + cb.WaitForCallback(); + EXPECT_EQ(U2fReturnCode::SUCCESS, response.first); + ASSERT_LT(static_cast<size_t>(0), response.second.size()); + EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kRegister), response.second[0]); +} + +} // namespace device
diff --git a/device/u2f/u2f_request.cc b/device/u2f/u2f_request.cc new file mode 100644 index 0000000..a010352 --- /dev/null +++ b/device/u2f/u2f_request.cc
@@ -0,0 +1,148 @@ +// Copyright 2017 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 "u2f_request.h" + +#include "base/bind.h" +#include "base/memory/ptr_util.h" +#include "base/threading/thread_task_runner_handle.h" +#include "device/base/device_client.h" +#include "u2f_hid_device.h" + +namespace device { + +U2fRequest::U2fRequest(const ResponseCallback& cb) + : state_(State::INIT), + cb_(cb), + hid_service_observer_(this), + weak_factory_(this) { + filter_.SetUsagePage(0xf1d0); +} + +void U2fRequest::Transition() { + switch (state_) { + case State::IDLE: + IterateDevice(); + if (!current_device_) { + // No devices available + state_ = State::OFF; + break; + } + state_ = State::WINK; + current_device_->TryWink( + base::Bind(&U2fRequest::Transition, weak_factory_.GetWeakPtr())); + break; + case State::WINK: + state_ = State::BUSY; + TryDevice(); + default: + break; + } +} + +void U2fRequest::Start() { + if (state_ == State::INIT) { + state_ = State::BUSY; + Enumerate(); + } +} + +void U2fRequest::Enumerate() { + HidService* hid_service = DeviceClient::Get()->GetHidService(); + DCHECK(hid_service); + hid_service_observer_.Add(hid_service); + hid_service->GetDevices( + base::Bind(&U2fRequest::OnEnumerate, weak_factory_.GetWeakPtr())); +} + +void U2fRequest::OnEnumerate( + const std::vector<scoped_refptr<HidDeviceInfo>>& devices) { + for (auto device_info : devices) { + if (filter_.Matches(device_info)) + devices_.push_back(base::MakeUnique<U2fHidDevice>(device_info)); + } + + state_ = State::IDLE; + Transition(); +} + +void U2fRequest::OnDeviceAdded(scoped_refptr<HidDeviceInfo> device_info) { + // Ignore non-U2F devices + if (!filter_.Matches(device_info)) + return; + + auto device = base::MakeUnique<U2fHidDevice>(device_info); + AddDevice(std::move(device)); +} + +void U2fRequest::OnDeviceRemoved(scoped_refptr<HidDeviceInfo> device_info) { + // Ignore non-U2F devices + if (!filter_.Matches(device_info)) + return; + + auto device = base::MakeUnique<U2fHidDevice>(device_info); + + // Check if the active device was removed + if (current_device_ && current_device_->GetId() == device->GetId()) { + current_device_ = nullptr; + state_ = State::IDLE; + Transition(); + return; + } + + // Remove the device if it exists in either device list + devices_.remove_if([&device](const std::unique_ptr<U2fDevice>& this_device) { + return this_device->GetId() == device->GetId(); + }); + attempted_devices_.remove_if( + [&device](const std::unique_ptr<U2fDevice>& this_device) { + return this_device->GetId() == device->GetId(); + }); +} + +void U2fRequest::IterateDevice() { + // Move active device to attempted device list + if (current_device_) + attempted_devices_.push_back(std::move(current_device_)); + + // If there is an additional device on device list, make it active. + // Otherwise, if all devices have been tried, move attempted devices back to + // the main device list. + if (devices_.size() > 0) { + current_device_ = std::move(devices_.front()); + devices_.pop_front(); + } else if (attempted_devices_.size() > 0) { + devices_ = std::move(attempted_devices_); + // After trying every device, wait 200ms before trying again + delay_callback_.Reset( + base::Bind(&U2fRequest::OnWaitComplete, weak_factory_.GetWeakPtr())); + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, delay_callback_.callback(), + base::TimeDelta::FromMilliseconds(200)); + } +} + +void U2fRequest::OnWaitComplete() { + state_ = State::IDLE; + Transition(); +} + +void U2fRequest::AddDevice(std::unique_ptr<U2fDevice> device) { + devices_.push_back(std::move(device)); + + // Start the state machine if this is the only device + if (state_ == State::OFF) { + state_ = State::IDLE; + delay_callback_.Cancel(); + Transition(); + } +} + +void U2fRequest::AddDeviceForTesting(std::unique_ptr<U2fDevice> device) { + AddDevice(std::move(device)); +} + +U2fRequest::~U2fRequest() {} + +} // namespace device
diff --git a/device/u2f/u2f_request.h b/device/u2f/u2f_request.h new file mode 100644 index 0000000..902a2ca --- /dev/null +++ b/device/u2f/u2f_request.h
@@ -0,0 +1,65 @@ +// Copyright 2017 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 DEVICE_U2F_U2F_REQUEST_H_ +#define DEVICE_U2F_U2F_REQUEST_H_ + +#include "base/cancelable_callback.h" +#include "base/scoped_observer.h" +#include "device/hid/hid_device_filter.h" +#include "device/hid/hid_service.h" +#include "u2f_device.h" + +namespace device { +class U2fRequest : HidService::Observer { + public: + using ResponseCallback = base::Callback<void(U2fReturnCode status_code, + std::vector<uint8_t> response)>; + + U2fRequest(const ResponseCallback& callback); + virtual ~U2fRequest(); + + void Start(); + void AddDeviceForTesting(std::unique_ptr<U2fDevice> device); + + protected: + enum class State { + INIT, + BUSY, + WINK, + IDLE, + OFF, + COMPLETE, + }; + + void Transition(); + virtual void TryDevice() = 0; + + std::unique_ptr<U2fDevice> current_device_; + State state_; + ResponseCallback cb_; + + private: + FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestAddRemoveDevice); + FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestIterateDevice); + FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestBasicMachine); + + void Enumerate(); + void IterateDevice(); + void OnWaitComplete(); + void AddDevice(std::unique_ptr<U2fDevice> device); + void OnDeviceAdded(scoped_refptr<HidDeviceInfo> device_info) override; + void OnDeviceRemoved(scoped_refptr<HidDeviceInfo> device_info) override; + void OnEnumerate(const std::vector<scoped_refptr<HidDeviceInfo>>& devices); + + std::list<std::unique_ptr<U2fDevice>> devices_; + std::list<std::unique_ptr<U2fDevice>> attempted_devices_; + base::CancelableClosure delay_callback_; + HidDeviceFilter filter_; + ScopedObserver<HidService, HidService::Observer> hid_service_observer_; + base::WeakPtrFactory<U2fRequest> weak_factory_; +}; +} // namespace device + +#endif // DEVICE_U2F_U2F_REQUEST_H_
diff --git a/device/u2f/u2f_request_unittest.cc b/device/u2f/u2f_request_unittest.cc new file mode 100644 index 0000000..53c2130a --- /dev/null +++ b/device/u2f/u2f_request_unittest.cc
@@ -0,0 +1,159 @@ +// Copyright 2017 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 <list> + +#include "base/memory/ptr_util.h" +#include "base/run_loop.h" +#include "base/test/test_io_thread.h" +#include "device/base/mock_device_client.h" +#include "device/hid/mock_hid_service.h" +#include "device/test/test_device_client.h" +#include "device/test/usb_test_gadget.h" +#include "device/u2f/u2f_hid_device.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "u2f_request.h" + +namespace device { +namespace { +#if defined(OS_MACOSX) +const uint64_t kTestDeviceId0 = 42; +const uint64_t kTestDeviceId1 = 43; +const uint64_t kTestDeviceId2 = 44; +#else +const char* kTestDeviceId0 = "device0"; +const char* kTestDeviceId1 = "device1"; +const char* kTestDeviceId2 = "device2"; +#endif + +class FakeU2fRequest : public U2fRequest { + public: + FakeU2fRequest(const ResponseCallback& cb) : U2fRequest(cb) {} + ~FakeU2fRequest() override {} + + void TryDevice() override { + cb_.Run(U2fReturnCode::SUCCESS, std::vector<uint8_t>()); + } +}; +} // namespace + +class TestResponseCallback { + public: + TestResponseCallback() + : callback_(base::Bind(&TestResponseCallback::ReceivedCallback, + base::Unretained(this))) {} + ~TestResponseCallback() {} + + void ReceivedCallback(U2fReturnCode status, std::vector<uint8_t> data) { + closure_.Run(); + } + + void WaitForCallback() { + closure_ = run_loop_.QuitClosure(); + run_loop_.Run(); + } + + U2fRequest::ResponseCallback& callback() { return callback_; } + + private: + base::Closure closure_; + U2fRequest::ResponseCallback callback_; + base::RunLoop run_loop_; +}; + +class U2fRequestTest : public testing::Test { + public: + U2fRequestTest() : io_thread_(base::TestIOThread::kAutoStart) {} + + protected: + base::MessageLoopForUI message_loop_; + base::TestIOThread io_thread_; + device::MockDeviceClient device_client_; +}; + +TEST_F(U2fRequestTest, TestAddRemoveDevice) { + MockHidService* hid_service = device_client_.hid_service(); + HidCollectionInfo c_info; + hid_service->FirstEnumerationComplete(); + + TestResponseCallback cb; + FakeU2fRequest request(cb.callback()); + request.Enumerate(); + EXPECT_EQ(static_cast<size_t>(0), request.devices_.size()); + + // Add one U2F device + c_info.usage = HidUsageAndPage(1, static_cast<HidUsageAndPage::Page>(0xf1d0)); + scoped_refptr<HidDeviceInfo> u2f_device = make_scoped_refptr( + new HidDeviceInfo(kTestDeviceId0, 0, 0, "Test Fido Device", "123FIDO", + kHIDBusTypeUSB, c_info, 64, 64, 0)); + hid_service->AddDevice(u2f_device); + EXPECT_EQ(static_cast<size_t>(1), request.devices_.size()); + + // Add one non-U2F device. Verify that it is not added to our device list. + scoped_refptr<HidDeviceInfo> other_device = make_scoped_refptr( + new HidDeviceInfo(kTestDeviceId2, 0, 0, "Other Device", "OtherDevice", + kHIDBusTypeUSB, std::vector<uint8_t>())); + hid_service->AddDevice(other_device); + EXPECT_EQ(static_cast<size_t>(1), request.devices_.size()); + + // Remove the non-U2F device and verify that device list was unchanged. + hid_service->RemoveDevice(kTestDeviceId2); + EXPECT_EQ(static_cast<size_t>(1), request.devices_.size()); + + // Remove the U2F device and verify that device list is empty. + hid_service->RemoveDevice(kTestDeviceId0); + EXPECT_EQ(static_cast<size_t>(0), request.devices_.size()); +} + +TEST_F(U2fRequestTest, TestIterateDevice) { + TestResponseCallback cb; + FakeU2fRequest request(cb.callback()); + HidCollectionInfo c_info; + // Add one U2F device and one non-U2f device + c_info.usage = HidUsageAndPage(1, static_cast<HidUsageAndPage::Page>(0xf1d0)); + scoped_refptr<HidDeviceInfo> device0 = make_scoped_refptr( + new HidDeviceInfo(kTestDeviceId0, 0, 0, "Test Fido Device", "123FIDO", + kHIDBusTypeUSB, c_info, 64, 64, 0)); + request.devices_.push_back(base::MakeUnique<U2fHidDevice>(device0)); + scoped_refptr<HidDeviceInfo> device1 = make_scoped_refptr( + new HidDeviceInfo(kTestDeviceId1, 0, 0, "Test Fido Device", "123FIDO", + kHIDBusTypeUSB, c_info, 64, 64, 0)); + request.devices_.push_back(base::MakeUnique<U2fHidDevice>(device1)); + + // Move first device to current + request.IterateDevice(); + ASSERT_NE(nullptr, request.current_device_); + EXPECT_EQ(static_cast<size_t>(1), request.devices_.size()); + + // Move second device to current, first to attempted + request.IterateDevice(); + ASSERT_NE(nullptr, request.current_device_); + EXPECT_EQ(static_cast<size_t>(1), request.attempted_devices_.size()); + + // Move second device from current to attempted, move attempted to devices as + // all devices have been attempted + request.IterateDevice(); + ASSERT_EQ(nullptr, request.current_device_); + EXPECT_EQ(static_cast<size_t>(2), request.devices_.size()); + EXPECT_EQ(static_cast<size_t>(0), request.attempted_devices_.size()); +} + +TEST_F(U2fRequestTest, TestBasicMachine) { + MockHidService* hid_service = device_client_.hid_service(); + hid_service->FirstEnumerationComplete(); + TestResponseCallback cb; + FakeU2fRequest request(cb.callback()); + request.Start(); + // Add one U2F device + HidCollectionInfo c_info; + c_info.usage = HidUsageAndPage(1, static_cast<HidUsageAndPage::Page>(0xf1d0)); + scoped_refptr<HidDeviceInfo> u2f_device = make_scoped_refptr( + new HidDeviceInfo(kTestDeviceId0, 0, 0, "Test Fido Device", "123FIDO", + kHIDBusTypeUSB, c_info, 64, 64, 0)); + hid_service->AddDevice(u2f_device); + cb.WaitForCallback(); + EXPECT_EQ(U2fRequest::State::BUSY, request.state_); +} + +} // namespace device
diff --git a/device/u2f/u2f_return_code.h b/device/u2f/u2f_return_code.h new file mode 100644 index 0000000..4aa0b42 --- /dev/null +++ b/device/u2f/u2f_return_code.h
@@ -0,0 +1,19 @@ +// Copyright 2017 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 DEVICE_U2F_U2F_RETURN_CODE_H_ +#define DEVICE_U2F_U2F_RETURN_CODE_H_ + +namespace device { + +enum class U2fReturnCode : uint8_t { + SUCCESS, + FAILURE, + INVALID_PARAMS, + CONDITIONS_NOT_SATISFIED, +}; + +} // namespace device + +#endif // DEVICE_U2F_U2F_RETURN_CODE_H_
diff --git a/device/u2f/u2f_sign.cc b/device/u2f/u2f_sign.cc new file mode 100644 index 0000000..d537c48 --- /dev/null +++ b/device/u2f/u2f_sign.cc
@@ -0,0 +1,92 @@ +// Copyright 2017 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 "u2f_sign.h" + +#include "base/memory/ptr_util.h" + +namespace device { + +U2fSign::U2fSign(const std::vector<std::vector<uint8_t>>& registered_keys, + const std::vector<uint8_t>& challenge_hash, + const std::vector<uint8_t>& app_param, + const ResponseCallback& cb) + : U2fRequest(cb), + registered_keys_(registered_keys), + challenge_hash_(challenge_hash), + app_param_(app_param), + weak_factory_(this) {} + +U2fSign::~U2fSign() {} + +// static +std::unique_ptr<U2fRequest> U2fSign::TrySign( + const std::vector<std::vector<uint8_t>>& registered_keys, + const std::vector<uint8_t>& challenge_hash, + const std::vector<uint8_t>& app_param, + const ResponseCallback& cb) { + std::unique_ptr<U2fRequest> request = + base::MakeUnique<U2fSign>(registered_keys, challenge_hash, app_param, cb); + request->Start(); + return request; +} + +void U2fSign::TryDevice() { + DCHECK(current_device_); + + if (registered_keys_.size() == 0) { + // Send registration (Fake enroll) if no keys were provided + current_device_->Register( + kBogusAppParam, kBogusChallenge, + base::Bind(&U2fSign::OnTryDevice, weak_factory_.GetWeakPtr(), + registered_keys_.cbegin())); + return; + } + // Try signing current device with the first registered key + auto it = registered_keys_.cbegin(); + current_device_->Sign( + app_param_, challenge_hash_, *it, + base::Bind(&U2fSign::OnTryDevice, weak_factory_.GetWeakPtr(), it)); +} + +void U2fSign::OnTryDevice(std::vector<std::vector<uint8_t>>::const_iterator it, + U2fReturnCode return_code, + std::vector<uint8_t> response_data) { + switch (return_code) { + case U2fReturnCode::SUCCESS: + state_ = State::COMPLETE; + cb_.Run(return_code, response_data); + break; + case U2fReturnCode::CONDITIONS_NOT_SATISFIED: { + // Key handle is accepted by this device, but waiting on user touch. Move + // on and try this device again later. + state_ = State::IDLE; + Transition(); + break; + } + case U2fReturnCode::INVALID_PARAMS: + if (++it != registered_keys_.end()) { + // Key is not for this device. Try signing with the next key. + current_device_->Sign( + app_param_, challenge_hash_, *it, + base::Bind(&U2fSign::OnTryDevice, weak_factory_.GetWeakPtr(), it)); + } else { + // No provided key was accepted by this device. Send registration + // (Fake enroll) request to device. + current_device_->Register( + kBogusAppParam, kBogusChallenge, + base::Bind(&U2fSign::OnTryDevice, weak_factory_.GetWeakPtr(), + registered_keys_.cbegin())); + } + break; + default: + // Some sort of failure occured. Abandon this device and move on. + state_ = State::IDLE; + current_device_ = nullptr; + Transition(); + break; + } +} + +} // namespace device
diff --git a/device/u2f/u2f_sign.h b/device/u2f/u2f_sign.h new file mode 100644 index 0000000..e924137 --- /dev/null +++ b/device/u2f/u2f_sign.h
@@ -0,0 +1,51 @@ +// Copyright 2017 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 DEVICE_U2F_U2F_SIGN_H_ +#define DEVICE_U2F_U2F_SIGN_H_ + +#include <vector> + +#include "u2f_request.h" + +namespace device { + +class U2fSign : public U2fRequest { + public: + U2fSign(const std::vector<std::vector<uint8_t>>& registered_keys, + const std::vector<uint8_t>& challenge_hash, + const std::vector<uint8_t>& app_param, + const ResponseCallback& cb); + ~U2fSign() override; + + static std::unique_ptr<U2fRequest> TrySign( + const std::vector<std::vector<uint8_t>>& registered_keys, + const std::vector<uint8_t>& challenge_hash, + const std::vector<uint8_t>& app_param, + const ResponseCallback& cb); + + private: + void TryDevice() override; + void OnTryDevice(std::vector<std::vector<uint8_t>>::const_iterator, + U2fReturnCode, + std::vector<uint8_t>); + + const std::vector<std::vector<uint8_t>> registered_keys_; + std::vector<uint8_t> challenge_hash_; + std::vector<uint8_t> app_param_; + const std::vector<uint8_t> kBogusAppParam = { + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}; + const std::vector<uint8_t> kBogusChallenge = { + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42}; + + base::WeakPtrFactory<U2fSign> weak_factory_; +}; + +} // namespace device + +#endif // DEVICE_U2F_U2F_SIGN_H_
diff --git a/device/u2f/u2f_sign_unittest.cc b/device/u2f/u2f_sign_unittest.cc new file mode 100644 index 0000000..8c846217 --- /dev/null +++ b/device/u2f/u2f_sign_unittest.cc
@@ -0,0 +1,215 @@ +// Copyright 2017 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 <list> + +#include "base/run_loop.h" +#include "base/test/test_io_thread.h" +#include "device/base/mock_device_client.h" +#include "device/hid/mock_hid_service.h" +#include "device/test/test_device_client.h" +#include "mock_u2f_device.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "u2f_sign.h" + +namespace device { +class U2fSignTest : public testing::Test { + public: + U2fSignTest() : io_thread_(base::TestIOThread::kAutoStart) {} + + void SetUp() override { + MockHidService* hid_service = device_client_.hid_service(); + hid_service->FirstEnumerationComplete(); + } + + protected: + base::MessageLoopForUI message_loop_; + base::TestIOThread io_thread_; + device::MockDeviceClient device_client_; +}; + +class TestSignCallback { + public: + TestSignCallback() + : callback_(base::Bind(&TestSignCallback::ReceivedCallback, + base::Unretained(this))) {} + ~TestSignCallback() {} + + void ReceivedCallback(U2fReturnCode status_code, + std::vector<uint8_t> response) { + response_ = std::make_pair(status_code, response); + closure_.Run(); + } + + std::pair<U2fReturnCode, std::vector<uint8_t>>& WaitForCallback() { + closure_ = run_loop_.QuitClosure(); + run_loop_.Run(); + return response_; + } + + const U2fRequest::ResponseCallback& callback() { return callback_; } + + private: + std::pair<U2fReturnCode, std::vector<uint8_t>> response_; + base::Closure closure_; + U2fRequest::ResponseCallback callback_; + base::RunLoop run_loop_; +}; + +TEST_F(U2fSignTest, TestSignSuccess) { + std::vector<uint8_t> key(32, 0xA); + std::vector<std::vector<uint8_t>> handles = {key}; + std::unique_ptr<MockU2fDevice> device(new MockU2fDevice()); + EXPECT_CALL(*device.get(), DeviceTransactPtr(testing::_, testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::NoErrorSign)); + EXPECT_CALL(*device.get(), TryWink(testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); + TestSignCallback cb; + std::unique_ptr<U2fRequest> request = + U2fSign::TrySign(handles, std::vector<uint8_t>(32), + std::vector<uint8_t>(32), cb.callback()); + request->Start(); + request->AddDeviceForTesting(std::move(device)); + std::pair<U2fReturnCode, std::vector<uint8_t>>& response = + cb.WaitForCallback(); + EXPECT_EQ(U2fReturnCode::SUCCESS, response.first); + // Correct key was sent so a sign response is expected + ASSERT_LT(static_cast<size_t>(0), response.second.size()); + EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kSign), response.second[0]); +} + +TEST_F(U2fSignTest, TestDelayedSuccess) { + std::vector<uint8_t> key(32, 0xA); + std::vector<std::vector<uint8_t>> handles = {key}; + std::unique_ptr<MockU2fDevice> device(new MockU2fDevice()); + + // Go through the state machine twice before success + EXPECT_CALL(*device.get(), DeviceTransactPtr(testing::_, testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::NotSatisfied)) + .WillOnce(testing::Invoke(MockU2fDevice::NoErrorSign)); + EXPECT_CALL(*device.get(), TryWink(testing::_)) + .Times(2) + .WillRepeatedly(testing::Invoke(MockU2fDevice::WinkDoNothing)); + TestSignCallback cb; + + std::unique_ptr<U2fRequest> request = + U2fSign::TrySign(handles, std::vector<uint8_t>(32), + std::vector<uint8_t>(32), cb.callback()); + request->Start(); + request->AddDeviceForTesting(std::move(device)); + std::pair<U2fReturnCode, std::vector<uint8_t>>& response = + cb.WaitForCallback(); + EXPECT_EQ(U2fReturnCode::SUCCESS, response.first); + // Correct key was sent so a sign response is expected + ASSERT_LT(static_cast<size_t>(0), response.second.size()); + EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kSign), response.second[0]); +} + +TEST_F(U2fSignTest, TestMultipleHandles) { + std::vector<uint8_t> key(32, 0xA); + std::vector<uint8_t> wrong_key0(32, 0xB); + std::vector<uint8_t> wrong_key1(32, 0xC); + std::vector<uint8_t> wrong_key2(32, 0xD); + // Put wrong key first to ensure that it will be tested before the correct key + std::vector<std::vector<uint8_t>> handles = {wrong_key0, wrong_key1, + wrong_key2, key}; + std::unique_ptr<MockU2fDevice> device(new MockU2fDevice()); + + // Wrong key would respond with SW_WRONG_DATA + EXPECT_CALL(*device.get(), DeviceTransactPtr(testing::_, testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) + .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) + .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) + .WillOnce(testing::Invoke(MockU2fDevice::NoErrorSign)); + // Only one wink expected per device + EXPECT_CALL(*device.get(), TryWink(testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); + + TestSignCallback cb; + std::unique_ptr<U2fRequest> request = + U2fSign::TrySign(handles, std::vector<uint8_t>(32), + std::vector<uint8_t>(32), cb.callback()); + request->Start(); + request->AddDeviceForTesting(std::move(device)); + std::pair<U2fReturnCode, std::vector<uint8_t>>& response = + cb.WaitForCallback(); + EXPECT_EQ(U2fReturnCode::SUCCESS, response.first); + // Correct key was sent so a sign response is expected + ASSERT_LT(static_cast<size_t>(0), response.second.size()); + EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kSign), response.second[0]); +} + +TEST_F(U2fSignTest, TestMultipleDevices) { + std::vector<uint8_t> key0(32, 0xA); + std::vector<uint8_t> key1(32, 0xB); + // Second device will have a successful touch + std::vector<std::vector<uint8_t>> handles = {key0, key1}; + std::unique_ptr<MockU2fDevice> device0(new MockU2fDevice()); + std::unique_ptr<MockU2fDevice> device1(new MockU2fDevice()); + + EXPECT_CALL(*device0.get(), DeviceTransactPtr(testing::_, testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) + .WillOnce(testing::Invoke(MockU2fDevice::NotSatisfied)); + // One wink per device + EXPECT_CALL(*device0.get(), TryWink(testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); + EXPECT_CALL(*device1.get(), DeviceTransactPtr(testing::_, testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::NoErrorSign)); + EXPECT_CALL(*device1.get(), TryWink(testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); + + TestSignCallback cb; + std::unique_ptr<U2fRequest> request = + U2fSign::TrySign(handles, std::vector<uint8_t>(32), + std::vector<uint8_t>(32), cb.callback()); + request->Start(); + request->AddDeviceForTesting(std::move(device0)); + request->AddDeviceForTesting(std::move(device1)); + std::pair<U2fReturnCode, std::vector<uint8_t>>& response = + cb.WaitForCallback(); + EXPECT_EQ(U2fReturnCode::SUCCESS, response.first); + // Correct key was sent so a sign response is expected + ASSERT_LT(static_cast<size_t>(0), response.second.size()); + EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kSign), response.second[0]); +} + +TEST_F(U2fSignTest, TestFakeEnroll) { + std::vector<uint8_t> key0(32, 0xA); + std::vector<uint8_t> key1(32, 0xB); + // Second device will be have a successful touch + std::vector<std::vector<uint8_t>> handles = {key0, key1}; + std::unique_ptr<MockU2fDevice> device0(new MockU2fDevice()); + std::unique_ptr<MockU2fDevice> device1(new MockU2fDevice()); + + EXPECT_CALL(*device0.get(), DeviceTransactPtr(testing::_, testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) + .WillOnce(testing::Invoke(MockU2fDevice::NotSatisfied)); + // One wink per device + EXPECT_CALL(*device0.get(), TryWink(testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); + // Both keys will be tried, when both fail, register is tried on that device + EXPECT_CALL(*device1.get(), DeviceTransactPtr(testing::_, testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) + .WillOnce(testing::Invoke(MockU2fDevice::WrongData)) + .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister)); + EXPECT_CALL(*device1.get(), TryWink(testing::_)) + .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing)); + + TestSignCallback cb; + std::unique_ptr<U2fRequest> request = + U2fSign::TrySign(handles, std::vector<uint8_t>(32), + std::vector<uint8_t>(32), cb.callback()); + request->Start(); + request->AddDeviceForTesting(std::move(device0)); + request->AddDeviceForTesting(std::move(device1)); + std::pair<U2fReturnCode, std::vector<uint8_t>>& response = + cb.WaitForCallback(); + EXPECT_EQ(U2fReturnCode::SUCCESS, response.first); + // Device that responded had no correct keys, so a registration response is + // expected + ASSERT_LT(static_cast<size_t>(0), response.second.size()); + EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kRegister), response.second[0]); +} + +} // namespace device
diff --git a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_image.txt b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_image.txt index 2ba1e39..0d6a4c4 100644 --- a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_image.txt +++ b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_image.txt
@@ -32,10 +32,10 @@ New Procedures and Functions - GLuint CreateImageCHROMIUM(ClientBuffer buffer, - GLsizei width, - GLsizei height, - GLenum internalformat) + GLuint CreateImageCHROMIUM(ClientBuffer buffer, + GLsizei width, + GLsizei height, + GLenum internalformat) Create an image from <buffer> with width equal to <width> and height equal to <height> and format equal to <internalformat>. @@ -50,12 +50,42 @@ COMPRESSED_RGB_S3TC_DXT1_EXT, COMPRESSED_RGBA_S3TC_DXT5_EXT or ETC1_RGB8_OES. - void DestroyImageCHROMIUM(GLuint image_id) + void DestroyImageCHROMIUM(GLuint image_id) Frees the image previously created by a call to CreateImageCHROMIUM. INVALID_OPERATION is generated if <image_id> is not a valid image id. + void BindTexImage2DCHROMIUM(GLenum target, GLint image_id) + + Binds the texture object currently bound to <target> to the image + <image_id> previously created by a call to CreateImageCHROMIUM. + + INVALID_OPERATION is generated if no texture is bound to <target>. + + INVALID_OPERATION is generated if <image_id> is not a valid image id. + + void BindTexImage2DWithInternalformatCHROMIUM(GLenum target, + GLenum internalformat, + GLint image_id) + + Behaves exactly like BindTexImage2DCHROMIUM, but forces the + texture to use the specified <internalformat> rather than the + default one. This function is provided solely as a workaround for + driver bugs on some platforms. BindTexImage2DCHROMIUM should be + used by almost all users. + + void ReleaseTexImage2DCHROMIUM(GLenum target, GLint image_id) + + Unbinds the texture object bound to <target> from the image + <image_id> previously created by a call to CreateImageCHROMIUM. If + the texture is not currently bound to the image, has no effect, + though may still generate errors. + + INVALID_OPERATION is generated if no texture is bound to <target>. + + INVALID_OPERATION is generated if <image_id> is not a valid image id. + Dependencies on EXT_texture_format_BGRA8888 If EXT_texture_format_BGRA8888 is not supported:
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h index b5114bd..8820d0f 100644 --- a/gpu/GLES2/gl2chromium_autogen.h +++ b/gpu/GLES2/gl2chromium_autogen.h
@@ -310,6 +310,8 @@ GLES2_GET_FUN(CreateAndConsumeTextureCHROMIUM) #define glBindUniformLocationCHROMIUM GLES2_GET_FUN(BindUniformLocationCHROMIUM) #define glBindTexImage2DCHROMIUM GLES2_GET_FUN(BindTexImage2DCHROMIUM) +#define glBindTexImage2DWithInternalformatCHROMIUM \ + GLES2_GET_FUN(BindTexImage2DWithInternalformatCHROMIUM) #define glReleaseTexImage2DCHROMIUM GLES2_GET_FUN(ReleaseTexImage2DCHROMIUM) #define glTraceBeginCHROMIUM GLES2_GET_FUN(TraceBeginCHROMIUM) #define glTraceEndCHROMIUM GLES2_GET_FUN(TraceEndCHROMIUM)
diff --git a/gpu/GLES2/gl2extchromium.h b/gpu/GLES2/gl2extchromium.h index e38949b..8c1f3df1 100644 --- a/gpu/GLES2/gl2extchromium.h +++ b/gpu/GLES2/gl2extchromium.h
@@ -84,6 +84,14 @@ GLsizei height, GLenum internalformat); GL_APICALL void GL_APIENTRY glDestroyImageCHROMIUM(GLuint image_id); +GL_APICALL void GL_APIENTRY glBindTexImage2DCHROMIUM(GLenum target, + GLint imageId); +GL_APICALL void GL_APIENTRY +glBindTexImage2DWithInternalformatCHROMIUM(GLenum target, + GLenum internalformat, + GLint imageId); +GL_APICALL void GL_APIENTRY glReleaseTexImage2DCHROMIUM(GLenum target, + GLint imageId); #endif typedef GLuint(GL_APIENTRYP PFNGLCREATEIMAGECHROMIUMPROC)( ClientBuffer buffer, @@ -92,6 +100,14 @@ GLenum internalformat); typedef void ( GL_APIENTRYP PFNGLDESTROYIMAGECHROMIUMPROC)(GLuint image_id); +typedef void(GL_APIENTRYP PFNGLBINDTEXIMAGE2DCHROMIUMPROC)(GLenum target, + GLint imageId); +typedef void(GL_APIENTRYP PFNGLBINDTEXIMAGE2DWITHINTERNALFORMATCHROMIUMPROC)( + GLenum target, + GLenum internalformat, + GLint imageId); +typedef void(GL_APIENTRYP PFNGLRELEASETEXIMAGE2DCHROMIUMPROC)(GLenum target, + GLint imageId); #endif /* GL_CHROMIUM_image */ #ifndef GL_RGB_YCRCB_420_CHROMIUM @@ -169,21 +185,6 @@ #endif #endif /* GL_CHROMIUM_get_error_query */ -/* GL_CHROMIUM_texture_from_image */ -#ifndef GL_CHROMIUM_texture_from_image -#define GL_CHROMIUM_texture_from_image 1 -#ifdef GL_GLEXT_PROTOTYPES -GL_APICALL void GL_APIENTRY glBindTexImage2DCHROMIUM( - GLenum target, GLint imageId); -GL_APICALL void GL_APIENTRY glReleaseTexImage2DCHROMIUM( - GLenum target, GLint imageId); -#endif -typedef void (GL_APIENTRYP PFNGLBINDTEXIMAGE2DCHROMIUMPROC) ( - GLenum target, GLint imageId); -typedef void (GL_APIENTRYP PFNGLRELEASETEXIMAGE2DCHROMIUMPROC) ( - GLenum target, GLint imageId); -#endif /* GL_CHROMIUM_texture_from_image */ - /* GL_CHROMIUM_post_sub_buffer */ #ifndef GL_CHROMIUM_post_sub_buffer #define GL_CHROMIUM_post_sub_buffer 1
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py index 479b2d6..01593a3 100755 --- a/gpu/command_buffer/build_gles2_cmd_buffer.py +++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -4278,6 +4278,11 @@ 'unit_test': False, 'extension': "CHROMIUM_image", }, + 'BindTexImage2DWithInternalformatCHROMIUM': { + 'decoder_func': 'DoBindTexImage2DWithInternalformatCHROMIUM', + 'unit_test': False, + 'extension': "CHROMIUM_image", + }, 'ReleaseTexImage2DCHROMIUM': { 'decoder_func': 'DoReleaseTexImage2DCHROMIUM', 'unit_test': False,
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h index b789a6b..8640ab7 100644 --- a/gpu/command_buffer/client/gles2_c_lib_autogen.h +++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -1415,6 +1415,13 @@ void GL_APIENTRY GLES2BindTexImage2DCHROMIUM(GLenum target, GLint imageId) { gles2::GetGLContext()->BindTexImage2DCHROMIUM(target, imageId); } +void GL_APIENTRY +GLES2BindTexImage2DWithInternalformatCHROMIUM(GLenum target, + GLenum internalformat, + GLint imageId) { + gles2::GetGLContext()->BindTexImage2DWithInternalformatCHROMIUM( + target, internalformat, imageId); +} void GL_APIENTRY GLES2ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) { gles2::GetGLContext()->ReleaseTexImage2DCHROMIUM(target, imageId); } @@ -2819,6 +2826,11 @@ reinterpret_cast<GLES2FunctionPointer>(glBindTexImage2DCHROMIUM), }, { + "glBindTexImage2DWithInternalformatCHROMIUM", + reinterpret_cast<GLES2FunctionPointer>( + glBindTexImage2DWithInternalformatCHROMIUM), + }, + { "glReleaseTexImage2DCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(glReleaseTexImage2DCHROMIUM), },
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h index 834b474..2573fd9 100644 --- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h +++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -2651,6 +2651,16 @@ } } +void BindTexImage2DWithInternalformatCHROMIUM(GLenum target, + GLenum internalformat, + GLint imageId) { + gles2::cmds::BindTexImage2DWithInternalformatCHROMIUM* c = + GetCmdSpace<gles2::cmds::BindTexImage2DWithInternalformatCHROMIUM>(); + if (c) { + c->Init(target, internalformat, imageId); + } +} + void ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) { gles2::cmds::ReleaseTexImage2DCHROMIUM* c = GetCmdSpace<gles2::cmds::ReleaseTexImage2DCHROMIUM>();
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h index ee52301..d605f61 100644 --- a/gpu/command_buffer/client/gles2_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -996,6 +996,10 @@ void BindTexImage2DCHROMIUM(GLenum target, GLint imageId) override; +void BindTexImage2DWithInternalformatCHROMIUM(GLenum target, + GLenum internalformat, + GLint imageId) override; + void ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) override; void TraceBeginCHROMIUM(const char* category_name,
diff --git a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h index db5d667..a5a5091 100644 --- a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
@@ -3195,6 +3195,21 @@ CheckGLError(); } +void GLES2Implementation::BindTexImage2DWithInternalformatCHROMIUM( + GLenum target, + GLenum internalformat, + GLint imageId) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << GetLogPrefix() + << "] glBindTexImage2DWithInternalformatCHROMIUM(" + << GLES2Util::GetStringTextureBindTarget(target) << ", " + << GLES2Util::GetStringEnum(internalformat) << ", " + << imageId << ")"); + helper_->BindTexImage2DWithInternalformatCHROMIUM(target, internalformat, + imageId); + CheckGLError(); +} + void GLES2Implementation::ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) { GPU_CLIENT_SINGLE_THREAD_CHECK();
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h index cee92feee..55e10eae 100644 --- a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
@@ -2781,6 +2781,17 @@ EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } +TEST_F(GLES2ImplementationTest, BindTexImage2DWithInternalformatCHROMIUM) { + struct Cmds { + cmds::BindTexImage2DWithInternalformatCHROMIUM cmd; + }; + Cmds expected; + expected.cmd.Init(GL_TEXTURE_2D, 2, 3); + + gl_->BindTexImage2DWithInternalformatCHROMIUM(GL_TEXTURE_2D, 2, 3); + EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); +} + TEST_F(GLES2ImplementationTest, ReleaseTexImage2DCHROMIUM) { struct Cmds { cmds::ReleaseTexImage2DCHROMIUM cmd;
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h index 65724af..07a37155 100644 --- a/gpu/command_buffer/client/gles2_interface_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -732,6 +732,9 @@ GLint location, const char* name) = 0; virtual void BindTexImage2DCHROMIUM(GLenum target, GLint imageId) = 0; +virtual void BindTexImage2DWithInternalformatCHROMIUM(GLenum target, + GLenum internalformat, + GLint imageId) = 0; virtual void ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) = 0; virtual void TraceBeginCHROMIUM(const char* category_name, const char* trace_name) = 0;
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h index 54dabbc..bee6141 100644 --- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -710,6 +710,9 @@ GLint location, const char* name) override; void BindTexImage2DCHROMIUM(GLenum target, GLint imageId) override; +void BindTexImage2DWithInternalformatCHROMIUM(GLenum target, + GLenum internalformat, + GLint imageId) override; void ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) override; void TraceBeginCHROMIUM(const char* category_name, const char* trace_name) override;
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h index 5073353b..fd49260 100644 --- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -965,6 +965,10 @@ const char* /* name */) {} void GLES2InterfaceStub::BindTexImage2DCHROMIUM(GLenum /* target */, GLint /* imageId */) {} +void GLES2InterfaceStub::BindTexImage2DWithInternalformatCHROMIUM( + GLenum /* target */, + GLenum /* internalformat */, + GLint /* imageId */) {} void GLES2InterfaceStub::ReleaseTexImage2DCHROMIUM(GLenum /* target */, GLint /* imageId */) {} void GLES2InterfaceStub::TraceBeginCHROMIUM(const char* /* category_name */,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h index 783d7cc..911c4fd0 100644 --- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -710,6 +710,9 @@ GLint location, const char* name) override; void BindTexImage2DCHROMIUM(GLenum target, GLint imageId) override; +void BindTexImage2DWithInternalformatCHROMIUM(GLenum target, + GLenum internalformat, + GLint imageId) override; void ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) override; void TraceBeginCHROMIUM(const char* category_name, const char* trace_name) override;
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h index 01db22e..4b096cd 100644 --- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -2054,6 +2054,16 @@ gl_->BindTexImage2DCHROMIUM(target, imageId); } +void GLES2TraceImplementation::BindTexImage2DWithInternalformatCHROMIUM( + GLenum target, + GLenum internalformat, + GLint imageId) { + TRACE_EVENT_BINARY_EFFICIENT0( + "gpu", "GLES2Trace::BindTexImage2DWithInternalformatCHROMIUM"); + gl_->BindTexImage2DWithInternalformatCHROMIUM(target, internalformat, + imageId); +} + void GLES2TraceImplementation::ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) { TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::ReleaseTexImage2DCHROMIUM");
diff --git a/gpu/command_buffer/cmd_buffer_functions.txt b/gpu/command_buffer/cmd_buffer_functions.txt index a1055e8..2b87eff 100644 --- a/gpu/command_buffer/cmd_buffer_functions.txt +++ b/gpu/command_buffer/cmd_buffer_functions.txt
@@ -293,6 +293,7 @@ GL_APICALL void GL_APIENTRY glCreateAndConsumeTextureINTERNAL (GLenumTextureBindTarget target, GLuint texture, const GLbyte* mailbox); GL_APICALL void GL_APIENTRY glBindUniformLocationCHROMIUM (GLidProgram program, GLint location, const char* name); GL_APICALL void GL_APIENTRY glBindTexImage2DCHROMIUM (GLenumTextureBindTarget target, GLint imageId); +GL_APICALL void GL_APIENTRY glBindTexImage2DWithInternalformatCHROMIUM (GLenumTextureBindTarget target, GLenum internalformat, GLint imageId); GL_APICALL void GL_APIENTRY glReleaseTexImage2DCHROMIUM (GLenumTextureBindTarget target, GLint imageId); GL_APICALL void GL_APIENTRY glTraceBeginCHROMIUM (const char* category_name, const char* trace_name); GL_APICALL void GL_APIENTRY glTraceEndCHROMIUM (void);
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h index 2f1e47f..3ec79a2 100644 --- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -13084,6 +13084,52 @@ static_assert(offsetof(BindTexImage2DCHROMIUM, imageId) == 8, "offset of BindTexImage2DCHROMIUM imageId should be 8"); +struct BindTexImage2DWithInternalformatCHROMIUM { + typedef BindTexImage2DWithInternalformatCHROMIUM ValueType; + static const CommandId kCmdId = kBindTexImage2DWithInternalformatCHROMIUM; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); + + static uint32_t ComputeSize() { + return static_cast<uint32_t>(sizeof(ValueType)); // NOLINT + } + + void SetHeader() { header.SetCmd<ValueType>(); } + + void Init(GLenum _target, GLenum _internalformat, GLint _imageId) { + SetHeader(); + target = _target; + internalformat = _internalformat; + imageId = _imageId; + } + + void* Set(void* cmd, GLenum _target, GLenum _internalformat, GLint _imageId) { + static_cast<ValueType*>(cmd)->Init(_target, _internalformat, _imageId); + return NextCmdAddress<ValueType>(cmd); + } + + gpu::CommandHeader header; + uint32_t target; + uint32_t internalformat; + int32_t imageId; +}; + +static_assert(sizeof(BindTexImage2DWithInternalformatCHROMIUM) == 16, + "size of BindTexImage2DWithInternalformatCHROMIUM should be 16"); +static_assert( + offsetof(BindTexImage2DWithInternalformatCHROMIUM, header) == 0, + "offset of BindTexImage2DWithInternalformatCHROMIUM header should be 0"); +static_assert( + offsetof(BindTexImage2DWithInternalformatCHROMIUM, target) == 4, + "offset of BindTexImage2DWithInternalformatCHROMIUM target should be 4"); +static_assert(offsetof(BindTexImage2DWithInternalformatCHROMIUM, + internalformat) == 8, + "offset of BindTexImage2DWithInternalformatCHROMIUM " + "internalformat should be 8"); +static_assert( + offsetof(BindTexImage2DWithInternalformatCHROMIUM, imageId) == 12, + "offset of BindTexImage2DWithInternalformatCHROMIUM imageId should be 12"); + struct ReleaseTexImage2DCHROMIUM { typedef ReleaseTexImage2DCHROMIUM ValueType; static const CommandId kCmdId = kReleaseTexImage2DCHROMIUM;
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h index 1bf9ced5..d7094ea1 100644 --- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -4434,6 +4434,21 @@ CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd)); } +TEST_F(GLES2FormatTest, BindTexImage2DWithInternalformatCHROMIUM) { + cmds::BindTexImage2DWithInternalformatCHROMIUM& cmd = + *GetBufferAs<cmds::BindTexImage2DWithInternalformatCHROMIUM>(); + void* next_cmd = cmd.Set(&cmd, static_cast<GLenum>(11), + static_cast<GLenum>(12), static_cast<GLint>(13)); + EXPECT_EQ(static_cast<uint32_t>( + cmds::BindTexImage2DWithInternalformatCHROMIUM::kCmdId), + cmd.header.command); + EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); + EXPECT_EQ(static_cast<GLenum>(11), cmd.target); + EXPECT_EQ(static_cast<GLenum>(12), cmd.internalformat); + EXPECT_EQ(static_cast<GLint>(13), cmd.imageId); + CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd)); +} + TEST_F(GLES2FormatTest, ReleaseTexImage2DCHROMIUM) { cmds::ReleaseTexImage2DCHROMIUM& cmd = *GetBufferAs<cmds::ReleaseTexImage2DCHROMIUM>();
diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h index ffef2d5..bd9ccf3 100644 --- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
@@ -281,59 +281,60 @@ OP(CreateAndConsumeTextureINTERNALImmediate) /* 522 */ \ OP(BindUniformLocationCHROMIUMBucket) /* 523 */ \ OP(BindTexImage2DCHROMIUM) /* 524 */ \ - OP(ReleaseTexImage2DCHROMIUM) /* 525 */ \ - OP(TraceBeginCHROMIUM) /* 526 */ \ - OP(TraceEndCHROMIUM) /* 527 */ \ - OP(DiscardFramebufferEXTImmediate) /* 528 */ \ - OP(LoseContextCHROMIUM) /* 529 */ \ - OP(InsertFenceSyncCHROMIUM) /* 530 */ \ - OP(WaitSyncTokenCHROMIUM) /* 531 */ \ - OP(DrawBuffersEXTImmediate) /* 532 */ \ - OP(DiscardBackbufferCHROMIUM) /* 533 */ \ - OP(ScheduleOverlayPlaneCHROMIUM) /* 534 */ \ - OP(ScheduleCALayerSharedStateCHROMIUM) /* 535 */ \ - OP(ScheduleCALayerCHROMIUM) /* 536 */ \ - OP(ScheduleCALayerInUseQueryCHROMIUMImmediate) /* 537 */ \ - OP(CommitOverlayPlanesCHROMIUM) /* 538 */ \ - OP(SwapInterval) /* 539 */ \ - OP(FlushDriverCachesCHROMIUM) /* 540 */ \ - OP(ScheduleDCLayerSharedStateCHROMIUM) /* 541 */ \ - OP(ScheduleDCLayerCHROMIUM) /* 542 */ \ - OP(MatrixLoadfCHROMIUMImmediate) /* 543 */ \ - OP(MatrixLoadIdentityCHROMIUM) /* 544 */ \ - OP(GenPathsCHROMIUM) /* 545 */ \ - OP(DeletePathsCHROMIUM) /* 546 */ \ - OP(IsPathCHROMIUM) /* 547 */ \ - OP(PathCommandsCHROMIUM) /* 548 */ \ - OP(PathParameterfCHROMIUM) /* 549 */ \ - OP(PathParameteriCHROMIUM) /* 550 */ \ - OP(PathStencilFuncCHROMIUM) /* 551 */ \ - OP(StencilFillPathCHROMIUM) /* 552 */ \ - OP(StencilStrokePathCHROMIUM) /* 553 */ \ - OP(CoverFillPathCHROMIUM) /* 554 */ \ - OP(CoverStrokePathCHROMIUM) /* 555 */ \ - OP(StencilThenCoverFillPathCHROMIUM) /* 556 */ \ - OP(StencilThenCoverStrokePathCHROMIUM) /* 557 */ \ - OP(StencilFillPathInstancedCHROMIUM) /* 558 */ \ - OP(StencilStrokePathInstancedCHROMIUM) /* 559 */ \ - OP(CoverFillPathInstancedCHROMIUM) /* 560 */ \ - OP(CoverStrokePathInstancedCHROMIUM) /* 561 */ \ - OP(StencilThenCoverFillPathInstancedCHROMIUM) /* 562 */ \ - OP(StencilThenCoverStrokePathInstancedCHROMIUM) /* 563 */ \ - OP(BindFragmentInputLocationCHROMIUMBucket) /* 564 */ \ - OP(ProgramPathFragmentInputGenCHROMIUM) /* 565 */ \ - OP(GetBufferSubDataAsyncCHROMIUM) /* 566 */ \ - OP(CoverageModulationCHROMIUM) /* 567 */ \ - OP(BlendBarrierKHR) /* 568 */ \ - OP(ApplyScreenSpaceAntialiasingCHROMIUM) /* 569 */ \ - OP(BindFragDataLocationIndexedEXTBucket) /* 570 */ \ - OP(BindFragDataLocationEXTBucket) /* 571 */ \ - OP(GetFragDataIndexEXT) /* 572 */ \ - OP(UniformMatrix4fvStreamTextureMatrixCHROMIUMImmediate) /* 573 */ \ - OP(OverlayPromotionHintCHROMIUM) /* 574 */ \ - OP(SwapBuffersWithBoundsCHROMIUMImmediate) /* 575 */ \ - OP(SetDrawRectangleCHROMIUM) /* 576 */ \ - OP(SetEnableDCLayersCHROMIUM) /* 577 */ + OP(BindTexImage2DWithInternalformatCHROMIUM) /* 525 */ \ + OP(ReleaseTexImage2DCHROMIUM) /* 526 */ \ + OP(TraceBeginCHROMIUM) /* 527 */ \ + OP(TraceEndCHROMIUM) /* 528 */ \ + OP(DiscardFramebufferEXTImmediate) /* 529 */ \ + OP(LoseContextCHROMIUM) /* 530 */ \ + OP(InsertFenceSyncCHROMIUM) /* 531 */ \ + OP(WaitSyncTokenCHROMIUM) /* 532 */ \ + OP(DrawBuffersEXTImmediate) /* 533 */ \ + OP(DiscardBackbufferCHROMIUM) /* 534 */ \ + OP(ScheduleOverlayPlaneCHROMIUM) /* 535 */ \ + OP(ScheduleCALayerSharedStateCHROMIUM) /* 536 */ \ + OP(ScheduleCALayerCHROMIUM) /* 537 */ \ + OP(ScheduleCALayerInUseQueryCHROMIUMImmediate) /* 538 */ \ + OP(CommitOverlayPlanesCHROMIUM) /* 539 */ \ + OP(SwapInterval) /* 540 */ \ + OP(FlushDriverCachesCHROMIUM) /* 541 */ \ + OP(ScheduleDCLayerSharedStateCHROMIUM) /* 542 */ \ + OP(ScheduleDCLayerCHROMIUM) /* 543 */ \ + OP(MatrixLoadfCHROMIUMImmediate) /* 544 */ \ + OP(MatrixLoadIdentityCHROMIUM) /* 545 */ \ + OP(GenPathsCHROMIUM) /* 546 */ \ + OP(DeletePathsCHROMIUM) /* 547 */ \ + OP(IsPathCHROMIUM) /* 548 */ \ + OP(PathCommandsCHROMIUM) /* 549 */ \ + OP(PathParameterfCHROMIUM) /* 550 */ \ + OP(PathParameteriCHROMIUM) /* 551 */ \ + OP(PathStencilFuncCHROMIUM) /* 552 */ \ + OP(StencilFillPathCHROMIUM) /* 553 */ \ + OP(StencilStrokePathCHROMIUM) /* 554 */ \ + OP(CoverFillPathCHROMIUM) /* 555 */ \ + OP(CoverStrokePathCHROMIUM) /* 556 */ \ + OP(StencilThenCoverFillPathCHROMIUM) /* 557 */ \ + OP(StencilThenCoverStrokePathCHROMIUM) /* 558 */ \ + OP(StencilFillPathInstancedCHROMIUM) /* 559 */ \ + OP(StencilStrokePathInstancedCHROMIUM) /* 560 */ \ + OP(CoverFillPathInstancedCHROMIUM) /* 561 */ \ + OP(CoverStrokePathInstancedCHROMIUM) /* 562 */ \ + OP(StencilThenCoverFillPathInstancedCHROMIUM) /* 563 */ \ + OP(StencilThenCoverStrokePathInstancedCHROMIUM) /* 564 */ \ + OP(BindFragmentInputLocationCHROMIUMBucket) /* 565 */ \ + OP(ProgramPathFragmentInputGenCHROMIUM) /* 566 */ \ + OP(GetBufferSubDataAsyncCHROMIUM) /* 567 */ \ + OP(CoverageModulationCHROMIUM) /* 568 */ \ + OP(BlendBarrierKHR) /* 569 */ \ + OP(ApplyScreenSpaceAntialiasingCHROMIUM) /* 570 */ \ + OP(BindFragDataLocationIndexedEXTBucket) /* 571 */ \ + OP(BindFragDataLocationEXTBucket) /* 572 */ \ + OP(GetFragDataIndexEXT) /* 573 */ \ + OP(UniformMatrix4fvStreamTextureMatrixCHROMIUMImmediate) /* 574 */ \ + OP(OverlayPromotionHintCHROMIUM) /* 575 */ \ + OP(SwapBuffersWithBoundsCHROMIUMImmediate) /* 576 */ \ + OP(SetDrawRectangleCHROMIUM) /* 577 */ \ + OP(SetEnableDCLayersCHROMIUM) /* 578 */ enum CommandId { kOneBeforeStartPoint =
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index b9097406..db2c9d2 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -1041,6 +1041,14 @@ void DoBindTexImage2DCHROMIUM( GLenum target, GLint image_id); + void DoBindTexImage2DWithInternalformatCHROMIUM(GLenum target, + GLenum internalformat, + GLint image_id); + // Common implementation of DoBindTexImage2DCHROMIUM entry points. + void BindTexImage2DCHROMIUMImpl(const char* function_name, + GLenum target, + GLenum internalformat, + GLint image_id); void DoReleaseTexImage2DCHROMIUM( GLenum target, GLint image_id); @@ -17763,10 +17771,26 @@ GLenum target, GLint image_id) { TRACE_EVENT0("gpu", "GLES2DecoderImpl::DoBindTexImage2DCHROMIUM"); + BindTexImage2DCHROMIUMImpl("glBindTexImage2DCHROMIUM", target, 0, image_id); +} + +void GLES2DecoderImpl::DoBindTexImage2DWithInternalformatCHROMIUM( + GLenum target, + GLenum internalformat, + GLint image_id) { + TRACE_EVENT0("gpu", + "GLES2DecoderImpl::DoBindTexImage2DWithInternalformatCHROMIUM"); + + BindTexImage2DCHROMIUMImpl("glBindTexImage2DWithInternalformatCHROMIUM", + target, internalformat, image_id); +} + +void GLES2DecoderImpl::BindTexImage2DCHROMIUMImpl(const char* function_name, + GLenum target, + GLenum internalformat, + GLint image_id) { if (target == GL_TEXTURE_CUBE_MAP) { - LOCAL_SET_GL_ERROR( - GL_INVALID_ENUM, - "glBindTexImage2DCHROMIUM", "invalid target"); + LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, function_name, "invalid target"); return; } @@ -17775,17 +17799,14 @@ TextureRef* texture_ref = texture_manager()->GetTextureInfoForTargetUnlessDefault(&state_, target); if (!texture_ref) { - LOCAL_SET_GL_ERROR( - GL_INVALID_OPERATION, - "glBindTexImage2DCHROMIUM", "no texture bound"); + LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, "no texture bound"); return; } gl::GLImage* image = image_manager()->LookupImage(image_id); if (!image) { - LOCAL_SET_GL_ERROR( - GL_INVALID_OPERATION, - "glBindTexImage2DCHROMIUM", "no image found with the given ID"); + LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, + "no image found with the given ID"); return; } @@ -17797,15 +17818,22 @@ // Note: We fallback to using CopyTexImage() before the texture is used // when BindTexImage() fails. - if (image->BindTexImage(target)) - image_state = Texture::BOUND; + if (internalformat) { + if (image->BindTexImageWithInternalformat(target, internalformat)) + image_state = Texture::BOUND; + } else { + if (image->BindTexImage(target)) + image_state = Texture::BOUND; + } } gfx::Size size = image->GetSize(); - GLenum internalformat = image->GetInternalFormat(); - texture_manager()->SetLevelInfo( - texture_ref, target, 0, internalformat, size.width(), size.height(), 1, 0, - internalformat, GL_UNSIGNED_BYTE, gfx::Rect(size)); + GLenum texture_internalformat = + internalformat ? internalformat : image->GetInternalFormat(); + texture_manager()->SetLevelInfo(texture_ref, target, 0, + texture_internalformat, size.width(), + size.height(), 1, 0, texture_internalformat, + GL_UNSIGNED_BYTE, gfx::Rect(size)); texture_manager()->SetLevelImage(texture_ref, target, 0, image, image_state); }
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h index fa2f5799..25237e2 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
@@ -4778,6 +4778,24 @@ return error::kNoError; } +error::Error GLES2DecoderImpl::HandleBindTexImage2DWithInternalformatCHROMIUM( + uint32_t immediate_data_size, + const volatile void* cmd_data) { + const volatile gles2::cmds::BindTexImage2DWithInternalformatCHROMIUM& c = + *static_cast<const volatile gles2::cmds:: + BindTexImage2DWithInternalformatCHROMIUM*>(cmd_data); + GLenum target = static_cast<GLenum>(c.target); + GLenum internalformat = static_cast<GLenum>(c.internalformat); + GLint imageId = static_cast<GLint>(c.imageId); + if (!validators_->texture_bind_target.IsValid(target)) { + LOCAL_SET_GL_ERROR_INVALID_ENUM( + "glBindTexImage2DWithInternalformatCHROMIUM", target, "target"); + return error::kNoError; + } + DoBindTexImage2DWithInternalformatCHROMIUM(target, internalformat, imageId); + return error::kNoError; +} + error::Error GLES2DecoderImpl::HandleReleaseTexImage2DCHROMIUM( uint32_t immediate_data_size, const volatile void* cmd_data) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc index eac5a9889..032369e0 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -885,6 +885,34 @@ } } +error::Error GLES2DecoderPassthroughImpl::BindTexImage2DCHROMIUMImpl( + GLenum target, + GLenum internalformat, + GLint imageId) { + if (target != GL_TEXTURE_2D) { + InsertError(GL_INVALID_ENUM, "Invalid target"); + return error::kNoError; + } + + gl::GLImage* image = image_manager_->LookupImage(imageId); + if (image == nullptr) { + InsertError(GL_INVALID_OPERATION, "No image found with the given ID"); + return error::kNoError; + } + + if (internalformat) { + if (!image->BindTexImageWithInternalformat(target, internalformat)) { + image->CopyTexImage(target); + } + } else { + if (!image->BindTexImage(target)) { + image->CopyTexImage(target); + } + } + + return error::kNoError; +} + #define GLES2_CMD_OP(name) \ { \ &GLES2DecoderPassthroughImpl::Handle##name, cmds::name::kArgFlags, \
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h index 0985a2c3..f6ab1099 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
@@ -303,6 +303,10 @@ void UpdateTextureBinding(GLenum target, GLuint client_id, GLuint service_id); + error::Error BindTexImage2DCHROMIUMImpl(GLenum target, + GLenum internalformat, + GLint image_id); + int commands_to_process_; DebugMarkerManager debug_marker_manager_;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h index 60282e14..7ab02bc 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
@@ -796,6 +796,9 @@ GLint location, const char* name); error::Error DoBindTexImage2DCHROMIUM(GLenum target, GLint imageId); +error::Error DoBindTexImage2DWithInternalformatCHROMIUM(GLenum target, + GLenum internalformat, + GLint imageId); error::Error DoReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId); error::Error DoTraceBeginCHROMIUM(const char* category_name, const char* trace_name);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc index 9bfcb6b..3600f809 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -3485,22 +3485,15 @@ error::Error GLES2DecoderPassthroughImpl::DoBindTexImage2DCHROMIUM( GLenum target, GLint imageId) { - if (target != GL_TEXTURE_2D) { - InsertError(GL_INVALID_ENUM, "Invalid target"); - return error::kNoError; - } + return BindTexImage2DCHROMIUMImpl(target, 0, imageId); +} - gl::GLImage* image = image_manager_->LookupImage(imageId); - if (image == nullptr) { - InsertError(GL_INVALID_OPERATION, "No image found with the given ID"); - return error::kNoError; - } - - if (!image->BindTexImage(target)) { - image->CopyTexImage(target); - } - - return error::kNoError; +error::Error +GLES2DecoderPassthroughImpl::DoBindTexImage2DWithInternalformatCHROMIUM( + GLenum target, + GLenum internalformat, + GLint imageId) { + return BindTexImage2DCHROMIUMImpl(target, internalformat, imageId); } error::Error GLES2DecoderPassthroughImpl::DoReleaseTexImage2DCHROMIUM(
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc index 9c6f95f..629a24d 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc
@@ -4024,6 +4024,24 @@ return error::kNoError; } +error::Error +GLES2DecoderPassthroughImpl::HandleBindTexImage2DWithInternalformatCHROMIUM( + uint32_t immediate_data_size, + const volatile void* cmd_data) { + const volatile gles2::cmds::BindTexImage2DWithInternalformatCHROMIUM& c = + *static_cast<const volatile gles2::cmds:: + BindTexImage2DWithInternalformatCHROMIUM*>(cmd_data); + GLenum target = static_cast<GLenum>(c.target); + GLenum internalformat = static_cast<GLenum>(c.internalformat); + GLint imageId = static_cast<GLint>(c.imageId); + error::Error error = DoBindTexImage2DWithInternalformatCHROMIUM( + target, internalformat, imageId); + if (error != error::kNoError) { + return error; + } + return error::kNoError; +} + error::Error GLES2DecoderPassthroughImpl::HandleReleaseTexImage2DCHROMIUM( uint32_t immediate_data_size, const volatile void* cmd_data) {
diff --git a/ios/chrome/app/spotlight/topsites_spotlight_manager.mm b/ios/chrome/app/spotlight/topsites_spotlight_manager.mm index 58b45fe..5d100de 100644 --- a/ios/chrome/app/spotlight/topsites_spotlight_manager.mm +++ b/ios/chrome/app/spotlight/topsites_spotlight_manager.mm
@@ -20,7 +20,7 @@ #include "ios/chrome/browser/suggestions/suggestions_service_factory.h" #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h" #include "ios/chrome/browser/sync/sync_observer_bridge.h" -#include "ios/chrome/browser/ui/ntp/google_landing_controller.h" +#include "ios/chrome/browser/ui/ntp/google_landing_mediator.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." @@ -229,7 +229,7 @@ - (void)onMostVisitedURLsAvailable: (const history::MostVisitedURLList&)top_sites { NSUInteger sitesToIndex = - MIN(top_sites.size(), [GoogleLandingController maxSitesShown]); + MIN(top_sites.size(), [GoogleLandingMediator maxSitesShown]); for (size_t i = 0; i < sitesToIndex; i++) { const GURL& URL = top_sites[i].url; @@ -247,8 +247,7 @@ (const suggestions::SuggestionsProfile&)suggestionsProfile { size_t size = suggestionsProfile.suggestions_size(); if (size) { - NSUInteger sitesToIndex = - MIN(size, [GoogleLandingController maxSitesShown]); + NSUInteger sitesToIndex = MIN(size, [GoogleLandingMediator maxSitesShown]); for (size_t i = 0; i < sitesToIndex; i++) { const suggestions::ChromeSuggestion& suggestion = suggestionsProfile.suggestions(i);
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn index 64726d7..8c223db 100644 --- a/ios/chrome/browser/ui/ntp/BUILD.gn +++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -113,8 +113,12 @@ sources = [ "centering_scrollview.h", "centering_scrollview.mm", + "google_landing_consumer.h", "google_landing_controller.h", "google_landing_controller.mm", + "google_landing_data_source.h", + "google_landing_mediator.h", + "google_landing_mediator.mm", "incognito_panel_controller.h", "incognito_panel_controller.mm", "most_visited_cell.h",
diff --git a/ios/chrome/browser/ui/ntp/google_landing_consumer.h b/ios/chrome/browser/ui/ntp/google_landing_consumer.h new file mode 100644 index 0000000..9c6b9ea --- /dev/null +++ b/ios/chrome/browser/ui/ntp/google_landing_consumer.h
@@ -0,0 +1,66 @@ +// Copyright 2017 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_CHROME_BROWSER_UI_NTP_GOOGLE_LANDING_CONSUMER_H_ +#define IOS_CHROME_BROWSER_UI_NTP_GOOGLE_LANDING_CONSUMER_H_ + +#import <Foundation/Foundation.h> + +// TODO(crbug.com/694750): Remove these two when the types below are changed. +#include "components/ntp_tiles/ntp_tile.h" +#include "ios/public/provider/chrome/browser/images/whats_new_icon.h" +#include "ios/public/provider/chrome/browser/ui/logo_vendor.h" + +// Handles google landing controller update notifications. +@protocol GoogleLandingConsumer<NSObject> + +// Whether the Google logo or doodle is being shown. +- (void)setLogoIsShowing:(BOOL)logoIsShowing; + +// Exposes view and methods to drive the doodle. +- (void)setLogoVendor:(id<LogoVendor>)logoVendor; + +// |YES| if this consumer is incognito. +- (void)setIsOffTheRecord:(BOOL)isOffTheRecord; + +// |YES| if this consumer is has voice search enabled. +- (void)setVoiceSearchIsEnabled:(BOOL)voiceSearchIsEnabled; + +// Sets the maximum number of sites shown. +- (void)setMaximumMostVisitedSitesShown: + (NSUInteger)maximumMostVisitedSitesShown; + +// Sets the text of a what's new promo. +- (void)setPromoText:(NSString*)promoText; + +// Sets the icon of a what's new promo. +// TODO(crbug.com/694750): This should not be WhatsNewIcon. +- (void)setPromoIcon:(WhatsNewIcon)promoIcon; + +// |YES| if a what's new promo can be displayed. +- (void)setPromoCanShow:(BOOL)promoCanShow; + +// TODO(crbug.com/694750): This should be replaced with consumer suitable data +// type property. +// Tells the consumer to that most visited data updated. +- (void)mostVisitedDataUpdated; + +// Tells the consumer a most visited icon was updated. +- (void)mostVisitedIconMadeAvailableAtIndex:(NSUInteger)index; + +// TODO(crbug.com/694750): These two calls can be made with dispatcher instead. +// The location bar has lost focus. +- (void)locationBarResignsFirstResponder; + +// Tell location bar has taken focus. +- (void)locationBarBecomesFirstResponder; + +// TODO(crbug.com/694750): This call will be removed once dispatching is +// available. +// Asks the consumer to execute a chrome command. +- (void)chromeExecuteCommand:(id)sender; + +@end + +#endif // IOS_CHROME_BROWSER_UI_NTP_GOOGLE_LANDING_CONSUMER_H_
diff --git a/ios/chrome/browser/ui/ntp/google_landing_controller.h b/ios/chrome/browser/ui/ntp/google_landing_controller.h index 94283429..af2352d 100644 --- a/ios/chrome/browser/ui/ntp/google_landing_controller.h +++ b/ios/chrome/browser/ui/ntp/google_landing_controller.h
@@ -9,34 +9,21 @@ #include <memory> +#import "ios/chrome/browser/ui/ntp/google_landing_consumer.h" #import "ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.h" #import "ios/chrome/browser/ui/toolbar/toolbar_owner.h" #import "ios/public/provider/chrome/browser/voice/logo_animation_controller.h" -@protocol OmniboxFocuser; -@class TabModel; -@protocol UrlLoader; -@protocol WebToolbarDelegate; - -namespace ios { -class ChromeBrowserState; -} +@protocol GoogleLandingDataSource; // Google centric new tab page. @interface GoogleLandingController - : UIViewController<LogoAnimationControllerOwnerOwner, + : UIViewController<GoogleLandingConsumer, + LogoAnimationControllerOwnerOwner, NewTabPagePanelProtocol, ToolbarOwner> -// Initialization method. -- (id)initWithLoader:(id<UrlLoader>)loader - browserState:(ios::ChromeBrowserState*)browserState - focuser:(id<OmniboxFocuser>)focuser - webToolbarDelegate:(id<WebToolbarDelegate>)webToolbarDelegate - tabModel:(TabModel*)tabModel; - -// Get the maximum number of sites shown. -+ (NSUInteger)maxSitesShown; +@property(nonatomic, assign) id<GoogleLandingDataSource> dataSource; @end
diff --git a/ios/chrome/browser/ui/ntp/google_landing_controller.mm b/ios/chrome/browser/ui/ntp/google_landing_controller.mm index fc420ea..7f1d3c7 100644 --- a/ios/chrome/browser/ui/ntp/google_landing_controller.mm +++ b/ios/chrome/browser/ui/ntp/google_landing_controller.mm
@@ -6,68 +6,29 @@ #include <algorithm> -#include "base/i18n/case_conversion.h" -#import "base/ios/weak_nsobject.h" -#include "base/json/json_reader.h" -#include "base/logging.h" -#include "base/mac/bind_objc_block.h" #include "base/mac/foundation_util.h" -#include "base/mac/scoped_nsobject.h" -#include "base/metrics/histogram_macros.h" #include "base/metrics/user_metrics.h" -#include "base/metrics/user_metrics_action.h" #include "base/strings/sys_string_conversions.h" -#include "components/favicon/core/large_icon_service.h" -#include "components/keyed_service/core/service_access_type.h" -#include "components/ntp_tiles/most_visited_sites.h" -#include "components/ntp_tiles/ntp_tile.h" -#include "components/rappor/rappor_service_impl.h" -#include "components/search_engines/template_url_service.h" -#include "components/search_engines/template_url_service_observer.h" #include "components/strings/grit/components_strings.h" -#include "ios/chrome/browser/application_context.h" -#include "ios/chrome/browser/browser_state/chrome_browser_state.h" -#import "ios/chrome/browser/favicon/favicon_loader.h" -#include "ios/chrome/browser/favicon/favicon_service_factory.h" -#include "ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h" -#include "ios/chrome/browser/favicon/large_icon_cache.h" -#import "ios/chrome/browser/metrics/new_tab_page_uma.h" -#include "ios/chrome/browser/notification_promo.h" -#include "ios/chrome/browser/ntp_tiles/ios_most_visited_sites_factory.h" -#import "ios/chrome/browser/ntp_tiles/most_visited_sites_observer_bridge.h" -#include "ios/chrome/browser/reading_list/reading_list_model_factory.h" -#include "ios/chrome/browser/search_engines/template_url_service_factory.h" -#include "ios/chrome/browser/suggestions/suggestions_service_factory.h" -#import "ios/chrome/browser/tabs/tab_model.h" -#import "ios/chrome/browser/ui/browser_view_controller.h" #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h" #import "ios/chrome/browser/ui/commands/generic_chrome_command.h" #include "ios/chrome/browser/ui/commands/ios_command_ids.h" #import "ios/chrome/browser/ui/context_menu/context_menu_coordinator.h" +#import "ios/chrome/browser/ui/ntp/google_landing_data_source.h" #import "ios/chrome/browser/ui/ntp/most_visited_cell.h" #import "ios/chrome/browser/ui/ntp/most_visited_layout.h" #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h" #import "ios/chrome/browser/ui/ntp/new_tab_page_header_view.h" -#import "ios/chrome/browser/ui/ntp/notification_promo_whats_new.h" #import "ios/chrome/browser/ui/ntp/whats_new_header_view.h" -#import "ios/chrome/browser/ui/orientation_limiting_navigation_controller.h" #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h" -#import "ios/chrome/browser/ui/toolbar/toolbar_owner.h" -#import "ios/chrome/browser/ui/toolbar/web_toolbar_controller.h" #include "ios/chrome/browser/ui/ui_util.h" #import "ios/chrome/browser/ui/uikit_ui_util.h" -#import "ios/chrome/browser/ui/url_loader.h" #include "ios/chrome/common/string_util.h" #include "ios/chrome/grit/ios_strings.h" -#include "ios/public/provider/chrome/browser/chrome_browser_provider.h" -#include "ios/public/provider/chrome/browser/ui/logo_vendor.h" -#include "ios/public/provider/chrome/browser/voice/voice_search_provider.h" #import "ios/third_party/material_components_ios/src/components/Snackbar/src/MaterialSnackbar.h" #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h" -#include "ios/web/public/referrer.h" #import "ios/web/public/web_state/context_menu_params.h" #import "net/base/mac/url_conversions.h" -#include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "ui/base/l10n/l10n_util.h" using base::UserMetricsAction; @@ -99,7 +60,6 @@ const CGFloat kWhatsNewHeaderHiddenHeight = 8; const CGFloat kDoodleTopMarginIPadPortrait = 82; const CGFloat kDoodleTopMarginIPadLandscape = 82; -const NSInteger kMaxNumMostVisitedFavicons = 8; const NSInteger kMaxNumMostVisitedFaviconRows = 2; const CGFloat kMaxSearchFieldFrameMargin = 200; const CGFloat kShiftTilesDownAnimationDuration = 0.2; @@ -109,38 +69,6 @@ } // namespace -namespace google_landing { - -// Observer used to hide the Google logo and doodle if the TemplateURLService -// changes. -class SearchEngineObserver : public TemplateURLServiceObserver { - public: - SearchEngineObserver(GoogleLandingController* owner, - TemplateURLService* urlService); - ~SearchEngineObserver() override; - void OnTemplateURLServiceChanged() override; - - private: - base::WeakNSObject<GoogleLandingController> _owner; - TemplateURLService* _templateURLService; // weak -}; - -SearchEngineObserver::SearchEngineObserver(GoogleLandingController* owner, - TemplateURLService* urlService) - : _owner(owner), _templateURLService(urlService) { - _templateURLService->AddObserver(this); -} - -SearchEngineObserver::~SearchEngineObserver() { - _templateURLService->RemoveObserver(this); -} - -void SearchEngineObserver::OnTemplateURLServiceChanged() { - [_owner reload]; -} - -} // namespace google_landing - @interface GoogleLandingController (UsedByGoogleLandingView) // Update frames for subviews depending on the interface orientation. - (void)updateSubviewFrames; @@ -160,11 +88,6 @@ @implementation GoogleLandingView -- (void)layoutSubviews { - [super layoutSubviews]; - [_googleLanding updateSubviewFrames]; -} - - (void)setFrameDelegate:(GoogleLandingController*)delegate { _googleLanding = delegate; } @@ -184,8 +107,7 @@ @end -@interface GoogleLandingController ()<MostVisitedSitesObserving, - OverscrollActionsControllerDelegate, +@interface GoogleLandingController ()<OverscrollActionsControllerDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, @@ -194,16 +116,6 @@ // Fake omnibox. base::scoped_nsobject<UIButton> _searchTapTarget; - // Controller to fetch and show doodles or a default Google logo. - base::scoped_nsprotocol<id<LogoVendor>> _doodleController; - - // Most visited data from the MostVisitedSites service (copied upon receiving - // the callback). - ntp_tiles::NTPTilesVector _mostVisitedData; - - // |YES| if impressions were logged already and shouldn't be logged again. - BOOL _recordedPageImpression; - // A collection view for the most visited sites. base::scoped_nsobject<UICollectionView> _mostVisitedView; @@ -214,9 +126,6 @@ // |YES| when notifications indicate the omnibox is focused. BOOL _omniboxFocused; - // Delegate to focus and blur the omnibox. - base::WeakNSProtocol<id<OmniboxFocuser>> _focuser; - // Tap and swipe gesture recognizers when the omnibox is focused. base::scoped_nsobject<UITapGestureRecognizer> _tapGestureRecognizer; base::scoped_nsobject<UISwipeGestureRecognizer> _swipeGestureRecognizer; @@ -224,57 +133,63 @@ // Handles displaying the context menu for all form factors. base::scoped_nsobject<ContextMenuCoordinator> _contextMenuCoordinator; - // What's new promo. - std::unique_ptr<NotificationPromoWhatsNew> _notification_promo; - - // A MostVisitedSites::Observer bridge object to get notified of most visited - // sites changes. - std::unique_ptr<ntp_tiles::MostVisitedSitesObserverBridge> - _most_visited_observer_bridge; - - std::unique_ptr<ntp_tiles::MostVisitedSites> _most_visited_sites; - // URL of the last deleted most viewed entry. If present the UI to restore it // is shown. base::scoped_nsobject<NSURL> _deletedUrl; - // Listen for default search engine changes. - std::unique_ptr<google_landing::SearchEngineObserver> _observer; - TemplateURLService* _templateURLService; // weak - // |YES| if the view has finished its first layout. This is useful when // determining if the view has sized itself for tablet. BOOL _viewLoaded; + // |YES| if the fakebox header should be animated on scroll. BOOL _animateHeader; + + // |YES| if the collection scrollView is scrolled all the way to the top. Used + // to lock this position in place on various frame changes. BOOL _scrolledToTop; + + // |YES| if this NTP panel is visible. When set to |NO| various UI updates + // are ignored. BOOL _isShowing; + CFTimeInterval _shiftTilesDownStartTime; CGSize _mostVisitedCellSize; - NSUInteger _maxNumMostVisited; - ios::ChromeBrowserState* _browserState; // Weak. - id<UrlLoader> _loader; // Weak. - std::unique_ptr< - suggestions::SuggestionsService::ResponseCallbackList::Subscription> - _suggestionsServiceResponseSubscription; base::scoped_nsobject<NSLayoutConstraint> _hintLabelLeadingConstraint; base::scoped_nsobject<NSLayoutConstraint> _voiceTapTrailingConstraint; base::scoped_nsobject<NSMutableArray> _supplementaryViews; base::scoped_nsobject<NewTabPageHeaderView> _headerView; base::scoped_nsobject<WhatsNewHeaderView> _promoHeaderView; - base::WeakNSProtocol<id<WebToolbarDelegate>> _webToolbarDelegate; - base::scoped_nsobject<TabModel> _tabModel; } -// Whether the Google logo or doodle is being shown. -@property(nonatomic, readonly, getter=isShowingLogo) BOOL showingLogo; - -@property(nonatomic) id<UrlLoader> loader; - // Redeclare the |view| property to be the GoogleLandingView subclass instead of // a generic UIView. @property(nonatomic, readwrite, strong) GoogleLandingView* view; +// Whether the Google logo or doodle is being shown. +@property(nonatomic, assign) BOOL logoIsShowing; + +// Exposes view and methods to drive the doodle. +@property(nonatomic, assign) id<LogoVendor> logoVendor; + +// |YES| if this consumer is incognito. +@property(nonatomic, assign) BOOL isOffTheRecord; + +// |YES| if this consumer is has voice search enabled. +@property(nonatomic, assign) BOOL voiceSearchIsEnabled; + +// Gets the maximum number of sites shown. +@property(nonatomic, assign) NSUInteger maximumMostVisitedSitesShown; + +// Gets the text of a what's new promo. +@property(nonatomic, retain) NSString* promoText; + +// Gets the icon of a what's new promo. +// TODO(crbug.com/694750): This should not be WhatsNewIcon. +@property(nonatomic, assign) WhatsNewIcon promoIcon; + +// |YES| if a what's new promo can be displayed. +@property(nonatomic, assign) BOOL promoCanShow; + // iPhone landscape uses a slightly different layout for the doodle and search // field frame. Returns the proper frame from |frames| based on orientation, // centered in the view. @@ -285,8 +200,6 @@ - (CGRect)searchFieldFrame; // Returns the height to use for the What's New promo view. - (CGFloat)promoHeaderHeight; -// Add the LogoController view. -- (void)addDoodle; // Add fake search field and voice search microphone. - (void)addSearchField; // Add most visited collection view. @@ -313,19 +226,12 @@ - (void)setFlowLayoutInset:(UICollectionViewFlowLayout*)layout; // Instructs the UICollectionView and UIView to reload it's data and layout. - (void)reloadData; -// Logs a histogram due to a Most Visited item being opened. -- (void)logMostVisitedClick:(const NSUInteger)visitedIndex - tileType:(ntp_tiles::TileVisualType)tileType; -// Returns the size of |_mostVisitedData|. +// Returns the size of |self.mostVisitedData|. - (NSUInteger)numberOfItems; // Returns the number of non empty tiles (as opposed to the placeholder tiles). - (NSInteger)numberOfNonEmptyTilesShown; -// Returns the URL for the mosted visited item in |_mostVisitedData|. +// Returns the URL for the mosted visited item in |self.mostVisitedData|. - (GURL)urlForIndex:(NSUInteger)index; -// Removes a blacklisted URL in both |_mostVisitedData|. -- (void)removeBlacklistedURL:(const GURL&)url; -// Adds URL to the blacklist in both |_mostVisitedData|. -- (void)addBlacklistedURL:(const GURL&)url; // Returns the expected height of the NewTabPageHeaderView. - (CGFloat)heightForSectionWithOmnibox; // Returns the nearest ancestor view that is kind of |aClass|. @@ -333,105 +239,112 @@ // Updates the collection view's scroll view offset for the next frame of the // shiftTilesDown animation. - (void)shiftTilesDownAnimationDidFire:(CADisplayLink*)link; -// Returns the size to use for Most Visited cells in the NTP contained in -// |view|. -+ (CGSize)mostVisitedCellSizeForView:(UIView*)view; +// Returns the size to use for Most Visited cells in the NTP. +- (CGSize)mostVisitedCellSize; // Returns the padding for use between Most Visited cells. -+ (CGFloat)mostVisitedCellPadding; +- (CGFloat)mostVisitedCellPadding; @end @implementation GoogleLandingController @dynamic view; -@synthesize loader = _loader; +@synthesize logoVendor = _logoVendor; +@synthesize dataSource = _dataSource; // Property declared in NewTabPagePanelProtocol. @synthesize delegate = _delegate; - -+ (NSUInteger)maxSitesShown { - return kMaxNumMostVisitedFavicons; -} - -- (id)initWithLoader:(id<UrlLoader>)loader - browserState:(ios::ChromeBrowserState*)browserState - focuser:(id<OmniboxFocuser>)focuser - webToolbarDelegate:(id<WebToolbarDelegate>)webToolbarDelegate - tabModel:(TabModel*)tabModel { - self = [super init]; - if (self) { - DCHECK(browserState); - _browserState = browserState; - _loader = loader; - _isShowing = YES; - _maxNumMostVisited = [GoogleLandingController maxSitesShown]; - - NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter]; - [defaultCenter - addObserver:self - selector:@selector(locationBarBecomesFirstResponder) - name:ios_internal::kLocationBarBecomesFirstResponderNotification - object:nil]; - [defaultCenter - addObserver:self - selector:@selector(locationBarResignsFirstResponder) - name:ios_internal::kLocationBarResignsFirstResponderNotification - object:nil]; - [defaultCenter - addObserver:self - selector:@selector(orientationDidChange:) - name:UIApplicationDidChangeStatusBarOrientationNotification - object:nil]; - - _notification_promo.reset(new NotificationPromoWhatsNew( - GetApplicationContext()->GetLocalState())); - _notification_promo->Init(); - _tapGestureRecognizer.reset([[UITapGestureRecognizer alloc] - initWithTarget:self - action:@selector(blurOmnibox)]); - [_tapGestureRecognizer setDelegate:self]; - _swipeGestureRecognizer.reset([[UISwipeGestureRecognizer alloc] - initWithTarget:self - action:@selector(blurOmnibox)]); - [_swipeGestureRecognizer - setDirection:UISwipeGestureRecognizerDirectionDown]; - - _focuser.reset(focuser); - _webToolbarDelegate.reset(webToolbarDelegate); - _tabModel.reset([tabModel retain]); - - _scrolledToTop = NO; - _animateHeader = YES; - } - return self; -} +@synthesize isOffTheRecord = _isOffTheRecord; +@synthesize logoIsShowing = _logoIsShowing; +@synthesize promoText = _promoText; +@synthesize promoIcon = _promoIcon; +@synthesize promoCanShow = _promoCanShow; +@synthesize maximumMostVisitedSitesShown = _maximumMostVisitedSitesShown; +@synthesize voiceSearchIsEnabled = _voiceSearchIsEnabled; - (void)loadView { - self.view = - [[GoogleLandingView alloc] initWithFrame:[UIScreen mainScreen].bounds]; + self.view = [[[GoogleLandingView alloc] + initWithFrame:[UIScreen mainScreen].bounds] autorelease]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; [self.view setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth]; [self.view setFrameDelegate:self]; + // Initialise |shiftTilesDownStartTime| to a sentinel value to indicate that // the animation has not yet started. _shiftTilesDownStartTime = -1; - _mostVisitedCellSize = - [GoogleLandingController mostVisitedCellSizeForView:self.view]; - [self addDoodle]; + _mostVisitedCellSize = [self mostVisitedCellSize]; + _isShowing = YES; + _scrolledToTop = NO; + _animateHeader = YES; + + _tapGestureRecognizer.reset([[UITapGestureRecognizer alloc] + initWithTarget:self + action:@selector(blurOmnibox)]); + [_tapGestureRecognizer setDelegate:self]; + _swipeGestureRecognizer.reset([[UISwipeGestureRecognizer alloc] + initWithTarget:self + action:@selector(blurOmnibox)]); + [_swipeGestureRecognizer setDirection:UISwipeGestureRecognizerDirectionDown]; + [self addSearchField]; [self addMostVisited]; [self addOverscrollActions]; [self reload]; } -+ (CGSize)mostVisitedCellSizeForView:(UIView*)view { +- (void)viewDidLayoutSubviews { + [self updateSubviewFrames]; +} + +- (void)viewWillTransitionToSize:(CGSize)size + withTransitionCoordinator: + (id<UIViewControllerTransitionCoordinator>)coordinator { + [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; + + void (^alongsideBlock)(id<UIViewControllerTransitionCoordinatorContext>) = ^( + id<UIViewControllerTransitionCoordinatorContext> context) { + if (IsIPadIdiom() && _scrolledToTop) { + // Keep the most visited thumbnails scrolled to the top. + [_mostVisitedView setContentOffset:CGPointMake(0, [self pinnedOffsetY])]; + return; + }; + + // Invalidate the layout so that the collection view's header size is reset + // for the new orientation. + if (!_scrolledToTop) { + [[_mostVisitedView collectionViewLayout] invalidateLayout]; + } + + // Call -scrollViewDidScroll: so that the omnibox's frame is adjusted for + // the scroll view's offset. + [self scrollViewDidScroll:_mostVisitedView]; + + }; + [coordinator animateAlongsideTransition:alongsideBlock completion:nil]; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [_mostVisitedView setDelegate:nil]; + [_mostVisitedView setDataSource:nil]; + [_overscrollActionsController invalidate]; + [super dealloc]; +} + +#pragma mark - Private + +- (CGSize)mostVisitedCellSize { if (IsIPadIdiom()) { // On iPads, split-screen and slide-over may require showing smaller cells. CGSize maximumCellSize = [MostVisitedCell maximumSize]; - CGSize viewSize = view.bounds.size; + CGSize viewSize = self.view.bounds.size; CGFloat smallestDimension = viewSize.height > viewSize.width ? viewSize.width : viewSize.height; CGFloat cellWidth = AlignValueToPixel( - (smallestDimension - 3 * [self.class mostVisitedCellPadding]) / 2); + (smallestDimension - 3 * [self mostVisitedCellPadding]) / 2); if (cellWidth < maximumCellSize.width) { return CGSizeMake(cellWidth, cellWidth); } else { @@ -442,58 +355,18 @@ } } -+ (CGFloat)mostVisitedCellPadding { +- (CGFloat)mostVisitedCellPadding { return IsIPadIdiom() ? kMostVisitedPaddingIPadFavicon : kMostVisitedPaddingIPhone; } -- (void)orientationDidChange:(NSNotification*)notification { - if (IsIPadIdiom() && _scrolledToTop) { - // Keep the most visited thumbnails scrolled to the top. - base::WeakNSObject<GoogleLandingController> weakSelf(self); - dispatch_after( - dispatch_time(DISPATCH_TIME_NOW, 0), dispatch_get_main_queue(), ^{ - base::scoped_nsobject<GoogleLandingController> strongSelf( - [weakSelf retain]); - if (!strongSelf) - return; - - [strongSelf.get()->_mostVisitedView - setContentOffset:CGPointMake(0, [strongSelf pinnedOffsetY])]; - }); - return; - } - - // Call inside a block to avoid the animation that -orientationDidChange is - // wrapped inside. - base::WeakNSObject<GoogleLandingController> weakSelf(self); - void (^layoutBlock)(void) = ^{ - base::scoped_nsobject<GoogleLandingController> strongSelf( - [weakSelf retain]); - // Invalidate the layout so that the collection view's header size is reset - // for the new orientation. - if (!_scrolledToTop) { - [[strongSelf.get()->_mostVisitedView collectionViewLayout] - invalidateLayout]; - [[strongSelf view] setNeedsLayout]; - } - - // Call -scrollViewDidScroll: so that the omnibox's frame is adjusted for - // the scroll view's offset. - [self scrollViewDidScroll:_mostVisitedView]; - }; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0), dispatch_get_main_queue(), - layoutBlock); -} - - (CGFloat)viewWidth { return [self.view frame].size.width; } - (int)numberOfColumns { CGFloat width = [self viewWidth]; - CGFloat padding = [self.class mostVisitedCellPadding]; + CGFloat padding = [self mostVisitedCellPadding]; // Try to fit 4 columns. if (width >= 5 * padding + _mostVisitedCellSize.width * 4) return 4; @@ -513,26 +386,18 @@ - (CGFloat)leftMargin { int columns = [self numberOfColumns]; CGFloat whitespace = [self viewWidth] - columns * _mostVisitedCellSize.width - - (columns - 1) * [self.class mostVisitedCellPadding]; + (columns - 1) * [self mostVisitedCellPadding]; CGFloat margin = AlignValueToPixel(whitespace / 2); - DCHECK(margin >= [self.class mostVisitedCellPadding]); + DCHECK(margin >= [self mostVisitedCellPadding]); return margin; } -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [_mostVisitedView setDelegate:nil]; - [_mostVisitedView setDataSource:nil]; - [_overscrollActionsController invalidate]; - [super dealloc]; -} - - (CGRect)doodleFrame { const CGRect kDoodleFrame[2] = { {{0, 66}, {0, 120}}, {{0, 56}, {0, 120}}, }; CGRect doodleFrame = [self getOrientationFrame:kDoodleFrame]; - if (!IsIPadIdiom() && !self.showingLogo) + if (!IsIPadIdiom() && !self.logoIsShowing) doodleFrame.size.height = kNonGoogleSearchDoodleHeight; if (IsIPadIdiom()) { doodleFrame.origin.y = IsPortrait() ? kDoodleTopMarginIPadPortrait @@ -574,33 +439,13 @@ - (CGFloat)promoHeaderHeight { CGFloat promoMaxWidth = [self viewWidth] - 2 * [self leftMargin]; - NSString* text = base::SysUTF8ToNSString(_notification_promo->promo_text()); + NSString* text = self.promoText; return [WhatsNewHeaderView heightToFitText:text inWidth:promoMaxWidth]; } -- (ToolbarController*)relinquishedToolbarController { - return [_headerView relinquishedToolbarController]; -} - -- (void)reparentToolbarController { - [_headerView reparentToolbarController]; -} - -- (BOOL)isShowingLogo { - return [_doodleController isShowingLogo]; -} - - (void)updateLogoAndFakeboxDisplay { - BOOL showLogo = NO; - TemplateURL* defaultURL = _templateURLService->GetDefaultSearchProvider(); - if (defaultURL) { - showLogo = - defaultURL->GetEngineType(_templateURLService->search_terms_data()) == - SEARCH_ENGINE_GOOGLE; - } - - if (self.showingLogo != showLogo) { - [_doodleController setShowingLogo:showLogo]; + if (self.logoVendor.showingLogo != self.logoIsShowing) { + self.logoVendor.showingLogo = self.logoIsShowing; if (_viewLoaded) { [self updateSubviewFrames]; @@ -622,25 +467,10 @@ [_promoHeaderView setFrame:whatsNewFrame]; } if (IsIPadIdiom()) - [_searchTapTarget setHidden:!self.showingLogo]; + [_searchTapTarget setHidden:!self.logoIsShowing]; } } -// Initialize and add a Google Doodle widget, show a Google logo by default. -- (void)addDoodle { - if (!_doodleController) { - _doodleController.reset(ios::GetChromeBrowserProvider()->CreateLogoVendor( - _browserState, _loader)); - } - [[_doodleController view] setFrame:[self doodleFrame]]; - - _templateURLService = - ios::TemplateURLServiceFactory::GetForBrowserState(_browserState); - _observer.reset( - new google_landing::SearchEngineObserver(self, _templateURLService)); - _templateURLService->Load(); -} - // Initialize and add a search field tap target and a voice search button. - (void)addSearchField { CGRect searchFieldFrame = [self searchFieldFrame]; @@ -764,9 +594,7 @@ IDS_IOS_ACCNAME_VOICE_SEARCH)]; [voiceTapTarget setAccessibilityIdentifier:@"Voice Search"]; - if (ios::GetChromeBrowserProvider() - ->GetVoiceSearchProvider() - ->IsVoiceSearchEnabled()) { + if (self.voiceSearchIsEnabled) { [voiceTapTarget addTarget:self action:@selector(loadVoiceSearch:) forControlEvents:UIControlEventTouchUpInside]; @@ -779,17 +607,13 @@ } - (void)loadVoiceSearch:(id)sender { - DCHECK(ios::GetChromeBrowserProvider() - ->GetVoiceSearchProvider() - ->IsVoiceSearchEnabled()); + DCHECK(self.voiceSearchIsEnabled); base::RecordAction(UserMetricsAction("MobileNTPMostVisitedVoiceSearch")); [sender chromeExecuteCommand:sender]; } - (void)preloadVoiceSearch:(id)sender { - DCHECK(ios::GetChromeBrowserProvider() - ->GetVoiceSearchProvider() - ->IsVoiceSearchEnabled()); + DCHECK(self.voiceSearchIsEnabled); [sender removeTarget:self action:@selector(preloadVoiceSearch:) forControlEvents:UIControlEventTouchDown]; @@ -813,13 +637,12 @@ } - (void)updateSubviewFrames { - _mostVisitedCellSize = - [GoogleLandingController mostVisitedCellSizeForView:self.view]; + _mostVisitedCellSize = [self mostVisitedCellSize]; UICollectionViewFlowLayout* flowLayout = base::mac::ObjCCastStrict<UICollectionViewFlowLayout>( [_mostVisitedView collectionViewLayout]); [flowLayout setItemSize:_mostVisitedCellSize]; - [[_doodleController view] setFrame:[self doodleFrame]]; + self.logoVendor.view.frame = [self doodleFrame]; [self setFlowLayoutInset:flowLayout]; [flowLayout invalidateLayout]; @@ -855,7 +678,7 @@ if (!_viewLoaded) { _viewLoaded = YES; - [_doodleController fetchDoodle]; + [self.logoVendor fetchDoodle]; } [self.delegate updateNtpBarShadowForPanelController:self]; } @@ -872,7 +695,7 @@ [flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical]; [flowLayout setItemSize:_mostVisitedCellSize]; [flowLayout setMinimumInteritemSpacing:8]; - [flowLayout setMinimumLineSpacing:[self.class mostVisitedCellPadding]]; + [flowLayout setMinimumLineSpacing:[self mostVisitedCellPadding]]; DCHECK(!_mostVisitedView); _mostVisitedView.reset([[UICollectionView alloc] initWithFrame:mostVisitedFrame @@ -896,12 +719,6 @@ [_mostVisitedView setAccessibilityIdentifier:@"Google Landing"]; [self.view addSubview:_mostVisitedView]; - _most_visited_sites = - IOSMostVisitedSitesFactory::NewForBrowserState(_browserState); - _most_visited_observer_bridge.reset( - new ntp_tiles::MostVisitedSitesObserverBridge(self)); - _most_visited_sites->SetMostVisitedURLsObserver( - _most_visited_observer_bridge.get(), kMaxNumMostVisitedFavicons); } - (void)updateSearchField { @@ -924,10 +741,8 @@ // Check to see if the promo label should be hidden. - (void)hideWhatsNewIfNecessary { - if (![_promoHeaderView isHidden] && _notification_promo && - !_notification_promo->CanShow()) { + if (![_promoHeaderView isHidden] && !self.promoCanShow) { [_promoHeaderView setHidden:YES]; - _notification_promo.reset(); [self.view setNeedsLayout]; } } @@ -963,7 +778,7 @@ if (_scrolledToTop) { _animateHeader = NO; if (!IsIPadIdiom()) { - [_focuser onFakeboxAnimationComplete]; + [self.dataSource onFakeboxAnimationComplete]; [_headerView fadeOutShadow]; [_searchTapTarget setHidden:YES]; } @@ -972,12 +787,12 @@ } - (void)searchFieldTapped:(id)sender { - [_focuser focusFakebox]; + [self.dataSource focusFakebox]; } - (void)blurOmnibox { if (_omniboxFocused) { - [_focuser cancelOmniboxEdit]; + [self.dataSource cancelOmniboxEdit]; } else { [self locationBarResignsFirstResponder]; } @@ -1000,7 +815,7 @@ _scrolledToTop = NO; if (!IsIPadIdiom()) { [_searchTapTarget setHidden:NO]; - [_focuser onFakeboxBlur]; + [self.dataSource onFakeboxBlur]; } // Reload most visited sites in case the number of placeholder cells needs to @@ -1056,17 +871,8 @@ } } -- (void)logMostVisitedClick:(const NSUInteger)visitedIndex - tileType:(ntp_tiles::TileVisualType)tileType { - new_tab_page_uma::RecordAction( - _browserState, new_tab_page_uma::ACTION_OPENED_MOST_VISITED_ENTRY); - base::RecordAction(UserMetricsAction("MobileNTPMostVisited")); - const ntp_tiles::NTPTile& tile = _mostVisitedData[visitedIndex]; - ntp_tiles::metrics::RecordTileClick(visitedIndex, tile.source, tileType); -} - - (void)reloadData { - // -reloadData updates from |_mostVisitedData|. + // -reloadData updates from |self.mostVisitedData|. // -invalidateLayout is necessary because sometimes the flowLayout has the // wrong cached size and will throw an internal exception if the // -numberOfItems shrinks. -setNeedsLayout is needed in case @@ -1077,16 +883,12 @@ [self.view setNeedsLayout]; } -- (void)willUpdateSnapshot { - [_overscrollActionsController clear]; -} - - (CGFloat)heightForSectionWithOmnibox { CGFloat headerHeight = CGRectGetMaxY([self searchFieldFrame]) + kNTPSearchFieldBottomPadding; if (IsIPadIdiom()) { - if (self.showingLogo) { - if (!_notification_promo || !_notification_promo->CanShow()) { + if (self.logoIsShowing) { + if (!self.promoCanShow) { UIInterfaceOrientation orient = [[UIApplication sharedApplication] statusBarOrientation]; const CGFloat kTopSpacingMaterialPortrait = 56; @@ -1102,34 +904,14 @@ return headerHeight; } -#pragma mark - MostVisitedSitesObserving +#pragma mark - ToolbarOwner -- (void)onMostVisitedURLsAvailable:(const ntp_tiles::NTPTilesVector&)data { - _mostVisitedData = data; - [self reloadData]; - - if (data.size() && !_recordedPageImpression) { - _recordedPageImpression = YES; - std::vector<ntp_tiles::metrics::TileImpression> tiles; - for (const ntp_tiles::NTPTile& ntpTile : data) { - tiles.emplace_back(ntpTile.source, ntp_tiles::UNKNOWN_TILE_TYPE, - ntpTile.url); - } - ntp_tiles::metrics::RecordPageImpression( - tiles, GetApplicationContext()->GetRapporServiceImpl()); - } +- (ToolbarController*)relinquishedToolbarController { + return [_headerView relinquishedToolbarController]; } -- (void)onIconMadeAvailable:(const GURL&)siteUrl { - for (size_t i = 0; i < [self numberOfItems]; ++i) { - const ntp_tiles::NTPTile& ntpTile = _mostVisitedData[i]; - if (ntpTile.url == siteUrl) { - NSIndexPath* indexPath = - [NSIndexPath indexPathForRow:i inSection:SectionWithMostVisited]; - [_mostVisitedView reloadItemsAtIndexPaths:@[ indexPath ]]; - break; - } - } +- (void)reparentToolbarController { + [_headerView reparentToolbarController]; } #pragma mark - UICollectionView Methods. @@ -1144,7 +926,7 @@ ((UICollectionViewFlowLayout*)collectionViewLayout).headerReferenceSize = CGSizeMake(0, headerHeight); } else if (section == SectionWithMostVisited) { - if (_notification_promo && _notification_promo->CanShow()) { + if (self.promoCanShow) { headerHeight = [self promoHeaderHeight]; } else { headerHeight = kWhatsNewHeaderHiddenHeight; @@ -1181,11 +963,11 @@ const NSUInteger visitedIndex = indexPath.row; [self blurOmnibox]; DCHECK(visitedIndex < [self numberOfItems]); - [self logMostVisitedClick:visitedIndex tileType:cell.tileType]; - [_loader loadURL:[self urlForIndex:visitedIndex] - referrer:web::Referrer() - transition:ui::PAGE_TRANSITION_AUTO_BOOKMARK - rendererInitiated:NO]; + [self.dataSource logMostVisitedClick:visitedIndex tileType:cell.tileType]; + [self.dataSource loadURL:[self urlForIndex:visitedIndex] + referrer:web::Referrer() + transition:ui::PAGE_TRANSITION_AUTO_BOOKMARK + rendererInitiated:NO]; } #pragma mark - UICollectionViewDataSource @@ -1205,19 +987,14 @@ UICollectionElementKindSectionHeader withReuseIdentifier:@"header" forIndexPath:indexPath] retain]); - [_headerView addSubview:[_doodleController view]]; + [_headerView addSubview:[self.logoVendor view]]; [_headerView addSubview:_searchTapTarget]; [_headerView addViewsToSearchField:_searchTapTarget]; if (!IsIPadIdiom()) { - ReadingListModel* readingListModel = - ReadingListModelFactory::GetForBrowserState(_browserState); // iPhone header also contains a toolbar since the normal toolbar is // hidden. - [_headerView addToolbarWithDelegate:_webToolbarDelegate - focuser:_focuser - tabModel:_tabModel - readingListModel:readingListModel]; + [_headerView addToolbarWithDataSource:self.dataSource]; } [_supplementaryViews addObject:_headerView]; } @@ -1233,11 +1010,10 @@ forIndexPath:indexPath] retain]); [_promoHeaderView setSideMargin:[self leftMargin]]; [_promoHeaderView setDelegate:self]; - if (_notification_promo && _notification_promo->CanShow()) { - [_promoHeaderView - setText:base::SysUTF8ToNSString(_notification_promo->promo_text())]; - [_promoHeaderView setIcon:_notification_promo->icon()]; - _notification_promo->HandleViewed(); + if (self.promoCanShow) { + [_promoHeaderView setText:self.promoText]; + [_promoHeaderView setIcon:self.promoIcon]; + [self.dataSource promoViewed]; } [_supplementaryViews addObject:_promoHeaderView]; } @@ -1262,7 +1038,7 @@ // Phone always contains the maximum number of cells. Cells in excess of the // number of thumbnails are used solely for layout/sizing. if (!IsIPadIdiom()) - return _maxNumMostVisited; + return self.maximumMostVisitedSitesShown; return [self numberOfNonEmptyTilesShown]; } @@ -1288,10 +1064,11 @@ return cell; } - const ntp_tiles::NTPTile& ntpTile = _mostVisitedData[indexPath.row]; + const ntp_tiles::NTPTile& ntpTile = + [self.dataSource mostVisitedAtIndex:indexPath.row]; NSString* title = base::SysUTF16ToNSString(ntpTile.title); - [cell setupWithURL:ntpTile.url title:title browserState:_browserState]; + [cell setupWithURL:ntpTile.url title:title dataSource:self.dataSource]; base::scoped_nsobject<UILongPressGestureRecognizer> longPress( [[UILongPressGestureRecognizer alloc] @@ -1345,18 +1122,19 @@ if (!strongSelf) return; MostVisitedCell* cell = (MostVisitedCell*)sender.view; - [strongSelf logMostVisitedClick:index tileType:cell.tileType]; - [[strongSelf loader] webPageOrderedOpen:url - referrer:web::Referrer() - inBackground:YES - appendTo:kCurrentTab]; + [[strongSelf dataSource] logMostVisitedClick:index + tileType:cell.tileType]; + [[strongSelf dataSource] webPageOrderedOpen:url + referrer:web::Referrer() + inBackground:YES + appendTo:kCurrentTab]; }; [_contextMenuCoordinator addItemWithTitle:l10n_util::GetNSStringWithFixup( IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB) action:action]; - if (!_browserState->IsOffTheRecord()) { + if (!self.isOffTheRecord) { // Open in Incognito Tab. action = ^{ base::scoped_nsobject<GoogleLandingController> strongSelf( @@ -1364,12 +1142,13 @@ if (!strongSelf) return; MostVisitedCell* cell = (MostVisitedCell*)sender.view; - [strongSelf logMostVisitedClick:index tileType:cell.tileType]; - [[strongSelf loader] webPageOrderedOpen:url - referrer:web::Referrer() - inIncognito:YES - inBackground:NO - appendTo:kCurrentTab]; + [[strongSelf dataSource] logMostVisitedClick:index + tileType:cell.tileType]; + [[strongSelf dataSource] webPageOrderedOpen:url + referrer:web::Referrer() + inIncognito:YES + inBackground:NO + appendTo:kCurrentTab]; }; [_contextMenuCoordinator addItemWithTitle:l10n_util::GetNSStringWithFixup( @@ -1387,7 +1166,7 @@ if (!strongSelf) return; base::RecordAction(UserMetricsAction("MostVisited_UrlBlacklisted")); - [strongSelf addBlacklistedURL:url]; + [[strongSelf dataSource] addBlacklistedURL:url]; [strongSelf showMostVisitedUndoForURL:net::NSURLWithGURL(url)]; }; [_contextMenuCoordinator addItemWithTitle:title action:action]; @@ -1410,7 +1189,8 @@ [weakSelf retain]); if (!strongSelf) return; - [strongSelf removeBlacklistedURL:net::GURLWithNSURL(_deletedUrl)]; + [[strongSelf dataSource] + removeBlacklistedURL:net::GURLWithNSURL(_deletedUrl)]; }; action.title = l10n_util::GetNSString(IDS_NEW_TAB_UNDO_THUMBNAIL_REMOVE); action.accessibilityIdentifier = @"Undo"; @@ -1425,30 +1205,10 @@ } - (void)onPromoLabelTapped { - [_focuser cancelOmniboxEdit]; - _notification_promo->HandleClosed(); + [self.dataSource cancelOmniboxEdit]; [_promoHeaderView setHidden:YES]; [self.view setNeedsLayout]; - - if (_notification_promo->IsURLPromo()) { - [_loader webPageOrderedOpen:_notification_promo->url() - referrer:web::Referrer() - inBackground:NO - appendTo:kCurrentTab]; - _notification_promo.reset(); - return; - } - - if (_notification_promo->IsChromeCommand()) { - base::scoped_nsobject<GenericChromeCommand> command( - [[GenericChromeCommand alloc] - initWithTag:_notification_promo->command_id()]); - [self.view chromeExecuteCommand:command]; - _notification_promo.reset(); - return; - } - - NOTREACHED(); + [self.dataSource promoTapped]; } // Returns the Y value to use for the scroll view's contentOffset when scrolling @@ -1469,7 +1229,7 @@ // Fetch the doodle after the view finishes laying out. Otherwise, tablet // may fetch the wrong sized doodle. if (_viewLoaded) - [_doodleController fetchDoodle]; + [self.logoVendor fetchDoodle]; [self updateLogoAndFakeboxDisplay]; [self hideWhatsNewIfNecessary]; } @@ -1522,10 +1282,14 @@ return alpha; } +- (void)willUpdateSnapshot { + [_overscrollActionsController clear]; +} + #pragma mark - LogoAnimationControllerOwnerOwner - (id<LogoAnimationControllerOwner>)logoAnimationControllerOwner { - return [_doodleController logoAnimationControllerOwner]; + return [self.logoVendor logoAnimationControllerOwner]; } #pragma mark - UIScrollViewDelegate Methods. @@ -1538,7 +1302,7 @@ CGFloat pinnedOffsetY = [self pinnedOffsetY]; if (_omniboxFocused && scrollView.dragging && scrollView.contentOffset.y < pinnedOffsetY) { - [_focuser cancelOmniboxEdit]; + [self.dataSource cancelOmniboxEdit]; } if (IsIPadIdiom()) { @@ -1611,32 +1375,20 @@ #pragma mark - Most visited / Suggestions service wrapper methods. -- (suggestions::SuggestionsService*)suggestionsService { - return suggestions::SuggestionsServiceFactory::GetForBrowserState( - _browserState); -} - - (NSUInteger)numberOfItems { - NSUInteger numItems = _mostVisitedData.size(); + NSUInteger numItems = [self.dataSource mostVisitedSize]; NSUInteger maxItems = [self numberOfColumns] * kMaxNumMostVisitedFaviconRows; return MIN(maxItems, numItems); } - (NSInteger)numberOfNonEmptyTilesShown { - NSInteger numCells = MIN([self numberOfItems], _maxNumMostVisited); + NSInteger numCells = + MIN([self numberOfItems], self.maximumMostVisitedSitesShown); return MAX(numCells, [self numberOfColumns]); } - (GURL)urlForIndex:(NSUInteger)index { - return _mostVisitedData[index].url; -} - -- (void)addBlacklistedURL:(const GURL&)url { - _most_visited_sites->AddOrRemoveBlacklistedUrl(url, true); -} - -- (void)removeBlacklistedURL:(const GURL&)url { - _most_visited_sites->AddOrRemoveBlacklistedUrl(url, false); + return [self.dataSource mostVisitedAtIndex:index].url; } #pragma mark - GoogleLandingController (ExposedForTesting) methods. @@ -1712,4 +1464,24 @@ return [self nearestAncestorOfView:[view superview] withClass:aClass]; } +#pragma mark - GoogleLandingConsumer + +- (void)setLogoIsShowing:(BOOL)logoIsShowing { + _logoIsShowing = logoIsShowing; + [self updateLogoAndFakeboxDisplay]; +} + +- (void)mostVisitedDataUpdated { + [self reloadData]; +} + +- (void)mostVisitedIconMadeAvailableAtIndex:(NSUInteger)index { + if (index > [self numberOfItems]) + return; + + NSIndexPath* indexPath = + [NSIndexPath indexPathForRow:index inSection:SectionWithMostVisited]; + [_mostVisitedView reloadItemsAtIndexPaths:@[ indexPath ]]; +} + @end
diff --git a/ios/chrome/browser/ui/ntp/google_landing_controller_unittest.mm b/ios/chrome/browser/ui/ntp/google_landing_controller_unittest.mm index 99b3cf8e..3a711ff 100644 --- a/ios/chrome/browser/ui/ntp/google_landing_controller_unittest.mm +++ b/ios/chrome/browser/ui/ntp/google_landing_controller_unittest.mm
@@ -11,6 +11,7 @@ #include "ios/chrome/browser/search_engines/template_url_service_factory.h" #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h" #import "ios/chrome/browser/ui/ntp/google_landing_controller.h" +#import "ios/chrome/browser/ui/ntp/google_landing_mediator.h" #include "ios/chrome/test/block_cleanup_test.h" #include "ios/chrome/test/ios_chrome_scoped_testing_local_state.h" #include "ios/web/public/test/test_web_thread.h" @@ -53,9 +54,11 @@ // Set up stub UrlLoader. mockUrlLoader_ = [OCMockObject mockForProtocol:@protocol(UrlLoader)]; - controller_ = [[GoogleLandingController alloc] - initWithLoader:(id<UrlLoader>)mockUrlLoader_ + controller_ = [[GoogleLandingController alloc] init]; + mediator_ = [[GoogleLandingMediator alloc] + initWithConsumer:controller_ browserState:chrome_browser_state_.get() + loader:(id<UrlLoader>)mockUrlLoader_ focuser:nil webToolbarDelegate:nil tabModel:nil]; @@ -67,6 +70,7 @@ IOSChromeScopedTestingLocalState local_state_; std::unique_ptr<TestChromeBrowserState> chrome_browser_state_; OCMockObject* mockUrlLoader_; + GoogleLandingMediator* mediator_; GoogleLandingController* controller_; };
diff --git a/ios/chrome/browser/ui/ntp/google_landing_data_source.h b/ios/chrome/browser/ui/ntp/google_landing_data_source.h new file mode 100644 index 0000000..48e78e7e7 --- /dev/null +++ b/ios/chrome/browser/ui/ntp/google_landing_data_source.h
@@ -0,0 +1,73 @@ +// Copyright 2017 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_CHROME_BROWSER_UI_NTP_GOOGLE_LANDING_DATA_SOURCE_H_ +#define IOS_CHROME_BROWSER_UI_NTP_GOOGLE_LANDING_DATA_SOURCE_H_ + +#import <Foundation/Foundation.h> + +#include "components/ntp_tiles/ntp_tile.h" +#include "components/ntp_tiles/tile_visual_type.h" +#import "ios/chrome/browser/ui/toolbar/web_toolbar_controller.h" +#import "ios/chrome/browser/ui/url_loader.h" +#include "url/gurl.h" + +class ReadingListModel; +class LargeIconCache; +namespace favicon { +class LargeIconService; +} +@class TabModel; +@protocol WebToolbarDelegate; + +// DataSource for the google landing controller. +// TODO(crbug.com/694750): Most everything here can be moved to dispatcher. +@protocol GoogleLandingDataSource<OmniboxFocuser, UrlLoader> + +// Removes a blacklisted URL in both |_mostVisitedData|. +- (void)removeBlacklistedURL:(const GURL&)url; + +// Adds URL to the blacklist in both |_mostVisitedData|. +- (void)addBlacklistedURL:(const GURL&)url; + +// Logs a histogram due to a Most Visited item being opened. +- (void)logMostVisitedClick:(const NSUInteger)visitedIndex + tileType:(ntp_tiles::TileVisualType)tileType; + +// Called when a what's new promo is viewed. +- (void)promoViewed; + +// Called when a what's new promo is tapped. +- (void)promoTapped; + +// TODO(crbug.com/694750): The following two methods should be moved to the +// consumer, and converted into types more suitable for a consumer. +// Gets an a most visited NTP tile at |index|. +- (ntp_tiles::NTPTile)mostVisitedAtIndex:(NSUInteger)index; + +// Gets the number of most visited entries. +- (NSUInteger)mostVisitedSize; + +// TODO(crbug.com/694750): The following five properties will be removed in +// subsequent CLs, with data provided via GoogleDataConsumer into types more +// suitable for a consumer. + +// Gets the reading list model. +- (ReadingListModel*)readingListModel; + +// Gets the large icon cache. +- (LargeIconCache*)largeIconCache; + +// Gets the large icon service. +- (favicon::LargeIconService*)largeIconService; + +// Gets the toolbar delegate. +- (id<WebToolbarDelegate>)toolbarDelegate; + +// Gets the tab model. +- (TabModel*)tabModel; + +@end + +#endif // IOS_CHROME_BROWSER_UI_NTP_GOOGLE_LANDING_DATA_SOURCE_H_
diff --git a/ios/chrome/browser/ui/ntp/google_landing_mediator.h b/ios/chrome/browser/ui/ntp/google_landing_mediator.h new file mode 100644 index 0000000..95112f9 --- /dev/null +++ b/ios/chrome/browser/ui/ntp/google_landing_mediator.h
@@ -0,0 +1,36 @@ +// Copyright 2017 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_CHROME_BROWSER_UI_NTP_GOOGLE_LANDING_MEDIATOR_H_ +#define IOS_CHROME_BROWSER_UI_NTP_GOOGLE_LANDING_MEDIATOR_H_ + +#import <Foundation/Foundation.h> + +#import "ios/chrome/browser/ui/ntp/google_landing_data_source.h" + +@protocol GoogleLandingConsumer; +@protocol OmniboxFocuser; +@protocol UrlLoader; + +namespace ios { +class ChromeBrowserState; +} + +// A mediator object that provides various data sources for google landing. +@interface GoogleLandingMediator : NSObject<GoogleLandingDataSource> + +- (instancetype)initWithConsumer:(id<GoogleLandingConsumer>)consumer + browserState:(ios::ChromeBrowserState*)browserState + loader:(id<UrlLoader>)loader + focuser:(id<OmniboxFocuser>)focuser + webToolbarDelegate:(id<WebToolbarDelegate>)webToolbarDelegate + tabModel:(TabModel*)tabModel NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; + +// Get the maximum number of sites shown. ++ (NSUInteger)maxSitesShown; + +@end + +#endif // IOS_CHROME_BROWSER_UI_NTP_GOOGLE_LANDING_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/ntp/google_landing_mediator.mm b/ios/chrome/browser/ui/ntp/google_landing_mediator.mm new file mode 100644 index 0000000..1014342 --- /dev/null +++ b/ios/chrome/browser/ui/ntp/google_landing_mediator.mm
@@ -0,0 +1,392 @@ +// Copyright 2017 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/chrome/browser/ui/ntp/google_landing_mediator.h" + +#import "base/ios/weak_nsobject.h" +#include "base/mac/scoped_nsobject.h" +#include "base/metrics/user_metrics.h" +#include "base/metrics/user_metrics_action.h" +#include "base/strings/sys_string_conversions.h" +#include "components/ntp_tiles/metrics.h" +#include "components/ntp_tiles/most_visited_sites.h" +#include "components/ntp_tiles/ntp_tile.h" +#include "components/rappor/rappor_service_impl.h" +#include "components/search_engines/template_url_service.h" +#include "components/search_engines/template_url_service_observer.h" +#include "ios/chrome/browser/application_context.h" +#include "ios/chrome/browser/browser_state/chrome_browser_state.h" +#include "ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h" +#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h" +#import "ios/chrome/browser/metrics/new_tab_page_uma.h" +#include "ios/chrome/browser/ntp_tiles/ios_most_visited_sites_factory.h" +#import "ios/chrome/browser/ntp_tiles/most_visited_sites_observer_bridge.h" +#include "ios/chrome/browser/reading_list/reading_list_model_factory.h" +#include "ios/chrome/browser/search_engines/template_url_service_factory.h" +#import "ios/chrome/browser/ui/browser_view_controller.h" +#import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h" +#import "ios/chrome/browser/ui/commands/generic_chrome_command.h" +#import "ios/chrome/browser/ui/ntp/google_landing_consumer.h" +#import "ios/chrome/browser/ui/ntp/notification_promo_whats_new.h" +#import "ios/chrome/browser/ui/toolbar/web_toolbar_controller.h" +#import "ios/chrome/browser/ui/url_loader.h" +#include "ios/public/provider/chrome/browser/chrome_browser_provider.h" +#include "ios/public/provider/chrome/browser/voice/voice_search_provider.h" + +using base::UserMetricsAction; + +namespace { + +const NSInteger kMaxNumMostVisitedFavicons = 8; + +} // namespace + +@interface GoogleLandingMediator (UsedBySearchEngineObserver) +// Check to see if the logo visibility should change. +- (void)updateShowLogo; +@end + +namespace google_landing { + +// Observer used to hide the Google logo and doodle if the TemplateURLService +// changes. +class SearchEngineObserver : public TemplateURLServiceObserver { + public: + SearchEngineObserver(GoogleLandingMediator* owner, + TemplateURLService* urlService); + ~SearchEngineObserver() override; + void OnTemplateURLServiceChanged() override; + + private: + base::WeakNSObject<GoogleLandingMediator> _owner; + TemplateURLService* _templateURLService; // weak +}; + +SearchEngineObserver::SearchEngineObserver(GoogleLandingMediator* owner, + TemplateURLService* urlService) + : _owner(owner), _templateURLService(urlService) { + _templateURLService->AddObserver(this); +} + +SearchEngineObserver::~SearchEngineObserver() { + _templateURLService->RemoveObserver(this); +} + +void SearchEngineObserver::OnTemplateURLServiceChanged() { + [_owner updateShowLogo]; +} + +} // namespace google_landing + +@interface GoogleLandingMediator ()<MostVisitedSitesObserving> { + // The ChromeBrowserState associated with this mediator. + ios::ChromeBrowserState* _browserState; // Weak. + + // |YES| if impressions were logged already and shouldn't be logged again. + BOOL _recordedPageImpression; + + // The designated url loader. + id<UrlLoader> _loader; // Weak. + + // Delegate to focus and blur the omnibox. + base::WeakNSProtocol<id<OmniboxFocuser>> _focuser; + + // Controller to fetch and show doodles or a default Google logo. + base::scoped_nsprotocol<id<LogoVendor>> _doodleController; + + // Listen for default search engine changes. + std::unique_ptr<google_landing::SearchEngineObserver> _observer; + TemplateURLService* _templateURLService; // weak + + // A MostVisitedSites::Observer bridge object to get notified of most visited + // sites changes. + std::unique_ptr<ntp_tiles::MostVisitedSitesObserverBridge> + _mostVisitedObserverBridge; + + std::unique_ptr<ntp_tiles::MostVisitedSites> _mostVisitedSites; + + // Most visited data from the MostVisitedSites service (copied upon receiving + // the callback). + ntp_tiles::NTPTilesVector _mostVisitedData; + + base::WeakNSProtocol<id<WebToolbarDelegate>> _webToolbarDelegate; + + base::scoped_nsobject<TabModel> _tabModel; + + // What's new promo. + std::unique_ptr<NotificationPromoWhatsNew> _notification_promo; +} + +// Consumer to handle google landing update notifications. +@property(nonatomic) id<GoogleLandingConsumer> consumer; + +// Perform initial setup. +- (void)setUp; + +@end + +@implementation GoogleLandingMediator + +@synthesize consumer = _consumer; + +- (instancetype)initWithConsumer:(id<GoogleLandingConsumer>)consumer + browserState:(ios::ChromeBrowserState*)browserState + loader:(id<UrlLoader>)loader + focuser:(id<OmniboxFocuser>)focuser + webToolbarDelegate:(id<WebToolbarDelegate>)webToolbarDelegate + tabModel:(TabModel*)tabModel { + self = [super init]; + if (self) { + _consumer = consumer; + _browserState = browserState; + _loader = loader; + _focuser.reset(focuser); + _webToolbarDelegate.reset(webToolbarDelegate); + _tabModel.reset([tabModel retain]); + [self setUp]; + } + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self.consumer]; + [super dealloc]; +} + +- (void)setUp { + [_consumer setIsOffTheRecord:_browserState->IsOffTheRecord()]; + [_consumer setVoiceSearchIsEnabled:ios::GetChromeBrowserProvider() + ->GetVoiceSearchProvider() + ->IsVoiceSearchEnabled()]; + [_consumer + setMaximumMostVisitedSitesShown:[GoogleLandingMediator maxSitesShown]]; + + // Set up template URL service to listen for default search engine changes. + _templateURLService = + ios::TemplateURLServiceFactory::GetForBrowserState(_browserState); + _observer.reset( + new google_landing::SearchEngineObserver(self, _templateURLService)); + _templateURLService->Load(); + _doodleController.reset(ios::GetChromeBrowserProvider()->CreateLogoVendor( + _browserState, _loader)); + [_consumer setLogoVendor:_doodleController]; + [self updateShowLogo]; + + // Set up most visited sites. This call may have the side effect of + // triggering -onMostVisitedURLsAvailable immediately, which can load the + // view before dataSource is set. + _mostVisitedSites = + IOSMostVisitedSitesFactory::NewForBrowserState(_browserState); + _mostVisitedObserverBridge.reset( + new ntp_tiles::MostVisitedSitesObserverBridge(self)); + _mostVisitedSites->SetMostVisitedURLsObserver( + _mostVisitedObserverBridge.get(), [GoogleLandingMediator maxSitesShown]); + + // Set up notifications; + NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter]; + [defaultCenter + addObserver:_consumer + selector:@selector(locationBarBecomesFirstResponder) + name:ios_internal::kLocationBarBecomesFirstResponderNotification + object:nil]; + [defaultCenter + addObserver:_consumer + selector:@selector(locationBarResignsFirstResponder) + name:ios_internal::kLocationBarResignsFirstResponderNotification + object:nil]; + + // Set up what's new. + _notification_promo.reset( + new NotificationPromoWhatsNew(GetApplicationContext()->GetLocalState())); + _notification_promo->Init(); + [_consumer setPromoText:[base::SysUTF8ToNSString( + _notification_promo->promo_text()) copy]]; + [_consumer setPromoIcon:_notification_promo->icon()]; + [_consumer setPromoCanShow:_notification_promo->CanShow()]; +} + +- (void)updateShowLogo { + BOOL showLogo = NO; + TemplateURL* defaultURL = _templateURLService->GetDefaultSearchProvider(); + if (defaultURL) { + showLogo = + defaultURL->GetEngineType(_templateURLService->search_terms_data()) == + SEARCH_ENGINE_GOOGLE; + } + [self.consumer setLogoIsShowing:showLogo]; +} + ++ (NSUInteger)maxSitesShown { + return kMaxNumMostVisitedFavicons; +} + +#pragma mark - MostVisitedSitesObserving + +- (void)onMostVisitedURLsAvailable:(const ntp_tiles::NTPTilesVector&)data { + _mostVisitedData = data; + [self.consumer mostVisitedDataUpdated]; + + if (data.size() && !_recordedPageImpression) { + _recordedPageImpression = YES; + std::vector<ntp_tiles::metrics::TileImpression> tiles; + for (const ntp_tiles::NTPTile& ntpTile : data) { + tiles.emplace_back(ntpTile.source, ntp_tiles::UNKNOWN_TILE_TYPE, + ntpTile.url); + } + ntp_tiles::metrics::RecordPageImpression( + tiles, GetApplicationContext()->GetRapporServiceImpl()); + } +} + +- (void)onIconMadeAvailable:(const GURL&)siteUrl { + for (size_t i = 0; i < _mostVisitedData.size(); ++i) { + const ntp_tiles::NTPTile& ntpTile = _mostVisitedData[i]; + if (ntpTile.url == siteUrl) { + [self.consumer mostVisitedIconMadeAvailableAtIndex:i]; + break; + } + } +} + +#pragma mark - GoogleLandingDataSource + +- (void)addBlacklistedURL:(const GURL&)url { + _mostVisitedSites->AddOrRemoveBlacklistedUrl(url, true); +} + +- (void)removeBlacklistedURL:(const GURL&)url { + _mostVisitedSites->AddOrRemoveBlacklistedUrl(url, false); +} + +- (ntp_tiles::NTPTile)mostVisitedAtIndex:(NSUInteger)index { + return _mostVisitedData[index]; +} + +- (NSUInteger)mostVisitedSize { + return _mostVisitedData.size(); +} + +- (void)logMostVisitedClick:(const NSUInteger)visitedIndex + tileType:(ntp_tiles::TileVisualType)tileType { + new_tab_page_uma::RecordAction( + _browserState, new_tab_page_uma::ACTION_OPENED_MOST_VISITED_ENTRY); + base::RecordAction(UserMetricsAction("MobileNTPMostVisited")); + const ntp_tiles::NTPTile& tile = _mostVisitedData[visitedIndex]; + ntp_tiles::metrics::RecordTileClick(visitedIndex, tile.source, tileType); +} + +- (ReadingListModel*)readingListModel { + return ReadingListModelFactory::GetForBrowserState(_browserState); +} + +- (LargeIconCache*)largeIconCache { + return IOSChromeLargeIconCacheFactory::GetForBrowserState(_browserState); +} + +- (favicon::LargeIconService*)largeIconService { + return IOSChromeLargeIconServiceFactory::GetForBrowserState(_browserState); +} + +- (id<WebToolbarDelegate>)toolbarDelegate { + return _webToolbarDelegate; +} + +- (TabModel*)tabModel { + return _tabModel; +} + +- (void)promoViewed { + DCHECK(_notification_promo); + _notification_promo->HandleViewed(); + [self.consumer setPromoCanShow:_notification_promo->CanShow()]; +} + +- (void)promoTapped { + DCHECK(_notification_promo); + _notification_promo->HandleClosed(); + [self.consumer setPromoCanShow:_notification_promo->CanShow()]; + + if (_notification_promo->IsURLPromo()) { + [_loader webPageOrderedOpen:_notification_promo->url() + referrer:web::Referrer() + inBackground:NO + appendTo:kCurrentTab]; + return; + } + + if (_notification_promo->IsChromeCommand()) { + base::scoped_nsobject<GenericChromeCommand> command( + [[GenericChromeCommand alloc] + initWithTag:_notification_promo->command_id()]); + [self.consumer chromeExecuteCommand:command]; + return; + } + NOTREACHED(); +} + +#pragma mark - UrlLoader + +- (void)loadURL:(const GURL&)url + referrer:(const web::Referrer&)referrer + transition:(ui::PageTransition)transition + rendererInitiated:(BOOL)rendererInitiated { + [_loader loadURL:url + referrer:referrer + transition:transition + rendererInitiated:rendererInitiated]; +} + +- (void)webPageOrderedOpen:(const GURL&)url + referrer:(const web::Referrer&)referrer + inBackground:(BOOL)inBackground + appendTo:(OpenPosition)appendTo { + [_loader webPageOrderedOpen:url + referrer:referrer + inBackground:inBackground + appendTo:appendTo]; +} + +- (void)webPageOrderedOpen:(const GURL&)url + referrer:(const web::Referrer&)referrer + inIncognito:(BOOL)inIncognito + inBackground:(BOOL)inBackground + appendTo:(OpenPosition)appendTo { + [_loader webPageOrderedOpen:url + referrer:referrer + inIncognito:inIncognito + inBackground:inBackground + appendTo:appendTo]; +} + +- (void)loadSessionTab:(const sessions::SessionTab*)sessionTab { + NOTREACHED(); +} + +- (void)loadJavaScriptFromLocationBar:(NSString*)script { + NOTREACHED(); +} + +#pragma mark - OmniboxFocuser + +- (void)focusOmnibox { + [_focuser focusOmnibox]; +} + +- (void)cancelOmniboxEdit { + [_focuser cancelOmniboxEdit]; +} + +- (void)focusFakebox { + [_focuser focusFakebox]; +} + +- (void)onFakeboxBlur { + [_focuser onFakeboxBlur]; +} + +- (void)onFakeboxAnimationComplete { + [_focuser onFakeboxAnimationComplete]; +} + +@end
diff --git a/ios/chrome/browser/ui/ntp/most_visited_cell.h b/ios/chrome/browser/ui/ntp/most_visited_cell.h index 691245a..873674f 100644 --- a/ios/chrome/browser/ui/ntp/most_visited_cell.h +++ b/ios/chrome/browser/ui/ntp/most_visited_cell.h
@@ -10,17 +10,13 @@ #include "components/ntp_tiles/metrics.h" #include "url/gurl.h" -namespace ios { -class ChromeBrowserState; -} +@protocol GoogleLandingDataSource; // Cell showing each most visited image, favicon and title. @interface MostVisitedCell : UICollectionViewCell // URL of the top site. @property(nonatomic, assign) GURL URL; -// Reference to the relevant ChromeBrowserState -@property(nonatomic, readonly) ios::ChromeBrowserState* browserState; // Type of tile (icon, scrabble tile, default) @property(nonatomic, readonly) ntp_tiles::TileVisualType tileType; @@ -34,7 +30,7 @@ // Setup the display state of the cell. - (void)setupWithURL:(GURL)URL title:(NSString*)title - browserState:(ios::ChromeBrowserState*)browserState; + dataSource:(id<GoogleLandingDataSource>)dataSource; // Preferred maximum cell size. + (CGSize)maximumSize;
diff --git a/ios/chrome/browser/ui/ntp/most_visited_cell.mm b/ios/chrome/browser/ui/ntp/most_visited_cell.mm index df2b223..c4bde11 100644 --- a/ios/chrome/browser/ui/ntp/most_visited_cell.mm +++ b/ios/chrome/browser/ui/ntp/most_visited_cell.mm
@@ -20,12 +20,10 @@ #include "components/suggestions/suggestions_service.h" #import "ios/chrome/browser/favicon/favicon_loader.h" #include "ios/chrome/browser/favicon/favicon_service_factory.h" -#include "ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h" -#include "ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h" -#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h" #include "ios/chrome/browser/favicon/large_icon_cache.h" #include "ios/chrome/browser/history/top_sites_factory.h" #include "ios/chrome/browser/suggestions/suggestions_service_factory.h" +#import "ios/chrome/browser/ui/ntp/google_landing_data_source.h" #import "ios/chrome/browser/ui/uikit_ui_util.h" #import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h" #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h" @@ -43,8 +41,8 @@ @interface MostVisitedCell () { // Backs property with the same name. GURL _URL; - // Weak reference to the relevant BrowserState. - ios::ChromeBrowserState* _browserState; + // Weak reference to the relevant GoogleLandingDataSource. + base::WeakNSProtocol<id<GoogleLandingDataSource>> _dataSource; // Backs property with the same name. ntp_tiles::TileVisualType _tileType; @@ -66,7 +64,6 @@ @implementation MostVisitedCell @synthesize URL = _URL; -@synthesize browserState = _browserState; @synthesize tileType = _tileType; - (instancetype)initWithFrame:(CGRect)frame { @@ -148,8 +145,8 @@ - (void)setupWithURL:(GURL)URL title:(NSString*)title - browserState:(ios::ChromeBrowserState*)browserState { - _browserState = browserState; + dataSource:(id<GoogleLandingDataSource>)dataSource { + _dataSource.reset(dataSource); _tileType = ntp_tiles::TileVisualType::NONE; [self setText:title]; [self setURL:URL]; @@ -183,14 +180,14 @@ } if (result.bitmap.is_valid() || result.fallback_icon_style) { - IOSChromeLargeIconCacheFactory::GetForBrowserState( - [strongSelf browserState]) - ->SetCachedResult(URL, result); + LargeIconCache* largeIconCache = + [strongSelf.get()->_dataSource largeIconCache]; + if (largeIconCache) + largeIconCache->SetCachedResult(URL, result); } }; - LargeIconCache* cache = - IOSChromeLargeIconCacheFactory::GetForBrowserState(self.browserState); + LargeIconCache* cache = [_dataSource largeIconCache]; std::unique_ptr<favicon_base::LargeIconResult> cached_result = cache->GetCachedResult(URL); if (cached_result) { @@ -199,7 +196,7 @@ // Always call LargeIconService in case the favicon was updated. favicon::LargeIconService* large_icon_service = - IOSChromeLargeIconServiceFactory::GetForBrowserState(self.browserState); + [_dataSource largeIconService]; CGFloat faviconSize = [UIScreen mainScreen].scale * kFaviconSize; CGFloat faviconMinSize = [UIScreen mainScreen].scale * kFaviconMinSize; large_icon_service->GetLargeIconOrFallbackStyle(
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm index 5b3f1c7..33f546cd 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm +++ b/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm
@@ -27,6 +27,7 @@ #import "ios/chrome/browser/ui/commands/generic_chrome_command.h" #include "ios/chrome/browser/ui/commands/ios_command_ids.h" #import "ios/chrome/browser/ui/ntp/google_landing_controller.h" +#import "ios/chrome/browser/ui/ntp/google_landing_mediator.h" #import "ios/chrome/browser/ui/ntp/incognito_panel_controller.h" #import "ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h" #import "ios/chrome/browser/ui/ntp/new_tab_page_view.h" @@ -114,6 +115,8 @@ NewTabPageView* newTabPageView_; + base::scoped_nsobject<GoogleLandingMediator> googleLandingMediator_; + base::scoped_nsobject<RecentTabsPanelController> openTabsController_; // Has the scrollView been initialized. BOOL scrollInitialized_; @@ -533,12 +536,15 @@ [bookmarkController_ setDelegate:self]; } else if (item.identifier == NewTabPage::kMostVisitedPanel) { if (!googleLandingController_) { - googleLandingController_.reset([[GoogleLandingController alloc] - initWithLoader:loader_ + googleLandingController_.reset([[GoogleLandingController alloc] init]); + googleLandingMediator_.reset([[GoogleLandingMediator alloc] + initWithConsumer:googleLandingController_ browserState:browserState_ + loader:loader_ focuser:focuser_ webToolbarDelegate:webToolbarDelegate_ tabModel:tabModel_]); + [googleLandingController_ setDataSource:googleLandingMediator_]; } panelController = googleLandingController_; view = [googleLandingController_ view];
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.h b/ios/chrome/browser/ui/ntp/new_tab_page_header_view.h index edc4dba..b7a2fd7 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.h +++ b/ios/chrome/browser/ui/ntp/new_tab_page_header_view.h
@@ -9,11 +9,7 @@ #import "ios/chrome/browser/ui/toolbar/toolbar_owner.h" -@protocol OmniboxFocuser; -@class TabModel; -@protocol WebToolbarDelegate; - -class ReadingListModel; +@protocol GoogleLandingDataSource; // Header view for the Material Design NTP. The header view contains all views // that are displayed above the list of most visited sites, which includes the @@ -25,10 +21,7 @@ // Creates a NewTabPageToolbarController using the given |toolbarDelegate|, // |focuser| and |readingListModel|, and adds the toolbar view to self. -- (void)addToolbarWithDelegate:(id<WebToolbarDelegate>)toolbarDelegate - focuser:(id<OmniboxFocuser>)focuser - tabModel:(TabModel*)tabModel - readingListModel:(ReadingListModel*)readingListModel; +- (void)addToolbarWithDataSource:(id<GoogleLandingDataSource>)dataSource; // Changes the frame of |searchField| based on its |initialFrame| and the scroll // view's y |offset|. Also adjust the alpha values for |_searchBoxBorder| and
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm b/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm index 5364fe2..4659015 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm +++ b/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm
@@ -9,6 +9,7 @@ #import "ios/chrome/browser/tabs/tab_model.h" #import "ios/chrome/browser/tabs/tab_model_observer.h" #import "ios/chrome/browser/ui/image_util.h" +#import "ios/chrome/browser/ui/ntp/google_landing_data_source.h" #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h" #import "ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.h" #import "ios/chrome/browser/ui/uikit_ui_util.h" @@ -65,19 +66,16 @@ [self addSubview:[_toolbarController view]]; } -- (void)addToolbarWithDelegate:(id<WebToolbarDelegate>)toolbarDelegate - focuser:(id<OmniboxFocuser>)focuser - tabModel:(TabModel*)tabModel - readingListModel:(ReadingListModel*)readingListModel { +- (void)addToolbarWithDataSource:(id<GoogleLandingDataSource>)dataSource { DCHECK(!_toolbarController); - DCHECK(focuser); + DCHECK(dataSource); _toolbarController.reset([[NewTabPageToolbarController alloc] - initWithToolbarDelegate:toolbarDelegate - focuser:focuser]); - _toolbarController.get().readingListModel = readingListModel; + initWithToolbarDelegate:[dataSource toolbarDelegate] + focuser:dataSource]); + _toolbarController.get().readingListModel = [dataSource readingListModel]; [_tabModel removeObserver:self]; - _tabModel.reset([tabModel retain]); + _tabModel.reset([[dataSource tabModel] retain]); [self addTabModelObserver]; UIView* toolbarView = [_toolbarController view];
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm index b5175c90..352000bf 100644 --- a/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm +++ b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm
@@ -5,6 +5,7 @@ #import "ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.h" #import "ios/chrome/browser/ui/ntp/google_landing_controller.h" +#import "ios/chrome/browser/ui/ntp/google_landing_mediator.h" #import "ios/clean/chrome/browser/ui/ntp/ntp_home_mediator.h" #import "ios/shared/chrome/browser/ui/browser_list/browser.h" #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h" @@ -15,23 +16,28 @@ @interface NTPHomeCoordinator () @property(nonatomic, strong) NTPHomeMediator* mediator; +@property(nonatomic, strong) GoogleLandingMediator* googleLandingMediator; @property(nonatomic, strong) GoogleLandingController* viewController; @end @implementation NTPHomeCoordinator @synthesize mediator = _mediator; +@synthesize googleLandingMediator = _googleLandingMediator; @synthesize viewController = _viewController; - (void)start { - // PLACEHOLDER: Re-using old view controllers for now. + // PLACEHOLDER: self.mediator and self.oldMediator should be merged together. self.mediator = [[NTPHomeMediator alloc] init]; self.mediator.dispatcher = static_cast<id>(self.browser->dispatcher()); - self.viewController = [[GoogleLandingController alloc] - initWithLoader:self.mediator + self.viewController = [[GoogleLandingController alloc] init]; + self.googleLandingMediator = [[GoogleLandingMediator alloc] + initWithConsumer:self.viewController browserState:self.browser->browser_state() + loader:self.mediator focuser:self.mediator webToolbarDelegate:nil tabModel:nil]; + self.viewController.dataSource = self.googleLandingMediator; [super start]; }
diff --git a/media/formats/mpeg/mpeg_audio_stream_parser_base.cc b/media/formats/mpeg/mpeg_audio_stream_parser_base.cc index 4c377e6..940d0b62 100644 --- a/media/formats/mpeg/mpeg_audio_stream_parser_base.cc +++ b/media/formats/mpeg/mpeg_audio_stream_parser_base.cc
@@ -54,6 +54,7 @@ AudioCodec audio_codec, int codec_delay) : state_(UNINITIALIZED), + media_log_(nullptr), in_media_segment_(false), start_code_mask_(start_code_mask), audio_codec_(audio_codec),
diff --git a/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom b/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom index adc4e7e..3858330a 100644 --- a/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom +++ b/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom
@@ -45,6 +45,11 @@ AsyncGetSender() => (associated IntegerSender sender); }; +interface IntegerSenderConnectionAtBothEnds { + GetSender(associated IntegerSender& sender); + SetSender(associated IntegerSender sender) => (int32 value); +}; + interface AssociatedPingProvider { GetPing(associated PingService& request); };
diff --git a/mojo/public/js/associated_bindings.js b/mojo/public/js/associated_bindings.js index fdfe3c2..18ac452 100644 --- a/mojo/public/js/associated_bindings.js +++ b/mojo/public/js/associated_bindings.js
@@ -9,9 +9,233 @@ "mojo/public/js/lib/interface_endpoint_handle", ], function(core, types, interfaceEndpointClient, interfaceEndpointHandle) { + var InterfaceEndpointClient = interfaceEndpointClient.InterfaceEndpointClient; + + // --------------------------------------------------------------------------- + + function makeRequest(associatedInterfacePtrInfo) { + var {handle0, handle1} = + interfaceEndpointHandle.createPairPendingAssociation(); + + associatedInterfacePtrInfo.interfaceEndpointHandle = handle0; + associatedInterfacePtrInfo.version = 0; + + var request = new types.AssociatedInterfaceRequest(handle1); + return request; + } + + // --------------------------------------------------------------------------- + + // Operations used to setup/configure an associated interface pointer. + // Exposed as |ptr| field of generated associated interface pointer classes. + // |associatedPtrInfo| could be omitted and passed into bind() later. + // + // Example: + // // IntegerSenderImpl implements mojom.IntegerSender + // function IntegerSenderImpl() { ... } + // IntegerSenderImpl.prototype.echo = function() { ... } + // + // // IntegerSenderConnectionImpl implements mojom.IntegerSenderConnection + // function IntegerSenderConnectionImpl() { + // this.senderBinding_ = null; + // } + // IntegerSenderConnectionImpl.prototype.getSender = function( + // associatedRequest) { + // this.senderBinding_ = new AssociatedBinding(mojom.IntegerSender, + // new IntegerSenderImpl(), + // associatedRequest); + // } + // + // var integerSenderConnection = new mojom.IntegerSenderConnectionPtr(); + // var integerSenderConnectionBinding = new Binding( + // mojom.IntegerSenderConnection, + // new IntegerSenderConnectionImpl(), + // bindings.makeRequest(integerSenderConnection)); + // + // // A locally-created associated interface pointer can only be used to + // // make calls when the corresponding associated request is sent over + // // another interface (either the master interface or another + // // associated interface). + // var associatedInterfacePtrInfo = new AssociatedInterfacePtrInfo(); + // var associatedRequest = makeRequest(interfacePtrInfo); + // + // integerSenderConnection.getSender(associatedRequest); + // + // // Create an associated interface and bind the associated handle. + // var integerSender = new mojom.AssociatedIntegerSenderPtr(); + // integerSender.ptr.bind(associatedInterfacePtrInfo); + // integerSender.echo(); + + function AssociatedInterfacePtrController(interfaceType, associatedPtrInfo) { + this.version = 0; + + this.interfaceType_ = interfaceType; + this.interfaceEndpointClient_ = null; + this.proxy_ = null; + + if (associatedPtrInfo) { + this.bind(associatedPtrInfo); + } + } + + AssociatedInterfacePtrController.prototype.bind = function( + associatedPtrInfo) { + this.reset(); + this.version = associatedPtrInfo.version; + + this.interfaceEndpointClient_ = new InterfaceEndpointClient( + associatedPtrInfo.interfaceEndpointHandle); + + this.interfaceEndpointClient_ .setPayloadValidators([ + this.interfaceType_.validateResponse]); + this.proxy_ = new this.interfaceType_.proxyClass( + this.interfaceEndpointClient_); + }; + + AssociatedInterfacePtrController.prototype.isBound = function() { + return this.interfaceEndpointClient_ !== null; + }; + + AssociatedInterfacePtrController.prototype.reset = function() { + this.version = 0; + if (this.interfaceEndpointClient_) { + this.interfaceEndpointClient_.close(); + this.interfaceEndpointClient_ = null; + } + if (this.proxy_) { + this.proxy_ = null; + } + }; + + AssociatedInterfacePtrController.prototype.resetWithReason = function( + reason) { + if (this.isBound()) { + this.interfaceEndpointClient_.close(reason); + this.interfaceEndpointClient_ = null; + } + this.reset(); + }; + + AssociatedInterfacePtrController.prototype.setConnectionErrorHandler = + function(callback) { + if (!this.isBound()) { + throw new Error("Cannot set connection error handler if not bound."); + } + + this.interfaceEndpointClient_.setConnectionErrorHandler(callback); + }; + + AssociatedInterfacePtrController.prototype.passInterface = function() { + if (!this.isBound()) { + return new types.AssociatedInterfacePtrInfo(null); + } + + var result = new types.AssociatedInterfacePtrInfo( + this.interfaceEndpointClient_.passHandle(), this.version); + this.reset(); + return result; + }; + + AssociatedInterfacePtrController.prototype.getProxy = function() { + return this.proxy_; + }; + + AssociatedInterfacePtrController.prototype.queryVersion = function() { + function onQueryVersion(version) { + this.version = version; + return version; + } + + return this.interfaceEndpointClient_.queryVersion().then( + onQueryVersion.bind(this)); + }; + + AssociatedInterfacePtrController.prototype.requireVersion = function( + version) { + if (this.version >= version) { + return; + } + this.version = version; + this.interfaceEndpointClient_.requireVersion(version); + }; + + // --------------------------------------------------------------------------- + + // |associatedInterfaceRequest| could be omitted and passed into bind() + // later. + function AssociatedBinding(interfaceType, impl, associatedInterfaceRequest) { + this.interfaceType_ = interfaceType; + this.impl_ = impl; + this.interfaceEndpointClient_ = null; + this.stub_ = null; + + if (associatedInterfaceRequest) { + this.bind(associatedInterfaceRequest); + } + } + + AssociatedBinding.prototype.isBound = function() { + return this.interfaceEndpointClient_ !== null; + }; + + AssociatedBinding.prototype.bind = function(associatedInterfaceRequest) { + this.close(); + + this.stub_ = new this.interfaceType_.stubClass(this.impl_); + this.interfaceEndpointClient_ = new InterfaceEndpointClient( + associatedInterfaceRequest.interfaceEndpointHandle, this.stub_, + this.interfaceType_.kVersion); + + this.interfaceEndpointClient_ .setPayloadValidators([ + this.interfaceType_.validateRequest]); + }; + + + AssociatedBinding.prototype.close = function() { + if (!this.isBound()) { + return; + } + + if (this.interfaceEndpointClient_) { + this.interfaceEndpointClient_.close(); + this.interfaceEndpointClient_ = null; + } + + this.stub_ = null; + }; + + AssociatedBinding.prototype.closeWithReason = function(reason) { + if (this.interfaceEndpointClient_) { + this.interfaceEndpointClient_.close(reason); + this.interfaceEndpointClient_ = null; + } + this.close(); + }; + + AssociatedBinding.prototype.setConnectionErrorHandler = function(callback) { + if (!this.isBound()) { + throw new Error("Cannot set connection error handler if not bound."); + } + this.interfaceEndpointClient_.setConnectionErrorHandler(callback); + }; + + AssociatedBinding.prototype.unbind = function() { + if (!this.isBound()) { + return new types.AssociatedInterfaceRequest(null); + } + + var result = new types.AssociatedInterfaceRequest( + this.interfaceEndpointClient_.passHandle()); + this.close(); + return result; + }; + var exports = {}; exports.AssociatedInterfacePtrInfo = types.AssociatedInterfacePtrInfo; exports.AssociatedInterfaceRequest = types.AssociatedInterfaceRequest; + exports.makeRequest = makeRequest; + exports.AssociatedInterfacePtrController = AssociatedInterfacePtrController; + exports.AssociatedBinding = AssociatedBinding; return exports; });
diff --git a/mojo/public/js/bindings.js b/mojo/public/js/bindings.js index a944e2f7..ed00554 100644 --- a/mojo/public/js/bindings.js +++ b/mojo/public/js/bindings.js
@@ -53,7 +53,7 @@ }; InterfacePtrController.prototype.isBound = function() { - return this.router_ !== null || this.handle_ !== null; + return this.interfaceEndpointClient_ !== null || this.handle_ !== null; }; // Although users could just discard the object, reset() closes the pipe @@ -77,9 +77,11 @@ }; InterfacePtrController.prototype.resetWithReason = function(reason) { - this.configureProxyIfNecessary_(); - this.interfaceEndpointClient_.close(reason); - this.interfaceEndpointClient_ = null; + if (this.isBound()) { + this.configureProxyIfNecessary_(); + this.interfaceEndpointClient_.close(reason); + this.interfaceEndpointClient_ = null; + } this.reset(); }; @@ -123,12 +125,11 @@ if (!this.handle_) return; - this.router_ = new router.Router(this.handle_); + this.router_ = new router.Router(this.handle_, true); this.handle_ = null; this.interfaceEndpointClient_ = new InterfaceEndpointClient( - this.router_.createLocalEndpointHandle(types.kMasterInterfaceId), - this.router_); + this.router_.createLocalEndpointHandle(types.kMasterInterfaceId)); this.interfaceEndpointClient_ .setPayloadValidators([ this.interfaceType_.validateResponse]); @@ -207,8 +208,8 @@ this.stub_ = new this.interfaceType_.stubClass(this.impl_); this.interfaceEndpointClient_ = new InterfaceEndpointClient( this.router_.createLocalEndpointHandle(types.kMasterInterfaceId), - this.router_, this.interfaceType_.kVersion); - this.interfaceEndpointClient_.setIncomingReceiver(this.stub_); + this.stub_, this.interfaceType_.kVersion); + this.interfaceEndpointClient_ .setPayloadValidators([ this.interfaceType_.validateRequest]); }; @@ -235,8 +236,7 @@ this.close(); }; - Binding.prototype.setConnectionErrorHandler - = function(callback) { + Binding.prototype.setConnectionErrorHandler = function(callback) { if (!this.isBound()) { throw new Error("Cannot set connection error handler if not bound."); }
diff --git a/mojo/public/js/codec.js b/mojo/public/js/codec.js index b78aac2..a57e94f0 100644 --- a/mojo/public/js/codec.js +++ b/mojo/public/js/codec.js
@@ -43,9 +43,10 @@ // Decoder ------------------------------------------------------------------ - function Decoder(buffer, handles, base) { + function Decoder(buffer, handles, associatedEndpointHandles, base) { this.buffer = buffer; this.handles = handles; + this.associatedEndpointHandles = associatedEndpointHandles; this.base = base; this.next = base; } @@ -129,13 +130,18 @@ }; Decoder.prototype.decodeAndCreateDecoder = function(pointer) { - return new Decoder(this.buffer, this.handles, pointer); + return new Decoder(this.buffer, this.handles, + this.associatedEndpointHandles, pointer); }; Decoder.prototype.decodeHandle = function() { return this.handles[this.readUint32()] || null; }; + Decoder.prototype.decodeAssociatedEndpointHandle = function() { + return this.associatedEndpointHandles[this.readUint32()] || null; + }; + Decoder.prototype.decodeString = function() { var numberOfBytes = this.readUint32(); var numberOfElements = this.readUint32(); @@ -214,9 +220,10 @@ // Encoder ------------------------------------------------------------------ - function Encoder(buffer, handles, base) { + function Encoder(buffer, handles, associatedEndpointHandles, base) { this.buffer = buffer; this.handles = handles; + this.associatedEndpointHandles = associatedEndpointHandles; this.base = base; this.next = base; } @@ -303,7 +310,8 @@ Encoder.prototype.createAndEncodeEncoder = function(size) { var pointer = this.buffer.alloc(align(size)); this.encodePointer(pointer); - return new Encoder(this.buffer, this.handles, pointer); + return new Encoder(this.buffer, this.handles, + this.associatedEndpointHandles, pointer); }; Encoder.prototype.encodeHandle = function(handle) { @@ -315,6 +323,15 @@ } }; + Encoder.prototype.encodeAssociatedEndpointHandle = function(endpointHandle) { + if (endpointHandle) { + this.associatedEndpointHandles.push(endpointHandle); + this.writeUint32(this.associatedEndpointHandles.length - 1); + } else { + this.writeUint32(kEncodedInvalidHandleValue); + } + }; + Encoder.prototype.encodeString = function(val) { var base = this.next + kArrayHeaderSize; var numberOfElements = unicode.encodeUtf8String( @@ -436,9 +453,14 @@ var kMessageExpectsResponse = 1 << 0; var kMessageIsResponse = 1 << 1; - function Message(buffer, handles) { + function Message(buffer, handles, associatedEndpointHandles) { + if (associatedEndpointHandles === undefined) { + associatedEndpointHandles = []; + } + this.buffer = buffer; this.handles = handles; + this.associatedEndpointHandles = associatedEndpointHandles; } Message.prototype.getHeaderNumBytes = function() { @@ -467,6 +489,7 @@ } var decoder = new Decoder(this.buffer, this.handles, + this.associatedEndpointHandles, kMessagePayloadInterfaceIdsPointerOffset); var payloadInterfaceIds = decoder.decodeArrayPointer(Uint32); return payloadInterfaceIds; @@ -489,10 +512,70 @@ this.buffer.setUint32(kMessageInterfaceIdOffset, interfaceId); }; + Message.prototype.setPayloadInterfaceIds_ = function(payloadInterfaceIds) { + if (this.getHeaderVersion() < 2) { + throw new Error( + "Version of message does not support payload interface ids"); + } - // MessageBuilder ----------------------------------------------------------- + var decoder = new Decoder(this.buffer, this.handles, + this.associatedEndpointHandles, + kMessagePayloadInterfaceIdsPointerOffset); + var payloadInterfaceIdsOffset = decoder.decodePointer(); + var encoder = new Encoder(this.buffer, this.handles, + this.associatedEndpointHandles, + payloadInterfaceIdsOffset); + encoder.encodeArray(Uint32, payloadInterfaceIds); + }; - function MessageBuilder(messageName, payloadSize) { + Message.prototype.serializeAssociatedEndpointHandles = function( + associatedGroupController) { + if (this.associatedEndpointHandles.length > 0) { + if (this.getHeaderVersion() < 2) { + throw new Error( + "Version of message does not support associated endpoint handles"); + } + + var data = []; + for (var i = 0; i < this.associatedEndpointHandles.length; i++) { + var handle = this.associatedEndpointHandles[i]; + data.push(associatedGroupController.associateInterface(handle)); + } + this.associatedEndpointHandles = []; + this.setPayloadInterfaceIds_(data); + } + }; + + Message.prototype.deserializeAssociatedEndpointHandles = function( + associatedGroupController) { + if (this.getHeaderVersion() < 2) { + return true; + } + + this.associatedEndpointHandles = []; + var ids = this.getPayloadInterfaceIds(); + + var result = true; + for (var i = 0; i < ids.length; i++) { + var handle = associatedGroupController.createLocalEndpointHandle(ids[i]); + if (types.isValidInterfaceId(ids[i]) && !handle.isValid()) { + // |ids[i]| itself is valid but handle creation failed. In that case, + // mark deserialization as failed but continue to deserialize the + // rest of handles. + result = false; + } + this.associatedEndpointHandles.push(handle); + ids[i] = types.kInvalidInterfaceId; + } + + this.setPayloadInterfaceIds_(ids); + return result; + }; + + + // MessageV0Builder --------------------------------------------------------- + + function MessageV0Builder(messageName, payloadSize) { // Currently, we don't compute the payload size correctly ahead of time. // Instead, we resize the buffer at the end. var numberOfBytes = kMessageV0HeaderSize + payloadSize; @@ -507,16 +590,16 @@ encoder.writeUint32(0); // padding. } - MessageBuilder.prototype.createEncoder = function(size) { + MessageV0Builder.prototype.createEncoder = function(size) { var pointer = this.buffer.alloc(size); - return new Encoder(this.buffer, this.handles, pointer); + return new Encoder(this.buffer, this.handles, [], pointer); }; - MessageBuilder.prototype.encodeStruct = function(cls, val) { + MessageV0Builder.prototype.encodeStruct = function(cls, val) { cls.encode(this.createEncoder(cls.encodedSize), val); }; - MessageBuilder.prototype.finish = function() { + MessageV0Builder.prototype.finish = function() { // TODO(abarth): Rather than resizing the buffer at the end, we could // compute the size we need ahead of time, like we do in C++. this.buffer.trim(); @@ -527,9 +610,9 @@ return message; }; - // MessageWithRequestIDBuilder ----------------------------------------------- + // MessageV1Builder ----------------------------------------------- - function MessageWithRequestIDBuilder(messageName, payloadSize, flags, + function MessageV1Builder(messageName, payloadSize, flags, requestID) { // Currently, we don't compute the payload size correctly ahead of time. // Instead, we resize the buffer at the end. @@ -546,16 +629,71 @@ encoder.writeUint64(requestID); } - MessageWithRequestIDBuilder.prototype = - Object.create(MessageBuilder.prototype); + MessageV1Builder.prototype = + Object.create(MessageV0Builder.prototype); - MessageWithRequestIDBuilder.prototype.constructor = - MessageWithRequestIDBuilder; + MessageV1Builder.prototype.constructor = + MessageV1Builder; + + // MessageV2 ----------------------------------------------- + + function MessageV2Builder(messageName, payloadSize, flags, requestID) { + // Currently, we don't compute the payload size correctly ahead of time. + // Instead, we resize the buffer at the end. + var numberOfBytes = kMessageV2HeaderSize + payloadSize; + this.buffer = new buffer.Buffer(numberOfBytes); + this.handles = []; + + this.payload = null; + this.associatedEndpointHandles = []; + + this.encoder = this.createEncoder(kMessageV2HeaderSize); + this.encoder.writeUint32(kMessageV2HeaderSize); + this.encoder.writeUint32(2); // version. + // Gets set to an appropriate interfaceId for the endpoint by the Router. + this.encoder.writeUint32(0); // interface ID. + this.encoder.writeUint32(messageName); + this.encoder.writeUint32(flags); + this.encoder.writeUint32(0); // padding. + this.encoder.writeUint64(requestID); + } + + MessageV2Builder.prototype.createEncoder = function(size) { + var pointer = this.buffer.alloc(size); + return new Encoder(this.buffer, this.handles, + this.associatedEndpointHandles, pointer); + }; + + MessageV2Builder.prototype.setPayload = function(cls, val) { + this.payload = {cls: cls, val: val}; + }; + + MessageV2Builder.prototype.finish = function() { + if (!this.payload) { + throw new Error("Payload needs to be set before calling finish"); + } + + this.encoder.encodeStructPointer(this.payload.cls, this.payload.val); + this.encoder.encodeArrayPointer(Uint32, + new Array(this.associatedEndpointHandles.length)); + + this.buffer.trim(); + var message = new Message(this.buffer, this.handles, + this.associatedEndpointHandles); + this.buffer = null; + this.handles = null; + this.encoder = null; + this.payload = null; + this.associatedEndpointHandles = null; + + return message; + }; // MessageReader ------------------------------------------------------------ function MessageReader(message) { - this.decoder = new Decoder(message.buffer, message.handles, 0); + this.decoder = new Decoder(message.buffer, message.handles, + message.associatedEndpointHandles, 0); var messageHeaderSize = this.decoder.readUint32(); this.payloadSize = message.buffer.byteLength - messageHeaderSize; var version = this.decoder.readUint32(); @@ -858,12 +996,31 @@ AssociatedInterfacePtrInfo.prototype.encodedSize = 8; + AssociatedInterfacePtrInfo.decode = function(decoder) { + return new types.AssociatedInterfacePtrInfo( + decoder.decodeAssociatedEndpointHandle(), decoder.readUint32()); + }; + + AssociatedInterfacePtrInfo.encode = function(encoder, val) { + var associatedinterfacePtrInfo = + val ? val : new types.AssociatedInterfacePtrInfo(null, 0); + encoder.encodeAssociatedEndpointHandle( + associatedinterfacePtrInfo.interfaceEndpointHandle); + encoder.writeUint32(associatedinterfacePtrInfo.version); + }; + function NullableAssociatedInterfacePtrInfo() { } NullableAssociatedInterfacePtrInfo.encodedSize = AssociatedInterfacePtrInfo.encodedSize; + NullableAssociatedInterfacePtrInfo.decode = + AssociatedInterfacePtrInfo.decode; + + NullableAssociatedInterfacePtrInfo.encode = + AssociatedInterfacePtrInfo.encode; + function InterfaceRequest() { } @@ -889,6 +1046,16 @@ function AssociatedInterfaceRequest() { } + AssociatedInterfaceRequest.decode = function(decoder) { + var handle = decoder.decodeAssociatedEndpointHandle(); + return new types.AssociatedInterfaceRequest(handle); + }; + + AssociatedInterfaceRequest.encode = function(encoder, val) { + encoder.encodeAssociatedEndpointHandle( + val ? val.interfaceEndpointHandle : null); + }; + AssociatedInterfaceRequest.encodedSize = 4; function NullableAssociatedInterfaceRequest() { @@ -897,6 +1064,12 @@ NullableAssociatedInterfaceRequest.encodedSize = AssociatedInterfaceRequest.encodedSize; + NullableAssociatedInterfaceRequest.decode = + AssociatedInterfaceRequest.decode; + + NullableAssociatedInterfaceRequest.encode = + AssociatedInterfaceRequest.encode; + function MapOf(keyClass, valueClass) { this.keyClass = keyClass; this.valueClass = valueClass; @@ -922,8 +1095,9 @@ exports.align = align; exports.isAligned = isAligned; exports.Message = Message; - exports.MessageBuilder = MessageBuilder; - exports.MessageWithRequestIDBuilder = MessageWithRequestIDBuilder; + exports.MessageV0Builder = MessageV0Builder; + exports.MessageV1Builder = MessageV1Builder; + exports.MessageV2Builder = MessageV2Builder; exports.MessageReader = MessageReader; exports.kArrayHeaderSize = kArrayHeaderSize; exports.kMapStructPayloadSize = kMapStructPayloadSize;
diff --git a/mojo/public/js/lib/control_message_handler.js b/mojo/public/js/lib/control_message_handler.js index 5da306e3..09c0b78c 100644 --- a/mojo/public/js/lib/control_message_handler.js +++ b/mojo/public/js/lib/control_message_handler.js
@@ -75,7 +75,7 @@ var messageName = controlMessages.kRunMessageId; var payloadSize = controlMessages.RunResponseMessageParams.encodedSize; var requestID = reader.requestID; - var builder = new codec.MessageWithRequestIDBuilder(messageName, + var builder = new codec.MessageV1Builder(messageName, payloadSize, codec.kMessageIsResponse, requestID); builder.encodeStruct(controlMessages.RunResponseMessageParams, runResponseMessageParams);
diff --git a/mojo/public/js/lib/control_message_proxy.js b/mojo/public/js/lib/control_message_proxy.js index b6f1d3c8..8cd84c5c3 100644 --- a/mojo/public/js/lib/control_message_proxy.js +++ b/mojo/public/js/lib/control_message_proxy.js
@@ -17,7 +17,7 @@ var messageName = controlMessages.kRunOrClosePipeMessageId; var payloadSize = controlMessages.RunOrClosePipeMessageParams.encodedSize; - var builder = new codec.MessageBuilder(messageName, payloadSize); + var builder = new codec.MessageV0Builder(messageName, payloadSize); builder.encodeStruct(controlMessages.RunOrClosePipeMessageParams, runOrClosePipeMessageParams); var message = builder.finish(); @@ -66,7 +66,7 @@ var messageName = controlMessages.kRunMessageId; var payloadSize = controlMessages.RunMessageParams.encodedSize; // |requestID| is set to 0, but is later properly set by Router. - var builder = new codec.MessageWithRequestIDBuilder(messageName, + var builder = new codec.MessageV1Builder(messageName, payloadSize, codec.kMessageExpectsResponse, 0); builder.encodeStruct(controlMessages.RunMessageParams, runMessageParams); var message = builder.finish();
diff --git a/mojo/public/js/lib/interface_endpoint_client.js b/mojo/public/js/lib/interface_endpoint_client.js index 631c52e..b74b6d2 100644 --- a/mojo/public/js/lib/interface_endpoint_client.js +++ b/mojo/public/js/lib/interface_endpoint_client.js
@@ -21,8 +21,8 @@ var ControlMessageHandler = controlMessageHandler.ControlMessageHandler; var ControlMessageProxy = controlMessageProxy.ControlMessageProxy; var MessageReader = codec.MessageReader; - var Validator = validator.Validator; var InterfaceEndpointHandle = interfaceEndpointHandle.InterfaceEndpointHandle; + var AssociationEvent = interfaceEndpointHandle.AssociationEvent; function InterfaceEndpointClient(interfaceEndpointHandle, receiver, interfaceVersion) { @@ -62,12 +62,10 @@ InterfaceEndpointClient.prototype.onAssociationEvent = function( associationEvent) { - if (associationEvent === - InterfaceEndpointHandle.AssociationEvent.ASSOCIATED) { + if (associationEvent === AssociationEvent.ASSOCIATED) { this.initControllerIfNecessary_(); } else if (associationEvent === - InterfaceEndpointHandle.AssociationEvent - .PEER_CLOSED_BEFORE_ASSOCIATION) { + AssociationEvent.PEER_CLOSED_BEFORE_ASSOCIATION) { timer.createOneShot(0, this.notifyError.bind(this, this.handle_.disconnectReason())); } @@ -96,6 +94,11 @@ }; InterfaceEndpointClient.prototype.accept = function(message) { + if (message.associatedEndpointHandles.length > 0) { + message.serializeAssociatedEndpointHandles( + this.handle_.groupController()); + } + if (this.encounteredError_) { return false; } @@ -106,6 +109,11 @@ InterfaceEndpointClient.prototype.acceptAndExpectResponse = function( message) { + if (message.associatedEndpointHandles.length > 0) { + message.serializeAssociatedEndpointHandles( + this.handle_.groupController()); + } + if (this.encounteredError_) { return Promise.reject(); } @@ -144,10 +152,9 @@ this.connectionErrorHandler_ = handler; }; - InterfaceEndpointClient.prototype.handleIncomingMessage_ = function( - message) { + InterfaceEndpointClient.prototype.handleIncomingMessage = function(message, + messageValidator) { var noError = validator.validationError.NONE; - var messageValidator = new Validator(message); var err = noError; for (var i = 0; err === noError && i < this.payloadValidators_.length; ++i) err = this.payloadValidators_[i](messageValidator);
diff --git a/mojo/public/js/lib/interface_endpoint_handle.js b/mojo/public/js/lib/interface_endpoint_handle.js index f48b89ba..dda951a 100644 --- a/mojo/public/js/lib/interface_endpoint_handle.js +++ b/mojo/public/js/lib/interface_endpoint_handle.js
@@ -92,6 +92,20 @@ } }; + State.prototype.notifyAssociation = function(interfaceId, + peerGroupController) { + var cachedPeerState = this.peerState_; + this.peerState_ = null; + + this.pendingAssociation = false; + + if (cachedPeerState) { + cachedPeerState.onAssociated(interfaceId, peerGroupController); + return true; + } + return false; + }; + State.prototype.onAssociated = function(interfaceId, associatedGroupController) { if (!this.pendingAssociation) { @@ -117,6 +131,14 @@ AssociationEvent.PEER_CLOSED_BEFORE_ASSOCIATION); }; + function createPairPendingAssociation() { + var handle0 = new InterfaceEndpointHandle(); + var handle1 = new InterfaceEndpointHandle(); + handle0.state_.initPendingState(handle1.state_); + handle1.state_.initPendingState(handle0.state_); + return {handle0: handle0, handle1: handle1}; + } + function InterfaceEndpointHandle(interfaceId, associatedGroupController) { this.state_ = new State(interfaceId, associatedGroupController); } @@ -146,13 +168,20 @@ this.state_.setAssociationEventHandler(handler); }; + InterfaceEndpointHandle.prototype.notifyAssociation = function(interfaceId, + peerGroupController) { + return this.state_.notifyAssociation(interfaceId, peerGroupController); + }; + InterfaceEndpointHandle.prototype.reset = function(reason) { this.state_.close(reason); this.state_ = new State(); }; var exports = {}; + exports.AssociationEvent = AssociationEvent; exports.InterfaceEndpointHandle = InterfaceEndpointHandle; + exports.createPairPendingAssociation = createPairPendingAssociation; return exports; });
diff --git a/mojo/public/js/lib/pipe_control_message_proxy.js b/mojo/public/js/lib/pipe_control_message_proxy.js index 4b8e7a20..71091d0 100644 --- a/mojo/public/js/lib/pipe_control_message_proxy.js +++ b/mojo/public/js/lib/pipe_control_message_proxy.js
@@ -17,7 +17,7 @@ var payloadSize = pipeControlMessages.RunOrClosePipeMessageParams.encodedSize; - var builder = new codec.MessageBuilder(messageName, payloadSize); + var builder = new codec.MessageV0Builder(messageName, payloadSize); builder.encodeStruct(pipeControlMessages.RunOrClosePipeMessageParams, runOrClosePipeMessageParams); var message = builder.finish();
diff --git a/mojo/public/js/new_bindings/codec.js b/mojo/public/js/new_bindings/codec.js index 339fc16..ba07cb7 100644 --- a/mojo/public/js/new_bindings/codec.js +++ b/mojo/public/js/new_bindings/codec.js
@@ -464,9 +464,9 @@ }; - // MessageBuilder ----------------------------------------------------------- + // MessageV0Builder --------------------------------------------------------- - function MessageBuilder(messageName, payloadSize) { + function MessageV0Builder(messageName, payloadSize) { // Currently, we don't compute the payload size correctly ahead of time. // Instead, we resize the buffer at the end. var numberOfBytes = kMessageHeaderSize + payloadSize; @@ -481,16 +481,16 @@ encoder.writeUint32(0); // padding. } - MessageBuilder.prototype.createEncoder = function(size) { + MessageV0Builder.prototype.createEncoder = function(size) { var pointer = this.buffer.alloc(size); return new Encoder(this.buffer, this.handles, pointer); }; - MessageBuilder.prototype.encodeStruct = function(cls, val) { + MessageV0Builder.prototype.encodeStruct = function(cls, val) { cls.encode(this.createEncoder(cls.encodedSize), val); }; - MessageBuilder.prototype.finish = function() { + MessageV0Builder.prototype.finish = function() { // TODO(abarth): Rather than resizing the buffer at the end, we could // compute the size we need ahead of time, like we do in C++. this.buffer.trim(); @@ -501,9 +501,9 @@ return message; }; - // MessageWithRequestIDBuilder ----------------------------------------------- + // MessageV1Builder ----------------------------------------------- - function MessageWithRequestIDBuilder(messageName, payloadSize, flags, + function MessageV1Builder(messageName, payloadSize, flags, requestID) { // Currently, we don't compute the payload size correctly ahead of time. // Instead, we resize the buffer at the end. @@ -520,11 +520,11 @@ encoder.writeUint64(requestID); } - MessageWithRequestIDBuilder.prototype = - Object.create(MessageBuilder.prototype); + MessageV1Builder.prototype = + Object.create(MessageV0Builder.prototype); - MessageWithRequestIDBuilder.prototype.constructor = - MessageWithRequestIDBuilder; + MessageV1Builder.prototype.constructor = + MessageV1Builder; // MessageReader ------------------------------------------------------------ @@ -877,8 +877,8 @@ internal.align = align; internal.isAligned = isAligned; internal.Message = Message; - internal.MessageBuilder = MessageBuilder; - internal.MessageWithRequestIDBuilder = MessageWithRequestIDBuilder; + internal.MessageV0Builder = MessageV0Builder; + internal.MessageV1Builder = MessageV1Builder; internal.MessageReader = MessageReader; internal.kArrayHeaderSize = kArrayHeaderSize; internal.kMapStructPayloadSize = kMapStructPayloadSize;
diff --git a/mojo/public/js/new_bindings/lib/control_message_handler.js b/mojo/public/js/new_bindings/lib/control_message_handler.js index 3f122fb..991291eb 100644 --- a/mojo/public/js/new_bindings/lib/control_message_handler.js +++ b/mojo/public/js/new_bindings/lib/control_message_handler.js
@@ -72,7 +72,7 @@ var payloadSize = mojo.interface_control2.RunResponseMessageParams.encodedSize; var requestID = reader.requestID; - var builder = new internal.MessageWithRequestIDBuilder(messageName, + var builder = new internal.MessageV1Builder(messageName, payloadSize, internal.kMessageIsResponse, requestID); builder.encodeStruct(mojo.interface_control2.RunResponseMessageParams, runResponseMessageParams);
diff --git a/mojo/public/js/new_bindings/lib/control_message_proxy.js b/mojo/public/js/new_bindings/lib/control_message_proxy.js index 1d57557..9d646752 100644 --- a/mojo/public/js/new_bindings/lib/control_message_proxy.js +++ b/mojo/public/js/new_bindings/lib/control_message_proxy.js
@@ -9,7 +9,7 @@ var messageName = mojo.interface_control2.kRunOrClosePipeMessageId; var payloadSize = mojo.interface_control2.RunOrClosePipeMessageParams.encodedSize; - var builder = new internal.MessageBuilder(messageName, payloadSize); + var builder = new internal.MessageV0Builder(messageName, payloadSize); builder.encodeStruct(mojo.interface_control2.RunOrClosePipeMessageParams, runOrClosePipeMessageParams); var message = builder.finish(); @@ -58,7 +58,7 @@ var messageName = mojo.interface_control2.kRunMessageId; var payloadSize = mojo.interface_control2.RunMessageParams.encodedSize; // |requestID| is set to 0, but is later properly set by Router. - var builder = new internal.MessageWithRequestIDBuilder(messageName, + var builder = new internal.MessageV1Builder(messageName, payloadSize, internal.kMessageExpectsResponse, 0); builder.encodeStruct(mojo.interface_control2.RunMessageParams, runMessageParams);
diff --git a/mojo/public/js/router.js b/mojo/public/js/router.js index 89d9a2f6..401a222 100644 --- a/mojo/public/js/router.js +++ b/mojo/public/js/router.js
@@ -74,11 +74,48 @@ this.setInterfaceIdNamespaceBit_ = setInterfaceIdNamespaceBit; this.controlMessageHandler_ = new PipeControlMessageHandler(this); this.controlMessageProxy_ = new PipeControlMessageProxy(this.connector_); - this.nextInterfaceIdValue = 1; + this.nextInterfaceIdValue_ = 1; this.encounteredError_ = false; this.endpoints_ = new Map(); } + Router.prototype.associateInterface = function(handleToSend) { + if (!handleToSend.pendingAssociation()) { + return types.kInvalidInterfaceId; + } + + var id = 0; + do { + if (this.nextInterfaceIdValue_ >= types.kInterfaceIdNamespaceMask) { + this.nextInterfaceIdValue_ = 1; + } + id = this.nextInterfaceIdValue_++; + if (this.setInterfaceIdNamespaceBit_) { + id += types.kInterfaceIdNamespaceMask; + } + } while (this.endpoints_.has(id)); + + var endpoint = new InterfaceEndpoint(this, id); + this.endpoints_.set(id, endpoint); + if (this.encounteredError_) { + this.updateEndpointStateMayRemove(endpoint, + EndpointStateUpdateType.PEER_ENDPOINT_CLOSED); + } + endpoint.handleCreated = true; + + if (!handleToSend.notifyAssociation(id, this)) { + // The peer handle of |handleToSend|, which is supposed to join this + // associated group, has been closed. + this.updateEndpointStateMayRemove(endpoint, + EndpointStateUpdateType.ENDPOINT_CLOSED); + + pipeControlMessageproxy.notifyPeerEndpointClosed(id, + handleToSend.disconnectReason()); + } + + return id; + }; + Router.prototype.attachEndpointClient = function( interfaceEndpointHandle, interfaceEndpointClient) { check(types.isValidInterfaceId(interfaceEndpointHandle.id())); @@ -149,21 +186,25 @@ var ok = false; if (err !== validator.validationError.NONE) { validator.reportValidationError(err); - } else if (controlMessageHandler.isPipeControlMessage(message)) { - ok = this.controlMessageHandler_.accept(message); - } else { - var interfaceId = message.getInterfaceId(); - var endpoint = this.endpoints_.get(interfaceId); - if (!endpoint || endpoint.closed) { - return true; - } + } else if (message.deserializeAssociatedEndpointHandles(this)) { + if (controlMessageHandler.isPipeControlMessage(message)) { + ok = this.controlMessageHandler_.accept(message); + } else { + var interfaceId = message.getInterfaceId(); + var endpoint = this.endpoints_.get(interfaceId); + if (!endpoint || endpoint.closed) { + return true; + } - if (!endpoint.client) { - // We need to wait until a client is attached in order to dispatch - // further messages. - return false; + if (!endpoint.client) { + // We need to wait until a client is attached in order to dispatch + // further messages. + // TODO(wangjimmy): Cache the message and send when the appropriate + // endpoint client is attached. + return false; + } + ok = endpoint.client.handleIncomingMessage(message, messageValidator); } - ok = endpoint.client.handleIncomingMessage_(message); } if (!ok) {
diff --git a/mojo/public/js/validator.js b/mojo/public/js/validator.js index 037f3f46..4abc359 100644 --- a/mojo/public/js/validator.js +++ b/mojo/public/js/validator.js
@@ -109,6 +109,8 @@ function isNullable(type) { return type === codec.NullableString || type === codec.NullableHandle || + type === codec.NullableAssociatedInterfacePtrInfo || + type === codec.NullableAssociatedInterfaceRequest || type === codec.NullableInterface || type === codec.NullableInterfaceRequest || type instanceof codec.NullableArrayOf ||
diff --git a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl index 11e319c1..85d95d6a 100644 --- a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
@@ -7,6 +7,16 @@ handleOrPtrInfo); } + function Associated{{interface.name}}Ptr(associatedInterfacePtrInfo) { + this.ptr = new associatedBindings.AssociatedInterfacePtrController( + {{interface.name}}, associatedInterfacePtrInfo); + } + + Associated{{interface.name}}Ptr.prototype = + Object.create({{interface.name}}Ptr.prototype); + Associated{{interface.name}}Ptr.prototype.constructor = + Associated{{interface.name}}Ptr; + function {{interface.name}}Proxy(receiver) { this.receiver_ = receiver; } @@ -28,19 +38,34 @@ {%- endfor %} {%- if method.response_parameters == None %} - var builder = new codec.MessageBuilder( +{%- if method|method_passes_associated_kinds and not use_new_js_bindings %} + var builder = new codec.MessageV2Builder( + k{{interface.name}}_{{method.name}}_Name, + codec.align({{interface.name}}_{{method.name}}_Params.encodedSize)); + builder.setPayload({{interface.name}}_{{method.name}}_Params, params); +{%- else %} + var builder = new codec.MessageV0Builder( k{{interface.name}}_{{method.name}}_Name, codec.align({{interface.name}}_{{method.name}}_Params.encodedSize)); builder.encodeStruct({{interface.name}}_{{method.name}}_Params, params); +{%- endif %} var message = builder.finish(); this.receiver_.accept(message); {%- else %} return new Promise(function(resolve, reject) { - var builder = new codec.MessageWithRequestIDBuilder( +{%- if method|method_passes_associated_kinds and not use_new_js_bindings %} + var builder = new codec.MessageV2Builder( + k{{interface.name}}_{{method.name}}_Name, + codec.align({{interface.name}}_{{method.name}}_Params.encodedSize), + codec.kMessageExpectsResponse, 0); + builder.setPayload({{interface.name}}_{{method.name}}_Params, params); +{%- else %} + var builder = new codec.MessageV1Builder( k{{interface.name}}_{{method.name}}_Name, codec.align({{interface.name}}_{{method.name}}_Params.encodedSize), codec.kMessageExpectsResponse, 0); builder.encodeStruct({{interface.name}}_{{method.name}}_Params, params); +{%- endif %} var message = builder.finish(); this.receiver_.acceptAndExpectResponse(message).then(function(message) { var reader = new codec.MessageReader(message); @@ -102,12 +127,22 @@ {%- for parameter in method.response_parameters %} responseParams.{{parameter.name}} = response.{{parameter.name}}; {%- endfor %} - var builder = new codec.MessageWithRequestIDBuilder( +{%- if method|method_passes_associated_kinds and not use_new_js_bindings %} + var builder = new codec.MessageV2Builder( + k{{interface.name}}_{{method.name}}_Name, + codec.align({{interface.name}}_{{method.name}}_ResponseParams + .encodedSize), + codec.kMessageIsResponse, reader.requestID); + builder.setPayload({{interface.name}}_{{method.name}}_ResponseParams, + responseParams); +{%- else %} + var builder = new codec.MessageV1Builder( k{{interface.name}}_{{method.name}}_Name, codec.align({{interface.name}}_{{method.name}}_ResponseParams.encodedSize), codec.kMessageIsResponse, reader.requestID); builder.encodeStruct({{interface.name}}_{{method.name}}_ResponseParams, responseParams); +{%- endif %} var message = builder.finish(); responder.accept(message); });
diff --git a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl index a119ee9..a18c333e 100644 --- a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl
@@ -46,4 +46,5 @@ {%- for interface in interfaces %} exports.{{interface.name}} = {{interface.name}}; exports.{{interface.name}}Ptr = {{interface.name}}Ptr; + exports.Associated{{interface.name}}Ptr = Associated{{interface.name}}Ptr; {%- endfor %}
diff --git a/mojo/public/tools/bindings/generators/mojom_js_generator.py b/mojo/public/tools/bindings/generators/mojom_js_generator.py index a42bd59..28b709a 100644 --- a/mojo/public/tools/bindings/generators/mojom_js_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py
@@ -365,6 +365,7 @@ "is_struct_pointer_field": IsStructPointerField, "is_union_field": IsUnionField, "js_type": JavaScriptType, + "method_passes_associated_kinds": mojom.MethodPassesAssociatedKinds, "payload_size": JavaScriptPayloadSize, "get_relative_path": GetRelativePath, "stylize_method": generator.StudlyCapsToCamel,
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module.py b/mojo/public/tools/bindings/pylib/mojom/generate/module.py index 3a5f188e..8fa4e44 100644 --- a/mojo/public/tools/bindings/pylib/mojom/generate/module.py +++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
@@ -817,7 +817,17 @@ # Finds out whether an interface passes associated interfaces and associated # interface requests. def PassesAssociatedKinds(interface): - def _ContainsAssociatedKinds(kind, visited_kinds): + visited_kinds = set() + for method in interface.methods: + if MethodPassesAssociatedKinds(method, visited_kinds): + return True + return False + + +# Finds out whether a method passes associated interfaces and associated +# interface requests. +def MethodPassesAssociatedKinds(method, visited_kinds=None): + def _ContainsAssociatedKinds(kind): if kind in visited_kinds: # No need to examine the kind again. return False @@ -825,26 +835,27 @@ if IsAssociatedKind(kind): return True if IsArrayKind(kind): - return _ContainsAssociatedKinds(kind.kind, visited_kinds) + return _ContainsAssociatedKinds(kind.kind) if IsStructKind(kind) or IsUnionKind(kind): for field in kind.fields: - if _ContainsAssociatedKinds(field.kind, visited_kinds): + if _ContainsAssociatedKinds(field.kind): return True if IsMapKind(kind): # No need to examine the key kind, only primitive kinds and non-nullable # string are allowed to be key kinds. - return _ContainsAssociatedKinds(kind.value_kind, visited_kinds) + return _ContainsAssociatedKinds(kind.value_kind) return False - visited_kinds = set() - for method in interface.methods: - for param in method.parameters: - if _ContainsAssociatedKinds(param.kind, visited_kinds): + if visited_kinds is None: + visited_kinds = set() + + for param in method.parameters: + if _ContainsAssociatedKinds(param.kind): + return True + if method.response_parameters != None: + for param in method.response_parameters: + if _ContainsAssociatedKinds(param.kind): return True - if method.response_parameters != None: - for param in method.response_parameters: - if _ContainsAssociatedKinds(param.kind, visited_kinds): - return True return False
diff --git a/net/cert/ct_log_verifier.cc b/net/cert/ct_log_verifier.cc index a55921a9..e017e36 100644 --- a/net/cert/ct_log_verifier.cc +++ b/net/cert/ct_log_verifier.cc
@@ -85,7 +85,7 @@ DCHECK(!dns_domain_.empty()); } -bool CTLogVerifier::Verify(const ct::LogEntry& entry, +bool CTLogVerifier::Verify(const ct::SignedEntryData& entry, const ct::SignedCertificateTimestamp& sct) const { if (sct.log_id != key_id()) { DVLOG(1) << "SCT is not signed by this log."; @@ -96,7 +96,7 @@ return false; std::string serialized_log_entry; - if (!ct::EncodeLogEntry(entry, &serialized_log_entry)) { + if (!ct::EncodeSignedEntry(entry, &serialized_log_entry)) { DVLOG(1) << "Unable to serialize entry."; return false; }
diff --git a/net/cert/ct_log_verifier.h b/net/cert/ct_log_verifier.h index 6e3b938..7c03581b 100644 --- a/net/cert/ct_log_verifier.h +++ b/net/cert/ct_log_verifier.h
@@ -60,7 +60,7 @@ const std::string& dns_domain() const { return dns_domain_; } // Verifies that |sct| is valid for |entry| and was signed by this log. - bool Verify(const ct::LogEntry& entry, + bool Verify(const ct::SignedEntryData& entry, const ct::SignedCertificateTimestamp& sct) const; // Verifies that |signed_tree_head| is a valid Signed Tree Head (RFC 6962,
diff --git a/net/cert/ct_log_verifier_unittest.cc b/net/cert/ct_log_verifier_unittest.cc index 9067cb6e3..4ec13ff 100644 --- a/net/cert/ct_log_verifier_unittest.cc +++ b/net/cert/ct_log_verifier_unittest.cc
@@ -389,8 +389,8 @@ } TEST_F(CTLogVerifierTest, VerifiesCertSCT) { - ct::LogEntry cert_entry; - ct::GetX509CertLogEntry(&cert_entry); + ct::SignedEntryData cert_entry; + ct::GetX509CertSignedEntry(&cert_entry); scoped_refptr<ct::SignedCertificateTimestamp> cert_sct; ct::GetX509CertSCT(&cert_sct); @@ -399,8 +399,8 @@ } TEST_F(CTLogVerifierTest, VerifiesPrecertSCT) { - ct::LogEntry precert_entry; - ct::GetPrecertLogEntry(&precert_entry); + ct::SignedEntryData precert_entry; + ct::GetPrecertSignedEntry(&precert_entry); scoped_refptr<ct::SignedCertificateTimestamp> precert_sct; ct::GetPrecertSCT(&precert_sct); @@ -409,8 +409,8 @@ } TEST_F(CTLogVerifierTest, FailsInvalidTimestamp) { - ct::LogEntry cert_entry; - ct::GetX509CertLogEntry(&cert_entry); + ct::SignedEntryData cert_entry; + ct::GetX509CertSignedEntry(&cert_entry); scoped_refptr<ct::SignedCertificateTimestamp> cert_sct; ct::GetX509CertSCT(&cert_sct); @@ -422,8 +422,8 @@ } TEST_F(CTLogVerifierTest, FailsInvalidLogID) { - ct::LogEntry cert_entry; - ct::GetX509CertLogEntry(&cert_entry); + ct::SignedEntryData cert_entry; + ct::GetX509CertSignedEntry(&cert_entry); scoped_refptr<ct::SignedCertificateTimestamp> cert_sct; ct::GetX509CertSCT(&cert_sct);
diff --git a/net/cert/ct_objects_extractor.cc b/net/cert/ct_objects_extractor.cc index 1a6e6e4..7c9eb71 100644 --- a/net/cert/ct_objects_extractor.cc +++ b/net/cert/ct_objects_extractor.cc
@@ -173,9 +173,9 @@ sct_list); } -bool GetPrecertLogEntry(X509Certificate::OSCertHandle leaf, - X509Certificate::OSCertHandle issuer, - LogEntry* result) { +bool GetPrecertSignedEntry(X509Certificate::OSCertHandle leaf, + X509Certificate::OSCertHandle issuer, + SignedEntryData* result) { result->Reset(); bssl::UniquePtr<X509> leaf_x509(OSCertHandleToOpenSSL(leaf)); @@ -228,8 +228,8 @@ if (!asn1::ExtractSPKIFromDERCert(issuer_der, &issuer_key)) return false; - // Fill in the LogEntry. - result->type = ct::LogEntry::LOG_ENTRY_TYPE_PRECERT; + // Fill in the SignedEntryData. + result->type = ct::SignedEntryData::LOG_ENTRY_TYPE_PRECERT; result->tbs_certificate.swap(to_be_signed); crypto::SHA256HashString(issuer_key, result->issuer_key_hash.data, sizeof(result->issuer_key_hash.data)); @@ -237,7 +237,8 @@ return true; } -bool GetX509LogEntry(X509Certificate::OSCertHandle leaf, LogEntry* result) { +bool GetX509SignedEntry(X509Certificate::OSCertHandle leaf, + SignedEntryData* result) { DCHECK(leaf); std::string encoded; @@ -245,7 +246,7 @@ return false; result->Reset(); - result->type = ct::LogEntry::LOG_ENTRY_TYPE_X509; + result->type = ct::SignedEntryData::LOG_ENTRY_TYPE_X509; result->leaf_certificate.swap(encoded); return true; }
diff --git a/net/cert/ct_objects_extractor.h b/net/cert/ct_objects_extractor.h index d5deb5b60..469e315 100644 --- a/net/cert/ct_objects_extractor.h +++ b/net/cert/ct_objects_extractor.h
@@ -15,7 +15,7 @@ namespace ct { -struct LogEntry; +struct SignedEntryData; // Extracts a SignedCertificateTimestampList that has been embedded within a // leaf cert as an X.509v3 extension with the OID 1.3.6.1.4.1.11129.2.4.2. @@ -33,9 +33,10 @@ // The filled |*result| should be verified using ct::CTLogVerifier::Verify // Note: If |leaf| does not contain the required extension, it is treated as // a failure. -NET_EXPORT_PRIVATE bool GetPrecertLogEntry(X509Certificate::OSCertHandle leaf, - X509Certificate::OSCertHandle issuer, - LogEntry* result); +NET_EXPORT_PRIVATE bool GetPrecertSignedEntry( + X509Certificate::OSCertHandle leaf, + X509Certificate::OSCertHandle issuer, + SignedEntryData* result); // Obtains an X509Chain log entry for |leaf|, an X.509v3 certificate that // is not expected to contain an X.509v3 extension with the OID @@ -43,8 +44,8 @@ // On success, fills |result| with the data for an X509Chain log entry and // returns true. // The filled |*result| should be verified using ct::CTLogVerifier::Verify -NET_EXPORT_PRIVATE bool GetX509LogEntry(X509Certificate::OSCertHandle leaf, - LogEntry* result); +NET_EXPORT_PRIVATE bool GetX509SignedEntry(X509Certificate::OSCertHandle leaf, + SignedEntryData* result); // Extracts a SignedCertificateTimestampList that has been embedded within // an OCSP response as an extension with the OID 1.3.6.1.4.1.11129.2.4.5.
diff --git a/net/cert/ct_objects_extractor_unittest.cc b/net/cert/ct_objects_extractor_unittest.cc index 1073645..5f870c1 100644 --- a/net/cert/ct_objects_extractor_unittest.cc +++ b/net/cert/ct_objects_extractor_unittest.cc
@@ -72,12 +72,12 @@ } TEST_F(CTObjectsExtractorTest, ExtractPrecert) { - LogEntry entry; - ASSERT_TRUE(GetPrecertLogEntry(precert_chain_[0]->os_cert_handle(), - precert_chain_[1]->os_cert_handle(), - &entry)); + SignedEntryData entry; + ASSERT_TRUE(GetPrecertSignedEntry(precert_chain_[0]->os_cert_handle(), + precert_chain_[1]->os_cert_handle(), + &entry)); - ASSERT_EQ(ct::LogEntry::LOG_ENTRY_TYPE_PRECERT, entry.type); + ASSERT_EQ(ct::SignedEntryData::LOG_ENTRY_TYPE_PRECERT, entry.type); // Should have empty leaf cert for this log entry type. ASSERT_TRUE(entry.leaf_certificate.empty()); // Compare hash values of issuer spki. @@ -87,10 +87,10 @@ } TEST_F(CTObjectsExtractorTest, ExtractOrdinaryX509Cert) { - LogEntry entry; - ASSERT_TRUE(GetX509LogEntry(test_cert_->os_cert_handle(), &entry)); + SignedEntryData entry; + ASSERT_TRUE(GetX509SignedEntry(test_cert_->os_cert_handle(), &entry)); - ASSERT_EQ(ct::LogEntry::LOG_ENTRY_TYPE_X509, entry.type); + ASSERT_EQ(ct::SignedEntryData::LOG_ENTRY_TYPE_X509, entry.type); // Should have empty tbs_certificate for this log entry type. ASSERT_TRUE(entry.tbs_certificate.empty()); // Length of leaf_certificate should be 718, see the CT Serialization tests. @@ -103,23 +103,23 @@ new ct::SignedCertificateTimestamp()); ExtractEmbeddedSCT(precert_chain_[0], &sct); - LogEntry entry; - ASSERT_TRUE(GetPrecertLogEntry(precert_chain_[0]->os_cert_handle(), - precert_chain_[1]->os_cert_handle(), - &entry)); + SignedEntryData entry; + ASSERT_TRUE(GetPrecertSignedEntry(precert_chain_[0]->os_cert_handle(), + precert_chain_[1]->os_cert_handle(), + &entry)); EXPECT_TRUE(log_->Verify(entry, *sct.get())); } -// Test that an externally-provided SCT verifies over the LogEntry +// Test that an externally-provided SCT verifies over the SignedEntryData // of a regular X.509 Certificate TEST_F(CTObjectsExtractorTest, ComplementarySCTVerifies) { scoped_refptr<ct::SignedCertificateTimestamp> sct( new ct::SignedCertificateTimestamp()); GetX509CertSCT(&sct); - LogEntry entry; - ASSERT_TRUE(GetX509LogEntry(test_cert_->os_cert_handle(), &entry)); + SignedEntryData entry; + ASSERT_TRUE(GetX509SignedEntry(test_cert_->os_cert_handle(), &entry)); EXPECT_TRUE(log_->Verify(entry, *sct.get())); }
diff --git a/net/cert/ct_serialization.cc b/net/cert/ct_serialization.cc index 64a6ff5..7bc70838 100644 --- a/net/cert/ct_serialization.cc +++ b/net/cert/ct_serialization.cc
@@ -28,7 +28,7 @@ // Common V1 struct members const size_t kTimestampLength = 8; -const size_t kLogEntryTypeLength = 2; +const size_t kSignedEntryTypeLength = 2; const size_t kAsn1CertificateLengthBytes = 3; const size_t kTbsCertificateLengthBytes = 3; const size_t kExtensionsLengthBytes = 2; @@ -247,20 +247,22 @@ return true; } -// Writes a LogEntry of type X.509 cert to |output|. -// |input| is the LogEntry containing the certificate. -// Returns true if the leaf_certificate in the LogEntry does not exceed +// Writes a SignedEntryData of type X.509 cert to |output|. +// |input| is the SignedEntryData containing the certificate. +// Returns true if the leaf_certificate in the SignedEntryData does not exceed // kMaxAsn1CertificateLength and so can be written to |output|. -bool EncodeAsn1CertLogEntry(const LogEntry& input, std::string* output) { +bool EncodeAsn1CertSignedEntry(const SignedEntryData& input, + std::string* output) { return WriteVariableBytes(kAsn1CertificateLengthBytes, input.leaf_certificate, output); } -// Writes a LogEntry of type PreCertificate to |output|. -// |input| is the LogEntry containing the TBSCertificate and issuer key hash. -// Returns true if the TBSCertificate component in the LogEntry does not -// exceed kMaxTbsCertificateLength and so can be written to |output|. -bool EncodePrecertLogEntry(const LogEntry& input, std::string* output) { +// Writes a SignedEntryData of type PreCertificate to |output|. +// |input| is the SignedEntryData containing the TBSCertificate and issuer key +// hash. Returns true if the TBSCertificate component in the SignedEntryData +// does not exceed kMaxTbsCertificateLength and so can be written to |output|. +bool EncodePrecertSignedEntry(const SignedEntryData& input, + std::string* output) { WriteEncodedBytes( base::StringPiece( reinterpret_cast<const char*>(input.issuer_key_hash.data), @@ -308,13 +310,13 @@ return true; } -bool EncodeLogEntry(const LogEntry& input, std::string* output) { - WriteUint(kLogEntryTypeLength, input.type, output); +bool EncodeSignedEntry(const SignedEntryData& input, std::string* output) { + WriteUint(kSignedEntryTypeLength, input.type, output); switch (input.type) { - case LogEntry::LOG_ENTRY_TYPE_X509: - return EncodeAsn1CertLogEntry(input, output); - case LogEntry::LOG_ENTRY_TYPE_PRECERT: - return EncodePrecertLogEntry(input, output); + case SignedEntryData::LOG_ENTRY_TYPE_X509: + return EncodeAsn1CertSignedEntry(input, output); + case SignedEntryData::LOG_ENTRY_TYPE_PRECERT: + return EncodePrecertSignedEntry(input, output); } return false; } @@ -349,7 +351,7 @@ WriteUint(kVersionLength, 0, output); // version: 1 WriteUint(kMerkleLeafTypeLength, 0, output); // type: timestamped entry WriteTimeSinceEpoch(leaf.timestamp, output); - if (!EncodeLogEntry(leaf.log_entry, output)) + if (!EncodeSignedEntry(leaf.signed_entry, output)) return false; if (!WriteVariableBytes(kExtensionsLengthBytes, leaf.extensions, output)) return false;
diff --git a/net/cert/ct_serialization.h b/net/cert/ct_serialization.h index b5b3d77d..5dfb438 100644 --- a/net/cert/ct_serialization.h +++ b/net/cert/ct_serialization.h
@@ -20,9 +20,9 @@ namespace ct { struct DigitallySigned; -struct LogEntry; struct MerkleTreeLeaf; struct SignedCertificateTimestamp; +struct SignedEntryData; struct SignedTreeHead; // If |input.signature_data| is less than kMaxSignatureLength, encodes the @@ -36,10 +36,10 @@ NET_EXPORT_PRIVATE bool DecodeDigitallySigned(base::StringPiece* input, DigitallySigned* output); -// Encodes the |input| LogEntry to |output|. Returns true if the entry size -// does not exceed allowed size in RFC6962, false otherwise. -NET_EXPORT_PRIVATE bool EncodeLogEntry(const LogEntry& input, - std::string* output); +// Encodes the |input| SignedEntryData to |output|. Returns true if the entry +// size does not exceed allowed size in RFC6962, false otherwise. +NET_EXPORT_PRIVATE bool EncodeSignedEntry(const SignedEntryData& input, + std::string* output); // Serialises the Merkle tree |leaf|, appending it to |output|. // These bytes can be hashed for use with audit proof fetching.
diff --git a/net/cert/ct_serialization_unittest.cc b/net/cert/ct_serialization_unittest.cc index 6ddb329..285643d 100644 --- a/net/cert/ct_serialization_unittest.cc +++ b/net/cert/ct_serialization_unittest.cc
@@ -79,13 +79,12 @@ EXPECT_EQ(test_digitally_signed_, encoded); } - -TEST_F(CtSerializationTest, EncodesLogEntryForX509Cert) { - ct::LogEntry entry; - ct::GetX509CertLogEntry(&entry); +TEST_F(CtSerializationTest, EncodesSignedEntryForX509Cert) { + ct::SignedEntryData entry; + ct::GetX509CertSignedEntry(&entry); std::string encoded; - ASSERT_TRUE(ct::EncodeLogEntry(entry, &encoded)); + ASSERT_TRUE(ct::EncodeSignedEntry(entry, &encoded)); EXPECT_EQ((718U + 5U), encoded.size()); // First two bytes are log entry type. Next, length: // Length is 718 which is 512 + 206, which is 0x2ce @@ -95,12 +94,12 @@ EXPECT_EQ(expected_prefix, encoded.substr(0, 5)); } -TEST_F(CtSerializationTest, EncodesLogEntryForPrecert) { - ct::LogEntry entry; - ct::GetPrecertLogEntry(&entry); +TEST_F(CtSerializationTest, EncodesSignedEntryForPrecert) { + ct::SignedEntryData entry; + ct::GetPrecertSignedEntry(&entry); std::string encoded; - ASSERT_TRUE(ct::EncodeLogEntry(entry, &encoded)); + ASSERT_TRUE(ct::EncodeSignedEntry(entry, &encoded)); EXPECT_EQ(604u, encoded.size()); // First two bytes are the log entry type. EXPECT_EQ(std::string("\x00\x01", 2), encoded.substr(0, 2)); @@ -203,8 +202,8 @@ "Log entry type encoded incorrectly"; EXPECT_EQ(std::string("\x00\x02\xce", 3), encoded.substr(12, 3)) << "Certificate length encoded incorrectly"; - EXPECT_EQ(tree_leaf.log_entry.leaf_certificate, encoded.substr(15, 718)) << - "Certificate encoded incorrectly"; + EXPECT_EQ(tree_leaf.signed_entry.leaf_certificate, encoded.substr(15, 718)) + << "Certificate encoded incorrectly"; EXPECT_EQ(std::string("\x00\x06", 2), encoded.substr(733, 2)) << "CT extensions length encoded incorrectly"; EXPECT_EQ(tree_leaf.extensions, encoded.substr(735, 6)) << @@ -228,12 +227,12 @@ EXPECT_EQ(std::string("\x00\x01", 2), encoded.substr(10, 2)) << "Log entry type encoded incorrectly"; EXPECT_THAT(encoded.substr(12, 32), - ElementsAreArray(tree_leaf.log_entry.issuer_key_hash.data)) << - "Issuer key hash encoded incorrectly"; + ElementsAreArray(tree_leaf.signed_entry.issuer_key_hash.data)) + << "Issuer key hash encoded incorrectly"; EXPECT_EQ(std::string("\x00\x02\x37", 3), encoded.substr(44, 3)) << "TBS certificate length encoded incorrectly"; - EXPECT_EQ(tree_leaf.log_entry.tbs_certificate, encoded.substr(47, 567)) << - "TBS certificate encoded incorrectly"; + EXPECT_EQ(tree_leaf.signed_entry.tbs_certificate, encoded.substr(47, 567)) + << "TBS certificate encoded incorrectly"; EXPECT_EQ(std::string("\x00\x06", 2), encoded.substr(614, 2)) << "CT extensions length encoded incorrectly"; EXPECT_EQ(tree_leaf.extensions, encoded.substr(616, 6)) <<
diff --git a/net/cert/merkle_tree_leaf.cc b/net/cert/merkle_tree_leaf.cc index 5dfe08e..5fb2eba 100644 --- a/net/cert/merkle_tree_leaf.cc +++ b/net/cert/merkle_tree_leaf.cc
@@ -36,14 +36,14 @@ MerkleTreeLeaf* merkle_tree_leaf) { if (sct->origin == SignedCertificateTimestamp::SCT_EMBEDDED) { if (cert->GetIntermediateCertificates().empty() || - !GetPrecertLogEntry(cert->os_cert_handle(), - cert->GetIntermediateCertificates().front(), - &merkle_tree_leaf->log_entry)) { + !GetPrecertSignedEntry(cert->os_cert_handle(), + cert->GetIntermediateCertificates().front(), + &merkle_tree_leaf->signed_entry)) { return false; } } else { - if (!GetX509LogEntry(cert->os_cert_handle(), - &merkle_tree_leaf->log_entry)) { + if (!GetX509SignedEntry(cert->os_cert_handle(), + &merkle_tree_leaf->signed_entry)) { return false; } }
diff --git a/net/cert/merkle_tree_leaf.h b/net/cert/merkle_tree_leaf.h index 21217bb6..fc566e6 100644 --- a/net/cert/merkle_tree_leaf.h +++ b/net/cert/merkle_tree_leaf.h
@@ -31,7 +31,7 @@ // new types are planned. // * The timestamped_entry's |timestamp| and |extensions| fields are directly // accessible. -// * The timestamped_entry's entry_type can be deduced from |log_entry|.type +// * The timestamped_entry's entry_type can be deduced from |signed_entry|.type struct NET_EXPORT MerkleTreeLeaf { MerkleTreeLeaf(); MerkleTreeLeaf(const MerkleTreeLeaf& other); @@ -39,7 +39,7 @@ ~MerkleTreeLeaf(); // Certificate / Precertificate and indication of entry type. - LogEntry log_entry; + SignedEntryData signed_entry; // Timestamp from the SCT. base::Time timestamp;
diff --git a/net/cert/merkle_tree_leaf_unittest.cc b/net/cert/merkle_tree_leaf_unittest.cc index 29c4c18..59ac7dce 100644 --- a/net/cert/merkle_tree_leaf_unittest.cc +++ b/net/cert/merkle_tree_leaf_unittest.cc
@@ -75,9 +75,9 @@ MerkleTreeLeaf leaf; ASSERT_TRUE(GetMerkleTreeLeaf(test_cert_.get(), x509_sct_.get(), &leaf)); - EXPECT_EQ(LogEntry::LOG_ENTRY_TYPE_X509, leaf.log_entry.type); - EXPECT_FALSE(leaf.log_entry.leaf_certificate.empty()); - EXPECT_TRUE(leaf.log_entry.tbs_certificate.empty()); + EXPECT_EQ(SignedEntryData::LOG_ENTRY_TYPE_X509, leaf.signed_entry.type); + EXPECT_FALSE(leaf.signed_entry.leaf_certificate.empty()); + EXPECT_TRUE(leaf.signed_entry.tbs_certificate.empty()); EXPECT_EQ(x509_sct_->timestamp, leaf.timestamp); EXPECT_EQ(x509_sct_->extensions, leaf.extensions); @@ -88,9 +88,9 @@ ASSERT_TRUE( GetMerkleTreeLeaf(test_precert_.get(), precert_sct_.get(), &leaf)); - EXPECT_EQ(LogEntry::LOG_ENTRY_TYPE_PRECERT, leaf.log_entry.type); - EXPECT_FALSE(leaf.log_entry.tbs_certificate.empty()); - EXPECT_TRUE(leaf.log_entry.leaf_certificate.empty()); + EXPECT_EQ(SignedEntryData::LOG_ENTRY_TYPE_PRECERT, leaf.signed_entry.type); + EXPECT_FALSE(leaf.signed_entry.tbs_certificate.empty()); + EXPECT_TRUE(leaf.signed_entry.leaf_certificate.empty()); EXPECT_EQ(precert_sct_->timestamp, leaf.timestamp); EXPECT_EQ(precert_sct_->extensions, leaf.extensions);
diff --git a/net/cert/multi_log_ct_verifier.cc b/net/cert/multi_log_ct_verifier.cc index a372b05c..e6b6c85 100644 --- a/net/cert/multi_log_ct_verifier.cc +++ b/net/cert/multi_log_ct_verifier.cc
@@ -97,11 +97,11 @@ ct::ExtractEmbeddedSCTList( cert->os_cert_handle(), &embedded_scts)) { - ct::LogEntry precert_entry; + ct::SignedEntryData precert_entry; - if (ct::GetPrecertLogEntry(cert->os_cert_handle(), - cert->GetIntermediateCertificates().front(), - &precert_entry)) { + if (ct::GetPrecertSignedEntry(cert->os_cert_handle(), + cert->GetIntermediateCertificates().front(), + &precert_entry)) { VerifySCTs(embedded_scts, precert_entry, ct::SignedCertificateTimestamp::SCT_EMBEDDED, cert, output_scts); @@ -125,8 +125,8 @@ net_log.AddEvent(NetLogEventType::SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED, net_log_callback); - ct::LogEntry x509_entry; - if (ct::GetX509LogEntry(cert->os_cert_handle(), &x509_entry)) { + ct::SignedEntryData x509_entry; + if (ct::GetX509SignedEntry(cert->os_cert_handle(), &x509_entry)) { VerifySCTs(sct_list_from_ocsp, x509_entry, ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE, cert, output_scts); @@ -147,7 +147,7 @@ void MultiLogCTVerifier::VerifySCTs( base::StringPiece encoded_sct_list, - const ct::LogEntry& expected_entry, + const ct::SignedEntryData& expected_entry, ct::SignedCertificateTimestamp::Origin origin, X509Certificate* cert, SignedCertificateTimestampAndStatusList* output_scts) { @@ -177,7 +177,7 @@ bool MultiLogCTVerifier::VerifySingleSCT( scoped_refptr<ct::SignedCertificateTimestamp> sct, - const ct::LogEntry& expected_entry, + const ct::SignedEntryData& expected_entry, X509Certificate* cert, SignedCertificateTimestampAndStatusList* output_scts) { // Assume this SCT is untrusted until proven otherwise.
diff --git a/net/cert/multi_log_ct_verifier.h b/net/cert/multi_log_ct_verifier.h index d4cb56f..3a69b5e2 100644 --- a/net/cert/multi_log_ct_verifier.h +++ b/net/cert/multi_log_ct_verifier.h
@@ -18,7 +18,7 @@ namespace net { namespace ct { -struct LogEntry; +struct SignedEntryData; } // namespace ct class CTLogVerifier; @@ -48,14 +48,14 @@ // placing the verification results in |output_scts|. The SCTs in the list // come from |origin| (as will be indicated in the origin field of each SCT). void VerifySCTs(base::StringPiece encoded_sct_list, - const ct::LogEntry& expected_entry, + const ct::SignedEntryData& expected_entry, ct::SignedCertificateTimestamp::Origin origin, X509Certificate* cert, SignedCertificateTimestampAndStatusList* output_scts); // Verifies a single, parsed SCT against all logs. bool VerifySingleSCT(scoped_refptr<ct::SignedCertificateTimestamp> sct, - const ct::LogEntry& expected_entry, + const ct::SignedEntryData& expected_entry, X509Certificate* cert, SignedCertificateTimestampAndStatusList* output_scts);
diff --git a/net/cert/signed_certificate_timestamp.cc b/net/cert/signed_certificate_timestamp.cc index 489c88c..9096fde 100644 --- a/net/cert/signed_certificate_timestamp.cc +++ b/net/cert/signed_certificate_timestamp.cc
@@ -78,12 +78,12 @@ return sct; } -LogEntry::LogEntry() : type(LOG_ENTRY_TYPE_X509) {} +SignedEntryData::SignedEntryData() : type(LOG_ENTRY_TYPE_X509) {} -LogEntry::~LogEntry() {} +SignedEntryData::~SignedEntryData() {} -void LogEntry::Reset() { - type = LogEntry::LOG_ENTRY_TYPE_X509; +void SignedEntryData::Reset() { + type = SignedEntryData::LOG_ENTRY_TYPE_X509; leaf_certificate.clear(); tbs_certificate.clear(); }
diff --git a/net/cert/signed_certificate_timestamp.h b/net/cert/signed_certificate_timestamp.h index 96eded2..a23b708 100644 --- a/net/cert/signed_certificate_timestamp.h +++ b/net/cert/signed_certificate_timestamp.h
@@ -24,16 +24,29 @@ // Structures related to Certificate Transparency (RFC6962). namespace ct { -// LogEntry struct in RFC 6962, Section 3.1 -struct NET_EXPORT LogEntry { +// Contains the data necessary to reconstruct the signed_entry of a +// SignedCertificateTimestamp, from RFC 6962, Section 3.2. +// +// All the data necessary to validate a SignedCertificateTimestamp is present +// within the SignedCertificateTimestamp, except for the signature_type, +// entry_type, and the actual entry. The only supported signature_type at +// present is certificate_timestamp. The entry_type is implicit from the +// context in which it is received (those in the X.509 extension are +// precert_entry, all others are x509_entry). The signed_entry itself is +// reconstructed from the certificate being verified, or from the corresponding +// precertificate. +// +// The SignedEntryData contains this reconstructed data, and can be used to +// either generate or verify the signature in SCTs. +struct NET_EXPORT SignedEntryData { // LogEntryType enum in RFC 6962, Section 3.1 enum Type { LOG_ENTRY_TYPE_X509 = 0, LOG_ENTRY_TYPE_PRECERT = 1 }; - LogEntry(); - ~LogEntry(); + SignedEntryData(); + ~SignedEntryData(); void Reset(); Type type;
diff --git a/net/test/ct_test_util.cc b/net/test/ct_test_util.cc index f9862e0..138f1386 100644 --- a/net/test/ct_test_util.cc +++ b/net/test/ct_test_util.cc
@@ -173,21 +173,21 @@ } // namespace -void GetX509CertLogEntry(LogEntry* entry) { - entry->type = ct::LogEntry::LOG_ENTRY_TYPE_X509; +void GetX509CertSignedEntry(SignedEntryData* entry) { + entry->type = ct::SignedEntryData::LOG_ENTRY_TYPE_X509; entry->leaf_certificate = HexToBytes(kDefaultDerCert); } void GetX509CertTreeLeaf(MerkleTreeLeaf* tree_leaf) { tree_leaf->timestamp = base::Time::FromJsTime(kTestTimestamp); - GetX509CertLogEntry(&tree_leaf->log_entry); + GetX509CertSignedEntry(&tree_leaf->signed_entry); tree_leaf->extensions = HexToBytes(kDefaultExtensions); } std::string GetDerEncodedX509Cert() { return HexToBytes(kDefaultDerCert); } -void GetPrecertLogEntry(LogEntry* entry) { - entry->type = ct::LogEntry::LOG_ENTRY_TYPE_PRECERT; +void GetPrecertSignedEntry(SignedEntryData* entry) { + entry->type = ct::SignedEntryData::LOG_ENTRY_TYPE_PRECERT; std::string issuer_hash(HexToBytes(kDefaultIssuerKeyHash)); memcpy(entry->issuer_key_hash.data, issuer_hash.data(), issuer_hash.size()); entry->tbs_certificate = HexToBytes(kDefaultDerTbsCert); @@ -195,7 +195,7 @@ void GetPrecertTreeLeaf(MerkleTreeLeaf* tree_leaf) { tree_leaf->timestamp = base::Time::FromJsTime(kTestTimestamp); - GetPrecertLogEntry(&tree_leaf->log_entry); + GetPrecertSignedEntry(&tree_leaf->signed_entry); tree_leaf->extensions = HexToBytes(kDefaultExtensions); }
diff --git a/net/test/ct_test_util.h b/net/test/ct_test_util.h index d39079690..a784ca2b 100644 --- a/net/test/ct_test_util.h +++ b/net/test/ct_test_util.h
@@ -20,15 +20,15 @@ namespace ct { struct DigitallySigned; -struct LogEntry; struct MerkleTreeLeaf; +struct SignedEntryData; struct SignedTreeHead; // Note: unless specified otherwise, all test data is taken from Certificate // Transparency test data repository. // Fills |entry| with test data for an X.509 entry. -void GetX509CertLogEntry(LogEntry* entry); +void GetX509CertSignedEntry(SignedEntryData* entry); // Fills |tree_leaf| with test data for an X.509 Merkle tree leaf. void GetX509CertTreeLeaf(MerkleTreeLeaf* tree_leaf); @@ -38,7 +38,7 @@ std::string GetDerEncodedX509Cert(); // Fills |entry| with test data for a Precertificate entry. -void GetPrecertLogEntry(LogEntry* entry); +void GetPrecertSignedEntry(SignedEntryData* entry); // Fills |tree_leaf| with test data for a Precertificate Merkle tree leaf. void GetPrecertTreeLeaf(MerkleTreeLeaf* tree_leaf);
diff --git a/third_party/WebKit/LayoutTests/mojo/associated_interface_ptr.html b/third_party/WebKit/LayoutTests/mojo/associated_interface_ptr.html new file mode 100644 index 0000000..361cff3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/mojo/associated_interface_ptr.html
@@ -0,0 +1,226 @@ +<!DOCTYPE html> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> +<script src="../resources/mojo-helpers.js"></script> +<script> +'use strict'; + +setup({ explicit_done: true }); + +define([ + "mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom", + "mojo/public/js/associated_bindings", + "mojo/public/js/bindings", +], function(testAssociatedInterfaces, associatedBindings, bindings) { + + function IntegerSenderImpl(callback) { + this.callback = callback; + } + + IntegerSenderImpl.prototype.echo = function(value) { + return Promise.resolve({value: value}); + }; + + IntegerSenderImpl.prototype.send = function(value) { + if (this.callback) { + this.callback(value); + } + }; + + function IntegerSenderConnectionImpl() { + this.integerSenderBinding_ = null; + } + + IntegerSenderConnectionImpl.prototype.getSender = function( + integerSenderRequest) { + this.integerSenderBinding_ = new associatedBindings.AssociatedBinding( + testAssociatedInterfaces.IntegerSender, + new IntegerSenderImpl(), + integerSenderRequest); + }; + + IntegerSenderConnectionImpl.prototype.asyncGetSender = function() { + var integerSenderPtrInfo = new + associatedBindings.AssociatedInterfacePtrInfo(); + var integerSenderRequest = associatedBindings.makeRequest( + integerSenderPtrInfo); + this.getSender(integerSenderRequest); + return Promise.resolve({sender: integerSenderPtrInfo}); + }; + + function IntegerSenderConnectionAtBothEndsImpl() { + this.integerSender_ = null; + } + + IntegerSenderConnectionAtBothEndsImpl.prototype.getSender = + IntegerSenderConnectionImpl.prototype.getSender; + + IntegerSenderConnectionAtBothEndsImpl.prototype.setSender = function( + integerSenderPtrInfo) { + this.integerSender_ = new + testAssociatedInterfaces.AssociatedIntegerSenderPtr(); + this.integerSender_.ptr.bind(integerSenderPtrInfo); + return this.integerSender_.echo(456); + }; + + function IntegerSenderConnectionImplWithConnectionError() { + this.integerSenderBinding_ = null; + } + + IntegerSenderConnectionImplWithConnectionError.prototype.getSender = + function(integerSenderRequest) { + this.integerSenderBinding_ = new associatedBindings.AssociatedBinding( + testAssociatedInterfaces.IntegerSender, + new IntegerSenderImpl(), + integerSenderRequest); + this.integerSenderBinding_.closeWithReason( + {custom_reason: 42, description: 'hey'}); + }; + + promise_test(async () => { + var integerSenderConnection = new + testAssociatedInterfaces.IntegerSenderConnectionPtr(); + var integerSenderConnectionBinding = new bindings.Binding( + testAssociatedInterfaces.IntegerSenderConnection, + new IntegerSenderConnectionImpl(), + bindings.makeRequest(integerSenderConnection)); + + // Sending AssociatedInterfaceRequest. + var integerSenderPtrInfo0 = new + associatedBindings.AssociatedInterfacePtrInfo(); + var integerSenderRequest0 = associatedBindings.makeRequest( + integerSenderPtrInfo0); + + var integerSender0 = new + testAssociatedInterfaces.AssociatedIntegerSenderPtr(); + integerSender0.ptr.bind(integerSenderPtrInfo0); + + integerSenderConnection.getSender(integerSenderRequest0); + assert_equals((await integerSender0.echo(123)).value, 123); + + // Recieving AssociatedInterfacePtrInfo. + var integerSenderPtrInfo1 = + (await integerSenderConnection.asyncGetSender()).sender; + var integerSender1 = new + testAssociatedInterfaces.AssociatedIntegerSenderPtr(); + integerSender1.ptr.bind(integerSenderPtrInfo1); + assert_equals((await integerSender1.echo(456)).value, 456); + }, 'pass associated interfaces'); + + // Bind to the same pipe two associated interfaces, whose implementation + // lives at different ends. Test that the two don't interfere. + promise_test(async () => { + var integerSenderConnectionAtBothEnds = new + testAssociatedInterfaces.IntegerSenderConnectionAtBothEndsPtr(); + var integerSenderConnectionAtBothEndsBinding = new bindings.Binding( + testAssociatedInterfaces.IntegerSenderConnectionAtBothEnds, + new IntegerSenderConnectionAtBothEndsImpl(), + bindings.makeRequest(integerSenderConnectionAtBothEnds)); + + // Associated Interface whose Binding Impl lives on the other side. + // Sending AssociatedInterfaceRequest. + var integerSenderPtrInfo0 = new + associatedBindings.AssociatedInterfacePtrInfo(); + var integerSenderRequest0 = associatedBindings.makeRequest( + integerSenderPtrInfo0); + + var integerSender0 = new + testAssociatedInterfaces.AssociatedIntegerSenderPtr(); + integerSender0.ptr.bind(integerSenderPtrInfo0); + + integerSenderConnectionAtBothEnds.getSender(integerSenderRequest0); + assert_equals((await integerSender0.echo(123)).value, 123); + + // Associated Interface whose Binding Impl lives on this side. + // Sending AssociatedInterfacePtrInfo. + var integerSenderPtrInfo1 = new + associatedBindings.AssociatedInterfacePtrInfo(); + var integerSenderRequest1 = associatedBindings.makeRequest( + integerSenderPtrInfo1); + + var integerSenderBinding = new associatedBindings.AssociatedBinding( + testAssociatedInterfaces.IntegerSender, + new IntegerSenderImpl(), + integerSenderRequest1); + + assert_equals((await integerSenderConnectionAtBothEnds.setSender( + integerSenderPtrInfo1)).value, 456); + }, 'associated interfaces on both ends'); + + promise_test(async () => { + var integerSenderConnection = new + testAssociatedInterfaces.IntegerSenderConnectionPtr(); + var integerSenderConnectionBinding = new bindings.Binding( + testAssociatedInterfaces.IntegerSenderConnection, + new IntegerSenderConnectionImplWithConnectionError(), + bindings.makeRequest(integerSenderConnection)); + + // Sending AssociatedInterfaceRequest. + var integerSenderPtrInfo0 = new + associatedBindings.AssociatedInterfacePtrInfo(); + var integerSenderRequest0 = associatedBindings.makeRequest( + integerSenderPtrInfo0); + + var integerSender0 = new + testAssociatedInterfaces.AssociatedIntegerSenderPtr(); + integerSender0.ptr.bind(integerSenderPtrInfo0); + + integerSenderConnection.getSender(integerSenderRequest0); + await new Promise((resolve, reject) => { + integerSender0.ptr.setConnectionErrorHandler(function({custom_reason, + description}) { + assert_equals(custom_reason, 42); + assert_equals(description, 'hey'); + resolve(); + }); + }); + }, 'connection error with reason'); + + promise_test(async () => { + var integerSenderConnection = new + testAssociatedInterfaces.IntegerSenderConnectionPtr(); + var integerSenderConnectionBinding = new bindings.Binding( + testAssociatedInterfaces.IntegerSenderConnection, + new IntegerSenderConnectionImpl(), + bindings.makeRequest(integerSenderConnection)); + + // Sending AssociatedInterfaceRequest. + var integerSenderPtrInfo0 = new + associatedBindings.AssociatedInterfacePtrInfo(); + var integerSenderRequest0 = associatedBindings.makeRequest( + integerSenderPtrInfo0); + var integerSender0 = new + testAssociatedInterfaces.AssociatedIntegerSenderPtr(); + integerSender0.ptr.bind(integerSenderPtrInfo0); + integerSenderConnection.getSender(integerSenderRequest0); + + // Recieving AssociatedInterfacePtrInfo. + var integerSenderPtrInfo1 = + (await integerSenderConnection.asyncGetSender()).sender; + var integerSender1 = new + testAssociatedInterfaces.AssociatedIntegerSenderPtr(); + integerSender1.ptr.bind(integerSenderPtrInfo1); + + // Master InterfacePtrController reset triggers connection error handler on + // interface endpoint clients for all associated endpoints. + var connectionErrorHandler0 = new Promise((resolve, reject) => { + integerSender0.ptr.setConnectionErrorHandler(() => { + resolve(); + }); + }); + + var connectionErrorHandler1 = new Promise((resolve, reject) => { + integerSender1.ptr.setConnectionErrorHandler(() => { + resolve(); + }); + }); + + setTimeout(integerSenderConnection.ptr.reset.bind( + integerSenderConnection.ptr), 0); + await Promise.all([connectionErrorHandler0, connectionErrorHandler1]); + }, 'all endpoints connectionErrorHandler called on master interface reset'); + + done(); +}); + +</script>
diff --git a/third_party/WebKit/LayoutTests/mojo/codec.html b/third_party/WebKit/LayoutTests/mojo/codec.html index 0cf09bc..a02ea1ef 100644 --- a/third_party/WebKit/LayoutTests/mojo/codec.html +++ b/third_party/WebKit/LayoutTests/mojo/codec.html
@@ -26,7 +26,7 @@ var messageName = 42; var payloadSize = sample.Bar.encodedSize; - var builder = new codec.MessageBuilder(messageName, payloadSize); + var builder = new codec.MessageV0Builder(messageName, payloadSize); builder.encodeStruct(sample.Bar, bar); var message = builder.finish(); @@ -98,7 +98,7 @@ var messageName = 31; var payloadSize = 304; - var builder = new codec.MessageBuilder(messageName, payloadSize); + var builder = new codec.MessageV0Builder(messageName, payloadSize); builder.encodeStruct(sample.Foo, foo); var message = builder.finish(); @@ -165,7 +165,7 @@ r.name = "rectangle"; r.rects = new Array(createRect(1, 2, 3, 4), createRect(10, 20, 30, 40)); - var builder = new codec.MessageBuilder(1, structs.NamedRegion.encodedSize); + var builder = new codec.MessageV0Builder(1, structs.NamedRegion.encodedSize); builder.encodeStruct(structs.NamedRegion, r); var reader = new codec.MessageReader(builder.finish()); var result = reader.decodeStruct(structs.NamedRegion); @@ -181,7 +181,7 @@ var single_bool = new structs.SingleBoolStruct(); single_bool.value = true; - var builder = new codec.MessageBuilder( + var builder = new codec.MessageV0Builder( 1, structs.SingleBoolStruct.encodedSize); builder.encodeStruct(structs.SingleBoolStruct, single_bool); var reader = new codec.MessageReader(builder.finish()); @@ -195,7 +195,7 @@ var messageName = 42; var payloadSize = encodedSize || cls.encodedSize; - var builder = new codec.MessageBuilder(messageName, payloadSize); + var builder = new codec.MessageV0Builder(messageName, payloadSize); builder.encodeStruct(cls, input) var message = builder.finish(); @@ -253,7 +253,7 @@ var messageName = 42; var payloadSize = 24; - var builder = new codec.MessageBuilder(messageName, payloadSize); + var builder = new codec.MessageV0Builder(messageName, payloadSize); var encoder = builder.createEncoder(8); encoder.encodeStringPointer(str); var message = builder.finish(); @@ -276,7 +276,7 @@ }, 'utf8'); test(() => { - var encoder = new codec.MessageBuilder(42, 24).createEncoder(8); + var encoder = new codec.MessageV0Builder(42, 24).createEncoder(8); function DummyClass() {}; var testCases = [ // method, args, invalid examples, valid examples @@ -294,7 +294,7 @@ var invalidExamples = test[2]; var validExamples = test[3]; - var encoder = new codec.MessageBuilder(42, 24).createEncoder(8); + var encoder = new codec.MessageV0Builder(42, 24).createEncoder(8); invalidExamples.forEach(function(invalid) { assert_throws(null, function() { method.apply(encoder, baseArgs.concat(invalid)); @@ -302,7 +302,7 @@ }); validExamples.forEach(function(valid) { - var encoder = new codec.MessageBuilder(42, 24).createEncoder(8); + var encoder = new codec.MessageV0Builder(42, 24).createEncoder(8); method.apply(encoder, baseArgs.concat(valid)); }); });
diff --git a/third_party/WebKit/LayoutTests/mojo/struct.html b/third_party/WebKit/LayoutTests/mojo/struct.html index 0e9c53ba..7a82e0c 100644 --- a/third_party/WebKit/LayoutTests/mojo/struct.html +++ b/third_party/WebKit/LayoutTests/mojo/struct.html
@@ -100,7 +100,7 @@ function structEncodeDecode(struct) { var structClass = struct.constructor; - var builder = new codec.MessageBuilder(1234, structClass.encodedSize); + var builder = new codec.MessageV0Builder(1234, structClass.encodedSize); builder.encodeStruct(structClass, struct); var message = builder.finish();
diff --git a/third_party/WebKit/LayoutTests/mojo/union.html b/third_party/WebKit/LayoutTests/mojo/union.html index 96f2b7e..167bd365 100644 --- a/third_party/WebKit/LayoutTests/mojo/union.html +++ b/third_party/WebKit/LayoutTests/mojo/union.html
@@ -47,7 +47,7 @@ function structEncodeDecode(struct) { var structClass = struct.constructor; - var builder = new codec.MessageBuilder(1234, structClass.encodedSize); + var builder = new codec.MessageV0Builder(1234, structClass.encodedSize); builder.encodeStruct(structClass, struct); var message = builder.finish(); @@ -146,7 +146,7 @@ function structValidate(struct) { var structClass = struct.constructor; - var builder = new codec.MessageBuilder(1234, structClass.encodedSize); + var builder = new codec.MessageV0Builder(1234, structClass.encodedSize); builder.encodeStruct(structClass, struct); var message = builder.finish();
diff --git a/third_party/WebKit/LayoutTests/vibration/vibration-expected.txt b/third_party/WebKit/LayoutTests/vibration/vibration-expected.txt index e258033..2b6fbea 100644 --- a/third_party/WebKit/LayoutTests/vibration/vibration-expected.txt +++ b/third_party/WebKit/LayoutTests/vibration/vibration-expected.txt
@@ -1,4 +1,4 @@ -CONSOLE ERROR: line 301: Uncaught TypeError: Cannot read property 'then' of undefined +CONSOLE ERROR: line 311: Uncaught TypeError: Cannot read property 'then' of undefined This is a testharness.js-based test. Harness Error. harness_status.status = 1 , harness_status.message = Uncaught TypeError: Cannot read property 'then' of undefined PASS VibrationManager Mojo bindings and mock interfaces are available to tests.
diff --git a/third_party/WebKit/Source/core/dom/Node.cpp b/third_party/WebKit/Source/core/dom/Node.cpp index e3cfde2..590efff 100644 --- a/third_party/WebKit/Source/core/dom/Node.cpp +++ b/third_party/WebKit/Source/core/dom/Node.cpp
@@ -1871,7 +1871,7 @@ } } - old_document.Markers().RemoveMarkers(this); + old_document.Markers().RemoveMarkersForNode(this); if (GetDocument().GetPage() && GetDocument().GetPage() != old_document.GetPage()) { GetDocument().GetPage()->GetEventHandlerRegistry().DidMoveIntoPage(*this);
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp index 5f47aa8..dfd39cf 100644 --- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp +++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
@@ -45,18 +45,6 @@ namespace blink { -MarkerRemoverPredicate::MarkerRemoverPredicate(const Vector<String>& words) - : words_(words) {} - -bool MarkerRemoverPredicate::operator()(const DocumentMarker& document_marker, - const Text& text_node) const { - unsigned start = document_marker.StartOffset(); - unsigned length = document_marker.EndOffset() - document_marker.StartOffset(); - - String marker_text = text_node.data().Substring(start, length); - return words_.Contains(marker_text); -} - namespace { DocumentMarker::MarkerTypeIndex MarkerTypeToMarkerIndex( @@ -486,7 +474,7 @@ SynchronousMutationObserver::Trace(visitor); } -void DocumentMarkerController::RemoveMarkers( +void DocumentMarkerController::RemoveMarkersForNode( Node* node, DocumentMarker::MarkerTypes marker_types) { if (!PossiblyHasMarkers(marker_types)) @@ -498,8 +486,8 @@ RemoveMarkersFromList(iterator, marker_types); } -void DocumentMarkerController::RemoveMarkers( - const MarkerRemoverPredicate& should_remove_marker) { +void DocumentMarkerController::RemoveSpellingMarkersUnderWords( + const Vector<String>& words) { for (auto& node_markers : markers_) { const Node& node = *node_markers.key; if (!node.IsTextNode()) // MarkerRemoverPredicate requires a Text node. @@ -511,8 +499,13 @@ continue; bool removed_markers = false; for (size_t j = list->size(); j > 0; --j) { - if (should_remove_marker(*list->at(j - 1), - static_cast<const Text&>(node))) { + const DocumentMarker& marker = *list->at(j - 1); + + const unsigned start = marker.StartOffset(); + const unsigned length = marker.EndOffset() - marker.StartOffset(); + + const String marker_text = ToText(node).data().Substring(start, length); + if (words.Contains(marker_text)) { list->erase(j - 1); removed_markers = true; }
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h index c14b01d..011d958 100644 --- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h +++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h
@@ -42,16 +42,6 @@ class Node; class RenderedDocumentMarker; -class Text; - -class MarkerRemoverPredicate final { - public: - explicit MarkerRemoverPredicate(const Vector<String>& words); - bool operator()(const DocumentMarker&, const Text&) const; - - private: - Vector<String> words_; -}; class CORE_EXPORT DocumentMarkerController final : public GarbageCollected<DocumentMarkerController>, @@ -80,10 +70,10 @@ void RemoveMarkersInRange(const EphemeralRange&, DocumentMarker::MarkerTypes); void RemoveMarkers( DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers()); - void RemoveMarkers( + void RemoveMarkersForNode( Node*, DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers()); - void RemoveMarkers(const MarkerRemoverPredicate& should_remove_marker); + void RemoveSpellingMarkersUnderWords(const Vector<String>& words); void RepaintMarkers( DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers()); // Returns true if markers within a range are found.
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp index d2ae2ab..236d2bb 100644 --- a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp +++ b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
@@ -788,8 +788,10 @@ DocumentMarker::MarkerTypes marker_types(DocumentMarker::kSpelling); marker_types.Add(DocumentMarker::kGrammar); for (Node& node : NodeTraversal::InclusiveDescendantsOf(element)) { - if (elements_type == ElementsType::kAll || !HasEditableStyle(node)) - GetFrame().GetDocument()->Markers().RemoveMarkers(&node, marker_types); + if (elements_type == ElementsType::kAll || !HasEditableStyle(node)) { + GetFrame().GetDocument()->Markers().RemoveMarkersForNode(&node, + marker_types); + } } } @@ -926,11 +928,9 @@ void SpellChecker::RemoveSpellingMarkersUnderWords( const Vector<String>& words) { - MarkerRemoverPredicate remover_predicate(words); - DocumentMarkerController& marker_controller = GetFrame().GetDocument()->Markers(); - marker_controller.RemoveMarkers(remover_predicate); + marker_controller.RemoveSpellingMarkersUnderWords(words); marker_controller.RepaintMarkers(); }
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/ListWidget.js b/third_party/WebKit/Source/devtools/front_end/ui/ListWidget.js index 6a3a19e7..2d1aa06 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/ListWidget.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/ListWidget.js
@@ -14,6 +14,7 @@ this._delegate = delegate; this._list = this.contentElement.createChild('div', 'list'); + this.element.tabIndex = -1; /** @type {?UI.ListWidget.Editor} */ this._editor = null; @@ -144,6 +145,7 @@ */ function onRemoveClicked() { var index = this._elements.indexOf(element); + this.element.focus(); this._delegate.removeItemRequested(this._items[index], index); } } @@ -176,6 +178,7 @@ return; this._stopEditing(); + this._focusRestorer = new UI.ElementFocusRestorer(this.element); this._list.classList.add('list-editing'); this._editItem = item; @@ -202,6 +205,8 @@ _stopEditing() { this._list.classList.remove('list-editing'); + if (this._focusRestorer) + this._focusRestorer.restore(); if (this._editElement) this._editElement.classList.remove('hidden'); if (this._editor && this._editor.element.parentElement)
diff --git a/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp index 89531ff..31b094c8 100644 --- a/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp +++ b/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
@@ -426,6 +426,8 @@ if (isContextLost()) return; + DrawingBuffer::ScopedRGBEmulationForBlitFramebuffer emulation( + GetDrawingBuffer()); ContextGL()->BlitFramebufferCHROMIUM(src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter); }
diff --git a/third_party/WebKit/Source/platform/fonts/skia/FontCacheSkia.cpp b/third_party/WebKit/Source/platform/fonts/skia/FontCacheSkia.cpp index 68b5c2c..6d2ce53 100644 --- a/third_party/WebKit/Source/platform/fonts/skia/FontCacheSkia.cpp +++ b/third_party/WebKit/Source/platform/fonts/skia/FontCacheSkia.cpp
@@ -243,7 +243,7 @@ #if OS(WIN) if (sideloaded_fonts_) { HashMap<String, sk_sp<SkTypeface>>::iterator sideloaded_font = - sideloaded_fonts_->Find(name.Data()); + sideloaded_fonts_->Find(name.data()); if (sideloaded_font != sideloaded_fonts_->end()) return sideloaded_font->value; }
diff --git a/third_party/WebKit/Source/platform/fonts/win/FontCacheSkiaWin.cpp b/third_party/WebKit/Source/platform/fonts/win/FontCacheSkiaWin.cpp index be25c3a6..f4e3ff6 100644 --- a/third_party/WebKit/Source/platform/fonts/win/FontCacheSkiaWin.cpp +++ b/third_party/WebKit/Source/platform/fonts/win/FontCacheSkiaWin.cpp
@@ -152,7 +152,7 @@ CString family_name = font_description.Family().Family().Utf8(); SkTypeface* typeface = font_manager_->matchFamilyStyleCharacter( - family_name.Data(), font_description.SkiaFontStyle(), &bcp47_locale, + family_name.data(), font_description.SkiaFontStyle(), &bcp47_locale, locale_count, character); if (typeface) { SkString skia_family; @@ -391,7 +391,7 @@ std::unique_ptr<FontPlatformData> result = WTF::WrapUnique(new FontPlatformData( - tf, name.Data(), font_size, + tf, name.data(), font_size, (font_description.Weight() >= kFontWeight600 && !tf->isBold()) || font_description.IsSyntheticBold(), ((font_description.Style() == kFontStyleItalic ||
diff --git a/third_party/WebKit/Source/platform/fonts/win/FontFallbackWin.cpp b/third_party/WebKit/Source/platform/fonts/win/FontFallbackWin.cpp index 1e54e49..7c933e0 100644 --- a/third_party/WebKit/Source/platform/fonts/win/FontFallbackWin.cpp +++ b/third_party/WebKit/Source/platform/fonts/win/FontFallbackWin.cpp
@@ -50,7 +50,7 @@ SkFontMgr* font_manager) { String family = font_name; sk_sp<SkTypeface> tf( - font_manager->matchFamilyStyle(family.Utf8().Data(), SkFontStyle())); + font_manager->matchFamilyStyle(family.Utf8().data(), SkFontStyle())); if (!tf) return false;
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp index 15555873..d4f3845 100644 --- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp +++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp
@@ -576,6 +576,19 @@ return CreateColorBuffer(size_); } +DrawingBuffer::ScopedRGBEmulationForBlitFramebuffer:: + ScopedRGBEmulationForBlitFramebuffer(DrawingBuffer* drawing_buffer) + : drawing_buffer_(drawing_buffer) { + doing_work_ = drawing_buffer->SetupRGBEmulationForBlitFramebuffer(); +} + +DrawingBuffer::ScopedRGBEmulationForBlitFramebuffer:: + ~ScopedRGBEmulationForBlitFramebuffer() { + if (doing_work_) { + drawing_buffer_->CleanupRGBEmulationForBlitFramebuffer(); + } +} + DrawingBuffer::ColorBuffer::ColorBuffer( DrawingBuffer* drawing_buffer, const ColorBufferParameters& parameters, @@ -599,6 +612,10 @@ if (image_id) { gl->BindTexture(parameters.target, texture_id); gl->ReleaseTexImage2DCHROMIUM(parameters.target, image_id); + if (rgb_workaround_texture_id) { + gl->BindTexture(parameters.target, rgb_workaround_texture_id); + gl->ReleaseTexImage2DCHROMIUM(parameters.target, image_id); + } gl->DestroyImageCHROMIUM(image_id); switch (parameters.target) { case GL_TEXTURE_2D: @@ -618,6 +635,7 @@ gpu_memory_buffer.reset(); } gl->DeleteTextures(1, &texture_id); + gl->DeleteTextures(1, &rgb_workaround_texture_id); } bool DrawingBuffer::Initialize(const IntSize& size, bool use_multisampling) { @@ -1258,6 +1276,78 @@ return GL_RGB8_OES; } +bool DrawingBuffer::SetupRGBEmulationForBlitFramebuffer() { + // We only need to do this work if: + // - The user has selected alpha:false and antialias:false + // - We are using CHROMIUM_image with RGB emulation + // macOS is the only platform on which this is necessary. + + if (want_alpha_channel_ || anti_aliasing_mode_ != kNone) + return false; + + if (!(ShouldUseChromiumImage() && + ContextProvider()->GetCapabilities().chromium_image_rgb_emulation)) + return false; + + if (!back_color_buffer_) + return false; + + // If for some reason the back buffer doesn't have a CHROMIUM_image, + // don't proceed with this workaround. + if (!back_color_buffer_->image_id) + return false; + + // Before allowing the BlitFramebuffer call to go through, it's necessary + // to swap out the RGBA texture that's bound to the CHROMIUM_image + // instance with an RGB texture. BlitFramebuffer requires the internal + // formats of the source and destination to match when doing a + // multisample resolve, and the best way to achieve this without adding + // more full-screen blits is to hook up a true RGB texture to the + // underlying IOSurface. Unfortunately, on macOS, this rendering path + // destroys the alpha channel and requires a fixup afterward, which is + // why it isn't used all the time. + + GLuint rgb_texture = back_color_buffer_->rgb_workaround_texture_id; + GLenum target = GC3D_TEXTURE_RECTANGLE_ARB; + if (!rgb_texture) { + gl_->GenTextures(1, &rgb_texture); + gl_->BindTexture(target, rgb_texture); + gl_->TexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl_->TexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl_->TexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_->TexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // Bind this texture to the CHROMIUM_image instance that the color + // buffer owns. This is an expensive operation, so it's important that + // the result be cached. + gl_->BindTexImage2DWithInternalformatCHROMIUM(target, GL_RGB, + back_color_buffer_->image_id); + back_color_buffer_->rgb_workaround_texture_id = rgb_texture; + } + + gl_->FramebufferTexture2D(GL_DRAW_FRAMEBUFFER_ANGLE, GL_COLOR_ATTACHMENT0, + target, rgb_texture, 0); + return true; +} + +void DrawingBuffer::CleanupRGBEmulationForBlitFramebuffer() { + // This will only be called if SetupRGBEmulationForBlitFramebuffer was. + // Put the framebuffer back the way it was, and clear the alpha channel. + DCHECK(back_color_buffer_); + DCHECK(back_color_buffer_->image_id); + GLenum target = GC3D_TEXTURE_RECTANGLE_ARB; + gl_->FramebufferTexture2D(GL_DRAW_FRAMEBUFFER_ANGLE, GL_COLOR_ATTACHMENT0, + target, back_color_buffer_->texture_id, 0); + // Clear the alpha channel. + gl_->ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + gl_->Disable(GL_SCISSOR_TEST); + gl_->ClearColor(0, 0, 0, 1); + gl_->Clear(GL_COLOR_BUFFER_BIT); + DCHECK(client_); + client_->DrawingBufferClientRestoreScissorTest(); + client_->DrawingBufferClientRestoreMaskAndClearValues(); +} + DrawingBuffer::ScopedStateRestorer::ScopedStateRestorer( DrawingBuffer* drawing_buffer) : drawing_buffer_(drawing_buffer) {
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h index 56f1eea..b99c5e7 100644 --- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h +++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h
@@ -232,6 +232,19 @@ new_mailbox_callback_ = std::move(closure); } + // This class helps implement correct semantics for BlitFramebuffer + // when the DrawingBuffer is using a CHROMIUM image for its backing + // store and RGB emulation is in use (basically, macOS only). + class PLATFORM_EXPORT ScopedRGBEmulationForBlitFramebuffer { + public: + ScopedRGBEmulationForBlitFramebuffer(DrawingBuffer*); + ~ScopedRGBEmulationForBlitFramebuffer(); + + private: + RefPtr<DrawingBuffer> drawing_buffer_; + bool doing_work_ = false; + }; + protected: // For unittests DrawingBuffer(std::unique_ptr<WebGraphicsContext3DProvider>, std::unique_ptr<Extensions3DUtil>, @@ -257,6 +270,7 @@ Vector<RecycledBitmap> recycled_bitmaps_; private: + friend class ScopedRGBEmulationForBlitFramebuffer; friend class ScopedStateRestorer; friend class ColorBuffer; @@ -317,6 +331,16 @@ const GLuint image_id = 0; std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer; + // If we're emulating an RGB back buffer using an RGBA Chromium + // image (essentially macOS only), then when performing + // BlitFramebuffer calls, we have to swap in an RGB texture in + // place of the RGBA texture bound to the image. The reason is + // that BlitFramebuffer requires the internal formats of the + // source and destination to match (e.g. RGB8 on both sides). + // There are bugs in the semantics of RGB8 textures in this + // situation (the alpha channel is zeroed), requiring more fixups. + GLuint rgb_workaround_texture_id = 0; + // The mailbox used to send this buffer to the compositor. gpu::Mailbox mailbox; @@ -428,6 +452,11 @@ // The format to use when creating a multisampled renderbuffer. GLenum GetMultisampledRenderbufferFormat(); + // Helpers to ensure correct behavior of BlitFramebuffer when using + // an emulated RGB CHROMIUM_image back buffer. + bool SetupRGBEmulationForBlitFramebuffer(); + void CleanupRGBEmulationForBlitFramebuffer(); + // Weak, reset by beginDestruction. Client* client_ = nullptr; @@ -448,9 +477,9 @@ std::unique_ptr<WTF::Closure> new_mailbox_callback_; - // The current state restorer, which is used to track state dirtying. It is in + // The current state restorer, which is used to track state dirtying. It is an // error to dirty state shared with WebGL while there is no existing state - // restorer. It is also in error to instantiate two state restorers at once. + // restorer. ScopedStateRestorer* state_restorer_ = nullptr; // This is used when the user requests either a depth or stencil buffer.
diff --git a/third_party/WebKit/Source/platform/text/HyphenationTest.cpp b/third_party/WebKit/Source/platform/text/HyphenationTest.cpp index f60bbf3..533c8e6 100644 --- a/third_party/WebKit/Source/platform/text/HyphenationTest.cpp +++ b/third_party/WebKit/Source/platform/text/HyphenationTest.cpp
@@ -73,7 +73,7 @@ if (location_index > 0 && location == locations[location_index - 1]) location_index--; EXPECT_EQ(locations[location_index], location) << String::Format( - "lastHyphenLocation(%s, %zd)", word.Utf8().Data(), before_index); + "lastHyphenLocation(%s, %zd)", word.Utf8().data(), before_index); } EXPECT_EQ(location_index, 0u)
diff --git a/third_party/WebKit/Source/platform/text/LocaleMacTest.cpp b/third_party/WebKit/Source/platform/text/LocaleMacTest.cpp index 9091a75..e90c7ac 100644 --- a/third_party/WebKit/Source/platform/text/LocaleMacTest.cpp +++ b/third_party/WebKit/Source/platform/text/LocaleMacTest.cpp
@@ -193,74 +193,74 @@ TEST_F(LocaleMacTest, formatWeek) { ScopedTestingPlatformSupport<LocalePlatformSupport> support; - EXPECT_STREQ("Week 04, 2005", FormatWeek("en_US", "2005-W04").Utf8().Data()); - EXPECT_STREQ("Week 52, 2005", FormatWeek("en_US", "2005-W52").Utf8().Data()); + EXPECT_STREQ("Week 04, 2005", FormatWeek("en_US", "2005-W04").Utf8().data()); + EXPECT_STREQ("Week 52, 2005", FormatWeek("en_US", "2005-W52").Utf8().data()); } TEST_F(LocaleMacTest, formatMonth) { EXPECT_STREQ("April 2005", - FormatMonth("en_US", "2005-04", false).Utf8().Data()); + FormatMonth("en_US", "2005-04", false).Utf8().data()); EXPECT_STREQ("avril 2005", - FormatMonth("fr_FR", "2005-04", false).Utf8().Data()); + FormatMonth("fr_FR", "2005-04", false).Utf8().data()); EXPECT_STREQ( "2005\xE5\xB9\xB4" "04\xE6\x9C\x88", - FormatMonth("ja_JP", "2005-04", false).Utf8().Data()); + FormatMonth("ja_JP", "2005-04", false).Utf8().data()); - EXPECT_STREQ("Apr 2005", FormatMonth("en_US", "2005-04", true).Utf8().Data()); + EXPECT_STREQ("Apr 2005", FormatMonth("en_US", "2005-04", true).Utf8().data()); EXPECT_STREQ("avr. 2005", - FormatMonth("fr_FR", "2005-04", true).Utf8().Data()); + FormatMonth("fr_FR", "2005-04", true).Utf8().data()); EXPECT_STREQ( "2005\xE5\xB9\xB4" "04\xE6\x9C\x88", - FormatMonth("ja_JP", "2005-04", true).Utf8().Data()); + FormatMonth("ja_JP", "2005-04", true).Utf8().data()); } TEST_F(LocaleMacTest, formatDate) { EXPECT_STREQ("04/27/2005", - FormatDate("en_US", 2005, kApril, 27).Utf8().Data()); + FormatDate("en_US", 2005, kApril, 27).Utf8().data()); EXPECT_STREQ("27/04/2005", - FormatDate("fr_FR", 2005, kApril, 27).Utf8().Data()); + FormatDate("fr_FR", 2005, kApril, 27).Utf8().data()); // Do not test ja_JP locale. OS X 10.8 and 10.7 have different formats. } TEST_F(LocaleMacTest, formatTime) { EXPECT_STREQ("1:23 PM", - FormatTime("en_US", 13, 23, 00, 000, true).Utf8().Data()); + FormatTime("en_US", 13, 23, 00, 000, true).Utf8().data()); EXPECT_STREQ("13:23", - FormatTime("fr_FR", 13, 23, 00, 000, true).Utf8().Data()); + FormatTime("fr_FR", 13, 23, 00, 000, true).Utf8().data()); EXPECT_STREQ("13:23", - FormatTime("ja_JP", 13, 23, 00, 000, true).Utf8().Data()); + FormatTime("ja_JP", 13, 23, 00, 000, true).Utf8().data()); EXPECT_STREQ("\xD9\xA1:\xD9\xA2\xD9\xA3 \xD9\x85", - FormatTime("ar", 13, 23, 00, 000, true).Utf8().Data()); + FormatTime("ar", 13, 23, 00, 000, true).Utf8().data()); EXPECT_STREQ("\xDB\xB1\xDB\xB3:\xDB\xB2\xDB\xB3", - FormatTime("fa", 13, 23, 00, 000, true).Utf8().Data()); + FormatTime("fa", 13, 23, 00, 000, true).Utf8().data()); EXPECT_STREQ("12:00 AM", - FormatTime("en_US", 00, 00, 00, 000, true).Utf8().Data()); + FormatTime("en_US", 00, 00, 00, 000, true).Utf8().data()); EXPECT_STREQ("00:00", - FormatTime("fr_FR", 00, 00, 00, 000, true).Utf8().Data()); + FormatTime("fr_FR", 00, 00, 00, 000, true).Utf8().data()); EXPECT_STREQ("0:00", - FormatTime("ja_JP", 00, 00, 00, 000, true).Utf8().Data()); + FormatTime("ja_JP", 00, 00, 00, 000, true).Utf8().data()); EXPECT_STREQ("\xD9\xA1\xD9\xA2:\xD9\xA0\xD9\xA0 \xD8\xB5", - FormatTime("ar", 00, 00, 00, 000, true).Utf8().Data()); + FormatTime("ar", 00, 00, 00, 000, true).Utf8().data()); EXPECT_STREQ("\xDB\xB0:\xDB\xB0\xDB\xB0", - FormatTime("fa", 00, 00, 00, 000, true).Utf8().Data()); + FormatTime("fa", 00, 00, 00, 000, true).Utf8().data()); EXPECT_STREQ("7:07:07.007 AM", - FormatTime("en_US", 07, 07, 07, 007, false).Utf8().Data()); + FormatTime("en_US", 07, 07, 07, 007, false).Utf8().data()); EXPECT_STREQ("07:07:07,007", - FormatTime("fr_FR", 07, 07, 07, 007, false).Utf8().Data()); + FormatTime("fr_FR", 07, 07, 07, 007, false).Utf8().data()); EXPECT_STREQ("7:07:07.007", - FormatTime("ja_JP", 07, 07, 07, 007, false).Utf8().Data()); + FormatTime("ja_JP", 07, 07, 07, 007, false).Utf8().data()); EXPECT_STREQ( "\xD9\xA7:\xD9\xA0\xD9\xA7:" "\xD9\xA0\xD9\xA7\xD9\xAB\xD9\xA0\xD9\xA0\xD9\xA7 \xD8\xB5", - FormatTime("ar", 07, 07, 07, 007, false).Utf8().Data()); + FormatTime("ar", 07, 07, 07, 007, false).Utf8().data()); EXPECT_STREQ( "\xDB\xB7:\xDB\xB0\xDB\xB7:" "\xDB\xB0\xDB\xB7\xD9\xAB\xDB\xB0\xDB\xB0\xDB\xB7", - FormatTime("fa", 07, 07, 07, 007, false).Utf8().Data()); + FormatTime("fa", 07, 07, 07, 007, false).Utf8().data()); } TEST_F(LocaleMacTest, firstDayOfWeek) { @@ -270,37 +270,37 @@ } TEST_F(LocaleMacTest, monthLabels) { - EXPECT_STREQ("January", MonthLabel("en_US", kJanuary).Utf8().Data()); - EXPECT_STREQ("June", MonthLabel("en_US", kJune).Utf8().Data()); - EXPECT_STREQ("December", MonthLabel("en_US", kDecember).Utf8().Data()); + EXPECT_STREQ("January", MonthLabel("en_US", kJanuary).Utf8().data()); + EXPECT_STREQ("June", MonthLabel("en_US", kJune).Utf8().data()); + EXPECT_STREQ("December", MonthLabel("en_US", kDecember).Utf8().data()); - EXPECT_STREQ("janvier", MonthLabel("fr_FR", kJanuary).Utf8().Data()); - EXPECT_STREQ("juin", MonthLabel("fr_FR", kJune).Utf8().Data()); + EXPECT_STREQ("janvier", MonthLabel("fr_FR", kJanuary).Utf8().data()); + EXPECT_STREQ("juin", MonthLabel("fr_FR", kJune).Utf8().data()); EXPECT_STREQ( "d\xC3\xA9" "cembre", - MonthLabel("fr_FR", kDecember).Utf8().Data()); + MonthLabel("fr_FR", kDecember).Utf8().data()); - EXPECT_STREQ("1\xE6\x9C\x88", MonthLabel("ja_JP", kJanuary).Utf8().Data()); - EXPECT_STREQ("6\xE6\x9C\x88", MonthLabel("ja_JP", kJune).Utf8().Data()); - EXPECT_STREQ("12\xE6\x9C\x88", MonthLabel("ja_JP", kDecember).Utf8().Data()); + EXPECT_STREQ("1\xE6\x9C\x88", MonthLabel("ja_JP", kJanuary).Utf8().data()); + EXPECT_STREQ("6\xE6\x9C\x88", MonthLabel("ja_JP", kJune).Utf8().data()); + EXPECT_STREQ("12\xE6\x9C\x88", MonthLabel("ja_JP", kDecember).Utf8().data()); } TEST_F(LocaleMacTest, weekDayShortLabels) { - EXPECT_STREQ("Sun", WeekDayShortLabel("en_US", kSunday).Utf8().Data()); - EXPECT_STREQ("Wed", WeekDayShortLabel("en_US", kWednesday).Utf8().Data()); - EXPECT_STREQ("Sat", WeekDayShortLabel("en_US", kSaturday).Utf8().Data()); + EXPECT_STREQ("Sun", WeekDayShortLabel("en_US", kSunday).Utf8().data()); + EXPECT_STREQ("Wed", WeekDayShortLabel("en_US", kWednesday).Utf8().data()); + EXPECT_STREQ("Sat", WeekDayShortLabel("en_US", kSaturday).Utf8().data()); - EXPECT_STREQ("dim.", WeekDayShortLabel("fr_FR", kSunday).Utf8().Data()); - EXPECT_STREQ("mer.", WeekDayShortLabel("fr_FR", kWednesday).Utf8().Data()); - EXPECT_STREQ("sam.", WeekDayShortLabel("fr_FR", kSaturday).Utf8().Data()); + EXPECT_STREQ("dim.", WeekDayShortLabel("fr_FR", kSunday).Utf8().data()); + EXPECT_STREQ("mer.", WeekDayShortLabel("fr_FR", kWednesday).Utf8().data()); + EXPECT_STREQ("sam.", WeekDayShortLabel("fr_FR", kSaturday).Utf8().data()); EXPECT_STREQ("\xE6\x97\xA5", - WeekDayShortLabel("ja_JP", kSunday).Utf8().Data()); + WeekDayShortLabel("ja_JP", kSunday).Utf8().data()); EXPECT_STREQ("\xE6\xB0\xB4", - WeekDayShortLabel("ja_JP", kWednesday).Utf8().Data()); + WeekDayShortLabel("ja_JP", kWednesday).Utf8().data()); EXPECT_STREQ("\xE5\x9C\x9F", - WeekDayShortLabel("ja_JP", kSaturday).Utf8().Data()); + WeekDayShortLabel("ja_JP", kSaturday).Utf8().data()); } TEST_F(LocaleMacTest, isRTL) { @@ -311,9 +311,9 @@ } TEST_F(LocaleMacTest, monthFormat) { - EXPECT_STREQ("MMMM yyyy", MonthFormat("en_US").Utf8().Data()); + EXPECT_STREQ("MMMM yyyy", MonthFormat("en_US").Utf8().data()); EXPECT_STREQ("yyyy\xE5\xB9\xB4M\xE6\x9C\x88", - MonthFormat("ja_JP").Utf8().Data()); + MonthFormat("ja_JP").Utf8().data()); // fr_FR and ru return different results on OS versions. // "MMM yyyy" "LLL yyyy" on 10.6 and 10.7 @@ -321,96 +321,96 @@ } TEST_F(LocaleMacTest, timeFormat) { - EXPECT_STREQ("h:mm:ss a", TimeFormat("en_US").Utf8().Data()); - EXPECT_STREQ("HH:mm:ss", TimeFormat("fr_FR").Utf8().Data()); - EXPECT_STREQ("H:mm:ss", TimeFormat("ja_JP").Utf8().Data()); + EXPECT_STREQ("h:mm:ss a", TimeFormat("en_US").Utf8().data()); + EXPECT_STREQ("HH:mm:ss", TimeFormat("fr_FR").Utf8().data()); + EXPECT_STREQ("H:mm:ss", TimeFormat("ja_JP").Utf8().data()); } TEST_F(LocaleMacTest, shortTimeFormat) { - EXPECT_STREQ("h:mm a", ShortTimeFormat("en_US").Utf8().Data()); - EXPECT_STREQ("HH:mm", ShortTimeFormat("fr_FR").Utf8().Data()); - EXPECT_STREQ("H:mm", ShortTimeFormat("ja_JP").Utf8().Data()); + EXPECT_STREQ("h:mm a", ShortTimeFormat("en_US").Utf8().data()); + EXPECT_STREQ("HH:mm", ShortTimeFormat("fr_FR").Utf8().data()); + EXPECT_STREQ("H:mm", ShortTimeFormat("ja_JP").Utf8().data()); } TEST_F(LocaleMacTest, standAloneMonthLabels) { EXPECT_STREQ("January", - StandAloneMonthLabel("en_US", kJanuary).Utf8().Data()); - EXPECT_STREQ("June", StandAloneMonthLabel("en_US", kJune).Utf8().Data()); + StandAloneMonthLabel("en_US", kJanuary).Utf8().data()); + EXPECT_STREQ("June", StandAloneMonthLabel("en_US", kJune).Utf8().data()); EXPECT_STREQ("December", - StandAloneMonthLabel("en_US", kDecember).Utf8().Data()); + StandAloneMonthLabel("en_US", kDecember).Utf8().data()); EXPECT_STREQ("janvier", - StandAloneMonthLabel("fr_FR", kJanuary).Utf8().Data()); - EXPECT_STREQ("juin", StandAloneMonthLabel("fr_FR", kJune).Utf8().Data()); + StandAloneMonthLabel("fr_FR", kJanuary).Utf8().data()); + EXPECT_STREQ("juin", StandAloneMonthLabel("fr_FR", kJune).Utf8().data()); EXPECT_STREQ( "d\xC3\xA9" "cembre", - StandAloneMonthLabel("fr_FR", kDecember).Utf8().Data()); + StandAloneMonthLabel("fr_FR", kDecember).Utf8().data()); EXPECT_STREQ("1\xE6\x9C\x88", - StandAloneMonthLabel("ja_JP", kJanuary).Utf8().Data()); + StandAloneMonthLabel("ja_JP", kJanuary).Utf8().data()); EXPECT_STREQ("6\xE6\x9C\x88", - StandAloneMonthLabel("ja_JP", kJune).Utf8().Data()); + StandAloneMonthLabel("ja_JP", kJune).Utf8().data()); EXPECT_STREQ("12\xE6\x9C\x88", - StandAloneMonthLabel("ja_JP", kDecember).Utf8().Data()); + StandAloneMonthLabel("ja_JP", kDecember).Utf8().data()); } TEST_F(LocaleMacTest, shortMonthLabels) { - EXPECT_STREQ("Jan", ShortMonthLabel("en_US", 0).Utf8().Data()); - EXPECT_STREQ("Jan", ShortStandAloneMonthLabel("en_US", 0).Utf8().Data()); - EXPECT_STREQ("Dec", ShortMonthLabel("en_US", 11).Utf8().Data()); - EXPECT_STREQ("Dec", ShortStandAloneMonthLabel("en_US", 11).Utf8().Data()); + EXPECT_STREQ("Jan", ShortMonthLabel("en_US", 0).Utf8().data()); + EXPECT_STREQ("Jan", ShortStandAloneMonthLabel("en_US", 0).Utf8().data()); + EXPECT_STREQ("Dec", ShortMonthLabel("en_US", 11).Utf8().data()); + EXPECT_STREQ("Dec", ShortStandAloneMonthLabel("en_US", 11).Utf8().data()); - EXPECT_STREQ("janv.", ShortMonthLabel("fr_FR", 0).Utf8().Data()); - EXPECT_STREQ("janv.", ShortStandAloneMonthLabel("fr_FR", 0).Utf8().Data()); + EXPECT_STREQ("janv.", ShortMonthLabel("fr_FR", 0).Utf8().data()); + EXPECT_STREQ("janv.", ShortStandAloneMonthLabel("fr_FR", 0).Utf8().data()); EXPECT_STREQ( "d\xC3\xA9" "c.", - ShortMonthLabel("fr_FR", 11).Utf8().Data()); + ShortMonthLabel("fr_FR", 11).Utf8().data()); EXPECT_STREQ( "d\xC3\xA9" "c.", - ShortStandAloneMonthLabel("fr_FR", 11).Utf8().Data()); + ShortStandAloneMonthLabel("fr_FR", 11).Utf8().data()); - EXPECT_STREQ("1\xE6\x9C\x88", ShortMonthLabel("ja_JP", 0).Utf8().Data()); + EXPECT_STREQ("1\xE6\x9C\x88", ShortMonthLabel("ja_JP", 0).Utf8().data()); EXPECT_STREQ("1\xE6\x9C\x88", - ShortStandAloneMonthLabel("ja_JP", 0).Utf8().Data()); - EXPECT_STREQ("12\xE6\x9C\x88", ShortMonthLabel("ja_JP", 11).Utf8().Data()); + ShortStandAloneMonthLabel("ja_JP", 0).Utf8().data()); + EXPECT_STREQ("12\xE6\x9C\x88", ShortMonthLabel("ja_JP", 11).Utf8().data()); EXPECT_STREQ("12\xE6\x9C\x88", - ShortStandAloneMonthLabel("ja_JP", 11).Utf8().Data()); + ShortStandAloneMonthLabel("ja_JP", 11).Utf8().data()); EXPECT_STREQ("\xD0\xBC\xD0\xB0\xD1\x80\xD1\x82\xD0\xB0", - ShortMonthLabel("ru_RU", 2).Utf8().Data()); + ShortMonthLabel("ru_RU", 2).Utf8().data()); EXPECT_STREQ("\xD0\xBC\xD0\xB0\xD1\x8F", - ShortMonthLabel("ru_RU", 4).Utf8().Data()); + ShortMonthLabel("ru_RU", 4).Utf8().data()); // The ru_RU locale returns different stand-alone month labels on OS versions. // "\xD0\xBC\xD0\xB0\xD1\x80\xD1\x82" "\xD0\xBC\xD0\xB0\xD0\xB9" on 10.7 // "\xD0\x9C\xD0\xB0\xD1\x80\xD1\x82" "\xD0\x9C\xD0\xB0\xD0\xB9" on 10.8 } TEST_F(LocaleMacTest, timeAMPMLabels) { - EXPECT_STREQ("AM", TimeAMPMLabel("en_US", 0).Utf8().Data()); - EXPECT_STREQ("PM", TimeAMPMLabel("en_US", 1).Utf8().Data()); + EXPECT_STREQ("AM", TimeAMPMLabel("en_US", 0).Utf8().data()); + EXPECT_STREQ("PM", TimeAMPMLabel("en_US", 1).Utf8().data()); - EXPECT_STREQ("AM", TimeAMPMLabel("fr_FR", 0).Utf8().Data()); - EXPECT_STREQ("PM", TimeAMPMLabel("fr_FR", 1).Utf8().Data()); + EXPECT_STREQ("AM", TimeAMPMLabel("fr_FR", 0).Utf8().data()); + EXPECT_STREQ("PM", TimeAMPMLabel("fr_FR", 1).Utf8().data()); EXPECT_STREQ("\xE5\x8D\x88\xE5\x89\x8D", - TimeAMPMLabel("ja_JP", 0).Utf8().Data()); + TimeAMPMLabel("ja_JP", 0).Utf8().data()); EXPECT_STREQ("\xE5\x8D\x88\xE5\xBE\x8C", - TimeAMPMLabel("ja_JP", 1).Utf8().Data()); + TimeAMPMLabel("ja_JP", 1).Utf8().data()); } TEST_F(LocaleMacTest, decimalSeparator) { - EXPECT_STREQ(".", DecimalSeparator("en_US").Utf8().Data()); - EXPECT_STREQ(",", DecimalSeparator("fr_FR").Utf8().Data()); + EXPECT_STREQ(".", DecimalSeparator("en_US").Utf8().data()); + EXPECT_STREQ(",", DecimalSeparator("fr_FR").Utf8().data()); } TEST_F(LocaleMacTest, invalidLocale) { - EXPECT_STREQ(MonthLabel("en_US", kJanuary).Utf8().Data(), - MonthLabel("foo", kJanuary).Utf8().Data()); - EXPECT_STREQ(DecimalSeparator("en_US").Utf8().Data(), - DecimalSeparator("foo").Utf8().Data()); + EXPECT_STREQ(MonthLabel("en_US", kJanuary).Utf8().data(), + MonthLabel("foo", kJanuary).Utf8().data()); + EXPECT_STREQ(DecimalSeparator("en_US").Utf8().data(), + DecimalSeparator("foo").Utf8().data()); } static void TestNumberIsReversible(const AtomicString& locale_string, @@ -421,7 +421,7 @@ if (should_have) EXPECT_TRUE(localized.Contains(should_have)); String converted = locale->ConvertFromLocalizedNumber(localized); - EXPECT_STREQ(original, converted.Utf8().Data()); + EXPECT_STREQ(original, converted.Utf8().data()); } void TestNumbers(const AtomicString& locale_string,
diff --git a/third_party/WebKit/Source/platform/text/LocaleWinTest.cpp b/third_party/WebKit/Source/platform/text/LocaleWinTest.cpp index 460c534..0b4685f2 100644 --- a/third_party/WebKit/Source/platform/text/LocaleWinTest.cpp +++ b/third_party/WebKit/Source/platform/text/LocaleWinTest.cpp
@@ -161,11 +161,11 @@ TEST_F(LocaleWinTest, formatDate) { EXPECT_STREQ("04/27/2005", - FormatDate(kEnglishUS, 2005, kApril, 27).Utf8().Data()); + FormatDate(kEnglishUS, 2005, kApril, 27).Utf8().data()); EXPECT_STREQ("27/04/2005", - FormatDate(kFrenchFR, 2005, kApril, 27).Utf8().Data()); + FormatDate(kFrenchFR, 2005, kApril, 27).Utf8().data()); EXPECT_STREQ("2005/04/27", - FormatDate(kJapaneseJP, 2005, kApril, 27).Utf8().Data()); + FormatDate(kJapaneseJP, 2005, kApril, 27).Utf8().data()); } TEST_F(LocaleWinTest, firstDayOfWeek) { @@ -175,39 +175,39 @@ } TEST_F(LocaleWinTest, monthLabels) { - EXPECT_STREQ("January", MonthLabel(kEnglishUS, kJanuary).Utf8().Data()); - EXPECT_STREQ("June", MonthLabel(kEnglishUS, kJune).Utf8().Data()); - EXPECT_STREQ("December", MonthLabel(kEnglishUS, kDecember).Utf8().Data()); + EXPECT_STREQ("January", MonthLabel(kEnglishUS, kJanuary).Utf8().data()); + EXPECT_STREQ("June", MonthLabel(kEnglishUS, kJune).Utf8().data()); + EXPECT_STREQ("December", MonthLabel(kEnglishUS, kDecember).Utf8().data()); - EXPECT_STREQ("janvier", MonthLabel(kFrenchFR, kJanuary).Utf8().Data()); - EXPECT_STREQ("juin", MonthLabel(kFrenchFR, kJune).Utf8().Data()); + EXPECT_STREQ("janvier", MonthLabel(kFrenchFR, kJanuary).Utf8().data()); + EXPECT_STREQ("juin", MonthLabel(kFrenchFR, kJune).Utf8().data()); EXPECT_STREQ( "d\xC3\xA9" "cembre", - MonthLabel(kFrenchFR, kDecember).Utf8().Data()); + MonthLabel(kFrenchFR, kDecember).Utf8().data()); EXPECT_STREQ("1\xE6\x9C\x88", - MonthLabel(kJapaneseJP, kJanuary).Utf8().Data()); - EXPECT_STREQ("6\xE6\x9C\x88", MonthLabel(kJapaneseJP, kJune).Utf8().Data()); + MonthLabel(kJapaneseJP, kJanuary).Utf8().data()); + EXPECT_STREQ("6\xE6\x9C\x88", MonthLabel(kJapaneseJP, kJune).Utf8().data()); EXPECT_STREQ("12\xE6\x9C\x88", - MonthLabel(kJapaneseJP, kDecember).Utf8().Data()); + MonthLabel(kJapaneseJP, kDecember).Utf8().data()); } TEST_F(LocaleWinTest, weekDayShortLabels) { - EXPECT_STREQ("Sun", WeekDayShortLabel(kEnglishUS, kSunday).Utf8().Data()); - EXPECT_STREQ("Wed", WeekDayShortLabel(kEnglishUS, kWednesday).Utf8().Data()); - EXPECT_STREQ("Sat", WeekDayShortLabel(kEnglishUS, kSaturday).Utf8().Data()); + EXPECT_STREQ("Sun", WeekDayShortLabel(kEnglishUS, kSunday).Utf8().data()); + EXPECT_STREQ("Wed", WeekDayShortLabel(kEnglishUS, kWednesday).Utf8().data()); + EXPECT_STREQ("Sat", WeekDayShortLabel(kEnglishUS, kSaturday).Utf8().data()); - EXPECT_STREQ("dim.", WeekDayShortLabel(kFrenchFR, kSunday).Utf8().Data()); - EXPECT_STREQ("mer.", WeekDayShortLabel(kFrenchFR, kWednesday).Utf8().Data()); - EXPECT_STREQ("sam.", WeekDayShortLabel(kFrenchFR, kSaturday).Utf8().Data()); + EXPECT_STREQ("dim.", WeekDayShortLabel(kFrenchFR, kSunday).Utf8().data()); + EXPECT_STREQ("mer.", WeekDayShortLabel(kFrenchFR, kWednesday).Utf8().data()); + EXPECT_STREQ("sam.", WeekDayShortLabel(kFrenchFR, kSaturday).Utf8().data()); EXPECT_STREQ("\xE6\x97\xA5", - WeekDayShortLabel(kJapaneseJP, kSunday).Utf8().Data()); + WeekDayShortLabel(kJapaneseJP, kSunday).Utf8().data()); EXPECT_STREQ("\xE6\xB0\xB4", - WeekDayShortLabel(kJapaneseJP, kWednesday).Utf8().Data()); + WeekDayShortLabel(kJapaneseJP, kWednesday).Utf8().data()); EXPECT_STREQ("\xE5\x9C\x9F", - WeekDayShortLabel(kJapaneseJP, kSaturday).Utf8().Data()); + WeekDayShortLabel(kJapaneseJP, kSaturday).Utf8().data()); } TEST_F(LocaleWinTest, isRTL) { @@ -216,13 +216,13 @@ } TEST_F(LocaleWinTest, dateFormat) { - EXPECT_STREQ("y-M-d", LocaleWin::DateFormat("y-M-d").Utf8().Data()); + EXPECT_STREQ("y-M-d", LocaleWin::DateFormat("y-M-d").Utf8().data()); EXPECT_STREQ("''yy'-'''MM'''-'dd", - LocaleWin::DateFormat("''yy-''MM''-dd").Utf8().Data()); + LocaleWin::DateFormat("''yy-''MM''-dd").Utf8().data()); EXPECT_STREQ("yyyy'-''''-'MMM'''''-'dd", - LocaleWin::DateFormat("yyyy-''''-MMM''''-dd").Utf8().Data()); + LocaleWin::DateFormat("yyyy-''''-MMM''''-dd").Utf8().data()); EXPECT_STREQ("yyyy'-'''''MMMM-dd", - LocaleWin::DateFormat("yyyy-''''MMMM-dd").Utf8().Data()); + LocaleWin::DateFormat("yyyy-''''MMMM-dd").Utf8().data()); } TEST_F(LocaleWinTest, monthFormat) { @@ -230,52 +230,52 @@ // "MMMM, yyyy" on Windows 7 or older. // "MMMM yyyy" on Window 8 or later. EXPECT_STREQ("MMMM yyyy", - MonthFormat(kEnglishUS).Replace(',', "").Utf8().Data()); - EXPECT_STREQ("MMMM yyyy", MonthFormat(kFrenchFR).Utf8().Data()); + MonthFormat(kEnglishUS).Replace(',', "").Utf8().data()); + EXPECT_STREQ("MMMM yyyy", MonthFormat(kFrenchFR).Utf8().data()); EXPECT_STREQ("yyyy\xE5\xB9\xB4M\xE6\x9C\x88", - MonthFormat(kJapaneseJP).Utf8().Data()); + MonthFormat(kJapaneseJP).Utf8().data()); } TEST_F(LocaleWinTest, timeFormat) { - EXPECT_STREQ("h:mm:ss a", TimeFormat(kEnglishUS).Utf8().Data()); - EXPECT_STREQ("HH:mm:ss", TimeFormat(kFrenchFR).Utf8().Data()); - EXPECT_STREQ("H:mm:ss", TimeFormat(kJapaneseJP).Utf8().Data()); + EXPECT_STREQ("h:mm:ss a", TimeFormat(kEnglishUS).Utf8().data()); + EXPECT_STREQ("HH:mm:ss", TimeFormat(kFrenchFR).Utf8().data()); + EXPECT_STREQ("H:mm:ss", TimeFormat(kJapaneseJP).Utf8().data()); } TEST_F(LocaleWinTest, shortTimeFormat) { - EXPECT_STREQ("h:mm a", ShortTimeFormat(kEnglishUS).Utf8().Data()); - EXPECT_STREQ("HH:mm", ShortTimeFormat(kFrenchFR).Utf8().Data()); - EXPECT_STREQ("H:mm", ShortTimeFormat(kJapaneseJP).Utf8().Data()); + EXPECT_STREQ("h:mm a", ShortTimeFormat(kEnglishUS).Utf8().data()); + EXPECT_STREQ("HH:mm", ShortTimeFormat(kFrenchFR).Utf8().data()); + EXPECT_STREQ("H:mm", ShortTimeFormat(kJapaneseJP).Utf8().data()); } TEST_F(LocaleWinTest, shortMonthLabels) { - EXPECT_STREQ("Jan", ShortMonthLabel(kEnglishUS, 0).Utf8().Data()); - EXPECT_STREQ("Dec", ShortMonthLabel(kEnglishUS, 11).Utf8().Data()); - EXPECT_STREQ("janv.", ShortMonthLabel(kFrenchFR, 0).Utf8().Data()); + EXPECT_STREQ("Jan", ShortMonthLabel(kEnglishUS, 0).Utf8().data()); + EXPECT_STREQ("Dec", ShortMonthLabel(kEnglishUS, 11).Utf8().data()); + EXPECT_STREQ("janv.", ShortMonthLabel(kFrenchFR, 0).Utf8().data()); EXPECT_STREQ( "d\xC3\xA9" "c.", - ShortMonthLabel(kFrenchFR, 11).Utf8().Data()); - EXPECT_STREQ("1", ShortMonthLabel(kJapaneseJP, 0).Utf8().Data()); - EXPECT_STREQ("12", ShortMonthLabel(kJapaneseJP, 11).Utf8().Data()); + ShortMonthLabel(kFrenchFR, 11).Utf8().data()); + EXPECT_STREQ("1", ShortMonthLabel(kJapaneseJP, 0).Utf8().data()); + EXPECT_STREQ("12", ShortMonthLabel(kJapaneseJP, 11).Utf8().data()); } TEST_F(LocaleWinTest, timeAMPMLabels) { - EXPECT_STREQ("AM", TimeAMPMLabel(kEnglishUS, 0).Utf8().Data()); - EXPECT_STREQ("PM", TimeAMPMLabel(kEnglishUS, 1).Utf8().Data()); + EXPECT_STREQ("AM", TimeAMPMLabel(kEnglishUS, 0).Utf8().data()); + EXPECT_STREQ("PM", TimeAMPMLabel(kEnglishUS, 1).Utf8().data()); - EXPECT_STREQ("", TimeAMPMLabel(kFrenchFR, 0).Utf8().Data()); - EXPECT_STREQ("", TimeAMPMLabel(kFrenchFR, 1).Utf8().Data()); + EXPECT_STREQ("", TimeAMPMLabel(kFrenchFR, 0).Utf8().data()); + EXPECT_STREQ("", TimeAMPMLabel(kFrenchFR, 1).Utf8().data()); EXPECT_STREQ("\xE5\x8D\x88\xE5\x89\x8D", - TimeAMPMLabel(kJapaneseJP, 0).Utf8().Data()); + TimeAMPMLabel(kJapaneseJP, 0).Utf8().data()); EXPECT_STREQ("\xE5\x8D\x88\xE5\xBE\x8C", - TimeAMPMLabel(kJapaneseJP, 1).Utf8().Data()); + TimeAMPMLabel(kJapaneseJP, 1).Utf8().data()); } TEST_F(LocaleWinTest, decimalSeparator) { - EXPECT_STREQ(".", DecimalSeparator(kEnglishUS).Utf8().Data()); - EXPECT_STREQ(",", DecimalSeparator(kFrenchFR).Utf8().Data()); + EXPECT_STREQ(".", DecimalSeparator(kEnglishUS).Utf8().data()); + EXPECT_STREQ(",", DecimalSeparator(kFrenchFR).Utf8().data()); } static void TestNumberIsReversible(LCID lcid, @@ -287,7 +287,7 @@ if (should_have) EXPECT_TRUE(localized.Contains(should_have)); String converted = locale->ConvertFromLocalizedNumber(localized); - EXPECT_STREQ(original, converted.Utf8().Data()); + EXPECT_STREQ(original, converted.Utf8().data()); } void TestNumbers(LCID lcid) {
diff --git a/third_party/WebKit/Source/platform/wtf/Vector.h b/third_party/WebKit/Source/platform/wtf/Vector.h index 07d10c01..71d9179 100644 --- a/third_party/WebKit/Source/platform/wtf/Vector.h +++ b/third_party/WebKit/Source/platform/wtf/Vector.h
@@ -1016,10 +1016,6 @@ T* data() { return Base::Buffer(); } const T* data() const { return Base::Buffer(); } - // TODO(dcheng): Temporary alias to make removing this easier. - T* Data() { return data(); } - const T* Data() const { return data(); } - // Iterators and reverse iterators. They are invalidated on a reallocation. iterator begin() { return data(); } iterator end() { return begin() + size_; }
diff --git a/third_party/WebKit/Source/platform/wtf/VectorTest.cpp b/third_party/WebKit/Source/platform/wtf/VectorTest.cpp index 9b8a789..aadaa94 100644 --- a/third_party/WebKit/Source/platform/wtf/VectorTest.cpp +++ b/third_party/WebKit/Source/platform/wtf/VectorTest.cpp
@@ -365,7 +365,7 @@ vector_a.push_back(10); vector_a.ReserveCapacity(32); - volatile int* int_pointer_a = vector_a.Data(); + volatile int* int_pointer_a = vector_a.data(); EXPECT_DEATH(int_pointer_a[1] = 11, "container-overflow"); vector_a.push_back(11); int_pointer_a[1] = 11; @@ -373,28 +373,28 @@ EXPECT_DEATH((void)int_pointer_a[2], "container-overflow"); vector_a.ShrinkToFit(); vector_a.ReserveCapacity(16); - int_pointer_a = vector_a.Data(); + int_pointer_a = vector_a.data(); EXPECT_DEATH((void)int_pointer_a[2], "container-overflow"); Vector<int> vector_b(vector_a); vector_b.ReserveCapacity(16); - volatile int* int_pointer_b = vector_b.Data(); + volatile int* int_pointer_b = vector_b.data(); EXPECT_DEATH((void)int_pointer_b[2], "container-overflow"); Vector<int> vector_c((Vector<int>(vector_a))); - volatile int* int_pointer_c = vector_c.Data(); + volatile int* int_pointer_c = vector_c.data(); EXPECT_DEATH((void)int_pointer_c[2], "container-overflow"); vector_c.push_back(13); vector_c.Swap(vector_b); - volatile int* int_pointer_b2 = vector_b.Data(); - volatile int* int_pointer_c2 = vector_c.Data(); + volatile int* int_pointer_b2 = vector_b.data(); + volatile int* int_pointer_c2 = vector_c.data(); int_pointer_b2[2] = 13; EXPECT_DEATH((void)int_pointer_b2[3], "container-overflow"); EXPECT_DEATH((void)int_pointer_c2[2], "container-overflow"); vector_b = vector_c; - volatile int* int_pointer_b3 = vector_b.Data(); + volatile int* int_pointer_b3 = vector_b.data(); EXPECT_DEATH((void)int_pointer_b3[2], "container-overflow"); } #endif // defined(ANNOTATE_CONTIGUOUS_CONTAINER)
diff --git a/third_party/WebKit/Source/platform/wtf/text/AtomicStringCF.cpp b/third_party/WebKit/Source/platform/wtf/text/AtomicStringCF.cpp index 90f7224..edd953d7 100644 --- a/third_party/WebKit/Source/platform/wtf/text/AtomicStringCF.cpp +++ b/third_party/WebKit/Source/platform/wtf/text/AtomicStringCF.cpp
@@ -49,9 +49,9 @@ reinterpret_cast<const UChar*>(ptr), length); Vector<UniChar, 1024> uchar_buffer(length); - CFStringGetCharacters(string, CFRangeMake(0, length), uchar_buffer.Data()); + CFStringGetCharacters(string, CFRangeMake(0, length), uchar_buffer.data()); return AtomicStringTable::Instance().Add( - reinterpret_cast<const UChar*>(uchar_buffer.Data()), length); + reinterpret_cast<const UChar*>(uchar_buffer.data()), length); } } // namespace WTF
diff --git a/third_party/WebKit/Source/platform/wtf/text/CString.h b/third_party/WebKit/Source/platform/wtf/text/CString.h index 79ccbf5b..08712ae 100644 --- a/third_party/WebKit/Source/platform/wtf/text/CString.h +++ b/third_party/WebKit/Source/platform/wtf/text/CString.h
@@ -84,8 +84,6 @@ // The bytes of the string, always NUL terminated. May be null. const char* data() const { return buffer_ ? buffer_->data() : 0; } - // TODO(dcheng): Temporary alias to make removing this easier. - const char* Data() const { return data(); } // The length of the data(), *not* including the NUL terminator. size_t length() const { return buffer_ ? buffer_->length() : 0; }
diff --git a/third_party/WebKit/Source/platform/wtf/text/StringMac.mm b/third_party/WebKit/Source/platform/wtf/text/StringMac.mm index 029581f4..fd97f7c 100644 --- a/third_party/WebKit/Source/platform/wtf/text/StringMac.mm +++ b/third_party/WebKit/Source/platform/wtf/text/StringMac.mm
@@ -37,16 +37,16 @@ CFIndex convertedsize = CFStringGetBytes(reinterpret_cast<CFStringRef>(str), CFRangeMake(0, size), kCFStringEncodingISOLatin1, 0, - false, lchar_buffer.Data(), size, &used_buf_len); + false, lchar_buffer.data(), size, &used_buf_len); if ((convertedsize == size) && (used_buf_len == size)) { - impl_ = StringImpl::Create(lchar_buffer.Data(), size); + impl_ = StringImpl::Create(lchar_buffer.data(), size); return; } Vector<UChar, 1024> uchar_buffer(size); CFStringGetCharacters(reinterpret_cast<CFStringRef>(str), - CFRangeMake(0, size), uchar_buffer.Data()); - impl_ = StringImpl::Create(uchar_buffer.Data(), size); + CFRangeMake(0, size), uchar_buffer.data()); + impl_ = StringImpl::Create(uchar_buffer.data(), size); } }
diff --git a/third_party/polymer/v1_0/chromium.patch b/third_party/polymer/v1_0/chromium.patch index 8b99b23..79c151d 100644 --- a/third_party/polymer/v1_0/chromium.patch +++ b/third_party/polymer/v1_0/chromium.patch
@@ -24,3 +24,21 @@ }, _ariaDescribedByChanged: function(ariaDescribedBy) { +diff --git a/components-chromium/paper-icon-button/paper-icon-button-light-extracted.js b/components-chromium/paper-icon-button/paper-icon-button-light-extracted.js +index bac589c7274c..bc25f54320fb 100644 +--- a/components-chromium/paper-icon-button/paper-icon-button-light-extracted.js ++++ b/components-chromium/paper-icon-button/paper-icon-button-light-extracted.js +@@ -14,11 +14,11 @@ Polymer({ + }, + + _rippleDown: function() { +- this.getRipple().downAction(); ++ this.getRipple().uiDownAction(); + }, + + _rippleUp: function() { +- this.getRipple().upAction(); ++ this.getRipple().uiUpAction(); + }, + + /**
diff --git a/third_party/polymer/v1_0/components-chromium/paper-icon-button/paper-icon-button-light-extracted.js b/third_party/polymer/v1_0/components-chromium/paper-icon-button/paper-icon-button-light-extracted.js index bac589c..bcf06c2 100644 --- a/third_party/polymer/v1_0/components-chromium/paper-icon-button/paper-icon-button-light-extracted.js +++ b/third_party/polymer/v1_0/components-chromium/paper-icon-button/paper-icon-button-light-extracted.js
@@ -14,11 +14,11 @@ }, _rippleDown: function() { - this.getRipple().downAction(); + this.getRipple().uiDownAction(); }, _rippleUp: function() { - this.getRipple().upAction(); + this.getRipple().uiUpAction(); }, /**
diff --git a/tools/codesearch/OWNERS b/tools/codesearch/OWNERS new file mode 100644 index 0000000..cd64b333d --- /dev/null +++ b/tools/codesearch/OWNERS
@@ -0,0 +1,4 @@ +asanka@chromium.org +jkarlin@chromium.org + +# COMPONENT: Tools
diff --git a/tools/codesearch/README.md b/tools/codesearch/README.md new file mode 100644 index 0000000..a2888caa --- /dev/null +++ b/tools/codesearch/README.md
@@ -0,0 +1,49 @@ +Chromium CodeSearch Library +=========================== + +The `codesearch` Python library provides an interface for talking to the +Chromium CodeSearch backend at https://cs.chromium.org/ + +The primary entry point into the library is the `CodeSearch` class. Various +message classes you are likely to encounter are defined in `messages.py`. + +A quick example: + +```py +import codesearch + +# The plugin needs to locate a local Chromium checkout. We are passing '.' as a +# path inside the source directory, which works if the current directory is +# inside the Chromium checkout. +cs = codesearch.CodeSearch(a_path_inside_source_dir='.') + +# The backend takes CompoundRequests ... +results = cs.SendRequestToServer(codesearch.CompoundRequest( + search_request=[ + codesearch.SearchRequest(query='hello world') + ])) + +# ... and returns a CompoundResponse +assert isinstance(results, codesearch.CompoundResponse) + +# both CompoundRequest and CompoundResponse are documented in messages.py. + +for search_result in results.search_response[0].search_result: + assert isinstance(search_result, codesearch.SearchResult) + + if not hasattr(search_result, 'snippet'): + continue + + for snippet in search_result.snippet: + assert isinstance(snippet, codesearch.Snippet) + + # Just print the text of the search result snippet. + print snippet.text.text +``` + +NOTE: This isn't quite production quality code and the infra folks may pull the rug +from under the library at any point. + +If you run into any bugs, or you'd like to contribute, please let someone in the +`OWNERS` file know. +
diff --git a/tools/codesearch/__init__.py b/tools/codesearch/__init__.py new file mode 100644 index 0000000..ca8ba6f --- /dev/null +++ b/tools/codesearch/__init__.py
@@ -0,0 +1,5 @@ +# Copyright 2017 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. + +__all__ = ['codesearch']
diff --git a/tools/codesearch/ccs b/tools/codesearch/ccs new file mode 100755 index 0000000..5500cf7 --- /dev/null +++ b/tools/codesearch/ccs
@@ -0,0 +1,8 @@ +#! /usr/bin/env python + +# Copyright 2017 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 runpy +runpy.run_module('codesearch')
diff --git a/tools/codesearch/codesearch/__init__.py b/tools/codesearch/codesearch/__init__.py new file mode 100644 index 0000000..5ea5adc --- /dev/null +++ b/tools/codesearch/codesearch/__init__.py
@@ -0,0 +1,22 @@ +# Copyright 2017 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. + +from __future__ import absolute_import + +from .client_api import CodeSearch +from .messages import Message, AnnotationTypeValue, AnnotationType, \ + InternalLink, XrefSignature, NodeEnumKind, KytheNodeEnumKind, \ + Annotation, FileSpec, FormatType, FormatRange, AnnotatedText, \ + CodeBlockType, Modifiers, CodeBlock, FileInfo, FileInfoResponse, \ + FileInfoRequest, AnnotationResponse, MatchReason, Snippet, Node, \ + CallGraphResponse, CallGraphRequest, EdgeEnumKind, XrefTypeCount, \ + XrefSingleMatch, XrefSearchResult, XrefSearchResponse, \ + XrefSearchRequest, VanityGitOnBorgHostname, InternalPackage, \ + StatusResponse, GobInfo, DirInfoResponseChild, DirInfoResponse, \ + DirInfoRequest, FileResult, SingleMatch, SearchResult, SearchResponse, \ + SearchRequest, StatusRequest, CompoundResponse, CompoundRequest, \ + CodeSearchProtoJsonEncoder, CodeSearchProtoJsonSymbolizedEncoder +from .paths import GetPackageRelativePath, GetSourceRoot + +__all__ = []
diff --git a/tools/codesearch/codesearch/__main__.py b/tools/codesearch/codesearch/__main__.py new file mode 100644 index 0000000..9560abb --- /dev/null +++ b/tools/codesearch/codesearch/__main__.py
@@ -0,0 +1,245 @@ +# Copyright 2017 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. +"""Command line interface to Chromium Code Search. + +Currently emits JSON formatted responses from the Chromium CodeSearch backend at +https://cs.chromium.org. +""" + +from __future__ import absolute_import + +import os +import argparse +import sys +import json +import logging + +from codesearch import CodeSearch, CompoundRequest, \ + CodeSearchProtoJsonSymbolizedEncoder, CodeSearchProtoJsonEncoder, \ + XrefSearchRequest, SearchRequest, FileInfoRequest, DirInfoRequest, \ + CallGraphRequest + + +def print_result(results, args): + if args.pretty: + print json.dumps( + results, + indent=4, + ensure_ascii=True, + cls=CodeSearchProtoJsonSymbolizedEncoder) + else: + print json.dumps(results, cls=CodeSearchProtoJsonEncoder) + + +def get_signature(cs, args): + if args.signature: + return args.signature + + if not (args.path and args.word): + print('Both PATH and WORD must be specified') + sys.exit(2) + + return cs.GetSignatureForSymbol(args.path, args.word) + + +def setup_logging(cs, args): + if args.loglevel: + if args.loglevel == 'info': + level = logging.INFO + else: + level = logging.DEBUG + cs.GetLogger().setLevel(level) + logging.basicConfig() + + +parser = argparse.ArgumentParser(description=__doc__) + +subcommands = parser.add_subparsers(help='Subcommands') + +path_specifiers = argparse.ArgumentParser(add_help=False) +path_specifiers.add_argument('path', help='Path to file.', metavar='PATH') + +signature_specifiers = argparse.ArgumentParser( + description='Used to specify a target for the query.', add_help=False) + +subgroup = signature_specifiers.add_argument_group( + 'arguments for specifying a target') +subgroup.add_argument('-p', '--path', help='Path to file.') +subgroup.add_argument( + '-w', + '--word', + help= + '''The word to search for in the file denoted by the path argument. You must + also specify -p''') +subgroup.add_argument( + '-s', + '--signature', + help='''A signature provided from a previous search. No -p or -w arguments + required.''') + +common_args = argparse.ArgumentParser( + description='Common options', add_help=False) +common_args.add_argument( + '--pretty', + help='Whether to pretty print the resulting JSON', + default=True, + action='store_true') +common_args.add_argument( + '--loglevel', '-l', help='Log level', choices=['info', 'debug']) +common_args.add_argument('--cache', '-C', help='Cache directory') + +# sig +signature_command = subcommands.add_parser( + 'sig', help='Query signature', parents=[signature_specifiers, common_args]) +signature_command.set_defaults( + func=lambda cs, a: {'signature': get_signature(cs, a)}) + +# xrefs +xrefs_command = subcommands.add_parser( + 'xrefs', + help='Query cross-references', + parents=[signature_specifiers, common_args]) +xrefs_command.set_defaults(func=lambda cs, a: cs.SendRequestToServer( + CompoundRequest( + xref_search_request=[ + XrefSearchRequest( + query=get_signature(cs, a), + file_spec=cs.GetFileSpec('.'), + max_num_results=100 + ) + ] + ))) + +# callers +callers_command = subcommands.add_parser( + 'callers', + help='Query callers for a signature', + parents=[signature_specifiers, common_args]) +callers_command.set_defaults( + func=lambda cs, a: cs.SendRequestToServer( + CompoundRequest( + call_graph_request=[ + CallGraphRequest( + signature=get_signature(cs, a), + file_spec=cs.GetFileSpec('.'), + max_num_results=100) + ] + ))) + +# annot +annotate_command = subcommands.add_parser( + 'annot', + help='Get annotations for file', + parents=[path_specifiers, common_args]) +annotate_command.add_argument( + '--type', + '-t', + help='Type', + action='append', + choices=['LINK_TO_DEFINITION', 'LINK_TO_URL', 'XREF_SIGNATURE'], + default=[]) +annotate_command.set_defaults( + func=lambda cs, a: cs.GetAnnotationsForFile( + a.path, [{'id': x} for x in a.type])) + +# file_info +file_info_command = subcommands.add_parser( + 'fileinfo', help='Get file info', parents=[path_specifiers, common_args]) +file_info_command.add_argument( + '--outline', + '-o', + help='Get outlining metadata.', + default=False, + action='store_true') +file_info_command.add_argument( + '--html', '-H', help='Get HTML.', default=False, action='store_true') +file_info_command.add_argument( + '--folding', + '-f', + help='Get folding metadata.', + default=False, + action='store_true') +file_info_command.set_defaults( + func=lambda cs, a: cs.SendRequestToServer( + CompoundRequest( + file_info_request=[ + FileInfoRequest( + file_spec=cs.GetFileSpec(a.path), + fetch_html_content=a.html, + fetch_outline=a.outline, + fetch_folding=a.folding, + fetch_generated_from=False + ) + ] + ))) + +# dir_info +dir_info_command = subcommands.add_parser( + 'dirinfo', + help='Get directory info', + parents=[path_specifiers, common_args]) +dir_info_command.set_defaults( + func=lambda cs, a: cs.SendRequestToServer( + CompoundRequest( + dir_info_request=[ + DirInfoRequest( + file_spec=cs.GetFileSpec(a.path) + ) + ] + ))) + +# search +search_command = subcommands.add_parser( + 'q', help='Search', parents=[common_args]) +search_command.add_argument('query', help='Search terms.', metavar='QUERY') +search_command.add_argument( + '--max_results', + '-N', + help='Maximum number of results to return.', + type=int, + default=50) +search_command.add_argument( + '--snippets', '-S', help='Include snippets.', action='store_true') +search_command.add_argument( + '--decorate', + '-D', + help='Decorate snippets with syntactic hints', + action='store_true') +search_command.add_argument( + '--context', + '-U', + help='Lines of context to inclued in snippets.', + type=int, + default=3) +search_command.set_defaults( + func=lambda cs, a: cs.SendRequestToServer( + CompoundRequest( + search_request=[ + SearchRequest( + query=a.query, + return_snippets=(a.snippets or a.decorate), + return_decorated_snippets=a.decorate, + max_num_results=a.max_results, + lines_context=a.context + ) + ] + ))) + +# status +status_command = subcommands.add_parser( + 'status', help='CodeSearch server status', parents=[common_args]) +status_command.set_defaults( + func= + lambda cs, a: cs.SendRequestToServer(CompoundRequest(status_request=[{}]))) + +arguments = parser.parse_args() + +try: + codesearch_instance = CodeSearch( + a_path_inside_source_dir=os.getcwd(), + cache_dir=arguments.cache if arguments.cache else None) + setup_logging(codesearch_instance, arguments) + print_result(arguments.func(codesearch_instance, arguments), arguments) +finally: + codesearch_instance.TeardownCache()
diff --git a/tools/codesearch/codesearch/client_api.py b/tools/codesearch/codesearch/client_api.py new file mode 100644 index 0000000..ef916f9 --- /dev/null +++ b/tools/codesearch/codesearch/client_api.py
@@ -0,0 +1,218 @@ +# Copyright 2017 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. +"""This file defines the entry point for most external consumers of the Python +Codesearch library. +""" + +from __future__ import absolute_import + +import logging +import os + +from .file_cache import FileCache +from .messages import CompoundRequest, AnnotationType, AnnotationTypeValue, \ + CompoundResponse, FileInfoRequest, FileSpec, AnnotationRequest, \ + EdgeEnumKind, XrefSearchRequest +from .paths import GetSourceRoot + +try: + from urllib.request import urlopen + from urllib.parse import urlencode +except ImportError: + from urllib2 import urlopen + from urllib import urlencode + + +class CodeSearch(object): + + def __init__(self, + should_cache=False, + cache_dir=None, + a_path_inside_source_dir=None, + package_name='chromium', + codesearch_host='https://cs.chromium.org', + request_timeout_in_seconds=3): + + self.file_cache = None + + self.logger = logging.getLogger('codesearch') + + self.source_root = '' + + self.package_name = package_name + + self.codesearch_host = codesearch_host + + self.request_timeout_in_seconds = request_timeout_in_seconds + + self.source_root = GetSourceRoot(a_path_inside_source_dir) + + if not should_cache: + self.file_cache = None + return + if self.file_cache: + return + self.file_cache = FileCache(cache_dir=cache_dir) + + def GetSourceRoot(self): + return self.source_root + + def GetLogger(self): + return self.logger + + def GetFileSpec(self, path=None): + if not path: + return FileSpec(name='.', package_name=self.package_name) + + return FileSpec( + name=os.path.relpath(os.path.abspath(path), self.source_root), + package_name=self.package_name) + + def TeardownCache(self): + if self.file_cache: + self.file_cache.close() + + self.file_cache = None + self.source_root = None + + def _Retrieve(self, url): + """Retrieve the URL by first checking the cache and then falling back to + using the network.""" + self.logger.debug('Fetching %s', url) + + if self.file_cache: + cached_response = self.file_cache.get(url) + self.logger.debug('Found cached response') + if (cached_response): + return cached_response.decode('utf8') + response = urlopen(url, timeout=self.request_timeout_in_seconds) + result = response.read() + if self.file_cache: + self.file_cache.put(url, result) + return result.decode('utf8') + + def SendRequestToServer(self, compound_request): + if not isinstance(compound_request, CompoundRequest): + raise ValueError( + '|compound_request| should be an instance of CompoundRequest') + + qs = urlencode(compound_request.AsQueryString(), doseq=True) + url = '{host}/codesearch/json?{qs}'.format(host=self.codesearch_host, qs=qs) + result = self._Retrieve(url) + return CompoundResponse.FromJsonString(result) + + def GetAnnotationsForFile(self, filename, annotation_types): + return self.SendRequestToServer( + CompoundRequest(annotation_request=[ + AnnotationRequest( + file_spec=self.GetFileSpec(filename), type=annotation_types) + ])) + + def GetSignatureForLocation(self, filename, line, column): + result = self.GetAnnotationsForFile( + filename, [AnnotationType(id=AnnotationTypeValue.XREF_SIGNATURE)]) + result = result.annotation_response[0] + + if result.return_code != 1: + raise Exception('Request failed. Response=%s' % (result.AsQueryString())) + + for annotation in result.annotation: + if not annotation.range.Contains(line, column): + continue + + if hasattr(annotation, 'xref_signature'): + return annotation.xref_signature.signature + + if hasattr(annotation, 'internal_link'): + return annotation.internal_link.signature + + raise Exception("Can't determine signature for %s at %d:%d" % + (filename, line, column)) + + def GetSignatureForSymbol(self, filename, symbol): + result = self.GetAnnotationsForFile( + filename, [AnnotationType(id=AnnotationTypeValue.XREF_SIGNATURE)]) + result = result.annotation_response[0] + + if result.return_code != 1: + raise Exception('Request failed. Response=%s' % (result.AsQueryString())) + + for snippet in result.annotation: + if hasattr(snippet, 'xref_signature'): + signature = snippet.xref_signature.signature + if '%s(' % symbol in signature: + return signature + + elif hasattr(snippet, 'internal_link'): + signature = snippet.internal_link.signature + if '::%s' % symbol in signature or 'class-%s' % symbol in signature: + return signature + + raise Exception("Can't determine signature for %s:%s" % (filename, symbol)) + + def GetXrefsFor(self, signature, edge_filter): + refs = self.SendRequestToServer( + CompoundRequest(xref_search_request=[ + XrefSearchRequest( + file_spec=self.GetFileSpec(), + query=signature, + edge_filter=edge_filter) + ])) + if not refs or not hasattr(refs.xref_search_response[0], 'search_result'): + return [] + return refs.xref_search_response[0].search_result + + def GetOverridingDefinitions(self, signature): + candidates = [] + refs = self.GetXrefsFor(signature, [EdgeEnumKind.OVERRIDDEN_BY]) + for result in refs: + matches = [] + for match in result.match: + if hasattr(match, 'grok_modifiers') and hasattr( + match.grok_modifiers, + 'definition') and match.grok_modifiers.definition: + matches.append(match) + if matches: + result.match = matches + candidates.append(result) + return candidates + + def GetCallTargets(self, signature): + # First look up the declaration for the callsite. + refs = self.GetXrefsFor(signature, [EdgeEnumKind.HAS_DECLARATION]) + + candidates = [] + for result in refs: + for match in result.match: + if hasattr(match, 'grok_modifiers') and hasattr( + match.grok_modifiers, 'virtual') and match.grok_modifiers.virtual: + candidates.extend(self.GetOverridingDefinitions(match.signature)) + if not candidates: + return self.GetXrefsFor(signature, [EdgeEnumKind.HAS_DEFINITION]) + return candidates + + def IsContentStale(self, filename, buffer_lines, check_prefix=False): + response = self.SendRequestToServer( + CompoundRequest(file_info_request=[ + FileInfoRequest( + file_spec=self.GetFileSpec(filename), + fetch_html_content=False, + fetch_outline=False, + fetch_folding=False, + fetch_generated_from=False) + ])) + + response = response.file_info_response[0] + content_lines = response.file_info.content.text.split('\n') + + if check_prefix: + content_lines = content_lines[:len(buffer_lines)] + if len(content_lines) != len(buffer_lines): + return True + + for left, right in zip(content_lines, buffer_lines): + if left != right: + return True + + return False
diff --git a/tools/codesearch/codesearch/file_cache.py b/tools/codesearch/codesearch/file_cache.py new file mode 100644 index 0000000..4442180 --- /dev/null +++ b/tools/codesearch/codesearch/file_cache.py
@@ -0,0 +1,121 @@ +# Copyright 2017 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 hashlib +import threading +import tempfile +import os +import datetime + +# A key/value store that stores objects to disk in temporary objects +# for 30 minutes. + + +class FileCache: + + def __init__(self, cache_dir=None, expiration_in_minutes=30): + + # Protects |self| but individual file objects in |store| are not + # protected once its returned from |_file_for|. + self.lock = threading.Lock() + + # Dictionary mapping a URL to a tuple containing a file object and a + # timestamp. The timestamp notes the creation time. + self.store = {} + + # Directory containing cache files. If |cache_dir| is None, then each + # file is created independently using tempfile.TemporaryFile(). + self.cache_dir = cache_dir + + # Garbage collector timer. + self.timer = threading.Timer(15 * 60, self.gc) + self.timer.start() + + self.expiration = datetime.timedelta(minutes=expiration_in_minutes) + + if cache_dir and not os.path.exists(cache_dir): + if not os.path.isabs(cache_dir): + raise ValueError('|cache_dir| should be an absolute path') + os.mkdirs(cache_dir) + + def _file_for(self, url, create=False): + with self.lock: + if url in self.store: + f, _ = self.store[url] + f.seek(0) + + elif create and self.cache_dir is None: + f = tempfile.TemporaryFile() + f.seek(0) + + elif self.cache_dir: + deterministic_filename = os.path.join(self.cache_dir, + hashlib.sha1(url).hexdigest()) + if os.path.exists(deterministic_filename): + st = os.stat(deterministic_filename) + if create: + f = open(deterministic_filename, 'w+') + elif datetime.datetime.utcfromtimestamp(st.st_mtime) + \ + self.expiration > datetime.datetime.now(): + f = open(deterministic_filename, 'r+') + self.store[url] = (f, + datetime.datetime.utcfromtimestamp(st.st_mtime)) + else: + # Existing file has expired. + os.remove(deterministic_filename) + return None + else: + if create: + f = open(deterministic_filename, 'w+') + else: + return None + else: + return None + + if url not in self.store: + self.store[url] = (f, datetime.datetime.now()) + return f + + def put(self, url, data): + f = self._file_for(url, create=True) + f.write(data) + f.flush() + + def get(self, url): + f = self._file_for(url, create=False) + if f is None: + return '' + f.seek(0) + return f.read() + + def gc(self): + dir_to_purge = None + expiration = None + with self.lock: + expired = datetime.datetime.now() - self.expiration + remove = [] + for url, (_, timestamp) in self.store.items(): + if timestamp < expired: + remove.append(url) + for url in remove: + self.store.pop(url) + self.timer = threading.Timer(15 * 60, self.gc) + self.timer.start() + dir_to_purge = self.cache_dir + expiration = self.expiration + + # This part doesn't require a lock on |self|. + if not dir_to_purge: + return + + now = datetime.datetime.now() + for entry in os.listdir(dir_to_purge): + full_path = os.path.join(dir_to_purge, entry) + st = os.stat(full_path) + expires_on = datetime.datetime.utcfromtimestamp(st.st_mtime) + expiration + if expires_on < now: + os.remove(full_path) + + def close(self): + self.timer.cancel()
diff --git a/tools/codesearch/codesearch/messages.py b/tools/codesearch/codesearch/messages.py new file mode 100644 index 0000000..f282ca1 --- /dev/null +++ b/tools/codesearch/codesearch/messages.py
@@ -0,0 +1,1014 @@ +# Copyright 2017 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 json +import sys + + +class CodeSearchProtoJsonEncoder(json.JSONEncoder): + + def default(self, o): + if isinstance(o, Message): + return o.__dict__ + return o + + +class CodeSearchProtoJsonSymbolizedEncoder(json.JSONEncoder): + + def default(self, o): + if isinstance(o, Message): + rv = {} + desc = o.__class__.DESCRIPTOR + for k, v in o.__dict__.iteritems(): + if k in desc and not isinstance(desc[k], list) and issubclass( + desc[k], Message) and desc[k].IsEnum(): + rv[k] = desc[k].ToSymbol(v) + else: + rv[k] = v + return rv + return o + + +class Message(object): + + class PARENT_TYPE: + pass + + def AsQueryString(self): + values = [] + for k, v in self.__dict__.iteritems(): + values.extend(Message.ToQueryString(k, v)) + return values + + @staticmethod + def ToQueryString(k, o): + if o is None: + return [] + if isinstance(o, Message): + return [(k, 'b')] + o.AsQueryString() + [(k, 'e')] + if isinstance(o, bool): + return [(k, 'true' if o else 'false')] + if isinstance(o, list): + values = [] + for v in o: + values.extend(Message.ToQueryString(k, v)) + return values + return [(k, str(o))] + + @staticmethod + def Coerce(source, target_type, parent_class=None): + if isinstance(target_type, list): + assert isinstance(source, list) + assert len(target_type) == 1 + target_type = target_type[0] + + return [Message.Coerce(x, target_type, parent_class) for x in source] + + if target_type == Message.PARENT_TYPE: + + assert parent_class is not None + + return Message.Coerce(source, parent_class, parent_class) + + if issubclass(target_type, Message): + if isinstance(source, target_type): + return source + + typespec = target_type.DESCRIPTOR + if isinstance(typespec, dict): + assert isinstance( + source, dict), 'Source is not a dictionary: %s; Mapping to %s' % ( + source, target_type) + dest = target_type() + for k, v in source.iteritems(): + if k in typespec: + dest.__dict__[k] = Message.Coerce(v, typespec[k], target_type) + else: + dest.__dict__[k] = v + return dest + if typespec is None: + assert isinstance(source, dict) + m = Message() + m.__dict__ = source.copy() + return m + if sys.version_info[0] == 2: + if typespec != str and isinstance(source, basestring) and hasattr( + target_type, source): + return typespec(getattr(target_type, source)) + else: + if typespec != str and isinstance(source, str) and hasattr( + target_type, source): + return typespec(getattr(target_type, source)) + return typespec(source) + return target_type(source) + + @classmethod + def IsEnum(cls): + return not isinstance(cls.DESCRIPTOR, dict) + + @classmethod + def ToSymbol(cls, v): + assert cls.IsEnum() + for prop, value in vars(cls).iteritems(): + if value == v: + return prop + return v + + @classmethod + def FromSymbol(cls, s): + assert cls.IsEnum() + return vars(cls)[s] + + @classmethod + def Make(cls, **kwargs): + return Message.Coerce(kwargs, cls) + + @classmethod + def FromShallowDict(cls, d): + return Message.Coerce(d, cls) + + @classmethod + def FromJsonString(cls, s): + d = json.loads(s, 'utf8') + return cls.FromShallowDict(d) + + DESCRIPTOR = None + + +def message(cls): + + def Constructor(self, **kwargs): + if len(kwargs) == 0: + return + self.__dict__ = cls.Make(**kwargs).__dict__ + + setattr(cls, '__init__', Constructor) + return cls + + +class AnnotationTypeValue(Message): + BLAME = 0x00040 + CODE_FINDINGS = 0x40000 + COMPILER = 0x00080 + COVERAGE = 0x00010 + DEPRECATED = 0x02000 + FINDBUGS = 0x00200 + LANG_COUNT = 0x04000 + LINK_TO_DEFINITION = 0x00001 + LINK_TO_URL = 0x00002 + LINT = 0x00020 + OFFLINE_QUERIES = 0x08000 + OVERRIDE = 0x01000 + TOOLS = 0x20000 + UNKNOWN = 0x00000 + XREF_SIGNATURE = 0x00004 + + DESCRIPTOR = int + + +@message +class AnnotationType(Message): + DESCRIPTOR = { + 'id': AnnotationTypeValue, + } + + +class TextRange(Message): + DESCRIPTOR = { + 'start_line': int, + 'start_column': int, + 'end_line': int, + 'end_column': int, + } + + def Contains(self, line, column): + return not (line < self.start_line or line > self.end_line or + (line == self.start_line and column < self.start_column) or + (line == self.end_line and column > self.end_column)) + + +class InternalLink(Message): + DESCRIPTOR = { + 'package_name': str, + 'signature': str, + 'signature_hash': str, + 'path': str, + 'range': TextRange, + } + + +class XrefSignature(Message): + DESCRIPTOR = { + 'signature': str, + 'signature_hash': str, + } + + +class NodeEnumKind(Message): + ALIAS_JOIN = 9100 + ANNOTATION = 900 + ARRAY = 5700 + BIGFLOAT = 3000 + BIGINT = 2900 + BOOLEAN = 2000 + CHANNEL = 6700 + CHAR = 2100 + CLASS = 500 + COMMENT = 9400 + COMMUNICATION = 3850 + COMPLEX = 2800 + CONSTRUCTOR = 1200 + CONST_TYPE = 5400 + DEF_DECL_JOIN = 9000 + DELIMITER = 10000 + DIAGNOSTIC = 4100 + DIRECTORY = 4000 + DOCUMENTATION = 9800 + DOCUMENTATION_TAG = 9900 + DYNAMIC_TYPE = 9300 + ENUM = 700 + ENUM_CONSTANT = 800 + FIELD = 1500 + FILE = 3900 + FIXED_POINT = 2600 + FLOAT = 2500 + FORWARD_DECLARATION = 5300 + FUNCTION = 1000 + FUNCTION_TYPE = 10200 + IMPORT = 8200 + INDEX_INFO = 31337 + INSTANCE = 4600 + INTEGER = 2400 + INTERFACE = 600 + LABEL = 11600 + LIST = 6300 + LOCAL = 1600 + LOST = 9600 + MAP = 6000 + MARKUP_ATTRIBUTE = 11300 + MARKUP_TAG = 11200 + MATRIX = 5800 + METHOD = 1100 + MODULE = 300 + NAME = 3300 + NAMESPACE = 100 + NULL_TYPE = 7300 + NUMBER = 3100 + OBJECT = 4500 + OPAQUE = 6500 + OPTION_TYPE = 5500 + PACKAGE = 200 + PACKAGE_JOIN = 9200 + PARAMETER = 1700 + PARAMETRIC_TYPE = 5600 + POINTER = 5000 + PROPERTY = 1900 + QUEUE = 6400 + RATIONAL = 2700 + REFERENCE_TYPE = 5100 + REGEXP = 2300 + RESTRICTION_TYPE = 10100 + RULE = 8100 + SEARCHABLE_IDENTIFIER = 11500 + SEARCHABLE_NAME = 9500 + SET = 5900 + STRING = 2200 + STRUCT = 400 + SYMBOL = 3200 + TAG_NAME = 11100 + TARGET = 8000 + TEMPLATE = 1400 + TEXT = 9700 + TEXT_MACRO = 1300 + THREAD = 6600 + TUPLE = 6100 + TYPE_ALIAS = 5200 + TYPE_DESCRIPTOR = 11400 + TYPE_SPECIALIZATION = 7000 + TYPE_VARIABLE = 7100 + TYPE_VARIABLE_TYPE = 10400 + UNION = 6200 + UNIT_TYPE = 6900 + UNRESOLVED_TYPE = 404 + USAGE = 3800 + USER_TYPE = 10300 + VALUE = 3400 + VARIABLE = 1800 + VARIADIC_TYPE = 7200 + VOID_TYPE = 6800 + + DESCRIPTOR = int + + +class KytheNodeEnumKind(Message): + ABS = 100 + ABSVAR = 200 + ANCHOR = 300 + CONSTANT = 500 + DEPRECATED_CALLABLE = 400 + DOC = 550 + FILE = 600 + FUNCTION = 800 + FUNCTION_CONSTRUCTOR = 810 + FUNCTION_DESTRUCTOR = 820 + INTERFACE = 700 + LOOKUP = 900 + MACRO = 1000 + META = 1050 + NAME = 1100 + PACKAGE = 1200 + RECORD = 1300 + RECORD_CLASS = 1310 + RECORD_STRUCT = 1320 + RECORD_UNION = 1330 + SUM = 1400 + SUM_ENUM = 1410 + SUM_ENUM_CLASS = 1420 + TALIAS = 1500 + TAPP = 1600 + TBUILTIN = 1700 + TBUILTIN_ARRAY = 1705 + TBUILTIN_BOOLEAN = 1710 + TBUILTIN_BYTE = 1715 + TBUILTIN_CHAR = 1720 + TBUILTIN_DOUBLE = 1725 + TBUILTIN_FLOAT = 1730 + TBUILTIN_FN = 1735 + TBUILTIN_INT = 1740 + TBUILTIN_LONG = 1745 + TBUILTIN_PTR = 1750 + TBUILTIN_SHORT = 1755 + TBUILTIN_VOID = 1760 + TNOMINAL = 1800 + TSIGMA = 1850 + UNRESOLVED_TYPE = 0 + VARIABLE = 1900 + VARIABLE_FIELD = 1910 + VARIABLE_LOCAL = 1920 + VARIABLE_LOCAL_EXCEPTION = 1940 + VARIABLE_LOCAL_PARAMETER = 1930 + VARIABLE_LOCAL_RESOURCE = 1950 + VCS = 2000 + + DESCRIPTOR = int + + +class Annotation(Message): + DESCRIPTOR = { + 'content': str, + 'file_name': str, + 'internal_link': InternalLink, + 'is_implicit_target': bool, + 'kythe_xref_kind': KytheNodeEnumKind, + 'range': TextRange, + 'status': int, + 'type': AnnotationType, + 'url': str, + 'xref_kind': NodeEnumKind, + 'xref_signature': XrefSignature, + } + + +@message +class FileSpec(Message): + DESCRIPTOR = {'name': str, 'package_name': str} + + +class FormatType(Message): + CARRIAGE_RETURN = 22 + CL_LINK = 33 + CODESEARCH_LINK = 36 + EXTERNAL_LINK = 31 + GOOGLE_INTERNAL_LINK = 30 + INCLUDE_QUERY = 35 + LINE = 1 + QUERY_MATCH = 40 + SNIPPET_QUERY_MATCH = 41 + SYNTAX_CLASS = 8 + SYNTAX_COMMENT = 5 + SYNTAX_CONST = 9 + SYNTAX_DEPRECATED = 11 + SYNTAX_DOC_NAME = 13 + SYNTAX_DOC_TAG = 12 + SYNTAX_ESCAPE_SEQUENCE = 10 + SYNTAX_KEYWORD = 3 + SYNTAX_KEYWORD_STRONG = 15 + SYNTAX_MACRO = 7 + SYNTAX_MARKUP_BOLD = 51 + SYNTAX_MARKUP_CODE = 54 + SYNTAX_MARKUP_ENTITY = 50 + SYNTAX_MARKUP_ITALIC = 52 + SYNTAX_MARKUP_LINK = 53 + SYNTAX_NUMBER = 6 + SYNTAX_PLAIN = 2 + SYNTAX_STRING = 4 + SYNTAX_TASK_TAG = 14 + TABS = 21 + TRAILING_SPACE = 20 + UNKNOWN_TYPE = 0 + USER_NAME_LINK = 32 + + DESCRIPTOR = int + + +class FormatRange(Message): + DESCRIPTOR = {'type': FormatType, 'range': TextRange, 'target': str} + + +class FileType(Message): + BINARY = 5 + CODE = 1 + DATA = 3 + DIR = 4 + DOC = 2 + SYMLINK = 6 + UNKNOWN = 0 + + DESCRIPTOR = int + + +class AnnotatedText(Message): + DESCRIPTOR = {'text': str, 'range': [FormatRange]} + + +class CodeBlockType(Message): + DESCRIPTOR = int + + ALLOCATION = 49 + ANONYMOUS_FUNCTION = 15 + BUILD_ARGUMENT = 25 + BUILD_BINARY = 21 + BUILD_GENERATOR = 24 + BUILD_LIBRARY = 23 + BUILD_RULE = 20 + BUILD_TEST = 22 + BUILD_VARIABLE = 26 + CLASS = 1 + COMMENT = 13 + DEFINE_CONST = 40 + DEFINE_MACRO = 41 + ENUM = 4 + ENUM_CONSTANT = 14 + ERROR = 0 + FIELD = 7 + FUNCTION = 8 + INTERFACE = 2 + JOB = 47 + JS_ASSIGNMENT = 38 + JS_CONST = 31 + JS_FUNCTION_ASSIGNMENT = 39 + JS_FUNCTION_LITERAL = 37 + JS_GETTER = 35 + JS_GOOG_PROVIDE = 32 + JS_GOOG_REQUIRE = 33 + JS_LITERAL = 36 + JS_SETTER = 34 + JS_VAR = 30 + METHOD = 6 + NAMESPACE = 11 + PACKAGE = 17 + PROPERTY = 12 + RESERVED_27 = 27 + RESERVED_28 = 28 + RESERVED_29 = 29 + SERVICE = 48 + STRUCT = 3 + TEMPLATE = 46 + TEST = 16 + TYPEDEF = 10 + UNION = 5 + VARIABLE = 9 + XML_TAG = 45 + + +class Modifiers(Message): + DESCRIPTOR = { + '_global': bool, + '_thread_local': bool, + 'abstract': bool, + 'anonymous': bool, + 'autogenerated': bool, + 'close_delimiter': bool, + 'constexpr_': bool, + 'declaration': bool, + 'definition': bool, + 'deprecated': bool, + 'discrete': bool, + 'dynamically_scoped': bool, + 'exported': bool, + 'file_scoped': bool, + 'foreign': bool, + 'getter': bool, + 'has_figment': bool, + 'immutable': bool, + 'implicit': bool, + 'inferred': bool, + 'is_figment': bool, + 'join_node': bool, + 'library_scoped': bool, + 'namespace_scoped': bool, + 'nonescaped': bool, + 'open_delimiter': bool, + 'operator': bool, + 'optional': bool, + 'package_scoped': bool, + 'parametric': bool, + 'predeclared': bool, + 'private': bool, + 'protected': bool, + 'public': bool, + 'receiver': bool, + 'register': bool, + 'renamed': bool, + 'repeated': bool, + 'setter': bool, + 'shadowing': bool, + 'signed': bool, + 'static': bool, + 'strict_math': bool, + 'synchronized': bool, + 'terminal': bool, + 'transient': bool, + 'unsigned': bool, + 'virtual': bool, + 'volatile': bool, + 'whitelisted': bool, + } + + +class CodeBlock(Message): + DESCRIPTOR = { + 'child': [Message.PARENT_TYPE], + 'modifiers': Modifiers, + 'name': str, + 'name_prefix': str, + 'signature': str, + 'text_range': TextRange, + 'type': CodeBlockType, + } + + +class FileInfo(Message): + DESCRIPTOR = { + 'actual_name': str, + 'changelist_num': str, + 'codeblock': [CodeBlock], + 'content': AnnotatedText, + 'converted_content': AnnotatedText, + 'converted_lines': int, + 'fold_ranges': [TextRange], + 'generated': bool, + 'generated_from': [str], + 'html_text': str, + 'language': str, + 'license_path': str, + 'license_type': str, + 'lines': int, + 'md5': str, + 'mime_type': str, + 'name': str, + 'package_name': str, + 'revision_num': str, + 'size': int, + 'type': FileType, + } + + +class FileInfoResponse(Message): + DESCRIPTOR = { + 'announcement': str, + 'error_message': str, + 'file_info': FileInfo, + 'return_code': int, + } + + +@message +class FileInfoRequest(Message): + DESCRIPTOR = { + 'file_spec': FileSpec, + 'fetch_html_content': bool, + 'fetch_outline': bool, + 'fetch_folding': bool, + 'fetch_generated_from': bool, + } + + +class AnnotationResponse(Message): + DESCRIPTOR = { + 'annotation': [Annotation], + 'file': str, + 'max_findings_reached': bool, + 'return_code': int, + } + + +@message +class AnnotationRequest(Message): + DESCRIPTOR = { + 'file_spec': FileSpec, + 'type': [AnnotationType], + } + + +class MatchReason(Message): + DESCRIPTOR = { + 'blame': bool, + 'content': bool, + 'filename': bool, + 'filename_lineno': bool, + 'scoped_symbol': bool, + } + + +class Snippet(Message): + DESCRIPTOR = { + 'first_line_number': int, + 'match_reason': MatchReason, + 'scope': str, + 'text': AnnotatedText, + } + + +class Node(Message): + DESCRIPTOR = { + 'call_scope_range': TextRange, + 'call_site_range': TextRange, + 'children': [Message.PARENT_TYPE], + 'display_name': str, + 'edge_kind': str, + 'file_path': str, + 'identifier': str, + 'node_kind': str, + 'override': bool, + 'package_name': str, + 'params': [str], + 'signature': str, + 'snippet': Snippet, + 'snippet_file_path': str, + 'snippet_package_name': str, + } + + +class CallGraphResponse(Message): + DESCRIPTOR = { + 'debug_message': str, + 'estimated_total_number_results': int, + 'is_call_graph': bool, + 'is_from_kythe': bool, + 'kythe_next_page_token': str, + 'node': Node, + 'results_offset': int, + 'return_code': int, + } + + +@message +class CallGraphRequest(Message): + DESCRIPTOR = { + 'file_spec': FileSpec, + 'max_num_results': int, + 'signature': str, + } + + +class EdgeEnumKind(Message): + DESCRIPTOR = int + + ALLOWED_ACCESS_TO = 4500 + ANNOTATED_WITH = 5000 + ANNOTATION_OF = 5100 + BASE_TYPE = 1300 + BELONGS_TO_NAMESPACE = 7200 + BELONGS_TO_PACKAGE = 6900 + CALL = 2200 + CALLED_AT = 2300 + CALLGRAPH_FROM = 4700 + CALLGRAPH_TO = 4600 + CAPTURED_BY = 1200 + CAPTURES = 1100 + CATCHES = 6400 + CAUGHT_BY = 6500 + CHANNEL_USED_BY = 2351 + CHILD = 5300 + COMMENT_IN_FILE = 7400 + COMPOSING_TYPE = 1400 + CONSUMED_BY = 4100 + CONTAINS_COMMENT = 7500 + CONTAINS_DECLARATION = 5800 + CONTAINS_USAGE = 6000 + DECLARATION_IN_FILE = 5900 + DECLARATION_OF = 3200 + DECLARED_BY = 400 + DECLARES = 300 + DEFINITION_OF = 3400 + DIAGNOSTIC_OF = 5400 + DIRECTLY_INHERITED_BY = 1060 + DIRECTLY_INHERITS = 1050 + DIRECTLY_OVERRIDDEN_BY = 860 + DIRECTLY_OVERRIDES = 850 + DOCUMENTED_WITH = 7700 + DOCUMENTS = 7600 + ENCLOSED_USAGE = 4900 + EXTENDED_BY = 200 + EXTENDS = 100 + GENERATED_BY = 3100 + GENERATES = 3000 + GENERATES_NAME = 3150 + HAS_DECLARATION = 3300 + HAS_DEFINITION = 3500 + HAS_DIAGNOSTIC = 5500 + HAS_FIGMENT = 9200 + HAS_IDENTIFIER = 9400 + HAS_INPUT = 4000 + HAS_OUTPUT = 4200 + HAS_PROPERTY = 2800 + HAS_SELECTION = 10900 + HAS_TYPE = 1800 + IMPLEMENTED_BY = 600 + IMPLEMENTS = 500 + INHERITED_BY = 1000 + INHERITS = 900 + INITIALIZED_WITH = 9100 + INITIALIZES = 9000 + INJECTED_AT = 10500 + INJECTS = 10400 + INSTANTIATED_AT = 2500 + INSTANTIATION = 2400 + IS_FIGMENT_OF = 9300 + IS_IDENTIFIER_OF = 9500 + IS_TYPE_OF = 1900 + KEY_METHOD = 3600 + KEY_METHOD_OF = 3700 + MEMBER_SELECTED_AT = 10700 + NAMESPACE_CONTAINS = 7300 + NAME_GENERATED_BY = 3160 + OUTLINE_CHILD = 5700 + OUTLINE_PARENT = 5600 + OVERRIDDEN_BY = 800 + OVERRIDES = 700 + PACKAGE_CONTAINS = 6800 + PARAMETER_TYPE = 8800 + PARAMETER_TYPE_OF = 8900 + PARENT = 5200 + PRODUCED_BY = 4300 + PROPERTY_OF = 2900 + RECEIVES_FROM = 2353 + REFERENCE = 2600 + REFERENCED_AT = 2700 + REQUIRED_BY = 3900 + REQUIRES = 3800 + RESTRICTED_TO = 4400 + RETURNED_BY = 2100 + RETURN_TYPE = 2000 + SELECTED_FROM = 10800 + SELECTS_MEMBER_OF = 10600 + SENDS_TO = 2352 + SPECIALIZATION_OF = 1600 + SPECIALIZED_BY = 1700 + THROWGRAPH_FROM = 6700 + THROWGRAPH_TO = 6600 + THROWN_BY = 6300 + THROWS = 6200 + TREE_CHILD = 7900 + TREE_PARENT = 7800 + TYPE_PARAMETER = 1500 + TYPE_PARAMETER_OF = 1550 + USAGE_CONTEXT = 4800 + USAGE_IN_FILE = 6100 + USES_CHANNEL = 2350 + USES_VARIABLE = 7000 + VARIABLE_USED_IN = 7100 + XLANG_PROVIDES = 8600 + XLANG_PROVIDES_NAME = 8400 + XLANG_USES = 8700 + XLANG_USES_NAME = 8500 + + +class XrefTypeCount(Message): + DESCRIPTOR = { + 'count': int, + 'type': str, + 'type_id': int, + } + + +class XrefSingleMatch(Message): + DESCRIPTOR = { + 'line_number': int, + 'line_text': str, + 'type': str, + 'type_id': EdgeEnumKind, + 'grok_modifiers': Modifiers, + 'signature': str, + } + + +class XrefSearchResult(Message): + DESCRIPTOR = { + 'file': FileSpec, + 'match': [XrefSingleMatch], + } + + +class XrefSearchResponse(Message): + DESCRIPTOR = { + 'eliminated_type_count': [XrefTypeCount], + 'estimated_total_type_count': [XrefTypeCount], + 'from_kythe': bool, + 'grok_total_number_of_results': int, + 'search_result': [XrefSearchResult], + 'status': int, + 'status_message': str, + } + + +@message +class XrefSearchRequest(Message): + DESCRIPTOR = { + 'edge_filter': [EdgeEnumKind], + 'file_spec': FileSpec, + 'max_num_results': int, + 'query': str, + } + + +class VanityGitOnBorgHostname(Message): + DESCRIPTOR = { + 'name': str, + 'hostname': str, + } + + +class InternalPackage(Message): + DESCRIPTOR = { + 'browse_path_prefix': str, + 'cs_changelist_num': str, + 'grok_languages': [str], + 'grok_name': str, + 'grok_path_prefix': [str], + 'id': str, + 'kythe_languages': [str], + 'name': str, + 'repo': str, + 'vanity_git_on_borg_hostnames': [VanityGitOnBorgHostname], + } + + +class StatusResponse(Message): + DESCRIPTOR = { + 'announcement': str, + 'build_label': str, + 'internal_package': [InternalPackage], + 'success': bool, + } + + +class GobInfo(Message): + DESCRIPTOR = { + 'commit': str, + 'path': str, + 'repo': str, + } + + +class DirInfoResponseChild(Message): + DESCRIPTOR = { + 'is_deleted': bool, + 'is_directory': bool, + 'name': str, + 'package_id': str, + 'path': str, + 'revision_num': str, + } + + +class DirInfoResponseParent(Message): + DESCRIPTOR = { + 'name': str, + 'path': str, + 'package_id': str, + } + + +class DirInfoResponse(Message): + DESCRIPTOR = { + 'child': [DirInfoResponseChild], + 'generated': bool, + 'gob_info': GobInfo, + 'name': str, + 'package_id': str, + 'parent': [DirInfoResponseParent], + 'path': str, + 'success': bool, + } + + +@message +class DirInfoRequest(Message): + DESCRIPTOR = { + 'file_spec': FileSpec, + } + + +class FileResult(Message): + DESCRIPTOR = { + 'display_name': AnnotatedText, + 'file': FileSpec, + 'license': FileSpec, + 'license_type': str, + 'size': int, + } + + +class SingleMatch(Message): + DESCRIPTOR = { + 'line_number': int, + 'line_text': str, + 'match_length': int, + 'match_offset': int, + 'post_context_num_lines': int, + 'post_context_text': str, + 'pre_context_num_lines': int, + 'pre_context_text': str, + 'score': int, + } + + +class SearchResult(Message): + DESCRIPTOR = { + 'best_matching_line_number': int, + 'children': [str], + 'docid': str, + 'duplicate': [FileResult], + 'has_unshown_matches': bool, + 'hit_max_matches': bool, + 'is_augmented': bool, + 'language': str, + 'match': [SingleMatch], + 'match_reason': MatchReason, + 'num_duplicates': int, + 'num_matches': int, + 'snippet': [Snippet], + 'top_file': FileResult, + } + + +class SearchResponse(Message): + DESCRIPTOR = { + 'estimated_total_number_of_results': int, + 'hit_max_matches_per_file': bool, + 'hit_max_results': bool, + 'hit_max_to_score': bool, + 'maybe_skipped_documents': bool, + 'results_offset': int, + 'search_result': [SearchResult], + 'status': int, + 'status_message': str, + } + + +@message +class SearchRequest(Message): + DESCRIPTOR = { + 'exhaustive': bool, + 'lines_context': int, + 'max_num_results': int, + 'query': str, + 'return_all_duplicates': bool, + 'return_all_snippets': bool, + 'return_decorated_snippets': bool, + 'return_directories': bool, + 'return_line_matches': bool, + 'return_snippets': bool, + } + + +class StatusRequest(Message): + DESCRIPTOR = {} + + +class CompoundResponse(Message): + DESCRIPTOR = { + 'annotation_response': [AnnotationResponse], + 'call_graph_response': [CallGraphResponse], + 'dir_info_response': [DirInfoResponse], + 'file_info_response': [FileInfoResponse], + 'search_response': [SearchResponse], + 'status_response': [StatusResponse], + 'xref_search_response': [XrefSearchResponse], + } + + +@message +class CompoundRequest(Message): + DESCRIPTOR = { + 'annotation_request': [AnnotationRequest], + 'call_graph_request': [CallGraphRequest], + 'dir_info_request': [DirInfoRequest], + 'file_info_request': [FileInfoRequest], + 'search_request': [SearchRequest], + 'status_request': [StatusRequest], + 'xref_search_request': [XrefSearchRequest], + }
diff --git a/tools/codesearch/codesearch/paths.py b/tools/codesearch/codesearch/paths.py new file mode 100644 index 0000000..cfa3493 --- /dev/null +++ b/tools/codesearch/codesearch/paths.py
@@ -0,0 +1,28 @@ +# Copyright 2017 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 os + + +def GetPackageRelativePath(filename): + return os.path.relpath(filename, GetSourceRoot(filename)) + + +def GetSourceRoot(filename): + # If filename is not absolute, then we are going to assume that it is + # relative to the current directory. + if not os.path.isabs(filename): + filename = os.path.abspath(filename) + if not os.path.exists(filename): + raise IOError('File not found: {}'.format(filename)) + source_root = os.path.dirname(filename) + while True: + gnfile = os.path.join(source_root, 'src', '.gn') + if os.path.exists(gnfile): + return source_root + + new_package_root = os.path.dirname(source_root) + if new_package_root == source_root: + raise Exception("Can't determine package root") + source_root = new_package_root
diff --git a/tools/codesearch/codesearch/test_file_cache.py b/tools/codesearch/codesearch/test_file_cache.py new file mode 100644 index 0000000..fd0336d --- /dev/null +++ b/tools/codesearch/codesearch/test_file_cache.py
@@ -0,0 +1,48 @@ +# Copyright 2017 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. + +from __future__ import absolute_import + +import unittest +import tempfile +import shutil +from .file_cache import FileCache + + +class TestFileCache(unittest.TestCase): + + def test_with_no_cache_dir(self): + try: + f = FileCache() + f.put('foo', 'hello') + self.assertEqual('hello', f.get('foo')) + finally: + f.close() + + def test_with_cache_dir(self): + f = None + g = None + test_dir = None + try: + test_dir = tempfile.mkdtemp() + f = FileCache(cache_dir=test_dir) + f.put('foo', 'hello') + f.close() + f = None + + g = FileCache(cache_dir=test_dir) + self.assertEqual('hello', g.get('foo')) + g.close() + g = None + finally: + if f: + f.close() + if g: + g.close() + if test_dir: + shutil.rmtree(test_dir) + + +if __name__ == '__main__': + unittest.main()
diff --git a/tools/codesearch/codesearch/test_messages.py b/tools/codesearch/codesearch/test_messages.py new file mode 100644 index 0000000..9e699ac8 --- /dev/null +++ b/tools/codesearch/codesearch/test_messages.py
@@ -0,0 +1,108 @@ +# Copyright 2017 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. + +from __future__ import absolute_import + +import unittest + +from .messages import Message, message + + +@message +class Foo(Message): + DESCRIPTOR = {'x': int} + + +class Bar(Message): + DESCRIPTOR = {'x': int, 'y': Message.PARENT_TYPE} + + +class Baz(Message): + DESCRIPTOR = {'x': int, 'y': [Message.PARENT_TYPE]} + + +class Qux(Message): + FOO = 1 + BAR = 2 + + DESCRIPTOR = int + + +class Quux(Message): + DESCRIPTOR = {'x': Qux} + + +class TestProto(unittest.TestCase): + + def test_proto_bridge(self): + v = Foo() + v.x = 3 + self.assertEqual(v.AsQueryString(), [('x', '3')]) + + def test_from_json_string_1(self): + v = Message.FromJsonString('{"x": 3}') + self.assertEqual(v.x, 3) + + def test_from_json_string_2(self): + v = Foo.FromJsonString('{"x": 3}') + self.assertTrue(isinstance(v, Foo)) + self.assertTrue(isinstance(v.x, int)) + self.assertEqual(v.x, 3) + + def test_from_json_string_3(self): + v = Bar.FromJsonString('{"x": 3, "y": {"x": 4}}') + self.assertTrue(isinstance(v, Bar)) + self.assertTrue(isinstance(v.y, Bar)) + self.assertEqual(v.x, 3) + self.assertEqual(v.y.x, 4) + + def test_from_json_string_4(self): + v = Foo.FromJsonString('{"y": 3}') + self.assertTrue(isinstance(v, Foo)) + + def test_from_json_string_5(self): + v = Foo.FromJsonString('{"y": 3}') + self.assertTrue(isinstance(v, Foo)) + self.assertEqual(v.y, 3) + + def test_from_json_string_6(self): + v = Quux.FromJsonString('{"x": 3}') + self.assertTrue(isinstance(v, Quux)) + self.assertTrue(isinstance(v.x, int)) + self.assertEqual(v.x, 3) + + def test_from_json_string_7(self): + v = Quux.FromJsonString('{"x": "FOO"}') + self.assertTrue(isinstance(v, Quux)) + self.assertTrue(isinstance(v.x, int)) + self.assertEqual(v.x, 1) + + def test_from_shallow_dict_1(self): + v = Baz.FromShallowDict({'x': 3, 'y': [{'x': 4}, {'x': 5}]}) + self.assertTrue(isinstance(v, Baz)) + self.assertTrue(isinstance(v.y, list)) + self.assertTrue(isinstance(v.y[0], Baz)) + self.assertTrue(isinstance(v.y[1], Baz)) + + +class TestConstructor(unittest.TestCase): + + def test_empty_class(self): + f = Foo() + self.assertFalse(hasattr(f, 'x')) + + def test_class_with_known_keyword(self): + f = Foo(x=10) + self.assertTrue(hasattr(f, 'x')) + self.assertEqual(10, f.x) + + def test_class_with_unknown_keyword(self): + f = Foo(x=10, y=9) + self.assertTrue(hasattr(f, 'x')) + self.assertTrue(hasattr(f, 'y')) + self.assertEqual(9, f.y) + + +if __name__ == '__main__': + unittest.main()
diff --git a/ui/gl/gl_image.cc b/ui/gl/gl_image.cc index c11006d..8525c6c 100644 --- a/ui/gl/gl_image.cc +++ b/ui/gl/gl_image.cc
@@ -6,6 +6,11 @@ namespace gl { +bool GLImage::BindTexImageWithInternalformat(unsigned target, + unsigned internalformat) { + return false; +} + bool GLImage::EmulatingRGB() const { return false; }
diff --git a/ui/gl/gl_image.h b/ui/gl/gl_image.h index f0d0842..be3d6f4c 100644 --- a/ui/gl/gl_image.h +++ b/ui/gl/gl_image.h
@@ -43,6 +43,13 @@ // It is valid for an implementation to always return false. virtual bool BindTexImage(unsigned target) = 0; + // Bind image to texture currently bound to |target|, forcing the texture's + // internal format to the specified one. This is a feature not available on + // all platforms. Returns true on success. It is valid for an implementation + // to always return false. + virtual bool BindTexImageWithInternalformat(unsigned target, + unsigned internalformat); + // Release image from texture currently bound to |target|. virtual void ReleaseTexImage(unsigned target) = 0;
diff --git a/ui/gl/gl_image_io_surface.h b/ui/gl/gl_image_io_surface.h index 70962e6d..9cffcec 100644 --- a/ui/gl/gl_image_io_surface.h +++ b/ui/gl/gl_image_io_surface.h
@@ -45,6 +45,8 @@ gfx::Size GetSize() override; unsigned GetInternalFormat() override; bool BindTexImage(unsigned target) override; + bool BindTexImageWithInternalformat(unsigned target, + unsigned internalformat) override; void ReleaseTexImage(unsigned target) override {} bool CopyTexImage(unsigned target) override; bool CopyTexSubImage(unsigned target,
diff --git a/ui/gl/gl_image_io_surface.mm b/ui/gl/gl_image_io_surface.mm index 8e20830..fc4df49 100644 --- a/ui/gl/gl_image_io_surface.mm +++ b/ui/gl/gl_image_io_surface.mm
@@ -168,8 +168,9 @@ } // When an IOSurface is bound to a texture with internalformat "GL_RGB", many -// OpenGL operations are broken. Therefore, never allow an IOSurface to be bound -// with GL_RGB. https://crbug.com/595948. +// OpenGL operations are broken. Therefore, don't allow an IOSurface to be bound +// with GL_RGB unless overridden via BindTexImageWithInternalformat. +// crbug.com/595948, crbug.com/699566. GLenum ConvertRequestedInternalFormat(GLenum internalformat) { if (internalformat == GL_RGB) return GL_RGBA; @@ -237,6 +238,11 @@ } bool GLImageIOSurface::BindTexImage(unsigned target) { + return BindTexImageWithInternalformat(target, 0); +} + +bool GLImageIOSurface::BindTexImageWithInternalformat(unsigned target, + unsigned internalformat) { DCHECK(thread_checker_.CalledOnValidThread()); TRACE_EVENT0("gpu", "GLImageIOSurface::BindTexImage"); base::TimeTicks start_time = base::TimeTicks::Now(); @@ -258,10 +264,12 @@ static_cast<CGLContextObj>(GLContext::GetCurrent()->GetHandle()); DCHECK(io_surface_); - CGLError cgl_error = - CGLTexImageIOSurface2D(cgl_context, target, TextureFormat(format_), - size_.width(), size_.height(), DataFormat(format_), - DataType(format_), io_surface_.get(), 0); + + GLenum texture_format = + internalformat ? internalformat : TextureFormat(format_); + CGLError cgl_error = CGLTexImageIOSurface2D( + cgl_context, target, texture_format, size_.width(), size_.height(), + DataFormat(format_), DataType(format_), io_surface_.get(), 0); if (cgl_error != kCGLNoError) { LOG(ERROR) << "Error in CGLTexImageIOSurface2D: " << CGLErrorString(cgl_error);