diff --git a/.vpython b/.vpython index 8273273..55da4c6c 100644 --- a/.vpython +++ b/.vpython
@@ -9,7 +9,7 @@ # CIPD (the "Chrome Infrastructure Package Deployer" service). Unlike `pip`, # this never requires the end-user machine to have a working python extension # compilation environment. All of these packages are built using: -# https://chromium.googlesource.com/infra/infra/+/master/infra/tools/dockerbuild/ +# https://chromium.googlesource.com/infra/infra/+/main/infra/tools/dockerbuild/ # # All python scripts in the repo share this same spec, to avoid dependency # fragmentation. @@ -20,10 +20,10 @@ # vpython path/to/script.py some --arguments # # Read more about `vpython` and how to modify this file here: -# https://chromium.googlesource.com/infra/infra/+/master/doc/users/vpython.md +# https://chromium.googlesource.com/infra/infra/+/main/doc/users/vpython.md # # For the definition of this spec, see: -# https://chromium.googlesource.com/infra/luci/luci-go/+/master/vpython/api/vpython/spec.proto +# https://chromium.googlesource.com/infra/luci/luci-go/+/main/vpython/api/vpython/spec.proto python_version: "2.7"
diff --git a/.vpython3 b/.vpython3 index 2f32b530..fbab716 100644 --- a/.vpython3 +++ b/.vpython3
@@ -9,7 +9,7 @@ # CIPD (the "Chrome Infrastructure Package Deployer" service). Unlike `pip`, # this never requires the end-user machine to have a working python extension # compilation environment. All of these packages are built using: -# https://chromium.googlesource.com/infra/infra/+/master/infra/tools/dockerbuild/ +# https://chromium.googlesource.com/infra/infra/+/main/infra/tools/dockerbuild/ # # All python scripts in the repo share this same spec, to avoid dependency # fragmentation. @@ -20,7 +20,7 @@ # vpython path/to/script.py some --arguments # # Read more about `vpython` and how to modify this file here: -# https://chromium.googlesource.com/infra/infra/+/master/doc/users/vpython.md +# https://chromium.googlesource.com/infra/infra/+/main/doc/users/vpython.md python_version: "3.8" # Used by:
diff --git a/BUILD.gn b/BUILD.gn index 1985d70f..299555e1 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -1216,7 +1216,7 @@ "100", ] - # https://chromium.googlesource.com/chromium/src/+/master/docs/testing/web_tests.md + # https://chromium.googlesource.com/chromium/src/+/main/docs/testing/web_tests.md script_test("blink_web_tests") { run_under_python2 = true script = _common_web_test_script
diff --git a/DEPS b/DEPS index 8a9479d..0c2879bb 100644 --- a/DEPS +++ b/DEPS
@@ -209,11 +209,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': 'aad4b80fa625d6e1d1ef18d67ba2c63d5e4c2be8', + 'skia_revision': 'a733cb07c66464f13bd41581dca3d54d16886842', # 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': 'd423c12549456042cbb0ae0dfd11d4b5b7508e83', + 'v8_revision': '583828a7ea1c6af3d5849ed37226e31eb9b51ccc', # 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. @@ -221,11 +221,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '77831a6dbb9470016b5b484d333f2270a4af20af', + 'angle_revision': 'a5b302d38fc7f5bb49b65da2b9272b9f52aa347c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. - 'swiftshader_revision': 'efe254de5d881c3d65030c6a5801d6969f9dde35', + 'swiftshader_revision': '3da42a3a2cd6d2205b05c224f50364caeab08de9', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. @@ -288,7 +288,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling devtools-frontend # and whatever else without interference from each other. - 'devtools_frontend_revision': '76d44bdb689e360db192add73a136e48b76a3221', + 'devtools_frontend_revision': '7554e1a13d8427c6f5292103845c03ca12d84278', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -328,7 +328,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': 'ed8a8c089396c3f5c07c2af36502a8976970f3f3', + 'dawn_revision': '6dc5562fbbc72724c517f4a5711875d9ec665225', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -643,7 +643,7 @@ 'packages': [ { 'package': 'chromium/rts/model/linux-amd64', - 'version': 'n6V5ugyuJB-3u-R-SP9WmlYEF8VQrbpbnUZZ3pHK6LoC', + 'version': 'G8WtHJPwyIMNSe0N-HqYlFwbXR7OHHU_pcPpkV44r7UC', }, ], 'dep_type': 'cipd', @@ -654,7 +654,7 @@ 'packages': [ { 'package': 'chromium/rts/model/mac-amd64', - 'version': 'SiPOVzox1GTuGDS_MCmmVmDDM9PU9tCzeaFwMTc5lkMC', + 'version': 'jwIX5QPwHRgLIbwtJgffXOE0q6JCils_fOKmdqL5ZzwC', }, ], 'dep_type': 'cipd', @@ -665,7 +665,7 @@ 'packages': [ { 'package': 'chromium/rts/model/windows-amd64', - 'version': 'Khuuxh5stE9IB4UMJq2-g399U6ul6Pp5QWV5-13d1h4C', + 'version': 'V_lSKkP-1tM9qB3o1N6fqWiHjnG23RSVsDclXrfaMZMC', }, ], 'dep_type': 'cipd', @@ -1349,7 +1349,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '2d7bf1e435f95bbe0b2f7973c5ec978bd72ec41b', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '468c71c71f9b2f51f88a71eddfcf1068e9c026a7', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1547,7 +1547,7 @@ 'src/third_party/usrsctp/usrsctplib': Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + '22ba62ffe79c3881581ab430368bf3764d9533eb', - 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@a192354a0d2ad9827d8e8c155d62d577e292b7f4', + 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@121f7d60f035f7dfb3fadb3633b292140d79b454', 'src/third_party/vulkan_memory_allocator': Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '732a76d9d3c70d6aa487216495eeb28518349c3a', @@ -1635,7 +1635,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c4f2a58e5b80f81a52296bb6daa2d840caefc656', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@aa017d9e3db52282ae37381214f1cc2a71a8fd27', 'condition': 'checkout_src_internal', },
diff --git a/DIR_METADATA b/DIR_METADATA index 72d5cc7..6f7b1955 100644 --- a/DIR_METADATA +++ b/DIR_METADATA
@@ -5,10 +5,10 @@ # team that works on the code. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { project: "chromium"
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 3a07e2d..cc586f8 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -1142,8 +1142,8 @@ 'build/android/gyp/prepare_resources.pydeps', 'build/android/gyp/process_native_prebuilt.pydeps', 'build/android/gyp/proguard.pydeps', - 'build/android/gyp/resources_shrinker/shrinker.pydeps', 'build/android/gyp/turbine.pydeps', + 'build/android/gyp/unused_resources.pydeps', 'build/android/gyp/validate_static_library_dex_references.pydeps', 'build/android/gyp/write_build_config.pydeps', 'build/android/gyp/write_native_libraries_java.pydeps', @@ -4030,7 +4030,7 @@ 'LLVMFuzzerInitialize should not be used, unless your fuzz target needs ' 'to access command line arguments passed to the fuzzer. Instead, prefer ' 'static initialization and shared resources as documented in ' - 'https://chromium.googlesource.com/chromium/src/+/master/testing/' + 'https://chromium.googlesource.com/chromium/src/+/main/testing/' 'libfuzzer/efficient_fuzzing.md#simplifying-initialization_cleanup.\n' % ( ', '.join(EXPORTED_SYMBOLS), REQUIRED_HEADER) )
diff --git a/WATCHLISTS b/WATCHLISTS index 0b42058..942ef27 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -3,7 +3,7 @@ # found in the LICENSE file. # Watchlist Rules -# Refer: https://chromium.googlesource.com/chromium/src/+/master/docs/infra/watchlists.md +# Refer: https://chromium.googlesource.com/chromium/src/+/main/docs/infra/watchlists.md # IMPORTANT: The regular expression filepath is tested against each path using # re.search, so it is not usually necessary to add .*.
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index 9165420a..2f8327926 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -2470,7 +2470,6 @@ "//ash/public/cpp/external_arc:unit_tests", "//ash/public/cpp/holding_space:test_support", "//ash/resources/vector_icons", - "//ash/services/recording:test_support", "//ash/shortcut_viewer:unit_tests", "//ash/strings", "//ash/system/machine_learning:user_settings_event_proto", @@ -2841,7 +2840,6 @@ "//ash/keyboard/ui:test_support", "//ash/public/cpp", "//ash/public/cpp:test_support", - "//ash/services/recording:test_support", "//ash/services/recording/public/mojom", "//base", "//base:i18n",
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc index 88fe699..c21ea9c 100644 --- a/ash/capture_mode/capture_mode_controller.cc +++ b/ash/capture_mode/capture_mode_controller.cc
@@ -851,7 +851,6 @@ // Resetting the service remote would terminate its process. recording_service_remote_.reset(); - delegate_->OnServiceRemoteReset(); recording_service_client_receiver_.reset(); OnVideoFileSaved(thumbnail, success);
diff --git a/ash/capture_mode/capture_mode_unittests.cc b/ash/capture_mode/capture_mode_unittests.cc index f8fcee1..4082a4b 100644 --- a/ash/capture_mode/capture_mode_unittests.cc +++ b/ash/capture_mode/capture_mode_unittests.cc
@@ -30,7 +30,6 @@ #include "ash/public/cpp/ash_features.h" #include "ash/public/cpp/capture_mode_test_api.h" #include "ash/root_window_controller.h" -#include "ash/services/recording/recording_service_test_api.h" #include "ash/shell.h" #include "ash/system/status_area_widget.h" #include "ash/test/ash_test_base.h" @@ -42,7 +41,6 @@ #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" -#include "base/bind.h" #include "base/callback_helpers.h" #include "base/run_loop.h" #include "base/scoped_observation.h" @@ -1802,31 +1800,27 @@ CaptureModeType::kVideo); controller->StartVideoRecordingImmediatelyForTesting(); EXPECT_TRUE(controller->is_recording_in_progress()); - CaptureModeTestApi().FlushRecordingServiceForTesting(); auto* test_delegate = static_cast<TestCaptureModeDelegate*>(controller->delegate_for_testing()); - // Request and wait for a video frame so that the recording service can use it - // to create a video thumbnail. - test_delegate->RequestAndWaitForVideoFrame(); - SkBitmap service_thumbnail = - gfx::Image(test_delegate->GetVideoThumbnail()).AsBitmap(); - EXPECT_FALSE(service_thumbnail.drawsNothing()); + // Use a random bitmap as the fake thumbnail. + SkBitmap thumbnail; + thumbnail.allocN32Pixels(400, 300); + EXPECT_FALSE(thumbnail.drawsNothing()); + test_delegate->SetVideoThumbnail( + gfx::ImageSkia::CreateFrom1xBitmap(thumbnail)); CaptureNotificationWaiter waiter; controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); EXPECT_FALSE(controller->is_recording_in_progress()); waiter.Wait(); - // Verify that the service's thumbnail is the same image shown in the - // notification shown when recording ends. const message_center::Notification* notification = GetPreviewNotification(); EXPECT_TRUE(notification); EXPECT_FALSE(notification->image().IsEmpty()); - const SkBitmap notification_thumbnail = notification->image().AsBitmap(); - EXPECT_TRUE( - gfx::test::AreBitmapsEqual(notification_thumbnail, service_thumbnail)); + const SkBitmap actual_thumbnail = notification->image().AsBitmap(); + EXPECT_TRUE(gfx::test::AreBitmapsEqual(actual_thumbnail, thumbnail)); } TEST_F(CaptureModeTest, WindowRecordingCaptureId) { @@ -2093,55 +2087,6 @@ EXPECT_EQ(gfx::Size(100, 200), test_delegate->GetCurrentVideoSize()); } -// Tests that the video frames delivered to the service for recorded windows are -// valid (i.e. they have the correct size, and suffer from no letterboxing, even -// when the window gets resized). -// This is a regression test for https://crbug.com/1214023. -TEST_F(CaptureModeTest, VerifyWindowRecordingVideoFrames) { - auto window = CreateTestWindow(gfx::Rect(100, 50, 200, 200)); - StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); - - auto* event_generator = GetEventGenerator(); - event_generator->MoveMouseToCenterOf(window.get()); - auto* controller = CaptureModeController::Get(); - controller->StartVideoRecordingImmediatelyForTesting(); - EXPECT_TRUE(controller->is_recording_in_progress()); - CaptureModeTestApi test_api; - test_api.FlushRecordingServiceForTesting(); - - auto verify_video_frame = [&window](const media::VideoFrame& frame, - const gfx::Rect& content_rect) { - // Having the content positioned at (0,0) with a size that matches the - // current window's size means that there is no letterboxing. - EXPECT_EQ(gfx::Point(), content_rect.origin()); - const gfx::Size window_size = window->bounds().size(); - EXPECT_EQ(window_size, content_rect.size()); - // The video frame contents should match the bounds of the video frame. - EXPECT_EQ(frame.visible_rect(), content_rect); - EXPECT_EQ(frame.coded_size(), window_size); - }; - - auto* test_delegate = - static_cast<TestCaptureModeDelegate*>(controller->delegate_for_testing()); - ASSERT_TRUE(test_delegate->recording_service()); - SCOPED_TRACE("Initial window size"); - test_delegate->recording_service()->RequestAndWaitForVideoFrame( - base::BindLambdaForTesting(verify_video_frame)); - - // Even when the window is resized and the throttled size reaches the service, - // new video frames should still be valid. - window->SetBounds(gfx::Rect(120, 60, 600, 500)); - auto* recording_watcher = controller->video_recording_watcher_for_testing(); - recording_watcher->SendThrottledWindowSizeChangedNowForTesting(); - test_api.FlushRecordingServiceForTesting(); - SCOPED_TRACE("After window resizing"); - test_delegate->recording_service()->RequestAndWaitForVideoFrame( - base::BindLambdaForTesting(verify_video_frame)); - - controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); - EXPECT_FALSE(controller->is_recording_in_progress()); -} - // Tests the behavior of screen recording with the presence of HDCP secure // content on the screen in all capture mode sources (fullscreen, region, and // window) depending on the test param.
diff --git a/ash/capture_mode/test_capture_mode_delegate.cc b/ash/capture_mode/test_capture_mode_delegate.cc index 22e7439..a599f5fb 100644 --- a/ash/capture_mode/test_capture_mode_delegate.cc +++ b/ash/capture_mode/test_capture_mode_delegate.cc
@@ -6,12 +6,115 @@ #include "ash/capture_mode/capture_mode_types.h" #include "ash/services/recording/public/mojom/recording_service.mojom.h" -#include "ash/services/recording/recording_service_test_api.h" #include "base/files/file_util.h" #include "base/threading/thread_restrictions.h" namespace ash { +// ----------------------------------------------------------------------------- +// FakeRecordingService: + +class FakeRecordingService : public recording::mojom::RecordingService { + public: + FakeRecordingService() : receiver_(this) {} + FakeRecordingService(const FakeRecordingService&) = delete; + FakeRecordingService& operator=(const FakeRecordingService&) = delete; + ~FakeRecordingService() override = default; + + const viz::FrameSinkId& current_frame_sink_id() const { + return current_frame_sink_id_; + } + gfx::Size frame_sink_size() const { return frame_sink_size_; } + const gfx::Size& video_size() const { return video_size_; } + void set_thumbnail(const gfx::ImageSkia& thumbnail) { + thumbnail_ = thumbnail; + } + + void Bind( + mojo::PendingReceiver<recording::mojom::RecordingService> receiver) { + receiver_.Bind(std::move(receiver)); + } + + // mojom::RecordingService: + void RecordFullscreen( + mojo::PendingRemote<recording::mojom::RecordingServiceClient> client, + mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer, + mojo::PendingRemote<media::mojom::AudioStreamFactory> + audio_stream_factory, + const base::FilePath& webm_file_path, + const viz::FrameSinkId& frame_sink_id, + const gfx::Size& frame_sink_size) override { + remote_client_.Bind(std::move(client)); + current_frame_sink_id_ = frame_sink_id; + current_capture_source_ = CaptureModeSource::kFullscreen; + frame_sink_size_ = frame_sink_size; + video_size_ = frame_sink_size; + } + void RecordWindow( + mojo::PendingRemote<recording::mojom::RecordingServiceClient> client, + mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer, + mojo::PendingRemote<media::mojom::AudioStreamFactory> + audio_stream_factory, + const base::FilePath& webm_file_path, + const viz::FrameSinkId& frame_sink_id, + const gfx::Size& frame_sink_size, + const viz::SubtreeCaptureId& subtree_capture_id, + const gfx::Size& window_size) override { + remote_client_.Bind(std::move(client)); + current_frame_sink_id_ = frame_sink_id; + current_capture_source_ = CaptureModeSource::kWindow; + frame_sink_size_ = frame_sink_size; + video_size_ = window_size; + } + void RecordRegion( + mojo::PendingRemote<recording::mojom::RecordingServiceClient> client, + mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer, + mojo::PendingRemote<media::mojom::AudioStreamFactory> + audio_stream_factory, + const base::FilePath& webm_file_path, + const viz::FrameSinkId& frame_sink_id, + const gfx::Size& frame_sink_size, + const gfx::Rect& crop_region) override { + remote_client_.Bind(std::move(client)); + current_frame_sink_id_ = frame_sink_id; + current_capture_source_ = CaptureModeSource::kRegion; + frame_sink_size_ = frame_sink_size; + video_size_ = crop_region.size(); + } + void StopRecording() override { + remote_client_->OnRecordingEnded( + recording::mojom::RecordingStatus::kSuccess, thumbnail_); + remote_client_.FlushForTesting(); + } + void OnRecordedWindowChangingRoot( + const viz::FrameSinkId& new_frame_sink_id, + const gfx::Size& new_frame_sink_size) override { + DCHECK_EQ(current_capture_source_, CaptureModeSource::kWindow); + current_frame_sink_id_ = new_frame_sink_id; + frame_sink_size_ = new_frame_sink_size; + } + void OnRecordedWindowSizeChanged(const gfx::Size& new_window_size) override { + DCHECK_EQ(current_capture_source_, CaptureModeSource::kWindow); + video_size_ = new_window_size; + } + void OnFrameSinkSizeChanged(const gfx::Size& new_frame_sink_size) override { + DCHECK_NE(current_capture_source_, CaptureModeSource::kFullscreen); + frame_sink_size_ = new_frame_sink_size; + } + + private: + mojo::Receiver<recording::mojom::RecordingService> receiver_; + mojo::Remote<recording::mojom::RecordingServiceClient> remote_client_; + viz::FrameSinkId current_frame_sink_id_; + CaptureModeSource current_capture_source_ = CaptureModeSource::kFullscreen; + gfx::Size frame_sink_size_; + gfx::Size video_size_; + gfx::ImageSkia thumbnail_; +}; + +// ----------------------------------------------------------------------------- +// TestCaptureModeDelegate: + TestCaptureModeDelegate::TestCaptureModeDelegate() { base::ScopedAllowBlockingForTesting allow_blocking; const bool result = @@ -22,29 +125,22 @@ TestCaptureModeDelegate::~TestCaptureModeDelegate() = default; viz::FrameSinkId TestCaptureModeDelegate::GetCurrentFrameSinkId() const { - return recording_service_ ? recording_service_->GetCurrentFrameSinkId() - : viz::FrameSinkId(); + return fake_service_ ? fake_service_->current_frame_sink_id() + : viz::FrameSinkId(); } gfx::Size TestCaptureModeDelegate::GetCurrentFrameSinkSize() const { - return recording_service_ ? recording_service_->GetCurrentFrameSinkSize() - : gfx::Size(); + return fake_service_ ? fake_service_->frame_sink_size() : gfx::Size(); } gfx::Size TestCaptureModeDelegate::GetCurrentVideoSize() const { - return recording_service_ ? recording_service_->GetCurrentVideoSize() - : gfx::Size(); + return fake_service_ ? fake_service_->video_size() : gfx::Size(); } -gfx::ImageSkia TestCaptureModeDelegate::GetVideoThumbnail() const { - return recording_service_ ? recording_service_->GetVideoThumbnail() - : gfx::ImageSkia(); -} - -void TestCaptureModeDelegate::RequestAndWaitForVideoFrame() { - DCHECK(recording_service_); - - recording_service_->RequestAndWaitForVideoFrame(); +void TestCaptureModeDelegate::SetVideoThumbnail( + const gfx::ImageSkia& thumbnail) { + if (fake_service_) + fake_service_->set_thumbnail(thumbnail); } base::FilePath TestCaptureModeDelegate::GetScreenCaptureDir() const { @@ -84,10 +180,10 @@ mojo::Remote<recording::mojom::RecordingService> TestCaptureModeDelegate::LaunchRecordingService() { - mojo::Remote<recording::mojom::RecordingService> service_remote; - recording_service_ = std::make_unique<recording::RecordingServiceTestApi>( - service_remote.BindNewPipeAndPassReceiver()); - return service_remote; + fake_service_ = std::make_unique<FakeRecordingService>(); + mojo::Remote<recording::mojom::RecordingService> service; + fake_service_->Bind(service.BindNewPipeAndPassReceiver()); + return service; } void TestCaptureModeDelegate::BindAudioStreamFactory( @@ -95,11 +191,4 @@ void TestCaptureModeDelegate::OnSessionStateChanged(bool started) {} -void TestCaptureModeDelegate::OnServiceRemoteReset() { - // We simulate what the ServiceProcessHost does when the service remote is - // reset (on which it shuts down the service process). Here since the service - // is running in-process with ash_unittests, we just delete the instance. - recording_service_.reset(); -} - } // namespace ash
diff --git a/ash/capture_mode/test_capture_mode_delegate.h b/ash/capture_mode/test_capture_mode_delegate.h index 1ced147..04f4e47 100644 --- a/ash/capture_mode/test_capture_mode_delegate.h +++ b/ash/capture_mode/test_capture_mode_delegate.h
@@ -14,12 +14,10 @@ #include "ui/gfx/geometry/size.h" #include "ui/gfx/image/image_skia.h" -namespace recording { -class RecordingServiceTestApi; -} // namespace recording - namespace ash { +class FakeRecordingService; + class TestCaptureModeDelegate : public CaptureModeDelegate { public: TestCaptureModeDelegate(); @@ -27,26 +25,18 @@ TestCaptureModeDelegate& operator=(const TestCaptureModeDelegate&) = delete; ~TestCaptureModeDelegate() override; - recording::RecordingServiceTestApi* recording_service() const { - return recording_service_.get(); - } - - // Gets the current frame sink id being captured by the service. + // Gets the current frame sink id being captured by the fake service. viz::FrameSinkId GetCurrentFrameSinkId() const; // Gets the current size of the frame sink being recorded. gfx::Size GetCurrentFrameSinkSize() const; - // Gets the current video size being captured by the service. + // Gets the current video size being captured by the fake service. gfx::Size GetCurrentVideoSize() const; - // Gets the thumbnail image that will be used by the service to provide it to - // the client. - gfx::ImageSkia GetVideoThumbnail() const; - - // Requests a video frame from the video capturer and waits for it to be - // delivered to the service. - void RequestAndWaitForVideoFrame(); + // Sets the thumbnail image that will be used by the fake service to provide + // it to the client. + void SetVideoThumbnail(const gfx::ImageSkia& thumbnail); // CaptureModeDelegate: base::FilePath GetScreenCaptureDir() const override; @@ -69,10 +59,9 @@ mojo::PendingReceiver<media::mojom::AudioStreamFactory> receiver) override; void OnSessionStateChanged(bool started) override; - void OnServiceRemoteReset() override; private: - std::unique_ptr<recording::RecordingServiceTestApi> recording_service_; + std::unique_ptr<FakeRecordingService> fake_service_; base::FilePath fake_downloads_dir_; };
diff --git a/ash/capture_mode/video_recording_watcher.cc b/ash/capture_mode/video_recording_watcher.cc index b25322b..a4b2d99 100644 --- a/ash/capture_mode/video_recording_watcher.cc +++ b/ash/capture_mode/video_recording_watcher.cc
@@ -91,10 +91,10 @@ const SkBitmap& cursor_bitmap) { DCHECK(recorded_window); - // The video size, and the resolution constraints will be matching the size of - // the recorded window (whether a root or a non-root window). Hence, the - // bounds of the cursor overlay should be relative to that size. - const auto window_size = recorded_window->bounds().size(); + // Even when recording a non-root window, we use the bounds of the root + // window, since it corresponds to the bounds of the source frame sink we are + // recording. + const auto window_size = recorded_window->GetRootWindow()->bounds().size(); if (window_size.IsEmpty()) return gfx::RectF();
diff --git a/ash/public/cpp/capture_mode_delegate.h b/ash/public/cpp/capture_mode_delegate.h index 05603f2..1356821 100644 --- a/ash/public/cpp/capture_mode_delegate.h +++ b/ash/public/cpp/capture_mode_delegate.h
@@ -88,10 +88,6 @@ // Called when a capture mode session starts or stops. virtual void OnSessionStateChanged(bool started) = 0; - - // Called after the controller resets its |mojo::Remote| instance of the - // service. - virtual void OnServiceRemoteReset() = 0; }; } // namespace ash
diff --git a/ash/services/recording/BUILD.gn b/ash/services/recording/BUILD.gn index f4910a0..a38155b 100644 --- a/ash/services/recording/BUILD.gn +++ b/ash/services/recording/BUILD.gn
@@ -20,19 +20,3 @@ "//services/audio/public/cpp", ] } - -source_set("test_support") { - testonly = true - - sources = [ - "recording_service_test_api.cc", - "recording_service_test_api.h", - ] - - deps = [ - "//ash/services/recording", - "//ash/services/recording/public/mojom", - "//base", - "//media", - ] -}
diff --git a/ash/services/recording/recording_service.cc b/ash/services/recording/recording_service.cc index e478677..36fc860 100644 --- a/ash/services/recording/recording_service.cc +++ b/ash/services/recording/recording_service.cc
@@ -322,11 +322,6 @@ if (video_thumbnail_.isNull()) video_thumbnail_ = ExtractImageFromVideoFrame(*frame); - if (on_video_frame_delivered_callback_for_testing_) { - std::move(on_video_frame_delivered_callback_for_testing_) - .Run(*frame, content_rect); - } - encoder_muxer_.AsyncCall(&RecordingEncoderMuxer::EncodeVideo).WithArgs(frame); }
diff --git a/ash/services/recording/recording_service.h b/ash/services/recording/recording_service.h index 27c68b5..d88ddce 100644 --- a/ash/services/recording/recording_service.h +++ b/ash/services/recording/recording_service.h
@@ -33,8 +33,6 @@ namespace recording { -class RecordingServiceTestApi; - // Implements the mojo interface of the recording service which handles // recording audio and video of the screen or portion of it, and writes the webm // muxed video chunks directly to a file at a path provided to the Record*() @@ -105,8 +103,6 @@ void OnCaptureMuted(bool is_muted) override; private: - friend class RecordingServiceTestApi; - void StartNewRecording( mojo::PendingRemote<mojom::RecordingServiceClient> client, mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer, @@ -204,14 +200,6 @@ mojo::Remote<mojom::RecordingServiceClient> client_remote_ GUARDED_BY_CONTEXT(main_thread_checker_); - // A callback used for testing, which will be triggered when a video frame is - // delivered to the service from the Viz capturer. - using OnVideoFrameDeliveredCallback = - base::OnceCallback<void(const media::VideoFrame& frame, - const gfx::Rect& content_rect)>; - OnVideoFrameDeliveredCallback on_video_frame_delivered_callback_for_testing_ - GUARDED_BY_CONTEXT(main_thread_checker_); - // A cached scaled down rgb image of the first valid video frame which will be // used to provide the client with an image thumbnail representing the // recorded video.
diff --git a/ash/services/recording/recording_service_test_api.cc b/ash/services/recording/recording_service_test_api.cc deleted file mode 100644 index 9e7d598..0000000 --- a/ash/services/recording/recording_service_test_api.cc +++ /dev/null
@@ -1,75 +0,0 @@ -// Copyright 2021 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 "ash/services/recording/recording_service_test_api.h" - -#include "base/bind.h" -#include "base/run_loop.h" -#include "base/threading/thread_checker.h" - -namespace recording { - -RecordingServiceTestApi::RecordingServiceTestApi( - mojo::PendingReceiver<mojom::RecordingService> receiver) - : recording_service_(std::move(receiver)) {} - -viz::FrameSinkId RecordingServiceTestApi::GetCurrentFrameSinkId() const { - DCHECK_CALLED_ON_VALID_THREAD(recording_service_.main_thread_checker_); - DCHECK(recording_service_.current_video_capture_params_); - - return recording_service_.current_video_capture_params_->frame_sink_id(); -} - -gfx::Size RecordingServiceTestApi::GetCurrentFrameSinkSize() const { - DCHECK_CALLED_ON_VALID_THREAD(recording_service_.main_thread_checker_); - DCHECK(recording_service_.current_video_capture_params_); - - return recording_service_.current_video_capture_params_ - ->current_frame_sink_size(); -} - -gfx::Size RecordingServiceTestApi::GetCurrentVideoSize() const { - DCHECK_CALLED_ON_VALID_THREAD(recording_service_.main_thread_checker_); - DCHECK(recording_service_.current_video_capture_params_); - - return recording_service_.current_video_capture_params_->GetVideoSize(); -} - -gfx::ImageSkia RecordingServiceTestApi::GetVideoThumbnail() const { - DCHECK_CALLED_ON_VALID_THREAD(recording_service_.main_thread_checker_); - - return recording_service_.video_thumbnail_; -} - -void RecordingServiceTestApi::RequestAndWaitForVideoFrame( - VerifyVideoFrameCallback verify_frame_callback) { - DCHECK_CALLED_ON_VALID_THREAD(recording_service_.main_thread_checker_); - DCHECK(recording_service_.video_capturer_remote_); - DCHECK(!recording_service_.on_video_frame_delivered_callback_for_testing_); - - // Flush any pending calls from before. - recording_service_.video_capturer_remote_.FlushForTesting(); - recording_service_.consumer_receiver_.FlushForTesting(); - - base::RunLoop run_loop; - - recording_service_.on_video_frame_delivered_callback_for_testing_ = - base::BindOnce( - [](base::OnceClosure run_loop_quit_closure, - VerifyVideoFrameCallback verify_callback, - const media::VideoFrame& frame, const gfx::Rect& content_rect) { - if (verify_callback) - std::move(verify_callback).Run(frame, content_rect); - - std::move(run_loop_quit_closure).Run(); - }, - run_loop.QuitClosure(), std::move(verify_frame_callback)); - - recording_service_.video_capturer_remote_->RequestRefreshFrame(); - recording_service_.video_capturer_remote_.FlushForTesting(); - - run_loop.Run(); -} - -} // namespace recording
diff --git a/ash/services/recording/recording_service_test_api.h b/ash/services/recording/recording_service_test_api.h deleted file mode 100644 index 6bb95ba..0000000 --- a/ash/services/recording/recording_service_test_api.h +++ /dev/null
@@ -1,57 +0,0 @@ -// Copyright 2021 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 ASH_SERVICES_RECORDING_RECORDING_SERVICE_TEST_API_H_ -#define ASH_SERVICES_RECORDING_RECORDING_SERVICE_TEST_API_H_ - -#include "ash/services/recording/public/mojom/recording_service.mojom.h" -#include "ash/services/recording/recording_service.h" -#include "base/callback_forward.h" -#include "base/callback_helpers.h" -#include "media/base/video_frame.h" -#include "mojo/public/cpp/bindings/pending_receiver.h" -#include "ui/gfx/image/image_skia.h" - -namespace recording { - -// Defines an API to test the internals of the recording service. The recording -// service instance is created (in-process) and owned by this class. -class RecordingServiceTestApi { - public: - explicit RecordingServiceTestApi( - mojo::PendingReceiver<mojom::RecordingService> receiver); - RecordingServiceTestApi(const RecordingServiceTestApi&) = delete; - RecordingServiceTestApi& operator=(const RecordingServiceTestApi&) = delete; - ~RecordingServiceTestApi() = default; - - // Gets the current frame sink id being captured by the service. - viz::FrameSinkId GetCurrentFrameSinkId() const; - - // Gets the current size of the frame sink being recorded. - gfx::Size GetCurrentFrameSinkSize() const; - - // Gets the current video size being captured by the service. - gfx::Size GetCurrentVideoSize() const; - - // Gets the thumbnail image that will be used by the service to provide it to - // the client. - gfx::ImageSkia GetVideoThumbnail() const; - - // Requests a video frame from the video capturer and waits for it to be - // delivered to the service. If the caller provided a non-null - // |verify_frame_callback| it will be called before this function returns. - using VerifyVideoFrameCallback = - base::OnceCallback<void(const media::VideoFrame& frame, - const gfx::Rect& content_rect)>; - void RequestAndWaitForVideoFrame( - VerifyVideoFrameCallback verify_frame_callback = base::NullCallback()); - - private: - // The actual recording service instance. - RecordingService recording_service_; -}; - -} // namespace recording - -#endif // ASH_SERVICES_RECORDING_RECORDING_SERVICE_TEST_API_H_
diff --git a/ash/services/recording/video_capture_params.cc b/ash/services/recording/video_capture_params.cc index 1c771faa..5d1d989 100644 --- a/ash/services/recording/video_capture_params.cc +++ b/ash/services/recording/video_capture_params.cc
@@ -16,14 +16,6 @@ namespace { -// Returns a rect that is the result of intersecting the given two rects. -gfx::Rect GetIntersectionRect(const gfx::Rect& rect_a, - const gfx::Rect& rect_b) { - auto result = rect_a; - result.Intersect(rect_b); - return result; -} - // ----------------------------------------------------------------------------- // FullscreenCaptureParams: @@ -68,22 +60,9 @@ ~WindowCaptureParams() override = default; // VideoCaptureParams: - void SetCapturerResolutionConstraints( - mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer) - const override { - DCHECK(capturer); - - // To avoid receiving letterboxed video frames from the capturer, we ask it - // to give us an exact resolution matching the window's size. - capturer->SetResolutionConstraints(/*min_size=*/current_window_size_, - /*max_size=*/current_window_size_, - /*use_fixed_aspect_ratio=*/true); - } - gfx::Rect GetVideoFrameVisibleRect( const gfx::Rect& original_frame_visible_rect) const override { - return GetIntersectionRect(original_frame_visible_rect, - gfx::Rect(current_window_size_)); + return gfx::Rect(current_window_size_); } gfx::Size GetVideoSize() const override { return current_window_size_; } @@ -95,16 +74,20 @@ DCHECK(new_frame_sink_id.is_valid()); DCHECK_NE(frame_sink_id_, new_frame_sink_id); + // The video encoder deals with video frames. Changing the frame sink ID + // doesn't affect the encoder. What affects it is a change in the video + // frames size. + const bool should_reconfigure_video_encoder = + current_frame_sink_size_ != new_frame_sink_size; + current_frame_sink_size_ = new_frame_sink_size; frame_sink_id_ = new_frame_sink_id; + capturer->SetResolutionConstraints(/*min_size=*/current_frame_sink_size_, + /*max_size=*/current_frame_sink_size_, + /*use_fixed_aspect_ratio=*/true); capturer->ChangeTarget(frame_sink_id_, subtree_capture_id_); - // Changing the frame sink (i.e. changing the root window) should not lead - // to reconfiguring the encoder, even if the new frame sink size is - // different. This is because the video size matches the recorded window's - // size (i.e. |current_window_size_|). This is already handled in - // OnRecordedWindowSizeChanged() below. - return false; + return should_reconfigure_video_encoder; } bool OnRecordedWindowSizeChanged( @@ -113,7 +96,6 @@ if (current_window_size_ == new_window_size) return false; current_window_size_ = new_window_size; - SetCapturerResolutionConstraints(capturer); return true; } @@ -142,7 +124,9 @@ const gfx::Rect& original_frame_visible_rect) const override { // We can't crop the video frame by an invalid bounds. The crop bounds must // be contained within the original frame bounds. - return GetIntersectionRect(original_frame_visible_rect, crop_region_); + gfx::Rect visible_rect = original_frame_visible_rect; + visible_rect.Intersect(crop_region_); + return visible_rect; } gfx::Size GetVideoSize() const override { @@ -192,7 +176,9 @@ capturer->SetMinCapturePeriod(kMinCapturePeriod); capturer->SetMinSizeChangePeriod(kMinPeriodForResizeThrottling); - SetCapturerResolutionConstraints(capturer); + capturer->SetResolutionConstraints(/*min_size=*/current_frame_sink_size_, + /*max_size=*/current_frame_sink_size_, + /*use_fixed_aspect_ratio=*/true); capturer->SetAutoThrottlingEnabled(false); // TODO(afakhry): Discuss with //media/ team the implications of color space // conversions. @@ -200,15 +186,6 @@ capturer->ChangeTarget(frame_sink_id_, subtree_capture_id_); } -void VideoCaptureParams::SetCapturerResolutionConstraints( - mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer) const { - DCHECK(capturer); - - capturer->SetResolutionConstraints(/*min_size=*/current_frame_sink_size_, - /*max_size=*/current_frame_sink_size_, - /*use_fixed_aspect_ratio=*/true); -} - gfx::Rect VideoCaptureParams::GetVideoFrameVisibleRect( const gfx::Rect& original_frame_visible_rect) const { return original_frame_visible_rect; @@ -236,7 +213,9 @@ return false; current_frame_sink_size_ = new_frame_sink_size; - SetCapturerResolutionConstraints(capturer); + capturer->SetResolutionConstraints(/*min_size=*/current_frame_sink_size_, + /*max_size=*/current_frame_sink_size_, + /*use_fixed_aspect_ratio=*/true); return true; }
diff --git a/ash/services/recording/video_capture_params.h b/ash/services/recording/video_capture_params.h index 5c9d82a..e338827 100644 --- a/ash/services/recording/video_capture_params.h +++ b/ash/services/recording/video_capture_params.h
@@ -38,7 +38,7 @@ const gfx::Size& frame_sink_size); // Returns a capture params instance for a recording of a window. The given - // |frame_sink_id| is either of that window (if it submits compositor frames + //|frame_sink_id| is either of that window (if it submits compositor frames // independently), or of the root window it descends from (if it doesn't // submit its compositor frames). In the latter case, the window must be // identifiable by a valid |subtree_capture_id| (created by calling @@ -62,20 +62,11 @@ const gfx::Size& frame_sink_size, const gfx::Rect& crop_region); - const viz::FrameSinkId& frame_sink_id() const { return frame_sink_id_; } - gfx::Size current_frame_sink_size() const { return current_frame_sink_size_; } - // Initializes the given |capturer| (passed by ref) according to the capture // parameters. The given |capturer| must be bound before calling this. void InitializeVideoCapturer( mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer) const; - // Sets the desired resolution constraints on the given |capturer|. By default - // the size of the recorded frame sink is used. Sub classes can override this - // behavior if needed. - virtual void SetCapturerResolutionConstraints( - mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer) const; - // Returns the bounds to which a video frame, whose // |original_frame_visible_rect| is given, should be cropped. If no cropping // is desired, |original_frame_visible_rect| is returned. All bounds are in
diff --git a/ash/strings/ash_strings_az.xtb b/ash/strings/ash_strings_az.xtb index b0de9ea..1d74921e 100644 --- a/ash/strings/ash_strings_az.xtb +++ b/ash/strings/ash_strings_az.xtb
@@ -435,7 +435,7 @@ <translation id="4430019312045809116">Həcm</translation> <translation id="4445159312344259901">Kiliddən çıxarmaq üçün daxil olun</translation> <translation id="4449692009715125625">{NUM_NOTIFICATIONS,plural, =1{1 əhəmiyyətli bildiriş}other{# əhəmiyyətli bildiriş}}</translation> -<translation id="4450893287417543264">Daha göstərməyin</translation> +<translation id="4450893287417543264">Göstərilməsin</translation> <translation id="4451374464530248585">Alt + Aşağı Ox klaviatura qısayolu dəyişib. Səhifənin Aşağısına Keç düyməsindən istifadə etmək üçün <ph name="LAUNCHER_KEY_NAME" /> + Aşağı Ox düyməsini basın.</translation> <translation id="445864333228800152">Axşamınız xeyir,</translation> <translation id="4458688154122353284">Ekran çəkilişini dayandırın</translation>
diff --git a/ash/strings/ash_strings_ne.xtb b/ash/strings/ash_strings_ne.xtb index 7b373fe7..3c15a231 100644 --- a/ash/strings/ash_strings_ne.xtb +++ b/ash/strings/ash_strings_ne.xtb
@@ -770,7 +770,7 @@ <translation id="7378889811480108604">ब्याट्री सेभर मोड अफ छ</translation> <translation id="7392563512730092880">तपाईं पछि जुनसुकै बेला सेटिङमा गई सेट अप गर्न सक्नुहुन्छ।</translation> <translation id="7398254312354928459">नेटवर्क जडान बदल्ने कार्य गरियो</translation> -<translation id="7405710164030118432">यन्त्र अनलक गर्न आफ्नो Family Link को अभिभावकको पहुँचसम्बन्धी कोड प्रविष्टि गर्नुहोस्</translation> +<translation id="7405710164030118432">डिभाइस अनलक गर्न आफ्नो Family Link को अभिभावकको पहुँचसम्बन्धी कोड प्रविष्टि गर्नुहोस्</translation> <translation id="7406608787870898861">आफ्नो मोबाइल नेटवर्क सेटअप गर्ने कार्य पूरा गर्नुहोस्</translation> <translation id="741244894080940828">रूपान्तरण</translation> <translation id="7413851974711031813">बन्द गर्न Escape थिच्नुहोस्</translation>
diff --git a/base/allocator/partition_allocator/partition_bucket.cc b/base/allocator/partition_allocator/partition_bucket.cc index df468c6..6177fc1 100644 --- a/base/allocator/partition_allocator/partition_bucket.cc +++ b/base/allocator/partition_allocator/partition_bucket.cc
@@ -128,8 +128,8 @@ int flags, size_t raw_size, size_t slot_span_alignment) { - PA_DCHECK(slot_span_alignment && - !(slot_span_alignment & PartitionPageOffsetMask())); + PA_DCHECK((slot_span_alignment >= PartitionPageSize()) && + bits::IsPowerOfTwo(slot_span_alignment)); // No static EXCLUSIVE_LOCKS_REQUIRED(), as the checker doesn't understand // scoped unlocking. @@ -784,8 +784,8 @@ size_t raw_size, size_t slot_span_alignment, bool* is_already_zeroed) { - PA_DCHECK(slot_span_alignment && - !(slot_span_alignment & PartitionPageOffsetMask())); + PA_DCHECK((slot_span_alignment >= PartitionPageSize()) && + bits::IsPowerOfTwo(slot_span_alignment)); // The slow path is called when the freelist is empty. The only exception is // when a higher-order alignment is requested, in which case the freelist
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h index 607634fb..22abbe11 100644 --- a/base/allocator/partition_allocator/partition_root.h +++ b/base/allocator/partition_allocator/partition_root.h
@@ -958,8 +958,8 @@ size_t slot_span_alignment, size_t* usable_size, bool* is_already_zeroed) { - PA_DCHECK(slot_span_alignment && - !(slot_span_alignment & PartitionPageOffsetMask())); + PA_DCHECK((slot_span_alignment >= PartitionPageSize()) && + bits::IsPowerOfTwo(slot_span_alignment)); SlotSpan* slot_span = bucket->active_slot_spans_head; // Check that this slot span is neither full nor freed. PA_DCHECK(slot_span); @@ -1356,8 +1356,8 @@ size_t requested_size, size_t slot_span_alignment, const char* type_name) { - PA_DCHECK(slot_span_alignment && - !(slot_span_alignment & PartitionPageOffsetMask())); + PA_DCHECK((slot_span_alignment >= PartitionPageSize()) && + bits::IsPowerOfTwo(slot_span_alignment)); PA_DCHECK(flags < PartitionAllocLastFlag << 1); PA_DCHECK((flags & PartitionAllocNoHooks) == 0); // Internal only. @@ -1398,8 +1398,8 @@ int flags, size_t requested_size, size_t slot_span_alignment) { - PA_DCHECK(slot_span_alignment && - !(slot_span_alignment & PartitionPageOffsetMask())); + PA_DCHECK((slot_span_alignment >= PartitionPageSize()) && + bits::IsPowerOfTwo(slot_span_alignment)); // The thread cache is added "in the middle" of the main allocator, that is: // - After all the cookie/ref-count management
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h index c0bcfbab..a9616406d 100644 --- a/base/trace_event/builtin_categories.h +++ b/base/trace_event/builtin_categories.h
@@ -151,6 +151,7 @@ X("stadia_rtc") \ X("startup") \ X("sync") \ + X("system_apps") \ X("test_gpu") \ X("thread_pool") \ X("toplevel") \
diff --git a/base/tracing/protos/chrome_track_event.proto b/base/tracing/protos/chrome_track_event.proto index 54e11ce..2f9b2ea 100644 --- a/base/tracing/protos/chrome_track_event.proto +++ b/base/tracing/protos/chrome_track_event.proto
@@ -135,6 +135,15 @@ optional string mark = 4; } +message ChromeWebAppBadNavigate { + optional bool is_kiosk = 1; + optional bool has_hosted_app_controller = 2; + optional string app_name = 3; + optional uint32 system_app_type = 4; + optional bool web_app_provider_registry_ready = 5; + optional bool system_web_app_manager_synchronized = 6; +} + // These IDs are generated at compile time and differ for each chrome version. // IDs are stable on for a given chrome version but are changing when resources // are added or removed to chrome. @@ -144,7 +153,7 @@ message ChromeTrackEvent { // Extension range for Chrome: 1000-1999 - // Next ID: 1017 + // Next ID: 1018 extend TrackEvent { optional ChromeAppState chrome_app_state = 1000; @@ -176,5 +185,7 @@ // reserved 1012 to 1015. optional ResourceBundle resource_bundle = 1016; + + optional ChromeWebAppBadNavigate chrome_web_app_bad_navigate = 1017; } }
diff --git a/build/android/gyp/compile_java.py b/build/android/gyp/compile_java.py index 2a92842..521631a4 100755 --- a/build/android/gyp/compile_java.py +++ b/build/android/gyp/compile_java.py
@@ -421,9 +421,7 @@ save_outputs=True): logging.info('Starting _RunCompiler') - # Compiles with Error Prone take twice as long to run as pure javac. Thus GN - # rules run both in parallel, with Error Prone only used for checks. - save_outputs = not options.enable_errorprone + java_files = java_files.copy() # Use jar_path's directory to ensure paths are relative (needed for goma). temp_dir = jar_path + '.staging'
diff --git a/build/android/gyp/compile_resources.py b/build/android/gyp/compile_resources.py index 48409de..42b34f2 100755 --- a/build/android/gyp/compile_resources.py +++ b/build/android/gyp/compile_resources.py
@@ -177,29 +177,10 @@ '--no-xml-namespaces', action='store_true', help='Whether to strip xml namespaces from processed xml resources.') - input_opts.add_argument( - '--short-resource-paths', - action='store_true', - help='Whether to shorten resource paths inside the apk or module.') - input_opts.add_argument( - '--strip-resource-names', - action='store_true', - help='Whether to strip resource names from the resource table of the apk ' - 'or module.') output_opts.add_argument('--arsc-path', help='Apk output for arsc format.') output_opts.add_argument('--proto-path', help='Apk output for proto format.') group = input_opts.add_mutually_exclusive_group() - group.add_argument( - '--optimized-arsc-path', - help='Output for `aapt2 optimize` for arsc format (enables the step).') - group.add_argument( - '--optimized-proto-path', - help='Output for `aapt2 optimize` for proto format (enables the step).') - input_opts.add_argument( - '--resources-config-paths', - default='[]', - help='GN list of paths to aapt2 resources config files.') output_opts.add_argument( '--info-path', help='Path to output info file for the partial apk.') @@ -222,11 +203,6 @@ output_opts.add_argument( '--emit-ids-out', help='Path to file produced by aapt2 --emit-ids.') - output_opts.add_argument( - '--resources-path-map-out-path', - help='Path to file produced by aapt2 that maps original resource paths ' - 'to shortened resource paths inside the apk or module.') - input_opts.add_argument( '--is-bundle-module', action='store_true', @@ -257,20 +233,10 @@ options.values_filter_rules) options.extra_main_r_text_files = build_utils.ParseGnList( options.extra_main_r_text_files) - options.resources_config_paths = build_utils.ParseGnList( - options.resources_config_paths) - - if options.optimized_proto_path and not options.proto_path: - # We could write to a temp file, but it's simpler to require it. - parser.error('--optimized-proto-path requires --proto-path') if not options.arsc_path and not options.proto_path: parser.error('One of --arsc-path or --proto-path is required.') - if options.resources_path_map_out_path and not options.short_resource_paths: - parser.error( - '--resources-path-map-out-path requires --short-resource-paths') - if options.package_id and options.shared_resources: parser.error('--package-id and --shared-resources are mutually exclusive') @@ -846,9 +812,6 @@ # We always create a binary arsc file first, then convert to proto, so flags # such as --shared-lib can be supported. - arsc_path = build.arsc_path - if arsc_path is None: - _, arsc_path = tempfile.mkstmp() link_command += ['-o', build.arsc_path] logging.debug('Starting: aapt2 link') @@ -896,100 +859,9 @@ build.arsc_path, build.proto_path ]) - if build.arsc_path is None: - os.remove(arsc_path) - - if options.optimized_proto_path: - _OptimizeApk(build.optimized_proto_path, options, build.temp_dir, - build.proto_path, build.r_txt_path) - elif options.optimized_arsc_path: - _OptimizeApk(build.optimized_arsc_path, options, build.temp_dir, - build.arsc_path, build.r_txt_path) - return desired_manifest_package_name -def _CombineResourceConfigs(resources_config_paths, out_config_path): - with open(out_config_path, 'w') as out_config: - for config_path in resources_config_paths: - with open(config_path) as config: - out_config.write(config.read()) - out_config.write('\n') - - -def _OptimizeApk(output, options, temp_dir, unoptimized_path, r_txt_path): - """Optimize intermediate .ap_ file with aapt2. - - Args: - output: Path to write to. - options: The command-line options. - temp_dir: A temporary directory. - unoptimized_path: path of the apk to optimize. - r_txt_path: path to the R.txt file of the unoptimized apk. - """ - optimize_command = [ - options.aapt2_path, - 'optimize', - unoptimized_path, - '-o', - output, - ] - - # Optimize the resources.arsc file by obfuscating resource names and only - # allow usage via R.java constant. - if options.strip_resource_names: - no_collapse_resources = _ExtractNonCollapsableResources(r_txt_path) - gen_config_path = os.path.join(temp_dir, 'aapt2.config') - if options.resources_config_paths: - _CombineResourceConfigs(options.resources_config_paths, gen_config_path) - with open(gen_config_path, 'a') as config: - for resource in no_collapse_resources: - config.write('{}#no_collapse\n'.format(resource)) - - optimize_command += [ - '--collapse-resource-names', - '--resources-config-path', - gen_config_path, - ] - - if options.short_resource_paths: - optimize_command += ['--shorten-resource-paths'] - if options.resources_path_map_out_path: - optimize_command += [ - '--resource-path-shortening-map', options.resources_path_map_out_path - ] - - logging.debug('Running aapt2 optimize') - build_utils.CheckOutput( - optimize_command, print_stdout=False, print_stderr=False) - - -def _ExtractNonCollapsableResources(rtxt_path): - """Extract resources that should not be collapsed from the R.txt file - - Resources of type ID are references to UI elements/views. They are used by - UI automation testing frameworks. They are kept in so that they don't break - tests, even though they may not actually be used during runtime. See - https://crbug.com/900993 - App icons (aka mipmaps) are sometimes referenced by other apps by name so must - be keps as well. See https://b/161564466 - - Args: - rtxt_path: Path to R.txt file with all the resources - Returns: - List of resources in the form of <resource_type>/<resource_name> - """ - resources = [] - _NO_COLLAPSE_TYPES = ['id', 'mipmap'] - with open(rtxt_path) as rtxt: - for line in rtxt: - for resource_type in _NO_COLLAPSE_TYPES: - if ' {} '.format(resource_type) in line: - resource_name = line.split()[2] - resources.append('{}/{}'.format(resource_type, resource_name)) - return resources - - @contextlib.contextmanager def _CreateStableIdsFile(in_path, out_path, package_name): """Transforms a file generated by --emit-ids from another package. @@ -1018,8 +890,6 @@ (options.r_text_out, build.r_txt_path), (options.arsc_path, build.arsc_path), (options.proto_path, build.proto_path), - (options.optimized_arsc_path, build.optimized_arsc_path), - (options.optimized_proto_path, build.optimized_proto_path), (options.proguard_file, build.proguard_path), (options.proguard_file_main_dex, build.proguard_main_dex_path), (options.emit_ids_out, build.emit_ids_path),
diff --git a/build/android/gyp/optimize_resources.py b/build/android/gyp/optimize_resources.py new file mode 100755 index 0000000..d3b11636 --- /dev/null +++ b/build/android/gyp/optimize_resources.py
@@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# +# Copyright 2021 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 argparse +import logging +import os +import sys + +from util import build_utils + + +def _ParseArgs(args): + """Parses command line options. + + Returns: + An options object as from argparse.ArgumentParser.parse_args() + """ + parser = argparse.ArgumentParser() + parser.add_argument('--aapt2-path', + required=True, + help='Path to the Android aapt2 tool.') + parser.add_argument( + '--short-resource-paths', + action='store_true', + help='Whether to shorten resource paths inside the apk or module.') + parser.add_argument( + '--strip-resource-names', + action='store_true', + help='Whether to strip resource names from the resource table of the apk ' + 'or module.') + parser.add_argument('--proto-path', + required=True, + help='Input proto format resources APK.') + parser.add_argument('--resources-config-paths', + default='[]', + help='GN list of paths to aapt2 resources config files.') + parser.add_argument('--r-text-in', + required=True, + help='Path to R.txt. Used to exclude id/ resources.') + parser.add_argument( + '--resources-path-map-out-path', + help='Path to file produced by aapt2 that maps original resource paths ' + 'to shortened resource paths inside the apk or module.') + parser.add_argument('--optimized-proto-path', + required=True, + help='Output for `aapt2 optimize`.') + options = parser.parse_args(args) + + options.resources_config_paths = build_utils.ParseGnList( + options.resources_config_paths) + + if options.resources_path_map_out_path and not options.short_resource_paths: + parser.error( + '--resources-path-map-out-path requires --short-resource-paths') + return options + + +def _CombineResourceConfigs(resources_config_paths, out_config_path): + with open(out_config_path, 'w') as out_config: + for config_path in resources_config_paths: + with open(config_path) as config: + out_config.write(config.read()) + out_config.write('\n') + + +def _ExtractNonCollapsableResources(rtxt_path): + """Extract resources that should not be collapsed from the R.txt file + + Resources of type ID are references to UI elements/views. They are used by + UI automation testing frameworks. They are kept in so that they don't break + tests, even though they may not actually be used during runtime. See + https://crbug.com/900993 + App icons (aka mipmaps) are sometimes referenced by other apps by name so must + be keps as well. See https://b/161564466 + + Args: + rtxt_path: Path to R.txt file with all the resources + Returns: + List of resources in the form of <resource_type>/<resource_name> + """ + resources = [] + _NO_COLLAPSE_TYPES = ['id', 'mipmap'] + with open(rtxt_path) as rtxt: + for line in rtxt: + for resource_type in _NO_COLLAPSE_TYPES: + if ' {} '.format(resource_type) in line: + resource_name = line.split()[2] + resources.append('{}/{}'.format(resource_type, resource_name)) + return resources + + +def _OptimizeApk(output, options, temp_dir, unoptimized_path, r_txt_path): + """Optimize intermediate .ap_ file with aapt2. + + Args: + output: Path to write to. + options: The command-line options. + temp_dir: A temporary directory. + unoptimized_path: path of the apk to optimize. + r_txt_path: path to the R.txt file of the unoptimized apk. + """ + optimize_command = [ + options.aapt2_path, + 'optimize', + unoptimized_path, + '-o', + output, + ] + + # Optimize the resources.pb file by obfuscating resource names and only + # allow usage via R.java constant. + if options.strip_resource_names: + no_collapse_resources = _ExtractNonCollapsableResources(r_txt_path) + gen_config_path = os.path.join(temp_dir, 'aapt2.config') + if options.resources_config_paths: + _CombineResourceConfigs(options.resources_config_paths, gen_config_path) + with open(gen_config_path, 'a') as config: + for resource in no_collapse_resources: + config.write('{}#no_collapse\n'.format(resource)) + + optimize_command += [ + '--collapse-resource-names', + '--resources-config-path', + gen_config_path, + ] + + if options.short_resource_paths: + optimize_command += ['--shorten-resource-paths'] + if options.resources_path_map_out_path: + optimize_command += [ + '--resource-path-shortening-map', options.resources_path_map_out_path + ] + + logging.debug('Running aapt2 optimize') + build_utils.CheckOutput(optimize_command, + print_stdout=False, + print_stderr=False) + + +def main(args): + options = _ParseArgs(args) + with build_utils.TempDir() as temp_dir: + _OptimizeApk(options.optimized_proto_path, options, temp_dir, + options.proto_path, options.r_text_in) + + +if __name__ == '__main__': + main(sys.argv[1:])
diff --git a/build/android/gyp/optimize_resources.pydeps b/build/android/gyp/optimize_resources.pydeps new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/build/android/gyp/optimize_resources.pydeps
diff --git a/build/android/gyp/resources_shrinker/BUILD.gn b/build/android/gyp/resources_shrinker/BUILD.gn deleted file mode 100644 index e6381e1..0000000 --- a/build/android/gyp/resources_shrinker/BUILD.gn +++ /dev/null
@@ -1,15 +0,0 @@ -import("//build/config/android/rules.gni") - -java_binary("resources_shrinker") { - sources = [ "//build/android/gyp/resources_shrinker/Shrinker.java" ] - main_class = "build.android.gyp.resources_shrinker.Shrinker" - deps = [ - "//third_party/android_deps:com_android_tools_common_java", - "//third_party/android_deps:com_android_tools_layoutlib_layoutlib_api_java", - "//third_party/android_deps:com_android_tools_sdk_common_java", - "//third_party/android_deps:com_google_guava_guava_java", - "//third_party/android_deps:org_jetbrains_kotlin_kotlin_stdlib_java", - "//third_party/r8:r8_java", - ] - wrapper_script_name = "helper/resources_shrinker" -}
diff --git a/build/android/gyp/resources_shrinker/shrinker.pydeps b/build/android/gyp/resources_shrinker/shrinker.pydeps deleted file mode 100644 index 92c8905e..0000000 --- a/build/android/gyp/resources_shrinker/shrinker.pydeps +++ /dev/null
@@ -1,30 +0,0 @@ -# Generated by running: -# build/print_python_deps.py --root build/android/gyp/resources_shrinker --output build/android/gyp/resources_shrinker/shrinker.pydeps build/android/gyp/resources_shrinker/shrinker.py -../../../../third_party/jinja2/__init__.py -../../../../third_party/jinja2/_compat.py -../../../../third_party/jinja2/asyncfilters.py -../../../../third_party/jinja2/asyncsupport.py -../../../../third_party/jinja2/bccache.py -../../../../third_party/jinja2/compiler.py -../../../../third_party/jinja2/defaults.py -../../../../third_party/jinja2/environment.py -../../../../third_party/jinja2/exceptions.py -../../../../third_party/jinja2/filters.py -../../../../third_party/jinja2/idtracking.py -../../../../third_party/jinja2/lexer.py -../../../../third_party/jinja2/loaders.py -../../../../third_party/jinja2/nodes.py -../../../../third_party/jinja2/optimizer.py -../../../../third_party/jinja2/parser.py -../../../../third_party/jinja2/runtime.py -../../../../third_party/jinja2/tests.py -../../../../third_party/jinja2/utils.py -../../../../third_party/jinja2/visitor.py -../../../../third_party/markupsafe/__init__.py -../../../../third_party/markupsafe/_compat.py -../../../../third_party/markupsafe/_native.py -../../../gn_helpers.py -../util/__init__.py -../util/build_utils.py -../util/resource_utils.py -shrinker.py
diff --git a/build/android/gyp/resources_shrinker/shrinker.py b/build/android/gyp/unused_resources.py similarity index 70% rename from build/android/gyp/resources_shrinker/shrinker.py rename to build/android/gyp/unused_resources.py index 2800ce29..cdaf4cf5 100755 --- a/build/android/gyp/resources_shrinker/shrinker.py +++ b/build/android/gyp/unused_resources.py
@@ -24,16 +24,18 @@ parser.add_argument( '--dependencies-res-zips', required=True, + action='append', help='Resources zip archives to investigate for unused resources.') - parser.add_argument('--dex', + parser.add_argument('--dexes', + action='append', required=True, help='Path to dex file, or zip with dex files.') parser.add_argument( '--proguard-mapping', - required=True, help='Path to proguard mapping file for the optimized dex.') parser.add_argument('--r-text', required=True, help='Path to R.txt') - parser.add_argument('--android-manifest', + parser.add_argument('--android-manifests', + action='append', required=True, help='Path to AndroidManifest') parser.add_argument('--output-config', @@ -54,20 +56,31 @@ for dependency_res_zip in options.dependencies_res_zips: dep_subdirs += resource_utils.ExtractDeps([dependency_res_zip], temp_dir) - build_utils.CheckOutput([ - options.script, '--rtxts', options.r_text, '--manifests', - options.android_manifest, '--resourceDirs', ':'.join(dep_subdirs), - '--dex', options.dex, '--mapping', options.proguard_mapping, - '--outputConfig', options.output_config - ]) + cmd = [ + options.script, + '--rtxts', + options.r_text, + '--manifests', + ':'.join(options.android_manifests), + '--resourceDirs', + ':'.join(dep_subdirs), + '--dexes', + ':'.join(options.dexes), + '--outputConfig', + options.output_config, + ] + if options.proguard_mapping: + cmd += [ + '--mapping', + options.proguard_mapping, + ] + build_utils.CheckOutput(cmd) if options.depfile: - depfile_deps = options.dependencies_res_zips + [ - options.r_text, - options.android_manifest, - options.dex, - options.proguard_mapping, - ] + depfile_deps = (options.dependencies_res_zips + options.android_manifests + + options.dexes) + [options.r_text] + if options.proguard_mapping: + depfile_deps.append(options.proguard_mapping) build_utils.WriteDepfile(options.depfile, options.output_config, depfile_deps)
diff --git a/build/android/gyp/unused_resources.pydeps b/build/android/gyp/unused_resources.pydeps new file mode 100644 index 0000000..4753ec3 --- /dev/null +++ b/build/android/gyp/unused_resources.pydeps
@@ -0,0 +1,30 @@ +# Generated by running: +# build/print_python_deps.py --root build/android/gyp --output build/android/gyp/unused_resources.pydeps build/android/gyp/unused_resources.py +../../../third_party/jinja2/__init__.py +../../../third_party/jinja2/_compat.py +../../../third_party/jinja2/asyncfilters.py +../../../third_party/jinja2/asyncsupport.py +../../../third_party/jinja2/bccache.py +../../../third_party/jinja2/compiler.py +../../../third_party/jinja2/defaults.py +../../../third_party/jinja2/environment.py +../../../third_party/jinja2/exceptions.py +../../../third_party/jinja2/filters.py +../../../third_party/jinja2/idtracking.py +../../../third_party/jinja2/lexer.py +../../../third_party/jinja2/loaders.py +../../../third_party/jinja2/nodes.py +../../../third_party/jinja2/optimizer.py +../../../third_party/jinja2/parser.py +../../../third_party/jinja2/runtime.py +../../../third_party/jinja2/tests.py +../../../third_party/jinja2/utils.py +../../../third_party/jinja2/visitor.py +../../../third_party/markupsafe/__init__.py +../../../third_party/markupsafe/_compat.py +../../../third_party/markupsafe/_native.py +../../gn_helpers.py +unused_resources.py +util/__init__.py +util/build_utils.py +util/resource_utils.py
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py index 489dc607..752ab30 100755 --- a/build/android/gyp/write_build_config.py +++ b/build/android/gyp/write_build_config.py
@@ -966,7 +966,10 @@ parser.add_option('--resources-zip', help='Path to target\'s resources zip.') parser.add_option('--package-name', help='Java package name for these resources.') - parser.add_option('--android-manifest', help='Path to android manifest.') + parser.add_option('--android-manifest', + help='Path to the root android manifest.') + parser.add_option('--merged-android-manifest', + help='Path to the merged android manifest.') parser.add_option('--resource-dirs', action='append', default=[], help='GYP-list of resource dirs') parser.add_option( @@ -1347,6 +1350,9 @@ if options.android_manifest: deps_info['android_manifest'] = options.android_manifest + if options.merged_android_manifest: + deps_info['merged_android_manifest'] = options.merged_android_manifest + if options.bundled_srcjars: deps_info['bundled_srcjars'] = build_utils.ParseGnList( options.bundled_srcjars)
diff --git a/build/android/pylib/local/device/local_device_test_run.py b/build/android/pylib/local/device/local_device_test_run.py index 4381538..e8eb173 100644 --- a/build/android/pylib/local/device/local_device_test_run.py +++ b/build/android/pylib/local/device/local_device_test_run.py
@@ -315,7 +315,6 @@ last_partition_size = 0 for test in tests: test_count = len(test) if CountTestsIndividually(test) else 1 - num_not_yet_allocated -= test_count # Make a new shard whenever we would overfill the previous one. However, # if the size of the test group is larger than the max partition size on # its own, just put the group in its own shard instead of splitting up the @@ -323,9 +322,6 @@ if (last_partition_size + test_count > partition_size and last_partition_size > 0): num_desired_partitions -= 1 - partitions.append([]) - partitions[-1].append(test) - last_partition_size = test_count if num_desired_partitions <= 0: # Too many tests for number of partitions, just fill all partitions # beyond num_desired_partitions. @@ -334,10 +330,15 @@ # Re-balance remaining partitions. partition_size = min(num_not_yet_allocated // num_desired_partitions, max_partition_size) + partitions.append([]) + partitions[-1].append(test) + last_partition_size = test_count else: partitions[-1].append(test) last_partition_size += test_count + num_not_yet_allocated -= test_count + if not partitions[-1]: partitions.pop() return partitions
diff --git a/build/android/unused_resources/BUILD.gn b/build/android/unused_resources/BUILD.gn new file mode 100644 index 0000000..1596104 --- /dev/null +++ b/build/android/unused_resources/BUILD.gn
@@ -0,0 +1,19 @@ +# Copyright 2021 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("//build/config/android/rules.gni") + +java_binary("unused_resources") { + sources = [ "//build/android/unused_resources/UnusedResources.java" ] + main_class = "build.android.unused_resources.UnusedResources" + deps = [ + "//third_party/android_deps:com_android_tools_common_java", + "//third_party/android_deps:com_android_tools_layoutlib_layoutlib_api_java", + "//third_party/android_deps:com_android_tools_sdk_common_java", + "//third_party/android_deps:com_google_guava_guava_java", + "//third_party/android_deps:org_jetbrains_kotlin_kotlin_stdlib_java", + "//third_party/r8:r8_java", + ] + wrapper_script_name = "helper/unused_resources" +}
diff --git a/build/android/gyp/resources_shrinker/Shrinker.java b/build/android/unused_resources/UnusedResources.java similarity index 94% rename from build/android/gyp/resources_shrinker/Shrinker.java rename to build/android/unused_resources/UnusedResources.java index 50e2f93e..6334223 100644 --- a/build/android/gyp/resources_shrinker/Shrinker.java +++ b/build/android/unused_resources/UnusedResources.java
@@ -19,7 +19,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package build.android.gyp.resources_shrinker; +package build.android.unused_resources; import static com.android.ide.common.symbols.SymbolIo.readFromAapt; import static com.android.utils.SdkUtils.endsWithIgnoreCase; @@ -44,6 +44,7 @@ import com.google.common.collect.Maps; import com.google.common.io.ByteStreams; import com.google.common.io.Closeables; +import com.google.common.io.Files; import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -54,7 +55,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; @@ -77,7 +77,7 @@ - Reduce dependencies unless absolutely required. */ -public class Shrinker { +public class UnusedResources { private static final String ANDROID_RES = "android_res/"; private static final String DOT_DEX = ".dex"; private static final String DOT_CLASS = ".class"; @@ -97,9 +97,6 @@ private final StringWriter mDebugOutput; private final PrintWriter mDebugPrinter; - /** Easy way to invoke more verbose output for debugging */ - private boolean mDebug = false; - /** The computed set of unused resources */ private List<Resource> mUnused; @@ -136,8 +133,8 @@ } } - public Shrinker(Iterable<File> rTxtFiles, Iterable<File> classes, Iterable<File> manifests, - File mapping, Iterable<File> resources, File reportFile) { + public UnusedResources(Iterable<File> rTxtFiles, Iterable<File> classes, + Iterable<File> manifests, File mapping, Iterable<File> resources, File reportFile) { mRTxtFiles = rTxtFiles; mProguardMapping = mapping; mClasses = classes; @@ -213,13 +210,11 @@ throws IOException, SAXException, ParserConfigurationException { for (File resDir : resources) { File[] resourceFolders = resDir.listFiles(); - if (resourceFolders != null) { - for (File folder : resourceFolders) { - ResourceFolderType folderType = - ResourceFolderType.getFolderType(folder.getName()); - if (folderType != null) { - recordResources(folderType, folder); - } + assert resourceFolders != null : "Invalid resource directory " + resDir; + for (File folder : resourceFolders) { + ResourceFolderType folderType = ResourceFolderType.getFolderType(folder.getName()); + if (folderType != null) { + recordResources(folderType, folder); } } } @@ -392,7 +387,7 @@ @Override public void referencedInt(int value) { - Shrinker.this.referencedInt("dex", value, file, name); + UnusedResources.this.referencedInt("dex", value, file, name); } @Override @@ -502,8 +497,7 @@ private void referencedInt(String context, int value, File file, String currentClass) { Resource resource = mModel.getResource(value); - if (ResourceUsageModel.markReachable(resource) && mDebug) { - assert mDebugPrinter != null : "mDebug is true, but mDebugPrinter is null."; + if (ResourceUsageModel.markReachable(resource) && mDebugPrinter != null) { mDebugPrinter.println("Marking " + resource + " reachable: referenced from " + context + " in " + file + ":" + currentClass); } @@ -563,7 +557,7 @@ .map(s -> new File(s)) .collect(Collectors.toList()); break; - case "--dex": + case "--dexes": classes = Arrays.stream(args[i + 1].split(":")) .map(s -> new File(s)) .collect(Collectors.toList()); @@ -591,9 +585,10 @@ throw new IllegalArgumentException(args[i] + " is not a valid arg."); } } - Shrinker shrinker = new Shrinker(rTxtFiles, classes, manifests, mapping, resources, log); - shrinker.analyze(); - shrinker.close(); - shrinker.emitConfig(configPath); + UnusedResources unusedResources = + new UnusedResources(rTxtFiles, classes, manifests, mapping, resources, log); + unusedResources.analyze(); + unusedResources.close(); + unusedResources.emitConfig(configPath); } }
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 11eb48cd..c3b165d 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -360,6 +360,12 @@ args += [ "--treat-as-locale-paks" ] } + if (defined(invoker.merged_android_manifest)) { + args += [ + "--merged-android-manifest", + rebase_path(invoker.merged_android_manifest, root_build_dir), + ] + } if (defined(invoker.android_manifest)) { inputs += [ invoker.android_manifest ] args += [ @@ -2249,29 +2255,15 @@ # Use resource IDs provided by another APK target when compiling resources # (via. "aapt2 link --stable-ids") # - # short_resource_paths: (optional) - # Rename the paths within a the apk to be randomly generated short - # strings to reduce binary size. - # - # strip_resource_names: (optional) - # Strip resource names from the resources table of the apk. # # Output variables: # arsc_output: Path to output .ap_ file (optional). # # proto_output: Path to output .proto.ap_ file (optional). # - # optimized_arsc_output: Path to optimized .ap_ file (optional). - # - # optimized_proto_output: Path to optimized .proto.ap_ file (optional). - # # r_text_out_path: (optional): # Path for the corresponding generated R.txt file. # - # resources_path_map_out_path: (optional): - # Path for the generated map between original resource paths and - # shortend resource paths. - # # proguard_file: (optional) # Path to proguard configuration file for this apk target. # @@ -2301,9 +2293,6 @@ if (defined(invoker.arsc_output)) { _arsc_output = invoker.arsc_output } - if (defined(invoker.optimized_arsc_output)) { - _optimized_arsc_output = invoker.optimized_arsc_output - } _final_srcjar_path = "${target_gen_dir}/${target_name}.srcjar" _script = "//build/android/gyp/compile_resources.py" @@ -2371,36 +2360,6 @@ rebase_path(invoker.size_info_path, root_build_dir), ] } - if (defined(_optimized_arsc_output)) { - _outputs += [ _optimized_arsc_output ] - _args += [ - "--optimized-arsc-path", - rebase_path(_optimized_arsc_output, root_build_dir), - ] - } - if (defined(invoker.optimized_proto_output)) { - _outputs += [ invoker.optimized_proto_output ] - _args += [ - "--optimized-proto-path", - rebase_path(invoker.optimized_proto_output, root_build_dir), - ] - } - if (defined(invoker.resources_config_paths)) { - _inputs += invoker.resources_config_paths - _rebased_resource_configs = - rebase_path(invoker.resources_config_paths, root_build_dir) - _args += [ "--resources-config-paths=${_rebased_resource_configs}" ] - } - if (defined(invoker.short_resource_paths) && invoker.short_resource_paths) { - _args += [ "--short-resource-paths" ] - if (defined(invoker.resources_path_map_out_path)) { - _outputs += [ invoker.resources_path_map_out_path ] - _args += [ - "--resources-path-map-out-path", - rebase_path(invoker.resources_path_map_out_path, root_build_dir), - ] - } - } if (defined(invoker.r_java_root_package_name)) { _args += [ @@ -2409,10 +2368,6 @@ ] } - if (defined(invoker.strip_resource_names) && invoker.strip_resource_names) { - _args += [ "--strip-resource-names" ] - } - # Useful to have android:debuggable in the manifest even for Release # builds. Just omit it for officai if (debuggable_apks) { @@ -2639,35 +2594,117 @@ } } + # A template that is used to optimize compiled resources using aapt2 optimize. + # + # proto_input_path: + # Path to input compiled .proto.ap_ file. + # + # short_resource_paths: (optional) + # Rename the paths within a the apk to be randomly generated short + # strings to reduce binary size. + # + # strip_resource_names: (optional) + # Strip resource names from the resources table of the apk. + # + # resources_configs_paths: (optional) + # List of resource configs to use for optimization. + # + # optimized_proto_output: + # Path to output optimized .proto.ap_ file. + # + # resources_path_map_out_path: (optional): + # Path for the generated map between original resource paths and + # shortened resource paths. + template("optimize_resources") { + forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) + action_with_pydeps(target_name) { + forward_variables_from(invoker, [ "deps" ]) + script = "//build/android/gyp/optimize_resources.py" + outputs = [ invoker.optimized_proto_output ] + inputs = [ + android_sdk_tools_bundle_aapt2, + invoker.r_text_path, + invoker.proto_input_path, + ] + args = [ + "--aapt2-path", + rebase_path(android_sdk_tools_bundle_aapt2, root_build_dir), + "--r-text-in", + rebase_path(invoker.r_text_path, root_build_dir), + "--proto-path", + rebase_path(invoker.proto_input_path, root_build_dir), + "--optimized-proto-path", + rebase_path(invoker.optimized_proto_output, root_build_dir), + ] + + if (defined(invoker.resources_config_paths)) { + inputs += invoker.resources_config_paths + _rebased_resource_configs = + rebase_path(invoker.resources_config_paths, root_build_dir) + args += [ "--resources-config-paths=${_rebased_resource_configs}" ] + } + + if (defined(invoker.short_resource_paths) && + invoker.short_resource_paths) { + args += [ "--short-resource-paths" ] + if (defined(invoker.resources_path_map_out_path)) { + outputs += [ invoker.resources_path_map_out_path ] + args += [ + "--resources-path-map-out-path", + rebase_path(invoker.resources_path_map_out_path, root_build_dir), + ] + } + } + + if (defined(invoker.strip_resource_names) && + invoker.strip_resource_names) { + args += [ "--strip-resource-names" ] + } + } + } + + # A template that is used to find unused resources. template("unused_resources") { - _rebased_build_config = rebase_path(invoker.build_config, root_build_dir) - _shrinker_dep = "//build/android/gyp/resources_shrinker:resources_shrinker" - _shrinker_script = "$root_build_dir/bin/helper/resources_shrinker" action_with_pydeps(target_name) { forward_variables_from(invoker, TESTONLY_AND_VISIBILITY + [ "deps" ]) - script = "//build/android/gyp/resources_shrinker/shrinker.py" - inputs = [ - invoker.build_config, - invoker.proguard_mapping_path, - _shrinker_script, - ] + script = "//build/android/gyp/unused_resources.py" + depfile = "$target_gen_dir/${target_name}.d" + _unused_resources_script = "$root_build_dir/bin/helper/unused_resources" + inputs = [ _unused_resources_script ] outputs = [ invoker.output_config ] if (!defined(deps)) { deps = [] } - deps += [ _shrinker_dep ] + deps += [ "//build/android/unused_resources:unused_resources" ] + _rebased_module_build_config = + rebase_path(invoker.build_config, root_build_dir) args = [ "--script", - rebase_path(_shrinker_script, root_build_dir), - "--dependencies-res-zips=@FileArg($_rebased_build_config:deps_info:dependency_zips)", - "--proguard-mapping", - rebase_path(invoker.proguard_mapping_path, root_build_dir), - "--r-text=@FileArg($_rebased_build_config:deps_info:r_text_path)", - "--dex=@FileArg($_rebased_build_config:final_dex:path)", - "--android-manifest=@FileArg($_rebased_build_config:deps_info:android_manifest)", + rebase_path(_unused_resources_script, root_build_dir), "--output-config", rebase_path(invoker.output_config, root_build_dir), + "--r-text=@FileArg($_rebased_module_build_config:deps_info:r_text_path)", + "--dependencies-res-zips=@FileArg($_rebased_module_build_config:deps_info:dependency_zips)", + "--depfile", + rebase_path(depfile, root_build_dir), ] + + if (defined(invoker.proguard_mapping_path)) { + inputs += [ invoker.proguard_mapping_path ] + args += [ + "--proguard-mapping", + rebase_path(invoker.proguard_mapping_path, root_build_dir), + ] + } + + foreach(_build_config, invoker.all_module_build_configs) { + inputs += [ _build_config ] + _rebased_build_config = rebase_path(_build_config, root_build_dir) + args += [ + "--dexes=@FileArg($_rebased_build_config:final_dex:path)", + "--android-manifests=@FileArg($_rebased_build_config:deps_info:merged_android_manifest)", + ] + } } } @@ -3622,6 +3659,7 @@ [ "android_manifest", "android_manifest_dep", + "merged_android_manifest", "final_dex_path", "loadable_modules", "native_lib_placeholders",
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index d33240d..53bfe3a 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -2077,6 +2077,8 @@ # uncompress_dex: Store final .dex files uncompressed in the apk. # strip_resource_names: True if resource names should be stripped from the # resources.arsc file in the apk or module. + # strip_unused_resources: True if unused resources should be stripped from + # the apk or module. # short_resource_paths: True if resource paths should be shortened in the # apk or module. # resources_config_paths: List of paths to the aapt2 optimize config files @@ -2163,20 +2165,8 @@ if (!_is_bundle_module) { _final_apk_path = invoker.final_apk_path - _final_rtxt_path = "${_final_apk_path}.R.txt" } - _short_resource_paths = - defined(invoker.short_resource_paths) && invoker.short_resource_paths && - enable_arsc_obfuscation - _strip_resource_names = - defined(invoker.strip_resource_names) && invoker.strip_resource_names && - enable_arsc_obfuscation - _optimize_resources = _strip_resource_names || _short_resource_paths - - if (!_is_bundle_module && _short_resource_paths) { - _final_pathmap_path = "${_final_apk_path}.pathmap.txt" - } _res_size_info_path = "$target_out_dir/$target_name.ap_.info" if (!_is_bundle_module) { _final_apk_path_no_ext_list = @@ -2193,20 +2183,12 @@ if (_is_bundle_module) { # Path to the intermediate proto-format resources zip file. _proto_resources_path = "$target_out_dir/$target_name.proto.ap_" - if (_optimize_resources) { - _optimized_proto_resources_path = - "$target_out_dir/$target_name.optimized.proto.ap_" - } } else { # resource_sizes.py needs to be able to find the unpacked resources.arsc # file based on apk name to compute normatlized size. _resource_sizes_arsc_path = "$root_out_dir/arsc/" + rebase_path(_final_apk_path_no_ext, root_build_dir) + ".ap_" - if (_optimize_resources) { - _optimized_arsc_resources_path = - "$target_out_dir/$target_name.optimized.ap_" - } } if (defined(invoker.version_code)) { @@ -2449,11 +2431,6 @@ "${_shared_resources_allowlist_target}__compile_resources" } - if (_short_resource_paths) { - _resources_path_map_out_path = - "${target_gen_dir}/${_template_name}_resources_path_map.txt" - } - _compile_resources_target = "${_template_name}__compile_resources" _compile_resources_rtxt_out = "${target_gen_dir}/${_compile_resources_target}_R.txt" @@ -2479,13 +2456,10 @@ "resource_exclusion_exceptions", "resource_exclusion_regex", "resource_values_filter_rules", - "resources_config_paths", "shared_resources", "shared_resources_allowlist_locales", "uses_split", ]) - short_resource_paths = _short_resource_paths - strip_resource_names = _strip_resource_names android_manifest = _android_manifest android_manifest_dep = ":$_merge_manifest_target" version_code = _version_code @@ -2511,9 +2485,6 @@ if (_enable_main_dex_list) { proguard_file_main_dex = _generated_proguard_main_dex_config } - if (_short_resource_paths) { - resources_path_map_out_path = _resources_path_map_out_path - } build_config = _build_config build_config_dep = ":$_build_config_target" @@ -2550,9 +2521,6 @@ if (_is_bundle_module) { is_bundle_module = true proto_output = _proto_resources_path - if (_optimize_resources) { - optimized_proto_output = _optimized_proto_resources_path - } if (defined(invoker.base_module_target)) { include_resource = @@ -2560,8 +2528,6 @@ "/" + get_label_info(invoker.base_module_target, "name") + ".ap_" _link_against = invoker.base_module_target } - } else if (_optimize_resources) { - optimized_arsc_output = _optimized_arsc_resources_path } if (defined(_link_against)) { @@ -2584,6 +2550,55 @@ } _srcjar_deps += [ ":$_compile_resources_target" ] + # We don't ship apks anymore, only optimize bundle builds + if (_is_bundle_module) { + _short_resource_paths = + defined(invoker.short_resource_paths) && + invoker.short_resource_paths && enable_arsc_obfuscation + _strip_resource_names = + defined(invoker.strip_resource_names) && + invoker.strip_resource_names && enable_arsc_obfuscation + _strip_unused_resources = defined(invoker.strip_unused_resources) && + invoker.strip_unused_resources + _optimize_resources = _strip_resource_names || _short_resource_paths || + _strip_unused_resources + } + + if (_is_bundle_module && _optimize_resources) { + _optimized_proto_resources_path = + "$target_out_dir/$target_name.optimized.proto.ap_" + if (_short_resource_paths) { + _resources_path_map_out_path = + "${target_gen_dir}/${_template_name}_resources_path_map.txt" + } + _optimize_resources_target = "${_template_name}__optimize_resources" + optimize_resources(_optimize_resources_target) { + deps = _deps + [ ":$_compile_resources_target" ] + short_resource_paths = _short_resource_paths + strip_resource_names = _strip_resource_names + if (_short_resource_paths) { + resources_path_map_out_path = _resources_path_map_out_path + } + r_text_path = _compile_resources_rtxt_out + proto_input_path = _proto_resources_path + optimized_proto_output = _optimized_proto_resources_path + if (_strip_unused_resources) { + # These need to be kept in sync with the target names + output paths + # in the android_app_bundle template. + _unused_resources_target = "${_template_name}__unused_resources" + _unused_resources_config_path = + "$target_gen_dir/${_template_name}_unused_resources.config" + resources_config_paths = [ _unused_resources_config_path ] + deps += [ ":$_unused_resources_target" ] + } else { + resources_config_paths = [] + } + if (defined(invoker.resources_config_paths)) { + resources_config_paths += invoker.resources_config_paths + } + } + } + if (defined(_resource_sizes_arsc_path)) { _copy_arsc_target = "${_template_name}__copy_arsc" copy(_copy_arsc_target) { @@ -2596,36 +2611,6 @@ _final_deps += [ ":$_copy_arsc_target" ] } - if (!_is_bundle_module) { - # Output the R.txt file to a more easily discoverable location for - # archiving. This is necessary when stripping resource names so that we - # have an archive of resource names to ids for shipped apks (for - # debugging purposes). We copy the file rather than change the location - # of the original because other targets rely on the location of the R.txt - # file. - _copy_rtxt_target = "${_template_name}__copy_rtxt" - copy(_copy_rtxt_target) { - deps = [ ":$_compile_resources_target" ] - sources = [ _compile_resources_rtxt_out ] - outputs = [ _final_rtxt_path ] - } - _final_deps += [ ":$_copy_rtxt_target" ] - - if (_short_resource_paths) { - # Do the same for path map - _copy_pathmap_target = "${_template_name}__copy_pathmap" - copy(_copy_pathmap_target) { - deps = [ ":$_compile_resources_target" ] - sources = [ _resources_path_map_out_path ] - outputs = [ _final_pathmap_path ] - - # The monochrome_public_apk_checker test needs pathmap when run on swarming. - data = [ _final_pathmap_path ] - } - _final_deps += [ ":$_copy_pathmap_target" ] - } - } - _generate_native_libraries_java = (!_is_bundle_module || _is_base_module) && (_native_libs_deps != [] || _secondary_abi_native_libs_deps != []) && @@ -2788,6 +2773,7 @@ supports_android = true requires_android = true srcjar_deps = _srcjar_deps + merged_android_manifest = _android_manifest if (defined(_final_dex_path)) { final_dex_path = _final_dex_path } @@ -3055,6 +3041,9 @@ ":$_build_config_target", ":$_compile_resources_target", ] + _all_native_libs_deps + if (_optimize_resources) { + _final_deps += [ ":$_optimize_resources_target" ] + } if (defined(_final_dex_target_dep)) { not_needed([ "_final_dex_target_dep" ]) } @@ -3198,11 +3187,7 @@ deps += [ _final_dex_target_dep ] } - if (_optimize_resources) { - packaged_resources_path = _optimized_arsc_resources_path - } else { - packaged_resources_path = _arsc_resources_path - } + packaged_resources_path = _arsc_resources_path if (defined(_native_libs_filearg)) { native_libs_filearg = _native_libs_filearg @@ -3481,7 +3466,6 @@ "resource_exclusion_regex", "resource_ids_provider_dep", "resource_values_filter_rules", - "resources_config_paths", "require_native_mocks", "secondary_abi_loadable_modules", "secondary_abi_shared_libraries", @@ -3490,13 +3474,11 @@ "shared_resources", "shared_resources_allowlist_locales", "shared_resources_allowlist_target", - "short_resource_paths", "sources", "srcjar_deps", "static_library_dependent_targets", "static_library_provider", "static_library_synchronized_proguard", - "strip_resource_names", "target_sdk_version", "testonly", "uncompress_dex", @@ -3624,6 +3606,7 @@ "static_library_provider", "static_library_synchronized_proguard", "strip_resource_names", + "strip_unused_resources", "target_sdk_version", "testonly", "uncompress_shared_libraries", @@ -4732,6 +4715,7 @@ _all_create_module_targets = [] _all_module_zip_paths = [] _all_module_build_configs = [] + _all_module_unused_resources_deps = [] foreach(_module, _modules) { _module_target = _module.module_target _module_build_config = _module.build_config @@ -4750,7 +4734,6 @@ # this file *must* be named ${_module.name}.zip _create_module_target = "${_target_name}__${_module.name}__create" _module_zip_path = "$target_gen_dir/$target_name/${_module.name}.zip" - create_android_app_bundle_module(_create_module_target) { forward_variables_from(invoker, [ @@ -4801,6 +4784,28 @@ ] _all_module_zip_paths += [ _module_zip_path ] _all_module_build_configs += [ _module_build_config ] + _all_module_unused_resources_deps += [ + "${_module_target}__compile_resources", + _dex_target_for_module, + _module_build_config_target, + ] + } + if (defined(invoker.strip_unused_resources) && + invoker.strip_unused_resources) { + # Resources only live in the base module so we define the unused resources + # target only on the base module target. + _unused_resources_target = "${_base_target_name}__unused_resources" + _unused_resources_config = + "${_base_target_gen_dir}/${_base_target_name}_unused_resources.config" + unused_resources(_unused_resources_target) { + deps = _all_module_unused_resources_deps + all_module_build_configs = _all_module_build_configs + build_config = _base_module_build_config + if (_proguard_enabled) { + proguard_mapping_path = _proguard_mapping_path + } + output_config = _unused_resources_config + } } _all_rebased_module_zip_paths =
diff --git a/buildtools/reclient_cfgs/OWNERS b/buildtools/reclient_cfgs/OWNERS index ae14021..fcb299a2 100644 --- a/buildtools/reclient_cfgs/OWNERS +++ b/buildtools/reclient_cfgs/OWNERS
@@ -1,5 +1,3 @@ -abdelaal@google.com -msavigny@google.com tikuta@google.com ukai@google.com yyanagisawa@google.com
diff --git a/buildtools/reclient_cfgs/win-cross-experiments/rewrapper_windows.cfg b/buildtools/reclient_cfgs/win-cross-experiments/rewrapper_windows.cfg index 1448dfa..d2b0aa0d 100644 --- a/buildtools/reclient_cfgs/win-cross-experiments/rewrapper_windows.cfg +++ b/buildtools/reclient_cfgs/win-cross-experiments/rewrapper_windows.cfg
@@ -1,4 +1,4 @@ -platform=container-image=docker://gcr.io/goma-foundry-experiments/re-client/chromium-win-cross@sha256:f18b6081b813cb64beab5c6d9b6f5608daffbc4abde6137a5a2185779da6332d,OSFamily=Linux +platform=container-image=docker://gcr.io/goma-foundry-experiments/re-client/chromium-win-cross@sha256:3a61bf4e67593e6b4f676dc0949fda581129e12309b36ca66148e8d31137c5fe,OSFamily=Linux server_address=pipe://reproxy.pipe labels=type=compile,compiler=clang-cl,lang=cpp exec_strategy=remote_local_fallback
diff --git a/chrome/VERSION b/chrome/VERSION index 2f09810..23e580a62 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=93 MINOR=0 -BUILD=4535 +BUILD=4536 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 1aad36e..ce3694f 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -2199,6 +2199,7 @@ "$google_play_services_package:google_play_services_gcm_java", "//base:base_java", "//base:base_java_test_support", + "//base:jni_java", "//chrome/browser/profiles/android:java", "//chrome/browser/tab:java", "//components/offline_items_collection/core:core_java", @@ -2211,6 +2212,8 @@ "//url:gurl_android_test_helper_java", "//url:gurl_java", ] + + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] } static_library("browser_test_support") {
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni index c485c30a..c5089dd 100644 --- a/chrome/android/chrome_public_apk_tmpl.gni +++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -179,10 +179,23 @@ png_to_webp = !is_java_debug } - # Removes metadata needed for Resources.getIdentifier("resource_name"). - strip_resource_names = !is_java_debug + # We only optimize resources for bundles since APKs are not shipped. + # Resources only live in the base module atm as such we only need to set + # these on the base module + if (_is_bundle) { + # Removes metadata needed for Resources.getIdentifier("resource_name"). + strip_resource_names = !is_java_debug - short_resource_paths = true + # Shortens resource file names in apk eg: res/drawable/button.xml -> res/a.xml + short_resource_paths = true + + # Removes unused resources from the apk. Only enabled on official builds + # since it adds a slow step and serializes the build graph causing fewer + # expensive tasks (eg: proguarding, resource optimization) to be run in + # parallel by adding dependencies between them (adds around 10-20 + # seconds on my machine). + strip_unused_resources = is_official_build + } if (!defined(aapt_locale_allowlist)) { # Include resource strings files only for supported locales. @@ -539,13 +552,16 @@ } } - # Resources config for blocklisting resource names from obfuscation - resources_config_paths = [ - "//android_webview/aapt2.config", - "//chrome/android/aapt2.config", - ] - if (defined(invoker.resources_config_paths)) { - resources_config_paths += invoker.resources_config_paths + # We only optimize resources in bundles. + if (_is_bundle_module) { + # Resources config for blocklisting resource names from obfuscation + resources_config_paths = [ + "//android_webview/aapt2.config", + "//chrome/android/aapt2.config", + ] + if (defined(invoker.resources_config_paths)) { + resources_config_paths += invoker.resources_config_paths + } } if (defined(invoker.never_incremental)) {
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni index 7297b6f..7730409 100644 --- a/chrome/android/chrome_test_java_sources.gni +++ b/chrome/android/chrome_test_java_sources.gni
@@ -507,6 +507,7 @@ "javatests/src/org/chromium/chrome/browser/share/ShareUrlTest.java", "javatests/src/org/chromium/chrome/browser/signin/IdentityManagerIntegrationTest.java", "javatests/src/org/chromium/chrome/browser/signin/SigninCheckerTest.java", + "javatests/src/org/chromium/chrome/browser/signin/SigninHeaderTest.java", "javatests/src/org/chromium/chrome/browser/signin/SigninSignoutIntegrationTest.java", "javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java", "javatests/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegateTest.java",
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java index 47909bf2..0c82b3a 100644 --- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java +++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java
@@ -20,6 +20,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast; import static androidx.test.espresso.matcher.ViewMatchers.isEnabled; import static androidx.test.espresso.matcher.ViewMatchers.withChild; import static androidx.test.espresso.matcher.ViewMatchers.withClassName; @@ -497,7 +498,7 @@ ArrayList<ActionProto> list = new ArrayList<>(); list.add( - (ActionProto) ActionProto.newBuilder() + ActionProto.newBuilder() .setCollectUserData( CollectUserDataProto.newBuilder() .setContactDetails(ContactDetailsProto.newBuilder() @@ -508,19 +509,19 @@ .setRequestTermsAndConditions(false)) .build()); list.add( - (ActionProto) ActionProto.newBuilder() + ActionProto.newBuilder() .setUseAddress( UseAddressProto.newBuilder().setName("contact").setFormFieldElement( SelectorProto.newBuilder().addFilters( SelectorProto.Filter.newBuilder().setCssSelector( "#profile_name")))) .build()); - list.add((ActionProto) ActionProto.newBuilder() + list.add(ActionProto.newBuilder() .setPrompt(PromptProto.newBuilder().setMessage("Prompt").addChoices( PromptProto.Choice.newBuilder())) .build()); AutofillAssistantTestScript script = new AutofillAssistantTestScript( - (SupportedScriptProto) SupportedScriptProto.newBuilder() + SupportedScriptProto.newBuilder() .setPath("form_target_website.html") .setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip( ChipProto.newBuilder().setText("Address"))) @@ -533,13 +534,17 @@ onView(withText("Continue")).check(matches(isEnabled())); // Select John Doe. onView(withText("Contact info")).perform(click()); - waitUntilViewMatchesCondition(withText(containsString("John Doe")), isDisplayed()); + waitUntilViewMatchesCondition( + withText(containsString("John Doe")), isDisplayingAtLeast(90)); onView(withText(containsString("John Doe"))).perform(click()); waitUntilViewMatchesCondition( withId(R.id.contact_summary), allOf(withText("johndoe@google.com"), isDisplayed())); // Edit John Doe to Jane Doe (does not collapse the list after editing). onView(withText("Contact info")).perform(click()); - waitUntilViewMatchesCondition(withText(containsString("John Doe")), isDisplayed()); + waitUntilViewMatchesCondition( + allOf(withContentDescription("Edit contact info"), + isNextAfterSibling(hasDescendant(withText(containsString("John Doe"))))), + isDisplayingAtLeast(90)); onView(allOf(withContentDescription("Edit contact info"), isNextAfterSibling(hasDescendant(withText(containsString("John Doe")))))) .perform(click());
diff --git a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_az.xtb b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_az.xtb index 3e523ab..1d643b8 100644 --- a/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_az.xtb +++ b/chrome/android/features/tab_ui/java/strings/translations/android_chrome_tab_ui_strings_az.xtb
@@ -31,7 +31,7 @@ <translation id="3996880007329611795">Bildiriş alın</translation> <translation id="4133493477912226187"><ph name="NUMBER_OF_TABS" /> tab əlaqəli görünür. Onlar qruplaşdırılsın?</translation> <translation id="4212246570487010370">Axtarmağa davam edin</translation> -<translation id="4450893287417543264">Daha göstərməyin</translation> +<translation id="4450893287417543264">Göstərilməsin</translation> <translation id="4648718555153979859">Tablar burada qruplaşdırılır</translation> <translation id="4788280460033928884">{REVIEWS,plural, =1{(<ph name="REVIEWS_COUNT_ONE" /> rəy)}other{(<ph name="REVIEWS_COUNT_MANY" /> rəy)}}</translation> <translation id="4850886885716139402">Görünüş</translation>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java b/chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java index ac452ac..07b792d8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java
@@ -22,6 +22,8 @@ import org.chromium.content_public.browser.UiThreadTaskTraits; import org.chromium.ui.base.WindowAndroid; +import java.util.List; + /** * This class serves as a simple interface for querying the child account information. It has a * method for querying the child account information asynchronously from the system. @@ -52,21 +54,20 @@ * It should be safe to invoke this method before the native library is initialized (after * AccountManagerFacade is set). * + * @param accounts The list of accounts on device. * @param listener The listener is called when the {@link ChildAccountStatus.Status} is ready. */ @MainThread - public static void checkChildAccountStatus(ChildAccountStatusListener listener) { + public static void checkChildAccountStatus( + List<Account> accounts, ChildAccountStatusListener listener) { ThreadUtils.assertOnUiThread(); - final AccountManagerFacade accountManagerFacade = - AccountManagerFacadeProvider.getInstance(); - accountManagerFacade.tryGetGoogleAccounts(accounts -> { - if (accounts.size() == 1) { - // Child accounts can't share a device. - accountManagerFacade.checkChildAccountStatus(accounts.get(0), listener); - } else { - listener.onStatusReady(ChildAccountStatus.NOT_CHILD); - } - }); + if (accounts.size() == 1) { + // Child accounts can't share a device. + AccountManagerFacadeProvider.getInstance().checkChildAccountStatus( + accounts.get(0), listener); + } else { + listener.onStatusReady(ChildAccountStatus.NOT_CHILD); + } } @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java index d37ae71..6fd29fa 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
@@ -71,11 +71,13 @@ */ public void start() { long childAccountStatusStart = SystemClock.elapsedRealtime(); - ChildAccountService.checkChildAccountStatus(status -> { - RecordHistogram.recordTimesHistogram("MobileFre.ChildAccountStatusDuration", - SystemClock.elapsedRealtime() - childAccountStatusStart); - initializeSharedState(status); - processFreEnvironmentPreNative(); + AccountManagerFacadeProvider.getInstance().tryGetGoogleAccounts(accounts -> { + ChildAccountService.checkChildAccountStatus(accounts, status -> { + RecordHistogram.recordTimesHistogram("MobileFre.ChildAccountStatusDuration", + SystemClock.elapsedRealtime() - childAccountStatusStart); + initializeSharedState(status, accounts); + processFreEnvironmentPreNative(); + }); }); } @@ -100,11 +102,6 @@ } @VisibleForTesting - protected List<Account> getGoogleAccounts() { - return AccountManagerFacadeProvider.getInstance().tryGetGoogleAccounts(); - } - - @VisibleForTesting protected boolean shouldSkipFirstUseHints() { return Settings.Secure.getInt( mActivity.getContentResolver(), Settings.Secure.SKIP_FIRST_USE_HINTS, 0) @@ -134,12 +131,11 @@ FirstRunSignInProcessor.setFirstRunFlowSignInComplete(true); } - void initializeSharedState(@ChildAccountStatus.Status int childAccountStatus) { + @VisibleForTesting + void initializeSharedState( + @ChildAccountStatus.Status int childAccountStatus, List<Account> accounts) { mChildAccountStatus = childAccountStatus; - // Note that fetching accounts isn't instrumented because it's typically very fast. It seems - // fetching child account status causes accounts to be cached. Revisit this if the ordering - // of these calls is changed. - mGoogleAccounts = getGoogleAccounts(); + mGoogleAccounts = accounts; } void processFreEnvironmentPreNative() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java index adab00c..25730758 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java
@@ -275,31 +275,6 @@ } /** - * Starts the sign-in flow, enables sync and executes the callback when finished. - * - * The sign-in flow goes through the following steps: - * - * - Wait for AccountTrackerService to be seeded. - * - Wait for policy to be checked for the account. - * - If managed, wait for the policy to be fetched. - * - Complete sign-in with the native IdentityManager. - * - Enable sync. - * - Call the callback if provided. - * - * @param accessPoint {@link SigninAccessPoint} that initiated the sign-in flow. - * @param accountInfo The account to sign in to. - * @param callback Optional callback for when the sign-in process is finished. - */ - @Override - public void signinAndEnableSync(@SigninAccessPoint int accessPoint, CoreAccountInfo accountInfo, - @Nullable SignInCallback callback) { - mAccountTrackerService.seedAccountsIfNeeded(() -> { - signinInternal( - SignInState.createForSigninAndEnableSync(accessPoint, accountInfo, callback)); - }); - } - - /** * Starts the sign-in flow, and executes the callback when finished. * * The sign-in flow goes through the following steps:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java index a945bd1..c510b0a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
@@ -213,23 +213,6 @@ } /** - * DEPRECATED: Use getChosenDataTypes() instead. - * - * Gets the set of data types that are "preferred" in sync. Those are the - * chosen ones (see getChosenDataTypes), plus any that are implied by them. - * - * NOTE: This returns "all types" by default, even if the user has never - * enabled Sync, or if only Sync-the-transport is running. - * - * @return Set of preferred data types. - */ - public Set<Integer> getPreferredDataTypes() { - int[] modelTypeArray = - ProfileSyncServiceJni.get().getPreferredDataTypes(mNativeProfileSyncServiceAndroid); - return modelTypeArrayToSet(modelTypeArray); - } - - /** * Gets the set of data types that are currently syncing. * * This is affected by whether sync is on. @@ -555,7 +538,7 @@ } @VisibleForTesting - public long getNativeProfileSyncServiceForTest() { + public long getNativeSyncServiceImplForTest() { return ProfileSyncServiceJni.get().getSyncServiceImplForTest( mNativeProfileSyncServiceAndroid); } @@ -633,55 +616,55 @@ interface Natives { long init(ProfileSyncService caller); + // Please keep all methods below in the same order as profile_sync_service_android.h. + boolean isSyncRequested(long nativeProfileSyncServiceAndroid); void setSyncRequested(long nativeProfileSyncServiceAndroid, boolean requested); + boolean canSyncFeatureStart(long nativeProfileSyncServiceAndroid); boolean isSyncAllowedByPlatform(long nativeProfileSyncServiceAndroid); void setSyncAllowedByPlatform(long nativeProfileSyncServiceAndroid, boolean allowed); - int getAuthError(long nativeProfileSyncServiceAndroid); - boolean requiresClientUpgrade(long nativeProfileSyncServiceAndroid); - void setDecoupledFromAndroidMasterSync(long nativeProfileSyncServiceAndroid); - boolean getDecoupledFromAndroidMasterSync(long nativeProfileSyncServiceAndroid); - @Nullable - CoreAccountInfo getAuthenticatedAccountInfo(long nativeProfileSyncServiceAndroid); - boolean isAuthenticatedAccountPrimary(long nativeProfileSyncServiceAndroid); + boolean isSyncFeatureActive(long nativeProfileSyncServiceAndroid); + boolean isSyncDisabledByEnterprisePolicy(long nativeProfileSyncServiceAndroid); boolean isEngineInitialized(long nativeProfileSyncServiceAndroid); + boolean isTransportStateActive(long nativeProfileSyncServiceAndroid); + void setSetupInProgress(long nativeProfileSyncServiceAndroid, boolean inProgress); + boolean isFirstSetupComplete(long nativeProfileSyncServiceAndroid); + void setFirstSetupComplete( + long nativeProfileSyncServiceAndroid, int syncFirstSetupCompleteSource); + int[] getActiveDataTypes(long nativeProfileSyncServiceAndroid); + int[] getChosenDataTypes(long nativeProfileSyncServiceAndroid); + void setChosenDataTypes( + long nativeProfileSyncServiceAndroid, boolean syncEverything, int[] modelTypeArray); boolean isCustomPassphraseAllowed(long nativeProfileSyncServiceAndroid); boolean isEncryptEverythingEnabled(long nativeProfileSyncServiceAndroid); - boolean isTransportStateActive(long nativeProfileSyncServiceAndroid); boolean isPassphraseRequiredForPreferredDataTypes(long nativeProfileSyncServiceAndroid); boolean isTrustedVaultKeyRequired(long nativeProfileSyncServiceAndroid); boolean isTrustedVaultKeyRequiredForPreferredDataTypes( long nativeProfileSyncServiceAndroid); boolean isTrustedVaultRecoverabilityDegraded(long nativeProfileSyncServiceAndroid); boolean isUsingExplicitPassphrase(long nativeProfileSyncServiceAndroid); - boolean setDecryptionPassphrase(long nativeProfileSyncServiceAndroid, String passphrase); - void setEncryptionPassphrase(long nativeProfileSyncServiceAndroid, String passphrase); int getPassphraseType(long nativeProfileSyncServiceAndroid); + void setEncryptionPassphrase(long nativeProfileSyncServiceAndroid, String passphrase); + boolean setDecryptionPassphrase(long nativeProfileSyncServiceAndroid, String passphrase); long getExplicitPassphraseTime(long nativeProfileSyncServiceAndroid); - int[] getActiveDataTypes(long nativeProfileSyncServiceAndroid); - int[] getChosenDataTypes(long nativeProfileSyncServiceAndroid); - int[] getPreferredDataTypes(long nativeProfileSyncServiceAndroid); - void setChosenDataTypes( - long nativeProfileSyncServiceAndroid, boolean syncEverything, int[] modelTypeArray); - void triggerRefresh(long nativeProfileSyncServiceAndroid); - void setSetupInProgress(long nativeProfileSyncServiceAndroid, boolean inProgress); - void setFirstSetupComplete( - long nativeProfileSyncServiceAndroid, int syncFirstSetupCompleteSource); - boolean isFirstSetupComplete(long nativeProfileSyncServiceAndroid); - boolean isSyncRequested(long nativeProfileSyncServiceAndroid); - boolean canSyncFeatureStart(long nativeProfileSyncServiceAndroid); - boolean isSyncFeatureActive(long nativeProfileSyncServiceAndroid); - boolean isSyncDisabledByEnterprisePolicy(long nativeProfileSyncServiceAndroid); - boolean hasKeepEverythingSynced(long nativeProfileSyncServiceAndroid); + void getAllNodes(long nativeProfileSyncServiceAndroid, GetAllNodesCallback callback); + int getAuthError(long nativeProfileSyncServiceAndroid); boolean hasUnrecoverableError(long nativeProfileSyncServiceAndroid); + boolean requiresClientUpgrade(long nativeProfileSyncServiceAndroid); + void setDecoupledFromAndroidMasterSync(long nativeProfileSyncServiceAndroid); + boolean getDecoupledFromAndroidMasterSync(long nativeProfileSyncServiceAndroid); + @Nullable + CoreAccountInfo getAuthenticatedAccountInfo(long nativeProfileSyncServiceAndroid); + boolean isAuthenticatedAccountPrimary(long nativeProfileSyncServiceAndroid); boolean isPassphrasePromptMutedForCurrentProductVersion( long nativeProfileSyncServiceAndroid); void markPassphrasePromptMutedForCurrentProductVersion( long nativeProfileSyncServiceAndroid); - long getSyncServiceImplForTest(long nativeProfileSyncServiceAndroid); - long getLastSyncedTimeForTest(long nativeProfileSyncServiceAndroid); - void getAllNodes(long nativeProfileSyncServiceAndroid, GetAllNodesCallback callback); + boolean hasKeepEverythingSynced(long nativeProfileSyncServiceAndroid); void recordKeyRetrievalTrigger( long nativeProfileSyncServiceAndroid, int keyRetrievalTrigger); boolean shouldOfferTrustedVaultOptIn(long nativeProfileSyncServiceAndroid); + void triggerRefresh(long nativeProfileSyncServiceAndroid); + long getSyncServiceImplForTest(long nativeProfileSyncServiceAndroid); + long getLastSyncedTimeForTest(long nativeProfileSyncServiceAndroid); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java index bf3bdab..6b48adfa 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java
@@ -59,6 +59,7 @@ import org.chromium.components.browser_ui.settings.SettingsUtils; import org.chromium.components.prefs.PrefService; import org.chromium.components.signin.AccountManagerFacadeProvider; +import org.chromium.components.signin.AccountUtils; import org.chromium.components.signin.base.CoreAccountInfo; import org.chromium.components.signin.identitymanager.ConsentLevel; import org.chromium.components.signin.metrics.SigninAccessPoint; @@ -491,9 +492,9 @@ } if (mCurrentSyncError == SyncError.OTHER_ERRORS) { - final CoreAccountInfo account = IdentityServicesProvider.get() - .getIdentityManager(getProfile()) - .getPrimaryAccountInfo(ConsentLevel.SYNC); + final CoreAccountInfo accountInfo = IdentityServicesProvider.get() + .getIdentityManager(getProfile()) + .getPrimaryAccountInfo(ConsentLevel.SYNC); // TODO(https://crbug.com/873116): Pass the correct reason for the signout. IdentityServicesProvider.get() .getSigninManager(getProfile()) @@ -502,7 +503,9 @@ -> IdentityServicesProvider.get() .getSigninManager(getProfile()) .signinAndEnableSync( - SigninAccessPoint.SYNC_ERROR_CARD, account, + SigninAccessPoint.SYNC_ERROR_CARD, + AccountUtils.createAccountFromName( + accountInfo.getEmail()), null), false); return;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java index a926835..4802868 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
@@ -259,12 +259,15 @@ @Test @SmallTest public void testSyncRowSummaryWhenNoDataTypeSynced() { + final ProfileSyncService profileSyncService = + TestThreadUtils.runOnUiThreadBlockingNoException(ProfileSyncService::get); TestThreadUtils.runOnUiThreadBlocking( - () -> { ProfileSyncService.get().setChosenDataTypes(false, new HashSet<>()); }); + () -> { profileSyncService.setChosenDataTypes(false, new HashSet<>()); }); CoreAccountInfo account = mSyncTestRule.addTestAccount(); - TestThreadUtils.runOnUiThreadBlocking( - () -> { SigninTestUtil.signinAndEnableSync(account, ProfileSyncService.get()); }); + SigninTestUtil.signinAndEnableSync(account, profileSyncService); + launchSettingsActivity(); + onView(withText(R.string.sync_data_types_off)).check(matches(isDisplayed())); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninHeaderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninHeaderTest.java new file mode 100644 index 0000000..1eb8fa7 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninHeaderTest.java
@@ -0,0 +1,119 @@ +// Copyright 2021 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. + +package org.chromium.chrome.browser.signin; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +import static org.chromium.chrome.browser.browserservices.TrustedWebActivityTestUtil.createSession; +import static org.chromium.chrome.browser.browserservices.TrustedWebActivityTestUtil.createTrustedWebActivityIntent; +import static org.chromium.chrome.browser.browserservices.TrustedWebActivityTestUtil.spoofVerification; + +import android.content.Intent; + +import androidx.test.filters.MediumTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.CommandLine; +import org.chromium.base.ContextUtils; +import org.chromium.base.library_loader.LibraryLoader; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule; +import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule; +import org.chromium.content_public.browser.test.util.JavaScriptUtils; +import org.chromium.net.test.EmbeddedTestServerRule; + +import java.util.concurrent.TimeoutException; + +/** + * Instrumentation tests for HTTP headers sent to GAIA server when user is signed in. + */ +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class SigninHeaderTest { + private static final String PACKAGE_NAME = + ContextUtils.getApplicationContext().getPackageName(); + + @Rule + public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule(); + + @Rule + public ChromeTabbedActivityTestRule mChromeActivityTestRule = + new ChromeTabbedActivityTestRule(); + + @Rule + public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule(); + + @Rule + public EmbeddedTestServerRule mEmbeddedTestServerRule = new EmbeddedTestServerRule(); + + private String mGAIAUrl; + + private void launchTrustedWebActivity(Intent intent) throws TimeoutException { + String url = intent.getData().toString(); + spoofVerification(PACKAGE_NAME, url); + createSession(intent, PACKAGE_NAME); + mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent); + } + + @Before + public void setUp() { + // Native needs to be initialized to start the test server. + LibraryLoader.getInstance().ensureInitialized(); + mEmbeddedTestServerRule.setServerUsesHttps(true); + // Specify a Gaia url path. + CommandLine.getInstance().appendSwitchWithValue( + "gaia-url", mEmbeddedTestServerRule.getServer().getURL("/")); + mChromeActivityTestRule.startMainActivityOnBlankPage(); + mAccountManagerTestRule.addTestAccountThenSignin(); + + mGAIAUrl = mEmbeddedTestServerRule.getServer().getURL("/echoheader?X-Chrome-Connected"); + } + + @Test + @MediumTest + public void testXChromeConnectedHeader_In_TWA_ReturnsModeValueWithIncognitoOff() + throws TimeoutException { + Intent intent = createTrustedWebActivityIntent(mGAIAUrl); + launchTrustedWebActivity(intent); + Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab(); + String output = JavaScriptUtils.executeJavaScriptAndWaitForResult( + tab.getWebContents(), "document.body.innerText"); + assertThat(output, containsString("mode=1,enable_account_consistency=true")); + } + + @Test + @MediumTest + public void testXChromeConnectedHeader_In_CCT_ReturnsModeValueWithIncognitoOff() + throws TimeoutException { + Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent( + ContextUtils.getApplicationContext(), mGAIAUrl); + mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent); + Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab(); + String output = JavaScriptUtils.executeJavaScriptAndWaitForResult( + tab.getWebContents(), "document.body.innerText"); + assertThat(output, containsString("mode=1,enable_account_consistency=true")); + } + + @Test + @MediumTest + public void testXChromeConnectedHeader_InNonCCT_ReturnsModeWithIncognitoOn() + throws TimeoutException { + mChromeActivityTestRule.loadUrl(mGAIAUrl); + Tab tab = mChromeActivityTestRule.getActivity().getActivityTab(); + String output = JavaScriptUtils.executeJavaScriptAndWaitForResult( + tab.getWebContents(), "document.body.innerText"); + assertThat(output, containsString("mode=0,enable_account_consistency=true")); + } +} \ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java index 12f7523..1d860611 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java
@@ -8,6 +8,8 @@ import androidx.test.filters.LargeTest; +import com.google.protobuf.InvalidProtocolBufferException; + import org.hamcrest.Matchers; import org.json.JSONException; import org.json.JSONObject; @@ -36,7 +38,6 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; /** * Test suite for the bookmarks sync data type. @@ -477,7 +478,7 @@ entity.getFolder() ? null : specifics.getUrl(), parentId)); } return bookmarks; - } catch (ExecutionException ex) { + } catch (InvalidProtocolBufferException ex) { Assert.fail(ex.toString()); return null; }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java index 815f9e0..f8644e7b 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
@@ -10,9 +10,6 @@ import org.chromium.components.signin.base.GoogleServiceAuthError; import org.chromium.content_public.browser.test.util.TestThreadUtils; -import java.util.HashSet; -import java.util.Set; - /** * Fake some ProfileSyncService methods for testing. * @@ -26,7 +23,6 @@ private boolean mTrustedVaultRecoverabilityDegraded; private boolean mEncryptEverythingEnabled; private boolean mRequiresClientUpgrade; - private Set<Integer> mChosenTypes = new HashSet<>(); private boolean mCanSyncFeatureStart; @GoogleServiceAuthError.State private int mAuthError; @@ -69,16 +65,6 @@ } @Override - public void setChosenDataTypes(boolean syncEverything, Set<Integer> enabledTypes) { - mChosenTypes = enabledTypes; - } - - @Override - public Set<Integer> getPreferredDataTypes() { - return mChosenTypes; - } - - @Override public boolean isPassphraseRequiredForPreferredDataTypes() { ThreadUtils.assertOnUiThread(); return mPassphraseRequiredForPreferredDataTypes;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java index 8545cc77..8c18bac 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java
@@ -4,11 +4,13 @@ package org.chromium.chrome.browser.sync; -import android.content.Context; +import androidx.annotation.Nullable; import com.google.protobuf.InvalidProtocolBufferException; +import org.chromium.base.Log; import org.chromium.base.ThreadUtils; +import org.chromium.base.annotations.NativeMethods; import org.chromium.components.sync.protocol.EntitySpecifics; import org.chromium.components.sync.protocol.SyncEntity; import org.chromium.content_public.browser.test.util.TestThreadUtils; @@ -16,105 +18,58 @@ import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; /** - * Assists in Java interaction the native Sync FakeServer. + * Assists in Java interaction the native Sync FakeServer. Can be used from any thread. */ public class FakeServerHelper { private static final String TAG = "FakeServerHelper"; - // Lazily-instantiated singleton FakeServerHelper. + // Singleton instance. Set on every createInstanceAndGet() and reset to null on every destroy(). + // This must be assigned on the UI thread. private static FakeServerHelper sFakeServerHelper; - // Pointer value for the FakeServer. This pointer is not owned by native - // code, so it must be stored here for future deletion. - private static long sNativeFakeServer; + // Must be used from the UI thread. + private final long mNativeFakeServer; - // The pointer to the native object called here. - private final long mNativeFakeServerHelperAndroid; + /** + * Creates the singleton FakeServerHelper and returns it. destroyInstance() must be called + * when done to prevent the native object from leaking. If this is called before the previous + * instance is destroyed, it will return null (just to avoid throwing ExecutionException). + * TODO(crbug.com/949504): When refactoring this method, throw an exception instead of returning + * null. + */ + public static @Nullable FakeServerHelper createInstanceAndGet() { + return TestThreadUtils.runOnUiThreadBlockingNoException(() -> { + if (sFakeServerHelper == null) { + sFakeServerHelper = new FakeServerHelper(); + return sFakeServerHelper; + } - // Accesses the singleton FakeServerHelper. There is at most one instance created per - // application lifetime, so no deletion mechanism is provided for the native object. - public static FakeServerHelper get() { - ThreadUtils.assertOnUiThread(); - if (sFakeServerHelper == null) { - sFakeServerHelper = new FakeServerHelper(); - } - return sFakeServerHelper; + Log.w(TAG, + "destroyInstance() must be called before another FakeServerHelper is created"); + return null; + }); + } + + /** + * Deletes the existing FakeServer if any. + */ + public static void destroyInstance() { + TestThreadUtils.runOnUiThreadBlocking(() -> { + if (sFakeServerHelper == null) return; + + FakeServerHelperJni.get().deleteFakeServer(sFakeServerHelper.mNativeFakeServer, + ProfileSyncService.get().getNativeSyncServiceImplForTest()); + sFakeServerHelper = null; + }); } private FakeServerHelper() { - mNativeFakeServerHelperAndroid = nativeInit(); - } - - /** - * Creates and configures FakeServer. - * - * Each call to this method should be accompanied by a later call to deleteFakeServer to avoid - * a memory leak. - */ - public static void useFakeServer(final Context context) { - if (sNativeFakeServer != 0L) { - throw new IllegalStateException( - "deleteFakeServer must be called before calling useFakeServer again."); - } - - sNativeFakeServer = TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Long>() { - @Override - public Long call() { - FakeServerHelper fakeServerHelper = FakeServerHelper.get(); - return fakeServerHelper.createFakeServer(); - } - }); - } - - /** - * Deletes the existing FakeServer. - */ - public static void deleteFakeServer() { - checkFakeServerInitialized("useFakeServer must be called before calling deleteFakeServer."); - TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Void>() { - @Override - public Void call() { - FakeServerHelper.get().deleteFakeServer(sNativeFakeServer); - sNativeFakeServer = 0L; - return null; - } - }); - } - - /** - * Creates a native FakeServer object and returns its pointer. This pointer is owned by the - * Java caller. - * - * @return the FakeServer pointer - */ - public long createFakeServer() { - return TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Long>() { - @Override - public Long call() { - return nativeCreateFakeServer(mNativeFakeServerHelperAndroid, - ProfileSyncService.get().getNativeProfileSyncServiceForTest()); - } - }); - } - - /** - * Deletes a native FakeServer. - * - * @param nativeFakeServer the pointer to be deleted - */ - public void deleteFakeServer(final long nativeFakeServer) { - TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Void>() { - @Override - public Void call() { - nativeDeleteFakeServer(mNativeFakeServerHelperAndroid, nativeFakeServer, - ProfileSyncService.get().getNativeProfileSyncServiceForTest()); - return null; - } - }); + ThreadUtils.assertOnUiThread(); + mNativeFakeServer = FakeServerHelperJni.get().createFakeServer( + ProfileSyncService.get().getNativeSyncServiceImplForTest()); + assert mNativeFakeServer != 0L; } /** @@ -129,14 +84,10 @@ */ public boolean verifyEntityCountByTypeAndName( final int count, final int modelType, final String name) { - checkFakeServerInitialized("useFakeServer must be called before data verification."); - return TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() { - @Override - public Boolean call() { - return nativeVerifyEntityCountByTypeAndName( - mNativeFakeServerHelperAndroid, sNativeFakeServer, count, modelType, name); - } - }); + return TestThreadUtils.runOnUiThreadBlockingNoException( + () + -> FakeServerHelperJni.get().verifyEntityCountByTypeAndName( + mNativeFakeServer, count, modelType, name)); } /** @@ -147,14 +98,8 @@ * @return whether the sessions on the server match the given urls. */ public boolean verifySessions(final String[] urls) { - checkFakeServerInitialized("useFakeServer must be called before data verification."); - return TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() { - @Override - public Boolean call() { - return nativeVerifySessions( - mNativeFakeServerHelperAndroid, sNativeFakeServer, urls); - } - }); + return TestThreadUtils.runOnUiThreadBlockingNoException( + () -> FakeServerHelperJni.get().verifySessions(mNativeFakeServer, urls)); } /** @@ -165,20 +110,16 @@ * @return a list of all the SyncEntity protos for that type. */ public List<SyncEntity> getSyncEntitiesByModelType(final int modelType) - throws ExecutionException { - checkFakeServerInitialized("useFakeServer must be called before getting sync entities."); - return TestThreadUtils.runOnUiThreadBlocking(new Callable<List<SyncEntity>>() { - @Override - public List<SyncEntity> call() throws InvalidProtocolBufferException { - byte[][] serializedEntities = nativeGetSyncEntitiesByModelType( - mNativeFakeServerHelperAndroid, sNativeFakeServer, modelType); - List<SyncEntity> entities = new ArrayList<SyncEntity>(serializedEntities.length); - for (int i = 0; i < serializedEntities.length; i++) { - entities.add(SyncEntity.parseFrom(serializedEntities[i])); - } - return entities; - } - }); + throws InvalidProtocolBufferException { + byte[][] serializedEntities = TestThreadUtils.runOnUiThreadBlockingNoException( + () + -> FakeServerHelperJni.get().getSyncEntitiesByModelType( + mNativeFakeServer, modelType)); + List<SyncEntity> entities = new ArrayList<SyncEntity>(serializedEntities.length); + for (byte[] serializedEntity : serializedEntities) { + entities.add(SyncEntity.parseFrom(serializedEntity)); + } + return entities; } /** @@ -193,17 +134,12 @@ */ public void injectUniqueClientEntity(final String nonUniqueName, final String clientTag, final EntitySpecifics entitySpecifics) { - checkFakeServerInitialized("useFakeServer must be called before data injection."); - TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Void>() { - @Override - public Void call() { - // The protocol buffer is serialized as a byte array because it can be easily - // deserialized from this format in native code. - nativeInjectUniqueClientEntity(mNativeFakeServerHelperAndroid, sNativeFakeServer, - nonUniqueName, clientTag, entitySpecifics.toByteArray()); - return null; - } - }); + // The protocol buffer is serialized as a byte array because it can be easily + // deserialized from this format in native code. + TestThreadUtils.runOnUiThreadBlocking( + () + -> FakeServerHelperJni.get().injectUniqueClientEntity(mNativeFakeServer, + nonUniqueName, clientTag, entitySpecifics.toByteArray())); } /** @@ -214,17 +150,12 @@ * @param entity the SyncEntity to serve for Wallet. */ public void setWalletData(final SyncEntity entity) { - checkFakeServerInitialized("useFakeServer must be called before data injection."); - TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Void>() { - @Override - public Void call() { - // The protocol buffer is serialized as a byte array because it can be easily - // deserialized from this format in native code. - nativeSetWalletData( - mNativeFakeServerHelperAndroid, sNativeFakeServer, entity.toByteArray()); - return null; - } - }); + // The protocol buffer is serialized as a byte array because it can be easily + // deserialized from this format in native code. + TestThreadUtils.runOnUiThreadBlocking( + () + -> FakeServerHelperJni.get().setWalletData( + mNativeFakeServer, entity.toByteArray())); } /** @@ -234,17 +165,12 @@ * @param entitySpecifics the new specifics proto for the entity */ public void modifyEntitySpecifics(final String id, final EntitySpecifics entitySpecifics) { - checkFakeServerInitialized("useFakeServer must be called before data modification."); - TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Void>() { - @Override - public Void call() { - // The protocol buffer is serialized as a byte array because it can be easily - // deserialized from this format in native code. - nativeModifyEntitySpecifics(mNativeFakeServerHelperAndroid, sNativeFakeServer, id, - entitySpecifics.toByteArray()); - return null; - } - }); + // The protocol buffer is serialized as a byte array because it can be easily + // deserialized from this format in native code. + TestThreadUtils.runOnUiThreadBlocking( + () + -> FakeServerHelperJni.get().modifyEntitySpecifics( + mNativeFakeServer, id, entitySpecifics.toByteArray())); } /** @@ -256,15 +182,10 @@ * @param parentId the ID of the desired parent bookmark folder */ public void injectBookmarkEntity(final String title, final GURL url, final String parentId) { - checkFakeServerInitialized("useFakeServer must be called before data injection."); - TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Void>() { - @Override - public Void call() { - nativeInjectBookmarkEntity( - mNativeFakeServerHelperAndroid, sNativeFakeServer, title, url, parentId); - return null; - } - }); + TestThreadUtils.runOnUiThreadBlocking( + () + -> FakeServerHelperJni.get().injectBookmarkEntity( + mNativeFakeServer, title, url, parentId)); } /** @@ -274,15 +195,10 @@ * @param parentId the ID of the desired parent bookmark folder */ public void injectBookmarkFolderEntity(final String title, final String parentId) { - checkFakeServerInitialized("useFakeServer must be called before data injection."); - TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Void>() { - @Override - public Void call() { - nativeInjectBookmarkFolderEntity( - mNativeFakeServerHelperAndroid, sNativeFakeServer, title, parentId); - return null; - } - }); + TestThreadUtils.runOnUiThreadBlocking( + () + -> FakeServerHelperJni.get().injectBookmarkFolderEntity( + mNativeFakeServer, title, parentId)); } /** @@ -296,15 +212,10 @@ */ public void modifyBookmarkEntity( final String bookmarkId, final String title, final GURL url, final String parentId) { - checkFakeServerInitialized("useFakeServer must be called before data injection."); - TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Void>() { - @Override - public Void call() { - nativeModifyBookmarkEntity(mNativeFakeServerHelperAndroid, sNativeFakeServer, - bookmarkId, title, url, parentId); - return null; - } - }); + TestThreadUtils.runOnUiThreadBlocking( + () + -> FakeServerHelperJni.get().modifyBookmarkEntity( + mNativeFakeServer, bookmarkId, title, url, parentId)); } /** @@ -316,15 +227,10 @@ */ public void modifyBookmarkFolderEntity( final String folderId, final String title, final String parentId) { - checkFakeServerInitialized("useFakeServer must be called before data injection."); - TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Void>() { - @Override - public Void call() { - nativeModifyBookmarkFolderEntity(mNativeFakeServerHelperAndroid, sNativeFakeServer, - folderId, title, parentId); - return null; - } - }); + TestThreadUtils.runOnUiThreadBlocking( + () + -> FakeServerHelperJni.get().modifyBookmarkFolderEntity( + mNativeFakeServer, folderId, title, parentId)); } /** @@ -341,15 +247,8 @@ } public void deleteEntity(final String id, final String clientTagHash) { - checkFakeServerInitialized("useFakeServer must be called before deleting an entity."); - TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Void>() { - @Override - public Void call() { - nativeDeleteEntity( - mNativeFakeServerHelperAndroid, sNativeFakeServer, id, clientTagHash); - return null; - } - }); + TestThreadUtils.runOnUiThreadBlocking( + () -> FakeServerHelperJni.get().deleteEntity(mNativeFakeServer, id, clientTagHash)); } /** @@ -359,80 +258,55 @@ * @return the opaque ID of the bookmark bar entity stored in the server */ public String getBookmarkBarFolderId() { - checkFakeServerInitialized("useFakeServer must be called before access"); - return TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<String>() { - @Override - public String call() { - return nativeGetBookmarkBarFolderId( - mNativeFakeServerHelperAndroid, sNativeFakeServer); - } - }); + return TestThreadUtils.runOnUiThreadBlockingNoException( + () -> FakeServerHelperJni.get().getBookmarkBarFolderId(mNativeFakeServer)); } /** * Sets trusted vault nigori with keys derived from trustedVaultKey on the server. */ public void setTrustedVaultNigori(byte[] trustedVaultKey) { - checkFakeServerInitialized("useFakeServer must be called before access"); - TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Void>() { - @Override - public Void call() { - nativeSetTrustedVaultNigori( - mNativeFakeServerHelperAndroid, sNativeFakeServer, trustedVaultKey); - return null; - } - }); + TestThreadUtils.runOnUiThreadBlocking( + () + -> FakeServerHelperJni.get().setTrustedVaultNigori( + mNativeFakeServer, trustedVaultKey)); } /** * Clear the server data (perform dashboard stop and clear). */ public void clearServerData() { - checkFakeServerInitialized("useFakeServer must be called before clearing data"); - TestThreadUtils.runOnUiThreadBlocking(() -> { - nativeClearServerData(mNativeFakeServerHelperAndroid, sNativeFakeServer); - }); + TestThreadUtils.runOnUiThreadBlocking( + () -> FakeServerHelperJni.get().clearServerData(mNativeFakeServer)); } - private static void checkFakeServerInitialized(String failureMessage) { - if (sNativeFakeServer == 0L) { - throw new IllegalStateException(failureMessage); - } + @NativeMethods + interface Natives { + // TODO(crbug.com/1215369): Remove the syncServiceImpl parameter below and retrieve the + // native SyncServiceImpl directly in native. ProfileSyncService.getSyncServiceImplForTest() + // can be removed then. + // TODO(crbug.com/1215369): SyncTestRule (the only current user of FakeServerHelper), should + // consider always using the real ProfileSyncService Java object, since the real native one + // is being used anyway. + long createFakeServer(long syncServiceImpl); + void deleteFakeServer(long fakeServer, long syncServiceImpl); + boolean verifyEntityCountByTypeAndName( + long fakeServer, int count, int modelType, String name); + boolean verifySessions(long fakeServer, String[] urlArray); + byte[][] getSyncEntitiesByModelType(long fakeServer, int modelType); + void injectUniqueClientEntity(long fakeServer, String nonUniqueName, String clientTag, + byte[] serializedEntitySpecifics); + void setWalletData(long fakeServer, byte[] serializedEntity); + void modifyEntitySpecifics(long fakeServer, String id, byte[] serializedEntitySpecifics); + void injectBookmarkEntity(long fakeServer, String title, GURL url, String parentId); + void injectBookmarkFolderEntity(long fakeServer, String title, String parentId); + void modifyBookmarkEntity( + long fakeServer, String bookmarkId, String title, GURL url, String parentId); + void modifyBookmarkFolderEntity( + long fakeServer, String bookmarkId, String title, String parentId); + String getBookmarkBarFolderId(long fakeServer); + void deleteEntity(long fakeServer, String id, String clientDefinedUniqueTag); + void setTrustedVaultNigori(long fakeServer, byte[] trustedVaultKey); + void clearServerData(long fakeServer); } - - // Native methods. - private native long nativeInit(); - private native long nativeCreateFakeServer( - long nativeFakeServerHelperAndroid, long nativeProfileSyncService); - private native void nativeDeleteFakeServer(long nativeFakeServerHelperAndroid, - long nativeFakeServer, long nativeProfileSyncService); - private native boolean nativeVerifyEntityCountByTypeAndName(long nativeFakeServerHelperAndroid, - long nativeFakeServer, int count, int modelType, String name); - private native boolean nativeVerifySessions( - long nativeFakeServerHelperAndroid, long nativeFakeServer, String[] urlArray); - private native byte[][] nativeGetSyncEntitiesByModelType( - long nativeFakeServerHelperAndroid, long nativeFakeServer, int modelType); - private native void nativeInjectUniqueClientEntity(long nativeFakeServerHelperAndroid, - long nativeFakeServer, String nonUniqueName, String clientTag, - byte[] serializedEntitySpecifics); - private native void nativeSetWalletData( - long nativeFakeServerHelperAndroid, long nativeFakeServer, byte[] serializedEntity); - private native void nativeModifyEntitySpecifics(long nativeFakeServerHelperAndroid, - long nativeFakeServer, String id, byte[] serializedEntitySpecifics); - private native void nativeInjectBookmarkEntity(long nativeFakeServerHelperAndroid, - long nativeFakeServer, String title, GURL url, String parentId); - private native void nativeInjectBookmarkFolderEntity(long nativeFakeServerHelperAndroid, - long nativeFakeServer, String title, String parentId); - private native void nativeModifyBookmarkEntity(long nativeFakeServerHelperAndroid, - long nativeFakeServer, String bookmarkId, String title, GURL url, String parentId); - private native void nativeModifyBookmarkFolderEntity(long nativeFakeServerHelperAndroid, - long nativeFakeServer, String bookmarkId, String title, String parentId); - private native String nativeGetBookmarkBarFolderId( - long nativeFakeServerHelperAndroid, long nativeFakeServer); - private native void nativeDeleteEntity(long nativeFakeServerHelperAndroid, - long nativeFakeServer, String id, String clientDefinedUniqueTag); - private native void nativeSetTrustedVaultNigori( - long nativeFakeServerHelperAndroid, long nativeFakeServer, byte[] trustedVaultKey); - private native void nativeClearServerData( - long nativeFakeServerHelperAndroid, long nativeFakeServer); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java index 09ef222..294b8ccf 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java
@@ -139,7 +139,8 @@ private void ruleTearDown() { TestThreadUtils.runOnUiThreadBlocking(() -> { mProfileSyncService.setSyncRequested(false); - FakeServerHelper.deleteFakeServer(); + mFakeServerHelper = null; + FakeServerHelper.destroyInstance(); }); ProfileSyncService.resetForTests(); } @@ -340,8 +341,7 @@ mProfileSyncService = ProfileSyncService.get(); mContext = InstrumentationRegistry.getTargetContext(); - FakeServerHelper.useFakeServer(mContext); - mFakeServerHelper = FakeServerHelper.get(); + mFakeServerHelper = FakeServerHelper.createInstanceAndGet(); }); startMainActivityForSyncTest();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/childaccounts/ChildAccountServiceTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/childaccounts/ChildAccountServiceTest.java index cbd0facc..74851b97 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/childaccounts/ChildAccountServiceTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/childaccounts/ChildAccountServiceTest.java
@@ -37,14 +37,22 @@ import org.chromium.ui.base.WindowAndroid; import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.List; /** * Unit tests for {@link ChildAccountService}. */ @RunWith(BaseRobolectricTestRunner.class) public class ChildAccountServiceTest { - private static final Account CHILD_ACCOUNT = - AccountUtils.createAccountFromName("child.account@gmail.com"); + private static final Account CHILD_ACCOUNT1 = + AccountUtils.createAccountFromName("child.account1@gmail.com"); + private static final Account CHILD_ACCOUNT2 = + AccountUtils.createAccountFromName("child.account2@gmail.com"); + private static final Account ADULT_ACCOUNT1 = + AccountUtils.createAccountFromName("adult.account1@gmail.com"); + private static final Account ADULT_ACCOUNT2 = + AccountUtils.createAccountFromName("adult.account2@gmail.com"); private static final long FAKE_NATIVE_CALLBACK = 1000L; private final FakeAccountManagerFacade mFakeFacade = spy(new FakeAccountManagerFacade(null) { @@ -82,7 +90,7 @@ @Test public void testChildAccountStatusWhenNoAccountsOnDevice() { - ChildAccountService.checkChildAccountStatus(mListenerMock); + ChildAccountService.checkChildAccountStatus(Collections.emptyList(), mListenerMock); verify(mListenerMock).onStatusReady(ChildAccountStatus.NOT_CHILD); } @@ -91,39 +99,34 @@ // For product reason, child account cannot share device, so as long // as more than one account detected on device, the child account status // on device should be NOT_CHILD. - mAccountManagerTestRule.addAccount(CHILD_ACCOUNT); - mAccountManagerTestRule.addAccount("child.account2@gmail.com"); - ChildAccountService.checkChildAccountStatus(mListenerMock); + ChildAccountService.checkChildAccountStatus( + List.of(CHILD_ACCOUNT1, CHILD_ACCOUNT2), mListenerMock); verify(mListenerMock).onStatusReady(ChildAccountStatus.NOT_CHILD); } @Test public void testChildAccountStatusWhenOneChildAndOneAdultAccountsOnDevice() { - mAccountManagerTestRule.addAccount(CHILD_ACCOUNT); - mAccountManagerTestRule.addAccount("adult.account@gmail.com"); - ChildAccountService.checkChildAccountStatus(mListenerMock); + ChildAccountService.checkChildAccountStatus( + List.of(CHILD_ACCOUNT1, ADULT_ACCOUNT1), mListenerMock); verify(mListenerMock).onStatusReady(ChildAccountStatus.NOT_CHILD); } @Test public void testChildAccountStatusWhenTwoAdultAccountsOnDevice() { - mAccountManagerTestRule.addAccount("adult.account1@gmail.com"); - mAccountManagerTestRule.addAccount("adult.account2@gmail.com"); - ChildAccountService.checkChildAccountStatus(mListenerMock); + ChildAccountService.checkChildAccountStatus( + List.of(ADULT_ACCOUNT1, ADULT_ACCOUNT2), mListenerMock); verify(mListenerMock).onStatusReady(ChildAccountStatus.NOT_CHILD); } @Test public void testChildAccountStatusWhenOnlyOneAdultAccountOnDevice() { - mAccountManagerTestRule.addAccount("adult.account1@gmail.com"); - ChildAccountService.checkChildAccountStatus(mListenerMock); + ChildAccountService.checkChildAccountStatus(List.of(ADULT_ACCOUNT1), mListenerMock); verify(mListenerMock).onStatusReady(ChildAccountStatus.NOT_CHILD); } @Test public void testChildAccountStatusWhenOnlyOneChildAccountOnDevice() { - mAccountManagerTestRule.addAccount(CHILD_ACCOUNT); - ChildAccountService.checkChildAccountStatus(mListenerMock); + ChildAccountService.checkChildAccountStatus(List.of(CHILD_ACCOUNT1), mListenerMock); verify(mListenerMock).onStatusReady(ChildAccountStatus.REGULAR_CHILD); } @@ -131,7 +134,7 @@ public void testReauthenticateChildAccountWhenActivityIsNull() { when(mWindowAndroidMock.getActivity()).thenReturn(new WeakReference<>(null)); ChildAccountService.reauthenticateChildAccount( - mWindowAndroidMock, CHILD_ACCOUNT.name, FAKE_NATIVE_CALLBACK); + mWindowAndroidMock, CHILD_ACCOUNT1.name, FAKE_NATIVE_CALLBACK); verify(mNativeMock).onReauthenticationFailed(FAKE_NATIVE_CALLBACK); } @@ -141,7 +144,7 @@ when(mWindowAndroidMock.getActivity()).thenReturn(new WeakReference<>(activity)); doAnswer(invocation -> { Account account = invocation.getArgument(0); - Assert.assertEquals(CHILD_ACCOUNT.name, account.name); + Assert.assertEquals(CHILD_ACCOUNT1.name, account.name); Callback<Boolean> callback = invocation.getArgument(2); callback.onResult(true); return null; @@ -150,7 +153,7 @@ .updateCredentials(any(Account.class), eq(activity), any()); ChildAccountService.reauthenticateChildAccount( - mWindowAndroidMock, CHILD_ACCOUNT.name, FAKE_NATIVE_CALLBACK); + mWindowAndroidMock, CHILD_ACCOUNT1.name, FAKE_NATIVE_CALLBACK); verify(mNativeMock, never()).onReauthenticationFailed(anyLong()); } @@ -160,7 +163,7 @@ when(mWindowAndroidMock.getActivity()).thenReturn(new WeakReference<>(activity)); doAnswer(invocation -> { Account account = invocation.getArgument(0); - Assert.assertEquals(CHILD_ACCOUNT.name, account.name); + Assert.assertEquals(CHILD_ACCOUNT1.name, account.name); Callback<Boolean> callback = invocation.getArgument(2); callback.onResult(false); return null; @@ -169,7 +172,7 @@ .updateCredentials(any(Account.class), eq(activity), any()); ChildAccountService.reauthenticateChildAccount( - mWindowAndroidMock, CHILD_ACCOUNT.name, FAKE_NATIVE_CALLBACK); + mWindowAndroidMock, CHILD_ACCOUNT1.name, FAKE_NATIVE_CALLBACK); verify(mNativeMock).onReauthenticationFailed(FAKE_NATIVE_CALLBACK); } } \ No newline at end of file
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencerTest.java index 4a03cab9..b152c31 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencerTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencerTest.java
@@ -26,10 +26,10 @@ import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Feature; import org.chromium.chrome.test.util.browser.Features; +import org.chromium.components.signin.AccountUtils; import org.chromium.components.signin.ChildAccountStatus; import java.util.Collections; -import java.util.List; /** * Tests FirstRunFlowSequencer which contains the core logic of what should be shown during the @@ -42,8 +42,8 @@ public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor(); /** Information for Google OS account */ - private static final String GOOGLE_ACCOUNT_TYPE = "com.google"; - private static final String DEFAULT_ACCOUNT = "test@gmail.com"; + private static final Account DEFAULT_ACCOUNT = + AccountUtils.createAccountFromName("test@gmail.com"); /** * Testing version of FirstRunFlowSequencer that allows us to override all needed checks. @@ -56,7 +56,6 @@ public boolean isFirstRunFlowComplete; public boolean isSignedIn; public boolean isSyncAllowed; - public List<Account> googleAccounts; public boolean shouldSkipFirstUseHints; public boolean isFirstRunEulaAccepted; public boolean shouldShowDataReductionPage; @@ -89,11 +88,6 @@ } @Override - public List<Account> getGoogleAccounts() { - return googleAccounts; - } - - @Override public boolean shouldSkipFirstUseHints() { return shouldSkipFirstUseHints; } @@ -139,10 +133,9 @@ mSequencer.isFirstRunFlowComplete = false; mSequencer.isSignedIn = false; mSequencer.isSyncAllowed = true; - mSequencer.googleAccounts = Collections.emptyList(); mSequencer.shouldSkipFirstUseHints = false; mSequencer.shouldShowDataReductionPage = false; - mSequencer.initializeSharedState(ChildAccountStatus.NOT_CHILD); + mSequencer.initializeSharedState(ChildAccountStatus.NOT_CHILD, Collections.emptyList()); mSequencer.processFreEnvironmentPreNative(); assertTrue(mSequencer.calledOnFlowIsKnown); @@ -163,11 +156,10 @@ mSequencer.isFirstRunFlowComplete = false; mSequencer.isSignedIn = false; mSequencer.isSyncAllowed = true; - mSequencer.googleAccounts = - Collections.singletonList(new Account(DEFAULT_ACCOUNT, GOOGLE_ACCOUNT_TYPE)); mSequencer.shouldSkipFirstUseHints = false; mSequencer.shouldShowDataReductionPage = false; - mSequencer.initializeSharedState(ChildAccountStatus.REGULAR_CHILD); + mSequencer.initializeSharedState( + ChildAccountStatus.REGULAR_CHILD, Collections.singletonList(DEFAULT_ACCOUNT)); mSequencer.processFreEnvironmentPreNative(); assertTrue(mSequencer.calledOnFlowIsKnown); @@ -188,11 +180,10 @@ mSequencer.isFirstRunFlowComplete = false; mSequencer.isSignedIn = false; mSequencer.isSyncAllowed = true; - mSequencer.googleAccounts = Collections.emptyList(); mSequencer.shouldSkipFirstUseHints = false; mSequencer.shouldShowDataReductionPage = true; mSequencer.shouldShowSearchEnginePage = false; - mSequencer.initializeSharedState(ChildAccountStatus.NOT_CHILD); + mSequencer.initializeSharedState(ChildAccountStatus.NOT_CHILD, Collections.emptyList()); mSequencer.processFreEnvironmentPreNative(); assertTrue(mSequencer.calledOnFlowIsKnown); @@ -213,11 +204,10 @@ mSequencer.isFirstRunFlowComplete = false; mSequencer.isSignedIn = false; mSequencer.isSyncAllowed = true; - mSequencer.googleAccounts = Collections.emptyList(); mSequencer.shouldSkipFirstUseHints = false; mSequencer.shouldShowDataReductionPage = true; mSequencer.shouldShowSearchEnginePage = true; - mSequencer.initializeSharedState(ChildAccountStatus.NOT_CHILD); + mSequencer.initializeSharedState(ChildAccountStatus.NOT_CHILD, Collections.emptyList()); mSequencer.processFreEnvironmentPreNative(); assertTrue(mSequencer.calledOnFlowIsKnown);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerImplTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerImplTest.java index fee9a7d..eff76de 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerImplTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerImplTest.java
@@ -30,6 +30,7 @@ import org.chromium.chrome.browser.sync.ProfileSyncService; import org.chromium.chrome.test.util.browser.Features; import org.chromium.components.externalauth.ExternalAuthUtils; +import org.chromium.components.signin.AccountUtils; import org.chromium.components.signin.base.AccountInfo; import org.chromium.components.signin.base.CoreAccountId; import org.chromium.components.signin.base.CoreAccountInfo; @@ -115,7 +116,8 @@ mSigninManager.onFirstRunCheckDone(); SigninManager.SignInCallback callback = mock(SigninManager.SignInCallback.class); - mSigninManager.signinAndEnableSync(SigninAccessPoint.START_PAGE, ACCOUNT_INFO, callback); + mSigninManager.signinAndEnableSync(SigninAccessPoint.START_PAGE, + AccountUtils.createAccountFromName(ACCOUNT_INFO.getEmail()), callback); verify(mNativeMock) .fetchAndApplyCloudPolicy(eq(NATIVE_SIGNIN_MANAGER), eq(ACCOUNT_INFO), any()); @@ -375,7 +377,8 @@ mSigninManager.onFirstRunCheckDone(); // Allow sign-in. - mSigninManager.signinAndEnableSync(SigninAccessPoint.UNKNOWN, ACCOUNT_INFO, null); + mSigninManager.signinAndEnableSync(SigninAccessPoint.UNKNOWN, + AccountUtils.createAccountFromName(ACCOUNT_INFO.getEmail()), null); AtomicInteger callCount = new AtomicInteger(0); mSigninManager.runAfterOperationInProgress(callCount::incrementAndGet); @@ -390,6 +393,7 @@ when(mIdentityManagerNativeMock.getPrimaryAccountInfo( eq(NATIVE_IDENTITY_MANAGER), anyInt())) .thenReturn(ACCOUNT_INFO); - mSigninManager.signinAndEnableSync(SigninAccessPoint.UNKNOWN, ACCOUNT_INFO, null); + mSigninManager.signinAndEnableSync(SigninAccessPoint.UNKNOWN, + AccountUtils.createAccountFromName(ACCOUNT_INFO.getEmail()), null); } }
diff --git a/chrome/android/modules/chrome_bundle_tmpl.gni b/chrome/android/modules/chrome_bundle_tmpl.gni index 18233436..e2c5808 100644 --- a/chrome/android/modules/chrome_bundle_tmpl.gni +++ b/chrome/android/modules/chrome_bundle_tmpl.gni
@@ -168,6 +168,9 @@ is_multi_abi = _is_multi_abi validate_services = _enable_chrome_module + # For this to be respected, it must also be set on the base module target. + strip_unused_resources = is_official_build + # List of DFMs that are installed by default by wrapper scripts, to make # testing easier. This removes the need to manually specify, e.g., # "-m dev_ui" on every install or run.
diff --git a/chrome/app/resources/generated_resources_af.xtb b/chrome/app/resources/generated_resources_af.xtb index 647bc3c..45550d3 100644 --- a/chrome/app/resources/generated_resources_af.xtb +++ b/chrome/app/resources/generated_resources_af.xtb
@@ -237,6 +237,7 @@ <translation id="1243436884219965846">Gaan wagwoorde na</translation> <translation id="1244265436519979884">Linux-terugstelling vind tans plaas</translation> <translation id="1244303850296295656">Uitbreidingfout</translation> +<translation id="1246863218384630739">Kon nie <ph name="VM_NAME" /> installeer nie: Prent-URL het 'n <ph name="HTTP_ERROR" />-foutkode gewys. Kontak jou administrateur.</translation> <translation id="1246905108078336582">Verwyder voorstel van knipbord af?</translation> <translation id="1251366534849411931">Verwag beginkrulhakie: <ph name="ERROR_LINE" /></translation> <translation id="1251480783646955802">Dit sal <ph name="TOTAL_USAGE" /> se data uitvee wat deur werwe en geïnstalleerde programme geberg is</translation> @@ -1537,6 +1538,7 @@ <translation id="2610260699262139870">Ware grootte</translation> <translation id="2610780100389066815">Microsoft-vertrouelys-ondertekening</translation> <translation id="2612676031748830579">Kaartnommer</translation> +<translation id="2613535083491958306"><ph name="ORIGIN" /> sal <ph name="FILENAME" /> kan wysig</translation> <translation id="2616366145935564096">Lees en verander jou data op <ph name="WEBSITE_1" /></translation> <translation id="2617342710774726426">SIM-kaart is gesluit</translation> <translation id="2618797463720777311">Stel Nabydeling op</translation> @@ -2295,6 +2297,7 @@ <translation id="3441653493275994384">Skerm</translation> <translation id="3441663102605358937">Meld weer by <ph name="ACCOUNT" /> aan om hierdie rekening te verifieer</translation> <translation id="3444641828375597683">Adverteerders en uitgewers kan FLoC gebruik, wat later op hierdie bladsy beskryf word.</translation> +<translation id="3444726579402183581"><ph name="ORIGIN" /> sal <ph name="FILENAME" /> kan bekyk</translation> <translation id="3445047461171030979">Vinnige antwoorde in Google Assistent</translation> <translation id="3445288400492335833"><ph name="MINUTES" /> min.</translation> <translation id="3445925074670675829">USB C-toestel</translation> @@ -3793,6 +3796,7 @@ <translation id="5086874064903147617">Stel verstektuisblad terug?</translation> <translation id="5087249366037322692">Bygevoeg deur 'n derde party</translation> <translation id="5087580092889165836">Voeg kaart by</translation> +<translation id="5087748406101774740"><ph name="APP_NAME" /> (<ph name="PROFILE_NAME" />)</translation> <translation id="5088534251099454936">PKCS #1 SHA-512 met RSA-enkripsie</translation> <translation id="5090637338841444533">Mag nie jou kameraposisie naspoor nie</translation> <translation id="5094721898978802975">Kommunikeer met plaaslike programme wat saamwerk</translation> @@ -4574,6 +4578,7 @@ <translation id="5964113968897211042">{COUNT,plural, =0{Maak almal in &nuwe venster oop}=1{Maak in &nuwe venster oop}other{Maak almal ({COUNT}) in &nuwe venster oop}}</translation> <translation id="5965661248935608907">Dit beheer ook watter bladsy gewys word wanneer jy die Tuisknoppie klik of vanuit die omnikassie soek.</translation> <translation id="5969419185858894314"><ph name="ORIGIN" /> kan hierdie lêers in <ph name="FOLDERNAME" /> bekyk</translation> +<translation id="5969728632630673489">Kortpadsleutelkennisgewing is toegemaak</translation> <translation id="5971037678316050792">Beheer Bluetooth-aansluitertoestand en -saambinding</translation> <translation id="597235323114979258">Sien nog bestemmings</translation> <translation id="5972666587303800813">Geen-handeling-diens</translation> @@ -6209,6 +6214,7 @@ <translation id="7766807826975222231">Gaan op 'n toer</translation> <translation id="7766838926148951335">Aanvaar toestemmings</translation> <translation id="7768507955883790804">Werwe volg outomaties hierdie instelling wanneer jy hulle besoek</translation> +<translation id="7768526219335215384"><ph name="ORIGIN" /> sal lêers in <ph name="FOLDERNAME" /> kan bekyk</translation> <translation id="7768770796815395237">Verander</translation> <translation id="7768784765476638775">Hardoplees</translation> <translation id="7770612696274572992">Prent is van ander toestel af gekopieer</translation> @@ -6512,6 +6518,7 @@ <translation id="8063235345342641131">Verstek- groen avatar</translation> <translation id="8063535366119089408">Bekyk lêer</translation> <translation id="8064279191081105977">Groep <ph name="GROUP_NAME" /> – <ph name="GROUP_CONTENTS" /> – <ph name="COLLAPSED_STATE" /></translation> +<translation id="8066444921260601116">Verbindingdialoog</translation> <translation id="8068253693380742035">Raak om aan te meld</translation> <translation id="8069615408251337349">Google Wolkdruk</translation> <translation id="8071432093239591881">Druk as prent</translation> @@ -7542,6 +7549,7 @@ <translation id="9186963452600581158">Meld met jou kind se Google-rekening aan</translation> <translation id="9188732951356337132">Stuur gebruik- en diagnostiese data. Hierdie toestel stuur tans diagnostiese, toestel- en programgebruikdata outomaties na Google toe. Dit sal nie gebruik word om jou kind te identifiseer nie en sal met stelsel- en programstabiliteit en ander verbeteringe help. Sekere saamgestelde data sal ook Google-programme en -vennote, soos Android-ontwikkelaars, help. As die bykomende Web- en Programaktiwiteit-instelling vir jou kind aangeskakel is, kan hierdie data in hul Google-rekening gestoor word. <ph name="BEGIN_LINK2" />Kom meer te wete<ph name="END_LINK2" /></translation> <translation id="9198090666959937775">Gebruik jou Android-foon as 'n sekuriteitsleutel</translation> +<translation id="9200339982498053969"><ph name="ORIGIN" /> sal lêers in <ph name="FOLDERNAME" /> kan wysig</translation> <translation id="920045321358709304">Deursoek <ph name="SEARCH_ENGINE" /></translation> <translation id="9201023452444595544">Enige vanlyn data sal uitgevee word</translation> <translation id="9201220332032049474">Skermslotopsies</translation>
diff --git a/chrome/app/resources/generated_resources_az.xtb b/chrome/app/resources/generated_resources_az.xtb index 3faa237..8c8232b 100644 --- a/chrome/app/resources/generated_resources_az.xtb +++ b/chrome/app/resources/generated_resources_az.xtb
@@ -1635,7 +1635,7 @@ <translation id="2731971182069536520">Növbəti dəfə cihazı yenidən başlatdığınız zaman administrator birdəfəlik güncəlləmə yerinə yetirəcək. Bu, lokal datanızı siləcək.</translation> <translation id="2732134891301408122"><ph name="CURRENT_ELEMENT" />/<ph name="TOTAL_ELEMENTS" /> əlavə məzmun</translation> <translation id="2734760316755174687"><ph name="SITE_GROUP_NAME" /> ünvanındakı saytlar da sıfırlanacaq.</translation> -<translation id="2735712963799620190">Cədvəl</translation> +<translation id="2735712963799620190">Rejim</translation> <translation id="2737363922397526254">Yığcamlaşdırın...</translation> <translation id="2738030019664645674">Saytlara cihazınızda quraşdırılmış şriftlərdən istifadə etmək icazəsi verməyin</translation> <translation id="2738771556149464852">Sonra yox</translation>
diff --git a/chrome/app/resources/generated_resources_fil.xtb b/chrome/app/resources/generated_resources_fil.xtb index 3b2fcce..589b0c4 100644 --- a/chrome/app/resources/generated_resources_fil.xtb +++ b/chrome/app/resources/generated_resources_fil.xtb
@@ -238,6 +238,7 @@ <translation id="1243436884219965846">Suriin ang mga password</translation> <translation id="1244265436519979884">Kasalukuyang isinasagawa ang pag-restore ng Linux</translation> <translation id="1244303850296295656">Error sa extension</translation> +<translation id="1246863218384630739">Hindi ma-install ang <ph name="VM_NAME" />: Nagbalik ang URL ng Larawan ng code ng error na <ph name="HTTP_ERROR" />. Makipag-ugnayan sa iyong administrator.</translation> <translation id="1246905108078336582">Alisin ang suhestyon mula sa clipboard?</translation> <translation id="1251366534849411931">Inaasahang opening curly brace: <ph name="ERROR_LINE" /></translation> <translation id="1251480783646955802">Iki-clear nito ang <ph name="TOTAL_USAGE" /> ng data na na-store ng mga site at naka-install na app</translation> @@ -1538,6 +1539,7 @@ <translation id="2610260699262139870">A&ktwal na Laki</translation> <translation id="2610780100389066815">Microsoft Trust List Signing</translation> <translation id="2612676031748830579">Numero ng card</translation> +<translation id="2613535083491958306">Mae-edit ng <ph name="ORIGIN" /> ang <ph name="FILENAME" /></translation> <translation id="2616366145935564096">Basahin at baguhin ang iyong data sa <ph name="WEBSITE_1" /></translation> <translation id="2617342710774726426">Naka-lock ang SIM card</translation> <translation id="2618797463720777311">I-set up ang Nearby Share</translation> @@ -2296,6 +2298,7 @@ <translation id="3441653493275994384">Screen</translation> <translation id="3441663102605358937">Mag-sign in ulit sa <ph name="ACCOUNT" /> para ma-verify ang account na ito</translation> <translation id="3444641828375597683">Magagamit ng mga advertiser at publisher ang FLoC, na inilalarawan sa ibang bahagi ng page na ito.</translation> +<translation id="3444726579402183581">Matitingnan ng <ph name="ORIGIN" /> ang <ph name="FILENAME" /></translation> <translation id="3445047461171030979">Mga mabilisang sagot ng Google Assistant</translation> <translation id="3445288400492335833"><ph name="MINUTES" /> (na) min</translation> <translation id="3445925074670675829">USB-C device</translation> @@ -3794,6 +3797,7 @@ <translation id="5086874064903147617">I-restore ang default na homepage?</translation> <translation id="5087249366037322692">Idinagdag ng isang third-party</translation> <translation id="5087580092889165836">Magdagdag ng card</translation> +<translation id="5087748406101774740"><ph name="APP_NAME" /> (<ph name="PROFILE_NAME" />)</translation> <translation id="5088534251099454936">PKCS #1 SHA-512 na May RSA Encryption</translation> <translation id="5090637338841444533">Hindi pinapayagang i-track ang posisyon ng iyong camera</translation> <translation id="5094721898978802975">Makipag-ugnay sa mga nakikipagtulungang native na application</translation> @@ -4574,6 +4578,7 @@ <translation id="5964113968897211042">{COUNT,plural, =0{Buksan lahat sa &bagong window}=1{Buksan sa &bagong window}one{Buksan lahat ({COUNT}) sa &bagong window}other{Buksan lahat ({COUNT}) sa &bagong window}}</translation> <translation id="5965661248935608907">Kinokontrol din nito kung anong pahina ang ipinapakita kapag na-click mo ang button ng Home o naghanap mula sa Omnibox.</translation> <translation id="5969419185858894314">Puwedeng tingnan ng <ph name="ORIGIN" /> ang mga file sa <ph name="FOLDERNAME" /></translation> +<translation id="5969728632630673489">Na-dismiss ang abiso sa keyboard shortcut</translation> <translation id="5971037678316050792">Kontrolin ang estado at pagpapares ng Bluetooth adapter</translation> <translation id="597235323114979258">Tumingin pa ng mga destinasyon</translation> <translation id="5972666587303800813">No-op na Serbisyo</translation> @@ -6209,6 +6214,7 @@ <translation id="7766807826975222231">Maglibot</translation> <translation id="7766838926148951335">Tanggapin ang mga pahintulot</translation> <translation id="7768507955883790804">Awtomatikong susundin ng mga site ang setting na ito kapag bumisita ka sa mga ito</translation> +<translation id="7768526219335215384">Matitingnan ng <ph name="ORIGIN" /> ang mga file sa <ph name="FOLDERNAME" /></translation> <translation id="7768770796815395237">Baguhin</translation> <translation id="7768784765476638775">Select to Speak</translation> <translation id="7770612696274572992">Nakopya ang larawan mula sa isa pang device</translation> @@ -6513,6 +6519,7 @@ <translation id="8063235345342641131">Default na berdeng avatar</translation> <translation id="8063535366119089408">Tingnan ang file</translation> <translation id="8064279191081105977">Grupong <ph name="GROUP_NAME" /> - <ph name="GROUP_CONTENTS" /> - <ph name="COLLAPSED_STATE" /></translation> +<translation id="8066444921260601116">Dialog ng Koneksyon</translation> <translation id="8068253693380742035">Pindutin upang mag-sign in</translation> <translation id="8069615408251337349">Google Cloud Print</translation> <translation id="8071432093239591881">I-print bilang larawan</translation> @@ -7546,6 +7553,7 @@ <translation id="9186963452600581158">Mag-sign in gamit ang Google Account ng isang bata</translation> <translation id="9188732951356337132">Magpadala ng data ng paggamit at diagnostic na data. Sa kasalukuyan, ang device na ito ay awtomatikong nagpapadala ng diagnostic na data, data ng device, at data ng paggamit sa app sa Google. Hindi ito gagamitin para tukuyin ang pagkakakilanlan ng iyong anak, at makakatulong ito sa stability ng system at ng app, at sa iba pang pagpapahusay. Makakatulong din ang ilang pinagsama-samang data sa mga app at partner ng Google, gaya ng mga developer ng Android. Kung naka-on ang karagdagang setting ng Aktibidad sa Web at App para sa iyong anak, maaaring ma-save ang data na ito sa kanyang Google Account. <ph name="BEGIN_LINK2" />Matuto Pa<ph name="END_LINK2" /></translation> <translation id="9198090666959937775">Gamitin ang iyong Android phone bilang security key</translation> +<translation id="9200339982498053969">Mae-edit ng <ph name="ORIGIN" /> ang mga file sa <ph name="FOLDERNAME" /></translation> <translation id="920045321358709304">Maghanap sa <ph name="SEARCH_ENGINE" /></translation> <translation id="9201023452444595544">Maki-clear ang anumang offline na data</translation> <translation id="9201220332032049474">Mga opsyon sa lock ng screen</translation>
diff --git a/chrome/app/resources/generated_resources_iw.xtb b/chrome/app/resources/generated_resources_iw.xtb index ccb6aa4d..169227a 100644 --- a/chrome/app/resources/generated_resources_iw.xtb +++ b/chrome/app/resources/generated_resources_iw.xtb
@@ -239,6 +239,7 @@ <translation id="1243436884219965846">בדיקת הסיסמאות</translation> <translation id="1244265436519979884">מתבצע כעת שחזור של Linux</translation> <translation id="1244303850296295656">שגיאת תוסף</translation> +<translation id="1246863218384630739">לא ניתן היה להתקין את <ph name="VM_NAME" />: כתובת ה-URL של התמונה החזירה קוד שגיאה <ph name="HTTP_ERROR" />. יש לפנות למנהל המערכת.</translation> <translation id="1246905108078336582">להסיר את ההצעה מהלוח?</translation> <translation id="1251366534849411931">חסרים סוגריים מסולסלים פותחים: <ph name="ERROR_LINE" /></translation> <translation id="1251480783646955802">הפעולה הזו תגרום למחיקת נתונים בנפח של <ph name="TOTAL_USAGE" /> שאוחסנו על ידי אתרים ואפליקציות מותקנות</translation> @@ -1536,6 +1537,7 @@ <translation id="2610260699262139870">גודל ממ&שי</translation> <translation id="2610780100389066815">חתימת רשימת יחסי אמון של Microsoft</translation> <translation id="2612676031748830579">מספר הכרטיס</translation> +<translation id="2613535083491958306">ל-<ph name="ORIGIN" /> תהיה הרשאה לערוך את <ph name="FILENAME" /></translation> <translation id="2616366145935564096">לקרוא ולשנות את הנתונים שלך ב-<ph name="WEBSITE_1" /></translation> <translation id="2617342710774726426">כרטיס ה-SIM נעול</translation> <translation id="2618797463720777311">הגדרת 'שיתוף בקרבת מקום'</translation> @@ -2295,6 +2297,7 @@ <translation id="3441653493275994384">מסך</translation> <translation id="3441663102605358937">יש להיכנס שוב לחשבון <ph name="ACCOUNT" /> כדי לאמת אותו.</translation> <translation id="3444641828375597683">מפרסמים ובעלי תוכן דיגיטלי יכולים להשתמש בשיטת FLoC, המתוארת בהמשך הדף.</translation> +<translation id="3444726579402183581">ל-<ph name="ORIGIN" /> תהיה הרשאה לצפות בקובץ <ph name="FILENAME" /></translation> <translation id="3445047461171030979">תשובות מהירות מ-Google Assistant</translation> <translation id="3445288400492335833"><ph name="MINUTES" /> דק'</translation> <translation id="3445925074670675829">מכשיר עם יציאת USB-C</translation> @@ -3793,6 +3796,7 @@ <translation id="5086874064903147617">לשחזר את דף הבית שהוגדר כברירת המחדל?</translation> <translation id="5087249366037322692">נוסף על ידי צד שלישי</translation> <translation id="5087580092889165836">הוספת כרטיס</translation> +<translation id="5087748406101774740"><ph name="APP_NAME" /> (<ph name="PROFILE_NAME" />)</translation> <translation id="5088534251099454936">PKCS #1 SHA-512 עם הצפנת RSA</translation> <translation id="5090637338841444533">לא מורשים לעקוב אחר מיקום המצלמה</translation> <translation id="5094721898978802975">יצירת קשר עם יישומים מקוריים שמשתפים פעולה</translation> @@ -4573,6 +4577,7 @@ <translation id="5964113968897211042">{COUNT,plural, =0{פתיחת כל הכתובות בחלון &חדש}=1{פתיחה בחלון &חדש}two{פתיחת כל הכתובות ({COUNT}) בחלון &חדש}many{פתיחת כל הכתובות ({COUNT}) בחלון &חדש}other{פתיחת כל הכתובות ({COUNT}) בחלון &חדש}}</translation> <translation id="5965661248935608907">הגדרה זו גם קובעת איזה דף מוצג כשלוחצים על הלחצן 'דף הבית' או מבצעים חיפוש מסרגל הכתובות.</translation> <translation id="5969419185858894314">ל-<ph name="ORIGIN" /> יש הרשאה להציג קבצים בתיקייה <ph name="FOLDERNAME" /></translation> +<translation id="5969728632630673489">ההודעה לגבי מקשי הקיצור נסגרה</translation> <translation id="5971037678316050792">הגדרת מצב ושליטה בהתאמה עבור מתאם Bluetooth</translation> <translation id="597235323114979258">הצגת יעדים נוספים</translation> <translation id="5972666587303800813">שירות ללא תפעול (no-ops)</translation> @@ -6208,6 +6213,7 @@ <translation id="7766807826975222231">הצטרף לסיור</translation> <translation id="7766838926148951335">אישור הרשאות</translation> <translation id="7768507955883790804">אתרים יפעלו לפי ההגדרה הזו באופן אוטומטי בזמן הביקור בהם</translation> +<translation id="7768526219335215384">ל-<ph name="ORIGIN" /> תהיה הרשאה לראות את הקבצים בתיקייה <ph name="FOLDERNAME" /></translation> <translation id="7768770796815395237">שינוי</translation> <translation id="7768784765476638775">הקראה</translation> <translation id="7770612696274572992">התמונה הועתקה ממכשיר אחר</translation> @@ -6511,6 +6517,7 @@ <translation id="8063235345342641131">דמות ברירת מחדל ירוקה</translation> <translation id="8063535366119089408">אישור לראות את הקובץ</translation> <translation id="8064279191081105977">קבוצה <ph name="GROUP_NAME" /> – <ph name="GROUP_CONTENTS" /> – <ph name="COLLAPSED_STATE" /></translation> +<translation id="8066444921260601116">תיבת דו-שיח לחיבור</translation> <translation id="8068253693380742035">יש לגעת כדי להיכנס</translation> <translation id="8069615408251337349">Google Cloud Print</translation> <translation id="8071432093239591881">הדפסה כתמונה</translation> @@ -7545,6 +7552,7 @@ <translation id="9186963452600581158">כניסה באמצעות חשבון Google של ילד או ילדה</translation> <translation id="9188732951356337132">שליחה של נתוני אבחון ונתונים על השימוש. המכשיר הזה שולח עכשיו אל Google באופן אוטומטי נתוני אבחון ונתונים לגבי השימוש במכשיר ובאפליקציות. נתונים אלה לא ישמשו כדי לזהות את הילד/ה שלך, והם יעזרו לשמור על יציבות המערכת והאפליקציות, כמו גם לביצוע שיפורים אחרים. חלק מהנתונים הנצברים יעזרו גם לאפליקציות ולשותפים של Google, כמו מפתחי Android. אם הופעלה בשביל הילד/ה שלך האפשרות 'פעילות באתרי אינטרנט ובאפליקציות נוספים', ייתכן שהנתונים יישמרו בחשבון Google שלו/ה. <ph name="BEGIN_LINK2" />מידע נוסף<ph name="END_LINK2" /></translation> <translation id="9198090666959937775">שימוש בטלפון Android כמפתח אבטחה</translation> +<translation id="9200339982498053969">ל-<ph name="ORIGIN" /> תהיה הרשאה לערוך את הקבצים בתיקייה <ph name="FOLDERNAME" /></translation> <translation id="920045321358709304">חיפוש ב-<ph name="SEARCH_ENGINE" /></translation> <translation id="9201023452444595544">המערכת תמחק את כל הנתונים שנשמרו במצב אופליין</translation> <translation id="9201220332032049474">אפשרויות נעילת מסך</translation>
diff --git a/chrome/app/resources/generated_resources_lo.xtb b/chrome/app/resources/generated_resources_lo.xtb index 3cd33f6f..202606b8 100644 --- a/chrome/app/resources/generated_resources_lo.xtb +++ b/chrome/app/resources/generated_resources_lo.xtb
@@ -237,6 +237,7 @@ <translation id="1243436884219965846">ກວດເບິ່ງລະຫັດຜ່ານ</translation> <translation id="1244265436519979884">ການກູ້ຄືນຂໍ້ມູນຂອງ Linux ພວມດຳເນີນຢູ່ໃນປັດຈຸບັນ</translation> <translation id="1244303850296295656">ສ່ວນຂະຫຍາຍຜິດພາດ</translation> +<translation id="1246863218384630739">ບໍ່ສາມາດຕິດຕັ້ງ <ph name="VM_NAME" /> ໄດ້: URL ຮູບພາບຕອບລະຫັດຜິດພາດ <ph name="HTTP_ERROR" /> ກັບມາ. ກະລຸນາຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ.</translation> <translation id="1246905108078336582">ລຶບການແນະນຳອອກຈາກຄລິບບອດບໍ?</translation> <translation id="1251366534849411931">ເຄື່ອງໝາຍວົງປີກກາເປີດທີ່ຕ້ອງມີ: <ph name="ERROR_LINE" /></translation> <translation id="1251480783646955802">ນີ້ຈະລຶບລ້າງຂໍ້ມູນ <ph name="TOTAL_USAGE" /> ທີ່ເກັບໄວ້ໂດຍເວັບໄຊ ແລະ ແອັບທີ່ຕິດຕັ້ງໄວ້</translation> @@ -1534,6 +1535,7 @@ <translation id="2610260699262139870">ຂະຫນາດຕົວຈິງ</translation> <translation id="2610780100389066815">ການລົງຊື່ເຂົ້າໃຊ້ລາຍຊື່ເຊື່ອໝັ້ນຂອງ Microsoft</translation> <translation id="2612676031748830579">ເລກບັດ</translation> +<translation id="2613535083491958306"><ph name="ORIGIN" /> ຈະສາມາດແກ້ໄຂ <ph name="FILENAME" /> ໄດ້</translation> <translation id="2616366145935564096">ອ່ານ ແລະປ່ຽນແປງຂໍ້ມູນຂອງທ່ານຢູ່ເທິງ <ph name="WEBSITE_1" /></translation> <translation id="2617342710774726426">ແຜ່ນ SIM ລັອກແລ້ວ</translation> <translation id="2618797463720777311">ຕັ້ງຄ່າການແບ່ງປັນໃກ້ຄຽງ</translation> @@ -2292,6 +2294,7 @@ <translation id="3441653493275994384">ຫນ້າຈໍ</translation> <translation id="3441663102605358937">ເຂົ້າສູ່ລະບົບ <ph name="ACCOUNT" /> ອີກຄັ້ງເພື່ອຢັ້ງຢືນບັນຊີນີ້</translation> <translation id="3444641828375597683">ຜູ້ລົງໂຄສະນາ ແລະ ຜູ້ເຜີຍແຜ່ສາມາດໃຊ້ FLoC ໄດ້, ຕາມທີ່ອະທິບາຍພາຍຫຼັງໃນໜ້ານີ້.</translation> +<translation id="3444726579402183581"><ph name="ORIGIN" /> ຈະສາມາດເບິ່ງ <ph name="FILENAME" /> ໄດ້</translation> <translation id="3445047461171030979">ຄຳຕອບດ່ວນຂອງຜູ້ຊ່ວຍ Google</translation> <translation id="3445288400492335833"><ph name="MINUTES" /> ນາທີ</translation> <translation id="3445925074670675829">ອຸປະກອນ USB-C</translation> @@ -3790,6 +3793,7 @@ <translation id="5086874064903147617">ກູ້ຄືນໜ້າຫຼັກເລີ່ມຕົ້ນບໍ?</translation> <translation id="5087249366037322692">ເພີ່ມໂດຍພາກສ່ວນທີສາມ</translation> <translation id="5087580092889165836">ເພີ່ມບັດ</translation> +<translation id="5087748406101774740"><ph name="APP_NAME" /> (<ph name="PROFILE_NAME" />)</translation> <translation id="5088534251099454936">PKCS #1 SHA-512 ດ້ວຍການໃສ່ລະຫັດ RSA</translation> <translation id="5090637338841444533">ບໍ່ໄດ້ຮັບອະນຸຍາດໃຫ້ຕິດຕາມຕຳແໜ່ງກ້ອງຂອງທ່ານ</translation> <translation id="5094721898978802975">ສື່ສານກັບແອັບພລິເຄຊັນເດີມທີ່ຮ່ວມມື</translation> @@ -4570,6 +4574,7 @@ <translation id="5964113968897211042">{COUNT,plural, =0{ເປີດທັງໝົດໃນ&ໜ້າຈໍໃໝ່}=1{ເປີດໃນ&ໜ້າຈໍໃໝ່}other{ເປີດທັງໝົດ ({COUNT}) ໃນ&ໜ້າຈໍໃໝ່}}</translation> <translation id="5965661248935608907">ມັນຍັງຄວບຄຸມວ່າຈະໃຫ້ສະແດງໜ້າໃດຂຶ້ນ ເມື່ອທ່ານຄລິກປຸ່ມໜ້າຫຼັກ ຫຼືຄົ້ນຫາຈາກ Omnibox.</translation> <translation id="5969419185858894314"><ph name="ORIGIN" /> ສາມາດເບິ່ງໄຟລ໌ໃນ <ph name="FOLDERNAME" /> ໄດ້</translation> +<translation id="5969728632630673489">ປິດປະກາດຄີລັດແລ້ວ</translation> <translation id="5971037678316050792">ຄວບຄຸມສະພາວະອາແດັບເຕີ ແລະການຈັບຄູ່ Bluetooth</translation> <translation id="597235323114979258">ເບິ່ງຈຸດໝາຍປາຍທາງເພີ່ມເຕີມ</translation> <translation id="5972666587303800813">ການບໍລິການທີ່ບໍ່ມີການດຳເນີນການ</translation> @@ -6205,6 +6210,7 @@ <translation id="7766807826975222231">ທ່ອງເບິ່ງ</translation> <translation id="7766838926148951335">ຍອມຮັບການອະນຸຍາດ</translation> <translation id="7768507955883790804">ເວັບໄຊຈະປະຕິບັດຕາມການຕັ້ງຄ່ານີ້ໂດຍອັດຕະໂນມັດເມື່ອທ່ານເຂົ້າເບິ່ງພວກມັນ</translation> +<translation id="7768526219335215384"><ph name="ORIGIN" /> ຈະສາມາດເບິ່ງໄຟລ໌ໃນ <ph name="FOLDERNAME" /> ໄດ້</translation> <translation id="7768770796815395237">ປ່ຽນ</translation> <translation id="7768784765476638775">ເລືອກເພື່ອເວົ້າ</translation> <translation id="7770612696274572992">ສຳເນົາຮູບຈາກອຸປະກອນອື່ນແລ້ວ</translation> @@ -6507,6 +6513,7 @@ <translation id="8063235345342641131">ຮູບແທນຕົວສີຂຽວຕາມຄ່າເລີ່ມຕົ້ນ</translation> <translation id="8063535366119089408">ເບິ່ງໄຟລ໌</translation> <translation id="8064279191081105977">ກຸ່ມ <ph name="GROUP_NAME" /> - <ph name="GROUP_CONTENTS" /> - <ph name="COLLAPSED_STATE" /></translation> +<translation id="8066444921260601116">ກ່ອງໂຕ້ຕອບການເຊື່ອມຕໍ່</translation> <translation id="8068253693380742035">ສຳຜັດເພື່ອເຂົ້າສູ່ລະບົບ</translation> <translation id="8069615408251337349">Google Cloud Print</translation> <translation id="8071432093239591881">ພິມເປັນຮູບພາບ</translation> @@ -7537,6 +7544,7 @@ <translation id="9186963452600581158">ເຂົ້າສູ່ລະບົບດ້ວຍບັນຊີ Google ຂອງເດັກ</translation> <translation id="9188732951356337132">ສົ່ງຂໍ້ມູນການນຳໃຊ້ ແລະ ການວິເຄາະ. ອຸປະກອນນີ້ກຳລັງສົ່ງຂໍ້ມູນການວິເຄາະ, ຂໍ້ມູນອຸປະກອນ ແລະ ການໃຊ້ແອັບໃຫ້ Google ໂດຍອັດຕະໂນມັດໃນຂະນະນີ້. ຂໍ້ມູນນີ້ຈະບໍ່ຖືກໃຊ້ເພື່ອລະບຸຕົວລູກຂອງທ່ານ ແລະ ຈະຊ່ວຍປັບປຸງຄວາມສະຖຽນຂອງລະບົບ ແລະ ແອັບ ແລະ ການປັບປຸງອື່ນໆ. ບາງຂໍ້ມູນແບບຮວມກັນຍັງຈະຊ່ວຍແອັບ ແລະ ຮຸ້ນສ່ວນຂອງ Google ນຳອີກ ເຊັ່ນ: ຜູ້ພັດທະນາ Android. ຖ້າເປີດການຕັ້ງຄ່າການເຄື່ອນໄຫວເວັບ ແລະ ແອັບເພີ່ມເຕີມສໍາລັບລູກຂອງທ່ານ, ລະບົບອາດຈະບັນທຶກຂໍ້ມູນນີ້ໄວ້ໃນບັນຊີ Google ຂອງເຂົາເຈົ້າ. <ph name="BEGIN_LINK2" />ສຶກສາເພີ່ມເຕີມ<ph name="END_LINK2" /></translation> <translation id="9198090666959937775">ໃຊ້ໂທລະສັບ Android ຂອງທ່ານເປັນກະແຈຄວາມປອດໄພ</translation> +<translation id="9200339982498053969"><ph name="ORIGIN" /> ຈະສາມາດແກ້ໄຂໄຟລ໌ໃນ <ph name="FOLDERNAME" /> ໄດ້</translation> <translation id="920045321358709304">ຊອກຫາ <ph name="SEARCH_ENGINE" /></translation> <translation id="9201023452444595544">ລະບົບຈະລຶບລ້າງຂໍ້ມູນອອບລາຍຕ່າງໆ</translation> <translation id="9201220332032049474">ຕົວເລືອກການລັອກໜ້າຈໍ</translation>
diff --git a/chrome/app/resources/generated_resources_mn.xtb b/chrome/app/resources/generated_resources_mn.xtb index b4383ca2..2cf3a06 100644 --- a/chrome/app/resources/generated_resources_mn.xtb +++ b/chrome/app/resources/generated_resources_mn.xtb
@@ -237,6 +237,7 @@ <translation id="1243436884219965846">Нууц үгсээ шалгах</translation> <translation id="1244265436519979884">Linux-г одоогоор сэргээж байна</translation> <translation id="1244303850296295656">Өргөтгөлийн алдаа</translation> +<translation id="1246863218384630739"><ph name="VM_NAME" />-г суулгаж чадсангүй: Зургийн URL <ph name="HTTP_ERROR" /> алдааны кодыг буцаасан. Админтайгаа холбогдоно уу.</translation> <translation id="1246905108078336582">Түр санах ойгоос зөвлөмжийг устгах уу?</translation> <translation id="1251366534849411931">Хээтэй хаалтыг нээхээр хүлээж байсан <ph name="ERROR_LINE" /></translation> <translation id="1251480783646955802">Энэ нь сайтууд болон суулгасан аппуудын хадгалсан <ph name="TOTAL_USAGE" />-н өгөгдлийг арилгана</translation> @@ -1533,6 +1534,7 @@ <translation id="2610260699262139870">Бодит хэмжээ</translation> <translation id="2610780100389066815">Microsoft-ын Итгэлцлийн Жагсаалтад гарын үсэг зурах явц</translation> <translation id="2612676031748830579">Картын дугаар</translation> +<translation id="2613535083491958306"><ph name="ORIGIN" /> <ph name="FILENAME" />-г засах боломжтой болно</translation> <translation id="2616366145935564096"><ph name="WEBSITE_1" /> дээрх мэдээллээ уших, өөрчлөх</translation> <translation id="2617342710774726426">SIM карт түгжигдсэн</translation> <translation id="2618797463720777311">Ойролцоо хуваалцах онцлогийг тохируулах</translation> @@ -2291,6 +2293,7 @@ <translation id="3441653493275994384">Дэлгэц</translation> <translation id="3441663102605358937">Энэ бүртгэлийг баталгаажуулахын тулд <ph name="ACCOUNT" />-д нэвтэрнэ үү</translation> <translation id="3444641828375597683">Сурталчлагчид болон нийтлэгчид FLoC-г дараа энэ хуудсанд тайлбарласны дагуу ашиглах боломжтой.</translation> +<translation id="3444726579402183581"><ph name="ORIGIN" /> <ph name="FILENAME" />-г үзэх боломжтой болно</translation> <translation id="3445047461171030979">Google Туслахын шуурхай хариултууд</translation> <translation id="3445288400492335833"><ph name="MINUTES" /> мин</translation> <translation id="3445925074670675829">USB-C төхөөрөмж</translation> @@ -3788,6 +3791,7 @@ <translation id="5086874064903147617">Өгөгдмөл нүүр хуудсыг идэвхжүүлэх үү?</translation> <translation id="5087249366037322692">Гуравдагч талаас нэмсэн</translation> <translation id="5087580092889165836">Карт нэмэх</translation> +<translation id="5087748406101774740"><ph name="APP_NAME" /> (<ph name="PROFILE_NAME" />)</translation> <translation id="5088534251099454936">PKCS #1 SHA-512 With RSA Encryption</translation> <translation id="5090637338841444533">Таны камерын байрлалыг хянахыг зөвшөөрөөгүй</translation> <translation id="5094721898978802975">Хамтарч буй суурин апп-уудтай харилцах</translation> @@ -4568,6 +4572,7 @@ <translation id="5964113968897211042">{COUNT,plural, =0{Бүгдийг &шинэ цонхонд нээх}=1{&Шинэ цонхонд нээх}other{Бүгдийг ({COUNT}) &шинэ цонхонд нээх}}</translation> <translation id="5965661248935608907">Энэ нь таныг Home Товчийг дарах эсвэл өмнө нь хайж байсан линкүүд дотроос айлт хийж байх үед гарч ирэх хуудсыг мөн хянадаг.</translation> <translation id="5969419185858894314"><ph name="ORIGIN" /> нь <ph name="FOLDERNAME" />-н файлыг харах боломжтой</translation> +<translation id="5969728632630673489">Товчлуурын шууд холбоосны мэдэгдлийг хаасан</translation> <translation id="5971037678316050792">Bluetooth адаптерийн статус болон хослолийг хянах</translation> <translation id="597235323114979258">Бусад очих газрыг харах</translation> <translation id="5972666587303800813">Ажилладаггүй үйлчилгээ</translation> @@ -6203,6 +6208,7 @@ <translation id="7766807826975222231">Аялцгаая</translation> <translation id="7766838926148951335">Зөвшөөрлийг зөвшөөрөх</translation> <translation id="7768507955883790804">Таныг сайт руу зочлоход энэ тохиргоог сайт автоматаар дагана</translation> +<translation id="7768526219335215384"><ph name="ORIGIN" /> <ph name="FOLDERNAME" /> дахь файлуудыг үзэх боломжтой болно</translation> <translation id="7768770796815395237">Өөрчлөх</translation> <translation id="7768784765476638775">Ярихаар-сонгох</translation> <translation id="7770612696274572992">Өөр төхөөрөмжөөс хуулсан зураг</translation> @@ -6504,6 +6510,7 @@ <translation id="8063235345342641131">Өгөгдмөл ногоон аватар</translation> <translation id="8063535366119089408">Файлыг харах</translation> <translation id="8064279191081105977"><ph name="GROUP_NAME" /> бүлэг - <ph name="GROUP_CONTENTS" /> - <ph name="COLLAPSED_STATE" /></translation> +<translation id="8066444921260601116">Холболтын харилцах цонх</translation> <translation id="8068253693380742035">Нэвтрэхийн тулд хүрнэ үү</translation> <translation id="8069615408251337349">Google Cloud Print</translation> <translation id="8071432093239591881">Зургаар хэвлэх</translation> @@ -7531,6 +7538,7 @@ <translation id="9186963452600581158">Хүүхдийн Google Бүртгэлээр нэвтрэх</translation> <translation id="9188732951356337132">Ашиглалт болон оношилгооны өгөгдлийг илгээнэ үү. Энэ төхөөрөмж одоогоор оношилгоо, төхөөрөмж болон аппын ашиглалтын өгөгдлийг Google-д автоматаар илгээж байна. Үүнийг таны хүүхдийг тодорхойлоход ашиглахгүй бөгөөд энэ нь систем, аппын тогтвортой байдал болон бусад зүйлийг сайжруулахад тусална. Зарим хуримтлуулсан өгөгдөл нь Google аппууд болон Android хөгжүүлэгч зэрэг түншүүдэд мөн адил тусална. Хэрэв таны хүүхдийн Веб, Аппын нэмэлт үйл ажиллагааны тохиргоо асаалттай байвал энэ өгөгдлийг тэдний Google Бүртгэлд хадгалж болзошгүй. <ph name="BEGIN_LINK2" />Нэмэлт мэдээлэл авах<ph name="END_LINK2" /></translation> <translation id="9198090666959937775">Android утсаа аюулгүй байдлын түлхүүрээр ашиглана уу</translation> +<translation id="9200339982498053969"><ph name="ORIGIN" /> <ph name="FOLDERNAME" /> дахь файлуудыг засах боломжтой болно</translation> <translation id="920045321358709304"><ph name="SEARCH_ENGINE" /> хайх</translation> <translation id="9201023452444595544">Офлайн аливаа өгөгдлийг устгах болно</translation> <translation id="9201220332032049474">Дэлгэцийн түгжээний сонголт</translation>
diff --git a/chrome/app/resources/generated_resources_ne.xtb b/chrome/app/resources/generated_resources_ne.xtb index 1ad9384..dcb3de2 100644 --- a/chrome/app/resources/generated_resources_ne.xtb +++ b/chrome/app/resources/generated_resources_ne.xtb
@@ -2438,7 +2438,7 @@ <translation id="3619115746895587757">कापाचिनो</translation> <translation id="362266093274784978">{COUNT,plural, =1{एउटा एप}other{# वटा एप}}</translation> <translation id="362333465072914957">CA ले प्रमाणपत्र जारी गर्ने प्रतीक्षा गर्दै</translation> -<translation id="3624567683873126087">यन्त्र अनलक गरी Google खातामा साइन इन गर्नुहोस्।</translation> +<translation id="3624567683873126087">डिभाइस अनलक गरी Google खातामा साइन इन गर्नुहोस्।</translation> <translation id="3625481642044239431">अमान्य फाइल चयन गरियो। फेरि प्रयास गर्नुहोस्।</translation> <translation id="3626296069957678981">यो Chromebook चार्ज गर्न यससँग मिल्दो Dell ब्याट्री प्रयोग गर्नुहोस्।</translation> <translation id="3627320433825461852">1 मिनेट भन्दा कम समय बाँकी</translation> @@ -3151,7 +3151,7 @@ <translation id="4406883609789734330">लाइभ क्याप्सन</translation> <translation id="4408599188496843485">म&द्दत</translation> <translation id="4409697491990005945">सीमान्तहरू</translation> -<translation id="4410545552906060960">आफ्नो यन्त्र अनलक गर्न पासवर्डको सट्टा सङ्ख्या (PIN) प्रयोग गर्नुहोस्। पछि आफ्नो PIN सेट गर्न सेटिङहरूमा जानुहोस्</translation> +<translation id="4410545552906060960">आफ्नो डिभाइस अनलक गर्न पासवर्डको सट्टा सङ्ख्या (PIN) प्रयोग गर्नुहोस्। पछि आफ्नो PIN सेट गर्न सेटिङहरूमा जानुहोस्</translation> <translation id="4411578466613447185">कोड हस्ताक्षरकर्ता</translation> <translation id="4412698727486357573">मद्दत केन्द्र</translation> <translation id="4413088271097062326">तपाईं कुन एप चलाउन चाहनुहुन्छ?</translation> @@ -4042,7 +4042,7 @@ <translation id="5414566801737831689">तपाईंले भ्रमण गर्नुहुने वेबसाइटहरूको प्रतिमा पढ्नुहोस्</translation> <translation id="5417312524372586921">ब्राउजरका विषयवस्तुहरू</translation> <translation id="5419405654816502573">Voice Match</translation> -<translation id="5420274697768050645">थप सुरक्षाका निम्ति यन्त्र अनलक गर्न पासवर्ड प्रविष्टि गर्नु पर्ने हुन्छ</translation> +<translation id="5420274697768050645">थप सुरक्षाका निम्ति डिभाइस अनलक गर्न पासवर्ड प्रविष्टि गर्नु पर्ने हुन्छ</translation> <translation id="5420438158931847627">पाठ तथा छविहरूको तिखोपना निर्धारण गर्छ</translation> <translation id="5422221874247253874">पहुँचको बिन्दु</translation> <translation id="5422781158178868512">माफ गर्नुहोस्, तपाइँको बाह्य भण्डारण चिनिन सकिएन।</translation> @@ -5483,7 +5483,7 @@ <translation id="7002055706763150362">Chromebook का लागि स्मार्ट लक सेटअप गर्न Google ले यो तपाईं नै हो भनेर सुनिश्चित गर्नुपर्छ— सुरु गर्न तपाईंको पासवर्ड टाइप गर्नुहोस्।</translation> <translation id="7003339318920871147">वेब डाटाबेसहरू</translation> <translation id="7003454175711353260">{COUNT,plural, =1{{COUNT} वटा फाइल}other{{COUNT} वटा फाइल}}</translation> -<translation id="7003723821785740825">आफ्नो यन्त्र अनलक गर्ने अझ द्रुत तरिका सेटअप गर्नुहोस्</translation> +<translation id="7003723821785740825">आफ्नो डिभाइस अनलक गर्ने अझ द्रुत तरिका सेटअप गर्नुहोस्</translation> <translation id="7003844668372540529"><ph name="VENDOR_NAME" /> बाट अज्ञात उत्पादन <ph name="PRODUCT_ID" /></translation> <translation id="7004402701596653846">साइटले MIDI प्रयोग गर्न सक्छ</translation> <translation id="7004499039102548441">हालका ट्याबहरू</translation> @@ -6787,7 +6787,7 @@ <translation id="8422787418163030046">ट्रे छैन</translation> <translation id="8425213833346101688">परिवर्तन गर्नुहोस्</translation> <translation id="8425492902634685834">कार्यपट्टीमा पिन गर्नुहोस्</translation> -<translation id="8425768983279799676">तपाईं आफ्नो यन्त्र अनलक गर्न आफ्नो PIN प्रयोग गर्न सक्नुहुन्छ।</translation> +<translation id="8425768983279799676">तपाईं आफ्नो डिभाइस अनलक गर्न आफ्नो PIN प्रयोग गर्न सक्नुहुन्छ।</translation> <translation id="8426713856918551002">सक्षम पार्दै</translation> <translation id="8427292751741042100">कुनै पनि होस्टमा इम्बेड गरिएको</translation> <translation id="8428213095426709021">सेटिङहरू</translation>
diff --git a/chrome/app/resources/generated_resources_si.xtb b/chrome/app/resources/generated_resources_si.xtb index fb35cab..f95e5ab6 100644 --- a/chrome/app/resources/generated_resources_si.xtb +++ b/chrome/app/resources/generated_resources_si.xtb
@@ -238,6 +238,7 @@ <translation id="1243436884219965846">මුරපද සමාලෝචනය</translation> <translation id="1244265436519979884">ලිනක්ස් ප්රතිසාධනය දැන් සිදු වෙමින් පවතියි</translation> <translation id="1244303850296295656">දිගු දෝෂය</translation> +<translation id="1246863218384630739"><ph name="VM_NAME" /> ස්ථාපනය කළ නොහැකි විය: රූප URL <ph name="HTTP_ERROR" /> දෝෂ කේතයක් ලබා දුණි. කරුණාකර ඔබගේ පරිපාලක සම්බන්ධ කර ගන්න.</translation> <translation id="1246905108078336582">පසුරු පුවරුවෙන් යෝජනාව ඉවත් කරන්නේද?</translation> <translation id="1251366534849411931">අපේක්ෂිත විවෘත රැළි කඹය: <ph name="ERROR_LINE" /></translation> <translation id="1251480783646955802">මෙය වෙබ් අඩවිවලින් සහ ස්ථාපිත යෙදුම්වලින් ගබඩා කරනු ලැබූ දත්ත <ph name="TOTAL_USAGE" /> ක් හිස් කරයි</translation> @@ -1526,6 +1527,7 @@ <translation id="2610260699262139870">සැබෑ ප්රමාණය</translation> <translation id="2610780100389066815">Microsoft විශ්වාසදායී ලැයිස්තු ප්රවිෂ්ටය</translation> <translation id="2612676031748830579">කාඩ්පත් අංකය</translation> +<translation id="2613535083491958306"><ph name="ORIGIN" /> හට <ph name="FILENAME" /> සංස්කරණය කිරීමට හැකි වනු ඇත</translation> <translation id="2616366145935564096"><ph name="WEBSITE_1" /> හි ඔබගේ දත්ත කියවා වෙනස් කරන්න</translation> <translation id="2617342710774726426">SIM පත අගුළු වැටී ඇත</translation> <translation id="2618797463720777311">ළඟ බෙදා ගැනීම පිහිටුවන්න</translation> @@ -2283,6 +2285,7 @@ <translation id="3441653493275994384">තිරය</translation> <translation id="3441663102605358937">මෙම ගිණුම සත්යාපනය කිරීමට <ph name="ACCOUNT" /> වෙත නැවත පුරන්න</translation> <translation id="3444641828375597683">වෙළඳ ප්රචාරකයන්ට සහ ප්රකාශකයින්ට, මෙම පිටුවේ පසුව විස්තර කර ඇති FLoC භාවිත කළ හැකිය.</translation> +<translation id="3444726579402183581"><ph name="ORIGIN" /> හට <ph name="FILENAME" /> බැලීමට හැකි වනු ඇත</translation> <translation id="3445047461171030979">Google සහකරු ක්ෂණික පිළිතුරු</translation> <translation id="3445288400492335833">මිනි <ph name="MINUTES" /></translation> <translation id="3445925074670675829">USB-C උපාංගය</translation> @@ -3781,6 +3784,7 @@ <translation id="5086874064903147617">පෙරනිමි මුල් පිටුව ප්රතිසාධනය කරනවා ද?</translation> <translation id="5087249366037322692">තුන් වන පාර්ශ්වයක් විසින් එක් කර ඇත</translation> <translation id="5087580092889165836">කාඩ්පත එක් කරන්න</translation> +<translation id="5087748406101774740"><ph name="APP_NAME" /> (<ph name="PROFILE_NAME" />)</translation> <translation id="5088534251099454936">PKCS #1 SHA-512 With RSA සංකේතනය</translation> <translation id="5090637338841444533">ඔබගේ කැමරා පිහිටුම හඹා යාමට ඉඩ නොදේ</translation> <translation id="5094721898978802975">සහය දක්වන දේශීය යෙදුම් හා සන්නිවේදනය කරන්න</translation> @@ -4562,6 +4566,7 @@ <translation id="5964113968897211042">{COUNT,plural, =0{සියල්ල &නව කවුළුවක විවෘත කරන්න}=1{&නව කවුළුවක විවෘත කරන්න}one{සියල්ල ({COUNT}) &නව කවුළුවක විවෘත කරන්න}other{සියල්ල ({COUNT}) &නව කවුළුවක විවෘත කරන්න}}</translation> <translation id="5965661248935608907">ඔබ මුල්පිටු බොත්තම ක්ලික් කළ විට හෝ Omnibox වෙතින් සොයන විට කුමන පිටුව පෙන්වන්නේද යන්නද එය පාලනය කරයි.</translation> <translation id="5969419185858894314"><ph name="ORIGIN" /> හට <ph name="FOLDERNAME" /> තුළ ඇති ගොනු බැලිය හැක</translation> +<translation id="5969728632630673489">යතුරු පුවරු කෙටි මං දැන්වීම ඉවත ලන ලදි</translation> <translation id="5971037678316050792">Bluetooth ඇඩැප්ටර තත්වය හා යුගල කිරීම</translation> <translation id="597235323114979258">තවත් ගමනාන්ත බලන්න</translation> <translation id="5972666587303800813">ඇතුළු නොවීම් සේවය</translation> @@ -6195,6 +6200,7 @@ <translation id="7766807826975222231">සංචාරයක් කරන්න</translation> <translation id="7766838926148951335">අවසර පිළිගන්න</translation> <translation id="7768507955883790804">ඔබ මෙම අඩවිවලට පිවිසෙන විට ඒවා ස්වයංක්රීයව මෙම සැකසීම අනුගමනය කරයි</translation> +<translation id="7768526219335215384"><ph name="ORIGIN" /> හට <ph name="FOLDERNAME" /> තුළ ඇති සියලු ගොනු බැලීමට හැකි වනු ඇත.</translation> <translation id="7768770796815395237">වෙනස</translation> <translation id="7768784765476638775">කීමට-තේරීම</translation> <translation id="7770612696274572992">අනෙකුත් උපාංගයෙන් පිටපත් කළ රූපය</translation> @@ -6496,6 +6502,7 @@ <translation id="8063235345342641131">පෙරනිමි කොළ නියුරුව</translation> <translation id="8063535366119089408">ගොනුව බලන්න</translation> <translation id="8064279191081105977">සමූහය <ph name="GROUP_NAME" /> - <ph name="GROUP_CONTENTS" /> - <ph name="COLLAPSED_STATE" /></translation> +<translation id="8066444921260601116">සබැඳුම් සංවාදය</translation> <translation id="8068253693380742035">පිරීමට ස්පර්ශ කරන්න</translation> <translation id="8069615408251337349">Google Cloud මුද්රණය</translation> <translation id="8071432093239591881">රූපයක් ලෙස මුද්රණය කරන්න</translation> @@ -7527,6 +7534,7 @@ <translation id="9186963452600581158">ඔබේ ළමයාගේ Google ගිණුම සමග පුරන්න</translation> <translation id="9188732951356337132">භාවිත සහ දෝෂ නිර්ණ දත්ත යවන්න. මෙම උපාංගය ස්වයංක්රියව රෝග විනිශ්චය, උපාංග සහ යෙදුම් භාවිත දත්ත Google වෙත යවයි. මෙය ඔබේ දරුවා හඳුනා ගැනීමට භාවිත නොකෙරෙන අතර, පද්ධති සහ යෙදුම් ස්ථායිතාවට සහ අනෙකුත් වැඩිදියුණු කිරීම්වලට උදවු කරයි. සමහර එකතු කළ දත්ත Google යෙදුම්වලට සහ Android සංවර්ධකයින් වැනි හවුල්කරුවන්ට ද උදවු කරති. ඔබේ දරුවා සඳහා අමතර වෙබ් සහ යෙදුම් ක්රියාකාරකම් සැකසීම සක්රීය කර තිබේ නම්, මෙම දත්ත ඔවුන්ගේ Google ගිණුමට සුරැකිය හැක. <ph name="BEGIN_LINK2" />තවත් දැන ගන්න<ph name="END_LINK2" /></translation> <translation id="9198090666959937775">ආරක්ෂක යතුරක් ලෙස ඔබගේ Android දුරකථනය භාවිත කරන්න</translation> +<translation id="9200339982498053969"><ph name="ORIGIN" /> හට <ph name="FOLDERNAME" /> තුළ ගොනු සංස්කරණය කිරීමට හැකි වනු ඇත</translation> <translation id="920045321358709304"><ph name="SEARCH_ENGINE" /> සෙවීම</translation> <translation id="9201023452444595544">නොබැඳි දත්ත කිසිවක් හිස් කෙරේ</translation> <translation id="9201220332032049474">තිර අගුළු විකල්ප</translation>
diff --git a/chrome/app/resources/generated_resources_sw.xtb b/chrome/app/resources/generated_resources_sw.xtb index 9413e76..e83e2b0 100644 --- a/chrome/app/resources/generated_resources_sw.xtb +++ b/chrome/app/resources/generated_resources_sw.xtb
@@ -237,6 +237,7 @@ <translation id="1243436884219965846">Kagua manenosiri</translation> <translation id="1244265436519979884">Shughuli ya kupakia metadata ya Linux inaendelea wakati huu</translation> <translation id="1244303850296295656">Hitilafu ya kiendelezi</translation> +<translation id="1246863218384630739">Imeshindwa kusakinisha <ph name="VM_NAME" />: URL ya picha imeleta msimbo wa hitilafu wa <ph name="HTTP_ERROR" />. Tafadhali wasiliana na msimamizi wako.</translation> <translation id="1246905108078336582">Je, ungependa kuondoa pendekezo kwenye ubao wa kunakili?</translation> <translation id="1251366534849411931">Hamna mchirizi wa kufungua: <ph name="ERROR_LINE" /></translation> <translation id="1251480783646955802">Hatua hii itafuta <ph name="TOTAL_USAGE" /> za data iliyohifadhiwa na tovuti na programu zilizosakinishwa</translation> @@ -1533,6 +1534,7 @@ <translation id="2610260699262139870">Ukubwa Halisi</translation> <translation id="2610780100389066815">Uwekaji Sahihi wa Orodha ya Zinazoaminiwa kutoka Microsoft</translation> <translation id="2612676031748830579">Nambari ya kadi</translation> +<translation id="2613535083491958306"><ph name="ORIGIN" /> itaweza kubadilisha <ph name="FILENAME" /></translation> <translation id="2616366145935564096">Kusoma na kubadilisha data yako kwenye <ph name="WEBSITE_1" /></translation> <translation id="2617342710774726426">SIM kadi imefungwa</translation> <translation id="2618797463720777311">Weka mipangilio ya Uhamishaji wa Karibu</translation> @@ -2291,6 +2293,7 @@ <translation id="3441653493275994384">Skrini</translation> <translation id="3441663102605358937">Ingia katika <ph name="ACCOUNT" /> tena ili uthibitishe akaunti hii</translation> <translation id="3444641828375597683">Watangazaji na wachapishaji wanaweza kutumia teknolojia ya FLoC, iliyofafanuliwa baadaye kwenye ukurasa huu.</translation> +<translation id="3444726579402183581"><ph name="ORIGIN" /> itaweza kuangalia <ph name="FILENAME" /></translation> <translation id="3445047461171030979">Majibu ya haraka ya programu ya Mratibu wa Google</translation> <translation id="3445288400492335833">Dakika <ph name="MINUTES" /></translation> <translation id="3445925074670675829">Kifaa cha USB-C</translation> @@ -3787,6 +3790,7 @@ <translation id="5086874064903147617">Ungependa kurejesha ukurasa wa kwanza chaguomsingi?</translation> <translation id="5087249366037322692">Kimeongezwa na mhusika mwingine</translation> <translation id="5087580092889165836">Ongeza kadi</translation> +<translation id="5087748406101774740"><ph name="APP_NAME" /> (<ph name="PROFILE_NAME" />)</translation> <translation id="5088534251099454936">PKCS #1 SHA-512 Na Usimbaji wa RSA</translation> <translation id="5090637338841444533">Zisizoruhusiwa kufuatilia mkao wa kamera yako</translation> <translation id="5094721898978802975">Kuwasiliana na programu za asili zinazoshirikiana</translation> @@ -4567,6 +4571,7 @@ <translation id="5964113968897211042">{COUNT,plural, =0{Fungua zote katika &dirisha jipya}=1{Fungua katika &dirisha jipya}other{Fungua zote ({COUNT}) katika &dirisha jipya}}</translation> <translation id="5965661248935608907">Pia inadhibiti ukurasa unaoonyeshwa unapobofya kitufe cha Mwanzo au unapotafuta kutoka Sanduku Kuu.</translation> <translation id="5969419185858894314"><ph name="ORIGIN" /> inaweza kuona faili katika <ph name="FOLDERNAME" /></translation> +<translation id="5969728632630673489">Ilani kuhusu mikato ya kibodi imeondolewa</translation> <translation id="5971037678316050792">Dhibiti hali na uoanishaji wa adapta ya Bluetooth</translation> <translation id="597235323114979258">Ona maeneo zaidi</translation> <translation id="5972666587303800813">Huduma ya No-op</translation> @@ -6202,6 +6207,7 @@ <translation id="7766807826975222231">Kagua</translation> <translation id="7766838926148951335">Kubali ruhusa</translation> <translation id="7768507955883790804">Tovuti hufuata mipangilio hii kiotomatiki unapozitembelea</translation> +<translation id="7768526219335215384"><ph name="ORIGIN" /> itaweza kuangalia faili zilizo katika <ph name="FOLDERNAME" /></translation> <translation id="7768770796815395237">Badilisha</translation> <translation id="7768784765476638775">Chagua ili izungumze</translation> <translation id="7770612696274572992">Picha imenakiliwa kutoka kwenye kifaa kingine</translation> @@ -6508,6 +6514,7 @@ <translation id="8063235345342641131">Ishara chaguomsingi ya kijani</translation> <translation id="8063535366119089408">Angalia faili</translation> <translation id="8064279191081105977">Kikundi cha <ph name="GROUP_NAME" /> - <ph name="GROUP_CONTENTS" /> - <ph name="COLLAPSED_STATE" /></translation> +<translation id="8066444921260601116">Kidirisha cha Muunganisho</translation> <translation id="8068253693380742035">Gusa ili uingie katika akaunti</translation> <translation id="8069615408251337349">Google Cloud Print</translation> <translation id="8071432093239591881">Chapisha kuwa picha</translation> @@ -7538,6 +7545,7 @@ <translation id="9186963452600581158">Ingia ukitumia Akaunti ya Google ya mtoto</translation> <translation id="9188732951356337132">Tuma data ya matumizi na uchunguzi. Kwa sasa, kifaa hiki kinatuma kiotomatiki data ya uchunguzi na matumizi ya programu na kifaa kwa Google. Hatutatumia hatua hii kumtambulisha mtoto wako na itatusaidia kuboresha uthabiti wa programu na mfumo na maboresho mengine. Baadhi ya maelezo yaliyojumlishwa pia yatasaidia programu za Google na washirika kama vile wasanidi programu za Android. Ikiwa umewasha mipangilio ya historia ya Shughuli za ziada kwenye Wavuti na Programu ya mtoto wako, data hii inaweza kuhifadhiwa kwenye Akaunti yake ya Google. <ph name="BEGIN_LINK2" />Pata maelezo zaidi<ph name="END_LINK2" /></translation> <translation id="9198090666959937775">Tumia simu yako ya Android kama ufunguo wa usalama</translation> +<translation id="9200339982498053969"><ph name="ORIGIN" /> itaweza kubadilisha faili zilizo katika <ph name="FOLDERNAME" /></translation> <translation id="920045321358709304">Tafuta <ph name="SEARCH_ENGINE" /></translation> <translation id="9201023452444595544">Data yoyote nje ya mtandao itafutwa</translation> <translation id="9201220332032049474">Chaguo za kufunga skrini</translation>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 29691cfb..3b831c777 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -1821,8 +1821,6 @@ "vr/ui_suppressed_element.h", "vr/vr_tab_helper.cc", "vr/vr_tab_helper.h", - "wake_lock/wake_lock_permission_context.cc", - "wake_lock/wake_lock_permission_context.h", "web_data_service_factory.cc", "web_data_service_factory.h", "webapps/chrome_webapps_client.cc",
diff --git a/chrome/browser/accuracy_tips/accuracy_service_factory.cc b/chrome/browser/accuracy_tips/accuracy_service_factory.cc index 49a69a53..476fe89a 100644 --- a/chrome/browser/accuracy_tips/accuracy_service_factory.cc +++ b/chrome/browser/accuracy_tips/accuracy_service_factory.cc
@@ -6,6 +6,7 @@ #include "base/feature_list.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/page_info/chrome_accuracy_tip_ui.h" #include "components/accuracy_tips/accuracy_service.h" #include "components/accuracy_tips/accuracy_tip_ui.h" #include "components/accuracy_tips/features.h" @@ -33,6 +34,6 @@ KeyedService* AccuracyServiceFactory::BuildServiceInstanceFor( content::BrowserContext* profile) const { DCHECK(base::FeatureList::IsEnabled(accuracy_tips::kAccuracyTipsFeature)); - // TODO(crbug.com/1210891): Implement UI. - return new accuracy_tips::AccuracyService(nullptr); + auto ui = std::make_unique<ChromeAccuracyTipUI>(); + return new accuracy_tips::AccuracyService(std::move(ui)); }
diff --git a/chrome/browser/apps/app_service/app_platform_metrics.cc b/chrome/browser/apps/app_service/app_platform_metrics.cc index cc7753c..658b5ae 100644 --- a/chrome/browser/apps/app_service/app_platform_metrics.cc +++ b/chrome/browser/apps/app_service/app_platform_metrics.cc
@@ -486,13 +486,6 @@ bool is_active = update.State() & apps::InstanceState::kActive; auto it = running_start_time_.find(update.Window()); if (is_active) { - // For browser tabs, record the top browser window running duration only, - // and skip the child tab windows to avoid duration duplicated calculation. - if (update.Window() != update.Window()->GetToplevelWindow() && - update.Window()->GetToplevelWindow()) { - return; - } - if (it == running_start_time_.end()) { AppTypeName app_type_name = GetAppTypeName(profile_, app_type, update.AppId(), @@ -501,6 +494,14 @@ return; } + // For browser tabs, record the top browser window running duration only, + // and skip web apps opened in browser tabs to avoid duration duplicated + // calculation. + if (app_type_name == AppTypeName::kChromeBrowser && + app_id != extension_misc::kChromeAppId) { + return; + } + running_start_time_[update.Window()].start_time = base::TimeTicks::Now(); running_start_time_[update.Window()].app_type_name = app_type_name;
diff --git a/chrome/browser/apps/app_service/app_service_metrics.cc b/chrome/browser/apps/app_service/app_service_metrics.cc index a2821f97..c4deb5b6 100644 --- a/chrome/browser/apps/app_service/app_service_metrics.cc +++ b/chrome/browser/apps/app_service/app_service_metrics.cc
@@ -31,7 +31,8 @@ kCalculator = 10, kText = 11, kGetHelp = 12, - kGallery = 13, + // Gallery was replaced by MediaApp in M86 and deleted in M91. + kDeletedGalleryChromeApp = 13, kVideoPlayer = 14, kAudioPlayer = 15, kChromeCanvas = 16, @@ -212,8 +213,6 @@ RecordDefaultAppLaunch(DefaultAppName::kCalculator, launch_source); } else if (app_id == extension_misc::kTextEditorAppId) { RecordDefaultAppLaunch(DefaultAppName::kText, launch_source); - } else if (app_id == file_manager::kGalleryAppId) { - RecordDefaultAppLaunch(DefaultAppName::kGallery, launch_source); } else if (app_id == file_manager::kVideoPlayerAppId) { RecordDefaultAppLaunch(DefaultAppName::kVideoPlayer, launch_source); } else if (app_id == file_manager::kAudioPlayerAppId) {
diff --git a/chrome/browser/apps/app_service/intent_util.cc b/chrome/browser/apps/app_service/intent_util.cc index 69a697d..5ba836b 100644 --- a/chrome/browser/apps/app_service/intent_util.cc +++ b/chrome/browser/apps/app_service/intent_util.cc
@@ -69,7 +69,7 @@ constexpr int kIntentPrefixLength = 2; -const char* GetArcIntentAction(const std::string& action) { +const char* ConvertAppServiceToArcIntentAction(const std::string& action) { if (action == apps_util::kIntentActionMain) { return arc::kIntentActionMain; } else if (action == apps_util::kIntentActionView) { @@ -209,7 +209,8 @@ arc_intent = arc::mojom::IntentInfo::New(); if (intent->action.has_value()) { - arc_intent->action = GetArcIntentAction(intent->action.value()); + arc_intent->action = + ConvertAppServiceToArcIntentAction(intent->action.value()); } else { arc_intent->action = arc::kIntentActionView; } @@ -239,6 +240,20 @@ return arc_intent; } +const char* ConvertArcToAppServiceIntentAction(const std::string& arc_action) { + if (arc_action == arc::kIntentActionMain) { + return apps_util::kIntentActionMain; + } else if (arc_action == arc::kIntentActionView) { + return apps_util::kIntentActionView; + } else if (arc_action == arc::kIntentActionSend) { + return apps_util::kIntentActionSend; + } else if (arc_action == arc::kIntentActionSendMultiple) { + return apps_util::kIntentActionSendMultiple; + } + + return nullptr; +} + std::string CreateLaunchIntent(const std::string& package_name, const apps::mojom::IntentPtr& intent) { // If |intent| has |ui_bypassed|, |url| or |data|, it is too complex to @@ -253,10 +268,11 @@ // Convert action. std::string action; if (intent->action.has_value()) { - action = GetArcIntentAction(intent->action.value()); + action = ConvertAppServiceToArcIntentAction(intent->action.value()); } - ret += base::StringPrintf("%s=%s;", arc::kAction, - GetArcIntentAction(intent->action.value())); + ret += base::StringPrintf( + "%s=%s;", arc::kAction, + ConvertAppServiceToArcIntentAction(intent->action.value())); // Convert categories. if (intent->categories.has_value()) { @@ -361,7 +377,8 @@ break; case apps::mojom::ConditionType::kAction: for (auto& condition_value : condition->condition_values) { - actions.push_back(GetArcIntentAction(condition_value->value)); + actions.push_back( + ConvertAppServiceToArcIntentAction(condition_value->value)); } break; case apps::mojom::ConditionType::kMimeType: @@ -384,16 +401,12 @@ if (base::FeatureList::IsEnabled(features::kIntentHandlingSharing)) { std::vector<apps::mojom::ConditionValuePtr> action_condition_values; for (auto& arc_action : arc_intent_filter.actions()) { - std::string action; - if (arc_action == arc::kIntentActionView) { - action = apps_util::kIntentActionView; - } else if (arc_action == arc::kIntentActionSend) { - action = apps_util::kIntentActionSend; - } else if (arc_action == arc::kIntentActionSendMultiple) { - action = apps_util::kIntentActionSendMultiple; - } else { + const char* action = ConvertArcToAppServiceIntentAction(arc_action); + + if (!action) { continue; } + action_condition_values.push_back(apps_util::MakeConditionValue( action, apps::mojom::PatternMatchType::kNone)); }
diff --git a/chrome/browser/apps/app_service/intent_util.h b/chrome/browser/apps/app_service/intent_util.h index 59d93ca..7383212a 100644 --- a/chrome/browser/apps/app_service/intent_util.h +++ b/chrome/browser/apps/app_service/intent_util.h
@@ -68,6 +68,10 @@ // Convert between App Service and ARC Intents. arc::mojom::IntentInfoPtr CreateArcIntent(const apps::mojom::IntentPtr& intent); +// Converts an ARC intent action to an App Service intent action. Returns +// nullptr if |arc_action| is an action which is not supported by App Service. +const char* ConvertArcToAppServiceIntentAction(const std::string& arc_action); + // Convert an apps::mojom::Intent struct to a string to call the LaunchIntent // interface from arc::mojom::AppInstance. If |intent| has |ui_bypassed|, |url| // or |data|, returns an empty string as these intents cannot be represented in
diff --git a/chrome/browser/apps/app_service/menu_util.cc b/chrome/browser/apps/app_service/menu_util.cc index c2735e2..f38f52a 100644 --- a/chrome/browser/apps/app_service/menu_util.cc +++ b/chrome/browser/apps/app_service/menu_util.cc
@@ -14,8 +14,8 @@ #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/chrome_features.h" #include "chrome/grit/generated_resources.h" +#include "content/public/common/content_features.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/image_model.h" #include "ui/gfx/image/image_skia.h"
diff --git a/chrome/browser/ash/borealis/borealis_installer.h b/chrome/browser/ash/borealis/borealis_installer.h index 1553dfc6..ddda90f7 100644 --- a/chrome/browser/ash/borealis/borealis_installer.h +++ b/chrome/browser/ash/borealis/borealis_installer.h
@@ -27,6 +27,9 @@ }; // Observer class for the Borealis installation related events. + // + // TODO(b/189720611): It is not possible to have more than one observer, so + // refactor this to use callbacks rather than observers. class Observer : public base::CheckedObserver { public: virtual void OnProgressUpdated(double fraction_complete) = 0;
diff --git a/chrome/browser/ash/borealis/borealis_installer_impl.cc b/chrome/browser/ash/borealis/borealis_installer_impl.cc index 49c68f0..145cc362 100644 --- a/chrome/browser/ash/borealis/borealis_installer_impl.cc +++ b/chrome/browser/ash/borealis/borealis_installer_impl.cc
@@ -389,12 +389,15 @@ void BorealisInstallerImpl::AddObserver(Observer* observer) { DCHECK(observer); + DCHECK(observers_.empty()); observers_.AddObserver(observer); } void BorealisInstallerImpl::RemoveObserver(Observer* observer) { DCHECK(observer); + DCHECK(observers_.HasObserver(observer)); observers_.RemoveObserver(observer); + DCHECK(observers_.empty()); } void BorealisInstallerImpl::SetMainAppTimeoutForTesting(
diff --git a/chrome/browser/ash/login/session/user_session_manager.cc b/chrome/browser/ash/login/session/user_session_manager.cc index 0b96ae90..e6b2ad1 100644 --- a/chrome/browser/ash/login/session/user_session_manager.cc +++ b/chrome/browser/ash/login/session/user_session_manager.cc
@@ -593,6 +593,11 @@ base::CommandLine command_line(browser_command_line.GetProgram()); GetOffTheRecordCommandLine(start_url, browser_command_line, &command_line); + // Trigger loading the shill profile before restarting. + // For regular user sessions, MGS or kiosk sessions, this is done by + // VoteForSavingLoginPassword. + LoadShillProfile(user_manager::GuestAccountId()); + // This makes sure that Chrome restarts with no per-session flags. The guest // profile will always have empty set of per-session flags. If this is not // done and device owner has some per-session flags, when Chrome is relaunched
diff --git a/chrome/browser/ash/login/shill_profile_loading_browsertest.cc b/chrome/browser/ash/login/shill_profile_loading_browsertest.cc index 5a2199b..cea4c49 100644 --- a/chrome/browser/ash/login/shill_profile_loading_browsertest.cc +++ b/chrome/browser/ash/login/shill_profile_loading_browsertest.cc
@@ -14,7 +14,9 @@ // This test case verifies that chrome triggers LoadShillProfile for the // unmanaged user case and the managed user with/without network policy cases. +#include "ash/public/cpp/login_screen_test_api.h" #include "base/bind.h" +#include "base/bind_internal.h" #include "base/run_loop.h" #include "chrome/browser/ash/login/login_manager_test.h" #include "chrome/browser/ash/login/test/login_manager_mixin.h" @@ -29,6 +31,7 @@ #include "chromeos/login/auth/user_context.h" #include "components/account_id/account_id.h" #include "components/policy/proto/chrome_settings.pb.h" +#include "components/user_manager/user_names.h" #include "content/public/test/browser_test.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -210,4 +213,38 @@ LoginManagerTest::kPassword); } +class ShillProfileLoadingGuestLoginTest : public ShillProfileLoadingTest { + protected: + ShillProfileLoadingGuestLoginTest() { + login_manager_.set_session_restore_enabled(); + } + + ~ShillProfileLoadingGuestLoginTest() override = default; + + // ShillProfileLoadingTest: + void SetUpInProcessBrowserTestFixture() override { + ShillProfileLoadingTest::SetUpInProcessBrowserTestFixture(); + FakeSessionManagerClient::Get()->set_supports_browser_restart(true); + } +}; + +IN_PROC_BROWSER_TEST_F(ShillProfileLoadingGuestLoginTest, GuestLogin) { + base::RunLoop restart_job_waiter; + FakeSessionManagerClient::Get()->set_restart_job_callback( + restart_job_waiter.QuitClosure()); + + LoadShillProfileWaiter load_shill_profile_waiter( + FakeSessionManagerClient::Get()); + ASSERT_TRUE(ash::LoginScreenTestApi::ClickGuestButton()); + + restart_job_waiter.Run(); + + // Before restarting, chrome is supposed to have triggered loading the shill + // profile for the guest. + EXPECT_THAT( + load_shill_profile_waiter.invocations(), + ElementsAre(EqualsProto(cryptohome::CreateAccountIdentifierFromAccountId( + user_manager::GuestAccountId())))); +} + } // namespace chromeos
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 3a67e5b..72ae0d7 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4522,6 +4522,7 @@ #if defined(OS_ANDROID) std::string client_data_header; bool is_tab_large_enough = false; + bool is_custom_tab = false; if (frame_tree_node_id != content::RenderFrameHost::kNoFrameTreeNodeId) { auto* web_contents = WebContents::FromFrameTreeNodeId(frame_tree_node_id); // Could be null if the FrameTreeNode's RenderFrameHost is shutting down. @@ -4539,6 +4540,7 @@ : nullptr; if (delegate) { is_tab_large_enough = delegate->IsTabLargeEnoughForDesktopSite(); + is_custom_tab = delegate->IsCustomTab(); } } } @@ -4567,8 +4569,14 @@ request.destination, frame_tree_node_id)); #endif +#if defined(OS_ANDROID) + auto delegate = std::make_unique<signin::HeaderModificationDelegateImpl>( + profile, /*incognito_enabled=*/!is_custom_tab); +#else auto delegate = std::make_unique<signin::HeaderModificationDelegateImpl>(profile); +#endif + auto signin_throttle = signin::URLLoaderThrottle::MaybeCreate(std::move(delegate), wc_getter); if (signin_throttle)
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc index 54f6f94..4a2a800 100644 --- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc +++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -59,6 +59,7 @@ #include "extensions/browser/extension_registry.h" #include "google_apis/drive/auth_service.h" #include "google_apis/drive/drive_api_url_generator.h" +#include "google_apis/gaia/gaia_constants.h" #include "mojo/public/cpp/bindings/callback_helpers.h" #include "services/network/public/cpp/network_connection_tracker.h" #include "services/network/public/cpp/shared_url_loader_factory.h" @@ -858,7 +859,7 @@ const CoreAccountId& account_id = identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSignin); std::vector<std::string> scopes; - scopes.emplace_back("https://www.googleapis.com/auth/drive.readonly"); + scopes.emplace_back(GaiaConstants::kDriveReadOnlyOAuth2Scope); scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory = browser_context()
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc index 2d6a658..25b534e 100644 --- a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc +++ b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc
@@ -12,7 +12,6 @@ #include <utility> #include <vector> -#include "ash/constants/ash_features.h" #include "base/bind.h" #include "base/command_line.h" #include "base/feature_list.h" @@ -28,7 +27,6 @@ #include "chrome/common/extensions/api/file_manager_private.h" #include "chrome/common/extensions/api/file_manager_private_internal.h" #include "content/public/browser/browser_thread.h" -#include "content/public/common/content_switches.h" #include "extensions/browser/api/file_handlers/directory_util.h" #include "extensions/browser/api/file_handlers/mime_util.h" #include "extensions/browser/entry_info.h"
diff --git a/chrome/browser/chromeos/file_manager/app_id.h b/chrome/browser/chromeos/file_manager/app_id.h index 35a8477a..da78bd7 100644 --- a/chrome/browser/chromeos/file_manager/app_id.h +++ b/chrome/browser/chromeos/file_manager/app_id.h
@@ -16,9 +16,6 @@ // The video player's app ID. const char kVideoPlayerAppId[] = "jcgeabjmjgoblfofpppfkcoakmfobdko"; -// The gallery's app ID. -const char kGalleryAppId[] = "nlkncpkkdoccmpiclbokaimcnedabhhm"; - // The audio player's app ID. const char kAudioPlayerAppId[] = "cjbfomnbifhcdnihkgipgfcihmgjfhbf";
diff --git a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc index 42a449f..8010927 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
@@ -181,67 +181,6 @@ IDS_FILE_BROWSER_MEDIA_VIEW_VIDEOS_ROOT_LABEL); } -void AddStringsForGallery(base::DictionaryValue* dict) { - SET_STRING("GALLERY_ASPECT_RATIO_16_9", - IDS_FILE_BROWSER_GALLERY_ASPECT_RATIO_16_9); - SET_STRING("GALLERY_ASPECT_RATIO_1_1", - IDS_FILE_BROWSER_GALLERY_ASPECT_RATIO_1_1); - SET_STRING("GALLERY_ASPECT_RATIO_6_4", - IDS_FILE_BROWSER_GALLERY_ASPECT_RATIO_6_4); - SET_STRING("GALLERY_ASPECT_RATIO_7_5", - IDS_FILE_BROWSER_GALLERY_ASPECT_RATIO_7_5); - SET_STRING("GALLERY_AUTOFIX", IDS_FILE_BROWSER_GALLERY_AUTOFIX); - SET_STRING("GALLERY_BRIGHTNESS", IDS_FILE_BROWSER_GALLERY_BRIGHTNESS); - SET_STRING("GALLERY_CANCEL_LABEL", IDS_FILE_BROWSER_CANCEL_LABEL); - SET_STRING("GALLERY_CONFIRM_DELETE_ONE", IDS_FILE_BROWSER_CONFIRM_DELETE_ONE); - SET_STRING("GALLERY_CONFIRM_DELETE_SOME", - IDS_FILE_BROWSER_CONFIRM_DELETE_SOME); - SET_STRING("GALLERY_CONTRAST", IDS_FILE_BROWSER_GALLERY_CONTRAST); - SET_STRING("GALLERY_CROP", IDS_FILE_BROWSER_GALLERY_CROP); - SET_STRING("GALLERY_DELETE", IDS_FILE_BROWSER_GALLERY_DELETE); - SET_STRING("GALLERY_EDIT", IDS_FILE_BROWSER_GALLERY_EDIT); - SET_STRING("GALLERY_EXIT", IDS_FILE_BROWSER_GALLERY_EXIT); - SET_STRING("GALLERY_ENTER_WHEN_DONE", - IDS_FILE_BROWSER_GALLERY_ENTER_WHEN_DONE); - SET_STRING("GALLERY_EXPOSURE", IDS_FILE_BROWSER_GALLERY_EXPOSURE); - SET_STRING("GALLERY_FILE_EXISTS", IDS_FILE_BROWSER_GALLERY_FILE_EXISTS); - SET_STRING("GALLERY_FIXED", IDS_FILE_BROWSER_GALLERY_FIXED); - SET_STRING("GALLERY_FIXRATIO", IDS_FILE_BROWSER_GALLERY_FIXRATIO); - SET_STRING("GALLERY_HEIGHT", IDS_FILE_BROWSER_GALLERY_HEIGHT); - SET_STRING("GALLERY_IMAGE_ERROR", IDS_FILE_BROWSER_GALLERY_IMAGE_ERROR); - SET_STRING("GALLERY_IMAGE_OFFLINE", IDS_FILE_BROWSER_GALLERY_IMAGE_OFFLINE); - SET_STRING("GALLERY_RESIZE", IDS_FILE_BROWSER_GALLERY_RESIZE); - SET_STRING("GALLERY_INVALIDVALUE", IDS_FILE_BROWSER_GALLERY_INVALIDVALUE); - SET_STRING("GALLERY_ITEMS_SELECTED", IDS_FILE_BROWSER_GALLERY_ITEMS_SELECTED); - SET_STRING("GALLERY_NO_IMAGES", IDS_FILE_BROWSER_GALLERY_NO_IMAGES); - SET_STRING("GALLERY_OK_LABEL", IDS_FILE_BROWSER_OK_LABEL); - SET_STRING("GALLERY_OVERWRITE_BUBBLE", - IDS_FILE_BROWSER_GALLERY_OVERWRITE_BUBBLE); - SET_STRING("GALLERY_OVERWRITE_ORIGINAL", - IDS_FILE_BROWSER_GALLERY_OVERWRITE_ORIGINAL); - SET_STRING("GALLERY_OVERWRITE_BUBBLE_CLOSE", - IDS_FILE_BROWSER_GALLERY_OVERWRITE_BUBBLE_CLOSE); - SET_STRING("GALLERY_PRINT", IDS_FILE_BROWSER_GALLERY_PRINT); - SET_STRING("GALLERY_READONLY_WARNING", - IDS_FILE_BROWSER_GALLERY_READONLY_WARNING); - SET_STRING("GALLERY_READONLY_AND_NON_WRITABLE_FORMAT_WARNING", - IDS_FILE_BROWSER_GALLERY_READONLY_AND_NON_WRITABLE_FORMAT_WARNING); - SET_STRING("GALLERY_NON_WRITABLE_FORMAT_WARNING", - IDS_FILE_BROWSER_GALLERY_NON_WRITABLE_FORMAT_WARNING); - SET_STRING("GALLERY_REDO", IDS_FILE_BROWSER_GALLERY_REDO); - SET_STRING("GALLERY_ROTATE_LEFT", IDS_FILE_BROWSER_GALLERY_ROTATE_LEFT); - SET_STRING("GALLERY_ROTATE_RIGHT", IDS_FILE_BROWSER_GALLERY_ROTATE_RIGHT); - SET_STRING("GALLERY_SAVED", IDS_FILE_BROWSER_GALLERY_SAVED); - SET_STRING("GALLERY_SAVE_FAILED", IDS_FILE_BROWSER_GALLERY_SAVE_FAILED); - SET_STRING("GALLERY_SHARE", IDS_FILE_BROWSER_GALLERY_SHARE); - SET_STRING("GALLERY_SLIDE", IDS_FILE_BROWSER_GALLERY_SLIDE); - SET_STRING("GALLERY_SLIDESHOW", IDS_FILE_BROWSER_GALLERY_SLIDESHOW); - SET_STRING("GALLERY_THUMBNAIL", IDS_FILE_BROWSER_GALLERY_THUMBNAIL); - SET_STRING("GALLERY_UNDO", IDS_FILE_BROWSER_GALLERY_UNDO); - SET_STRING("GALLERY_DONE", IDS_FILE_BROWSER_GALLERY_DONE); - SET_STRING("GALLERY_WIDTH", IDS_FILE_BROWSER_GALLERY_WIDTH); -} - void AddStringsForMediaPlayer(base::DictionaryValue* dict) { SET_STRING("MEDIA_PLAYER_PLAY_BUTTON_LABEL", IDS_MEDIA_PLAYER_PLAY_BUTTON_LABEL); @@ -440,7 +379,6 @@ AddStringsForDrive(dict.get()); AddStringsForMediaView(dict.get()); AddStringsForFileTypes(dict.get()); - AddStringsForGallery(dict.get()); AddStringsForMediaPlayer(dict.get()); AddStringsForVideoPlayer(dict.get()); AddStringsForAudioPlayer(dict.get()); @@ -479,6 +417,8 @@ IDS_FILE_BROWSER_CONFIRM_MOBILE_DATA_USE_PLURAL); SET_STRING("CONFIRM_OVERWRITE_FILE", IDS_FILE_BROWSER_CONFIRM_OVERWRITE_FILE); SET_STRING("CONFIRM_EMPTY_TRASH", IDS_FILE_BROWSER_CONFIRM_EMPTY_TRASH); + SET_STRING("CONFIRM_DELETE_ONE", IDS_FILE_BROWSER_CONFIRM_DELETE_ONE); + SET_STRING("CONFIRM_DELETE_SOME", IDS_FILE_BROWSER_CONFIRM_DELETE_SOME); SET_STRING("CONFLICT_DIALOG_APPLY_TO_ALL", IDS_FILE_BROWSER_CONFLICT_DIALOG_APPLY_TO_ALL); SET_STRING("CONFLICT_DIALOG_KEEP_BOTH",
diff --git a/chrome/browser/chromeos/file_manager/file_tasks.cc b/chrome/browser/chromeos/file_manager/file_tasks.cc index 351d000..4d06664 100644 --- a/chrome/browser/chromeos/file_manager/file_tasks.cc +++ b/chrome/browser/chromeos/file_manager/file_tasks.cc
@@ -183,15 +183,6 @@ }); }; - // Gallery app was replaced by media app in m86, deprecated in m91, and will - // be deleted in m93. However, it still sometimes appears in the file task - // list because its manifest registers it as a handler for many file types. - // Manually erase it from the list here. This can be removed at the same time - // as the gallery app deletion (currently planned for m93). See b/180347590. - const auto gallery_task = task_for_app(kGalleryAppId); - if (gallery_task != tasks->end()) - tasks->erase(gallery_task); - const auto media_app_task = task_for_app(web_app::kMediaAppId); if (media_app_task == tasks->end()) return;
diff --git a/chrome/browser/chromeos/file_manager/file_tasks_browsertest.cc b/chrome/browser/chromeos/file_manager/file_tasks_browsertest.cc index ea2b23d6..afb9ffa 100644 --- a/chrome/browser/chromeos/file_manager/file_tasks_browsertest.cc +++ b/chrome/browser/chromeos/file_manager/file_tasks_browsertest.cc
@@ -53,11 +53,6 @@ ASSERT_TRUE(result) << expectation.file_extensions; --*remaining; - for (const auto& t : *result) { - EXPECT_FALSE(t.task_descriptor().app_id == kGalleryAppId) - << "Gallery has been deprecated and should never appear as a task."; - } - auto default_task = std::find_if(result->begin(), result->end(), [](const auto& task) { return task.is_default(); });
diff --git a/chrome/browser/chromeos/file_manager/gallery_browsertest.cc b/chrome/browser/chromeos/file_manager/gallery_browsertest.cc deleted file mode 100644 index b2a0a556..0000000 --- a/chrome/browser/chromeos/file_manager/gallery_browsertest.cc +++ /dev/null
@@ -1,373 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h" -#include "content/public/test/browser_test.h" - -namespace file_manager { - -template <GuestMode MODE> -class GalleryBrowserTestBase : public FileManagerBrowserTestBase { - public: - GalleryBrowserTestBase() = default; - - protected: - Options GetOptions() const override { - Options opts; - opts.guest_mode = MODE; - return opts; - } - - const char* GetTestCaseName() const override { - return test_case_name_.c_str(); - } - - std::string GetFullTestCaseName() const override { - return test_case_name_; - } - - const char* GetTestExtensionManifestName() const override { - return "gallery_test_manifest.json"; - } - - void set_test_case_name(const std::string& name) { test_case_name_ = name; } - - private: - std::string test_case_name_; - - DISALLOW_COPY_AND_ASSIGN(GalleryBrowserTestBase); -}; - -typedef GalleryBrowserTestBase<NOT_IN_GUEST_MODE> GalleryBrowserTest; -typedef GalleryBrowserTestBase<IN_GUEST_MODE> GalleryBrowserTestInGuestMode; - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, OpenSingleImageOnDownloads) { - set_test_case_name("openSingleImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode, - OpenSingleImageOnDownloads) { - set_test_case_name("openSingleImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, OpenSingleImageOnDrive) { - set_test_case_name("openSingleImageOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, OpenMultipleImagesOnDownloads) { - set_test_case_name("openMultipleImagesOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode, - OpenMultipleImagesOnDownloads) { - set_test_case_name("openMultipleImagesOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - OpenMultipleImagesAndSwitchToSlideModeOnDownloads) { - set_test_case_name("openMultipleImagesAndChangeToSlideModeOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, OpenMultipleImagesOnDrive) { - set_test_case_name("openMultipleImagesOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - CheckAvailabilityOfEditAndPrintButtons) { - set_test_case_name("checkAvailabilityOfEditAndPrintButtons"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, TraverseSlideImagesOnDownloads) { - set_test_case_name("traverseSlideImagesOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode, - TraverseSlideImagesOnDownloads) { - set_test_case_name("traverseSlideImagesOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, TraverseSlideImagesOnDrive) { - set_test_case_name("traverseSlideImagesOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode, - TraverseSlideThumbnailsOnDownloads) { - set_test_case_name("traverseSlideThumbnailsOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, TraverseSlideThumbnailsOnDownloads) { - set_test_case_name("traverseSlideThumbnailsOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, TraverseSlideThumbnailsOnDrive) { - set_test_case_name("traverseSlideThumbnailsOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, RenameImageOnDownloads) { - set_test_case_name("renameImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode, RenameImageOnDownloads) { - set_test_case_name("renameImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, RenameImageOnDrive) { - set_test_case_name("renameImageOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, DeleteImageOnDownloads) { - set_test_case_name("deleteImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode, DeleteImageOnDownloads) { - set_test_case_name("deleteImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, DeleteImageOnDrive) { - set_test_case_name("deleteImageOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - CheckAvailabilityOfShareButtonOnDownloads) { - set_test_case_name("checkAvailabilityOfShareButtonOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode, - CheckAvailabilityOfShareButtonOnDownloads) { - set_test_case_name("checkAvailabilityOfShareButtonOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - CheckAvailabilityOfShareButtonOnDrive) { - set_test_case_name("checkAvailabilityOfShareButtonOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, RotateImageOnDownloads) { - set_test_case_name("rotateImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode, RotateImageOnDownloads) { - set_test_case_name("rotateImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, RotateImageOnDrive) { - set_test_case_name("rotateImageOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, CropImageOnDownloads) { - set_test_case_name("cropImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode, CropImageOnDownloads) { - set_test_case_name("cropImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, CropImageOnDrive) { - set_test_case_name("cropImageOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, ExposureImageOnDownloads) { - set_test_case_name("exposureImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode, - ExposureImageOnDownloads) { - set_test_case_name("exposureImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, ExposureImageOnDrive) { - set_test_case_name("exposureImageOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, ResizeImageOnDownloads) { - set_test_case_name("resizeImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode, ResizeImageOnDownloads) { - set_test_case_name("resizeImageOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, ResizeImageOnDrive) { - set_test_case_name("resizeImageOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - EnableDisableOverwriteOriginalCheckboxOnDownloads) { - set_test_case_name("enableDisableOverwriteOriginalCheckboxOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - EnableDisableOverwriteOriginalCheckboxOnDrive) { - set_test_case_name("enableDisableOverwriteOriginalCheckboxOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - RenameImageInThumbnailModeOnDownloads) { - set_test_case_name("renameImageInThumbnailModeOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, RenameImageInThumbnailModeOnDrive) { - set_test_case_name("renameImageInThumbnailModeOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - DeleteAllImagesInThumbnailModeOnDownloads) { - set_test_case_name("deleteAllImagesInThumbnailModeOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - DeleteAllImagesInThumbnailModeOnDrive) { - set_test_case_name("deleteAllImagesInThumbnailModeOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - DeleteAllImagesInThumbnailModeWithEnterKey) { - set_test_case_name("deleteAllImagesInThumbnailModeWithEnterKey"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - DeleteAllImagesInThumbnailModeWithDeleteKey) { - set_test_case_name("deleteAllImagesInThumbnailModeWithDeleteKey"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - EmptySpaceClickUnselectsInThumbnailModeOnDownloads) { - set_test_case_name("emptySpaceClickUnselectsInThumbnailModeOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - EmptySpaceClickUnselectsInThumbnailModeOnDrive) { - set_test_case_name("emptySpaceClickUnselectsInThumbnailModeOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - SelectMultipleImagesWithShiftKeyOnDownloads) { - set_test_case_name("selectMultipleImagesWithShiftKeyOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, - SelectAllImagesAfterImageDeletionOnDownloads) { - set_test_case_name("selectAllImagesAfterImageDeletionOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode, - SlideshowTraversalOnDownloads) { - set_test_case_name("slideshowTraversalOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, SlideshowTraversalOnDownloads) { - set_test_case_name("slideshowTraversalOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, SlideshowTraversalOnDrive) { - set_test_case_name("slideshowTraversalOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode, - StopStartSlideshowOnDownloads) { - set_test_case_name("stopStartSlideshowOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, StopStartSlideshowOnDownloads) { - set_test_case_name("stopStartSlideshowOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, StopStartSlideshowOnDrive) { - set_test_case_name("stopStartSlideshowOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, ActivateVideoFromThumbnailMode) { - set_test_case_name("activateVideoFromThumbnailMode"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, DeleteSingleOpenPhotoOnDownloads) { - set_test_case_name("deleteSingleOpenPhotoOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, OneImageSlideshowNoPauseButtonOnDownloads) { - set_test_case_name("oneImageSlideshowNoPauseButtonOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, OneImageSlideshowNoPauseButtonOnDrive) { - set_test_case_name("oneImageSlideshowNoPauseButtonOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, ShiftSelectFromNothingSelectedOnDownloads) { - set_test_case_name("shiftSelectFromNothingSelectedOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, ShiftSelectFromNothingSelectedOnDrive) { - set_test_case_name("shiftSelectFromNothingSelectedOnDrive"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, DeleteImageWhileEditingOnDownloads) { - set_test_case_name("deleteImageWhileEditingOnDownloads"); - StartTest(); -} - -IN_PROC_BROWSER_TEST_F(GalleryBrowserTest, DeleteImageWhileEditingOnDrive) { - set_test_case_name("deleteImageWhileEditingOnDrive"); - StartTest(); -} - -} // namespace file_manager
diff --git a/chrome/browser/chromeos/file_manager/gallery_jstest.cc b/chrome/browser/chromeos/file_manager/gallery_jstest.cc deleted file mode 100644 index 0a9dbd9..0000000 --- a/chrome/browser/chromeos/file_manager/gallery_jstest.cc +++ /dev/null
@@ -1,59 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/chromeos/file_manager/file_manager_jstest_base.h" -#include "content/public/test/browser_test.h" - -class GalleryJsTest : public FileManagerJsTestBase { - protected: - GalleryJsTest() - : FileManagerJsTestBase( - base::FilePath(FILE_PATH_LITERAL("ui/file_manager/gallery/js"))) {} -}; - -IN_PROC_BROWSER_TEST_F(GalleryJsTest, ImageEncoderTest) { - RunTestURL("image_editor/image_encoder_unittest.m_gen.html"); -} - -// Disabled on ASan builds due to a consistent failure. https://crbug.com/762831 -#if defined(ADDRESS_SANITIZER) -#define MAYBE_ExifEncoderTest DISABLED_ExifEncoderTest -#else -#define MAYBE_ExifEncoderTest ExifEncoderTest -#endif -IN_PROC_BROWSER_TEST_F(GalleryJsTest, MAYBE_ExifEncoderTest) { - RunTestURL("image_editor/exif_encoder_unittest.m_gen.html"); -} - -IN_PROC_BROWSER_TEST_F(GalleryJsTest, ImageViewTest) { - RunTestURL("image_editor/image_view_unittest_gen.html"); -} - -IN_PROC_BROWSER_TEST_F(GalleryJsTest, EntryListWatcherTest) { - RunTestURL("entry_list_watcher_unittest_gen.html"); -} - -IN_PROC_BROWSER_TEST_F(GalleryJsTest, GalleryUtilTest) { - RunTestURL("gallery_util_unittest_gen.html"); -} - -IN_PROC_BROWSER_TEST_F(GalleryJsTest, GalleryItemTest) { - RunTestURL("gallery_item_unittest_gen.html"); -} - -IN_PROC_BROWSER_TEST_F(GalleryJsTest, GalleryDataModelTest) { - RunTestURL("gallery_data_model_unittest_gen.html"); -} - -IN_PROC_BROWSER_TEST_F(GalleryJsTest, RibbonTest) { - RunTestURL("ribbon_unittest_gen.html"); -} - -IN_PROC_BROWSER_TEST_F(GalleryJsTest, SlideModeTest) { - RunTestURL("slide_mode_unittest_gen.html"); -} - -IN_PROC_BROWSER_TEST_F(GalleryJsTest, DimmableUIControllerTest) { - RunTestURL("dimmable_ui_controller_unittest_gen.html"); -}
diff --git a/chrome/browser/chromeos/file_manager/web_file_tasks_unittest.cc b/chrome/browser/chromeos/file_manager/web_file_tasks_unittest.cc index 89ed2138..767a3e4 100644 --- a/chrome/browser/chromeos/file_manager/web_file_tasks_unittest.cc +++ b/chrome/browser/chromeos/file_manager/web_file_tasks_unittest.cc
@@ -14,10 +14,11 @@ #include "chrome/browser/chromeos/file_manager/path_util.h" #include "chrome/browser/web_applications/components/app_registrar.h" #include "chrome/browser/web_applications/components/web_app_id.h" -#include "chrome/browser/web_applications/test/test_app_registrar.h" #include "chrome/browser/web_applications/test/test_file_handler_manager.h" #include "chrome/browser/web_applications/test/test_os_integration_manager.h" #include "chrome/browser/web_applications/test/test_web_app_provider.h" +#include "chrome/browser/web_applications/web_app.h" +#include "chrome/browser/web_applications/web_app_registrar.h" #include "chrome/test/base/testing_profile.h" #include "content/public/test/browser_task_environment.h" #include "extensions/browser/entry_info.h" @@ -38,7 +39,8 @@ app_provider_ = web_app::TestWebAppProvider::Get(profile_.get()); - auto app_registrar = std::make_unique<web_app::TestAppRegistrar>(); + auto app_registrar = + std::make_unique<web_app::WebAppRegistrarMutable>(profile_.get()); app_registrar_ = app_registrar.get(); app_provider_->SetRegistrar(std::move(app_registrar)); @@ -60,10 +62,28 @@ const web_app::AppId& app_id, const GURL& install_url, const web_app::TestFileHandlerManager::AcceptMap& accept) { - app_registrar_->AddExternalApp(app_id, {install_url}); + auto web_app = CreateWebApp(app_id, install_url); + RegisterApp(std::move(web_app)); file_handler_manager_->InstallFileHandler(app_id, install_url, accept); } + std::unique_ptr<web_app::WebApp> CreateWebApp(const web_app::AppId& app_id, + const GURL& app_url) { + auto web_app = std::make_unique<web_app::WebApp>(app_id); + web_app->AddSource(web_app::Source::kDefault); + web_app->SetDisplayMode(web_app::DisplayMode::kStandalone); + web_app->SetUserDisplayMode(web_app::DisplayMode::kStandalone); + web_app->SetName("Name"); + web_app->SetStartUrl(app_url); + + return web_app; + } + + void RegisterApp(std::unique_ptr<web_app::WebApp> web_app) { + web_app::AppId app_id = web_app->app_id(); + app_registrar_->registry().emplace(std::move(app_id), std::move(web_app)); + } + Profile* profile() { return profile_.get(); } web_app::TestFileHandlerManager* file_handler_manager() { return file_handler_manager_; @@ -73,7 +93,7 @@ content::BrowserTaskEnvironment task_environment_; std::unique_ptr<TestingProfile> profile_; web_app::TestWebAppProvider* app_provider_; - web_app::TestAppRegistrar* app_registrar_; + web_app::WebAppRegistrarMutable* app_registrar_; web_app::TestFileHandlerManager* file_handler_manager_; };
diff --git a/chrome/browser/chromeos/input_method/assistive_suggester.cc b/chrome/browser/chromeos/input_method/assistive_suggester.cc index 70da439..e34970ba 100644 --- a/chrome/browser/chromeos/input_method/assistive_suggester.cc +++ b/chrome/browser/chromeos/input_method/assistive_suggester.cc
@@ -21,6 +21,7 @@ #include "components/prefs/scoped_user_pref_update.h" #include "ui/base/ime/chromeos/ime_bridge.h" #include "ui/base/ime/chromeos/ime_input_context_handler_interface.h" +#include "ui/base/ime/chromeos/input_method_ukm.h" #include "url/gurl.h" namespace chromeos { @@ -47,6 +48,13 @@ "voice.google.com", }; +const char* kAllowedDomainsForMultiWordSuggester[] = { + "discord.com", "messenger.com", "web.whatsapp.com", + "web.skype.com", "duo.google.com", "hangouts.google.com", + "chat.google.com", "messages.google.com", "web.telegram.org", + "voice.google.com", +}; + const char* kTestUrls[] = { "e14s-test", "simple_textarea.html", @@ -111,8 +119,43 @@ "mmfbcljfglbokpmkimbfghdkjmjhdgbg", // System text }; +// For ARC++ apps, use arc package name. For system apps, use app ID. +const char* kAllowedAppsForMultiWordSuggester[] = { + "com.discord", + "com.facebook.orca", + "com.whatsapp", + "com.skype.raider", + "com.google.android.apps.tachyon", + "com.google.android.talk", + "org.telegram.messenger", + "com.enflick.android.TextNow", + "com.facebook.mlite", + "com.viber.voip", + "com.skype.m2", + "com.imo.android.imoim", + "com.google.android.apps.googlevoice", + "com.playstation.mobilemessenger", + "kik.android", + "com.link.messages.sms", + "jp.naver.line.android", + "com.skype.m2", + "co.happybits.marcopolo", + "com.imo.android.imous", + "mmfbcljfglbokpmkimbfghdkjmjhdgbg", // System text +}; + void RecordAssistiveMatch(AssistiveType type) { base::UmaHistogramEnumeration("InputMethod.Assistive.Match", type); + + ui::IMEInputContextHandlerInterface* input_context = + ui::IMEBridge::Get()->GetInputContextHandler(); + if (!input_context) + return; + + auto sourceId = input_context->GetClientSourceForMetrics(); + if (sourceId != ukm::kInvalidSourceId) { + ui::RecordUkmAssistiveMatch(sourceId, static_cast<int>(type)); + } } void RecordAssistiveDisabled(AssistiveType type) { @@ -128,6 +171,11 @@ base::UmaHistogramEnumeration("InputMethod.Assistive.Disabled.Emoji", reason); } +void RecordAssistiveDisabledReasonForMultiWord(DisabledReason reason) { + base::UmaHistogramEnumeration("InputMethod.Assistive.Disabled.MultiWord", + reason); +} + void RecordAssistiveUserPrefForPersonalInfo(bool value) { base::UmaHistogramBoolean("InputMethod.Assistive.UserPref.PersonalInfo", value); @@ -225,6 +273,11 @@ IsAllowedApp(kAllowedAppsForEmojiSuggester); } +bool IsAllowedUrlOrAppForMultiWordSuggestion() { + return IsAllowedUrl(kAllowedDomainsForMultiWordSuggester) || + IsAllowedApp(kAllowedAppsForMultiWordSuggester); +} + bool IsTopResultMultiWord(const std::vector<TextSuggestion>& suggestions) { if (suggestions.empty()) return false; @@ -320,6 +373,20 @@ return DisabledReason::kNone; } +DisabledReason AssistiveSuggester::GetDisabledReasonForMultiWord() { + if (!base::FeatureList::IsEnabled(chromeos::features::kAssistMultiWord)) { + return DisabledReason::kFeatureFlagOff; + } + if (!profile_->GetPrefs()->GetBoolean( + prefs::kAssistPredictiveWritingEnabled)) { + return DisabledReason::kUserSettingsOff; + } + if (!IsAllowedUrlOrAppForMultiWordSuggestion()) { + return DisabledReason::kUrlOrAppNotAllowed; + } + return DisabledReason::kNone; +} + bool AssistiveSuggester::IsActionEnabled(AssistiveType action) { switch (action) { case AssistiveType::kPersonalEmail: @@ -380,15 +447,6 @@ default: break; } - - if (IsMultiWordSuggestEnabled() && current_suggester_) { - auto proposed_action = current_suggester_->GetProposeActionType(); - if (proposed_action == AssistiveType::kMultiWordCompletion || - proposed_action == AssistiveType::kMultiWordPrediction) { - current_suggester_->DismissSuggestion(); - current_suggester_ = nullptr; - } - } } return false; @@ -396,8 +454,11 @@ void AssistiveSuggester::OnExternalSuggestionsUpdated( const std::vector<TextSuggestion>& suggestions) { - if (!IsMultiWordSuggestEnabled()) + if (!IsMultiWordSuggestEnabled() || + !IsAllowedUrlOrAppForMultiWordSuggestion()) { + RecordAssistiveDisabledReasonForMultiWord(GetDisabledReasonForMultiWord()); return; + } RecordSuggestionsMatch(suggestions);
diff --git a/chrome/browser/chromeos/input_method/assistive_suggester.h b/chrome/browser/chromeos/input_method/assistive_suggester.h index 6143c666..16ca951 100644 --- a/chrome/browser/chromeos/input_method/assistive_suggester.h +++ b/chrome/browser/chromeos/input_method/assistive_suggester.h
@@ -89,6 +89,9 @@ // Only the first applicable reason in DisabledReason enum is returned. DisabledReason GetDisabledReasonForPersonalInfo(); + // Only the first applicable reason in DisabledReason enum is returned. + DisabledReason GetDisabledReasonForMultiWord(); + bool IsActionEnabled(AssistiveType action); bool WithinGrammarFragment(int cursor_pos, int anchor_pos);
diff --git a/chrome/browser/chromeos/input_method/assistive_suggester_unittest.cc b/chrome/browser/chromeos/input_method/assistive_suggester_unittest.cc index 84b2dcb..a5326015 100644 --- a/chrome/browser/chromeos/input_method/assistive_suggester_unittest.cc +++ b/chrome/browser/chromeos/input_method/assistive_suggester_unittest.cc
@@ -13,6 +13,7 @@ #include "components/prefs/scoped_user_pref_update.h" #include "content/public/test/browser_task_environment.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/ime/chromeos/ime_bridge.h" namespace chromeos { @@ -32,6 +33,7 @@ "InputMethod.Assistive.UserPref.PersonalInfo", true, 1); histogram_tester_.ExpectUniqueSample("InputMethod.Assistive.UserPref.Emoji", true, 1); + ui::IMEBridge::Initialize(); } content::BrowserTaskEnvironment task_environment_; @@ -163,26 +165,4 @@ EXPECT_FALSE(assistive_suggester_->IsAssistiveFeatureEnabled()); } -TEST_F(AssistiveSuggesterTest, RecordsCoverageForMultiWordCompletion) { - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures( - /*enabled_features=*/{chromeos::features::kAssistMultiWord}, - /*disabled_features=*/{chromeos::features::kEmojiSuggestAddition, - chromeos::features::kAssistPersonalInfo}); - - // TODO(crbug/1146266): Add prediction case once prediction is supported - std::vector<TextSuggestion> suggestions = { - TextSuggestion{.mode = TextSuggestionMode::kCompletion, - .type = TextSuggestionType::kMultiWord, - .text = "some text"}, - }; - - assistive_suggester_->OnExternalSuggestionsUpdated(suggestions); - - histogram_tester_.ExpectUniqueSample("InputMethod.Assistive.Match", - AssistiveType::kMultiWordCompletion, 1); - histogram_tester_.ExpectUniqueSample("InputMethod.Assistive.Coverage", - AssistiveType::kMultiWordCompletion, 1); -} - } // namespace chromeos
diff --git a/chrome/browser/chromeos/input_method/multi_word_suggester.cc b/chrome/browser/chromeos/input_method/multi_word_suggester.cc index 12e88ac..9e6594e 100644 --- a/chrome/browser/chromeos/input_method/multi_word_suggester.cc +++ b/chrome/browser/chromeos/input_method/multi_word_suggester.cc
@@ -7,7 +7,6 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/chromeos/input_method/ui/suggestion_details.h" #include "chromeos/services/ime/public/cpp/suggestions.h" -#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/events/keycodes/dom/dom_code.h" namespace chromeos { @@ -29,23 +28,24 @@ return absl::nullopt; } -int CalculateConfirmedLength(const std::u16string& suggestion, - const std::u16string& last_known_text_) { - int last_space_index = last_known_text_.rfind(u' '); - int offset = last_space_index < 0 ? 0 : last_space_index + 1; - +int CalculateNumberMatchingChars(const std::u16string& first, + const std::u16string& second) { int matching_character_count = 0; - for (int i = 0; i < suggestion.size(); i++) { - if (last_known_text_.size() < i + offset) - break; - if (last_known_text_[i + offset] != suggestion[i]) + for (int i = 0; i < first.size() && i < second.size(); i++) { + if (first[i] != second[i]) break; matching_character_count++; } - return matching_character_count; } +std::u16string ExtractFinalWord(const std::u16string& text) { + size_t last_space_index = text.rfind(u' '); + size_t offset = + last_space_index == std::u16string::npos ? 0 : last_space_index + 1; + return text.substr(offset); +} + } // namespace MultiWordSuggester::MultiWordSuggester( @@ -56,30 +56,56 @@ void MultiWordSuggester::OnFocus(int context_id) { focused_context_id_ = context_id; + ResetSuggestionState(); + ResetTextState(); } void MultiWordSuggester::OnBlur() { focused_context_id_ = 0; + ResetSuggestionState(); + ResetTextState(); } void MultiWordSuggester::OnSurroundingTextChanged(const std::u16string& text, - int cursor_pos, - int anchor_pos) { - last_known_text_ = text; - last_known_cursor_pos_ = cursor_pos; - last_known_anchor_pos_ = anchor_pos; + size_t cursor_pos, + size_t anchor_pos) { + bool cursor_at_end_of_text = + cursor_pos == anchor_pos && cursor_pos == text.length(); + + text_state_ = LastKnownTextState{ + .text = text, .cursor_at_end_of_text = cursor_at_end_of_text}; } void MultiWordSuggester::OnExternalSuggestionsUpdated( const std::vector<TextSuggestion>& suggestions) { - auto multi_word_suggestion = GetMultiWordSuggestion(suggestions); + if (suggestion_state_ || !text_state_.cursor_at_end_of_text) + return; + + absl::optional<TextSuggestion> multi_word_suggestion = + GetMultiWordSuggestion(suggestions); + if (multi_word_suggestion) { - DisplaySuggestion(multi_word_suggestion.value()); + auto suggestion = multi_word_suggestion.value(); + auto suggestion_text = base::UTF8ToUTF16(suggestion.text); + auto final_word = ExtractFinalWord(text_state_.text); + int confirmed_length = + suggestion.mode == TextSuggestionMode::kCompletion + ? CalculateNumberMatchingChars(suggestion_text, final_word) + : 0; + + DisplaySuggestion(suggestion_text, confirmed_length); + + int start_pos = text_state_.text.length() >= confirmed_length + ? text_state_.text.length() - confirmed_length + : 0; + + suggestion_state_ = LastKnownSuggestionState{.start_pos = start_pos, + .text = suggestion_text}; } } SuggestionStatus MultiWordSuggester::HandleKeyEvent(const ui::KeyEvent& event) { - if (!suggestion_shown_) + if (!suggestion_state_) return SuggestionStatus::kNotHandled; switch (event.code()) { @@ -94,6 +120,25 @@ bool MultiWordSuggester::Suggest(const std::u16string& text, size_t cursor_pos, size_t anchor_pos) { + if (!suggestion_state_ || cursor_pos != text.length()) + return false; + + auto last_suggestion_shown = suggestion_state_.value(); + auto possibly_confirmed_text = + last_suggestion_shown.start_pos < text.length() && + last_suggestion_shown.start_pos >= 0 + ? text.substr(last_suggestion_shown.start_pos) + : base::EmptyString16(); + bool matches_last_suggestion = + base::StartsWith(last_suggestion_shown.text, possibly_confirmed_text, + base::CompareCase::INSENSITIVE_ASCII); + + if (matches_last_suggestion) { + DisplaySuggestion(last_suggestion_shown.text, + possibly_confirmed_text.length()); + return true; + } + return false; } @@ -105,7 +150,7 @@ return false; } - suggestion_shown_ = false; + ResetSuggestionState(); return true; } @@ -117,7 +162,7 @@ return; } - suggestion_shown_ = false; + ResetSuggestionState(); } AssistiveType MultiWordSuggester::GetProposeActionType() { @@ -132,19 +177,13 @@ return {}; } -void MultiWordSuggester::DisplaySuggestion(const TextSuggestion& suggestion) { +void MultiWordSuggester::DisplaySuggestion(const std::u16string& text, + int confirmed_length) { ui::ime::SuggestionDetails details; - details.text = base::UTF8ToUTF16(suggestion.text); + details.text = text; details.show_accept_annotation = false; details.show_quick_accept_annotation = true; - - // TODO(crbug/1146266): Move to suggestions service - details.confirmed_length = - suggestion.mode == TextSuggestionMode::kCompletion - ? CalculateConfirmedLength(details.text, last_known_text_) - : 0; - - // TODO(crbug/1146266): Add required pref counter to hide settings link. + details.confirmed_length = confirmed_length; details.show_setting_link = false; std::string error; @@ -153,8 +192,17 @@ LOG(ERROR) << "suggest: Failed to show suggestion in assistive framework" << " - " << error; } +} - suggestion_shown_ = true; +void MultiWordSuggester::ResetSuggestionState() { + suggestion_state_ = absl::nullopt; +} + +void MultiWordSuggester::ResetTextState() { + text_state_ = LastKnownTextState{ + .text = u"", + .cursor_at_end_of_text = true, + }; } } // namespace chromeos
diff --git a/chrome/browser/chromeos/input_method/multi_word_suggester.h b/chrome/browser/chromeos/input_method/multi_word_suggester.h index 4eb8b6f..827fb03 100644 --- a/chrome/browser/chromeos/input_method/multi_word_suggester.h +++ b/chrome/browser/chromeos/input_method/multi_word_suggester.h
@@ -9,9 +9,20 @@ #include "chrome/browser/chromeos/input_method/suggestion_enums.h" #include "chrome/browser/chromeos/input_method/suggestion_handler_interface.h" #include "chromeos/services/ime/public/cpp/suggestions.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace chromeos { +struct LastKnownTextState { + std::u16string text; + bool cursor_at_end_of_text; +}; + +struct LastKnownSuggestionState { + size_t start_pos; + std::u16string text; +}; + // Integrates multi word suggestions produced by the system with the assistive // framework. Handles showing / accepting / dismissing any multi word // suggestions generated by the system. @@ -38,21 +49,22 @@ std::vector<ime::TextSuggestion> GetSuggestions() override; void OnSurroundingTextChanged(const std::u16string& text, - int cursor_pos, - int anchor_pos); + size_t cursor_pos, + size_t anchor_pos); private: - void DisplaySuggestion(const ime::TextSuggestion& suggestion); + void DisplaySuggestion(const std::u16string& text, int confirmed_length); + void ResetSuggestionState(); + void ResetTextState(); // The currently focused input (zero if none are focused) int focused_context_id_ = 0; - bool suggestion_shown_ = false; + // Previous suggestion details + absl::optional<LastKnownSuggestionState> suggestion_state_; // The last known state of text in the focused text input - std::u16string last_known_text_; - int last_known_cursor_pos_ = 0; - int last_known_anchor_pos_ = 0; + LastKnownTextState text_state_; // Not owned by this class SuggestionHandlerInterface* suggestion_handler_;
diff --git a/chrome/browser/chromeos/input_method/multi_word_suggester_unittest.cc b/chrome/browser/chromeos/input_method/multi_word_suggester_unittest.cc index 77aed6d..3b23e7c 100644 --- a/chrome/browser/chromeos/input_method/multi_word_suggester_unittest.cc +++ b/chrome/browser/chromeos/input_method/multi_word_suggester_unittest.cc
@@ -15,9 +15,9 @@ namespace chromeos { namespace { -using TextSuggestion = ::chromeos::ime::TextSuggestion; -using TextSuggestionMode = ::chromeos::ime::TextSuggestionMode; -using TextSuggestionType = ::chromeos::ime::TextSuggestionType; +using ::chromeos::ime::TextSuggestion; +using ::chromeos::ime::TextSuggestionMode; +using ::chromeos::ime::TextSuggestionType; void SendKeyEvent(MultiWordSuggester* suggester, const ui::DomCode& code) { suggester->HandleKeyEvent(ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_UNKNOWN, @@ -158,4 +158,108 @@ EXPECT_EQ(suggestion_handler.GetConfirmedLength(), 3); // whe } +TEST(MultiWordSuggesterTest, TracksLastSuggestionOnSurroundingTextChange) { + FakeSuggestionHandler suggestion_handler; + MultiWordSuggester suggester(&suggestion_handler); + int focused_context_id = 5; + + std::vector<TextSuggestion> suggestions = { + TextSuggestion{.mode = TextSuggestionMode::kCompletion, + .type = TextSuggestionType::kMultiWord, + .text = "where are you going"}, + }; + + suggester.OnFocus(focused_context_id); + suggester.OnSurroundingTextChanged(u"hey there sam whe", 17, 17); + suggester.OnExternalSuggestionsUpdated(suggestions); + suggester.OnSurroundingTextChanged(u"hey there sam wher", 18, 18); + suggester.Suggest(u"hey there sam wher", 18, 18); + suggester.OnSurroundingTextChanged(u"hey there sam where", 19, 19); + suggester.Suggest(u"hey there sam where", 19, 19); + suggester.OnSurroundingTextChanged(u"hey there sam where ", 20, 20); + suggester.Suggest(u"hey there sam where ", 20, 20); + suggester.OnSurroundingTextChanged(u"hey there sam where a", 21, 21); + suggester.Suggest(u"hey there sam where a", 21, 21); + suggester.OnSurroundingTextChanged(u"hey there sam where ar", 22, 22); + suggester.Suggest(u"hey there sam where ar", 22, 22); + suggester.OnSurroundingTextChanged(u"hey there sam where are", 23, 23); + suggester.Suggest(u"hey there sam where are", 23, 23); + + EXPECT_TRUE(suggestion_handler.GetShowingSuggestion()); + EXPECT_EQ(suggestion_handler.GetSuggestionText(), u"where are you going"); + EXPECT_EQ(suggestion_handler.GetConfirmedLength(), 9); // where are +} + +TEST(MultiWordSuggesterTest, + TracksLastSuggestionOnSurroundingTextChangeAtBeginningText) { + FakeSuggestionHandler suggestion_handler; + MultiWordSuggester suggester(&suggestion_handler); + int focused_context_id = 5; + + std::vector<TextSuggestion> suggestions = { + TextSuggestion{.mode = TextSuggestionMode::kCompletion, + .type = TextSuggestionType::kMultiWord, + .text = "how are you"}, + }; + + suggester.OnFocus(focused_context_id); + suggester.OnSurroundingTextChanged(u"h", 1, 1); + suggester.OnExternalSuggestionsUpdated(suggestions); + suggester.OnSurroundingTextChanged(u"ho", 2, 2); + suggester.Suggest(u"ho", 2, 2); + suggester.OnSurroundingTextChanged(u"how", 3, 3); + suggester.Suggest(u"how", 3, 3); + + EXPECT_TRUE(suggestion_handler.GetShowingSuggestion()); + EXPECT_EQ(suggestion_handler.GetSuggestionText(), u"how are you"); + EXPECT_EQ(suggestion_handler.GetConfirmedLength(), 3); // how +} + +TEST(MultiWordSuggesterTest, TracksLastSuggestionOnLargeSurroundingTextChange) { + FakeSuggestionHandler suggestion_handler; + MultiWordSuggester suggester(&suggestion_handler); + int focused_context_id = 5; + + std::vector<TextSuggestion> suggestions = { + TextSuggestion{.mode = TextSuggestionMode::kCompletion, + .type = TextSuggestionType::kMultiWord, + .text = "how are you"}, + }; + + suggester.OnFocus(focused_context_id); + suggester.OnSurroundingTextChanged(u"h", 1, 1); + suggester.OnExternalSuggestionsUpdated(suggestions); + suggester.OnSurroundingTextChanged(u"how ar", 6, 6); + suggester.Suggest(u"how ar", 6, 6); + suggester.OnSurroundingTextChanged(u"how are yo", 10, 10); + suggester.Suggest(u"how are yo", 10, 10); + + EXPECT_TRUE(suggestion_handler.GetShowingSuggestion()); + EXPECT_EQ(suggestion_handler.GetSuggestionText(), u"how are you"); + EXPECT_EQ(suggestion_handler.GetConfirmedLength(), 10); // how are yo +} + +TEST(MultiWordSuggesterTest, + DoesNotTrackLastSuggestionIfSurroundingTextChange) { + FakeSuggestionHandler suggestion_handler; + MultiWordSuggester suggester(&suggestion_handler); + int focused_context_id = 5; + + std::vector<TextSuggestion> suggestions = { + TextSuggestion{.mode = TextSuggestionMode::kCompletion, + .type = TextSuggestionType::kMultiWord, + .text = "how are you"}, + }; + + suggester.OnFocus(focused_context_id); + suggester.OnSurroundingTextChanged(u"h", 1, 1); + suggester.OnExternalSuggestionsUpdated(suggestions); + suggester.OnSurroundingTextChanged(u"how ar", 6, 6); + suggester.Suggest(u"how ar", 6, 6); + suggester.OnSurroundingTextChanged(u"how yo", 6, 6); + + // The consumer will handle dismissing the suggestion + EXPECT_FALSE(suggester.Suggest(u"how yo", 6, 6)); +} + } // namespace chromeos
diff --git a/chrome/browser/chromeos/input_method/native_input_method_engine.cc b/chrome/browser/chromeos/input_method/native_input_method_engine.cc index 59ba6a1d..ebe5669 100644 --- a/chrome/browser/chromeos/input_method/native_input_method_engine.cc +++ b/chrome/browser/chromeos/input_method/native_input_method_engine.cc
@@ -419,11 +419,10 @@ void NativeInputMethodEngine::ImeObserver::OnReset( const std::string& engine_id) { - if (remote_to_engine_.is_bound() && ShouldRouteToRuleBasedEngine(engine_id)) { - remote_to_engine_->ResetForRulebased(); - } else if (ShouldRouteToFstMojoEngine(engine_id)) { + if (ShouldRouteToFstMojoEngine(engine_id) || + ShouldRouteToRuleBasedEngine(engine_id)) { if (remote_to_engine_.is_bound()) { - remote_to_engine_->OnCompositionCanceled(); + remote_to_engine_->OnCompositionCanceledBySystem(); } } else { ime_base_observer_->OnReset(engine_id);
diff --git a/chrome/browser/chromeos/input_method/native_input_method_engine.h b/chrome/browser/chromeos/input_method/native_input_method_engine.h index 7cfa75e..0a6c8a05f 100644 --- a/chrome/browser/chromeos/input_method/native_input_method_engine.h +++ b/chrome/browser/chromeos/input_method/native_input_method_engine.h
@@ -137,13 +137,12 @@ const std::string& text, uint32_t offset, ime::mojom::SelectionRangePtr selection_range) override {} - void OnCompositionCanceled() override {} + void OnCompositionCanceledBySystem() override {} void ProcessKeypressForRulebased( ime::mojom::PhysicalKeyEventPtr event, ProcessKeypressForRulebasedCallback callback) override {} void OnKeyEvent(ime::mojom::PhysicalKeyEventPtr event, OnKeyEventCallback callback) override {} - void ResetForRulebased() override {} void CommitText( const std::string& text, ime::mojom::CommitTextCursorBehavior cursor_behavior) override;
diff --git a/chrome/browser/chromeos/input_method/native_input_method_engine_unittest.cc b/chrome/browser/chromeos/input_method/native_input_method_engine_unittest.cc index 71300c90..c999674 100644 --- a/chrome/browser/chromeos/input_method/native_input_method_engine_unittest.cc +++ b/chrome/browser/chromeos/input_method/native_input_method_engine_unittest.cc
@@ -410,7 +410,8 @@ machine_learning::FakeServiceConnectionImpl fake_service_connection_; }; -TEST_F(NativeInputMethodEngineWithRenderViewHostTest, RecordUkmAddsUkmEntry) { +TEST_F(NativeInputMethodEngineWithRenderViewHostTest, + RecordUkmAddsNonCompliantApiUkmEntry) { GURL url("https://www.example.com/"); content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(), url); @@ -463,5 +464,48 @@ InputMethodManager::Shutdown(); } +TEST_F(NativeInputMethodEngineWithRenderViewHostTest, + RecordUkmAddsAssistiveMatchUkmEntry) { + GURL url("https://www.example.com/"); + content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(), + url); + + auto* testing_profile = static_cast<TestingProfile*>(browser_context()); + testing::NiceMock<ime::MockInputChannel> mock_input_channel; + mojo::Remote<ime::mojom::InputChannel> remote; + input_method::InputMethodManager::Initialize( + new TestInputMethodManager(&mock_input_channel, &remote)); + NativeInputMethodEngine engine; + engine.Initialize(std::make_unique<StubInputMethodEngineObserver>(), + /*extension_id=*/"", testing_profile); + + ui::FakeTextInputClient fake_text_input_client(ui::TEXT_INPUT_TYPE_TEXT); + fake_text_input_client.set_source_id( + ukm::GetSourceIdForWebContentsDocument(web_contents())); + + ui::InputMethodChromeOS ime(nullptr); + ime.SetFocusedTextInputClient(&fake_text_input_client); + ui::IMEBridge::Get()->SetInputContextHandler(&ime); + + ukm::TestAutoSetUkmRecorder test_recorder; + test_recorder.EnableRecording(false /* extensions */); + ASSERT_EQ(0u, test_recorder.entries_count()); + + // Should not record when random text is entered. + engine.SetSurroundingText(u"random text ", 12, 12, 0); + EXPECT_EQ(0u, test_recorder.entries_count()); + + // Should record when match is triggered. + engine.SetSurroundingText(u"my email is ", 12, 12, 0); + EXPECT_EQ(0u, test_recorder.sources_count()); + EXPECT_EQ(1u, test_recorder.entries_count()); + const auto entries = + test_recorder.GetEntriesByName("InputMethod.Assistive.Match"); + ukm::TestAutoSetUkmRecorder::ExpectEntryMetric( + entries[0], "Type", (int)AssistiveType::kPersonalEmail); + + InputMethodManager::Shutdown(); +} + } // namespace } // namespace chromeos
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job.cc index 1cde15b..0248274 100644 --- a/chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job.cc +++ b/chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job.cc
@@ -27,7 +27,7 @@ // The time that we wait for the server to get the ACK, if that passes we // immediately start the powerwash process. constexpr base::TimeDelta kFailsafeTimerTimeout = - base::TimeDelta::FromSeconds(1); + base::TimeDelta::FromSeconds(10); void StartPowerwash(enterprise_management::SignedData signed_command) { chromeos::SessionManagerClient::Get()->StartRemoteDeviceWipe(signed_command);
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job_unittest.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job_unittest.cc index 54639a64..e9637e4 100644 --- a/chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job_unittest.cc +++ b/chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job_unittest.cc
@@ -160,13 +160,13 @@ run_loop_.QuitClosure())); run_loop_.Run(); - // After 500ms the timer is not run yet. - task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(500)); + // After 5s the timer is not run yet. + task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(5)); EXPECT_EQ(0, chromeos::FakeSessionManagerClient::Get() ->start_device_wipe_call_count()); - // After 1s the timer is run. - task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(500)); + // After 10s the timer is run. + task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(5)); EXPECT_EQ(1, chromeos::FakeSessionManagerClient::Get() ->start_device_wipe_call_count()); }
diff --git a/chrome/browser/extensions/api/file_system/consent_provider.cc b/chrome/browser/extensions/api/file_system/consent_provider.cc index 2b72eb9..635d854 100644 --- a/chrome/browser/extensions/api/file_system/consent_provider.cc +++ b/chrome/browser/extensions/api/file_system/consent_provider.cc
@@ -33,8 +33,8 @@ // chrome.fileSystem.requestFileSystem. const char* const kRequestFileSystemComponentAllowlist[] = { file_manager::kFileManagerAppId, file_manager::kVideoPlayerAppId, - file_manager::kGalleryAppId, file_manager::kAudioPlayerAppId, - file_manager::kImageLoaderExtensionId, file_manager::kZipArchiverId, + file_manager::kAudioPlayerAppId, file_manager::kImageLoaderExtensionId, + file_manager::kZipArchiverId, // TODO(henryhsu,b/110126438): Remove this extension id, and add it only // for tests. "pkplfbidichfdicaijlchgnapepdginl" // Testing extensions.
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc index 66527ba..430354e 100644 --- a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc +++ b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
@@ -168,7 +168,7 @@ // Tests that tab capture video frames can be received in a VIDEO element. // Disabled due to flakes on multiple platforms; see https://crbug.com/1040894. -IN_PROC_BROWSER_TEST_F(TabCaptureApiPixelTest, DISABLED_EndToEndWithoutRemoting) { +IN_PROC_BROWSER_TEST_F(TabCaptureApiPixelTest, EndToEndWithoutRemoting) { if (IsTooIntensiveForThisPlatform()) { LOG(WARNING) << "Skipping this CPU-intensive test on this platform/build."; return;
diff --git a/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc b/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc index 0a9395b..0680c1c 100644 --- a/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc +++ b/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc
@@ -84,7 +84,6 @@ case IDR_DEMO_APP_MANIFEST: case IDR_ECHO_MANIFEST: case IDR_FILEMANAGER_MANIFEST: - case IDR_GALLERY_MANIFEST: case IDR_IMAGE_LOADER_MANIFEST: case IDR_KEYBOARD_MANIFEST: case IDR_MOBILE_MANIFEST:
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc index 95d3448..c2b8263 100644 --- a/chrome/browser/extensions/component_loader.cc +++ b/chrome/browser/extensions/component_loader.cc
@@ -386,16 +386,6 @@ base::FilePath(FILE_PATH_LITERAL("audio_player"))); } -void ComponentLoader::AddGalleryExtension() { - // TODO(crbug.com/1030935): Delete this entirely around M93 when it has has a - // chance to be cleaned up. - if (extensions::ExtensionPrefs::Get(profile_) - ->ShouldInstallObsoleteComponentExtension( - file_manager::kGalleryAppId)) { - Add(IDR_GALLERY_MANIFEST, base::FilePath(FILE_PATH_LITERAL("gallery"))); - } -} - void ComponentLoader::AddImageLoaderExtension() { Add(IDR_IMAGE_LOADER_MANIFEST, base::FilePath(FILE_PATH_LITERAL("image_loader"))); @@ -556,7 +546,6 @@ AddVideoPlayerExtension(); AddAudioPlayerExtension(); AddFileManagerExtension(); - AddGalleryExtension(); AddImageLoaderExtension(); #if BUILDFLAG(ENABLE_NACL)
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc b/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc index 0423809..78ddb5e 100644 --- a/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc +++ b/chrome/browser/extensions/forced_extensions/force_installed_metrics.cc
@@ -295,7 +295,9 @@ base::UmaHistogramEnumeration( "Extensions.ForceInstalledFailureCrxInstallError", detail); } - if (installation.unpacker_failure_reason) { + if (failure_reason == + FailureReason::CRX_INSTALL_ERROR_SANDBOXED_UNPACKER_FAILURE) { + DCHECK(installation.unpacker_failure_reason); base::UmaHistogramEnumeration( "Extensions.ForceInstalledFailureSandboxUnpackFailureReason2", installation.unpacker_failure_reason.value(),
diff --git a/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc b/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc index 245e18e..fe521d7 100644 --- a/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc +++ b/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc
@@ -550,8 +550,6 @@ NavigateAndCommit(notification8); permissions::MockPermissionRequest notification8_request( u"request8", permissions::RequestType::kNotifications, notification8); - // For the rest of the quiet permission prompts, do not show promo. - EXPECT_TRUE(QuietNotificationPermissionUiState::ShouldShowPromo(profile())); manager_->AddRequest(web_contents()->GetMainFrame(), ¬ification8_request); WaitForBubbleToBeShown(); EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
diff --git a/chrome/browser/permissions/permission_manager_factory.cc b/chrome/browser/permissions/permission_manager_factory.cc index 8000877..b8e8c8c 100644 --- a/chrome/browser/permissions/permission_manager_factory.cc +++ b/chrome/browser/permissions/permission_manager_factory.cc
@@ -22,7 +22,6 @@ #include "chrome/browser/storage/durable_storage_permission_context.h" #include "chrome/browser/storage_access_api/storage_access_grant_permission_context.h" #include "chrome/browser/tab_contents/tab_util.h" -#include "chrome/browser/wake_lock/wake_lock_permission_context.h" #include "chrome/browser/window_placement/window_placement_permission_context.h" #include "chrome/common/buildflags.h" #include "chrome/common/url_constants.h" @@ -36,6 +35,7 @@ #include "components/permissions/contexts/midi_sysex_permission_context.h" #include "components/permissions/contexts/payment_handler_permission_context.h" #include "components/permissions/contexts/sensor_permission_context.h" +#include "components/permissions/contexts/wake_lock_permission_context.h" #include "components/permissions/contexts/webxr_permission_context.h" #include "components/permissions/permission_manager.h" #include "ppapi/buildflags/buildflags.h" @@ -110,10 +110,10 @@ permission_contexts[ContentSettingsType::PERIODIC_BACKGROUND_SYNC] = std::make_unique<PeriodicBackgroundSyncPermissionContext>(profile); permission_contexts[ContentSettingsType::WAKE_LOCK_SCREEN] = - std::make_unique<WakeLockPermissionContext>( + std::make_unique<permissions::WakeLockPermissionContext>( profile, ContentSettingsType::WAKE_LOCK_SCREEN); permission_contexts[ContentSettingsType::WAKE_LOCK_SYSTEM] = - std::make_unique<WakeLockPermissionContext>( + std::make_unique<permissions::WakeLockPermissionContext>( profile, ContentSettingsType::WAKE_LOCK_SYSTEM); auto nfc_delegate = std::make_unique<ChromeNfcPermissionContextDelegate>(); #if !defined(OS_ANDROID)
diff --git a/chrome/browser/resources/chromeos/password_change/confirm_password_change.html b/chrome/browser/resources/chromeos/password_change/confirm_password_change.html index cc559d4..60b963d 100644 --- a/chrome/browser/resources/chromeos/password_change/confirm_password_change.html +++ b/chrome/browser/resources/chromeos/password_change/confirm_password_change.html
@@ -3,20 +3,10 @@ <head> <meta charset="utf-8"> - <link rel="import" href="chrome://resources/html/polymer.html"> - <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html"> - <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> - <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html"> - <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> - <link rel="import" href="chrome://resources/html/load_time_data.html"> - <link rel="import" href="chrome://resources/html/i18n_behavior.html"> - <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> - <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html"> <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> - <script src="confirm_password_change.js"></script> - <script src="strings.js"></script> + <script type="module" src="confirm_password_change.js"></script> <dom-module id="confirm-password-change"> <template>
diff --git a/chrome/browser/resources/chromeos/password_change/confirm_password_change.js b/chrome/browser/resources/chromeos/password_change/confirm_password_change.js index efc2e13c..c6cb47d 100644 --- a/chrome/browser/resources/chromeos/password_change/confirm_password_change.js +++ b/chrome/browser/resources/chromeos/password_change/confirm_password_change.js
@@ -15,6 +15,18 @@ // TODO(https://crbug.com/930109): Add logic to show only some of the passwords // fields if some of the passwords were successfully scraped. +import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js'; +import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; +import {Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {sendWithPromise} from 'chrome://resources/js/cr.m.js'; +import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js'; +import 'chrome://confirm-password-change/strings.js'; +import 'chrome://resources/cr_elements/cr_button/cr_button.m.js'; +import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js'; +import 'chrome://resources/cr_elements/cr_input/cr_input.m.js'; +import 'chrome://resources/cr_elements/shared_vars_css.m.js'; +import 'chrome://resources/polymer/v3_0/paper-spinner/paper-spinner-lite.js'; + /** @enum{number} */ const ValidationErrorType = { NO_ERROR: 0, @@ -84,7 +96,7 @@ /** @private */ getInitialState_() { - cr.sendWithPromise('getInitialState').then((result) => { + sendWithPromise('getInitialState').then((result) => { this.showOldPasswordPrompt_ = result.showOldPasswordPrompt; this.showNewPasswordPrompt_ = result.showNewPasswordPrompt; this.showSpinner_ = result.showSpinner;
diff --git a/chrome/browser/resources/tools/optimize_webui.gni b/chrome/browser/resources/tools/optimize_webui.gni index f21fbd98..8b4098d 100644 --- a/chrome/browser/resources/tools/optimize_webui.gni +++ b/chrome/browser/resources/tools/optimize_webui.gni
@@ -30,6 +30,8 @@ rebase_path(target_gen_dir, root_build_dir), "--depfile", rebase_path(depfile, root_build_dir), + "--target_name", + target_name, ] args += [ "--js_out_files" ] + invoker.js_out_files
diff --git a/chrome/browser/resources/tools/optimize_webui.py b/chrome/browser/resources/tools/optimize_webui.py index 32c4bba3..ddbdbf3 100755 --- a/chrome/browser/resources/tools/optimize_webui.py +++ b/chrome/browser/resources/tools/optimize_webui.py
@@ -45,9 +45,11 @@ _BASE_EXCLUDES.append("//" + excluded_file) -def _request_list_path(out_path, host_url): - host = host_url[host_url.find('://') + 3:-1] - return os.path.join(out_path, host + '_requestlist.txt') +def _request_list_path(out_path, target_name): + # Using |target_name| as a prefix which is guaranteed to be unique within the + # same folder, to avoid problems when multiple optimize_webui() targets in the + # same BUILD.gn file exist. + return os.path.join(out_path, target_name + '_requestlist.txt') def _get_dep_path(dep, host_url, in_path): if dep.startswith(host_url): @@ -190,7 +192,7 @@ def _optimize(in_folder, args): in_path = os.path.normpath(os.path.join(_CWD, in_folder)).replace('\\', '/') out_path = os.path.join(_CWD, args.out_folder).replace('\\', '/') - manifest_out_path = _request_list_path(out_path, args.host_url) + manifest_out_path = _request_list_path(out_path, args.target_name) tmp_out_dir = tempfile.mkdtemp(dir=out_path).replace('\\', '/') excludes = _BASE_EXCLUDES + [ @@ -228,6 +230,7 @@ def main(argv): parser = argparse.ArgumentParser() parser.add_argument('--depfile', required=True) + parser.add_argument('--target_name', required=True) parser.add_argument('--exclude', nargs='*') parser.add_argument('--external_paths', nargs='*') parser.add_argument('--host', required=True)
diff --git a/chrome/browser/resources/tools/optimize_webui_test.py b/chrome/browser/resources/tools/optimize_webui_test.py index 15b7a867..77842e9 100755 --- a/chrome/browser/resources/tools/optimize_webui_test.py +++ b/chrome/browser/resources/tools/optimize_webui_test.py
@@ -53,6 +53,7 @@ # TODO(dbeam): make it possible to _run_optimize twice? Is that useful? args = input_args + [ '--depfile', os.path.join(self._out_folder, 'depfile.d'), + '--target_name', 'dummy_target_name', '--input', self._tmp_src_dir, '--out_folder', self._out_folder, ]
diff --git a/chrome/browser/search/drive/drive_service.cc b/chrome/browser/search/drive/drive_service.cc index e1f7fb5..3302f735 100644 --- a/chrome/browser/search/drive/drive_service.cc +++ b/chrome/browser/search/drive/drive_service.cc
@@ -20,11 +20,10 @@ #include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h" #include "components/signin/public/identity_manager/scope_set.h" +#include "google_apis/gaia/gaia_constants.h" #include "net/base/load_flags.h" namespace { -// The scope required for an access token in order to query ItemSuggest. -constexpr char kDriveScope[] = "https://www.googleapis.com/auth/drive.readonly"; #if OS_LINUX constexpr char kPlatform[] = "LINUX"; #elif OS_WIN @@ -181,7 +180,8 @@ } token_fetcher_ = std::make_unique<signin::PrimaryAccountAccessTokenFetcher>( - "ntp_drive_module", identity_manager_, signin::ScopeSet({kDriveScope}), + "ntp_drive_module", identity_manager_, + signin::ScopeSet({GaiaConstants::kDriveReadOnlyOAuth2Scope}), base::BindOnce(&DriveService::OnTokenReceived, weak_factory_.GetWeakPtr()), signin::PrimaryAccountAccessTokenFetcher::Mode::kImmediate,
diff --git a/chrome/browser/signin/chrome_signin_helper.h b/chrome/browser/signin/chrome_signin_helper.h index 0ce6b13c..9e85a81 100644 --- a/chrome/browser/signin/chrome_signin_helper.h +++ b/chrome/browser/signin/chrome_signin_helper.h
@@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/supports_user_data.h" +#include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" #include "components/signin/core/browser/signin_header_helper.h"
diff --git a/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.cc b/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.cc index e7f2099d..64b57d21 100644 --- a/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.cc +++ b/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.cc
@@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" #include "base/supports_user_data.h" +#include "build/build_config.h" #include "build/buildflag.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/chrome_signin_helper.h" @@ -25,6 +26,11 @@ #include "services/network/public/mojom/fetch_api.mojom-shared.h" #include "services/network/public/mojom/url_loader.mojom.h" +#if defined(OS_ANDROID) +#include "chrome/browser/android/tab_android.h" +#include "chrome/browser/android/tab_web_contents_delegate_android.h" +#endif + namespace signin { namespace { @@ -49,7 +55,22 @@ profile->SetUserData(kBrowserContextUserDataKey, base::WrapUnique(self)); } +#if defined(OS_ANDROID) + bool is_custom_tab = false; + content::WebContents* web_contents = web_contents_getter.Run(); + if (web_contents) { + auto* delegate = + TabAndroid::FromWebContents(web_contents) + ? static_cast<android::TabWebContentsDelegateAndroid*>( + web_contents->GetDelegate()) + : nullptr; + is_custom_tab = delegate && delegate->IsCustomTab(); + } + auto delegate = std::make_unique<HeaderModificationDelegateImpl>( + profile, /*incognito_enabled=*/!is_custom_tab); +#else auto delegate = std::make_unique<HeaderModificationDelegateImpl>(profile); +#endif auto proxy = std::make_unique<ProxyingURLLoaderFactory>( std::move(delegate), std::move(web_contents_getter), std::move(receiver), std::move(target_factory),
diff --git a/chrome/browser/signin/header_modification_delegate_impl.cc b/chrome/browser/signin/header_modification_delegate_impl.cc index 4b4e5c7..a91b588e 100644 --- a/chrome/browser/signin/header_modification_delegate_impl.cc +++ b/chrome/browser/signin/header_modification_delegate_impl.cc
@@ -33,9 +33,18 @@ namespace signin { +#if defined(OS_ANDROID) +HeaderModificationDelegateImpl::HeaderModificationDelegateImpl( + Profile* profile, + bool incognito_enabled) + : profile_(profile), + cookie_settings_(CookieSettingsFactory::GetForProfile(profile_)), + incognito_enabled_(incognito_enabled) {} +#else HeaderModificationDelegateImpl::HeaderModificationDelegateImpl(Profile* profile) : profile_(profile), cookie_settings_(CookieSettingsFactory::GetForProfile(profile_)) {} +#endif HeaderModificationDelegateImpl::~HeaderModificationDelegateImpl() = default; @@ -89,9 +98,17 @@ } } + int incognito_mode_availability = + prefs->GetInteger(prefs::kIncognitoModeAvailability); +#if defined(OS_ANDROID) + incognito_mode_availability = incognito_enabled_ + ? incognito_mode_availability + : IncognitoModePrefs::DISABLED; +#endif + FixAccountConsistencyRequestHeader( request_adapter, redirect_url, profile_->IsOffTheRecord(), - prefs->GetInteger(prefs::kIncognitoModeAvailability), + incognito_mode_availability, AccountConsistencyModeManager::GetMethodForProfile(profile_), account.gaia, is_child_account, #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/signin/header_modification_delegate_impl.h b/chrome/browser/signin/header_modification_delegate_impl.h index 01d2c48a..f7ad789 100644 --- a/chrome/browser/signin/header_modification_delegate_impl.h +++ b/chrome/browser/signin/header_modification_delegate_impl.h
@@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_SIGNIN_HEADER_MODIFICATION_DELEGATE_IMPL_H_ #define CHROME_BROWSER_SIGNIN_HEADER_MODIFICATION_DELEGATE_IMPL_H_ +#include "build/build_config.h" #include "build/buildflag.h" #include "chrome/browser/signin/header_modification_delegate.h" #include "components/content_settings/core/browser/cookie_settings.h" @@ -20,7 +21,13 @@ // interface. class HeaderModificationDelegateImpl : public HeaderModificationDelegate { public: +#if defined(OS_ANDROID) + explicit HeaderModificationDelegateImpl(Profile* profile, + bool incognito_enabled); +#else explicit HeaderModificationDelegateImpl(Profile* profile); +#endif + ~HeaderModificationDelegateImpl() override; // HeaderModificationDelegate @@ -45,6 +52,10 @@ Profile* profile_; scoped_refptr<content_settings::CookieSettings> cookie_settings_; +#if defined(OS_ANDROID) + bool incognito_enabled_; +#endif + DISALLOW_COPY_AND_ASSIGN(HeaderModificationDelegateImpl); };
diff --git a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninManager.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninManager.java index e720d7d..2e4319cbc 100644 --- a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninManager.java +++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninManager.java
@@ -154,25 +154,6 @@ void signin(CoreAccountInfo accountInfo, @Nullable SignInCallback callback); /** - * Starts the sign-in flow, enables sync and executes the callback when finished. - * - * The sign-in flow goes through the following steps: - * - * - Wait for AccountTrackerService to be seeded. - * - Wait for policy to be checked for the account. - * - If managed, wait for the policy to be fetched. - * - Complete sign-in with the native IdentityManager. - * - Enable sync. - * - Call the callback if provided. - * - * @param accessPoint {@link SigninAccessPoint} that initiated the sign-in flow. - * @param accountInfo The account to sign in to. - * @param callback Optional callback for when the sign-in process is finished. - */ - void signinAndEnableSync(@SigninAccessPoint int accessPoint, CoreAccountInfo accountInfo, - @Nullable SignInCallback callback); - - /** * Starts the sign-in flow, and executes the callback when finished. * * The sign-in flow goes through the following steps:
diff --git a/chrome/browser/sync/profile_sync_service_android.cc b/chrome/browser/sync/profile_sync_service_android.cc index 7f2d57a5..4755f49 100644 --- a/chrome/browser/sync/profile_sync_service_android.cc +++ b/chrome/browser/sync/profile_sync_service_android.cc
@@ -95,24 +95,22 @@ Java_ProfileSyncService_syncStateChanged(env, java_sync_service_.get(env)); } -// Pure SyncServiceImpl calls. - jboolean ProfileSyncServiceAndroid::IsSyncRequested(JNIEnv* env) { DCHECK_CURRENTLY_ON(BrowserThread::UI); return native_sync_service_->GetUserSettings()->IsSyncRequested(); } -jboolean ProfileSyncServiceAndroid::CanSyncFeatureStart(JNIEnv* env) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - return native_sync_service_->CanSyncFeatureStart(); -} - void ProfileSyncServiceAndroid::SetSyncRequested(JNIEnv* env, jboolean requested) { DCHECK_CURRENTLY_ON(BrowserThread::UI); native_sync_service_->GetUserSettings()->SetSyncRequested(requested); } +jboolean ProfileSyncServiceAndroid::CanSyncFeatureStart(JNIEnv* env) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + return native_sync_service_->CanSyncFeatureStart(); +} + jboolean ProfileSyncServiceAndroid::IsSyncAllowedByPlatform(JNIEnv* env) { DCHECK_CURRENTLY_ON(BrowserThread::UI); return !native_sync_service_->HasDisableReason( @@ -196,13 +194,6 @@ return ModelTypeSetToJavaIntArray(env, model_types); } -ScopedJavaLocalRef<jintArray> ProfileSyncServiceAndroid::GetPreferredDataTypes( - JNIEnv* env) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - return ModelTypeSetToJavaIntArray( - env, native_sync_service_->GetPreferredDataTypes()); -} - void ProfileSyncServiceAndroid::SetChosenDataTypes( JNIEnv* env, jboolean sync_everything, @@ -382,7 +373,12 @@ return syncer::ShouldOfferTrustedVaultOptIn(native_sync_service_); } -// Functionality only available for testing purposes. +void ProfileSyncServiceAndroid::TriggerRefresh(JNIEnv* env) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + // Only allowed to trigger refresh/schedule nudges for protocol types, things + // like PROXY_TABS are not allowed. + native_sync_service_->TriggerRefresh(syncer::ProtocolTypes()); +} jlong ProfileSyncServiceAndroid::GetSyncServiceImplForTest(JNIEnv* env) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -397,21 +393,6 @@ (last_sync_time - base::Time::UnixEpoch()).InMicroseconds()); } -void ProfileSyncServiceAndroid::OverrideNetworkForTest( - const syncer::CreateHttpPostProviderFactory& - create_http_post_provider_factory_cb) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - native_sync_service_->OverrideNetworkForTest( - create_http_post_provider_factory_cb); -} - -void ProfileSyncServiceAndroid::TriggerRefresh(JNIEnv* env) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - // Only allowed to trigger refresh/schedule nudges for protocol types, things - // like PROXY_TABS are not allowed. - native_sync_service_->TriggerRefresh(syncer::ProtocolTypes()); -} - static jlong JNI_ProfileSyncService_Init( JNIEnv* env, const JavaParamRef<jobject>& java_sync_service) {
diff --git a/chrome/browser/sync/profile_sync_service_android.h b/chrome/browser/sync/profile_sync_service_android.h index 6c7ee197..c8da817e 100644 --- a/chrome/browser/sync/profile_sync_service_android.h +++ b/chrome/browser/sync/profile_sync_service_android.h
@@ -36,7 +36,7 @@ // syncer::SyncServiceObserver: void OnStateChanged(syncer::SyncService* sync) override; - // Pure SyncServiceImpl calls. + // Please keep all methods below in the same order as ProfileSyncService.java. jboolean IsSyncRequested(JNIEnv* env); void SetSyncRequested(JNIEnv* env, jboolean requested); @@ -55,8 +55,6 @@ jint source); base::android::ScopedJavaLocalRef<jintArray> GetActiveDataTypes(JNIEnv* env); base::android::ScopedJavaLocalRef<jintArray> GetChosenDataTypes(JNIEnv* env); - base::android::ScopedJavaLocalRef<jintArray> GetPreferredDataTypes( - JNIEnv* env); void SetChosenDataTypes( JNIEnv* env, jboolean sync_everything, @@ -81,40 +79,25 @@ const base::android::JavaParamRef<jobject>& callback); jint GetAuthError(JNIEnv* env); jboolean HasUnrecoverableError(JNIEnv* env); - jboolean IsUrlKeyedDataCollectionEnabled( - JNIEnv* env, - jboolean personalized); jboolean RequiresClientUpgrade(JNIEnv* env); void SetDecoupledFromAndroidMasterSync(JNIEnv* env); jboolean GetDecoupledFromAndroidMasterSync(JNIEnv* env); base::android::ScopedJavaLocalRef<jobject> GetAuthenticatedAccountInfo( JNIEnv* env); jboolean IsAuthenticatedAccountPrimary(JNIEnv* env); - - // Pure SyncPrefs calls. jboolean IsPassphrasePromptMutedForCurrentProductVersion(JNIEnv* env); void MarkPassphrasePromptMutedForCurrentProductVersion(JNIEnv* env); jboolean HasKeepEverythingSynced(JNIEnv* env); - void RecordKeyRetrievalTrigger( JNIEnv* env, jint trigger); - jboolean ShouldOfferTrustedVaultOptIn(JNIEnv* env); - - // Functionality only available for testing purposes. - + void TriggerRefresh(JNIEnv* env); jlong GetSyncServiceImplForTest(JNIEnv* env); - // Returns a timestamp for when a sync was last executed. The return value is // the internal value of base::Time. jlong GetLastSyncedTimeForTest(JNIEnv* env); - void OverrideNetworkForTest(const syncer::CreateHttpPostProviderFactory& - create_http_post_provider_factory_cb); - - void TriggerRefresh(JNIEnv* env); - private: // A reference to the sync service for this profile. syncer::SyncServiceImpl* const native_sync_service_;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 71c55fc4..2823737e 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1095,6 +1095,8 @@ "omnibox/omnibox_theme.cc", "omnibox/omnibox_theme.h", "page_action/page_action_icon_type.h", + "page_info/chrome_accuracy_tip_ui.cc", + "page_info/chrome_accuracy_tip_ui.h", "page_info/page_info_dialog.cc", "page_info/page_info_dialog.h", "page_info/page_info_infobar_delegate.cc", @@ -4053,6 +4055,8 @@ "views/page_action/pwa_install_view.h", "views/page_action/zoom_view.cc", "views/page_action/zoom_view.h", + "views/page_info/accuracy_tip_bubble_view.cc", + "views/page_info/accuracy_tip_bubble_view.h", "views/page_info/chosen_object_view.cc", "views/page_info/chosen_object_view.h", "views/page_info/chosen_object_view_observer.h",
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_az.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_az.xtb index f61c2a0..79620f16 100644 --- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_az.xtb +++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_az.xtb
@@ -99,7 +99,7 @@ <translation id="1668122540633280551">Video önizləmələrinin Chrome əsas səhifəsində avtomatik olaraq necə oxudulacağını seçin.</translation> <translation id="1670399744444387456">Əsas</translation> <translation id="1671236975893690980">Endirilmə gözlənilir...</translation> -<translation id="1672586136351118594">Daha göstərməyin</translation> +<translation id="1672586136351118594">Göstərilməsin</translation> <translation id="1680919990519905526">Google Linza ilə şəkil alın <ph name="BEGIN_NEW" />Yeni<ph name="END_NEW" /></translation> <translation id="1682195225331129001">İndi sınayın</translation> <translation id="1692118695553449118">Sinxronizasiya aktivdir</translation> @@ -172,7 +172,7 @@ <translation id="2126426811489709554">Chrome Powered by</translation> <translation id="2131665479022868825"><ph name="DATA" /> yadda saxlandı</translation> <translation id="213279576345780926"><ph name="TAB_TITLE" /> qapadıldı</translation> -<translation id="2139186145475833000">Ev ekranına əlavə edin</translation> +<translation id="2139186145475833000">Əsas ekrana əlavə edin</translation> <translation id="2141396931810938595">İstifadənizə əsasən</translation> <translation id="214888715418183969">Chrome ilə nəyi paylaşmağa hazır olduğunuzu seçin. Paylaşdığınız göstəricilər Chrome funksiyaları, məhsuldarlığı və stabilliyini yaxşılaşdırmaq üçün istifadə ediləcək.</translation> <translation id="2154484045852737596">Kartı redaktə edin</translation>
diff --git a/chrome/browser/ui/app_list/search/files/item_suggest_cache.cc b/chrome/browser/ui/app_list/search/files/item_suggest_cache.cc index 4fba17d..b284a53 100644 --- a/chrome/browser/ui/app_list/search/files/item_suggest_cache.cc +++ b/chrome/browser/ui/app_list/search/files/item_suggest_cache.cc
@@ -18,6 +18,7 @@ #include "components/signin/public/identity_manager/consent_level.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/scope_set.h" +#include "google_apis/gaia/gaia_constants.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" #include "net/http/http_request_headers.h" @@ -62,9 +63,6 @@ } })"); -// The scope required for an access token in order to query ItemSuggest. -constexpr char kDriveScope[] = "https://www.googleapis.com/auth/drive.readonly"; - bool IsDisabledByPolicy(const Profile* profile) { return profile->GetPrefs()->GetBoolean(drive::prefs::kDisableDrive); } @@ -250,11 +248,10 @@ return; } - signin::ScopeSet scopes({kDriveScope}); - // Fetch an OAuth2 access token. token_fetcher_ = std::make_unique<signin::PrimaryAccountAccessTokenFetcher>( - "launcher_item_suggest", identity_manager, scopes, + "launcher_item_suggest", identity_manager, + signin::ScopeSet({GaiaConstants::kDriveReadOnlyOAuth2Scope}), base::BindOnce(&ItemSuggestCache::OnTokenReceived, weak_factory_.GetWeakPtr()), signin::PrimaryAccountAccessTokenFetcher::Mode::kImmediate,
diff --git a/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc b/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc index edd5233b..b0c7ea1 100644 --- a/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc +++ b/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc
@@ -173,5 +173,3 @@ void ChromeCaptureModeDelegate::OnSessionStateChanged(bool started) { is_session_active_ = started; } - -void ChromeCaptureModeDelegate::OnServiceRemoteReset() {}
diff --git a/chrome/browser/ui/ash/chrome_capture_mode_delegate.h b/chrome/browser/ui/ash/chrome_capture_mode_delegate.h index 2b8054b..d5bb70d 100644 --- a/chrome/browser/ui/ash/chrome_capture_mode_delegate.h +++ b/chrome/browser/ui/ash/chrome_capture_mode_delegate.h
@@ -51,7 +51,6 @@ mojo::PendingReceiver<media::mojom::AudioStreamFactory> receiver) override; void OnSessionStateChanged(bool started) override; - void OnServiceRemoteReset() override; private: // Used to temporarily disable capture mode in certain cases for which neither
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc index 9991062..495e5d5 100644 --- a/chrome/browser/ui/ash/chrome_new_window_client.cc +++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -18,6 +18,7 @@ #include "chrome/browser/apps/app_service/app_service_metrics.h" #include "chrome/browser/apps/app_service/app_service_proxy.h" #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" +#include "chrome/browser/apps/app_service/intent_util.h" #include "chrome/browser/apps/app_service/launch_utils.h" #include "chrome/browser/ash/apps/apk_web_app_service.h" #include "chrome/browser/ash/apps/metrics/intent_handling_metrics.h" @@ -53,6 +54,7 @@ #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h" #include "chrome/browser/web_applications/components/app_registrar.h" #include "chrome/browser/web_applications/components/web_app_helpers.h" +#include "chrome/browser/web_applications/components/web_app_id.h" #include "chrome/browser/web_applications/components/web_app_provider_base.h" #include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h" #include "chrome/common/extensions/extension_constants.h" @@ -61,6 +63,10 @@ #include "components/arc/arc_util.h" #include "components/arc/intent_helper/arc_intent_helper_bridge.h" #include "components/arc/intent_helper/custom_tab.h" +#include "components/arc/mojom/intent_helper.mojom.h" +#include "components/services/app_service/public/cpp/intent_util.h" +#include "components/services/app_service/public/cpp/types_util.h" +#include "components/services/app_service/public/mojom/types.mojom.h" #include "components/sessions/core/tab_restore_service.h" #include "components/sessions/core/tab_restore_service_observer.h" #include "components/url_formatter/url_fixer.h" @@ -252,6 +258,23 @@ return result; } +apps::mojom::IntentPtr ConvertLaunchIntent( + const arc::mojom::LaunchIntentPtr& launch_intent) { + apps::mojom::IntentPtr intent = apps::mojom::Intent::New(); + + const char* action = + apps_util::ConvertArcToAppServiceIntentAction(launch_intent->action); + if (action) + intent->action = action; + + intent->url = launch_intent->data; + intent->mime_type = launch_intent->type; + intent->share_title = launch_intent->extra_subject; + intent->share_text = launch_intent->extra_text; + + return intent; +} + } // namespace ChromeNewWindowClient::ChromeNewWindowClient() @@ -686,6 +709,58 @@ NOTREACHED(); } +void ChromeNewWindowClient::OpenAppWithIntent( + const GURL& start_url, + arc::mojom::LaunchIntentPtr arc_intent) { + DCHECK(start_url.is_valid()); + DCHECK(start_url.SchemeIs(url::kHttpsScheme)); + + // Fetch the profile associated with ARC. This method should only be called + // for a |url| which was installed via ARC, and so we want the web app that is + // opened through here to be installed in the profile associated with ARC. + const auto* user = user_manager::UserManager::Get()->GetPrimaryUser(); + DCHECK(user); + + // |profile| may be null if sign-in has happened but the profile isn't loaded + // yet. + Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(user); + if (!profile) + return; + + web_app::AppId app_id = web_app::GenerateAppIdFromURL(start_url); + + bool app_installed = false; + auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile); + proxy->AppRegistryCache().ForOneApp( + app_id, [&app_installed](const apps::AppUpdate& update) { + app_installed = apps_util::IsInstalled(update.Readiness()); + }); + + if (!app_installed) { + if (arc_intent->data) + OpenUrlFromArc(*arc_intent->data); + return; + } + + apps::mojom::IntentPtr intent = ConvertLaunchIntent(arc_intent); + + auto launch_container = apps::mojom::LaunchContainer::kLaunchContainerWindow; + auto disposition = WindowOpenDisposition::NEW_WINDOW; + proxy->AppRegistryCache().ForOneApp( + app_id, [&launch_container, &disposition](const apps::AppUpdate& update) { + if (update.WindowMode() == apps::mojom::WindowMode::kBrowser) { + launch_container = apps::mojom::LaunchContainer::kLaunchContainerTab; + disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; + } + }); + + int event_flags = apps::GetEventFlags(launch_container, disposition, + /*prefer_container=*/false); + + proxy->LaunchAppWithIntent(app_id, event_flags, std::move(intent), + apps::mojom::LaunchSource::kFromArc); +} + void ChromeNewWindowClient::LaunchCameraApp(const std::string& queries, int32_t task_id) { apps::RecordAppLaunch(extension_misc::kCameraAppId,
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.h b/chrome/browser/ui/ash/chrome_new_window_client.h index 12da55e..a1fbc3a 100644 --- a/chrome/browser/ui/ash/chrome_new_window_client.h +++ b/chrome/browser/ui/ash/chrome_new_window_client.h
@@ -59,6 +59,8 @@ int32_t task_id, arc::mojom::IntentHelperHost::OnOpenCustomTabCallback callback) override; void OpenChromePageFromArc(arc::mojom::ChromePage page) override; + void OpenAppWithIntent(const GURL& start_url, + arc::mojom::LaunchIntentPtr intent) override; // arc::ControlCameraAppDelegate: void LaunchCameraApp(const std::string& queries, int32_t task_id) override;
diff --git a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc index b605c9f0..9875b4ed 100644 --- a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc +++ b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
@@ -4,6 +4,8 @@ #include "chrome/browser/ui/ash/chrome_new_window_client.h" +#include "chrome/browser/apps/app_service/app_service_proxy.h" +#include "chrome/browser/apps/app_service/app_service_proxy_factory.h" #include "chrome/browser/ash/arc/arc_web_contents_data.h" #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" @@ -17,11 +19,15 @@ #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h" #include "chrome/browser/ui/web_applications/test/web_app_navigation_browsertest.h" #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h" +#include "chrome/browser/web_applications/components/web_application_info.h" #include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h" +#include "chrome/browser/web_applications/test/web_app_install_test_utils.h" #include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/common/webui_url_constants.h" #include "chrome/test/base/in_process_browser_test.h" #include "components/account_id/account_id.h" +#include "components/arc/intent_helper/intent_constants.h" +#include "components/services/app_service/public/cpp/share_target.h" #include "components/session_manager/core/session_manager.h" #include "components/user_manager/known_user.h" #include "components/user_manager/user_manager.h" @@ -230,6 +236,94 @@ EXPECT_EQ(GURL(chrome::kChromeUIHistoryURL), contents->GetVisibleURL()); } +IN_PROC_BROWSER_TEST_F(ChromeNewWindowClientWebAppBrowserTest, + OpenAppWithIntent) { + ASSERT_TRUE(https_server().Start()); + const GURL app_url = https_server().GetURL(GetAppUrlHost(), GetAppUrlPath()); + + // InstallTestWebApp() but with a ShareTarget definition added. + auto web_app_info = std::make_unique<WebApplicationInfo>(); + web_app_info->start_url = app_url; + web_app_info->scope = + https_server().GetURL(GetAppUrlHost(), GetAppScopePath()); + web_app_info->title = base::UTF8ToUTF16(GetAppName()); + web_app_info->open_as_window = true; + apps::ShareTarget share_target; + share_target.method = apps::ShareTarget::Method::kGet; + share_target.action = app_url; + share_target.params.text = "text"; + web_app_info->share_target = share_target; + std::string id = + web_app::test::InstallWebApp(profile(), std::move(web_app_info)); + apps::AppServiceProxyFactory::GetForProfile(profile()) + ->FlushMojoCallsForTesting(); + + const char* arc_transition_key = + arc::ArcWebContentsData::ArcWebContentsData::kArcTransitionFlag; + + { + // Calling OpenAppWithIntent for a not installed HTTPS URL should open in + // an ordinary browser tab. + const GURL url("https://www.google.com"); + arc::mojom::LaunchIntentPtr intent = arc::mojom::LaunchIntent::New(); + intent->action = arc::kIntentActionView; + intent->data = url; + + auto observer = GetTestNavigationObserver(url); + ChromeNewWindowClient::Get()->OpenAppWithIntent(url, std::move(intent)); + observer->WaitForNavigationFinished(); + + EXPECT_EQ(1u, chrome::GetTotalBrowserCount()); + EXPECT_FALSE(GetLastActiveBrowser()->is_type_app()); + content::WebContents* contents = + GetLastActiveBrowser()->tab_strip_model()->GetActiveWebContents(); + EXPECT_EQ(url, contents->GetLastCommittedURL()); + EXPECT_NE(nullptr, contents->GetUserData(arc_transition_key)); + } + + { + // Calling OpenAppWithIntent for an installed web app URL should open the + // intent in an app window. + GURL launch_url = + https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath()); + arc::mojom::LaunchIntentPtr intent = arc::mojom::LaunchIntent::New(); + intent->action = arc::kIntentActionView; + intent->data = launch_url; + + auto observer = GetTestNavigationObserver(launch_url); + ChromeNewWindowClient::Get()->OpenAppWithIntent(app_url, std::move(intent)); + observer->WaitForNavigationFinished(); + + EXPECT_EQ(2u, chrome::GetTotalBrowserCount()); + EXPECT_TRUE(GetLastActiveBrowser()->is_type_app()); + content::WebContents* contents = + GetLastActiveBrowser()->tab_strip_model()->GetActiveWebContents(); + EXPECT_EQ(launch_url, contents->GetLastCommittedURL()); + EXPECT_NE(nullptr, contents->GetUserData(arc_transition_key)); + } + { + // Calling OpenAppWithIntent for an installed web app URL with shared + // content should open the app with the share data passed through. + arc::mojom::LaunchIntentPtr intent = arc::mojom::LaunchIntent::New(); + intent->action = arc::kIntentActionSend; + intent->extra_text = "shared_text"; + + GURL::Replacements add_query; + add_query.SetQueryStr("text=shared_text"); + GURL launch_url = app_url.ReplaceComponents(add_query); + + auto observer = GetTestNavigationObserver(launch_url); + ChromeNewWindowClient::Get()->OpenAppWithIntent(app_url, std::move(intent)); + observer->WaitForNavigationFinished(); + + EXPECT_EQ(3u, chrome::GetTotalBrowserCount()); + EXPECT_TRUE(GetLastActiveBrowser()->is_type_app()); + content::WebContents* contents = + GetLastActiveBrowser()->tab_strip_model()->GetActiveWebContents(); + EXPECT_EQ(launch_url, contents->GetLastCommittedURL()); + } +} + void TestOpenChromePage(ChromePage page, const GURL& expected_url) { ChromeNewWindowClient::Get()->OpenChromePageFromArc(page); content::WebContents* contents =
diff --git a/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.cc index e07b2e0..c9023b4 100644 --- a/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.cc +++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.cc
@@ -85,9 +85,11 @@ void AppServiceAppWindowArcTracker::HandleWindowVisibilityChanged( aura::Window* window) { - auto task_id = arc::GetWindowTaskId(window); - if (!task_id.has_value() || *task_id == arc::kSystemWindowTaskId) + auto task_or_session_id = arc::GetWindowTaskOrSessionId(window); + if (!task_or_session_id.has_value() || + *task_or_session_id == arc::kSystemWindowTaskId) { return; + } // Attach window to multi-user manager now to let it manage visibility state // of the ARC window correctly. @@ -301,18 +303,18 @@ void AppServiceAppWindowArcTracker::AttachControllerToWindow( aura::Window* window) { - auto task_id = arc::GetWindowTaskId(window); - if (!task_id.has_value()) + auto task_or_session_id = arc::GetWindowTaskOrSessionId(window); + if (!task_or_session_id.has_value()) return; // System windows are also arc apps. window->SetProperty(aura::client::kAppType, static_cast<int>(ash::AppType::ARC_APP)); - if (*task_id == arc::kSystemWindowTaskId) + if (*task_or_session_id == arc::kSystemWindowTaskId) return; - auto it = task_id_to_arc_app_window_info_.find(*task_id); + auto it = task_id_to_arc_app_window_info_.find(*task_or_session_id); if (it == task_id_to_arc_app_window_info_.end()) return; @@ -329,7 +331,7 @@ DCHECK(widget); info->set_window(window); const ash::ShelfID shelf_id = info->shelf_id(); - AttachControllerToTask(*task_id); + AttachControllerToTask(*task_or_session_id); app_service_controller_->AddWindowToShelf(window, shelf_id); AppWindowBase* app_window = app_service_controller_->GetAppWindow(window); if (app_window)
diff --git a/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.cc index b8067f1..1d7250b 100644 --- a/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.cc +++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.cc
@@ -83,7 +83,7 @@ // Transient windows are set up after window init, so remove them here. // Crostini shouldn't need to know about ARC app windows. if (wm::GetTransientParent(window) || - arc::GetWindowTaskId(window).has_value() || + arc::GetWindowTaskOrSessionId(window).has_value() || crosapi::browser_util::IsLacrosWindow(window) || plugin_vm::IsPluginVmAppWindow(window)) { return; @@ -203,7 +203,7 @@ // Transient windows are set up after window init, so remove them here. // Crostini shouldn't need to know about ARC app windows. if (wm::GetTransientParent(window) || - arc::GetWindowTaskId(window).has_value() || + arc::GetWindowTaskOrSessionId(window).has_value() || crosapi::browser_util::IsLacrosWindow(window) || plugin_vm::IsPluginVmAppWindow(window)) { return std::string();
diff --git a/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.cc index 53bc1c8c..eb3cb9a7 100644 --- a/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.cc +++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.cc
@@ -292,7 +292,7 @@ // Note, for ARC apps, window may be recreated in some cases, so do not close // controller on window destroying. Controller will be closed onTaskDestroyed // event which is generated when actual task is destroyed. - if (arc_tracker_ && arc::GetWindowTaskId(window).has_value()) { + if (arc_tracker_ && arc::GetWindowTaskOrSessionId(window).has_value()) { arc_tracker_->HandleWindowDestroying(window); aura_window_to_app_window_.erase(window); return; @@ -418,7 +418,7 @@ // TODO(jamescook): Clean up this block. The code is repetitive. AppWindowBase* app_window; - if (arc::GetWindowTaskId(window).has_value()) { + if (arc::GetWindowTaskOrSessionId(window).has_value()) { std::unique_ptr<ArcAppWindow> app_window_ptr = std::make_unique<ArcAppWindow>( arc::ArcAppShelfId::FromString(shelf_id.app_id), @@ -508,7 +508,7 @@ // For the ARC apps window, AttachControllerToWindow calls AddWindowToShelf, // so we don't need to call AddWindowToShelf again. - if (arc_tracker_ && arc::GetWindowTaskId(window).has_value()) { + if (arc_tracker_ && arc::GetWindowTaskOrSessionId(window).has_value()) { arc_tracker_->AttachControllerToWindow(window); return; }
diff --git a/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu_browsertest.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu_browsertest.cc index 542a5fc..1d8266f 100644 --- a/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu_browsertest.cc +++ b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu_browsertest.cc
@@ -18,8 +18,8 @@ #include "chrome/browser/web_applications/components/web_app_id.h" #include "chrome/browser/web_applications/components/web_application_info.h" #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" -#include "chrome/common/chrome_features.h" #include "chrome/test/base/in_process_browser_test.h" +#include "content/public/common/content_features.h" #include "content/public/test/browser_test.h" #include "ui/base/models/simple_menu_model.h" #include "ui/display/display.h"
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc index f5ec500..21160b67 100644 --- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc +++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
@@ -4788,8 +4788,7 @@ InitShelfController(); const char* kPinnableApp = file_manager::kFileManagerAppId; - const char* kNoPinApps[] = {file_manager::kGalleryAppId, - extension_misc::kFeedbackExtensionId}; + const char* kNoPinApps[] = {extension_misc::kFeedbackExtensionId}; EXPECT_EQ(AppListControllerDelegate::PIN_EDITABLE, GetPinnableForAppID(kPinnableApp, profile()));
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc index 4670789..8d7db5f7 100644 --- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc +++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc
@@ -96,7 +96,6 @@ // context when launching. Pinning these creates an item that does nothing. const char* kNoPinAppIds[] = { file_manager::kVideoPlayerAppId, - file_manager::kGalleryAppId, file_manager::kAudioPlayerAppId, extension_misc::kFeedbackExtensionId, };
diff --git a/chrome/browser/ui/page_info/chrome_accuracy_tip_ui.cc b/chrome/browser/ui/page_info/chrome_accuracy_tip_ui.cc new file mode 100644 index 0000000..a717b834c --- /dev/null +++ b/chrome/browser/ui/page_info/chrome_accuracy_tip_ui.cc
@@ -0,0 +1,16 @@ +// Copyright 2021 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 "chrome/browser/ui/page_info/chrome_accuracy_tip_ui.h" + +#include "base/callback.h" +#include "components/accuracy_tips/accuracy_tip_status.h" + +void ChromeAccuracyTipUI::ShowAccuracyTip( + content::WebContents* web_contents, + accuracy_tips::AccuracyTipStatus status, + base::OnceCallback<void(Interaction)> close_callback) { + // TODO(crbug.com/1210891): Try to use DialogModel instead of custom view. + ShowAccuracyTipDialog(web_contents, status, std::move(close_callback)); +}
diff --git a/chrome/browser/ui/page_info/chrome_accuracy_tip_ui.h b/chrome/browser/ui/page_info/chrome_accuracy_tip_ui.h new file mode 100644 index 0000000..184abf1 --- /dev/null +++ b/chrome/browser/ui/page_info/chrome_accuracy_tip_ui.h
@@ -0,0 +1,26 @@ +// Copyright 2021 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 CHROME_BROWSER_UI_PAGE_INFO_CHROME_ACCURACY_TIP_UI_H_ +#define CHROME_BROWSER_UI_PAGE_INFO_CHROME_ACCURACY_TIP_UI_H_ + +#include "components/accuracy_tips/accuracy_tip_status.h" +#include "components/accuracy_tips/accuracy_tip_ui.h" + +class ChromeAccuracyTipUI : public accuracy_tips::AccuracyTipUI { + public: + void ShowAccuracyTip( + content::WebContents* web_contents, + accuracy_tips::AccuracyTipStatus type, + base::OnceCallback<void(Interaction)> close_callback) override; +}; + +// Definition for platform specific view implementation. +void ShowAccuracyTipDialog( + content::WebContents* web_contents, + accuracy_tips::AccuracyTipStatus type, + base::OnceCallback<void(accuracy_tips::AccuracyTipUI::Interaction)> + close_callback); + +#endif // CHROME_BROWSER_UI_PAGE_INFO_CHROME_ACCURACY_TIP_UI_H_
diff --git a/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle_browsertest.cc b/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle_browsertest.cc index e64e605..01f9dc3 100644 --- a/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle_browsertest.cc +++ b/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle_browsertest.cc
@@ -8,6 +8,7 @@ #include <utility> #include "base/run_loop.h" +#include "base/strings/string_util.h" #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "base/time/time.h" @@ -644,7 +645,8 @@ const GURL kNavigateUrl = GURL("https://passwords.google.com/checkup"); // The well known password change URL. The above URL will request a prerender - // of this page via the <link rel='prerender'> tag in the response. + // of this page via the <script type="speculationrules"> script in the + // response. // TODO(bokan): Normally the change-password URL would lead to a different // origin (e.g. example.com) but prerender2 doesn't yet support cross-origin // prerendering. Using passwords.google.com here is a bit unrealistic but @@ -664,15 +666,24 @@ // is running so fail the test. CHECK_NE(params->url_request.url.path(), kWellKnownNotExistingResourcePath); + std::string speculation_script = base::ReplaceStringPlaceholders( + R"( + <script type="speculationrules"> + { + "prerender":[ + {"source": "list", + "urls": ["$1"]} + ] + } + </script> + )", + {kWellKnownUrl.spec()}, nullptr); if (params->url_request.url == kNavigateUrl) { URLLoaderInterceptor::WriteResponse( "HTTP/1.1 200 OK\n" "Content-Type: text/html\n\n", - base::StringPrintf( - "<!DOCTYPE html><link rel='prerender' href='%s'>", - kWellKnownUrl.spec().c_str()), - params->client.get()); + speculation_script, params->client.get()); return true; }
diff --git a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc index 07afb24e..10c7b15 100644 --- a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc +++ b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
@@ -7,6 +7,7 @@ #include <utility> #include "base/strings/utf_string_conversions.h" +#include "base/token.h" #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h" #include "chrome/browser/ui/content_settings/content_setting_image_model.h" @@ -101,6 +102,7 @@ if (!content_setting_image_model_->is_visible()) { SetVisible(false); + current_iph_id_for_testing_.reset(); return; } DCHECK(web_contents); @@ -247,7 +249,12 @@ auto* promo_controller = FeaturePromoControllerViews::GetForView(this); DCHECK(promo_controller); - promo_controller->ShowCriticalPromo(bubble_params); + current_iph_id_for_testing_ = + promo_controller->ShowCriticalPromo(bubble_params); + content_setting_image_model_->SetPromoWasShown(web_contents); + } else { + // Set a token that is is_zero() to make it not empty for testing. + current_iph_id_for_testing_.emplace(0, 0); } }
diff --git a/chrome/browser/ui/views/location_bar/content_setting_image_view.h b/chrome/browser/ui/views/location_bar/content_setting_image_view.h index 3f7709f..6f6acd9b 100644 --- a/chrome/browser/ui/views/location_bar/content_setting_image_view.h +++ b/chrome/browser/ui/views/location_bar/content_setting_image_view.h
@@ -32,6 +32,10 @@ class BubbleDialogDelegateView; } +namespace base { +class Token; +} + // The ContentSettingImageView displays an icon and optional text label for // various content settings affordances in the location bar (i.e. plugin // blocking, geolocation). @@ -88,6 +92,13 @@ ContentSettingImageModel::ImageType GetTypeForTesting() const; + void reset_animation_for_testing() { + IconLabelBubbleView::ResetSlideAnimation(true); + } + absl::optional<base::Token> get_critical_promo_id_for_testing() { + return current_iph_id_for_testing_; + } + private: // views::WidgetObserver: void OnWidgetDestroying(views::Widget* widget) override; @@ -104,6 +115,11 @@ base::ScopedObservation<views::Widget, views::WidgetObserver> observation_{ this}; bool can_animate_ = true; + + // Has a value that is not is_zero() if a promo is showing, or has an + // is_zero() value if the promo was considered but it was decided not to show + // it. + absl::optional<base::Token> current_iph_id_for_testing_; }; #endif // CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_CONTENT_SETTING_IMAGE_VIEW_H_
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc index c996365b..da92f8e8 100644 --- a/chrome/browser/ui/views/overlay/overlay_window_views.cc +++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -291,14 +291,13 @@ UpdateMaxSize(work_area); - gfx::Size window_size = window_bounds_.size(); + const gfx::Rect bounds = native_widget() ? GetBounds() : gfx::Rect(); + + gfx::Size window_size = bounds.size(); if (!has_been_shown_) { window_size = gfx::Size(work_area.width() / 5, work_area.height() / 5); - window_size.set_width(std::min( - max_size_.width(), std::max(min_size_.width(), window_size.width()))); - window_size.set_height( - std::min(max_size_.height(), - std::max(min_size_.height(), window_size.height()))); + window_size.SetToMin(max_size_); + window_size.SetToMax(min_size_); } // Determine the window size by fitting |natural_size_| within @@ -306,8 +305,7 @@ if (!window_size.IsEmpty() && !natural_size_.IsEmpty()) { float aspect_ratio = (float)natural_size_.width() / natural_size_.height(); - WindowQuadrant quadrant = - GetCurrentWindowQuadrant(GetBounds(), controller_); + WindowQuadrant quadrant = GetCurrentWindowQuadrant(bounds, controller_); gfx::ResizeEdge resize_edge; switch (quadrant) { case OverlayWindowViews::WindowQuadrant::kBottomRight: @@ -325,18 +323,16 @@ } // Update the window size to adhere to the aspect ratio. - gfx::Size min_size = min_size_; - gfx::Size max_size = max_size_; - gfx::Rect window_rect(GetBounds().origin(), window_size); - gfx::SizeRectToAspectRatio(resize_edge, aspect_ratio, min_size, max_size, + gfx::Rect window_rect(bounds.origin(), window_size); + gfx::SizeRectToAspectRatio(resize_edge, aspect_ratio, min_size_, max_size_, &window_rect); - window_size.SetSize(window_rect.width(), window_rect.height()); + window_size = window_rect.size(); UpdateLayerBoundsWithLetterboxing(window_size); } // Use the previous window origin location, if exists. - gfx::Point origin = window_bounds_.origin(); + gfx::Point origin = bounds.origin(); int window_diff_width = work_area.right() - window_size.width(); int window_diff_height = work_area.bottom() - window_size.height(); @@ -355,8 +351,7 @@ origin = default_origin; } - window_bounds_ = gfx::Rect(origin, window_size); - return window_bounds_; + return gfx::Rect(origin, window_size); } void OverlayWindowViews::SetUpViews() { @@ -594,7 +589,7 @@ gfx::Size window_size) { // This is the case when the window is initially created or the video surface // id has not been embedded. - if (window_bounds_.size().IsEmpty() || natural_size_.IsEmpty()) + if (!native_widget() || GetBounds().IsEmpty() || natural_size_.IsEmpty()) return; gfx::Rect letterbox_region = media::ComputeLetterboxRegion( @@ -1006,11 +1001,6 @@ // when the user interacts with the window again. UpdateControlsVisibility(false); - // Update the existing |window_bounds_| when the window moves. This allows - // the window to reappear with the same origin point when a new video is - // shown. - window_bounds_ = GetBounds(); - // Update the maximum size of the widget in case we have moved to another // window. UpdateMaxSize(GetWorkAreaForWindow()); @@ -1259,8 +1249,8 @@ // native_widget() is required for OnSizeConstraintsChanged. OnSizeConstraintsChanged(); - if (window_bounds_.width() <= max_size_.width() && - window_bounds_.height() <= max_size_.height()) { + if (GetBounds().width() <= max_size_.width() && + GetBounds().height() <= max_size_.height()) { return; }
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.h b/chrome/browser/ui/views/overlay/overlay_window_views.h index 37fcb0e..8ef0834 100644 --- a/chrome/browser/ui/views/overlay/overlay_window_views.h +++ b/chrome/browser/ui/views/overlay/overlay_window_views.h
@@ -213,9 +213,6 @@ gfx::Size min_size_; gfx::Size max_size_; - // Current bounds of the Picture-in-Picture window. - gfx::Rect window_bounds_; - // Bounds of |video_view_|. gfx::Rect video_bounds_;
diff --git a/chrome/browser/ui/views/page_info/OWNERS b/chrome/browser/ui/views/page_info/OWNERS index 98400ac..23c4281 100644 --- a/chrome/browser/ui/views/page_info/OWNERS +++ b/chrome/browser/ui/views/page_info/OWNERS
@@ -1,5 +1,6 @@ file://chrome/browser/ui/page_info/OWNERS per-file safety_tip*=jdeblasio@chromium.org +per-file accuracy_tip*=file://components/accuracy_tips/OWNERS per-file page_info_main_view.*=olesiamarukhno@google.com per-file page_info_new_bubble_view.*=olesiamarukhno@google.com
diff --git a/chrome/browser/ui/views/page_info/accuracy_tip_bubble_view.cc b/chrome/browser/ui/views/page_info/accuracy_tip_bubble_view.cc new file mode 100644 index 0000000..12c35d4b --- /dev/null +++ b/chrome/browser/ui/views/page_info/accuracy_tip_bubble_view.cc
@@ -0,0 +1,164 @@ +// Copyright 2021 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 "chrome/browser/ui/views/page_info/accuracy_tip_bubble_view.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "chrome/browser/platform_util.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/page_info/chrome_accuracy_tip_ui.h" +#include "chrome/browser/ui/views/accessibility/theme_tracking_non_accessible_image_view.h" +#include "chrome/browser/ui/views/bubble_anchor_util_views.h" +#include "chrome/browser/ui/views/chrome_layout_provider.h" +#include "chrome/common/url_constants.h" +#include "chrome/grit/theme_resources.h" +#include "components/accuracy_tips/accuracy_tip_status.h" +#include "components/accuracy_tips/accuracy_tip_ui.h" +#include "components/strings/grit/components_strings.h" +#include "content/public/browser/navigation_handle.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/views/bubble/bubble_frame_view.h" +#include "ui/views/controls/label.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/widget/widget.h" +#include "url/gurl.h" + +AccuracyTipBubbleView::AccuracyTipBubbleView( + views::View* anchor_view, + const gfx::Rect& anchor_rect, + gfx::NativeView parent_window, + content::WebContents* web_contents, + accuracy_tips::AccuracyTipStatus status, + base::OnceCallback<void(AccuracyTipUI::Interaction)> close_callback) + : PageInfoBubbleViewBase(anchor_view, + anchor_rect, + parent_window, + PageInfoBubbleViewBase::BUBBLE_ACCURACY_TIP, + web_contents), + close_callback_(std::move(close_callback)) { + DCHECK(status == accuracy_tips::AccuracyTipStatus::kMisinformation); + + views::BubbleDialogDelegateView::CreateBubble(this); + + SetTitle(l10n_util::GetStringUTF16(IDS_PAGE_INFO_ACCURACY_TIP_TITLE)); + + // Configure buttons. + SetButtons(ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL); + SetButtonLabel( + ui::DIALOG_BUTTON_OK, + l10n_util::GetStringUTF16(IDS_PAGE_INFO_ACCURACY_TIP_LEARN_MORE_BUTTON)); + SetButtonLabel( + ui::DIALOG_BUTTON_CANCEL, + l10n_util::GetStringUTF16(IDS_PAGE_INFO_ACCURACY_TIP_IGNORE_BUTTON)); + SetAcceptCallback(base::BindRepeating(&AccuracyTipBubbleView::OpenHelpCenter, + base::Unretained(this))); + + // Configure header view. + auto& bundle = ui::ResourceBundle::GetSharedInstance(); + auto header_view = std::make_unique<ThemeTrackingNonAccessibleImageView>( + *bundle.GetImageSkiaNamed(IDR_SAFETY_TIP_ILLUSTRATION_LIGHT), + *bundle.GetImageSkiaNamed(IDR_SAFETY_TIP_ILLUSTRATION_DARK), + base::BindRepeating(&views::BubbleFrameView::GetBackgroundColor, + base::Unretained(GetBubbleFrameView()))); + GetBubbleFrameView()->SetHeaderView(std::move(header_view)); + + // Configure main content. + auto* provider = ChromeLayoutProvider::Get(); + SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::Orientation::kVertical, gfx::Insets(), + provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL))); + + // TODO(crbug.com/1210891): Replace placeholder strings. + auto text = std::make_unique<views::Label>(std::u16string(), + views::style::CONTEXT_LABEL, + views::style::STYLE_SECONDARY); + text->SetMultiLine(true); + text->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT); + text->SetText(u"Something seems wrong here..."); + AddChildView(std::move(text)); + + Layout(); + SizeToContents(); +} + +AccuracyTipBubbleView::~AccuracyTipBubbleView() = default; + +void AccuracyTipBubbleView::OnWidgetDestroying(views::Widget* widget) { + PageInfoBubbleViewBase::OnWidgetDestroying(widget); + + switch (widget->closed_reason()) { + case views::Widget::ClosedReason::kUnspecified: + // Do not modify action_taken_. This may correspond to the + // WebContentsObserver functions below, in which case a more explicit + // action_taken_ may be set. Otherwise, keep default of kNoAction. + break; + case views::Widget::ClosedReason::kAcceptButtonClicked: + action_taken_ = AccuracyTipUI::Interaction::kLearnMorePressed; + break; + case views::Widget::ClosedReason::kCancelButtonClicked: + action_taken_ = AccuracyTipUI::Interaction::kIgnorePressed; + break; + case views::Widget::ClosedReason::kLostFocus: + action_taken_ = AccuracyTipUI::Interaction::kLostFocus; + break; + case views::Widget::ClosedReason::kEscKeyPressed: + case views::Widget::ClosedReason::kCloseButtonClicked: + action_taken_ = AccuracyTipUI::Interaction::kClosed; + break; + } + std::move(close_callback_).Run(action_taken_); +} + +void AccuracyTipBubbleView::OpenHelpCenter() { + action_taken_ = AccuracyTipUI::Interaction::kLearnMorePressed; + // TODO(crbug.com/1210891): Add link to the right info page. + web_contents()->OpenURL(content::OpenURLParams( + GURL(chrome::kSafetyTipHelpCenterURL), content::Referrer(), + WindowOpenDisposition::NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK, + false /*is_renderer_initiated*/)); +} + +void AccuracyTipBubbleView::DidStartNavigation( + content::NavigationHandle* handle) { + if (!handle->IsInMainFrame() || handle->IsSameDocument()) { + return; + } + GetWidget()->Close(); +} + +void AccuracyTipBubbleView::DidChangeVisibleSecurityState() { + // Do nothing. (Base class closes the bubble.) +} + +// Implementation for c/b/ui/chrome_accuracy_tip_ui.h +void ShowAccuracyTipDialog( + content::WebContents* web_contents, + accuracy_tips::AccuracyTipStatus status, + base::OnceCallback<void(accuracy_tips::AccuracyTipUI::Interaction)> + close_callback) { + Browser* browser = chrome::FindBrowserWithWebContents(web_contents); + if (!browser) + return; + + bubble_anchor_util::AnchorConfiguration configuration = + bubble_anchor_util::GetPageInfoAnchorConfiguration( + browser, bubble_anchor_util::kLocationBar); + gfx::Rect anchor_rect = + configuration.anchor_view + ? gfx::Rect() + : bubble_anchor_util::GetPageInfoAnchorRect(browser); + gfx::NativeWindow parent_window = browser->window()->GetNativeWindow(); + gfx::NativeView parent_view = platform_util::GetViewForWindow(parent_window); + + views::BubbleDialogDelegateView* bubble = new AccuracyTipBubbleView( + configuration.anchor_view, anchor_rect, parent_view, web_contents, status, + std::move(close_callback)); + + bubble->SetHighlightedButton(configuration.highlighted_button); + bubble->SetArrow(configuration.bubble_arrow); + bubble->GetWidget()->Show(); +}
diff --git a/chrome/browser/ui/views/page_info/accuracy_tip_bubble_view.h b/chrome/browser/ui/views/page_info/accuracy_tip_bubble_view.h new file mode 100644 index 0000000..9fe8a9b --- /dev/null +++ b/chrome/browser/ui/views/page_info/accuracy_tip_bubble_view.h
@@ -0,0 +1,65 @@ +// Copyright 2021 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 CHROME_BROWSER_UI_VIEWS_PAGE_INFO_ACCURACY_TIP_BUBBLE_VIEW_H_ +#define CHROME_BROWSER_UI_VIEWS_PAGE_INFO_ACCURACY_TIP_BUBBLE_VIEW_H_ + +#include "base/callback.h" +#include "chrome/browser/ui/views/page_info/page_info_bubble_view_base.h" +#include "components/accuracy_tips/accuracy_tip_status.h" +#include "components/accuracy_tips/accuracy_tip_ui.h" + +namespace content { +class WebContents; +} // namespace content + +namespace gfx { +class Rect; +} // namespace gfx + +namespace views { +class View; +class Widget; +} // namespace views + +// When Chrome displays an accuracy tip, we create a bubble view anchored to the +// lock icon. Accuracy tip info is also displayed in the usual +// PageInfoBubbleView, just less prominently. +class AccuracyTipBubbleView : public PageInfoBubbleViewBase { + public: + using AccuracyTipUI = accuracy_tips::AccuracyTipUI; + + // If |anchor_view| is nullptr, or has no Widget, |parent_window| may be + // provided to ensure this bubble is closed when the parent closes. + // + // |close_callback| will be called when the bubble is destroyed. The argument + // indicates what action (if any) the user took to close the bubble. + AccuracyTipBubbleView( + views::View* anchor_view, + const gfx::Rect& anchor_rect, + gfx::NativeView parent_window, + content::WebContents* web_contents, + accuracy_tips::AccuracyTipStatus status, + base::OnceCallback<void(AccuracyTipUI::Interaction)> close_callback); + ~AccuracyTipBubbleView() override; + + AccuracyTipBubbleView(const AccuracyTipBubbleView&) = delete; + AccuracyTipBubbleView& operator=(const AccuracyTipBubbleView&) = delete; + + // views::WidgetObserver: + void OnWidgetDestroying(views::Widget* widget) override; + + private: + void OpenHelpCenter(); + + // WebContentsObserver: + void DidStartNavigation(content::NavigationHandle* handle) override; + void DidChangeVisibleSecurityState() override; + + base::OnceCallback<void(AccuracyTipUI::Interaction)> close_callback_; + AccuracyTipUI::Interaction action_taken_ = + AccuracyTipUI::Interaction::kNoAction; +}; + +#endif // CHROME_BROWSER_UI_VIEWS_PAGE_INFO_ACCURACY_TIP_BUBBLE_VIEW_H_
diff --git a/chrome/browser/ui/views/page_info/accuracy_tip_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/accuracy_tip_bubble_view_browsertest.cc new file mode 100644 index 0000000..ce3e51e --- /dev/null +++ b/chrome/browser/ui/views/page_info/accuracy_tip_bubble_view_browsertest.cc
@@ -0,0 +1,133 @@ +// Copyright 2021 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 <algorithm> +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" +#include "base/threading/thread_task_runner_handle.h" +#include "chrome/browser/accuracy_tips/accuracy_service_factory.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/views/page_info/page_info_bubble_view_base.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/accuracy_tips/accuracy_service.h" +#include "components/accuracy_tips/accuracy_tip_ui.h" +#include "components/accuracy_tips/features.h" +#include "content/public/test/browser_test.h" +#include "net/dns/mock_host_resolver.h" +#include "ui/views/test/widget_test.h" +#include "ui/views/test/widget_test_api.h" + +namespace { +using accuracy_tips::AccuracyTipStatus; +using accuracy_tips::AccuracyTipUI; + +bool IsUIShowing() { + return PageInfoBubbleViewBase::BUBBLE_ACCURACY_TIP == + PageInfoBubbleViewBase::GetShownBubbleType(); +} + +} // namespace + +class AccuracyTipBubbleViewBrowserTest : public InProcessBrowserTest { + protected: + GURL GetUrl(const std::string& host) { + return embedded_test_server()->GetURL(host, "/title1.html"); + } + + void SetUp() override { + feature_list_.InitAndEnableFeature(accuracy_tips::kAccuracyTipsFeature); + + // Disable "close on deactivation" since there seems to be an issue with + // windows losing focus during tests. + views::DisableActivationChangeHandlingForTests(); + + InProcessBrowserTest::SetUp(); + } + + void SetUpOnMainThread() override { + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(embedded_test_server()->Start()); + AccuracyServiceFactory::GetForProfile(browser()->profile()) + ->SetSampleUrlForTesting(GetUrl("badurl.com")); + } + + base::HistogramTester* histogram_tester() { return &histogram_tester_; } + + private: + base::test::ScopedFeatureList feature_list_; + base::HistogramTester histogram_tester_; +}; + +IN_PROC_BROWSER_TEST_F(AccuracyTipBubbleViewBrowserTest, NoShowOnRegularUrl) { + ui_test_utils::NavigateToURL(browser(), GetUrl("example.com")); + EXPECT_FALSE(IsUIShowing()); + + histogram_tester()->ExpectUniqueSample("Privacy.AccuracyTip.PageStatus", + AccuracyTipStatus::kNone, 1); +} + +IN_PROC_BROWSER_TEST_F(AccuracyTipBubbleViewBrowserTest, ShowOnBadUrl) { + ui_test_utils::NavigateToURL(browser(), GetUrl("badurl.com")); + EXPECT_TRUE(IsUIShowing()); + + histogram_tester()->ExpectUniqueSample("Privacy.AccuracyTip.PageStatus", + AccuracyTipStatus::kMisinformation, 1); +} + +IN_PROC_BROWSER_TEST_F(AccuracyTipBubbleViewBrowserTest, PressIgnoreButton) { + ui_test_utils::NavigateToURL(browser(), GetUrl("badurl.com")); + EXPECT_TRUE(IsUIShowing()); + + auto* view = PageInfoBubbleViewBase::GetPageInfoBubbleForTesting(); + views::test::WidgetDestroyedWaiter waiter(view->GetWidget()); + view->CancelDialog(); + waiter.Wait(); + EXPECT_FALSE(IsUIShowing()); + + histogram_tester()->ExpectUniqueSample( + "Privacy.AccuracyTip.AccuracyTipInteraction", + AccuracyTipUI::Interaction::kIgnorePressed, 1); +} + +IN_PROC_BROWSER_TEST_F(AccuracyTipBubbleViewBrowserTest, DisappearOnNavigate) { + ui_test_utils::NavigateToURL(browser(), GetUrl("badurl.com")); + EXPECT_TRUE(IsUIShowing()); + + // Tip disappears when navigating somewhere else. + auto* view = PageInfoBubbleViewBase::GetPageInfoBubbleForTesting(); + views::test::WidgetDestroyedWaiter waiter(view->GetWidget()); + ui_test_utils::NavigateToURL(browser(), GetUrl("example.com")); + waiter.Wait(); + EXPECT_FALSE(IsUIShowing()); + + histogram_tester()->ExpectUniqueSample( + "Privacy.AccuracyTip.AccuracyTipInteraction", + AccuracyTipUI::Interaction::kNoAction, 1); +} + +IN_PROC_BROWSER_TEST_F(AccuracyTipBubbleViewBrowserTest, OpenLearnMoreLink) { + ui_test_utils::NavigateToURL(browser(), GetUrl("badurl.com")); + EXPECT_TRUE(IsUIShowing()); + + // Click "learn more" and expect help center to open. + auto* view = PageInfoBubbleViewBase::GetPageInfoBubbleForTesting(); + content::WebContentsAddedObserver new_tab_observer; + views::test::WidgetDestroyedWaiter waiter(view->GetWidget()); + view->AcceptDialog(); + EXPECT_EQ(GURL(chrome::kSafetyTipHelpCenterURL), + new_tab_observer.GetWebContents()->GetURL()); + waiter.Wait(); + EXPECT_FALSE(IsUIShowing()); + + histogram_tester()->ExpectUniqueSample( + "Privacy.AccuracyTip.AccuracyTipInteraction", + AccuracyTipUI::Interaction::kLearnMorePressed, 1); +}
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_base.h b/chrome/browser/ui/views/page_info/page_info_bubble_view_base.h index 541a537..1db476c 100644 --- a/chrome/browser/ui/views/page_info/page_info_bubble_view_base.h +++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_base.h
@@ -38,7 +38,9 @@ // Custom bubble for internal pages like chrome:// and chrome-extensions://. BUBBLE_INTERNAL_PAGE, // Custom bubble for displaying safety tips. - BUBBLE_SAFETY_TIP + BUBBLE_SAFETY_TIP, + // Custom bubble for displaying accuracy tips. + BUBBLE_ACCURACY_TIP, }; // Returns the type of the bubble being shown. For testing only.
diff --git a/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view_browsertest.cc b/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view_browsertest.cc index dc8aeb9..0600188 100644 --- a/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view_browsertest.cc
@@ -11,6 +11,8 @@ #include "chrome/browser/custom_handlers/register_protocol_handler_permission_request.h" #include "chrome/browser/download/download_permission_request.h" #include "chrome/browser/permissions/attestation_permission_request.h" +#include "chrome/browser/permissions/quiet_notification_permission_ui_config.h" +#include "chrome/browser/permissions/quiet_notification_permission_ui_state.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" @@ -22,10 +24,17 @@ #include "chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.h" #include "chrome/browser/ui/views/permission_bubble/permission_prompt_impl.h" #include "chrome/browser/ui/views/toolbar/toolbar_view.h" +#include "chrome/browser/ui/views/user_education/feature_promo_controller_views.h" +#include "chrome/common/chrome_features.h" +#include "chrome/common/pref_names.h" #include "chrome/test/base/ui_test_utils.h" #include "chrome/test/permissions/permission_request_manager_test_api.h" +#include "components/content_settings/core/common/pref_names.h" #include "components/permissions/features.h" #include "components/permissions/permission_request_impl.h" +#include "components/permissions/permission_ui_selector.h" +#include "components/permissions/request_type.h" +#include "components/permissions/test/mock_permission_request.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" @@ -337,6 +346,128 @@ ShowAndVerifyUi(); } +class QuietUIPromoBrowserTest : public PermissionPromptBubbleViewBrowserTest { + public: + QuietUIPromoBrowserTest() { + scoped_feature_list_.InitAndEnableFeatureWithParameters( + features::kQuietNotificationPrompts, + {{QuietNotificationPermissionUiConfig::kEnableAdaptiveActivation, + "true"}}); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_P(QuietUIPromoBrowserTest, InvokeUi_QuietUIPromo) { + auto* profile = browser()->profile(); + // Promo is not enabled by default. + EXPECT_FALSE(QuietNotificationPermissionUiState::ShouldShowPromo(profile)); + + for (const char* origin_spec : + {"https://a.com", "https://b.com", "https://c.com"}) { + GURL requesting_origin(origin_spec); + ui_test_utils::NavigateToURL(browser(), requesting_origin); + permissions::MockPermissionRequest notification_request( + u"request", permissions::RequestType::kNotifications, + requesting_origin); + test_api_->manager()->AddRequest(GetActiveMainFrame(), + ¬ification_request); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(test_api_->manager()->ShouldCurrentRequestUseQuietUI()); + EXPECT_FALSE(QuietNotificationPermissionUiState::ShouldShowPromo(profile)); + test_api_->manager()->Deny(); + base::RunLoop().RunUntilIdle(); + } + + LocationBarView* location_bar_view = + BrowserView::GetBrowserViewForBrowser(browser())->GetLocationBarView(); + ContentSettingImageView& quiet_ui_icon = **std::find_if( + location_bar_view->GetContentSettingViewsForTest().begin(), + location_bar_view->GetContentSettingViewsForTest().end(), + [](ContentSettingImageView* view) { + return view->GetTypeForTesting() == + ContentSettingImageModel::ImageType::NOTIFICATIONS_QUIET_PROMPT; + }); + + EXPECT_FALSE(quiet_ui_icon.GetVisible()); + // `ContentSettingImageView::AnimationEnded()` was not triggered and IPH is + // not shown. + EXPECT_FALSE(quiet_ui_icon.get_critical_promo_id_for_testing().has_value()); + + GURL notification("http://www.notification1.com/"); + ui_test_utils::NavigateToURL(browser(), notification); + permissions::MockPermissionRequest notification_request( + u"request", permissions::RequestType::kNotifications, notification); + test_api_->manager()->AddRequest(GetActiveMainFrame(), ¬ification_request); + base::RunLoop().RunUntilIdle(); + + // After 3 denied Notifications requests, Adaptive activation enabled quiet + // permission prompt. + EXPECT_TRUE(test_api_->manager()->ShouldCurrentRequestUseQuietUI()); + // At the first quiet permission prompt we show IPH. + ASSERT_TRUE(QuietNotificationPermissionUiState::ShouldShowPromo(profile)); + + EXPECT_TRUE(quiet_ui_icon.GetVisible()); + EXPECT_TRUE(quiet_ui_icon.is_animating_label()); + // Animation is reset to trigger `ContentSettingImageView::AnimationEnded()`. + // `AnimationEnded` contains logic for displaying IPH and marking it as shown. + quiet_ui_icon.reset_animation_for_testing(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(quiet_ui_icon.is_animating_label()); + + // The IPH is showing. + ASSERT_TRUE(quiet_ui_icon.get_critical_promo_id_for_testing().has_value()); + FeaturePromoControllerViews* iph_controller = + BrowserView::GetBrowserViewForBrowser(browser()) + ->feature_promo_controller(); + // The critical promo that is currently showing is the one created by a quiet + // permission prompt. + EXPECT_TRUE(iph_controller->CriticalPromoIsShowing( + quiet_ui_icon.get_critical_promo_id_for_testing().value())); + + iph_controller->CloseBubbleForCriticalPromo( + quiet_ui_icon.get_critical_promo_id_for_testing().value()); + + test_api_->manager()->Deny(); + base::RunLoop().RunUntilIdle(); + + // After quiet permission prompt was resolved, the critical promo is reset. + EXPECT_FALSE(quiet_ui_icon.get_critical_promo_id_for_testing().has_value()); + + EXPECT_FALSE(quiet_ui_icon.GetVisible()); + + // The second Notifications permission request to verify that the IPH is not + // shown. + GURL notification2("http://www.notification2.com/"); + ui_test_utils::NavigateToURL(browser(), notification2); + permissions::MockPermissionRequest notification_request2( + u"request", permissions::RequestType::kNotifications, notification2); + test_api_->manager()->AddRequest(GetActiveMainFrame(), + ¬ification_request2); + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(test_api_->manager()->ShouldCurrentRequestUseQuietUI()); + // At the second quiet permission prompt the IPH should be disabled. + EXPECT_FALSE(QuietNotificationPermissionUiState::ShouldShowPromo(profile)); + + EXPECT_TRUE(quiet_ui_icon.GetVisible()); + EXPECT_TRUE(quiet_ui_icon.is_animating_label()); + quiet_ui_icon.reset_animation_for_testing(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(quiet_ui_icon.is_animating_label()); + + // The IPH id is not empty because `ContentSettingImageView::AnimationEnded()` + // was triggered. + EXPECT_TRUE(quiet_ui_icon.get_critical_promo_id_for_testing().has_value()); + // The critical promo is not shown. + EXPECT_FALSE(iph_controller->CriticalPromoIsShowing( + quiet_ui_icon.get_critical_promo_id_for_testing().value())); + + test_api_->manager()->Deny(); + base::RunLoop().RunUntilIdle(); +} + // ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER is ChromeOS only. #if BUILDFLAG(IS_CHROMEOS_ASH) IN_PROC_BROWSER_TEST_P(PermissionPromptBubbleViewBrowserTest, @@ -370,3 +501,5 @@ INSTANTIATE_TEST_SUITE_P(All, OneTimePermissionPromptBubbleViewBrowserTest, ::testing::Values(false, true)); + +INSTANTIATE_TEST_SUITE_P(All, QuietUIPromoBrowserTest, ::testing::Values(true));
diff --git a/chrome/browser/ui/views/user_education/feature_promo_controller_views.cc b/chrome/browser/ui/views/user_education/feature_promo_controller_views.cc index 6eddedab..7f61ea8 100644 --- a/chrome/browser/ui/views/user_education/feature_promo_controller_views.cc +++ b/chrome/browser/ui/views/user_education/feature_promo_controller_views.cc
@@ -107,6 +107,11 @@ bubble_owner_->CloseBubble(*bubble_id_); } +bool FeaturePromoControllerViews::CriticalPromoIsShowing( + const base::Token& critical_promo_id) const { + return bubble_id_ && (current_critical_promo_ == critical_promo_id); +} + bool FeaturePromoControllerViews::MaybeShowPromo( const base::Feature& iph_feature, BubbleCloseCallback close_callback) { @@ -329,7 +334,7 @@ DCHECK_NE(current_iph_feature_ != nullptr, current_critical_promo_.has_value()); - bubble_id_ = absl::nullopt; + bubble_id_.reset(); if (anchor_view_tracker_.view()) anchor_view_tracker_.view()->SetProperty(kHasInProductHelpPromoKey, false); @@ -341,6 +346,6 @@ tracker_->Dismissed(*current_iph_feature_); current_iph_feature_ = nullptr; } else { - current_critical_promo_ = absl::nullopt; + current_critical_promo_.reset(); } }
diff --git a/chrome/browser/ui/views/user_education/feature_promo_controller_views.h b/chrome/browser/ui/views/user_education/feature_promo_controller_views.h index 2281941..4fab01d3 100644 --- a/chrome/browser/ui/views/user_education/feature_promo_controller_views.h +++ b/chrome/browser/ui/views/user_education/feature_promo_controller_views.h
@@ -70,6 +70,9 @@ // Ends a promo started by ShowCriticalPromo() if it's still showing. void CloseBubbleForCriticalPromo(const base::Token& critical_promo_id); + // Returns whether a critical promo is showing for the given `Token`. + bool CriticalPromoIsShowing(const base::Token& critical_promo_id) const; + // FeaturePromoController: bool MaybeShowPromo( const base::Feature& iph_feature,
diff --git a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc index 49edbb6..1bdaa6d 100644 --- a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc +++ b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc
@@ -23,12 +23,12 @@ #include "chrome/browser/web_applications/components/web_app_constants.h" #include "chrome/browser/web_applications/components/web_app_helpers.h" #include "chrome/browser/web_applications/components/web_app_prefs_utils.h" -#include "chrome/common/chrome_features.h" #include "chrome/grit/generated_resources.h" #include "components/feature_engagement/public/event_constants.h" #include "components/feature_engagement/public/tracker.h" #include "components/strings/grit/components_strings.h" #include "components/url_formatter/elide_url.h" +#include "content/public/common/content_features.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/text_elider.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_confirmation_view.cc b/chrome/browser/ui/views/web_apps/web_app_confirmation_view.cc index e0cb657..bca8a182 100644 --- a/chrome/browser/ui/views/web_apps/web_app_confirmation_view.cc +++ b/chrome/browser/ui/views/web_apps/web_app_confirmation_view.cc
@@ -15,11 +15,11 @@ #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/web_apps/web_app_info_image_source.h" #include "chrome/browser/web_applications/components/web_app_constants.h" -#include "chrome/common/chrome_features.h" #include "chrome/grit/generated_resources.h" #include "components/constrained_window/constrained_window_views.h" #include "components/strings/grit/components_strings.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/content_features.h" #include "ui/accessibility/ax_node_data.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/metadata/metadata_impl_macros.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc index b11802a..3396715 100644 --- a/chrome/browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc +++ b/chrome/browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc
@@ -22,9 +22,9 @@ #include "chrome/browser/web_applications/components/web_application_info.h" #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" #include "chrome/browser/web_applications/web_app_provider.h" -#include "chrome/common/chrome_features.h" #include "chrome/test/base/in_process_browser_test.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/content_features.h" #include "content/public/test/background_color_change_waiter.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h"
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.cc b/chrome/browser/ui/web_applications/app_browser_controller.cc index ab765f0..5f6e376 100644 --- a/chrome/browser/ui/web_applications/app_browser_controller.cc +++ b/chrome/browser/ui/web_applications/app_browser_controller.cc
@@ -37,6 +37,7 @@ #include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/content_features.h" #include "content/public/common/url_constants.h" #include "extensions/browser/extension_registry.h" #include "extensions/common/constants.h"
diff --git a/chrome/browser/ui/web_applications/system_web_app_ui_utils.h b/chrome/browser/ui/web_applications/system_web_app_ui_utils.h index 9c7ac9a..3a112b9 100644 --- a/chrome/browser/ui/web_applications/system_web_app_ui_utils.h +++ b/chrome/browser/ui/web_applications/system_web_app_ui_utils.h
@@ -66,7 +66,7 @@ // a crash report // // In tests, remember to call FlushSystemWebAppLaunchesForTesting on the same -// |profile|, or use TestNavigationObserver to wait the navigation. +// |profile|, or use content::TestNavigationObserver to wait the navigation. void LaunchSystemWebAppAsync( Profile* profile, const SystemAppType type,
diff --git a/chrome/browser/ui/web_applications/web_app_launch_manager.cc b/chrome/browser/ui/web_applications/web_app_launch_manager.cc index 3af76c0..8f86bf9 100644 --- a/chrome/browser/ui/web_applications/web_app_launch_manager.cc +++ b/chrome/browser/ui/web_applications/web_app_launch_manager.cc
@@ -14,6 +14,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" +#include "base/trace_event/base_tracing.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/app_mode/app_mode_utils.h" @@ -44,6 +45,7 @@ #include "chrome/browser/web_launch/web_launch_files_helper.h" #include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" +#include "components/services/app_service/public/cpp/intent_util.h" #include "components/site_engagement/content/site_engagement_service.h" #include "components/webapps/browser/banners/app_banner_settings_helper.h" #include "content/public/browser/page_navigator.h" @@ -57,6 +59,10 @@ #include "ui/display/scoped_display_for_new_windows.h" #include "url/gurl.h" +#if BUILDFLAG(IS_CHROMEOS_ASH) +#include "components/user_manager/user_manager.h" +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + namespace web_app { namespace { @@ -89,6 +95,33 @@ if (capturing_system_app_type && (!browser || !web_app::IsBrowserForSystemWebApp( browser, capturing_system_app_type.value()))) { +#if BUILDFLAG(IS_CHROMEOS_ASH) + auto* user_manager = user_manager::UserManager::Get(); + bool is_kiosk = user_manager && user_manager->IsLoggedInAsAnyKioskApp(); + AppBrowserController* app_controller = browser->app_controller(); + WebAppProvider* web_app_provider = WebAppProvider::Get(browser->profile()); + TRACE_EVENT_INSTANT( + "system_apps", "BadNavigate", [&](perfetto::EventContext ctx) { + auto* bad_navigate = + ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>() + ->set_chrome_web_app_bad_navigate(); + bad_navigate->set_is_kiosk(is_kiosk); + bad_navigate->set_has_hosted_app_controller(!!app_controller); + bad_navigate->set_app_name(browser->app_name()); + if (app_controller && app_controller->system_app_type()) { + bad_navigate->set_system_app_type( + static_cast<uint32_t>(*app_controller->system_app_type())); + } + bad_navigate->set_web_app_provider_registry_ready( + web_app_provider->on_registry_ready().is_signaled()); + bad_navigate->set_system_web_app_manager_synchronized( + web_app_provider->system_web_app_manager() + .on_apps_synchronized() + .is_signaled()); + }); + UMA_HISTOGRAM_ENUMERATION("WebApp.SystemApps.BadNavigate.Type", + capturing_system_app_type.value()); +#endif // BUILDFLAG(IS_CHROMEOS_ASH) return nullptr; } @@ -260,9 +293,13 @@ if (GetOpenApplicationCallback()) return GetOpenApplicationCallback().Run(std::move(params)); + bool is_share_intent = + params.intent && + (params.intent->action == apps_util::kIntentActionSend || + params.intent->action == apps_util::kIntentActionSendMultiple); const apps::ShareTarget* const share_target = - params.intent ? provider_->registrar().GetAppShareTarget(params.app_id) - : nullptr; + is_share_intent ? provider_->registrar().GetAppShareTarget(params.app_id) + : nullptr; const GURL url = GetLaunchUrl(*provider_, params, share_target); DCHECK(url.is_valid());
diff --git a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc index 2543ce6d..4bf7e20 100644 --- a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc +++ b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
@@ -27,6 +27,7 @@ #include "chrome/test/base/ui_test_utils.h" #include "components/embedder_support/switches.h" #include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h" +#include "content/public/common/content_features.h" #include "content/public/test/browser_test.h" #include "content/public/test/test_navigation_observer.h" #include "content/public/test/url_loader_interceptor.h"
diff --git a/chrome/browser/ui/webui/settings/site_settings_helper.cc b/chrome/browser/ui/webui/settings/site_settings_helper.cc index 7831684..3a156ea2 100644 --- a/chrome/browser/ui/webui/settings/site_settings_helper.cc +++ b/chrome/browser/ui/webui/settings/site_settings_helper.cc
@@ -410,26 +410,34 @@ // First build the list of permissions that will be shown regardless of // `origin`. Some categories such as COOKIES store their data in a custom way, // so are not included here. - static base::NoDestructor<std::vector<ContentSettingsType>> base_types({ - ContentSettingsType::AR, ContentSettingsType::AUTOMATIC_DOWNLOADS, - ContentSettingsType::BACKGROUND_SYNC, - ContentSettingsType::CLIPBOARD_READ_WRITE, - ContentSettingsType::FILE_HANDLING, - ContentSettingsType::FILE_SYSTEM_WRITE_GUARD, - ContentSettingsType::FONT_ACCESS, ContentSettingsType::GEOLOCATION, - ContentSettingsType::HID_GUARD, ContentSettingsType::IDLE_DETECTION, - ContentSettingsType::IMAGES, ContentSettingsType::JAVASCRIPT, - ContentSettingsType::MEDIASTREAM_CAMERA, - ContentSettingsType::MEDIASTREAM_MIC, ContentSettingsType::MIDI_SYSEX, - ContentSettingsType::MIXEDSCRIPT, ContentSettingsType::NOTIFICATIONS, - ContentSettingsType::POPUPS, + static base::NoDestructor<std::vector<ContentSettingsType>> base_types{{ + ContentSettingsType::AR, + ContentSettingsType::AUTOMATIC_DOWNLOADS, + ContentSettingsType::BACKGROUND_SYNC, + ContentSettingsType::CLIPBOARD_READ_WRITE, + ContentSettingsType::FILE_HANDLING, + ContentSettingsType::FILE_SYSTEM_WRITE_GUARD, + ContentSettingsType::FONT_ACCESS, + ContentSettingsType::GEOLOCATION, + ContentSettingsType::HID_GUARD, + ContentSettingsType::IDLE_DETECTION, + ContentSettingsType::IMAGES, + ContentSettingsType::JAVASCRIPT, + ContentSettingsType::MEDIASTREAM_CAMERA, + ContentSettingsType::MEDIASTREAM_MIC, + ContentSettingsType::MIDI_SYSEX, + ContentSettingsType::MIXEDSCRIPT, + ContentSettingsType::NOTIFICATIONS, + ContentSettingsType::POPUPS, #if defined(IS_CHROMEOS_ASH) || defined(OS_WIN) - ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, + ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, #endif - ContentSettingsType::SENSORS, ContentSettingsType::SERIAL_GUARD, - ContentSettingsType::SOUND, ContentSettingsType::USB_GUARD, - ContentSettingsType::VR, - }); + ContentSettingsType::SENSORS, + ContentSettingsType::SERIAL_GUARD, + ContentSettingsType::SOUND, + ContentSettingsType::USB_GUARD, + ContentSettingsType::VR, + }}; static bool initialized = false; if (!initialized) { // The permission categories in this block are only shown when running with
diff --git a/chrome/browser/wake_lock/OWNERS b/chrome/browser/wake_lock/OWNERS index eb31e9229..776c59b 100644 --- a/chrome/browser/wake_lock/OWNERS +++ b/chrome/browser/wake_lock/OWNERS
@@ -1,3 +1 @@ file://third_party/blink/renderer/modules/wake_lock/OWNERS - -per-file *permission_context*=file://components/permissions/PERMISSIONS_OWNERS
diff --git a/chrome/browser/web_applications/components/app_registrar.cc b/chrome/browser/web_applications/components/app_registrar.cc index c5d9597..d82afd1 100644 --- a/chrome/browser/web_applications/components/app_registrar.cc +++ b/chrome/browser/web_applications/components/app_registrar.cc
@@ -13,6 +13,7 @@ #include "chrome/browser/web_applications/components/web_app_helpers.h" #include "chrome/browser/web_applications/components/web_app_prefs_utils.h" #include "chrome/common/chrome_features.h" +#include "content/public/common/content_features.h" namespace web_app {
diff --git a/chrome/browser/web_applications/components/app_registry_controller.cc b/chrome/browser/web_applications/components/app_registry_controller.cc index 1c0d719..4f8abc9 100644 --- a/chrome/browser/web_applications/components/app_registry_controller.cc +++ b/chrome/browser/web_applications/components/app_registry_controller.cc
@@ -7,7 +7,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/web_applications/components/os_integration_manager.h" #include "chrome/browser/web_applications/components/web_app_prefs_utils.h" -#include "chrome/common/chrome_features.h" +#include "content/public/common/content_features.h" namespace web_app {
diff --git a/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc b/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc index 8df08c03..acb134b 100644 --- a/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc +++ b/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc
@@ -29,14 +29,15 @@ #include "chrome/browser/web_applications/components/web_app_id.h" #include "chrome/browser/web_applications/components/web_app_provider_base.h" #include "chrome/browser/web_applications/components/web_application_info.h" -#include "chrome/browser/web_applications/test/test_app_registrar.h" #include "chrome/browser/web_applications/test/test_data_retriever.h" #include "chrome/browser/web_applications/test/test_install_finalizer.h" #include "chrome/browser/web_applications/test/test_os_integration_manager.h" #include "chrome/browser/web_applications/test/test_web_app_provider.h" #include "chrome/browser/web_applications/test/test_web_app_ui_manager.h" #include "chrome/browser/web_applications/test/test_web_app_url_loader.h" +#include "chrome/browser/web_applications/web_app.h" #include "chrome/browser/web_applications/web_app_install_manager.h" +#include "chrome/browser/web_applications/web_app_registrar.h" #include "chrome/common/chrome_features.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" @@ -88,7 +89,8 @@ class TestExternallyManagedAppInstallFinalizer : public InstallFinalizer { public: - explicit TestExternallyManagedAppInstallFinalizer(TestAppRegistrar* registrar) + explicit TestExternallyManagedAppInstallFinalizer( + WebAppRegistrarMutable* registrar) : registrar_(registrar) {} TestExternallyManagedAppInstallFinalizer( const TestExternallyManagedAppInstallFinalizer&) = delete; @@ -101,6 +103,29 @@ return TestInstallFinalizer::GetAppIdForUrl(url); } + std::unique_ptr<WebApp> CreateWebApp(const AppId& app_id, + const GURL& start_url) { + auto web_app = std::make_unique<WebApp>(app_id); + web_app->SetStartUrl(start_url); + web_app->SetName("App Name"); + web_app->AddSource(Source::kPolicy); + web_app->SetDisplayMode(DisplayMode::kStandalone); + web_app->SetUserDisplayMode(DisplayMode::kStandalone); + return web_app; + } + + void RegisterApp(std::unique_ptr<web_app::WebApp> web_app) { + web_app::AppId app_id = web_app->app_id(); + registrar_->registry().emplace(std::move(app_id), std::move(web_app)); + } + + void UnregisterApp(const AppId& app_id) { + auto it = registrar_->registry().find(app_id); + DCHECK(it != registrar_->registry().end()); + + registrar_->registry().erase(it); + } + void SetNextFinalizeInstallResult(const GURL& url, InstallResultCode code) { DCHECK(!base::Contains(next_finalize_install_results_, url)); @@ -154,8 +179,8 @@ FROM_HERE, base::BindLambdaForTesting( [&, app_id, url, code, callback = std::move(callback)]() mutable { - registrar_->AddExternalApp( - app_id, {url, ExternalInstallSource::kExternalPolicy}); + auto web_app = CreateWebApp(app_id, url); + RegisterApp(std::move(web_app)); std::move(callback).Run(app_id, code); })); } @@ -175,7 +200,7 @@ const AppId& app_id, webapps::WebappUninstallSource external_install_source, UninstallWebAppCallback callback) override { - registrar_->RemoveExternalApp(app_id); + UnregisterApp(app_id); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), /*uninstalled=*/true)); @@ -199,7 +224,7 @@ base::BindLambdaForTesting( [&, app_id, uninstalled, callback = std::move(callback)]() mutable { if (uninstalled) - registrar_->RemoveExternalApp(app_id); + UnregisterApp(app_id); std::move(callback).Run(uninstalled); })); } @@ -232,7 +257,7 @@ } private: - TestAppRegistrar* registrar_ = nullptr; + WebAppRegistrarMutable* registrar_ = nullptr; std::vector<WebApplicationInfo> web_app_info_list_; std::vector<FinalizeOptions> finalize_options_list_; @@ -268,7 +293,7 @@ auto* provider = TestWebAppProvider::Get(profile()); - auto registrar = std::make_unique<TestAppRegistrar>(); + auto registrar = std::make_unique<WebAppRegistrarMutable>(profile()); registrar_ = registrar.get(); auto install_finalizer = @@ -304,7 +329,7 @@ TestWebAppUrlLoader& url_loader() { return *url_loader_; } TestWebAppUiManager* ui_manager() { return ui_manager_; } - TestAppRegistrar* registrar() { return registrar_; } + WebAppRegistrar* registrar() { return registrar_; } TestExternallyManagedAppInstallFinalizer* finalizer() { return install_finalizer_; } @@ -358,7 +383,7 @@ private: std::unique_ptr<TestWebAppUrlLoader> url_loader_; WebAppInstallManager* install_manager_ = nullptr; - TestAppRegistrar* registrar_ = nullptr; + WebAppRegistrar* registrar_ = nullptr; TestDataRetriever* data_retriever_ = nullptr; TestExternallyManagedAppInstallFinalizer* install_finalizer_ = nullptr; TestWebAppUiManager* ui_manager_ = nullptr;
diff --git a/chrome/browser/web_applications/system_web_apps/system_web_app_types.h b/chrome/browser/web_applications/system_web_apps/system_web_app_types.h index c0ae775..f866202 100644 --- a/chrome/browser/web_applications/system_web_apps/system_web_app_types.h +++ b/chrome/browser/web_applications/system_web_apps/system_web_app_types.h
@@ -100,6 +100,9 @@ // 5. Use web_app::LaunchSystemWebAppAsync to launch your SWA (with the type // added above). This provides extra safety in edge cases (e.g. when in // incognito or guest sessions). + // + // 6. Update kMaxValue. + kMaxValue = OS_FEEDBACK }; } // namespace web_app
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 81e428e..905ec32c 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-master-1622915871-c81a07f3e6a7be3afbf2c5504ba04c476ffae5b1.profdata +chrome-win64-master-1623056245-fcc3a9d89ac881252be573c70c765ad476913960.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index 784ec2e0..0e2fe418 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -302,11 +302,6 @@ const base::Feature kDesktopPWAsSubApps{"DesktopPWAsSubApps", base::FEATURE_DISABLED_BY_DEFAULT}; -// Adds a tab strip to PWA windows, used for UI experimentation. -// TODO(crbug.com/897314): Enable this feature. -const base::Feature kDesktopPWAsTabStrip{"DesktopPWAsTabStrip", - base::FEATURE_DISABLED_BY_DEFAULT}; - // Makes user navigations via links within web app scopes get captured tab // tabbed app windows. // TODO(crbug.com/897314): Enable this feature.
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index 4718253..7028824 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -207,9 +207,6 @@ extern const base::Feature kDesktopPWAsSubApps; COMPONENT_EXPORT(CHROME_FEATURES) -extern const base::Feature kDesktopPWAsTabStrip; - -COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kDesktopPWAsTabStripLinkCapturing; COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/renderer/resources/extensions/chromeos_ime_service_bindings.js b/chrome/renderer/resources/extensions/chromeos_ime_service_bindings.js index aa2ceca..ece25e5 100644 --- a/chrome/renderer/resources/extensions/chromeos_ime_service_bindings.js +++ b/chrome/renderer/resources/extensions/chromeos_ime_service_bindings.js
@@ -228,8 +228,6 @@ /** * Activates an input method based on its specification. - * If |activateIME| was called previously, this will call |onConnectionError| - * for the previous connection, because only one IME can be active at a time. * * @param {string} imeSpec The specification of an IME (e.g. the engine ID). * @param {!Uint8Array} extra The extra data (e.g. initial tasks to run). @@ -263,8 +261,6 @@ if (bound && onConnectionError) { this.activeEngine_.ptr.setConnectionErrorHandler( onConnectionError); - this.clientChannel_.ptr.setConnectionErrorHandler( - onConnectionError); }; if (onConnection) { onConnection(bound);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 1d849f61..45b5075 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -989,6 +989,7 @@ "//chrome/test/data:web_ui_test_bindings", "//chrome/test/data:webui_test_resources", "//chrome/test/payments:test_support", + "//components/accuracy_tips", "//components/autofill/content/browser:risk_proto", "//components/autofill/content/common/mojom", "//components/autofill/content/renderer:test_support", @@ -2112,7 +2113,6 @@ "//chromeos/test/data/", "//ui/file_manager/base/", "//ui/file_manager/file_manager/", - "//ui/file_manager/gallery/", "//ui/file_manager/image_loader/", "//ui/file_manager/integration_tests/", "//ui/file_manager/video_player", @@ -2141,7 +2141,6 @@ "//ui/file_manager/file_manager/foreground/js/metadata:modulize_runtime_data", "//ui/file_manager/file_manager/foreground/js/ui:modulize_runtime_data", "//ui/file_manager/file_manager/foreground/js/ui/table:modulize_runtime_data", - "//ui/file_manager/gallery/js/image_editor:modulize_runtime_data", "//ui/file_manager/image_loader:modulize_runtime_data", ] @@ -2857,6 +2856,7 @@ "../browser/ui/views/omnibox/omnibox_suggestion_button_row_browsertest.cc", "../browser/ui/views/page_action/pwa_install_view_browsertest.cc", "../browser/ui/views/page_action/zoom_view_browsertest.cc", + "../browser/ui/views/page_info/accuracy_tip_bubble_view_browsertest.cc", "../browser/ui/views/page_info/page_info_bubble_view_browsertest.cc", "../browser/ui/views/page_info/page_info_bubble_view_dialog_browsertest.cc", "../browser/ui/views/page_info/page_info_bubble_view_sync_browsertest.cc", @@ -3245,8 +3245,6 @@ "../browser/chromeos/file_manager/file_manager_jstest_base.cc", "../browser/chromeos/file_manager/file_manager_jstest_base.h", "../browser/chromeos/file_manager/file_tasks_browsertest.cc", - "../browser/chromeos/file_manager/gallery_browsertest.cc", - "../browser/chromeos/file_manager/gallery_jstest.cc", "../browser/chromeos/file_manager/image_loader_jstest.cc", "../browser/chromeos/file_manager/video_player_browsertest.cc", "../browser/chromeos/file_manager/video_player_jstest.cc", @@ -4557,7 +4555,6 @@ "../browser/update_client/chrome_update_query_params_delegate_unittest.cc", "../browser/visibility_timer_tab_helper_unittest.cc", "../browser/vr/vr_tab_helper_unittest.cc", - "../browser/wake_lock/wake_lock_permission_context_unittest.cc", "../browser/webid/federated_identity_request_permission_context_unittest.cc", "../browser/webid/federated_identity_sharing_permission_context_unittest.cc", "../common/chrome_content_client_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java index a3a8241..8cc9df9 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java
@@ -5,6 +5,7 @@ package org.chromium.chrome.test.util.browser.signin; import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; import org.junit.Assert; @@ -15,6 +16,7 @@ import org.chromium.chrome.browser.signin.services.IdentityServicesProvider; import org.chromium.chrome.browser.signin.services.SigninManager; import org.chromium.chrome.browser.sync.ProfileSyncService; +import org.chromium.components.signin.AccountUtils; import org.chromium.components.signin.base.CoreAccountInfo; import org.chromium.components.signin.identitymanager.ConsentLevel; import org.chromium.components.signin.metrics.SigninAccessPoint; @@ -77,6 +79,7 @@ * * @param profileSyncService Enable the sync with it if it is not null. */ + @WorkerThread public static void signinAndEnableSync( CoreAccountInfo coreAccountInfo, @Nullable ProfileSyncService profileSyncService) { CallbackHelper callbackHelper = new CallbackHelper(); @@ -84,8 +87,9 @@ SigninManager signinManager = IdentityServicesProvider.get().getSigninManager( Profile.getLastUsedRegularProfile()); signinManager.onFirstRunCheckDone(); // Allow sign-in - signinManager.signinAndEnableSync( - SigninAccessPoint.UNKNOWN, coreAccountInfo, new SigninManager.SignInCallback() { + signinManager.signinAndEnableSync(SigninAccessPoint.UNKNOWN, + AccountUtils.createAccountFromName(coreAccountInfo.getEmail()), + new SigninManager.SignInCallback() { @Override public void onSignInComplete() { if (profileSyncService != null) {
diff --git a/chromeos/components/camera_app_ui/resources/strings/camera_strings_ko.xtb b/chromeos/components/camera_app_ui/resources/strings/camera_strings_ko.xtb index 1cf13849..0aa9692 100644 --- a/chromeos/components/camera_app_ui/resources/strings/camera_strings_ko.xtb +++ b/chromeos/components/camera_app_ui/resources/strings/camera_strings_ko.xtb
@@ -38,7 +38,7 @@ <translation id="3227137524299004712">마이크</translation> <translation id="3240426699337459095">링크 복사됨</translation> <translation id="3259149966178251584">카메라 해상도</translation> -<translation id="3448774564454087943">동영상이 너무 짧아서 저장할 수 없습니다.</translation> +<translation id="3448774564454087943">동영상이 너무 짧아 저장할 수 없습니다.</translation> <translation id="3517926952904427380">세로 모드 사진을 찍을 수 없습니다.</translation> <translation id="3569311554794739032"><ph name="FILE" /> 파일을 삭제하시겠습니까?</translation> <translation id="3733597664308256288">추가 카메라 제어 기능을 사용할 수 있습니다. 검색 키와 오른쪽 화살표 키를 사용하여 액세스하세요.</translation>
diff --git a/ui/file_manager/gallery/images/icon128.png b/chromeos/components/media_app_ui/resources/assets/icon128.png similarity index 100% rename from ui/file_manager/gallery/images/icon128.png rename to chromeos/components/media_app_ui/resources/assets/icon128.png Binary files differ
diff --git a/ui/file_manager/gallery/images/icon16.png b/chromeos/components/media_app_ui/resources/assets/icon16.png similarity index 100% rename from ui/file_manager/gallery/images/icon16.png rename to chromeos/components/media_app_ui/resources/assets/icon16.png Binary files differ
diff --git a/ui/file_manager/gallery/images/icon192.png b/chromeos/components/media_app_ui/resources/assets/icon192.png similarity index 100% rename from ui/file_manager/gallery/images/icon192.png rename to chromeos/components/media_app_ui/resources/assets/icon192.png Binary files differ
diff --git a/ui/file_manager/gallery/images/icon256.png b/chromeos/components/media_app_ui/resources/assets/icon256.png similarity index 100% rename from ui/file_manager/gallery/images/icon256.png rename to chromeos/components/media_app_ui/resources/assets/icon256.png Binary files differ
diff --git a/ui/file_manager/gallery/images/icon32.png b/chromeos/components/media_app_ui/resources/assets/icon32.png similarity index 100% rename from ui/file_manager/gallery/images/icon32.png rename to chromeos/components/media_app_ui/resources/assets/icon32.png Binary files differ
diff --git a/ui/file_manager/gallery/images/icon48.png b/chromeos/components/media_app_ui/resources/assets/icon48.png similarity index 100% rename from ui/file_manager/gallery/images/icon48.png rename to chromeos/components/media_app_ui/resources/assets/icon48.png Binary files differ
diff --git a/ui/file_manager/gallery/images/icon64.png b/chromeos/components/media_app_ui/resources/assets/icon64.png similarity index 100% rename from ui/file_manager/gallery/images/icon64.png rename to chromeos/components/media_app_ui/resources/assets/icon64.png Binary files differ
diff --git a/ui/file_manager/gallery/images/icon96.png b/chromeos/components/media_app_ui/resources/assets/icon96.png similarity index 100% rename from ui/file_manager/gallery/images/icon96.png rename to chromeos/components/media_app_ui/resources/assets/icon96.png Binary files differ
diff --git a/chromeos/components/media_app_ui/resources/media_app_resources.grd b/chromeos/components/media_app_ui/resources/media_app_resources.grd index a394f19..7a47e54 100644 --- a/chromeos/components/media_app_ui/resources/media_app_resources.grd +++ b/chromeos/components/media_app_ui/resources/media_app_resources.grd
@@ -13,14 +13,14 @@ <include name="IDR_MEDIA_APP_LAUNCH_JS" file="${root_gen_dir}/chromeos/components/media_app_ui/resources/js/launch.rollup.js" use_base_dir="false" compress="brotli" type="BINDATA" /> <!-- TODO(b/141588875): Switch these to IDR_MEDIA_APP_APP_ICON_*_PNG in the internal media_app_bundle_resources.grd file (and add more icon resolutions) when the final icon is ready. --> - <include name="IDR_MEDIA_APP_GALLERY_ICON_16_PNG" file="../../../../ui/file_manager/gallery/images/icon16.png" type="BINDATA" /> - <include name="IDR_MEDIA_APP_GALLERY_ICON_32_PNG" file="../../../../ui/file_manager/gallery/images/icon32.png" type="BINDATA" /> - <include name="IDR_MEDIA_APP_GALLERY_ICON_48_PNG" file="../../../../ui/file_manager/gallery/images/icon48.png" type="BINDATA" /> - <include name="IDR_MEDIA_APP_GALLERY_ICON_64_PNG" file="../../../../ui/file_manager/gallery/images/icon64.png" type="BINDATA" /> - <include name="IDR_MEDIA_APP_GALLERY_ICON_96_PNG" file="../../../../ui/file_manager/gallery/images/icon96.png" type="BINDATA" /> - <include name="IDR_MEDIA_APP_GALLERY_ICON_128_PNG" file="../../../../ui/file_manager/gallery/images/icon128.png" type="BINDATA" /> - <include name="IDR_MEDIA_APP_GALLERY_ICON_192_PNG" file="../../../../ui/file_manager/gallery/images/icon192.png" type="BINDATA" /> - <include name="IDR_MEDIA_APP_GALLERY_ICON_256_PNG" file="../../../../ui/file_manager/gallery/images/icon256.png" type="BINDATA" /> + <include name="IDR_MEDIA_APP_GALLERY_ICON_16_PNG" file="assets/icon16.png" type="BINDATA" /> + <include name="IDR_MEDIA_APP_GALLERY_ICON_32_PNG" file="assets/icon32.png" type="BINDATA" /> + <include name="IDR_MEDIA_APP_GALLERY_ICON_48_PNG" file="assets/icon48.png" type="BINDATA" /> + <include name="IDR_MEDIA_APP_GALLERY_ICON_64_PNG" file="assets/icon64.png" type="BINDATA" /> + <include name="IDR_MEDIA_APP_GALLERY_ICON_96_PNG" file="assets/icon96.png" type="BINDATA" /> + <include name="IDR_MEDIA_APP_GALLERY_ICON_128_PNG" file="assets/icon128.png" type="BINDATA" /> + <include name="IDR_MEDIA_APP_GALLERY_ICON_192_PNG" file="assets/icon192.png" type="BINDATA" /> + <include name="IDR_MEDIA_APP_GALLERY_ICON_256_PNG" file="assets/icon256.png" type="BINDATA" /> <!-- Unprivileged guest contents. --> <include name="IDR_MEDIA_APP_APP_HTML" file="app.html" type="BINDATA" />
diff --git a/chromeos/components/media_app_ui/resources/mock/media_app_bundle_mock_resources.grd b/chromeos/components/media_app_ui/resources/mock/media_app_bundle_mock_resources.grd index b5620bf..65b7cc4b 100644 --- a/chromeos/components/media_app_ui/resources/mock/media_app_bundle_mock_resources.grd +++ b/chromeos/components/media_app_ui/resources/mock/media_app_bundle_mock_resources.grd
@@ -20,8 +20,7 @@ type="BINDATA" /> <!-- Use a placeholder icon when enable_cros_media_app = false. --> <include name="IDR_MEDIA_APP_APP_ICON_256_PNG" - file="../../../../../ui/file_manager/gallery/images/icon256.png" - type="BINDATA" /> + file="../assets/icon256.png" type="BINDATA" /> </includes> </release> </grit>
diff --git a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom index 313fac1..f28ab6f 100644 --- a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom +++ b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
@@ -548,6 +548,10 @@ uint64 output_volume; // Active output device's name. string output_device_name; + // Active input device's gain in [0, 100]. + uint32 input_gain; + // Active input device's name. + string input_device_name; // Numbers of underruns. uint32 underruns; // Numbers of severe underruns.
diff --git a/chromeos/services/ime/BUILD.gn b/chromeos/services/ime/BUILD.gn index 8ac83ec3..aa40500 100644 --- a/chromeos/services/ime/BUILD.gn +++ b/chromeos/services/ime/BUILD.gn
@@ -41,8 +41,8 @@ "decoder/system_engine.h", "ime_service.cc", "ime_service.h", - "input_engine.cc", - "input_engine.h", + "rule_based_engine.cc", + "rule_based_engine.h", ] deps = [ @@ -87,6 +87,7 @@ "//ash/constants", "//base", "//base/test:test_support", + "//chromeos/services/ime:decoder", "//chromeos/services/ime/public/mojom", "//chromeos/services/ime/public/proto:messages_proto", "//mojo/public/cpp/bindings",
diff --git a/chromeos/services/ime/decoder/decoder_engine.h b/chromeos/services/ime/decoder/decoder_engine.h index 92272fd..4ae5460 100644 --- a/chromeos/services/ime/decoder/decoder_engine.h +++ b/chromeos/services/ime/decoder/decoder_engine.h
@@ -7,9 +7,12 @@ #include "base/scoped_native_library.h" #include "chromeos/services/ime/ime_decoder.h" -#include "chromeos/services/ime/input_engine.h" #include "chromeos/services/ime/public/cpp/shared_lib/interfaces.h" #include "chromeos/services/ime/public/mojom/input_engine.mojom.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace chromeos { @@ -42,11 +45,10 @@ const std::string& text, uint32_t offset, mojom::SelectionRangePtr selection_range) override {} - void OnCompositionCanceled() override {} + void OnCompositionCanceledBySystem() override {} void ProcessKeypressForRulebased( mojom::PhysicalKeyEventPtr event, ProcessKeypressForRulebasedCallback callback) override {} - void ResetForRulebased() override {} void CommitText(const std::string& text, mojom::CommitTextCursorBehavior cursor_behavior) override {} void SetComposition(const std::string& text) override {}
diff --git a/chromeos/services/ime/decoder/system_engine.cc b/chromeos/services/ime/decoder/system_engine.cc index f99a940..0c9604b 100644 --- a/chromeos/services/ime/decoder/system_engine.cc +++ b/chromeos/services/ime/decoder/system_engine.cc
@@ -112,50 +112,26 @@ const std::string& ime_spec, mojo::PendingReceiver<mojom::InputChannel> receiver, mojo::PendingRemote<mojom::InputChannel> remote, - const std::vector<uint8_t>& extra) { - if (IsImeSupportedByDecoder(ime_spec)) { - // There can only be one client using the decoder at any time. There are two - // possible clients: NativeInputMethodEngine (for physical keyboard) and the - // XKB extension (for virtual keyboard). The XKB extension may try to - // connect the decoder even when it's not supposed to (due to race - // conditions), so we must prevent the extension from taking over the - // NativeInputMethodEngine connection. - // - // This is a hack to to determine whether a connection came from - // NativeInputMethodEngine or the extension. NativeInputMethodEngine will - // send some extra bytes, whereas the extension doesn't. Thus, we can - // prevent the extension from taking over the NativeInputMethodEngine's - // connection to the decoder. NativeInputMethodEngine will voluntarily give - // up its connection when tswitching to tablet mode, allowing the extension - // to connect again. - // TODO(b/184115850): Create a separate Mojo API for NativeInputMethodEngine - // so that we don't need to inspect `extra` to distinguish the client. - if (is_decoder_receiver_connected_ && extra.size() == 0) { - return false; - } - - // Activates an IME engine via the shared library. Passing a - // |ClientDelegate| for engine instance created by the shared library to - // make safe calls on the client. - if (decoder_entry_points_->activate_ime( - ime_spec.c_str(), - new ClientDelegate(ime_spec, std::move(remote), - base::BindRepeating(&SystemEngine::OnReply, - base::Unretained(this))))) { - decoder_channel_receiver_.Bind(std::move(receiver)); - is_decoder_receiver_connected_ = true; - decoder_channel_receiver_.set_disconnect_handler(base::BindOnce( - [](bool& is_decoder_receiver_connected) { - is_decoder_receiver_connected = false; - }, - std::ref(is_decoder_receiver_connected_))); - return true; - } - is_decoder_receiver_connected_ = false; + base::OnceCallback<void()> disconnect_callback) { + if (!IsImeSupportedByDecoder(ime_spec)) { return false; } - return false; + // Activates an IME engine via the shared library. Passing a + // |ClientDelegate| for engine instance created by the shared library to + // make safe calls on the client. + if (!decoder_entry_points_->activate_ime( + ime_spec.c_str(), + new ClientDelegate(ime_spec, std::move(remote), + base::BindRepeating(&SystemEngine::OnReply, + base::Unretained(this))))) { + return false; + } + + decoder_channel_receiver_.Bind(std::move(receiver)); + decoder_channel_receiver_.set_disconnect_handler( + std::move(disconnect_callback)); + return true; } bool SystemEngine::IsImeSupportedByDecoder(const std::string& ime_spec) { @@ -212,7 +188,7 @@ base::DoNothing()); } -void SystemEngine::OnCompositionCanceled() { +void SystemEngine::OnCompositionCanceledBySystem() { const uint64_t seq_id = current_seq_id_; ++current_seq_id_;
diff --git a/chromeos/services/ime/decoder/system_engine.h b/chromeos/services/ime/decoder/system_engine.h index e812551..07bc0711 100644 --- a/chromeos/services/ime/decoder/system_engine.h +++ b/chromeos/services/ime/decoder/system_engine.h
@@ -7,9 +7,12 @@ #include "base/scoped_native_library.h" #include "chromeos/services/ime/ime_decoder.h" -#include "chromeos/services/ime/input_engine.h" #include "chromeos/services/ime/public/cpp/shared_lib/interfaces.h" #include "chromeos/services/ime/public/mojom/input_engine.mojom.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace chromeos { @@ -33,7 +36,7 @@ bool BindRequest(const std::string& ime_spec, mojo::PendingReceiver<mojom::InputChannel> receiver, mojo::PendingRemote<mojom::InputChannel> remote, - const std::vector<uint8_t>& extra); + base::OnceCallback<void()> disconnect_callback); // mojom::InputChannel: void ProcessMessage(const std::vector<uint8_t>& message, @@ -50,8 +53,7 @@ const std::string& text, uint32_t offset, mojom::SelectionRangePtr selection_range) override; - void OnCompositionCanceled() override; - void ResetForRulebased() override {} + void OnCompositionCanceledBySystem() override; void CommitText(const std::string& text, mojom::CommitTextCursorBehavior cursor_behavior) override {} void SetComposition(const std::string& text) override {} @@ -90,9 +92,6 @@ mojo::Receiver<mojom::InputChannel> decoder_channel_receiver_; - // Whether `decoder_channel_receiver_` is connected. - bool is_decoder_receiver_connected_ = false; - // Sequence ID for protobuf messages sent from the engine. uint64_t current_seq_id_ = 0;
diff --git a/chromeos/services/ime/decoder/system_engine_unittest.cc b/chromeos/services/ime/decoder/system_engine_unittest.cc index f6c36ff..9d3c55d5 100644 --- a/chromeos/services/ime/decoder/system_engine_unittest.cc +++ b/chromeos/services/ime/decoder/system_engine_unittest.cc
@@ -96,7 +96,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; EXPECT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); EXPECT_TRUE(client.is_bound()); EXPECT_TRUE(mock_channel.IsBound()); @@ -107,7 +108,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); ime::Wrapper expected_proto; *expected_proto.mutable_public_message() = OnInputMethodChangedToProto(/*seq_id=*/0, "xkb:us::eng"); @@ -123,7 +125,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); auto info = mojom::InputFieldInfo::New(mojom::InputFieldType::kNumber, mojom::AutocorrectMode::kEnabled, @@ -144,7 +147,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); ime::Wrapper expected_proto; *expected_proto.mutable_public_message() = OnBlurToProto(/*seq_id=*/0); @@ -159,7 +163,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); auto key_event = mojom::PhysicalKeyEvent::New( mojom::KeyEventType::kKeyDown, "KeyA", "A", mojom::ModifierState::New()); ime::Wrapper expected_proto; @@ -194,7 +199,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); const auto selection = mojom::SelectionRange::New(/*anchor=*/3, /*focus=*/2); ime::Wrapper expected_proto; *expected_proto.mutable_public_message() = OnSurroundingTextChangedToProto( @@ -211,14 +217,15 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); ime::Wrapper expected_proto; *expected_proto.mutable_public_message() = OnCompositionCanceledToProto(/*seq_id=*/0); EXPECT_CALL(decoder_entry_points_, Process).With(EqualsProto(expected_proto)); - client->OnCompositionCanceled(); + client->OnCompositionCanceledBySystem(); client.FlushForTesting(); } @@ -227,7 +234,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); ime::Wrapper proto; proto.mutable_public_message()->mutable_commit_text()->set_text("hello"); @@ -251,7 +259,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); ime::Wrapper proto; proto.mutable_public_message()->mutable_set_composition()->set_text("hello"); @@ -268,7 +277,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); ime::Wrapper proto; proto.mutable_public_message() ->mutable_set_composition_range() @@ -290,7 +300,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); ime::Wrapper proto; *proto.mutable_public_message()->mutable_finish_composition() = ime::FinishComposition(); @@ -308,7 +319,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); ime::Wrapper proto; proto.mutable_public_message() ->mutable_delete_surrounding_text() @@ -333,7 +345,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); ime::Wrapper proto; auto* suggestions_request = @@ -370,7 +383,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); ime::Wrapper expected_response_proto; ime::PublicMessage* expected_message = @@ -418,7 +432,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); ime::Wrapper proto; auto* candidate = proto.mutable_public_message() @@ -450,7 +465,8 @@ MockInputChannel mock_channel; mojo::Remote<mojom::InputChannel> client; ASSERT_TRUE(engine.BindRequest(kImeSpec, client.BindNewPipeAndPassReceiver(), - mock_channel.CreatePendingRemote(), {})); + mock_channel.CreatePendingRemote(), + base::DoNothing())); Wrapper proto; proto.mutable_public_message() ->mutable_record_ukm()
diff --git a/chromeos/services/ime/ime_service.cc b/chromeos/services/ime/ime_service.cc index b266ef5..565576c 100644 --- a/chromeos/services/ime/ime_service.cc +++ b/chromeos/services/ime/ime_service.cc
@@ -21,6 +21,7 @@ #include "chromeos/services/ime/decoder/decoder_engine.h" #include "chromeos/services/ime/decoder/system_engine.h" #include "chromeos/services/ime/public/cpp/buildflags.h" +#include "chromeos/services/ime/rule_based_engine.h" namespace chromeos { namespace ime { @@ -74,11 +75,40 @@ mojo::PendingRemote<mojom::InputChannel> from_engine, const std::vector<uint8_t>& extra, ConnectToImeEngineCallback callback) { + // There can only be one client using the decoder at any time. There are two + // possible clients: NativeInputMethodEngine (for physical keyboard) and the + // XKB extension (for virtual keyboard). The XKB extension may try to + // connect the decoder even when it's not supposed to (due to race + // conditions), so we must prevent the extension from taking over the + // NativeInputMethodEngine connection. + // + // This is a hack to to determine whether a connection came from + // NativeInputMethodEngine or the extension. NativeInputMethodEngine will + // send some extra bytes, whereas the extension doesn't. Thus, we can + // prevent the extension from taking over the NativeInputMethodEngine's + // connection to the decoder. NativeInputMethodEngine will voluntarily give + // up its connection when tswitching to tablet mode, allowing the extension + // to connect again. + // TODO(b/184115850): Create a separate Mojo API for NativeInputMethodEngine + // so that we don't need to inspect `extra` to distinguish the client. + if (is_privileged_connection_ && extra.size() == 0) { + std::move(callback).Run(/*bound=*/false); + return; + } + if (base::FeatureList::IsEnabled( chromeos::features::kSystemLatinPhysicalTyping)) { auto system_engine = std::make_unique<SystemEngine>(this); bool bound = system_engine->BindRequest( - ime_spec, std::move(to_engine_request), std::move(from_engine), extra); + ime_spec, std::move(to_engine_request), std::move(from_engine), + base::BindOnce( + [](bool& is_decoder_receiver_connected) { + is_decoder_receiver_connected = false; + }, + std::ref(is_privileged_connection_))); + if (bound) { + is_privileged_connection_ = extra.size() > 0; + } input_engine_ = std::move(system_engine); std::move(callback).Run(bound); } else { @@ -94,7 +124,7 @@ const std::string& ime_spec, mojo::PendingReceiver<mojom::InputChannel> to_engine, ConnectToInputMethodCallback callback) { - input_engine_ = InputEngine::Create(ime_spec, std::move(to_engine)); + input_engine_ = RuleBasedEngine::Create(ime_spec, std::move(to_engine)); std::move(callback).Run(/*bound=*/input_engine_ != nullptr); }
diff --git a/chromeos/services/ime/ime_service.h b/chromeos/services/ime/ime_service.h index 7aab78e..3793929 100644 --- a/chromeos/services/ime/ime_service.h +++ b/chromeos/services/ime/ime_service.h
@@ -11,7 +11,6 @@ #include <vector> #include "base/files/file_path.h" -#include "chromeos/services/ime/input_engine.h" #include "chromeos/services/ime/public/cpp/shared_lib/interfaces.h" #include "chromeos/services/ime/public/mojom/ime_service.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" @@ -85,6 +84,10 @@ mojo::Remote<mojom::PlatformAccessProvider> platform_access_; mojo::ReceiverSet<mojom::InputEngineManager> manager_receivers_; + // If the current connection is privileged, then `ConnectToImeEngine` must be + // called with a non-empty `extra` in order to override the connection. + bool is_privileged_connection_ = false; + DISALLOW_COPY_AND_ASSIGN(ImeService); };
diff --git a/chromeos/services/ime/ime_service_unittest.cc b/chromeos/services/ime/ime_service_unittest.cc index ac54bd70..e90e7584 100644 --- a/chromeos/services/ime/ime_service_unittest.cc +++ b/chromeos/services/ime/ime_service_unittest.cc
@@ -4,9 +4,13 @@ #include "chromeos/services/ime/ime_service.h" +#include "ash/constants/ash_features.h" #include "base/bind.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" +#include "chromeos/services/ime/decoder/system_engine.h" +#include "chromeos/services/ime/ime_decoder.h" #include "chromeos/services/ime/mock_input_channel.h" #include "chromeos/services/ime/public/mojom/input_engine.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" @@ -24,12 +28,26 @@ namespace { const char kInvalidImeSpec[] = "ime_spec_never_support"; +constexpr char kArabicImeSpec[] = "m17n:ar"; const std::vector<uint8_t> extra{0x66, 0x77, 0x88}; void ConnectCallback(bool* success, bool result) { *success = result; } +ImeDecoder::EntryPoints CreateDecoderEntryPoints() { + ImeDecoder::EntryPoints entry_points; + entry_points.init_once = [](ImeCrosPlatform* platform) {}; + entry_points.supports = [](const char* ime_spec) { + return strcmp(kInvalidImeSpec, ime_spec) != 0; + }; + entry_points.activate_ime = [](const char* ime_spec, + ImeClientDelegate* delegate) { return true; }; + entry_points.process = [](const uint8_t* data, size_t size) {}; + entry_points.close = []() {}; + return entry_points; +} + void TestProcessKeypressForRulebasedCallback( mojom::KeypressResponseForRulebased* res_out, mojom::KeypressResponseForRulebasedPtr response) { @@ -49,6 +67,12 @@ protected: void SetUp() override { + feature_list_.InitWithFeatures( + /*enabled_features=*/{features::kImeMojoDecoder, + features::kSystemLatinPhysicalTyping}, + /*disabled_features=*/{}); + + FakeDecoderEntryPointsForTesting(CreateDecoderEntryPoints()); remote_service_->BindInputEngineManager( remote_manager_.BindNewPipeAndPassReceiver()); } @@ -58,6 +82,7 @@ private: base::test::TaskEnvironment task_environment_; + base::test::ScopedFeatureList feature_list_; ImeService service_; DISALLOW_COPY_AND_ASSIGN(ImeServiceTest); @@ -67,7 +92,7 @@ // Tests that the service is instantiated and it will return false when // activating an IME engine with an invalid IME spec. -TEST_F(ImeServiceTest, ConnectInvalidImeEngine) { +TEST_F(ImeServiceTest, ConnectInvalidImeEngineDoesNotConnectRemote) { bool success = true; MockInputChannel test_channel; mojo::Remote<mojom::InputChannel> remote_engine; @@ -77,7 +102,97 @@ test_channel.CreatePendingRemote(), extra, base::BindOnce(&ConnectCallback, &success)); remote_manager_.FlushForTesting(); + EXPECT_FALSE(success); + EXPECT_FALSE(remote_engine.is_connected()); +} + +TEST_F(ImeServiceTest, ConnectToValidEngineConnectsRemote) { + bool success = true; + MockInputChannel test_channel; + mojo::Remote<mojom::InputChannel> remote_engine; + + remote_manager_->ConnectToImeEngine( + kArabicImeSpec, remote_engine.BindNewPipeAndPassReceiver(), + test_channel.CreatePendingRemote(), {}, + base::BindOnce(&ConnectCallback, &success)); + remote_manager_.FlushForTesting(); + + EXPECT_TRUE(success); + EXPECT_TRUE(remote_engine.is_connected()); +} + +TEST_F(ImeServiceTest, + ConnectWithEmptyExtraCanOverrideExistingConnectionWithEmptyExtra) { + bool success1, success2 = true; + MockInputChannel test_channel1, test_channel2; + mojo::Remote<mojom::InputChannel> remote_engine1, remote_engine2; + + remote_manager_->ConnectToImeEngine( + kArabicImeSpec, remote_engine1.BindNewPipeAndPassReceiver(), + test_channel1.CreatePendingRemote(), /*extra=*/{}, + base::BindOnce(&ConnectCallback, &success1)); + remote_manager_->ConnectToImeEngine( + kArabicImeSpec, remote_engine2.BindNewPipeAndPassReceiver(), + test_channel2.CreatePendingRemote(), /*extra=*/{}, + base::BindOnce(&ConnectCallback, &success2)); + remote_manager_.FlushForTesting(); + + EXPECT_TRUE(success1); + EXPECT_TRUE(success2); + EXPECT_FALSE(remote_engine1.is_connected()); + EXPECT_TRUE(remote_engine2.is_connected()); +} + +TEST_F(ImeServiceTest, + ConnectWithEmptyExtraCannotOverrideExistingConnectionWithExtra) { + bool success1, success2 = true; + MockInputChannel test_channel1, test_channel2; + mojo::Remote<mojom::InputChannel> remote_engine1, remote_engine2; + + remote_manager_->ConnectToImeEngine( + kArabicImeSpec, remote_engine1.BindNewPipeAndPassReceiver(), + test_channel1.CreatePendingRemote(), /*extra=*/{0}, + base::BindOnce(&ConnectCallback, &success1)); + remote_manager_->ConnectToImeEngine( + kArabicImeSpec, remote_engine2.BindNewPipeAndPassReceiver(), + test_channel2.CreatePendingRemote(), /*extra=*/{}, + base::BindOnce(&ConnectCallback, &success2)); + remote_manager_.FlushForTesting(); + + // The second connection should have failed. + EXPECT_TRUE(success1); + EXPECT_FALSE(success2); + EXPECT_TRUE(remote_engine1.is_connected()); + EXPECT_FALSE(remote_engine2.is_connected()); +} + +TEST_F(ImeServiceTest, ConnectWithExtraCanOverrideExistingConnection) { + bool success1, success2, success3 = true; + MockInputChannel test_channel1, test_channel2, test_channel3; + mojo::Remote<mojom::InputChannel> remote_engine1, remote_engine2, + remote_engine3; + + remote_manager_->ConnectToImeEngine( + kArabicImeSpec, remote_engine1.BindNewPipeAndPassReceiver(), + test_channel1.CreatePendingRemote(), /*extra=*/{}, + base::BindOnce(&ConnectCallback, &success1)); + remote_manager_->ConnectToImeEngine( + kArabicImeSpec, remote_engine2.BindNewPipeAndPassReceiver(), + test_channel2.CreatePendingRemote(), /*extra=*/{0}, + base::BindOnce(&ConnectCallback, &success2)); + remote_manager_->ConnectToImeEngine( + kArabicImeSpec, remote_engine3.BindNewPipeAndPassReceiver(), + test_channel3.CreatePendingRemote(), /*extra=*/{0}, + base::BindOnce(&ConnectCallback, &success3)); + remote_manager_.FlushForTesting(); + + EXPECT_TRUE(success1); + EXPECT_TRUE(success2); + EXPECT_TRUE(success3); + EXPECT_FALSE(remote_engine1.is_connected()); + EXPECT_FALSE(remote_engine2.is_connected()); + EXPECT_TRUE(remote_engine3.is_connected()); } TEST_F(ImeServiceTest, RuleBasedDoesNotHandleModifierKeys) { @@ -250,7 +365,7 @@ EXPECT_EQ(response.result, false); // TODO(keithlee) Test reset function - to_engine_remote->ResetForRulebased(); + to_engine_remote->OnCompositionCanceledBySystem(); // Test invalid request. to_engine_remote->ProcessKeypressForRulebased(
diff --git a/chromeos/services/ime/mock_input_channel.h b/chromeos/services/ime/mock_input_channel.h index f267b0d..3b0d06cf 100644 --- a/chromeos/services/ime/mock_input_channel.h +++ b/chromeos/services/ime/mock_input_channel.h
@@ -54,8 +54,7 @@ uint32_t offset, mojom::SelectionRangePtr selection_range), (override)); - MOCK_METHOD(void, OnCompositionCanceled, (), (override)); - MOCK_METHOD(void, ResetForRulebased, (), (override)); + MOCK_METHOD(void, OnCompositionCanceledBySystem, (), (override)); MOCK_METHOD(void, CommitText, (const std::string& text,
diff --git a/chromeos/services/ime/public/mojom/input_engine.mojom b/chromeos/services/ime/public/mojom/input_engine.mojom index cd2c3ce..e891233 100644 --- a/chromeos/services/ime/public/mojom/input_engine.mojom +++ b/chromeos/services/ime/public/mojom/input_engine.mojom
@@ -273,7 +273,7 @@ // Informs the IME that composition was canceled by the system. This can // for example happen if the user presses the Escape key. - OnCompositionCanceled(); + OnCompositionCanceledBySystem(); // Process a PhysicalKeyEvent using the rule-based engine and return a // KeypressResponseForRulebased object with a list of operations to be handled @@ -281,9 +281,6 @@ ProcessKeypressForRulebased(PhysicalKeyEvent event) => (KeypressResponseForRulebased result); - // Resets the engine for rulebased logic. - ResetForRulebased(); - // Asks the input field to commit `text`. If there is a composition, then that // composition is removed and `text` is inserted. Otherwise, `text` is // inserted at the current cursor position, deleting any text in the current
diff --git a/chromeos/services/ime/input_engine.cc b/chromeos/services/ime/rule_based_engine.cc similarity index 77% rename from chromeos/services/ime/input_engine.cc rename to chromeos/services/ime/rule_based_engine.cc index 5536513..eccd16e 100644 --- a/chromeos/services/ime/input_engine.cc +++ b/chromeos/services/ime/rule_based_engine.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chromeos/services/ime/input_engine.h" +#include "chromeos/services/ime/rule_based_engine.h" #include "base/notreached.h" #include "base/strings/string_util.h" @@ -73,46 +73,52 @@ } // namespace -std::unique_ptr<InputEngine> InputEngine::Create( +std::unique_ptr<RuleBasedEngine> RuleBasedEngine::Create( const std::string& ime_spec, mojo::PendingReceiver<mojom::InputChannel> receiver) { - // InputEngine constructor is private, so have to use WrapUnique here. + // RuleBasedEngine constructor is private, so have to use WrapUnique here. return IsImeSupportedByRulebased(ime_spec) - ? base::WrapUnique(new InputEngine(ime_spec, std::move(receiver))) + ? base::WrapUnique( + new RuleBasedEngine(ime_spec, std::move(receiver))) : nullptr; } -InputEngine::~InputEngine() = default; +RuleBasedEngine::~RuleBasedEngine() = default; -void InputEngine::ProcessMessage(const std::vector<uint8_t>& message, - ProcessMessageCallback callback) { +void RuleBasedEngine::ProcessMessage(const std::vector<uint8_t>& message, + ProcessMessageCallback callback) { NOTIMPLEMENTED(); // Protobuf message is not used in the rulebased engine. } -void InputEngine::OnInputMethodChanged(const std::string& engine_id) { +void RuleBasedEngine::OnInputMethodChanged(const std::string& engine_id) { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -void InputEngine::OnFocus(mojom::InputFieldInfoPtr input_field_info) { +void RuleBasedEngine::OnFocus(mojom::InputFieldInfoPtr input_field_info) { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -void InputEngine::OnBlur() { +void RuleBasedEngine::OnBlur() { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -void InputEngine::OnSurroundingTextChanged( +void RuleBasedEngine::OnSurroundingTextChanged( const std::string& text, uint32_t offset, mojom::SelectionRangePtr selection_range) { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -void InputEngine::OnCompositionCanceled() { - NOTIMPLEMENTED(); // Not used in the rulebased engine. +void RuleBasedEngine::OnCompositionCanceledBySystem() { + // TODO(https://crbug.com/1633694) Handle the case when the engine is not + // defined + if (engine_) { + engine_->Reset(); + } + isAltRightDown_ = false; } -void InputEngine::ProcessKeypressForRulebased( +void RuleBasedEngine::ProcessKeypressForRulebased( mojom::PhysicalKeyEventPtr event, ProcessKeypressForRulebasedCallback callback) { // According to the W3C spec, |altKey| is false if the AltGr key @@ -151,63 +157,56 @@ std::move(callback).Run(std::move(keypress_response)); } -void InputEngine::OnKeyEvent(mojom::PhysicalKeyEventPtr event, - OnKeyEventCallback callback) { +void RuleBasedEngine::OnKeyEvent(mojom::PhysicalKeyEventPtr event, + OnKeyEventCallback callback) { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -void InputEngine::ResetForRulebased() { - // TODO(https://crbug.com/1633694) Handle the case when the engine is not - // defined - if (engine_) { - engine_->Reset(); - } - isAltRightDown_ = false; -} - -void InputEngine::CommitText(const std::string& text, - mojom::CommitTextCursorBehavior cursor_behavior) { +void RuleBasedEngine::CommitText( + const std::string& text, + mojom::CommitTextCursorBehavior cursor_behavior) { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -void InputEngine::SetComposition(const std::string& text) { +void RuleBasedEngine::SetComposition(const std::string& text) { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -void InputEngine::SetCompositionRange(uint32_t start, uint32_t end) { +void RuleBasedEngine::SetCompositionRange(uint32_t start, uint32_t end) { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -void InputEngine::FinishComposition() { +void RuleBasedEngine::FinishComposition() { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -void InputEngine::DeleteSurroundingText(uint32_t num_bytes_before_cursor, - uint32_t num_bytes_after_cursor) { +void RuleBasedEngine::DeleteSurroundingText(uint32_t num_bytes_before_cursor, + uint32_t num_bytes_after_cursor) { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -void InputEngine::HandleAutocorrect( +void RuleBasedEngine::HandleAutocorrect( mojom::AutocorrectSpanPtr autocorrect_span) { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -void InputEngine::RequestSuggestions(mojom::SuggestionsRequestPtr request, - RequestSuggestionsCallback callback) { +void RuleBasedEngine::RequestSuggestions(mojom::SuggestionsRequestPtr request, + RequestSuggestionsCallback callback) { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -void InputEngine::DisplaySuggestions( +void RuleBasedEngine::DisplaySuggestions( const std::vector<TextSuggestion>& suggestions) { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -void InputEngine::RecordUkm(mojom::UkmEntryPtr entry) { +void RuleBasedEngine::RecordUkm(mojom::UkmEntryPtr entry) { NOTIMPLEMENTED(); // Not used in the rulebased engine. } -InputEngine::InputEngine(const std::string& ime_spec, - mojo::PendingReceiver<mojom::InputChannel> receiver) +RuleBasedEngine::RuleBasedEngine( + const std::string& ime_spec, + mojo::PendingReceiver<mojom::InputChannel> receiver) : receiver_(this, std::move(receiver)), engine_(std::make_unique<rulebased::Engine>()) { DCHECK(IsImeSupportedByRulebased(ime_spec));
diff --git a/chromeos/services/ime/input_engine.h b/chromeos/services/ime/rule_based_engine.h similarity index 74% rename from chromeos/services/ime/input_engine.h rename to chromeos/services/ime/rule_based_engine.h index 686ce7a..63da65a1 100644 --- a/chromeos/services/ime/input_engine.h +++ b/chromeos/services/ime/rule_based_engine.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMEOS_SERVICES_IME_INPUT_ENGINE_H_ -#define CHROMEOS_SERVICES_IME_INPUT_ENGINE_H_ +#ifndef CHROMEOS_SERVICES_IME_RULE_BASED_ENGINE_H_ +#define CHROMEOS_SERVICES_IME_RULE_BASED_ENGINE_H_ #include "chromeos/services/ime/public/cpp/suggestions.h" #include "chromeos/services/ime/public/mojom/input_engine.mojom.h" @@ -18,18 +18,19 @@ class Engine; } -// A basic implementation of InputEngine without using any decoder. -// TODO(https://crbug.com/1019541): Rename this to RuleBasedEngine. -class InputEngine : public mojom::InputChannel { +// Handles rule-based input methods such as Arabic and Vietnamese. +// Rule-based input methods are based off deterministic rules and do not +// provide features such as suggestions. +class RuleBasedEngine : public mojom::InputChannel { public: - // Returns nullptr if |ime_spec| is not valid for this InputEngine. - static std::unique_ptr<InputEngine> Create( + // Returns nullptr if |ime_spec| is not valid for this RuleBasedEngine. + static std::unique_ptr<RuleBasedEngine> Create( const std::string& ime_spec, mojo::PendingReceiver<mojom::InputChannel> receiver); - InputEngine(const InputEngine& other) = delete; - InputEngine& operator=(const InputEngine& other) = delete; - ~InputEngine() override; + RuleBasedEngine(const RuleBasedEngine& other) = delete; + RuleBasedEngine& operator=(const RuleBasedEngine& other) = delete; + ~RuleBasedEngine() override; // mojom::InputChannel overrides: void ProcessMessage(const std::vector<uint8_t>& message, @@ -41,13 +42,12 @@ const std::string& text, uint32_t offset, mojom::SelectionRangePtr selection_range) override; - void OnCompositionCanceled() override; + void OnCompositionCanceledBySystem() override; void ProcessKeypressForRulebased( mojom::PhysicalKeyEventPtr event, ProcessKeypressForRulebasedCallback callback) override; void OnKeyEvent(mojom::PhysicalKeyEventPtr event, OnKeyEventCallback callback) override; - void ResetForRulebased() override; void CommitText(const std::string& text, mojom::CommitTextCursorBehavior cursor_behavior) override; void SetComposition(const std::string& text) override; @@ -66,8 +66,8 @@ // TODO(https://crbug.com/837156): Implement a state for the interface. private: - InputEngine(const std::string& ime_spec, - mojo::PendingReceiver<mojom::InputChannel> receiver); + RuleBasedEngine(const std::string& ime_spec, + mojo::PendingReceiver<mojom::InputChannel> receiver); mojo::Receiver<mojom::InputChannel> receiver_; std::unique_ptr<rulebased::Engine> engine_; @@ -79,4 +79,4 @@ } // namespace ime } // namespace chromeos -#endif // CHROMEOS_SERVICES_IME_INPUT_ENGINE_H_ +#endif // CHROMEOS_SERVICES_IME_RULE_BASED_ENGINE_H_
diff --git a/components/accuracy_tips/accuracy_service.cc b/components/accuracy_tips/accuracy_service.cc index 539ad64..b61678b 100644 --- a/components/accuracy_tips/accuracy_service.cc +++ b/components/accuracy_tips/accuracy_service.cc
@@ -5,7 +5,10 @@ #include "components/accuracy_tips/accuracy_service.h" #include "base/bind.h" +#include "base/callback.h" #include "base/memory/scoped_refptr.h" +#include "base/metrics/histogram_functions.h" +#include "base/time/time.h" #include "components/accuracy_tips/accuracy_tip_status.h" #include "components/accuracy_tips/accuracy_tip_ui.h" #include "components/accuracy_tips/features.h" @@ -30,16 +33,23 @@ void AccuracyService::MaybeShowAccuracyTip(content::WebContents* web_contents) { // TODO(crbug.com/1210891): Implement rate limiting. - if (!ui_) - return; - ui_->ShowAccuracyTip(web_contents, AccuracyTipStatus::kMisinformation, - base::BindOnce(&AccuracyService::OnAccuracyTipClosed, - weak_factory_.GetWeakPtr())); + ui_->ShowAccuracyTip( + web_contents, AccuracyTipStatus::kMisinformation, + base::BindOnce(&AccuracyService::OnAccuracyTipClosed, + weak_factory_.GetWeakPtr(), base::TimeTicks::Now())); } void AccuracyService::OnAccuracyTipClosed( + base::TimeTicks time_opened, AccuracyTipUI::Interaction interaction) { - // TODO(crbug.com/1210891): Log histograms. + base::UmaHistogramEnumeration("Privacy.AccuracyTip.AccuracyTipInteraction", + interaction); + base::UmaHistogramMediumTimes("Privacy.AccuracyTip.AccuracyTipTimeOpen", + base::TimeTicks::Now() - time_opened); +} + +void AccuracyService::SetSampleUrlForTesting(const GURL& url) { + sample_url_ = url; } } // namespace accuracy_tips
diff --git a/components/accuracy_tips/accuracy_service.h b/components/accuracy_tips/accuracy_service.h index 2bb5bb8..e254d77 100644 --- a/components/accuracy_tips/accuracy_service.h +++ b/components/accuracy_tips/accuracy_service.h
@@ -38,8 +38,11 @@ // Shows an accuracy tip UI for web_contents after checking rate limits. virtual void MaybeShowAccuracyTip(content::WebContents* web_contents); + void SetSampleUrlForTesting(const GURL& url); + private: - void OnAccuracyTipClosed(AccuracyTipUI::Interaction interaction); + void OnAccuracyTipClosed(base::TimeTicks time_opened, + AccuracyTipUI::Interaction interaction); std::unique_ptr<AccuracyTipUI> ui_; GURL sample_url_;
diff --git a/components/accuracy_tips/accuracy_service_unittest.cc b/components/accuracy_tips/accuracy_service_unittest.cc index f254d9bf..2877223 100644 --- a/components/accuracy_tips/accuracy_service_unittest.cc +++ b/components/accuracy_tips/accuracy_service_unittest.cc
@@ -30,10 +30,8 @@ AccuracyServiceTest() = default; void SetUp() override { - base::FieldTrialParams params; - params[kSampleUrl.name] = "https://badurl.com"; - feature_list.InitAndEnableFeatureWithParameters(kAccuracyTipsFeature, - params); + feature_list.InitAndEnableFeatureWithParameters( + kAccuracyTipsFeature, {{kSampleUrl.name, "https://badurl.com"}}); auto ui = std::make_unique<testing::StrictMock<MockAccuracyTipUI>>(); ui_ = ui.get(); service_ = std::make_unique<AccuracyService>(std::move(ui));
diff --git a/components/accuracy_tips/accuracy_tip_status.h b/components/accuracy_tips/accuracy_tip_status.h index cc373fde..3a92b13c 100644 --- a/components/accuracy_tips/accuracy_tip_status.h +++ b/components/accuracy_tips/accuracy_tip_status.h
@@ -8,11 +8,15 @@ namespace accuracy_tips { // Represents the different results of the accuracy check. +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. enum class AccuracyTipStatus { // No accuracy information for the site. kNone = 0, // Site classified as being misleading. kMisinformation = 1, + + kMaxValue = kMisinformation, }; } // namespace accuracy_tips
diff --git a/components/accuracy_tips/accuracy_tip_ui.h b/components/accuracy_tips/accuracy_tip_ui.h index 058936d0..d231bad 100644 --- a/components/accuracy_tips/accuracy_tip_ui.h +++ b/components/accuracy_tips/accuracy_tip_ui.h
@@ -5,7 +5,7 @@ #ifndef COMPONENTS_ACCURACY_TIPS_ACCURACY_TIP_UI_H_ #define COMPONENTS_ACCURACY_TIPS_ACCURACY_TIP_UI_H_ -#include "base/callback.h" +#include "base/callback_forward.h" #include "components/accuracy_tips/accuracy_tip_status.h" namespace content { @@ -24,13 +24,20 @@ AccuracyTipUI& operator=(const AccuracyTipUI&) = delete; // Represents the different user interactions with a AccuracyTip dialog. + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. enum class Interaction { kNoAction = 0, - kDismiss = 1, - kIgnore = 2, - kLearnMore = 3, + // Closed because user clicked outside the UI. + kLostFocus = 1, + // Pressed ESC or close button. + kClosed = 2, + // Ignore button pressed. + kIgnorePressed = 3, + // Learn more button pressed. + kLearnMorePressed = 4, - kMaxValue = kLearnMore, + kMaxValue = kLearnMorePressed, }; // Shows AccuracyTip UI using the specified information if it is not already
diff --git a/components/accuracy_tips/accuracy_web_contents_observer.cc b/components/accuracy_tips/accuracy_web_contents_observer.cc index 5677aaf3..4900b8d 100644 --- a/components/accuracy_tips/accuracy_web_contents_observer.cc +++ b/components/accuracy_tips/accuracy_web_contents_observer.cc
@@ -4,6 +4,7 @@ #include "components/accuracy_tips/accuracy_web_contents_observer.h" +#include "base/metrics/histogram_macros.h" #include "components/accuracy_tips/accuracy_service.h" #include "components/accuracy_tips/accuracy_tip_status.h" #include "components/accuracy_tips/features.h" @@ -58,6 +59,8 @@ void AccuracyWebContentsObserver::OnAccuracyStatusObtained( const GURL& url, AccuracyTipStatus result) { + UMA_HISTOGRAM_ENUMERATION("Privacy.AccuracyTip.PageStatus", result); + if (result == AccuracyTipStatus::kNone) return;
diff --git a/components/accuracy_tips/accuracy_web_contents_observer_unittest.cc b/components/accuracy_tips/accuracy_web_contents_observer_unittest.cc index 15216997..ae06939 100644 --- a/components/accuracy_tips/accuracy_web_contents_observer_unittest.cc +++ b/components/accuracy_tips/accuracy_web_contents_observer_unittest.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "components/accuracy_tips/accuracy_web_contents_observer.h" + #include <memory> #include "base/test/bind.h"
diff --git a/components/arc/arc_features.cc b/components/arc/arc_features.cc index d20471d..9278253 100644 --- a/components/arc/arc_features.cc +++ b/components/arc/arc_features.cc
@@ -49,6 +49,10 @@ const base::Feature kEnableUsap{"ArcEnableUsap", base::FEATURE_DISABLED_BY_DEFAULT}; +// Controls whether ARC apps can share to Web Apps through WebAPKs and TWAs. +const base::Feature kEnableWebAppShareFeature{ + "ArcEnableWebAppShare", base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls experimental file picker feature for ARC. const base::Feature kFilePickerExperimentFeature{ "ArcFilePickerExperiment", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/components/arc/arc_features.h b/components/arc/arc_features.h index 6b464c27..194e4eb 100644 --- a/components/arc/arc_features.h +++ b/components/arc/arc_features.h
@@ -21,6 +21,7 @@ extern const base::Feature kEnableUnifiedAudioFocusFeature; extern const base::Feature kEnableUnmanagedToManagedTransitionFeature; extern const base::Feature kEnableUsap; +extern const base::Feature kEnableWebAppShareFeature; extern const base::Feature kFilePickerExperimentFeature; extern const base::Feature kNativeBridge64BitSupportExperimentFeature; extern const base::Feature kNativeBridgeToggleFeature;
diff --git a/components/arc/compat_mode/resize_toggle_menu.cc b/components/arc/compat_mode/resize_toggle_menu.cc index 3692b78..df04ad8 100644 --- a/components/arc/compat_mode/resize_toggle_menu.cc +++ b/components/arc/compat_mode/resize_toggle_menu.cc
@@ -18,6 +18,7 @@ #include "ui/views/background.h" #include "ui/views/border.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h" +#include "ui/views/controls/highlight_path_generator.h" #include "ui/views/controls/image_view.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/layout_provider.h" @@ -46,8 +47,6 @@ class MenuButtonView : public views::Button { public: - static constexpr int kBorderThicknessDp = 1; - MenuButtonView(PressedCallback callback, const gfx::VectorIcon& icon, int title_string_id, @@ -69,10 +68,21 @@ .SetMultiLine(true) .SetMaxLines(2) .Build()); - SetBorder(views::CreateEmptyBorder(gfx::Insets(kBorderThicknessDp))); SetPreferredSize(gfx::Size(96, 86)); SetAccessibleName(l10n_util::GetStringUTF16(title_string_id)); GetViewAccessibility().OverrideRole(ax::mojom::Role::kMenuItem); + + constexpr int kBorderThicknessDp = 1; + const auto radius = views::LayoutProvider::Get()->GetCornerRadiusMetric( + views::Emphasis::kHigh); + SetBorder(views::CreateRoundedRectBorder(kBorderThicknessDp, radius, + gfx::kPlaceholderColor)); + SetBackground( + views::CreateRoundedRectBackground(gfx::kPlaceholderColor, radius)); + + SetFocusBehavior(FocusBehavior::ALWAYS); + SetInstallFocusRingOnFocus(true); + views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(), radius); } MenuButtonView(const MenuButtonView&) = delete; MenuButtonView& operator=(const MenuButtonView&) = delete; @@ -80,6 +90,8 @@ private: void Layout() override { + views::View::Layout(); + constexpr int kIconSize = 24; constexpr int kIconTopPadding = 17; @@ -93,43 +105,29 @@ title_->SetBoundsRect(content_bounds_with_padding); } - void OnFocus() override { SchedulePaint(); } - - void OnBlur() override { SchedulePaint(); } - void OnThemeChanged() override { views::Button::OnThemeChanged(); - const auto color = GetNativeTheme()->GetSystemColor( + + const auto* theme = GetNativeTheme(); + + const auto foreground_color = theme->GetSystemColor( is_selected_ ? ui::NativeTheme::kColorId_ProminentButtonColor : ui::NativeTheme::kColorId_LabelEnabledColor); - icon_view_->SetImage(gfx::CreateVectorIcon(icon_, color)); - title_->SetEnabledColor(color); - } + icon_view_->SetImage(gfx::CreateVectorIcon(icon_, foreground_color)); + title_->SetEnabledColor(foreground_color); - void PaintButtonContents(gfx::Canvas* canvas) override { - auto* const provider = views::LayoutProvider::Get(); - const int round_radius = - provider->GetCornerRadiusMetric(views::Emphasis::kHigh); + const auto background_color = + is_selected_ + ? theme->GetSystemColor( + ui::NativeTheme::kColorId_MenuItemTargetAlertBackgroundColor) + : SK_ColorTRANSPARENT; + background()->SetNativeControlColor(background_color); - const gfx::Rect content_bounds = GetContentsBounds(); - cc::PaintFlags flags; - flags.setAntiAlias(true); - if (is_selected_) { - flags.setColor(GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_MenuItemTargetAlertBackgroundColor)); - flags.setStyle(cc::PaintFlags::kFill_Style); - canvas->DrawRoundRect(content_bounds, round_radius, flags); - } else { - flags.setColor(SK_ColorTRANSPARENT); - flags.setStyle(cc::PaintFlags::kFill_Style); - canvas->DrawRoundRect(content_bounds, round_radius, flags); - - flags.setColor(GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_MenuBorderColor)); - flags.setStrokeWidth(kBorderThicknessDp); - flags.setStyle(cc::PaintFlags::Style::kStroke_Style); - canvas->DrawRoundRect(content_bounds, round_radius, flags); - } + const auto border_color = + is_selected_ + ? SK_ColorTRANSPARENT + : theme->GetSystemColor(ui::NativeTheme::kColorId_MenuBorderColor); + border()->set_color(border_color); } // Owned by views hierarchy.
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge.cc b/components/arc/intent_helper/arc_intent_helper_bridge.cc index 53380623..c10b905e 100644 --- a/components/arc/intent_helper/arc_intent_helper_bridge.cc +++ b/components/arc/intent_helper/arc_intent_helper_bridge.cc
@@ -17,6 +17,7 @@ #include "base/strings/string_util.h" #include "base/values.h" #include "components/arc/arc_browser_context_keyed_service_factory_base.h" +#include "components/arc/arc_features.h" #include "components/arc/arc_service_manager.h" #include "components/arc/audio/arc_audio_bridge.h" #include "components/arc/intent_helper/control_camera_app_delegate.h" @@ -326,6 +327,27 @@ observer.OnArcDownloadAdded(relative_path, owner_package_name); } +void ArcIntentHelperBridge::OnOpenAppWithIntent( + const GURL& start_url, + arc::mojom::LaunchIntentPtr intent) { + // Fall-back to the previous behavior where the web app opens without any + // share data. + if (!base::FeatureList::IsEnabled(arc::kEnableWebAppShareFeature)) { + if (intent->data) + OnOpenWebApp(intent->data->spec()); + return; + } + + if (!start_url.is_valid()) + return; + + RecordOpenType(ArcIntentHelperOpenType::WEB_APP); + + // Web app launches should only be invoked on HTTPS URLs. + if (start_url.SchemeIs(url::kHttpsScheme)) + g_open_url_delegate->OpenAppWithIntent(start_url, std::move(intent)); +} + ArcIntentHelperBridge::GetResult ArcIntentHelperBridge::GetActivityIcons( const std::vector<ActivityName>& activities, OnIconsReadyCallback callback) {
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge.h b/components/arc/intent_helper/arc_intent_helper_bridge.h index b6640dba..173e3fe 100644 --- a/components/arc/intent_helper/arc_intent_helper_bridge.h +++ b/components/arc/intent_helper/arc_intent_helper_bridge.h
@@ -110,6 +110,8 @@ std::vector<IntentFilter> deleted) override; void OnDownloadAdded(const std::string& relative_path, const std::string& owner_package_name) override; + void OnOpenAppWithIntent(const GURL& start_url, + arc::mojom::LaunchIntentPtr intent) override; // Retrieves icons for the |activities| and calls |callback|. // See ActivityIconLoader::GetActivityIcons() for more details.
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc b/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc index 2a3f6f5..903f74b3 100644 --- a/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc +++ b/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc
@@ -10,6 +10,9 @@ #include <vector> #include "base/memory/ptr_util.h" +#include "base/test/scoped_feature_list.h" +#include "components/arc/arc_features.h" +#include "components/arc/intent_helper/intent_constants.h" #include "components/arc/intent_helper/open_url_delegate.h" #include "components/arc/mojom/intent_helper.mojom.h" #include "components/arc/session/arc_bridge_service.h" @@ -56,6 +59,11 @@ std::move(callback).Run(mojo::NullRemote()); } void OpenChromePageFromArc(mojom::ChromePage chrome_page) override {} + void OpenAppWithIntent(const GURL& url, + mojom::LaunchIntentPtr intent) override { + last_opened_url_ = url; + last_opened_intent_ = std::move(intent); + } GURL TakeLastOpenedUrl() { GURL result = std::move(last_opened_url_); @@ -63,8 +71,15 @@ return result; } + mojom::LaunchIntentPtr TakeLastOpenedIntent() { + auto result = std::move(last_opened_intent_); + last_opened_intent_.reset(); + return result; + } + private: GURL last_opened_url_; + mojom::LaunchIntentPtr last_opened_intent_; }; std::unique_ptr<ArcBridgeService> arc_bridge_service_; @@ -394,6 +409,55 @@ EXPECT_FALSE(test_open_url_delegate_->TakeLastOpenedUrl().is_valid()); } +// Tests that OnOpenAppWithIntents opens only HTTPS URLs. +TEST_F(ArcIntentHelperTest, TestOnOpenAppWithIntent) { + { + // When the feature is enabled, open the Intent through the delegate. + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(arc::kEnableWebAppShareFeature); + + auto intent = mojom::LaunchIntent::New(); + intent->action = arc::kIntentActionSend; + intent->extra_text = "Foo"; + instance_->OnOpenAppWithIntent(GURL("https://www.google.com"), + std::move(intent)); + EXPECT_EQ(GURL("https://www.google.com"), + test_open_url_delegate_->TakeLastOpenedUrl()); + EXPECT_EQ("Foo", + test_open_url_delegate_->TakeLastOpenedIntent()->extra_text); + + instance_->OnOpenAppWithIntent(GURL("http://www.google.com"), + mojom::LaunchIntent::New()); + EXPECT_FALSE(test_open_url_delegate_->TakeLastOpenedUrl().is_valid()); + EXPECT_TRUE(test_open_url_delegate_->TakeLastOpenedIntent().is_null()); + + instance_->OnOpenAppWithIntent(GURL("chrome://settings"), + mojom::LaunchIntent::New()); + EXPECT_FALSE(test_open_url_delegate_->TakeLastOpenedUrl().is_valid()); + EXPECT_TRUE(test_open_url_delegate_->TakeLastOpenedIntent().is_null()); + } + { + // When the feature is disabled, open the Intent's URL through the delegate. + base::test::ScopedFeatureList feature_list; + feature_list.InitAndDisableFeature(arc::kEnableWebAppShareFeature); + + auto intent = mojom::LaunchIntent::New(); + intent->data = GURL("https://www.google.com/maps"); + instance_->OnOpenAppWithIntent(GURL("https://www.google.com"), + std::move(intent)); + EXPECT_EQ(GURL("https://www.google.com/maps"), + test_open_url_delegate_->TakeLastOpenedUrl()); + EXPECT_TRUE(test_open_url_delegate_->TakeLastOpenedIntent().is_null()); + + intent = mojom::LaunchIntent::New(); + intent->data = GURL("chrome://settings"); + instance_->OnOpenAppWithIntent(GURL("https://www.google.com"), + std::move(intent)); + EXPECT_FALSE(test_open_url_delegate_->TakeLastOpenedUrl().is_valid()); + EXPECT_TRUE(test_open_url_delegate_->TakeLastOpenedIntent().is_null()); + } +} + // Tests that AppendStringToIntentHelperPackageName works. TEST_F(ArcIntentHelperTest, TestAppendStringToIntentHelperPackageName) { std::string package_name = ArcIntentHelperBridge::kArcIntentHelperPackageName;
diff --git a/components/arc/intent_helper/open_url_delegate.h b/components/arc/intent_helper/open_url_delegate.h index 3d490bd..42831a44 100644 --- a/components/arc/intent_helper/open_url_delegate.h +++ b/components/arc/intent_helper/open_url_delegate.h
@@ -31,6 +31,13 @@ // Opens the requested |chrome_page|. If |chrome_page| is a settings page, // this will open the settings window. virtual void OpenChromePageFromArc(mojom::ChromePage chrome_page) = 0; + + // Opens the installed web app for the given |start_url|, delivering the + // |intent| to that app. If no app is installed, falls back to opening the + // |intent->data| as a tab in the browser, unless |intent->data| is not set, + // in which case nothing happens. + virtual void OpenAppWithIntent(const GURL& start_url, + arc::mojom::LaunchIntentPtr intent) = 0; }; } // namespace arc
diff --git a/components/arc/mojom/intent_helper.mojom b/components/arc/mojom/intent_helper.mojom index b02394a..521fd5b 100644 --- a/components/arc/mojom/intent_helper.mojom +++ b/components/arc/mojom/intent_helper.mojom
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -// Next MinVersion: 45 +// Next MinVersion: 46 module arc.mojom; @@ -11,6 +11,7 @@ import "components/arc/mojom/camera_intent.mojom"; import "components/arc/mojom/intent_common.mojom"; import "components/arc/mojom/scale_factor.mojom"; +import "url/mojom/url.mojom"; [Extensible] enum PatternType { @@ -235,6 +236,24 @@ [MinVersion=23] ArcBitmap? bitmap_icon; }; +// Contains the information needed to launch an app along with any data which +// was shared to it. +struct LaunchIntent { + string action; + + // URI for the data to be handled by this intent. + url.mojom.Url? data; + + // MIME type of the intent data. + string? type; + + // The contents of Intent.EXTRA_SUBJECT. + string? extra_subject; + + // The contents of Intent.EXTRA_TEXT. + string? extra_text; +}; + // Interface to interact with a custom tab. // Close the interface pointer to close the custom tab. // Next method ID: 1 @@ -245,7 +264,7 @@ // Handles intents from ARC in Chrome. // Deprecated method ID: 4, 10 -// Next method ID: 19 +// Next method ID: 20 interface IntentHelperHost { // Called when icons associated with the package are no longer up to date. [MinVersion=3] OnIconInvalidated@1(string package_name); @@ -340,6 +359,15 @@ // "com.bar.foo"). [MinVersion=44] OnDownloadAdded@18( string relative_path, string owner_package_name); + + // Opens the web app for a specified URL, if it is installed, and delivers + // |intent| to it. If the URL is not installed, will open |intent.data| in a + // browser tab, unless |intent.data| is not set, in which case nothing + // happens. + // Note that the |start_url| is used for resolving the app to open, but the + // actual URL which is loaded is based on the contents of |intent|. + [MinVersion=45] OnOpenAppWithIntent@19( + url.mojom.Url start_url, LaunchIntent intent); }; // Sends intents to ARC on behalf of Chrome.
diff --git a/components/browsing_data/content/cache_storage_helper.cc b/components/browsing_data/content/cache_storage_helper.cc index d3f0ba4..60a93702 100644 --- a/components/browsing_data/content/cache_storage_helper.cc +++ b/components/browsing_data/content/cache_storage_helper.cc
@@ -24,7 +24,7 @@ namespace browsing_data { namespace { -void GetAllOriginsInfoForCacheStorageCallback( +void GetAllStorageKeysInfoForCacheStorageCallback( CacheStorageHelper::FetchCallback callback, std::vector<storage::mojom::StorageUsageInfoPtr> usage_info) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -53,13 +53,17 @@ void CacheStorageHelper::StartFetching(FetchCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!callback.is_null()); - partition_->GetCacheStorageControl()->GetAllOriginsInfo(base::BindOnce( - &GetAllOriginsInfoForCacheStorageCallback, std::move(callback))); + partition_->GetCacheStorageControl()->GetAllStorageKeysInfo(base::BindOnce( + &GetAllStorageKeysInfoForCacheStorageCallback, std::move(callback))); } void CacheStorageHelper::DeleteCacheStorage(const url::Origin& origin) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - partition_->GetCacheStorageControl()->DeleteForOrigin(origin); + + // TODO(https://crbug.com/1199077): Pass the real StorageKey into this + // function directly. + partition_->GetCacheStorageControl()->DeleteForStorageKey( + blink::StorageKey(origin)); } CannedCacheStorageHelper::CannedCacheStorageHelper(
diff --git a/components/page_info_strings.grdp b/components/page_info_strings.grdp index bd91ad9..a081335 100644 --- a/components/page_info_strings.grdp +++ b/components/page_info_strings.grdp
@@ -64,6 +64,17 @@ Attackers sometimes mimic sites by making hard-to-see changes to the web address. </message> + <!-- Accuracy Tip strings --> + <message name="IDS_PAGE_INFO_ACCURACY_TIP_TITLE" desc="Message to display in the page info bubble when the page you are on triggered an accuracy tip."> + Is this website accurate? + </message> + <message name="IDS_PAGE_INFO_ACCURACY_TIP_LEARN_MORE_BUTTON" desc="Text of button to learn more about inaccurate pages. Shown on the accuracy tip page info bubble."> + Learn more + </message> + <message name="IDS_PAGE_INFO_ACCURACY_TIP_IGNORE_BUTTON" desc="Text of button to close the accuracy tip page info bubble."> + Ignore + </message> + <!-- Viewing file strings --> <message name="IDS_PAGE_INFO_FILE_PAGE" desc="Message to display in the page info bubble when the page the user has navigated to is a file:// page"> You're viewing a local or shared file
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_ACCURACY_TIP_IGNORE_BUTTON.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_ACCURACY_TIP_IGNORE_BUTTON.png.sha1 new file mode 100644 index 0000000..bfb8b6c --- /dev/null +++ b/components/page_info_strings_grdp/IDS_PAGE_INFO_ACCURACY_TIP_IGNORE_BUTTON.png.sha1
@@ -0,0 +1 @@ +fc83a4960d9fd91e5b8a5a02b8253709950cc347 \ No newline at end of file
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_ACCURACY_TIP_LEARN_MORE_BUTTON.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_ACCURACY_TIP_LEARN_MORE_BUTTON.png.sha1 new file mode 100644 index 0000000..bfb8b6c --- /dev/null +++ b/components/page_info_strings_grdp/IDS_PAGE_INFO_ACCURACY_TIP_LEARN_MORE_BUTTON.png.sha1
@@ -0,0 +1 @@ +fc83a4960d9fd91e5b8a5a02b8253709950cc347 \ No newline at end of file
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_ACCURACY_TIP_TITLE.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_ACCURACY_TIP_TITLE.png.sha1 new file mode 100644 index 0000000..bfb8b6c --- /dev/null +++ b/components/page_info_strings_grdp/IDS_PAGE_INFO_ACCURACY_TIP_TITLE.png.sha1
@@ -0,0 +1 @@ +fc83a4960d9fd91e5b8a5a02b8253709950cc347 \ No newline at end of file
diff --git a/components/permissions/BUILD.gn b/components/permissions/BUILD.gn index 3545bd1..b61a6dc 100644 --- a/components/permissions/BUILD.gn +++ b/components/permissions/BUILD.gn
@@ -39,6 +39,8 @@ "contexts/payment_handler_permission_context.h", "contexts/sensor_permission_context.cc", "contexts/sensor_permission_context.h", + "contexts/wake_lock_permission_context.cc", + "contexts/wake_lock_permission_context.h", "contexts/webxr_permission_context.cc", "contexts/webxr_permission_context.h", "object_permission_context_base.cc", @@ -190,6 +192,7 @@ "contexts/midi_permission_context_unittest.cc", "contexts/midi_sysex_permission_context_unittest.cc", "contexts/nfc_permission_context_unittest.cc", + "contexts/wake_lock_permission_context_unittest.cc", "object_permission_context_base_unittest.cc", "permission_auditing_database_unittest.cc", "permission_auditing_service_unittest.cc",
diff --git a/chrome/browser/wake_lock/wake_lock_permission_context.cc b/components/permissions/contexts/wake_lock_permission_context.cc similarity index 85% rename from chrome/browser/wake_lock/wake_lock_permission_context.cc rename to components/permissions/contexts/wake_lock_permission_context.cc index 5e0b284..680a36c 100644 --- a/chrome/browser/wake_lock/wake_lock_permission_context.cc +++ b/components/permissions/contexts/wake_lock_permission_context.cc
@@ -2,12 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/wake_lock/wake_lock_permission_context.h" +#include "components/permissions/contexts/wake_lock_permission_context.h" #include "base/check.h" #include "base/notreached.h" #include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom.h" +namespace permissions { + WakeLockPermissionContext::WakeLockPermissionContext( content::BrowserContext* browser_context, ContentSettingsType content_settings_type) @@ -16,19 +18,18 @@ content_settings_type, content_settings_type == ContentSettingsType::WAKE_LOCK_SCREEN ? blink::mojom::PermissionsPolicyFeature::kScreenWakeLock - : blink::mojom::PermissionsPolicyFeature::kNotFound), - content_settings_type_(content_settings_type) { + : blink::mojom::PermissionsPolicyFeature::kNotFound) { DCHECK(content_settings_type == ContentSettingsType::WAKE_LOCK_SCREEN || content_settings_type == ContentSettingsType::WAKE_LOCK_SYSTEM); } -WakeLockPermissionContext::~WakeLockPermissionContext() {} +WakeLockPermissionContext::~WakeLockPermissionContext() = default; ContentSetting WakeLockPermissionContext::GetPermissionStatusInternal( content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, const GURL& embedding_origin) const { - switch (content_settings_type_) { + switch (content_settings_type()) { case ContentSettingsType::WAKE_LOCK_SCREEN: return CONTENT_SETTING_ALLOW; case ContentSettingsType::WAKE_LOCK_SYSTEM: @@ -42,3 +43,5 @@ bool WakeLockPermissionContext::IsRestrictedToSecureOrigins() const { return true; } + +} // namespace permissions
diff --git a/chrome/browser/wake_lock/wake_lock_permission_context.h b/components/permissions/contexts/wake_lock_permission_context.h similarity index 61% rename from chrome/browser/wake_lock/wake_lock_permission_context.h rename to components/permissions/contexts/wake_lock_permission_context.h index d6138d6..15996b9 100644 --- a/chrome/browser/wake_lock/wake_lock_permission_context.h +++ b/components/permissions/contexts/wake_lock_permission_context.h
@@ -2,18 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_WAKE_LOCK_WAKE_LOCK_PERMISSION_CONTEXT_H_ -#define CHROME_BROWSER_WAKE_LOCK_WAKE_LOCK_PERMISSION_CONTEXT_H_ +#ifndef COMPONENTS_PERMISSIONS_CONTEXTS_WAKE_LOCK_PERMISSION_CONTEXT_H_ +#define COMPONENTS_PERMISSIONS_CONTEXTS_WAKE_LOCK_PERMISSION_CONTEXT_H_ -#include "base/macros.h" #include "components/content_settings/core/common/content_settings_types.h" #include "components/permissions/permission_context_base.h" -class WakeLockPermissionContext : public permissions::PermissionContextBase { +namespace permissions { + +class WakeLockPermissionContext : public PermissionContextBase { public: WakeLockPermissionContext(content::BrowserContext* browser_context, ContentSettingsType content_settings_type); - + WakeLockPermissionContext(const WakeLockPermissionContext&) = delete; + WakeLockPermissionContext& operator=(const WakeLockPermissionContext&) = + delete; ~WakeLockPermissionContext() override; private: @@ -23,10 +26,8 @@ const GURL& requesting_origin, const GURL& embedding_origin) const override; bool IsRestrictedToSecureOrigins() const override; - - ContentSettingsType content_settings_type_; - - DISALLOW_COPY_AND_ASSIGN(WakeLockPermissionContext); }; -#endif // CHROME_BROWSER_WAKE_LOCK_WAKE_LOCK_PERMISSION_CONTEXT_H_ +} // namespace permissions + +#endif // COMPONENTS_PERMISSIONS_CONTEXTS_WAKE_LOCK_PERMISSION_CONTEXT_H_
diff --git a/chrome/browser/wake_lock/wake_lock_permission_context_unittest.cc b/components/permissions/contexts/wake_lock_permission_context_unittest.cc similarity index 77% rename from chrome/browser/wake_lock/wake_lock_permission_context_unittest.cc rename to components/permissions/contexts/wake_lock_permission_context_unittest.cc index c430e1eb..acc1b55 100644 --- a/chrome/browser/wake_lock/wake_lock_permission_context_unittest.cc +++ b/components/permissions/contexts/wake_lock_permission_context_unittest.cc
@@ -2,22 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/wake_lock/wake_lock_permission_context.h" +#include "components/permissions/contexts/wake_lock_permission_context.h" -#include "chrome/test/base/testing_profile.h" #include "components/content_settings/core/common/content_settings.h" +#include "components/permissions/test/test_permissions_client.h" #include "content/public/test/browser_task_environment.h" +#include "content/public/test/test_browser_context.h" #include "testing/gtest/include/gtest/gtest.h" -namespace { +namespace permissions { class WakeLockPermissionContextTests : public testing::Test { public: - TestingProfile* profile() { return &profile_; } + content::TestBrowserContext* browser_context() { return &browser_context_; } private: content::BrowserTaskEnvironment task_environment_; - TestingProfile profile_; + content::TestBrowserContext browser_context_; + TestPermissionsClient client_; }; TEST_F(WakeLockPermissionContextTests, InsecureOriginsAreRejected) { @@ -29,7 +31,7 @@ ContentSettingsType::WAKE_LOCK_SYSTEM}; for (const auto& content_settings_type : kWakeLockTypes) { - WakeLockPermissionContext permission_context(profile(), + WakeLockPermissionContext permission_context(browser_context(), content_settings_type); EXPECT_EQ(CONTENT_SETTING_BLOCK, permission_context @@ -46,7 +48,7 @@ TEST_F(WakeLockPermissionContextTests, TestScreenLockPermissionRequest) { WakeLockPermissionContext permission_context( - profile(), ContentSettingsType::WAKE_LOCK_SCREEN); + browser_context(), ContentSettingsType::WAKE_LOCK_SCREEN); GURL url("https://www.example.com"); EXPECT_EQ(CONTENT_SETTING_ALLOW, permission_context @@ -56,7 +58,7 @@ TEST_F(WakeLockPermissionContextTests, TestSystemLockPermissionRequest) { WakeLockPermissionContext permission_context( - profile(), ContentSettingsType::WAKE_LOCK_SYSTEM); + browser_context(), ContentSettingsType::WAKE_LOCK_SYSTEM); GURL url("https://www.example.com"); EXPECT_EQ(CONTENT_SETTING_BLOCK, permission_context @@ -64,4 +66,4 @@ .content_setting); } -} // namespace +} // namespace permissions
diff --git a/components/policy/BUILD.gn b/components/policy/BUILD.gn index 7c3eba8e..dfbf9af0 100644 --- a/components/policy/BUILD.gn +++ b/components/policy/BUILD.gn
@@ -6,7 +6,6 @@ import("//build/config/chrome_build.gni") import("//build/config/chromeos/ui_mode.gni") import("//build/config/features.gni") -import("//build/config/python.gni") import("//build/timestamp.gni") import("//build/toolchain/toolchain.gni") import("//components/policy/resources/policy_templates.gni") @@ -191,8 +190,7 @@ } # Generate the various templates and docs (admx, doc, json, etc.) -# TODO(crbug.com/1112471): Get this to run cleanly under Python 3. -python2_action("policy_templates") { +action("policy_templates") { script = "tools/template_writers/template_formatter.py" chrome_version_abspath = "//chrome/VERSION" chrome_version_path = rebase_path(chrome_version_abspath, root_build_dir)
diff --git a/components/policy/tools/template_writers/template_formatter.py b/components/policy/tools/template_writers/template_formatter.py index f997883a..7f22da6 100755 --- a/components/policy/tools/template_writers/template_formatter.py +++ b/components/policy/tools/template_writers/template_formatter.py
@@ -203,7 +203,7 @@ _LANG_PLACEHOLDER = "${lang}" assert _LANG_PLACEHOLDER in args.translations - languages = filter(bool, args.languages.split(',')) + languages = list(filter(bool, args.languages.split(','))) assert _DEFAULT_LANGUAGE in languages config = _GetWriterConfiguration(args.grit_defines)
diff --git a/components/policy/tools/template_writers/writers/adm_writer_unittest.py b/components/policy/tools/template_writers/writers/adm_writer_unittest.py index 38389e0d..77992865 100755 --- a/components/policy/tools/template_writers/writers/adm_writer_unittest.py +++ b/components/policy/tools/template_writers/writers/adm_writer_unittest.py
@@ -1388,6 +1388,7 @@ ''') self.CompareOutputs(output, expected_output) + @unittest.skip("skipped due to https://crbug.com/1216996") def testRemovedPolicy(self): # Tests that a deprecated policy gets placed in the special # 'RemovedPolicies' group.
diff --git a/components/policy/tools/template_writers/writers/admx_writer.py b/components/policy/tools/template_writers/writers/admx_writer.py index 8da5024..865558e 100755 --- a/components/policy/tools/template_writers/writers/admx_writer.py +++ b/components/policy/tools/template_writers/writers/admx_writer.py
@@ -176,8 +176,9 @@ display_name: Display name of the category. parent_category_name: Name of the parent category. Defaults to None. ''' - existing = filter(lambda e: e.getAttribute('name') == name, - parent.getElementsByTagName('category')) + existing = list( + filter(lambda e: e.getAttribute('name') == name, + parent.getElementsByTagName('category'))) if existing: assert len(existing) == 1 assert existing[0].getAttribute('name') == name
diff --git a/components/policy/tools/template_writers/writers/android_policy_writer.py b/components/policy/tools/template_writers/writers/android_policy_writer.py index 593e04c..9466d8c 100755 --- a/components/policy/tools/template_writers/writers/android_policy_writer.py +++ b/components/policy/tools/template_writers/writers/android_policy_writer.py
@@ -22,7 +22,15 @@ ''' if resource == None or type(resource) in (int, bool): return str(resource) - return xml_escape.escape(resource, {"'": "\\'", '"': '\\"', '\\': '\\\\'}) + return xml_escape.escape( + resource, + { + # Written order is matter to prevent "'" becomes "\\\\'" instead of + # "\\'". + "\\": "\\\\", + "'": "\\'", + '"': '\\"', + }) class AndroidPolicyWriter(xml_formatted_writer.XMLFormattedWriter):
diff --git a/components/policy/tools/template_writers/writers/gpo_editor_writer.py b/components/policy/tools/template_writers/writers/gpo_editor_writer.py index b662eb0..c6bdbb68 100755 --- a/components/policy/tools/template_writers/writers/gpo_editor_writer.py +++ b/components/policy/tools/template_writers/writers/gpo_editor_writer.py
@@ -31,7 +31,7 @@ since_version = supported_on.get('since_version', None) - return not since_version or since_version >= major_version + return not since_version or int(since_version) >= major_version def IsPolicyOnWin7Only(self, policy): ''' Returns true if the policy is supported on win7 only.'''
diff --git a/components/services/storage/public/mojom/cache_storage_control.mojom b/components/services/storage/public/mojom/cache_storage_control.mojom index 74acfde1..57484f73 100644 --- a/components/services/storage/public/mojom/cache_storage_control.mojom +++ b/components/services/storage/public/mojom/cache_storage_control.mojom
@@ -9,6 +9,7 @@ import "mojo/public/mojom/base/time.mojom"; import "services/network/public/mojom/cross_origin_embedder_policy.mojom"; import "third_party/blink/public/mojom/cache_storage/cache_storage.mojom"; +import "third_party/blink/public/mojom/storage_key/storage_key.mojom"; import "url/mojom/origin.mojom"; enum CacheStorageOwner { @@ -36,21 +37,21 @@ // out-of-process. interface CacheStorageControl { // Binds a CacheStorage receiver to the CacheStorageControl to access - // the cache storage for a particular origin and owner. + // the cache storage for a particular storage key and owner. AddReceiver( network.mojom.CrossOriginEmbedderPolicy policy, pending_remote<network.mojom.CrossOriginEmbedderPolicyReporter>? coep_reporter, - url.mojom.Origin origin, + blink.mojom.StorageKey storage_key, CacheStorageOwner owner, pending_receiver<blink.mojom.CacheStorage> receiver); - // Deletes the data for a given origin. - DeleteForOrigin(url.mojom.Origin origin); + // Deletes the data for a given storage key. + DeleteForStorageKey(blink.mojom.StorageKey storage_key); // Used in response to browsing data and quota manager requests to get - // the per-origin size and last time used data. - GetAllOriginsInfo() => (array<StorageUsageInfo> usage_info); + // the per-StorageKey size and last time used data. + GetAllStorageKeysInfo() => (array<StorageUsageInfo> usage_info); // Adds an observer that will receive cache change callbacks. AddObserver(pending_remote<CacheStorageObserver> observer);
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc index 0de4283..626fc037 100644 --- a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc +++ b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc
@@ -237,33 +237,6 @@ return accounts_; } -std::vector<std::string> -ProfileOAuth2TokenServiceDelegateAndroid::GetSystemAccountNames() { - std::vector<std::string> account_names; - JNIEnv* env = AttachCurrentThread(); - - // TODO(crbug.com/1028580) Pass |j_accounts| as a list of - // org.chromium.components.signin.identitymanager.CoreAccountId and convert it - // to CoreAccountId using ConvertFromJavaCoreAccountId. - ScopedJavaLocalRef<jobjectArray> j_accounts = - signin::Java_ProfileOAuth2TokenServiceDelegate_getSystemAccountNames( - env, java_ref_); - base::android::AppendJavaStringArrayToStringVector(env, j_accounts, - &account_names); - return account_names; -} - -std::vector<CoreAccountId> -ProfileOAuth2TokenServiceDelegateAndroid::GetSystemAccounts() { - std::vector<CoreAccountId> ids; - for (const std::string& name : GetSystemAccountNames()) { - CoreAccountId id(MapAccountNameToAccountId(name)); - if (!id.empty()) - ids.push_back(std::move(id)); - } - return ids; -} - std::vector<CoreAccountId> ProfileOAuth2TokenServiceDelegateAndroid::GetValidAccounts() { std::vector<CoreAccountId> ids; @@ -325,13 +298,24 @@ void ProfileOAuth2TokenServiceDelegateAndroid:: ReloadAllAccountsWithPrimaryAccountAfterSeeding( JNIEnv* env, - const base::android::JavaParamRef<jstring>& account_id) { - absl::optional<CoreAccountId> core_account_id; - if (account_id) { - core_account_id = - CoreAccountId::FromString(ConvertJavaStringToUTF8(env, account_id)); + const base::android::JavaParamRef<jstring>& j_primary_account_id, + const base::android::JavaParamRef<jobjectArray>& + j_device_account_names) { + absl::optional<CoreAccountId> primary_account_id; + if (j_primary_account_id) { + primary_account_id = CoreAccountId::FromString( + ConvertJavaStringToUTF8(env, j_primary_account_id)); } - UpdateAccountList(core_account_id, GetValidAccounts(), GetSystemAccounts()); + std::vector<std::string> device_account_names; + base::android::AppendJavaStringArrayToStringVector( + env, j_device_account_names, &device_account_names); + std::vector<CoreAccountId> account_ids; + for (const std::string& name : device_account_names) { + CoreAccountId id(MapAccountNameToAccountId(name)); + if (!id.empty()) + account_ids.push_back(std::move(id)); + } + UpdateAccountList(primary_account_id, GetValidAccounts(), account_ids); } void ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList(
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h index 46ed5cd..d01f161 100644 --- a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h +++ b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h
@@ -58,7 +58,8 @@ // internalized, use CoreAccountId instead of String. void ReloadAllAccountsWithPrimaryAccountAfterSeeding( JNIEnv* env, - const base::android::JavaParamRef<jstring>& account_id); + const base::android::JavaParamRef<jstring>& j_primary_account_id, + const base::android::JavaParamRef<jobjectArray>& j_device_account_names); // Takes a the signed in sync account as well as all the other // android account ids and check the token status of each. @@ -105,11 +106,6 @@ const std::vector<CoreAccountId>& curr_ids, std::vector<CoreAccountId>* refreshed_ids, std::vector<CoreAccountId>* revoked_ids); - - // Lists account names at the OS level. - std::vector<std::string> GetSystemAccountNames(); - // As |GetSystemAccountNames| but returning account IDs. - std::vector<CoreAccountId> GetSystemAccounts(); // As |GetAccounts| but with only validated account IDs. std::vector<CoreAccountId> GetValidAccounts(); // Set accounts that have been advertised by OnRefreshTokenAvailable.
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegate.java b/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegate.java index 9a2ed5c9..1098ffa4 100644 --- a/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegate.java +++ b/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegate.java
@@ -72,18 +72,6 @@ } /** - * Called by the native method - * ProfileOAuth2TokenServiceDelegate::GetSystemAccountNames() - * to list the active account names on device. - */ - @CalledByNative - @VisibleForTesting - String[] getSystemAccountNames() { - return AccountUtils.toAccountNames(mAccountManagerFacade.tryGetGoogleAccounts()) - .toArray(new String[0]); - } - - /** * Called by native method AndroidAccessTokenFetcher::Start() to retrieve OAuth2 tokens. * @param accountEmail The account email. * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix). @@ -95,28 +83,30 @@ private void getAccessTokenFromNative( String accountEmail, String scope, final long nativeCallback) { assert accountEmail != null : "Account email cannot be null!"; - final Account account = AccountUtils.findAccountByName( - mAccountManagerFacade.tryGetGoogleAccounts(), accountEmail); - if (account == null) { - ThreadUtils.postOnUiThread(() -> { - ProfileOAuth2TokenServiceDelegateJni.get().onOAuth2TokenFetched( - null, AccessTokenData.NO_KNOWN_EXPIRATION_TIME, false, nativeCallback); - }); - return; - } - String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope; - getAccessToken(account, oauth2Scope, new GetAccessTokenCallback() { - @Override - public void onGetTokenSuccess(AccessTokenData token) { - ProfileOAuth2TokenServiceDelegateJni.get().onOAuth2TokenFetched( - token.getToken(), token.getExpirationTimeSecs(), false, nativeCallback); + mAccountManagerFacade.tryGetGoogleAccounts(accounts -> { + final Account account = AccountUtils.findAccountByName(accounts, accountEmail); + if (account == null) { + ThreadUtils.postOnUiThread(() -> { + ProfileOAuth2TokenServiceDelegateJni.get().onOAuth2TokenFetched( + null, AccessTokenData.NO_KNOWN_EXPIRATION_TIME, false, nativeCallback); + }); + return; } + String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope; + getAccessToken(account, oauth2Scope, new GetAccessTokenCallback() { + @Override + public void onGetTokenSuccess(AccessTokenData token) { + ProfileOAuth2TokenServiceDelegateJni.get().onOAuth2TokenFetched( + token.getToken(), token.getExpirationTimeSecs(), false, nativeCallback); + } - @Override - public void onGetTokenFailure(boolean isTransientError) { - ProfileOAuth2TokenServiceDelegateJni.get().onOAuth2TokenFetched(null, - AccessTokenData.NO_KNOWN_EXPIRATION_TIME, isTransientError, nativeCallback); - } + @Override + public void onGetTokenFailure(boolean isTransientError) { + ProfileOAuth2TokenServiceDelegateJni.get().onOAuth2TokenFetched(null, + AccessTokenData.NO_KNOWN_EXPIRATION_TIME, isTransientError, + nativeCallback); + } + }); }); } @@ -170,12 +160,15 @@ @VisibleForTesting @CalledByNative - void seedAndReloadAccountsWithPrimaryAccount(@Nullable String accountId) { + void seedAndReloadAccountsWithPrimaryAccount(@Nullable String primaryAccountId) { ThreadUtils.assertOnUiThread(); - mAccountTrackerService.seedAccountsIfNeeded(() -> { - ProfileOAuth2TokenServiceDelegateJni.get() - .reloadAllAccountsWithPrimaryAccountAfterSeeding( - mNativeProfileOAuth2TokenServiceDelegate, accountId); + mAccountManagerFacade.tryGetGoogleAccounts(accounts -> { + mAccountTrackerService.seedAccountsIfNeeded(() -> { + ProfileOAuth2TokenServiceDelegateJni.get() + .reloadAllAccountsWithPrimaryAccountAfterSeeding( + mNativeProfileOAuth2TokenServiceDelegate, primaryAccountId, + AccountUtils.toAccountNames(accounts).toArray(new String[0])); + }); }); } @@ -194,6 +187,7 @@ void onOAuth2TokenFetched(String authToken, long expirationTimeSecs, boolean isTransientError, long nativeCallback); void reloadAllAccountsWithPrimaryAccountAfterSeeding( - long nativeProfileOAuth2TokenServiceDelegateAndroid, @Nullable String accountId); + long nativeProfileOAuth2TokenServiceDelegateAndroid, @Nullable String accountId, + String[] deviceAccountNames); } }
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegateTest.java b/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegateTest.java index 6f46890..a6eab011 100644 --- a/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegateTest.java +++ b/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegateTest.java
@@ -37,8 +37,6 @@ import org.chromium.components.signin.AccountUtils; import org.chromium.components.signin.test.util.FakeAccountManagerFacade; -import java.util.Arrays; -import java.util.List; import java.util.concurrent.CountDownLatch; /** Tests for {@link ProfileOAuth2TokenServiceDelegate}. */ @@ -113,32 +111,6 @@ @Test @SmallTest - public void testGetAccountsNoAccountsRegistered() { - Assert.assertArrayEquals(new String[] {}, mDelegate.getSystemAccountNames()); - } - - @Test - @SmallTest - public void testGetAccountsOneAccountRegistered() { - mAccountManagerFacade.addAccount(ACCOUNT); - Assert.assertArrayEquals(new String[] {ACCOUNT.name}, mDelegate.getSystemAccountNames()); - } - - @Test - @SmallTest - public void testGetAccountsTwoAccountsRegistered() { - mAccountManagerFacade.addAccount(ACCOUNT); - final Account account2 = AccountUtils.createAccountFromName("bar@gmail.com"); - mAccountManagerFacade.addAccount(account2); - - final List<String> accounts = Arrays.asList(mDelegate.getSystemAccountNames()); - Assert.assertEquals("There should be two registered account", 2, accounts.size()); - Assert.assertTrue("The list should contain " + ACCOUNT, accounts.contains(ACCOUNT.name)); - Assert.assertTrue("The list should contain " + account2, accounts.contains(account2.name)); - } - - @Test - @SmallTest public void testGetOAuth2AccessTokenOnSuccess() { final String scope = "oauth2:http://example.com/scope"; mAccountManagerFacade.addAccount(ACCOUNT); @@ -186,6 +158,7 @@ @Test @SmallTest public void testSeedAndReloadAccountsWhenAccountsAreSeeded() { + mAccountManagerFacade.addAccount(ACCOUNT); doAnswer(invocation -> { Runnable runnable = invocation.getArgument(0); runnable.run(); @@ -195,6 +168,8 @@ .seedAccountsIfNeeded(any(Runnable.class)); ThreadUtils.runOnUiThreadBlocking( () -> { mDelegate.seedAndReloadAccountsWithPrimaryAccount(null); }); - verify(mNativeMock).reloadAllAccountsWithPrimaryAccountAfterSeeding(NATIVE_DELEGATE, null); + verify(mNativeMock) + .reloadAllAccountsWithPrimaryAccountAfterSeeding( + NATIVE_DELEGATE, null, new String[] {ACCOUNT.name}); } }
diff --git a/components/signin/public/identity_manager/access_token_constants.cc b/components/signin/public/identity_manager/access_token_constants.cc index 381209fc..2f32661f 100644 --- a/components/signin/public/identity_manager/access_token_constants.cc +++ b/components/signin/public/identity_manager/access_token_constants.cc
@@ -53,15 +53,9 @@ GaiaConstants::kGCMGroupServerOAuth2Scope, GaiaConstants::kGCMCheckinServerOAuth2Scope, - // Required by Suggestions. - GaiaConstants::kDriveReadOnlyOAuth2Scope, - // Required by Permission Request Creator. GaiaConstants::kClassifyUrlKidPermissionOAuth2Scope, - // Required by Enterprise policy extensions. - GaiaConstants::kChromeWebstoreOAuth2Scope, - // Required by ChromeOS only. #if BUILDFLAG(IS_CHROMEOS_ASH) GaiaConstants::kAccountsReauthOAuth2Scope, @@ -71,6 +65,7 @@ GaiaConstants::kClearCutOAuth2Scope, GaiaConstants::kCloudTranslationOAuth2Scope, GaiaConstants::kDriveOAuth2Scope, + GaiaConstants::kDriveReadOnlyOAuth2Scope, GaiaConstants::kKidFamilyReadonlyOAuth2Scope, GaiaConstants::kKidManagementOAuth2Scope, GaiaConstants::kKidManagementPrivilegedOAuth2Scope,
diff --git a/components/signin/public/identity_manager/access_token_fetcher.cc b/components/signin/public/identity_manager/access_token_fetcher.cc index 0e6161b..99ac8d43 100644 --- a/components/signin/public/identity_manager/access_token_fetcher.cc +++ b/components/signin/public/identity_manager/access_token_fetcher.cc
@@ -113,6 +113,12 @@ AccessTokenFetcher::~AccessTokenFetcher() {} void AccessTokenFetcher::VerifyScopeAccess() { + if (account_id_.empty()) { + // Fetching access tokens for an empty account should fail, but not crash. + // Verifying the OAuth scopes based on the consent level is thus not needed. + return; + } + // The consumer has privileged access to all scopes, return early. if (GetPrivilegedOAuth2Consumers().count(/*oauth_consumer_name=*/id())) { VLOG(1) << id() << " has access rights to scopes: " @@ -169,7 +175,7 @@ DCHECK(!access_token_request_); // Ensure that the client has the appropriate user consent for accessing the - // API scopes in this request. + // OAuth API scopes in this request. VerifyScopeAccess(); // TODO(843510): Consider making the request to ProfileOAuth2TokenService
diff --git a/components/signin/public/identity_manager/access_token_fetcher_unittest.cc b/components/signin/public/identity_manager/access_token_fetcher_unittest.cc index bb85084..4cbd506 100644 --- a/components/signin/public/identity_manager/access_token_fetcher_unittest.cc +++ b/components/signin/public/identity_manager/access_token_fetcher_unittest.cc
@@ -199,6 +199,26 @@ base::OnceClosure on_access_token_request_callback_; }; +TEST_F(AccessTokenFetcherTest, EmptyAccountFailsButDoesNotCrash) { + TestTokenCallback callback; + + base::RunLoop run_loop; + + // This should result in a request for an access token. + auto fetcher = CreateFetcher(CoreAccountId(), callback.Get(), + AccessTokenFetcher::Mode::kImmediate); + + // Fetching access tokens for an empty account id should respond with + // USER_NOT_SIGNED_UP error. + EXPECT_CALL(callback, + Run(GoogleServiceAuthError( + GoogleServiceAuthError::State::USER_NOT_SIGNED_UP), + AccessTokenInfo())) + .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); + + run_loop.Run(); +} + TEST_F(AccessTokenFetcherTest, OneShotShouldCallBackOnFulfilledRequest) { TestTokenCallback callback;
diff --git a/components/strings/components_strings_af.xtb b/components/strings/components_strings_af.xtb index 0ab26e38..99a0e57 100644 --- a/components/strings/components_strings_af.xtb +++ b/components/strings/components_strings_af.xtb
@@ -401,6 +401,7 @@ <translation id="2359629602545592467">Veelvuldige</translation> <translation id="2359808026110333948">Gaan voort</translation> <translation id="2359961752320758691">Jou virtuelekaartnommer is toegepas.</translation> +<translation id="236340516568226369">Verander grootte van Wissel-kieslys</translation> <translation id="2367567093518048410">Vlak</translation> <translation id="2372464001869762664">Nadat jy bevestig het, sal kaartbesonderhede vanaf jou Google-rekening met hierdie werf gedeel word. Kry die CVC in jou Plex-rekeningbesonderhede.</translation> <translation id="2380886658946992094">Wetlik</translation>
diff --git a/components/strings/components_strings_fil.xtb b/components/strings/components_strings_fil.xtb index ba50639..c12b2e7 100644 --- a/components/strings/components_strings_fil.xtb +++ b/components/strings/components_strings_fil.xtb
@@ -401,6 +401,7 @@ <translation id="2359629602545592467">Marami</translation> <translation id="2359808026110333948">Magpatuloy</translation> <translation id="2359961752320758691">Inilapat ang numero ng iyong virtual card.</translation> +<translation id="236340516568226369">Menu ng toggle sa pag-resize</translation> <translation id="2367567093518048410">Antas</translation> <translation id="2372464001869762664">Pagkatapos mong magkumpirma, ibabahagi sa site na ito ang mga detalye ng card mula sa iyong Google Account. Hanapin ang CVC sa mga detalye ng iyong Plex Account.</translation> <translation id="2380886658946992094">Legal</translation>
diff --git a/components/strings/components_strings_iw.xtb b/components/strings/components_strings_iw.xtb index 70394086..a2af869d 100644 --- a/components/strings/components_strings_iw.xtb +++ b/components/strings/components_strings_iw.xtb
@@ -401,6 +401,7 @@ <translation id="2359629602545592467">מטבעות מרובים</translation> <translation id="2359808026110333948">המשך</translation> <translation id="2359961752320758691">מספר הכרטיס הווירטואלי שלך הוזן באופן אוטומטי.</translation> +<translation id="236340516568226369">תפריט מתגים לשינוי גודל</translation> <translation id="2367567093518048410">רמה</translation> <translation id="2372464001869762664">אחרי שנקבל ממך אישור, נשתף עם האתר הזה את פרטי הכרטיס מחשבון Google. ניתן למצוא את ה-CVC בפרטים של חשבון Plex.</translation> <translation id="2380886658946992094">Legal</translation>
diff --git a/components/strings/components_strings_lo.xtb b/components/strings/components_strings_lo.xtb index 64fe0d7..408018b4 100644 --- a/components/strings/components_strings_lo.xtb +++ b/components/strings/components_strings_lo.xtb
@@ -401,6 +401,7 @@ <translation id="2359629602545592467">ຫຼາຍສະກຸນເງິນ</translation> <translation id="2359808026110333948">ສືບຕໍ່</translation> <translation id="2359961752320758691">ນຳໃຊ້ໝາຍເລກບັດສະເໝືອນຂອງທ່ານແລ້ວ.</translation> +<translation id="236340516568226369">ປັບຂະໜາດເມນູສະຫຼັບ</translation> <translation id="2367567093518048410">ລະດັບ</translation> <translation id="2372464001869762664">ຫຼັງຈາກທີ່ທ່ານຢືນຢັນ, ລະບົບຈະແບ່ງປັນລາຍລະອຽດບັດຈາກບັນຊີ Google ຂອງທ່ານກັບເວັບໄຊນີ້. ຊອກຫາ CVC ໃນລາຍລະອຽດບັນຊີ Plex ຂອງທ່ານ.</translation> <translation id="2380886658946992094">Legal</translation>
diff --git a/components/strings/components_strings_mn.xtb b/components/strings/components_strings_mn.xtb index d3f3492..77ccab15 100644 --- a/components/strings/components_strings_mn.xtb +++ b/components/strings/components_strings_mn.xtb
@@ -401,6 +401,7 @@ <translation id="2359629602545592467">Олон</translation> <translation id="2359808026110333948">Цааш</translation> <translation id="2359961752320758691">Таны виртуал картын дугаарыг автоматаар бөглөлөө.</translation> +<translation id="236340516568226369">Хэмжээг өөрчлөхийг асаах/унтраах цэс</translation> <translation id="2367567093518048410">Түвшин</translation> <translation id="2372464001869762664">Таныг баталгаажуулсны дараа таны Google бүртгэлээс картын дэлгэрэнгүй мэдээллийг энэ сайтад хуваалцах болно. Plex бүртгэлийн дэлгэрэнгүй мэдээллээсээ Карт баталгаажуулалтын кодыг олно уу.</translation> <translation id="2380886658946992094">Хууль ёсны</translation>
diff --git a/components/strings/components_strings_si.xtb b/components/strings/components_strings_si.xtb index 92a32b1..f31f4e8a 100644 --- a/components/strings/components_strings_si.xtb +++ b/components/strings/components_strings_si.xtb
@@ -401,6 +401,7 @@ <translation id="2359629602545592467">බහුවිධ</translation> <translation id="2359808026110333948">කරගෙන යන්න</translation> <translation id="2359961752320758691">ඔබගේ අථත්ය කාඩ්පත් අංකය යොදනු ලැබේ.</translation> +<translation id="236340516568226369">ටොගල මෙනුව ප්රතිප්රමාණ කරන්න</translation> <translation id="2367567093518048410">මට්ටම</translation> <translation id="2372464001869762664">ඔබ තහවුරු කළ පසු, ඔබගේ Google ගිණුමෙන් කාඩ්පත් විස්තර මෙම අඩවිය සමඟ බෙදා ගනු ඇත. ඔබගේ Plex ගිණුම් විස්තර තුළ CVC සොයා ගන්න.</translation> <translation id="2380886658946992094">Legal</translation>
diff --git a/components/strings/components_strings_sq.xtb b/components/strings/components_strings_sq.xtb index dba9f1684..38e0bbb 100644 --- a/components/strings/components_strings_sq.xtb +++ b/components/strings/components_strings_sq.xtb
@@ -1720,7 +1720,7 @@ <translation id="7378594059915113390">Kontrollet e klipeve media</translation> <translation id="7378627244592794276">Jo</translation> <translation id="7378810950367401542">/</translation> -<translation id="7386364858855961704">Nuk është e zbatueshme</translation> +<translation id="7386364858855961704">Nuk është i zbatueshëm</translation> <translation id="7390545607259442187">Konfirmo kartën</translation> <translation id="7392089738299859607">Përditëso adresën</translation> <translation id="7399802613464275309">Kontrolli i sigurisë</translation>
diff --git a/components/strings/components_strings_sw.xtb b/components/strings/components_strings_sw.xtb index 9529b87..e92a4a3e 100644 --- a/components/strings/components_strings_sw.xtb +++ b/components/strings/components_strings_sw.xtb
@@ -400,6 +400,7 @@ <translation id="2359629602545592467">Nyingi</translation> <translation id="2359808026110333948">Endelea</translation> <translation id="2359961752320758691">Nambari yako ya kadi pepe imewekwa.</translation> +<translation id="236340516568226369">Menyu ya kugeuza vitufe vya kubadilisha ukubwa</translation> <translation id="2367567093518048410">Kiwango</translation> <translation id="2372464001869762664">Ukishathibitisha, maelezo ya kadi kutoka Akaunti yako ya Google yatashirikiwa na tovuti hii. Pata CVC katika maelezo ya Akaunti yako ya Plex.</translation> <translation id="2380886658946992094">Legal</translation>
diff --git a/components/sync/protocol/sync.proto b/components/sync/protocol/sync.proto index 8ea1fa8..0a965bc 100644 --- a/components/sync/protocol/sync.proto +++ b/components/sync/protocol/sync.proto
@@ -983,10 +983,13 @@ // which clients experience as NOT_MY_BIRTHDAY error, and involves clearing // all local sync metadata including the cached store birthday. // - // This mechanism allows the server to deal reliably with in-flight changes - // from other clients upon ClearServerDataMessage (or equivalent triggers), - // because all writes issued with an outdated birthday (which in-flight writes - // would use) can be detected by the server. + // This mechanism allows the server to implement clear-data/reset + // functionality that reliably identifies and deletes sync entities uploaded + // before the clear-data/reset event (e.g. via ClearServerDataMessage). + // Furthermore, it allows the server to deal reliably with in-flight changes + // from other clients upon clear-data event, because all writes issued with an + // outdated birthday (which in-flight writes would use) can be detected by the + // server. optional string store_birthday = 6; optional ClientCommand client_command = 7;
diff --git a/components/sync/test/fake_server/BUILD.gn b/components/sync/test/fake_server/BUILD.gn index fb4a1e0..e572965 100644 --- a/components/sync/test/fake_server/BUILD.gn +++ b/components/sync/test/fake_server/BUILD.gn
@@ -53,10 +53,7 @@ static_library("fake_server_android") { testonly = true - sources = [ - "android/fake_server_helper_android.cc", - "android/fake_server_helper_android.h", - ] + sources = [ "android/fake_server_helper_android.cc" ] deps = [ ":fake_server", ":fake_server_jni",
diff --git a/components/sync/test/fake_server/android/fake_server_helper_android.cc b/components/sync/test/fake_server/android/fake_server_helper_android.cc index 8875aab..4839b84 100644 --- a/components/sync/test/fake_server/android/fake_server_helper_android.cc +++ b/components/sync/test/fake_server/android/fake_server_helper_android.cc
@@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/sync/test/fake_server/android/fake_server_helper_android.h" - #include <stddef.h> #include <set> @@ -21,6 +19,7 @@ #include "components/sync/nigori/nigori_test_utils.h" #include "components/sync/protocol/sync.pb.h" #include "components/sync/test/fake_server/bookmark_entity_builder.h" +#include "components/sync/test/fake_server/entity_builder_factory.h" #include "components/sync/test/fake_server/fake_server.h" #include "components/sync/test/fake_server/fake_server_jni/FakeServerHelper_jni.h" #include "components/sync/test/fake_server/fake_server_network_resources.h" @@ -32,57 +31,85 @@ using base::android::JavaParamRef; -FakeServerHelperAndroid::FakeServerHelperAndroid(JNIEnv* env, jobject obj) {} +namespace { -FakeServerHelperAndroid::~FakeServerHelperAndroid() {} +void DeserializeEntity(JNIEnv* env, + jbyteArray serialized_entity, + sync_pb::SyncEntity* entity) { + int bytes_length = env->GetArrayLength(serialized_entity); + jbyte* bytes = env->GetByteArrayElements(serialized_entity, nullptr); + std::string string(reinterpret_cast<char*>(bytes), bytes_length); -static jlong JNI_FakeServerHelper_Init(JNIEnv* env, - const JavaParamRef<jobject>& obj) { - FakeServerHelperAndroid* fake_server_android = - new FakeServerHelperAndroid(env, obj); - return reinterpret_cast<intptr_t>(fake_server_android); + bool success = entity->ParseFromString(string); + DCHECK(success) << "Could not deserialize Entity"; } -jlong FakeServerHelperAndroid::CreateFakeServer( +void DeserializeEntitySpecifics( JNIEnv* env, - const JavaParamRef<jobject>& obj, - jlong sync_service_impl) { - fake_server::FakeServer* fake_server = new fake_server::FakeServer(); - syncer::SyncServiceImpl* sync_service = + const JavaParamRef<jbyteArray>& serialized_entity_specifics, + sync_pb::EntitySpecifics* entity_specifics) { + std::string specifics_string; + base::android::JavaByteArrayToString(env, serialized_entity_specifics, + &specifics_string); + + bool success = entity_specifics->ParseFromString(specifics_string); + DCHECK(success) << "Could not deserialize EntitySpecifics"; +} + +std::unique_ptr<syncer::LoopbackServerEntity> CreateBookmarkEntity( + JNIEnv* env, + jstring title, + const base::android::JavaRef<jobject>& url, + jstring parent_id) { + auto gurl = *url::GURLAndroid::ToNativeGURL(env, url); + DCHECK(gurl.is_valid()) << "The given string (" + << gurl.possibly_invalid_spec() + << ") is not a valid URL."; + + fake_server::EntityBuilderFactory entity_builder_factory; + fake_server::BookmarkEntityBuilder bookmark_builder = + entity_builder_factory.NewBookmarkEntityBuilder( + base::android::ConvertJavaStringToUTF8(env, title)); + bookmark_builder.SetParentId( + base::android::ConvertJavaStringToUTF8(env, parent_id)); + return bookmark_builder.BuildBookmark(gurl); +} + +} // namespace + +static jlong JNI_FakeServerHelper_CreateFakeServer(JNIEnv* env, + jlong sync_service_impl) { + auto* fake_server = new fake_server::FakeServer(); + auto* service_ptr = reinterpret_cast<syncer::SyncServiceImpl*>(sync_service_impl); - sync_service->OverrideNetworkForTest( + service_ptr->OverrideNetworkForTest( fake_server::CreateFakeServerHttpPostProviderFactory( fake_server->AsWeakPtr())); return reinterpret_cast<intptr_t>(fake_server); } -void FakeServerHelperAndroid::DeleteFakeServer(JNIEnv* env, - const JavaParamRef<jobject>& obj, - jlong fake_server, - jlong sync_service_impl) { - base::ScopedAllowBlockingForTesting scoped_allow; - syncer::SyncServiceImpl* sync_service = +static void JNI_FakeServerHelper_DeleteFakeServer(JNIEnv* env, + jlong fake_server, + jlong sync_service_impl) { + auto* service_ptr = reinterpret_cast<syncer::SyncServiceImpl*>(sync_service_impl); - sync_service->OverrideNetworkForTest(syncer::CreateHttpPostProviderFactory()); - fake_server::FakeServer* fake_server_ptr = - reinterpret_cast<fake_server::FakeServer*>(fake_server); - delete fake_server_ptr; + service_ptr->OverrideNetworkForTest(syncer::CreateHttpPostProviderFactory()); + delete reinterpret_cast<fake_server::FakeServer*>(fake_server); } -jboolean FakeServerHelperAndroid::VerifyEntityCountByTypeAndName( +static jboolean JNI_FakeServerHelper_VerifyEntityCountByTypeAndName( JNIEnv* env, - const JavaParamRef<jobject>& obj, jlong fake_server, - jlong count, - jint model_type_int, + jint count, + jint model_type, const JavaParamRef<jstring>& name) { - syncer::ModelType model_type = static_cast<syncer::ModelType>(model_type_int); fake_server::FakeServer* fake_server_ptr = reinterpret_cast<fake_server::FakeServer*>(fake_server); fake_server::FakeServerVerifier fake_server_verifier(fake_server_ptr); testing::AssertionResult result = fake_server_verifier.VerifyEntityCountByTypeAndName( - count, model_type, base::android::ConvertJavaStringToUTF8(env, name)); + count, static_cast<syncer::ModelType>(model_type), + base::android::ConvertJavaStringToUTF8(env, name)); if (!result) LOG(WARNING) << result.message(); @@ -90,9 +117,8 @@ return result; } -jboolean FakeServerHelperAndroid::VerifySessions( +static jboolean JNI_FakeServerHelper_VerifySessions( JNIEnv* env, - const JavaParamRef<jobject>& obj, jlong fake_server, const JavaParamRef<jobjectArray>& url_array) { std::multiset<std::string> tab_urls; @@ -114,32 +140,27 @@ return result; } -base::android::ScopedJavaLocalRef<jobjectArray> -FakeServerHelperAndroid::GetSyncEntitiesByModelType( - JNIEnv* env, - const JavaParamRef<jobject>& obj, - jlong fake_server, - jint model_type_int) { +static base::android::ScopedJavaLocalRef<jobjectArray> +JNI_FakeServerHelper_GetSyncEntitiesByModelType(JNIEnv* env, + jlong fake_server, + jint model_type) { fake_server::FakeServer* fake_server_ptr = reinterpret_cast<fake_server::FakeServer*>(fake_server); - - syncer::ModelType model_type = static_cast<syncer::ModelType>(model_type_int); - std::vector<sync_pb::SyncEntity> entities = - fake_server_ptr->GetSyncEntitiesByModelType(model_type); + fake_server_ptr->GetSyncEntitiesByModelType( + static_cast<syncer::ModelType>(model_type)); std::vector<std::string> entity_strings; - for (size_t i = 0; i < entities.size(); ++i) { + for (const sync_pb::SyncEntity& entity : entities) { std::string s; - entities[i].SerializeToString(&s); + entity.SerializeToString(&s); entity_strings.push_back(s); } return base::android::ToJavaArrayOfByteArray(env, entity_strings); } -void FakeServerHelperAndroid::InjectUniqueClientEntity( +static void JNI_FakeServerHelper_InjectUniqueClientEntity( JNIEnv* env, - const JavaParamRef<jobject>& obj, jlong fake_server, const JavaParamRef<jstring>& non_unique_name, const JavaParamRef<jstring>& client_tag, @@ -159,9 +180,8 @@ entity_specifics, /*creation_time=*/now, /*last_modified_time=*/now)); } -void FakeServerHelperAndroid::SetWalletData( +static void JNI_FakeServerHelper_SetWalletData( JNIEnv* env, - const JavaParamRef<jobject>& obj, jlong fake_server, const JavaParamRef<jbyteArray>& serialized_entity) { fake_server::FakeServer* fake_server_ptr = @@ -173,9 +193,8 @@ fake_server_ptr->SetWalletData({entity}); } -void FakeServerHelperAndroid::ModifyEntitySpecifics( +static void JNI_FakeServerHelper_ModifyEntitySpecifics( JNIEnv* env, - const JavaParamRef<jobject>& obj, jlong fake_server, const JavaParamRef<jstring>& id, const JavaParamRef<jbyteArray>& serialized_entity_specifics) { @@ -190,32 +209,8 @@ base::android::ConvertJavaStringToUTF8(env, id), entity_specifics); } -void FakeServerHelperAndroid::DeserializeEntity(JNIEnv* env, - jbyteArray serialized_entity, - sync_pb::SyncEntity* entity) { - int bytes_length = env->GetArrayLength(serialized_entity); - jbyte* bytes = env->GetByteArrayElements(serialized_entity, nullptr); - std::string string(reinterpret_cast<char*>(bytes), bytes_length); - - if (!entity->ParseFromString(string)) - NOTREACHED() << "Could not deserialize Entity"; -} - -void FakeServerHelperAndroid::DeserializeEntitySpecifics( +static void JNI_FakeServerHelper_InjectBookmarkEntity( JNIEnv* env, - const JavaParamRef<jbyteArray>& serialized_entity_specifics, - sync_pb::EntitySpecifics* entity_specifics) { - std::string specifics_string; - base::android::JavaByteArrayToString(env, serialized_entity_specifics, - &specifics_string); - - if (!entity_specifics->ParseFromString(specifics_string)) - NOTREACHED() << "Could not deserialize EntitySpecifics"; -} - -void FakeServerHelperAndroid::InjectBookmarkEntity( - JNIEnv* env, - const JavaParamRef<jobject>& obj, jlong fake_server, const JavaParamRef<jstring>& title, const JavaParamRef<jobject>& url, @@ -226,9 +221,8 @@ CreateBookmarkEntity(env, title, url, parent_id)); } -void FakeServerHelperAndroid::InjectBookmarkFolderEntity( +static void JNI_FakeServerHelper_InjectBookmarkFolderEntity( JNIEnv* env, - const JavaParamRef<jobject>& obj, jlong fake_server, const JavaParamRef<jstring>& title, const JavaParamRef<jstring>& parent_id) { @@ -245,9 +239,8 @@ fake_server_ptr->InjectEntity(bookmark_builder.BuildFolder()); } -void FakeServerHelperAndroid::ModifyBookmarkEntity( +static void JNI_FakeServerHelper_ModifyBookmarkEntity( JNIEnv* env, - const JavaParamRef<jobject>& obj, jlong fake_server, const JavaParamRef<jstring>& entity_id, const JavaParamRef<jstring>& title, @@ -269,9 +262,8 @@ proto.specifics()); } -void FakeServerHelperAndroid::ModifyBookmarkFolderEntity( +static void JNI_FakeServerHelper_ModifyBookmarkFolderEntity( JNIEnv* env, - const JavaParamRef<jobject>& obj, jlong fake_server, const JavaParamRef<jstring>& entity_id, const JavaParamRef<jstring>& title, @@ -298,40 +290,15 @@ proto.specifics()); } -std::unique_ptr<syncer::LoopbackServerEntity> -FakeServerHelperAndroid::CreateBookmarkEntity( - JNIEnv* env, - jstring title, - const base::android::JavaRef<jobject>& url, - jstring parent_id) { - auto gurl = *url::GURLAndroid::ToNativeGURL(env, url); - if (!gurl.is_valid()) { - NOTREACHED() << "The given string (" << gurl.possibly_invalid_spec() - << ") is not a valid URL."; - } - - fake_server::EntityBuilderFactory entity_builder_factory; - fake_server::BookmarkEntityBuilder bookmark_builder = - entity_builder_factory.NewBookmarkEntityBuilder( - base::android::ConvertJavaStringToUTF8(env, title)); - bookmark_builder.SetParentId( - base::android::ConvertJavaStringToUTF8(env, parent_id)); - return bookmark_builder.BuildBookmark(gurl); -} - -base::android::ScopedJavaLocalRef<jstring> -FakeServerHelperAndroid::GetBookmarkBarFolderId( - JNIEnv* env, - const JavaParamRef<jobject>& obj, - jlong fake_server) { +static base::android::ScopedJavaLocalRef<jstring> +JNI_FakeServerHelper_GetBookmarkBarFolderId(JNIEnv* env, jlong fake_server) { // Rather hard code this here then incur the cost of yet another method. // It is very unlikely that this will ever change. return base::android::ConvertUTF8ToJavaString(env, "32904_bookmark_bar"); } -void FakeServerHelperAndroid::DeleteEntity( +static void JNI_FakeServerHelper_DeleteEntity( JNIEnv* env, - const JavaParamRef<jobject>& obj, jlong fake_server, const JavaParamRef<jstring>& id, const JavaParamRef<jstring>& client_tag_hash) { @@ -342,9 +309,8 @@ native_id, base::android::ConvertJavaStringToUTF8(env, client_tag_hash))); } -void FakeServerHelperAndroid::SetTrustedVaultNigori( +static void JNI_FakeServerHelper_SetTrustedVaultNigori( JNIEnv* env, - const JavaParamRef<jobject>& obj, jlong fake_server, const JavaParamRef<jbyteArray>& trusted_vault_key) { std::vector<uint8_t> native_trusted_vault_key; @@ -355,9 +321,8 @@ reinterpret_cast<fake_server::FakeServer*>(fake_server)); } -void FakeServerHelperAndroid::ClearServerData(JNIEnv* env, - const JavaParamRef<jobject>& obj, - jlong fake_server) { +static void JNI_FakeServerHelper_ClearServerData(JNIEnv* env, + jlong fake_server) { fake_server::FakeServer* fake_server_ptr = reinterpret_cast<fake_server::FakeServer*>(fake_server); fake_server_ptr->ClearServerData();
diff --git a/components/sync/test/fake_server/android/fake_server_helper_android.h b/components/sync/test/fake_server/android/fake_server_helper_android.h deleted file mode 100644 index 019a354..0000000 --- a/components/sync/test/fake_server/android/fake_server_helper_android.h +++ /dev/null
@@ -1,171 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_SYNC_TEST_FAKE_SERVER_ANDROID_FAKE_SERVER_HELPER_ANDROID_H_ -#define COMPONENTS_SYNC_TEST_FAKE_SERVER_ANDROID_FAKE_SERVER_HELPER_ANDROID_H_ - -#include <jni.h> - -#include <memory> - -#include "base/android/scoped_java_ref.h" -#include "components/sync/test/fake_server/entity_builder_factory.h" - -// Helper for utilizing native FakeServer infrastructure in Android tests. -class FakeServerHelperAndroid { - public: - // Creates a FakeServerHelperAndroid. - FakeServerHelperAndroid(JNIEnv* env, jobject obj); - - // Factory method for creating a native FakeServer object. The caller assumes - // ownership. - jlong CreateFakeServer(JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong sync_service_impl); - - // Deletes the given |fake_server| (a FakeServer pointer created via - // CreateFakeServer). - void DeleteFakeServer(JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server, - jlong sync_service_impl); - - // Returns true if and only if |fake_server| contains |count| entities that - // match |model_type_string| and |name|. - jboolean VerifyEntityCountByTypeAndName( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server, - jlong count, - jint model_type_int, - const base::android::JavaParamRef<jstring>& name); - - // Returns true iff |fake_server| has exactly one window of sessions with - // tabs matching |url_array|. The order of the array does not matter. - jboolean VerifySessions( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server, - const base::android::JavaParamRef<jobjectArray>& url_array); - - // Return the entities for |model_type_string| on |fake_server|. - base::android::ScopedJavaLocalRef<jobjectArray> GetSyncEntitiesByModelType( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server, - jint model_type_int); - - // Injects a UniqueClientEntity into |fake_server|. - void InjectUniqueClientEntity( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server, - const base::android::JavaParamRef<jstring>& non_unique_name, - const base::android::JavaParamRef<jstring>& client_tag, - const base::android::JavaParamRef<jbyteArray>& - serialized_entity_specifics); - - // Sets the Wallet card and address data to be served in following GetUpdates - // requests. - void SetWalletData( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server, - const base::android::JavaParamRef<jbyteArray>& serialized_entity); - - // Modifies the entity with |id| on |fake_server|. - void ModifyEntitySpecifics(JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server, - const base::android::JavaParamRef<jstring>& name, - const base::android::JavaParamRef<jbyteArray>& - serialized_entity_specifics); - - // Injects a BookmarkEntity into |fake_server|. - void InjectBookmarkEntity( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server, - const base::android::JavaParamRef<jstring>& title, - const base::android::JavaParamRef<jobject>& url, - const base::android::JavaParamRef<jstring>& parent_id); - - // Injects a bookmark folder entity into |fake_server|. - void InjectBookmarkFolderEntity( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server, - const base::android::JavaParamRef<jstring>& title, - const base::android::JavaParamRef<jstring>& parent_id); - - // Modify the BookmarkEntity with |entity_id| on |fake_server|. - void ModifyBookmarkEntity( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server, - const base::android::JavaParamRef<jstring>& entity_id, - const base::android::JavaParamRef<jstring>& title, - const base::android::JavaParamRef<jobject>& url, - const base::android::JavaParamRef<jstring>& parent_id); - - // Modify the bookmark folder with |entity_id| on |fake_server|. - void ModifyBookmarkFolderEntity( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server, - const base::android::JavaParamRef<jstring>& entity_id, - const base::android::JavaParamRef<jstring>& title, - const base::android::JavaParamRef<jstring>& parent_id); - - // Returns the bookmark bar folder ID. - base::android::ScopedJavaLocalRef<jstring> GetBookmarkBarFolderId( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server); - - // Deletes an entity on the server. This is the JNI way of injecting a - // tombstone. - void DeleteEntity( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server, - const base::android::JavaParamRef<jstring>& id, - const base::android::JavaParamRef<jstring>& client_tag_hash); - - // Sets trusted vault nigori with keys derived from |trusted_vault_key| on - // the server. - void SetTrustedVaultNigori( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server, - const base::android::JavaParamRef<jbyteArray>& trusted_vault_key); - - // Simulates a dashboard stop and clear. - void ClearServerData(JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jlong fake_server); - - private: - virtual ~FakeServerHelperAndroid(); - - // Deserializes |serialized_entity| into |entity|. - void DeserializeEntity(JNIEnv* env, - jbyteArray serialized_entity, - sync_pb::SyncEntity* entity); - - // Deserializes |serialized_entity_specifics| into |entity_specifics|. - void DeserializeEntitySpecifics(JNIEnv* env, - const base::android::JavaParamRef<jbyteArray>& - serialized_entity_specifics, - sync_pb::EntitySpecifics* entity_specifics); - - // Creates a bookmark entity. - std::unique_ptr<syncer::LoopbackServerEntity> CreateBookmarkEntity( - JNIEnv* env, - jstring title, - const base::android::JavaRef<jobject>& url, - jstring parent_id); -}; - -#endif // COMPONENTS_SYNC_TEST_FAKE_SERVER_ANDROID_FAKE_SERVER_HELPER_ANDROID_H_
diff --git a/components/webapps/browser/android/translations/android_webapps_strings_az.xtb b/components/webapps/browser/android/translations/android_webapps_strings_az.xtb index 5af20e2..1c3d919 100644 --- a/components/webapps/browser/android/translations/android_webapps_strings_az.xtb +++ b/components/webapps/browser/android/translations/android_webapps_strings_az.xtb
@@ -1,7 +1,7 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="az"> -<translation id="2139186145475833000">Ev ekranına əlavə edin</translation> +<translation id="2139186145475833000">Əsas ekrana əlavə edin</translation> <translation id="2478076885740497414">Tətbiqi quraşdırın</translation> <translation id="3789841737615482174">Quraşdırın</translation> <translation id="4665282149850138822"><ph name="NAME" /> əsas ekranınıza əlavə edildi</translation>
diff --git a/content/browser/background_fetch/background_fetch_data_manager.cc b/content/browser/background_fetch/background_fetch_data_manager.cc index 22c876e..b9c0ee7 100644 --- a/content/browser/background_fetch/background_fetch_data_manager.cc +++ b/content/browser/background_fetch/background_fetch_data_manager.cc
@@ -90,8 +90,12 @@ // This origin and unique_id has never been opened before. mojo::Remote<blink::mojom::CacheStorage> remote; network::CrossOriginEmbedderPolicy cross_origin_embedder_policy; + + // TODO(https://crbug.com/1199077): `BackgroundFetchDataManager` needs to be + // updated to use StorageKey. storage_partition_->GetCacheStorageControl()->AddReceiver( - cross_origin_embedder_policy, mojo::NullRemote(), origin, + cross_origin_embedder_policy, mojo::NullRemote(), + blink::StorageKey(origin), storage::mojom::CacheStorageOwner::kBackgroundFetch, remote.BindNewPipeAndPassReceiver());
diff --git a/content/browser/cache_storage/cache_storage.proto b/content/browser/cache_storage/cache_storage.proto index 4b3af9d..87fedf7 100644 --- a/content/browser/cache_storage/cache_storage.proto +++ b/content/browser/cache_storage/cache_storage.proto
@@ -58,6 +58,7 @@ optional string request_method = 14; optional int64 padding = 15; optional int64 side_data_padding = 16; + optional bool request_include_credentials = 17; } message CacheMetadata {
diff --git a/content/browser/cache_storage/cache_storage_cache_unittest.cc b/content/browser/cache_storage/cache_storage_cache_unittest.cc index 59351884..5a44f402 100644 --- a/content/browser/cache_storage/cache_storage_cache_unittest.cc +++ b/content/browser/cache_storage/cache_storage_cache_unittest.cc
@@ -680,7 +680,8 @@ net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN, /*alpn_negotiated_protocol=*/"unknown", /*was_fetched_via_spdy=*/false, /*has_range_requested=*/false, - /*auth_challenge_info=*/absl::nullopt); + /*auth_challenge_info=*/absl::nullopt, + /*request_include_credentials=*/true); } void CopySideDataToResponse(const std::string& uuid,
diff --git a/content/browser/cache_storage/cache_storage_context_impl.cc b/content/browser/cache_storage/cache_storage_context_impl.cc index f3c09f18..0df4c5a6 100644 --- a/content/browser/cache_storage/cache_storage_context_impl.cc +++ b/content/browser/cache_storage/cache_storage_context_impl.cc
@@ -96,7 +96,7 @@ const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy, mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> coep_reporter, - const url::Origin& origin, + const blink::StorageKey& storage_key, storage::mojom::CacheStorageOwner owner, mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -104,21 +104,23 @@ if (!dispatcher_host_) dispatcher_host_ = std::make_unique<CacheStorageDispatcherHost>(this); dispatcher_host_->AddReceiver(cross_origin_embedder_policy, - std::move(coep_reporter), origin, owner, + std::move(coep_reporter), storage_key, owner, std::move(receiver)); } -void CacheStorageContextImpl::GetAllOriginsInfo( - storage::mojom::CacheStorageControl::GetAllOriginsInfoCallback callback) { +void CacheStorageContextImpl::GetAllStorageKeysInfo( + storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback + callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); cache_manager_->GetAllStorageKeysUsage( storage::mojom::CacheStorageOwner::kCacheAPI, std::move(callback)); } -void CacheStorageContextImpl::DeleteForOrigin(const url::Origin& origin) { +void CacheStorageContextImpl::DeleteForStorageKey( + const blink::StorageKey& storage_key) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); cache_manager_->DeleteStorageKeyData( - blink::StorageKey(origin), storage::mojom::CacheStorageOwner::kCacheAPI); + storage_key, storage::mojom::CacheStorageOwner::kCacheAPI); } void CacheStorageContextImpl::AddObserver(
diff --git a/content/browser/cache_storage/cache_storage_context_impl.h b/content/browser/cache_storage/cache_storage_context_impl.h index 0a009c8..26650ed 100644 --- a/content/browser/cache_storage/cache_storage_context_impl.h +++ b/content/browser/cache_storage/cache_storage_context_impl.h
@@ -34,10 +34,6 @@ class QuotaManagerProxy; } -namespace url { -class Origin; -} - namespace content { class CacheStorageDispatcherHost; @@ -72,13 +68,13 @@ const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy, mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> coep_reporter_remote, - const url::Origin& origin, + const blink::StorageKey& storage_key, storage::mojom::CacheStorageOwner owner, mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) override; - void DeleteForOrigin(const url::Origin& origin) override; - void GetAllOriginsInfo( - storage::mojom::CacheStorageControl::GetAllOriginsInfoCallback callback) - override; + void DeleteForStorageKey(const blink::StorageKey& storage_key) override; + void GetAllStorageKeysInfo( + storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback + callback) override; void AddObserver(mojo::PendingRemote<storage::mojom::CacheStorageObserver> observer) override; void ApplyPolicyUpdates(std::vector<storage::mojom::StoragePolicyUpdatePtr>
diff --git a/content/browser/cache_storage/cache_storage_control_wrapper.cc b/content/browser/cache_storage/cache_storage_control_wrapper.cc index 15ae5e5..40cc472 100644 --- a/content/browser/cache_storage/cache_storage_control_wrapper.cc +++ b/content/browser/cache_storage/cache_storage_control_wrapper.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "content/browser/cache_storage/cache_storage_control_wrapper.h" +#include "third_party/blink/public/common/storage_key/storage_key.h" namespace content { @@ -63,29 +64,31 @@ const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy, mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> coep_reporter_remote, - const url::Origin& origin, + const blink::StorageKey& storage_key, storage::mojom::CacheStorageOwner owner, mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (storage_policy_observer_) - storage_policy_observer_->StartTrackingOrigin(origin); + storage_policy_observer_->StartTrackingOrigin(storage_key.origin()); cache_storage_control_->AddReceiver(cross_origin_embedder_policy, - std::move(coep_reporter_remote), origin, - owner, std::move(receiver)); + std::move(coep_reporter_remote), + storage_key, owner, std::move(receiver)); } -void CacheStorageControlWrapper::DeleteForOrigin(const url::Origin& origin) { +void CacheStorageControlWrapper::DeleteForStorageKey( + const blink::StorageKey& storage_key) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - cache_storage_control_->DeleteForOrigin(origin); + cache_storage_control_->DeleteForStorageKey(storage_key); } -void CacheStorageControlWrapper::GetAllOriginsInfo( - storage::mojom::CacheStorageControl::GetAllOriginsInfoCallback callback) { +void CacheStorageControlWrapper::GetAllStorageKeysInfo( + storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback + callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - cache_storage_control_->GetAllOriginsInfo(std::move(callback)); + cache_storage_control_->GetAllStorageKeysInfo(std::move(callback)); } void CacheStorageControlWrapper::AddObserver(
diff --git a/content/browser/cache_storage/cache_storage_control_wrapper.h b/content/browser/cache_storage/cache_storage_control_wrapper.h index a4eb3c9..e4522b3d 100644 --- a/content/browser/cache_storage/cache_storage_control_wrapper.h +++ b/content/browser/cache_storage/cache_storage_control_wrapper.h
@@ -13,14 +13,15 @@ #include "storage/browser/quota/quota_manager_proxy.h" #include "storage/browser/quota/special_storage_policy.h" #include "storage/browser/quota/storage_policy_observer.h" +#include "third_party/blink/public/common/storage_key/storage_key.h" #include "url/origin.h" namespace content { // This class is a browser-side implementation of the browser <-> storage -// service mojo for cache storage. It wraps mojo calls to track origin usage -// and forwards them to the storage service remote. All functions should be -// called on the UI thread. +// service mojo for cache storage. It wraps mojo calls to track storage keys +// usage and forwards them to the storage service remote. All functions should +// be called on the UI thread. class CONTENT_EXPORT CacheStorageControlWrapper : public storage::mojom::CacheStorageControl { public: @@ -46,13 +47,13 @@ const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy, mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> coep_reporter_remote, - const url::Origin& origin, + const blink::StorageKey& storage_key, storage::mojom::CacheStorageOwner owner, mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) override; - void DeleteForOrigin(const url::Origin& origin) override; - void GetAllOriginsInfo( - storage::mojom::CacheStorageControl::GetAllOriginsInfoCallback callback) - override; + void DeleteForStorageKey(const blink::StorageKey& storage_key) override; + void GetAllStorageKeysInfo( + storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback + callback) override; void AddObserver(mojo::PendingRemote<storage::mojom::CacheStorageObserver> observer) override; void ApplyPolicyUpdates(std::vector<storage::mojom::StoragePolicyUpdatePtr>
diff --git a/content/browser/cache_storage/cache_storage_dispatcher_host.cc b/content/browser/cache_storage/cache_storage_dispatcher_host.cc index 89edbca..208a4b2 100644 --- a/content/browser/cache_storage/cache_storage_dispatcher_host.cc +++ b/content/browser/cache_storage/cache_storage_dispatcher_host.cc
@@ -152,7 +152,8 @@ response->url_list.back(), response->url_list.front(), document_origin, corp_header_value, RequestMode::kNoCors, document_origin, network::mojom::RequestDestination::kEmpty, - document_coep, coep_reporter ? coep_reporter.get() : nullptr) + response->request_include_credentials, document_coep, + coep_reporter ? coep_reporter.get() : nullptr) .has_value(); } @@ -970,22 +971,17 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } -// TODO(https://crbug.com/1211797): Replace `url::Origin` with -// `blink::StorageKey` in the argument list. void CacheStorageDispatcherHost::AddReceiver( const CrossOriginEmbedderPolicy& cross_origin_embedder_policy, mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> coep_reporter, - const url::Origin& origin, + const blink::StorageKey& storage_key, storage::mojom::CacheStorageOwner owner, mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); bool incognito = context_ ? context_->is_incognito() : false; - // TODO(https://crbug.com/1211797): Pass a StorageKey into - // CacheStorageDispatcherHost::AddReceiver directly and avoid the translation - // here. auto impl = std::make_unique<CacheStorageImpl>( - this, blink::StorageKey(origin), incognito, cross_origin_embedder_policy, + this, storage_key, incognito, cross_origin_embedder_policy, std::move(coep_reporter), owner); receivers_.Add(std::move(impl), std::move(receiver)); }
diff --git a/content/browser/cache_storage/cache_storage_dispatcher_host.h b/content/browser/cache_storage/cache_storage_dispatcher_host.h index 833aaa3..04b8c16 100644 --- a/content/browser/cache_storage/cache_storage_dispatcher_host.h +++ b/content/browser/cache_storage/cache_storage_dispatcher_host.h
@@ -16,10 +16,6 @@ #include "mojo/public/cpp/bindings/unique_receiver_set.h" #include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom.h" -namespace url { -class Origin; -} - namespace network { struct CrossOriginEmbedderPolicy; } @@ -47,7 +43,7 @@ const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy, mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> coep_reporter, - const url::Origin& origin, + const blink::StorageKey& storage_key, storage::mojom::CacheStorageOwner owner, mojo::PendingReceiver<blink::mojom::CacheStorage> receiver);
diff --git a/content/browser/cache_storage/cache_storage_manager.h b/content/browser/cache_storage/cache_storage_manager.h index 69504004..eebd63c 100644 --- a/content/browser/cache_storage/cache_storage_manager.h +++ b/content/browser/cache_storage/cache_storage_manager.h
@@ -35,7 +35,7 @@ // QuotaClient and Browsing Data Deletion support. virtual void GetAllStorageKeysUsage( storage::mojom::CacheStorageOwner owner, - storage::mojom::CacheStorageControl::GetAllOriginsInfoCallback + storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback callback) = 0; virtual void GetStorageKeyUsage( const blink::StorageKey& storage_key,
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc index 0ae00fc..3a440027 100644 --- a/content/browser/cache_storage/cache_storage_manager_unittest.cc +++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -653,7 +653,8 @@ net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN, /*alpn_negotiated_protocol=*/"unknown", /*was_fetched_via_spdy=*/false, /*has_range_requested=*/false, - /*auth_challenge_info=*/absl::nullopt); + /*auth_challenge_info=*/absl::nullopt, + /*request_include_credentials=*/true); blink::mojom::BatchOperationPtr operation = blink::mojom::BatchOperation::New();
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc index d14ef408..be3a3786 100644 --- a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc +++ b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
@@ -441,6 +441,11 @@ padding = storage::ComputeRandomResponsePadding(); } + bool request_include_credentials = + metadata.response().has_request_include_credentials() + ? metadata.response().request_include_credentials() + : true; + // Note that |has_range_requested| can be safely set to false since it only // affects HTTP 206 (Partial) responses, which are blocked from cache storage. // See https://fetch.spec.whatwg.org/#main-fetch for usage of @@ -462,7 +467,8 @@ static_cast<net::HttpResponseInfo::ConnectionInfo>( metadata.response().connection_info()), alpn_negotiated_protocol, metadata.response().was_fetched_via_spdy(), - /*has_range_requested=*/false, /*auth_challenge_info=*/absl::nullopt); + /*has_range_requested=*/false, /*auth_challenge_info=*/absl::nullopt, + request_include_credentials); } int64_t CalculateSideDataPadding( @@ -1929,6 +1935,8 @@ storage_key_, response_metadata, put_context->side_data_blob_size); } response_metadata->set_side_data_padding(side_data_padding); + response_metadata->set_request_include_credentials( + put_context->response->request_include_credentials); // Get a temporary copy of the entry pointer before passing it in base::Bind. disk_cache::Entry* temp_entry_ptr = put_context->cache_entry.get();
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc index b1555e96..3784f53 100644 --- a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc +++ b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc
@@ -127,7 +127,7 @@ // Open the various cache directories' index files and extract their storage // keys, sizes (if current), and last modified times. std::vector<storage::mojom::StorageUsageInfoPtr> -GetOriginsAndLastModifiedOnTaskRunner( +GetStorageKeysAndLastModifiedOnTaskRunner( std::vector<storage::mojom::StorageUsageInfoPtr> usages, base::FilePath root_path, storage::mojom::CacheStorageOwner owner) { @@ -195,7 +195,7 @@ base::FilePath root_path, storage::mojom::CacheStorageOwner owner) { std::vector<storage::mojom::StorageUsageInfoPtr> usages = - GetOriginsAndLastModifiedOnTaskRunner( + GetStorageKeysAndLastModifiedOnTaskRunner( std::vector<storage::mojom::StorageUsageInfoPtr>(), root_path, owner); std::vector<url::Origin> out_origins; @@ -221,7 +221,8 @@ void AllOriginSizesReported( std::vector<storage::mojom::StorageUsageInfoPtr> usages, - storage::mojom::CacheStorageControl::GetAllOriginsInfoCallback callback) { + storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback + callback) { // On scheduler sequence. base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), std::move(usages))); @@ -340,17 +341,18 @@ void LegacyCacheStorageManager::GetAllStorageKeysUsage( storage::mojom::CacheStorageOwner owner, - storage::mojom::CacheStorageControl::GetAllOriginsInfoCallback callback) { + storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback + callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); std::vector<storage::mojom::StorageUsageInfoPtr> usages; if (IsMemoryBacked()) { - for (const auto& origin_details : cache_storage_map_) { - if (origin_details.first.second != owner) + for (const auto& storage_keys_details : cache_storage_map_) { + if (storage_keys_details.first.second != owner) continue; usages.emplace_back(storage::mojom::StorageUsageInfo::New( - origin_details.first.first.origin(), + storage_keys_details.first.first.origin(), /*total_size_bytes=*/0, /*last_modified=*/base::Time())); } @@ -360,14 +362,14 @@ cache_task_runner_->PostTaskAndReplyWithResult( FROM_HERE, - base::BindOnce(&GetOriginsAndLastModifiedOnTaskRunner, std::move(usages), - root_path_, owner), + base::BindOnce(&GetStorageKeysAndLastModifiedOnTaskRunner, + std::move(usages), root_path_, owner), base::BindOnce(&LegacyCacheStorageManager::GetAllStorageKeysUsageGetSizes, base::WrapRefCounted(this), std::move(callback))); } void LegacyCacheStorageManager::GetAllStorageKeysUsageGetSizes( - storage::mojom::CacheStorageControl::GetAllOriginsInfoCallback callback, + storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback callback, std::vector<storage::mojom::StorageUsageInfoPtr> usages) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h index 9728080..774a3dd8 100644 --- a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h +++ b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.h
@@ -71,8 +71,8 @@ void GetAllStorageKeysUsage( storage::mojom::CacheStorageOwner owner, - storage::mojom::CacheStorageControl::GetAllOriginsInfoCallback callback) - override; + storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback + callback) override; void GetStorageKeyUsage( const blink::StorageKey& storage_key, storage::mojom::CacheStorageOwner owner, @@ -124,7 +124,8 @@ ~LegacyCacheStorageManager() override; void GetAllStorageKeysUsageGetSizes( - storage::mojom::CacheStorageControl::GetAllOriginsInfoCallback callback, + storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback + callback, std::vector<storage::mojom::StorageUsageInfoPtr> usage_info); void DeleteStorageKeyDidClose(
diff --git a/content/browser/loader/cached_navigation_url_loader.cc b/content/browser/loader/cached_navigation_url_loader.cc index 670174b..08e0ed32 100644 --- a/content/browser/loader/cached_navigation_url_loader.cc +++ b/content/browser/loader/cached_navigation_url_loader.cc
@@ -20,8 +20,11 @@ CachedNavigationURLLoader::CachedNavigationURLLoader( std::unique_ptr<NavigationRequestInfo> request_info, - NavigationURLLoaderDelegate* delegate) - : request_info_(std::move(request_info)), delegate_(delegate) { + NavigationURLLoaderDelegate* delegate, + network::mojom::URLResponseHeadPtr cached_response_head) + : request_info_(std::move(request_info)), + delegate_(delegate), + cached_response_head_(std::move(cached_response_head)) { // Respond with a fake response. We use PostTask here to mimic the flow of // a normal navigation. // @@ -36,10 +39,9 @@ void CachedNavigationURLLoader::OnResponseStarted() { GlobalRequestID global_id = GlobalRequestID::MakeBrowserInitiated(); - auto response_head = network::mojom::URLResponseHead::New(); - response_head->parsed_headers = network::mojom::ParsedHeaders::New(); + DCHECK(cached_response_head_); delegate_->OnResponseStarted( - /*url_loader_client_endpoints=*/nullptr, std::move(response_head), + /*url_loader_client_endpoints=*/nullptr, std::move(cached_response_head_), /*response_body=*/mojo::ScopedDataPipeConsumerHandle(), global_id, /*is_download=*/false, blink::NavigationDownloadPolicy(), request_info_->isolation_info.network_isolation_key(), absl::nullopt, @@ -50,9 +52,10 @@ // static std::unique_ptr<NavigationURLLoader> CachedNavigationURLLoader::Create( std::unique_ptr<NavigationRequestInfo> request_info, - NavigationURLLoaderDelegate* delegate) { - return std::make_unique<CachedNavigationURLLoader>(std::move(request_info), - delegate); + NavigationURLLoaderDelegate* delegate, + network::mojom::URLResponseHeadPtr cached_response_head) { + return std::make_unique<CachedNavigationURLLoader>( + std::move(request_info), delegate, std::move(cached_response_head)); } void CachedNavigationURLLoader::FollowRedirect(
diff --git a/content/browser/loader/cached_navigation_url_loader.h b/content/browser/loader/cached_navigation_url_loader.h index f98a44e..a481bfab 100644 --- a/content/browser/loader/cached_navigation_url_loader.h +++ b/content/browser/loader/cached_navigation_url_loader.h
@@ -16,13 +16,16 @@ // prerendered). class CachedNavigationURLLoader : public NavigationURLLoader { public: - CachedNavigationURLLoader(std::unique_ptr<NavigationRequestInfo> request_info, - NavigationURLLoaderDelegate* delegate); + CachedNavigationURLLoader( + std::unique_ptr<NavigationRequestInfo> request_info, + NavigationURLLoaderDelegate* delegate, + network::mojom::URLResponseHeadPtr cached_response_head); ~CachedNavigationURLLoader() override; static std::unique_ptr<NavigationURLLoader> Create( std::unique_ptr<NavigationRequestInfo> request_info, - NavigationURLLoaderDelegate* delegate); + NavigationURLLoaderDelegate* delegate, + network::mojom::URLResponseHeadPtr cached_response_head); // NavigationURLLoader implementation. void FollowRedirect( @@ -35,6 +38,7 @@ void OnResponseStarted(); std::unique_ptr<NavigationRequestInfo> request_info_; NavigationURLLoaderDelegate* delegate_; + network::mojom::URLResponseHeadPtr cached_response_head_; base::WeakPtrFactory<CachedNavigationURLLoader> weak_factory_{this}; };
diff --git a/content/browser/loader/navigation_url_loader.cc b/content/browser/loader/navigation_url_loader.cc index 2ac4f9e8..c7263c5 100644 --- a/content/browser/loader/navigation_url_loader.cc +++ b/content/browser/loader/navigation_url_loader.cc
@@ -35,6 +35,7 @@ mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> url_loader_network_observer, mojo::PendingRemote<network::mojom::DevToolsObserver> devtools_observer, + network::mojom::URLResponseHeadPtr cached_response_head, std::vector<std::unique_ptr<NavigationLoaderInterceptor>> initial_interceptors) { if (g_loader_factory) { @@ -44,8 +45,11 @@ loader_type); } - if (loader_type == LoaderType::kNoop) - return CachedNavigationURLLoader::Create(std::move(request_info), delegate); + if (loader_type == LoaderType::kNoop) { + DCHECK(cached_response_head); + return CachedNavigationURLLoader::Create(std::move(request_info), delegate, + std::move(cached_response_head)); + } return std::make_unique<NavigationURLLoaderImpl>( browser_context, storage_partition, std::move(request_info),
diff --git a/content/browser/loader/navigation_url_loader.h b/content/browser/loader/navigation_url_loader.h index 2813dae..bc8b87d 100644 --- a/content/browser/loader/navigation_url_loader.h +++ b/content/browser/loader/navigation_url_loader.h
@@ -71,6 +71,7 @@ mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver> url_loader_network_observer, mojo::PendingRemote<network::mojom::DevToolsObserver> devtools_observer, + network::mojom::URLResponseHeadPtr cached_response_head = nullptr, std::vector<std::unique_ptr<NavigationLoaderInterceptor>> initial_interceptors = {});
diff --git a/content/browser/prerender/prerender_browsertest.cc b/content/browser/prerender/prerender_browsertest.cc index 6869cbb5..f9cb3e6 100644 --- a/content/browser/prerender/prerender_browsertest.cc +++ b/content/browser/prerender/prerender_browsertest.cc
@@ -47,6 +47,7 @@ #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" +#include "content/public/test/navigation_handle_observer.h" #include "content/public/test/prerender_test_util.h" #include "content/public/test/test_navigation_observer.h" #include "content/public/test/test_utils.h" @@ -294,140 +295,6 @@ std::unique_ptr<test::PrerenderTestHelper> prerender_helper_; }; -// Tests for the legacy prerender trigger of <link rel="prerender"> ============ - -// TODO(https://crbug.com/1214964): Remove this test when we stop supporting -// <link rel="prerender">. -IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, LinkRelPrerender) { - const GURL kInitialUrl = GetUrl("/empty.html"); - const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender"); - - // Navigate to an initial page. - ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); - ASSERT_EQ(web_contents()->GetURL(), kInitialUrl); - - // Add <link rel=prerender> that will prerender `kPrerenderingUrl`. - ASSERT_EQ(GetRequestCount(kPrerenderingUrl), 0); - AddLinkRelPrerender(kPrerenderingUrl); - EXPECT_EQ(GetRequestCount(kPrerenderingUrl), 1); - - // A prerender host for the URL should be registered. - EXPECT_TRUE(HasHostForUrl(kPrerenderingUrl)); - - // Activate the prerendered page. - NavigatePrimaryPage(kPrerenderingUrl); - EXPECT_EQ(web_contents()->GetURL(), kPrerenderingUrl); - - // The prerender host should be consumed. - EXPECT_FALSE(HasHostForUrl(kPrerenderingUrl)); - - // Activating the prerendered page should not issue a request. - EXPECT_EQ(GetRequestCount(kPrerenderingUrl), 1); -} - -// TODO(https://crbug.com/1214964): Remove this test when we stop supporting -// <link rel="prerender">. -IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, LinkRelPrerender_Multiple) { - const GURL kInitialUrl = GetUrl("/empty.html"); - const GURL kPrerenderingUrl1 = GetUrl("/empty.html?prerender1"); - const GURL kPrerenderingUrl2 = GetUrl("/empty.html?prerender2"); - - // TODO(https://crbug.com/1186893): PrerenderHost is not deleted when the - // page enters BackForwardCache, though it should be. While this functionality - // is not implemented, disable BackForwardCache for testing and wait for the - // old RenderFrameHost to be deleted after we navigate away from it. - DisableBackForwardCacheForTesting( - web_contents(), BackForwardCacheImpl::TEST_ASSUMES_NO_CACHING); - - // Navigate to an initial page. - ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); - ASSERT_EQ(web_contents()->GetURL(), kInitialUrl); - - // Add <link rel=prerender> that will prerender `kPrerenderingUrl1` and - // `kPrerenderingUrl2`. - ASSERT_EQ(GetRequestCount(kPrerenderingUrl1), 0); - ASSERT_EQ(GetRequestCount(kPrerenderingUrl2), 0); - AddLinkRelPrerender(kPrerenderingUrl1); - AddLinkRelPrerender(kPrerenderingUrl2); - EXPECT_EQ(GetRequestCount(kPrerenderingUrl1), 1); - EXPECT_EQ(GetRequestCount(kPrerenderingUrl2), 1); - - // Prerender hosts for `kPrerenderingUrl1` and `kPrerenderingUrl2` should be - // registered. - EXPECT_TRUE(HasHostForUrl(kPrerenderingUrl1)); - EXPECT_TRUE(HasHostForUrl(kPrerenderingUrl2)); - - RenderFrameDeletedObserver delete_observer_rfh( - web_contents()->GetMainFrame()); - - // Activate the prerendered page. - NavigatePrimaryPage(kPrerenderingUrl2); - EXPECT_EQ(web_contents()->GetURL(), kPrerenderingUrl2); - - // Other PrerenderHost instances are deleted with the RFH. - delete_observer_rfh.WaitUntilDeleted(); - - // The prerender hosts should be consumed or destroyed for activation. - EXPECT_FALSE(HasHostForUrl(kPrerenderingUrl1)); - EXPECT_FALSE(HasHostForUrl(kPrerenderingUrl2)); - - // Activating the prerendered page should not issue a request. - EXPECT_EQ(GetRequestCount(kPrerenderingUrl1), 1); - EXPECT_EQ(GetRequestCount(kPrerenderingUrl2), 1); -} - -// TODO(https://crbug.com/1214964): Remove this test when we stop supporting -// <link rel="prerender">. -IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, LinkRelPrerender_Duplicate) { - const GURL kInitialUrl = GetUrl("/prerender/duplicate_prerenders.html"); - const GURL kPrerenderingUrl1 = GetUrl("/empty.html?1"); - const GURL kPrerenderingUrl2 = GetUrl("/empty.html?2"); - - // TODO(https://crbug.com/1186893): PrerenderHost is not deleted when the - // page enters BackForwardCache, though it should be. While this functionality - // is not implemented, disable BackForwardCache for testing and wait for the - // old RenderFrameHost to be deleted after we navigate away from it. - DisableBackForwardCacheForTesting( - web_contents(), BackForwardCacheImpl::TEST_ASSUMES_NO_CACHING); - - // Navigate to a page that initiates prerendering for `kPrerenderingUrl1` - // twice. The second prerendering request should be ignored. - ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); - - // Wait until the completion of prerendering. - WaitForPrerenderLoadCompletion(kPrerenderingUrl1); - WaitForPrerenderLoadCompletion(kPrerenderingUrl2); - - // Requests should be issued once per prerendering URL. - EXPECT_EQ(GetRequestCount(kPrerenderingUrl1), 1); - EXPECT_EQ(GetRequestCount(kPrerenderingUrl2), 1); - - // Prerender hosts for `kPrerenderingUrl1` and `kPrerenderingUrl2` should be - // registered. - EXPECT_TRUE(HasHostForUrl(kPrerenderingUrl1)); - EXPECT_TRUE(HasHostForUrl(kPrerenderingUrl2)); - - RenderFrameDeletedObserver delete_observer_rfh( - web_contents()->GetMainFrame()); - - // Activate the prerendered page. - NavigatePrimaryPage(kPrerenderingUrl1); - EXPECT_EQ(web_contents()->GetURL(), kPrerenderingUrl1); - - // Other PrerenderHost instances are deleted with the RFH. - delete_observer_rfh.WaitUntilDeleted(); - - // The prerender hosts should be consumed or destroyed for activation. - EXPECT_FALSE(HasHostForUrl(kPrerenderingUrl1)); - EXPECT_FALSE(HasHostForUrl(kPrerenderingUrl2)); - - // Activating the prerendered page should not issue a request. - EXPECT_EQ(GetRequestCount(kPrerenderingUrl1), 1); - EXPECT_EQ(GetRequestCount(kPrerenderingUrl2), 1); -} - -// END: Tests for the legacy prerender trigger of <link rel="prerender"> ======= - // Tests that the speculationrules trigger works. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, SpeculationRulesPrerender) { const GURL kInitialUrl = GetUrl("/empty.html"); @@ -479,6 +346,30 @@ EXPECT_FALSE(HasHostForUrl(kPrerenderingUrl)); } +IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, ResponseHeaders) { + const GURL kInitialUrl = GetUrl("/empty.html"); + const GURL kPrerenderingUrl = GetUrl("/set-header?X-Foo: bar"); + + // Navigate to an initial page. + ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); + ASSERT_EQ(web_contents()->GetURL(), kInitialUrl); + + // Start prerendering `kPrerenderingUrl` and check if `X-Foo` header is + // observed. + NavigationHandleObserver observer1(web_contents(), kPrerenderingUrl); + const int host_id = AddPrerender(kPrerenderingUrl); + ASSERT_NE(host_id, RenderFrameHost::kNoFrameTreeNodeId); + ASSERT_EQ(GetRequestCount(kPrerenderingUrl), 1); + EXPECT_TRUE(observer1.has_committed()); + EXPECT_EQ("bar", observer1.GetNormalizedResponseHeader("x-foo")); + + // Activate the page and check if `X-Foo` header is observed again. + NavigationHandleObserver observer2(web_contents(), kPrerenderingUrl); + NavigatePrimaryPage(kPrerenderingUrl); + EXPECT_TRUE(observer2.has_committed()); + EXPECT_EQ("bar", observer2.GetNormalizedResponseHeader("x-foo")); +} + // Tests that prerendering triggered by prerendered pages is deferred until // activation. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderChain) { @@ -2341,6 +2232,11 @@ "activated, initial", EvalJs(current_frame_host(), "getSessionStorageKeys()").ExtractString()); + // Speculative fix for the test flakiness (crbug.com/1216038), which may be + // caused by the delayed async IPC of Session Storage (StorageArea.Put()). + EXPECT_TRUE(ExecJs(shell()->web_contents(), + "new Promise(resolve => requestIdleCallback(resolve));")); + // Make sure that the initial renderer process is destroyed. So that the // initial renderer process will not be reused after the back forward // navigation below. @@ -2384,6 +2280,11 @@ "activated, initial", EvalJs(current_frame_host(), "getSessionStorageKeys()").ExtractString()); + // Speculative fix for the test flakiness (crbug.com/1216038), which may be + // caused by the delayed async IPC of Session Storage (StorageArea.Put()). + EXPECT_TRUE(ExecJs(shell()->web_contents(), + "new Promise(resolve => requestIdleCallback(resolve));")); + // Navigate back to the initial page. content::TestNavigationObserver observer(shell()->web_contents()); shell()->GoBackOrForward(-1); @@ -2503,6 +2404,11 @@ "activated, initial", EvalJs(current_frame_host(), "getSessionStorageKeys()").ExtractString()); + // Speculative fix for the test flakiness (crbug.com/1216038), which may be + // caused by the delayed async IPC of Session Storage (StorageArea.Put()). + EXPECT_TRUE(ExecJs(shell()->web_contents(), + "new Promise(resolve => requestIdleCallback(resolve));")); + // Navigate back to the initial page. shell()->GoBackOrForward(-1); WaitForLoadStop(shell()->web_contents());
diff --git a/content/browser/prerender/prerender_host.cc b/content/browser/prerender/prerender_host.cc index 91d9737..979e76b 100644 --- a/content/browser/prerender/prerender_host.cc +++ b/content/browser/prerender/prerender_host.cc
@@ -288,6 +288,18 @@ if (!created_navigation_handle) return false; + if (initial_navigation_id_.has_value()) { + // In usual code path, `initial_navigation_id_` should be set by + // PrerenderNavigationThrottle during `LoadURLWithParams` above. + DCHECK_EQ(*initial_navigation_id_, + created_navigation_handle->GetNavigationId()); + } else { + // In some exceptional code path, such as the navigation failed due to CSP + // violations, PrerenderNavigationThrottle didn't run at this point. So, + // set the ID here. + initial_navigation_id_ = created_navigation_handle->GetNavigationId(); + } + NavigationRequest* navigation_request = NavigationRequest::From(created_navigation_handle.get()); // The initial navigation in the prerender frame tree should not wait for @@ -386,4 +398,13 @@ observers_.RemoveObserver(observer); } +absl::optional<int64_t> PrerenderHost::GetInitialNavigationId() const { + return initial_navigation_id_; +} + +void PrerenderHost::SetInitialNavigationId(int64_t navigation_id) { + DCHECK(!initial_navigation_id_.has_value()); + initial_navigation_id_ = navigation_id; +} + } // namespace content
diff --git a/content/browser/prerender/prerender_host.h b/content/browser/prerender/prerender_host.h index 7da69d4..23774d8e 100644 --- a/content/browser/prerender/prerender_host.h +++ b/content/browser/prerender/prerender_host.h
@@ -119,6 +119,9 @@ void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); + absl::optional<int64_t> GetInitialNavigationId() const; + void SetInitialNavigationId(int64_t navigation_id); + url::Origin initiator_origin() const { return initiator_origin_; } int frame_tree_node_id() const { return frame_tree_node_id_; } @@ -157,6 +160,9 @@ // attempting to reserve the prerender host for a navigation. mojom::BeginNavigationParamsPtr begin_params_; mojom::CommonNavigationParamsPtr common_params_; + + // Holds the navigation ID for the main frame initial navigation. + absl::optional<int64_t> initial_navigation_id_; }; } // namespace content
diff --git a/content/browser/prerender/prerender_navigation_throttle.cc b/content/browser/prerender/prerender_navigation_throttle.cc index 4b238f4..fc95c2c 100644 --- a/content/browser/prerender/prerender_navigation_throttle.cc +++ b/content/browser/prerender/prerender_navigation_throttle.cc
@@ -58,25 +58,47 @@ DCHECK(frame_tree_node->IsMainFrame()); DCHECK(frame_tree_node->frame_tree()->is_prerendering()); + // Get the prerender host of the prerendering page. PrerenderHostRegistry* prerender_host_registry = frame_tree_node->current_frame_host() ->delegate() ->GetPrerenderHostRegistry(); + PrerenderHost* prerender_host = + prerender_host_registry->FindNonReservedHostById( + frame_tree_node->frame_tree_node_id()); + if (!prerender_host) { + // If there is no host, we are already reserved for activation. Just let the + // navigation proceed, since abandoning prerendering now might break the + // activation navigation and cancelling the request while continuing the + // activation will break compatibility. We also cannot defer because the + // activation machinery waits for the navigation to commit before + // activating. + // TODO(https://crbug.com/1198395): Somehow handle this, probably by + // deferring after support is added to activate while the main frame is + // still being navigated; or else cancelling prerendering. + DCHECK(prerender_host_registry->FindReservedHostById( + frame_tree_node->frame_tree_node_id())); + return PROCEED; + } - // Disallow navigation from a prerendering page and cancel prerendering. - RenderFrameHostImpl* initiator_render_frame_host_impl = - navigation_request->GetInitiatorFrameToken().has_value() - ? RenderFrameHostImpl::FromFrameToken( - navigation_request->GetInitiatorProcessID(), - navigation_request->GetInitiatorFrameToken().value()) - : nullptr; - if (initiator_render_frame_host_impl && - initiator_render_frame_host_impl->frame_tree()->is_prerendering()) { + // Navigation after the initial prerendering navigation are disallowed. + absl::optional<int64_t> initial_navigation_id = + prerender_host->GetInitialNavigationId(); + if (!initial_navigation_id.has_value()) { + // If the PrerenderHost has no initial navigation ID yet, this must be the + // initial one, so set it here. This throttle is responsible for setting it + // since the PrerenderHost obtains the NavigationRequest, which has the ID, + // only after the navigation throttles run. + prerender_host->SetInitialNavigationId( + navigation_request->GetNavigationId()); + } else if (*initial_navigation_id != navigation_request->GetNavigationId()) { + // If this is not the initial prerendering navigation, cancel the navigation + // and cancel prerendering. Same document navigation is exceptionally + // allowed but we do nothing here as throttles don't run against the same + // document navigation. It should just work. prerender_host_registry->AbandonHost( frame_tree_node->frame_tree_node_id(), PrerenderHost::FinalStatus::kMainFrameNavigation); - // TODO(https://crbug.com/1194414): Handle the case the prerendering page - // is reserved for activation. return CANCEL; } @@ -91,23 +113,6 @@ return CANCEL; } - // Get the prerender host of the prerendering page. - const PrerenderHost* prerender_host = - prerender_host_registry->FindNonReservedHostById( - frame_tree_node->frame_tree_node_id()); - if (!prerender_host) { - // If there is no host, we are already reserved for activation. Just let - // the navigation proceed, since cancelling it now might break the - // activation navigation. We also cannot defer because the activation - // machinery waits for the navigation to commit before activating. - // TODO(https://crbug.com/1198395): Somehow handle this, probably by - // deferring after support is added to activate while the main frame is - // still being navigated; or else cancelling prerendering. - DCHECK(prerender_host_registry->FindReservedHostById( - frame_tree_node->frame_tree_node_id())); - return PROCEED; - } - // Cancel prerendering if this is cross-origin prerendering, cross-origin // redirection during prerendering, or cross-origin navigation from a // prerendered page.
diff --git a/content/browser/renderer_host/code_cache_host_impl.cc b/content/browser/renderer_host/code_cache_host_impl.cc index 39cb9a7..22d70f3 100644 --- a/content/browser/renderer_host/code_cache_host_impl.cc +++ b/content/browser/renderer_host/code_cache_host_impl.cc
@@ -202,8 +202,12 @@ ? cache_storage_control_for_testing_ : render_process_host_->GetStoragePartition() ->GetCacheStorageControl(); + + // TODO(https://crbug.com/1199077): `CodeCacheHostImpl` will need to get the + // real StorageKey somehow. cache_storage_control->AddReceiver( - cross_origin_embedder_policy, mojo::NullRemote(), cache_storage_origin, + cross_origin_embedder_policy, mojo::NullRemote(), + blink::StorageKey(cache_storage_origin), storage::mojom::CacheStorageOwner::kCacheAPI, remote.BindNewPipeAndPassReceiver());
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc index 8974f4e..2adda5d1 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc
@@ -1894,6 +1894,8 @@ // Reset navigation handle timings. navigation_handle_timing_ = NavigationHandleTiming(); + + policy_container_navigation_bundle_->ResetForCrossDocumentRestart(); } void NavigationRequest::ResetStateForSiteInstanceChange() { @@ -2566,11 +2568,6 @@ early_hints.was_preload_link_header_received; early_hints_manager_ = std::move(early_hints.manager); - if (IsServedFromBackForwardCache()) { - response_head_ = - rfh_restored_from_back_forward_cache_->last_response_head()->Clone(); - } - bool is_mhtml_archive = response_head_->mime_type == "multipart/related" || response_head_->mime_type == "message/rfc822"; if (is_mhtml_archive) @@ -3372,8 +3369,29 @@ } auto loader_type = NavigationURLLoader::LoaderType::kRegular; - if (IsPageActivation()) + network::mojom::URLResponseHeadPtr cached_response_head = nullptr; + if (IsServedFromBackForwardCache()) { loader_type = NavigationURLLoader::LoaderType::kNoop; + DCHECK(rfh_restored_from_back_forward_cache_); + cached_response_head = + rfh_restored_from_back_forward_cache_->last_response_head()->Clone(); + } else if (IsPrerenderedPageActivation()) { + loader_type = NavigationURLLoader::LoaderType::kNoop; + DCHECK(prerender_frame_tree_node_id_.has_value()); + const network::mojom::URLResponseHeadPtr& last_response_head = + GetPrerenderHostRegistry() + .GetRenderFrameHostForReservedHost(*prerender_frame_tree_node_id_) + ->last_response_head(); + if (last_response_head) { + cached_response_head = last_response_head->Clone(); + } else { + // TODO(https://crbug.com/1216997): Support the case the initial + // navigation haven't received the response head at this point. + cached_response_head = network::mojom::URLResponseHead::New(); + cached_response_head->parsed_headers = + network::mojom::ParsedHeaders::New(); + } + } loader_ = NavigationURLLoader::Create( browser_context, partition, @@ -3396,7 +3414,7 @@ ->CreateURLLoaderNetworkObserverForNavigationRequest( frame_tree_node_->frame_tree_node_id()), NetworkServiceDevToolsObserver::MakeSelfOwned(frame_tree_node_), - std::move(interceptor)); + std::move(cached_response_head), std::move(interceptor)); DCHECK(!render_frame_host_); }
diff --git a/content/browser/renderer_host/policy_container_navigation_bundle.cc b/content/browser/renderer_host/policy_container_navigation_bundle.cc index 9713a7a5..d8e649db 100644 --- a/content/browser/renderer_host/policy_container_navigation_bundle.cc +++ b/content/browser/renderer_host/policy_container_navigation_bundle.cc
@@ -286,4 +286,9 @@ return std::move(host_); } +void PolicyContainerNavigationBundle::ResetForCrossDocumentRestart() { + host_ = nullptr; + delivered_policies_ = std::make_unique<PolicyContainerPolicies>(); +} + } // namespace content
diff --git a/content/browser/renderer_host/policy_container_navigation_bundle.h b/content/browser/renderer_host/policy_container_navigation_bundle.h index 188493dc..63459f0 100644 --- a/content/browser/renderer_host/policy_container_navigation_bundle.h +++ b/content/browser/renderer_host/policy_container_navigation_bundle.h
@@ -152,6 +152,12 @@ // previously. scoped_refptr<PolicyContainerHost> TakePolicyContainerHost() &&; + // Called by same-document navigation requests that need to be restarted as + // cross-document navigations. This happens when a same-document commit fails + // due to another navigation committing in the meantime. This resets the + // PolicyContainerNavigationBundle to the state when it was first created. + void ResetForCrossDocumentRestart(); + private: // Whether either of |ComputePolicies()| or |ComputePoliciesForError()| has // been called yet. @@ -194,7 +200,7 @@ // // See the comment on |SetIsOriginPotentiallyTrustworthy()| regarding this // member's |is_web_secure_context| field. - const std::unique_ptr<PolicyContainerPolicies> delivered_policies_; + std::unique_ptr<PolicyContainerPolicies> delivered_policies_; // Nullptr until |ComputePolicies()| or |ComputePoliciesForError()| is // called, then moved from by |TakePolicyContainerHost()|.
diff --git a/content/browser/renderer_host/policy_container_navigation_bundle_browsertest.cc b/content/browser/renderer_host/policy_container_navigation_bundle_browsertest.cc index da50480..288284ed 100644 --- a/content/browser/renderer_host/policy_container_navigation_bundle_browsertest.cc +++ b/content/browser/renderer_host/policy_container_navigation_bundle_browsertest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "content/browser/renderer_host/policy_container_host.h" #include "content/browser/renderer_host/policy_container_navigation_bundle.h" #include "content/browser/renderer_host/frame_tree_node.h" @@ -317,5 +318,37 @@ EXPECT_EQ(PublicUrl(), tab->GetLastCommittedURL()); } +// Verifies that the history policies are preserved on +// ResetForCrossDocumentRestart. +IN_PROC_BROWSER_TEST_F(PolicyContainerNavigationBundleBrowserTest, + ResetForCrossDocumentRestartHistoryPolicies) { + RenderFrameHostImpl* root = root_frame_host(); + + // First navigate to a local scheme with non-default policies. To do that, we + // first navigate to a document with a public address space, then have that + // document navigate itself to `about:blank`. The final blank document + // inherits its policies from the first document, and stores them in its frame + // navigation entry for restoring later. + EXPECT_TRUE(NavigateToURL(shell()->web_contents(), PublicUrl())); + EXPECT_TRUE(NavigateToURLFromRenderer(root, AboutBlankUrl())); + + PolicyContainerNavigationBundle bundle( + nullptr, nullptr, GetLastCommittedFrameNavigationEntry()); + + std::unique_ptr<PolicyContainerPolicies> history_policies = + bundle.HistoryPolicies()->Clone(); + + bundle.ComputePolicies(GURL("http://foo.test")); + + EXPECT_EQ(bundle.FinalPolicies(), PolicyContainerPolicies()); + + bundle.ResetForCrossDocumentRestart(); + EXPECT_THAT(bundle.HistoryPolicies(), Pointee(Eq(ByRef(*history_policies)))); + + bundle.ComputePolicies(AboutBlankUrl()); + + EXPECT_EQ(bundle.FinalPolicies(), *history_policies); +} + } // namespace } // namespace content
diff --git a/content/browser/renderer_host/policy_container_navigation_bundle_unittest.cc b/content/browser/renderer_host/policy_container_navigation_bundle_unittest.cc index 281d5ff..a4572c5 100644 --- a/content/browser/renderer_host/policy_container_navigation_bundle_unittest.cc +++ b/content/browser/renderer_host/policy_container_navigation_bundle_unittest.cc
@@ -559,5 +559,50 @@ EXPECT_THAT(bundle.ParentPolicies(), Pointee(Eq(ByRef(*parent_policies)))); } +// Verifies that the parent policies are preserved on +// ResetForCrossDocumentRestart. +TEST_F(PolicyContainerNavigationBundleTest, + ResetForCrossDocumentRestartParentPolicies) { + std::unique_ptr<PolicyContainerPolicies> parent_policies = MakeTestPolicies(); + + TestRenderFrameHost* parent = contents()->GetMainFrame(); + parent->SetPolicyContainerHost(NewHost(parent_policies->Clone())); + + PolicyContainerNavigationBundle bundle(parent, nullptr, nullptr); + bundle.ComputePolicies(GURL("https://foo.test")); + EXPECT_EQ(bundle.FinalPolicies(), PolicyContainerPolicies()); + + bundle.ResetForCrossDocumentRestart(); + EXPECT_THAT(bundle.ParentPolicies(), Pointee(Eq(ByRef(*parent_policies)))); + bundle.ComputePolicies(AboutSrcdocUrl()); + + EXPECT_EQ(bundle.FinalPolicies(), *parent_policies); +} + +// Verifies that the initiator policies are preserved on +// ResetForCrossDocumentRestart. +TEST_F(PolicyContainerNavigationBundleTest, + ResetForCrossDocumentRestartInitiatorPolicies) { + std::unique_ptr<PolicyContainerPolicies> initiator_policies = + MakeTestPolicies(); + + TestRenderFrameHost* initiator = contents()->GetMainFrame(); + initiator->SetPolicyContainerHost(NewHost(initiator_policies->Clone())); + + // Force implicit conversion from LocalFrameToken to UnguessableToken. + const blink::LocalFrameToken& token = initiator->GetFrameToken(); + PolicyContainerNavigationBundle bundle(nullptr, &token, nullptr); + + bundle.ComputePolicies(GURL("https://foo.test")); + EXPECT_EQ(bundle.FinalPolicies(), PolicyContainerPolicies()); + + bundle.ResetForCrossDocumentRestart(); + EXPECT_THAT(bundle.InitiatorPolicies(), + Pointee(Eq(ByRef(*initiator_policies)))); + bundle.ComputePolicies(AboutBlankUrl()); + + EXPECT_EQ(bundle.FinalPolicies(), *initiator_policies); +} + } // namespace } // namespace content
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index d90b900..491c8b4 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -367,6 +367,11 @@ const void* const kRenderFrameHostAndroidKey = &kRenderFrameHostAndroidKey; #endif // OS_ANDROID +// Causes RenderAccessibilityHost HandleAXEvents messages to be handled with +// minimal copying of the data. +const base::Feature kRenderAccessibilityHostAvoidCopying{ + "RenderAccessibilityHostAvoidCopying", base::FEATURE_DISABLED_BY_DEFAULT}; + // The next value to use for the accessibility reset token. int g_next_accessibility_reset_token = 1; @@ -6427,10 +6432,12 @@ } void RenderFrameHostImpl::HandleAXEvents( - const std::vector<ui::AXTreeUpdate>& updates, - const std::vector<ui::AXEvent>& events, + mojom::AXUpdatesAndEventsPtr updates_and_events, int32_t reset_token, HandleAXEventsCallback callback) { + TRACE_EVENT0("accessibility", "RenderFrameHostImpl::HandleAXEvents"); + SCOPED_UMA_HISTOGRAM_TIMER("Accessibility.Performance.HandleAXEvents"); + // Don't process this IPC if either we're waiting on a reset and this IPC // doesn't have the matching token ID, or if we're not waiting on a reset but // this message includes a reset token. @@ -6451,14 +6458,28 @@ AXEventNotificationDetails details; details.ax_tree_id = GetAXTreeID(); - details.events = events; - details.updates.resize(updates.size()); - for (size_t i = 0; i < updates.size(); ++i) { - details.updates[i] = updates[i]; - if (updates[i].has_tree_data) { - ax_tree_data_ = updates[i].tree_data; - details.updates[i].tree_data = GetAXTreeData(); + // TODO(1213848): Remove the false path when the experiment is complete. + if (base::FeatureList::IsEnabled(kRenderAccessibilityHostAvoidCopying)) { + details.events = std::move(updates_and_events->events); + + details.updates = std::move(updates_and_events->updates); + for (auto& update : details.updates) { + if (update.has_tree_data) { + ax_tree_data_ = update.tree_data; + update.tree_data = GetAXTreeData(); + } + } + } else { + details.events = updates_and_events->events; + + details.updates.resize(updates_and_events->updates.size()); + for (size_t i = 0; i < updates_and_events->updates.size(); ++i) { + details.updates[i] = updates_and_events->updates[i]; + if (updates_and_events->updates[i].has_tree_data) { + ax_tree_data_ = updates_and_events->updates[i].tree_data; + details.updates[i].tree_data = GetAXTreeData(); + } } }
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h index b6c9e40..6210f6b 100644 --- a/content/browser/renderer_host/render_frame_host_impl.h +++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -2387,8 +2387,7 @@ #endif // mojom::RenderAccessibilityHost: - void HandleAXEvents(const std::vector<ui::AXTreeUpdate>& updates, - const std::vector<ui::AXEvent>& events, + void HandleAXEvents(mojom::AXUpdatesAndEventsPtr updates_and_events, int32_t reset_token, HandleAXEventsCallback callback) override; void HandleAXLocationChanges(
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index b1ccf7dd7..f0b0a33 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2113,9 +2113,13 @@ const url::Origin& origin, mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + + // TODO(https://crbug.com/1199077): Pass the real StorageKey into this + // function directly. storage_partition_impl_->GetCacheStorageControl()->AddReceiver( - cross_origin_embedder_policy, std::move(coep_reporter_remote), origin, - storage::mojom::CacheStorageOwner::kCacheAPI, std::move(receiver)); + cross_origin_embedder_policy, std::move(coep_reporter_remote), + blink::StorageKey(origin), storage::mojom::CacheStorageOwner::kCacheAPI, + std::move(receiver)); } void RenderProcessHostImpl::BindIndexedDB( @@ -2638,7 +2642,9 @@ // There should be at most one CodeCacheHostImpl for any given // RenderProcessHost. - DCHECK(code_cache_host_receivers_.empty()); + if (!code_cache_host_receivers_.empty()) { + mojo::ReportBadMessage("CodeCacheHost is already bound"); + } // Create a new CodeCacheHostImpl and bind it to the given receiver. auto code_cache_host = std::make_unique<CodeCacheHostImpl>(
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc index 8c3be7d..92b7985 100644 --- a/content/browser/service_worker/service_worker_browsertest.cc +++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -2126,7 +2126,7 @@ network::CrossOriginEmbedderPolicy cross_origin_embedder_policy; cache_storage_control->AddReceiver( cross_origin_embedder_policy, mojo::NullRemote(), - url::Origin::Create(origin), + blink::StorageKey(url::Origin::Create(origin)), storage::mojom::CacheStorageOwner::kCacheAPI, cache_storage_remote.InitWithNewPipeAndPassReceiver()); @@ -2400,17 +2400,19 @@ const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy, mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> coep_reporter_remote, - const url::Origin& origin, + const blink::StorageKey& storage_key, storage::mojom::CacheStorageOwner owner, mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) override { - // The CodeCacheHostImpl should not try to add a receiver if the origin is - // bad. + // The CodeCacheHostImpl should not try to add a receiver if the StorageKey + // is bad. NOTREACHED(); } - void DeleteForOrigin(const url::Origin& origin) override { NOTREACHED(); } - void GetAllOriginsInfo( - storage::mojom::CacheStorageControl::GetAllOriginsInfoCallback callback) - override { + void DeleteForStorageKey(const blink::StorageKey& storage_key) override { + NOTREACHED(); + } + void GetAllStorageKeysInfo( + storage::mojom::CacheStorageControl::GetAllStorageKeysInfoCallback + callback) override { NOTREACHED(); } void AddObserver(mojo::PendingRemote<storage::mojom::CacheStorageObserver>
diff --git a/content/common/background_fetch/background_fetch_types.cc b/content/common/background_fetch/background_fetch_types.cc index 499e4ee..8a4561e 100644 --- a/content/common/background_fetch/background_fetch_types.cc +++ b/content/common/background_fetch/background_fetch_types.cc
@@ -41,7 +41,8 @@ CloneSerializedBlob(response->side_data_blob_for_cache_put), mojo::Clone(response->parsed_headers), response->connection_info, response->alpn_negotiated_protocol, response->was_fetched_via_spdy, - response->has_range_requested, response->auth_challenge_info); + response->has_range_requested, response->auth_challenge_info, + response->request_include_credentials); } // static
diff --git a/content/common/render_accessibility.mojom b/content/common/render_accessibility.mojom index 3fde013..fffb06e9 100644 --- a/content/common/render_accessibility.mojom +++ b/content/common/render_accessibility.mojom
@@ -36,6 +36,13 @@ int32 hit_node_id; }; +// Structure containing the large parameters to HandleAXEvents, that allows them +// to be moved rather than copied. +struct AXUpdatesAndEvents { + array<ax.mojom.AXTreeUpdate> updates; + array<ax.mojom.AXEvent> events; +}; + // Interface for accessibility messages sent from the renderer to the browser, // implemented by RenderFrameHostImpl in the browser process. interface RenderAccessibilityHost { @@ -56,8 +63,8 @@ // become arbitrarily large. // TODO(crbug.com/1088484) tracks work to improve the payload size. [UnlimitedSize] - HandleAXEvents(array<ax.mojom.AXTreeUpdate> updates, - array<ax.mojom.AXEvent> events, int32 reset_token) => (); + HandleAXEvents(AXUpdatesAndEvents events_and_updates, + int32 reset_token) => (); // Sent to update the browser of the location of accessibility objects. // Similar to HandleAXEvents, the message size is also unbounded (though in
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc index c0ac9feb..14042cf 100644 --- a/content/public/common/content_features.cc +++ b/content/public/common/content_features.cc
@@ -206,6 +206,11 @@ const base::Feature kDesktopCaptureChangeSource{ "DesktopCaptureChangeSource", base::FEATURE_ENABLED_BY_DEFAULT}; +// Adds a tab strip to PWA windows. +// TODO(crbug.com/897314): Enable this feature. +const base::Feature kDesktopPWAsTabStrip{"DesktopPWAsTabStrip", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Enable document policy for configuring and restricting feature behavior. const base::Feature kDocumentPolicy{"DocumentPolicy", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h index fca527ac..62f90078 100644 --- a/content/public/common/content_features.h +++ b/content/public/common/content_features.h
@@ -52,6 +52,7 @@ CONTENT_EXPORT extern const base::Feature kCriticalClientHint; CONTENT_EXPORT extern const base::Feature kDataSaverHoldback; CONTENT_EXPORT extern const base::Feature kDesktopCaptureChangeSource; +CONTENT_EXPORT extern const base::Feature kDesktopPWAsTabStrip; CONTENT_EXPORT extern const base::Feature kDocumentPolicy; CONTENT_EXPORT extern const base::Feature kDocumentPolicyNegotiation; CONTENT_EXPORT extern const base::Feature kEarlyHintsPreloadForNavigation;
diff --git a/content/public/test/test_navigation_observer.cc b/content/public/test/test_navigation_observer.cc index 65942c47..c962af0 100644 --- a/content/public/test/test_navigation_observer.cc +++ b/content/public/test/test_navigation_observer.cc
@@ -85,17 +85,6 @@ ignore_uncommitted_navigations) {} TestNavigationObserver::TestNavigationObserver( - const GURL& target_url, - MessageLoopRunner::QuitMode quit_mode, - bool ignore_uncommitted_navigations) - : TestNavigationObserver(nullptr, - 1 /* num_of_navigations */, - target_url, - absl::nullopt /* target_error */, - quit_mode, - ignore_uncommitted_navigations) {} - -TestNavigationObserver::TestNavigationObserver( WebContents* web_contents, net::Error target_error, MessageLoopRunner::QuitMode quit_mode, @@ -107,6 +96,17 @@ quit_mode, ignore_uncommitted_navigations) {} +TestNavigationObserver::TestNavigationObserver( + const GURL& target_url, + MessageLoopRunner::QuitMode quit_mode, + bool ignore_uncommitted_navigations) + : TestNavigationObserver(nullptr, + 1 /* num_of_navigations */, + target_url, + absl::nullopt /* target_error */, + quit_mode, + ignore_uncommitted_navigations) {} + TestNavigationObserver::~TestNavigationObserver() { StopWatchingNewWebContents(); }
diff --git a/content/public/test/test_navigation_observer.h b/content/public/test/test_navigation_observer.h index 473944a..b59a4f13 100644 --- a/content/public/test/test_navigation_observer.h +++ b/content/public/test/test_navigation_observer.h
@@ -43,17 +43,17 @@ MessageLoopRunner::QuitMode::IMMEDIATE, bool ignore_uncommitted_navigations = true); // Create and register a new TestNavigationObserver that will wait for - // |target_url| to complete loading or for a finished navigation to - // |target_url|. - explicit TestNavigationObserver(const GURL& target_url, + // a navigation with |target_error|. + explicit TestNavigationObserver(WebContents* web_contents, + net::Error target_error, MessageLoopRunner::QuitMode quit_mode = MessageLoopRunner::QuitMode::IMMEDIATE, bool ignore_uncommitted_navigations = true); // Create and register a new TestNavigationObserver that will wait for - // a navigation with |target_error|. - explicit TestNavigationObserver(WebContents* web_contents, - net::Error target_error, + // |target_url| to complete loading or for a finished navigation to + // |target_url|. + explicit TestNavigationObserver(const GURL& target_url, MessageLoopRunner::QuitMode quit_mode = MessageLoopRunner::QuitMode::IMMEDIATE, bool ignore_uncommitted_navigations = true);
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc index 7373213..938e00a 100644 --- a/content/renderer/accessibility/render_accessibility_impl.cc +++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -800,8 +800,10 @@ pending_events_.clear(); // The serialized list of updates and events to send to the browser. - std::vector<ui::AXTreeUpdate> updates; - std::vector<ui::AXEvent> events; + mojom::AXUpdatesAndEventsPtr updates_and_events = + mojom::AXUpdatesAndEvents::New(); + std::vector<ui::AXTreeUpdate>& updates = updates_and_events->updates; + std::vector<ui::AXEvent>& events = updates_and_events->events; // Keep track of nodes in the tree that need to be updated. std::vector<DirtyObject> dirty_objects = dirty_objects_; @@ -1017,9 +1019,12 @@ } } + if (image_annotation_debugging_) + AddImageAnnotationDebuggingAttributes(updates); + event_schedule_status_ = EventScheduleStatus::kWaitingForAck; render_accessibility_manager_->HandleAccessibilityEvents( - updates, events, reset_token_, + std::move(updates_and_events), reset_token_, base::BindOnce(&RenderAccessibilityImpl::OnAccessibilityEventsHandled, weak_factory_for_pending_events_.GetWeakPtr())); reset_token_ = 0; @@ -1036,8 +1041,6 @@ // If any interactive events come in, the batch will be processed immediately. event_schedule_mode_ = EventScheduleMode::kDeferEvents; - if (image_annotation_debugging_) - AddImageAnnotationDebuggingAttributes(updates); // Measure the amount of time spent in this function. Keep track of the // maximum within a time interval so we can upload UKM.
diff --git a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc index f60eefa1..387ef45dbb 100644 --- a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc +++ b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
@@ -170,12 +170,12 @@ content::mojom::RenderAccessibilityHost>(std::move(handle))); } - void HandleAXEvents(const std::vector<::ui::AXTreeUpdate>& updates, - const std::vector<::ui::AXEvent>& events, + void HandleAXEvents(mojom::AXUpdatesAndEventsPtr updates_and_events, int32_t reset_token, HandleAXEventsCallback callback) override { - handled_updates_.insert(handled_updates_.end(), updates.begin(), - updates.end()); + handled_updates_.insert(handled_updates_.end(), + updates_and_events->updates.begin(), + updates_and_events->updates.end()); std::move(callback).Run(); }
diff --git a/content/renderer/accessibility/render_accessibility_manager.cc b/content/renderer/accessibility/render_accessibility_manager.cc index 8082be1..05104025 100644 --- a/content/renderer/accessibility/render_accessibility_manager.cc +++ b/content/renderer/accessibility/render_accessibility_manager.cc
@@ -91,12 +91,11 @@ } void RenderAccessibilityManager::HandleAccessibilityEvents( - const std::vector<ui::AXTreeUpdate>& updates, - const std::vector<ui::AXEvent>& events, + mojom::AXUpdatesAndEventsPtr updates_and_events, int32_t reset_token, mojom::RenderAccessibilityHost::HandleAXEventsCallback callback) { GetOrCreateRemoteRenderAccessibilityHost()->HandleAXEvents( - updates, events, reset_token, std::move(callback)); + std::move(updates_and_events), reset_token, std::move(callback)); } void RenderAccessibilityManager::HandleLocationChanges(
diff --git a/content/renderer/accessibility/render_accessibility_manager.h b/content/renderer/accessibility/render_accessibility_manager.h index ac6c717..35f4968 100644 --- a/content/renderer/accessibility/render_accessibility_manager.h +++ b/content/renderer/accessibility/render_accessibility_manager.h
@@ -69,8 +69,7 @@ // Communication with the browser process. void HandleAccessibilityEvents( - const std::vector<ui::AXTreeUpdate>& updates, - const std::vector<ui::AXEvent>& events, + mojom::AXUpdatesAndEventsPtr updates_and_events, int32_t reset_token, mojom::RenderAccessibilityHost::HandleAXEventsCallback callback); void HandleLocationChanges(std::vector<mojom::LocationChangesPtr> changes);
diff --git a/docs/speed/bot_health_sheriffing/how_to_handle_a_new_problem.md b/docs/speed/bot_health_sheriffing/how_to_handle_a_new_problem.md index 5931cf3..1916347 100644 --- a/docs/speed/bot_health_sheriffing/how_to_handle_a_new_problem.md +++ b/docs/speed/bot_health_sheriffing/how_to_handle_a_new_problem.md
@@ -38,7 +38,7 @@ * The revision range at which the story started failing (most easily found [through the flakiness dashboard](https://chromium.googlesource.com/chromium/src/+/main/docs/speed/bot_health_sheriffing/what_test_is_failing.md)) -Once the bug is filed, [disable the failing story](https://chromium.googlesource.com/chromium/src/+/master/docs/speed/bot_health_sheriffing/how_to_disable_a_story.md) and [launch a bisect over the revision range of the breakage](https://chromium.googlesource.com/chromium/src/+/main/docs/speed/bot_health_sheriffing/how_to_launch_a_functional_bisect.md), and snooze the alert for 24h to give the bisect time to finish. Once the story is disabled, lower its priority to P2 and assign the bug to the benchmark owner. +Once the bug is filed, [disable the failing story](https://chromium.googlesource.com/chromium/src/+/main/docs/speed/bot_health_sheriffing/how_to_disable_a_story.md) and [launch a bisect over the revision range of the breakage](https://chromium.googlesource.com/chromium/src/+/main/docs/speed/bot_health_sheriffing/how_to_launch_a_functional_bisect.md), and snooze the alert for 24h to give the bisect time to finish. Once the story is disabled, lower its priority to P2 and assign the bug to the benchmark owner. **[Canonical example](https://bugs.cromium.org/p/chromium/issues/detail?id=809063)**
diff --git a/docs/ui/android/browser_controls.md b/docs/ui/android/browser_controls.md index 5b9a1a4..e1dbc2a 100644 --- a/docs/ui/android/browser_controls.md +++ b/docs/ui/android/browser_controls.md
@@ -16,15 +16,15 @@ There are several classes that are used to draw composited textures: - [ViewResourceFrameLayout](https://source.chromium.org/chromium/chromium/src/+/main:components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ViewResourceFrameLayout.java): A view group that can easily be transformed into a texture used by the compositor system. -- [SceneLayer (native)](https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/ui/android/layouts/scene_layer.h): A wrapper that provides a [cc::Layer](https://source.chromium.org/chromium/chromium/src/+/main:cc/layers/layer.h) and dictates how that layout is supposed to interact with the layout system. +- [SceneLayer (native)](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/layouts/scene_layer.h): A wrapper that provides a [cc::Layer](https://source.chromium.org/chromium/chromium/src/+/main:cc/layers/layer.h) and dictates how that layout is supposed to interact with the layout system. - [SceneLayer](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/layouts/java/src/org/chromium/chrome/browser/layouts/scene_layer/SceneLayer.java): The Java representation of SceneLayer. - [SceneOverlay](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/layouts/java/src/org/chromium/chrome/browser/layouts/SceneOverlay.java): An interface that allows for other texture-like things to be drawn on a layout without being a layout itself. - [SceneOverlayLayer](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/layouts/java/src/org/chromium/chrome/browser/layouts/scene_layer/SceneOverlayLayer.java): Extends SceneLayer for SceneOverlay. - [LayoutManager](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/layouts/java/src/org/chromium/chrome/browser/layouts/LayoutManager.java): The class that manages the Layouts. In the browser controls context, this is the manager that adds the SceneOverlay to the Layout to be drawn. -The Android view is wrapped in a `ViewResourceFrameLayout`. If we look at [bottom_control_container.xml](https://source.chromium.org/chromium/chromium/src/+/master:chrome/android/java/res/layout/bottom_control_container.xml), the xml layout for the bottom controls, the views are wrapped in [ScrollingBottomViewResourceFrameLayout](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewResourceFrameLayout.java). +The Android view is wrapped in a `ViewResourceFrameLayout`. If we look at [bottom_control_container.xml](https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/res/layout/bottom_control_container.xml), the xml layout for the bottom controls, the views are wrapped in [ScrollingBottomViewResourceFrameLayout](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewResourceFrameLayout.java). -A scene layer ([ScrollingBottomViewSceneLayer](https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewSceneLayer.java) and its native counterpart [scrolling_bottom_view_scene_layer.cc](https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/android/compositor/scene_layer/scrolling_bottom_view_scene_layer.cc) in this example) is responsible for creating a compositor layer using the view resource. [LayoutManager](https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java) adds the scene layer to the global layout. +A scene layer ([ScrollingBottomViewSceneLayer](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewSceneLayer.java) and its native counterpart [scrolling_bottom_view_scene_layer.cc](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/android/compositor/scene_layer/scrolling_bottom_view_scene_layer.cc) in this example) is responsible for creating a compositor layer using the view resource. [LayoutManager](https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java) adds the scene layer to the global layout. See these example CLs [adding a scene layer](https://chromium-review.googlesource.com/c/chromium/src/+/1769631) and [adding the Android view to use as a resource](https://chromium-review.googlesource.com/c/chromium/src/+/1809813). @@ -44,11 +44,11 @@ ### Browser controls in the browser process -[BrowserControlsManager](https://source.chromium.org/chromium/chromium/src/+/master:chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java) is the class that manages the browser controls properties and responds to the browser controls related information that comes from the renderer process. It provides an interface, [BrowserControlsStateProvider](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java), that can be used to get/set browser controls properties. BrowserControlsStateProvider in addition provides an Observer interface for observing changes in browser controls properties and offsets. +[BrowserControlsManager](https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java) is the class that manages the browser controls properties and responds to the browser controls related information that comes from the renderer process. It provides an interface, [BrowserControlsStateProvider](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/browser_controls/android/java/src/org/chromium/chrome/browser/browser_controls/BrowserControlsStateProvider.java), that can be used to get/set browser controls properties. BrowserControlsStateProvider in addition provides an Observer interface for observing changes in browser controls properties and offsets. ### Browser controls in the renderer: cc and Blink -There are 2 classes that are responsible for calculating the browser controls offsets in the renderer: [cc::BrowserControlsOffsetManager](https://source.chromium.org/chromium/chromium/src/+/master:cc/input/browser_controls_offset_manager.cc) and [blink::BrowserControls](https://source.chromium.org/chromium/chromium/src/+/main:cc/input/browser_controls_offset_manager.cc). These two classes are almost identical in behavior except for some small differences: +There are 2 classes that are responsible for calculating the browser controls offsets in the renderer: [cc::BrowserControlsOffsetManager](https://source.chromium.org/chromium/chromium/src/+/main:cc/input/browser_controls_offset_manager.cc) and [blink::BrowserControls](https://source.chromium.org/chromium/chromium/src/+/main:cc/input/browser_controls_offset_manager.cc). These two classes are almost identical in behavior except for some small differences: - Both classes calculate browser controls offsets during scroll events. Both classes have functions ScrollBegin(), ScrollBy(), and ScrollEnd() that notify the class of the changes in scrolling state. The biggest difference here is that cc::BrowserControlsOffsetManager calculates the offsets when the scroll happens on the cc thread (or impl thread, more on that in [this doc](https://source.chromium.org/chromium/chromium/src/+/main:docs/how_cc_works.md)) while blink::BrowserControls calculates the offsets for main thread scrolling. Today, most of the scrolling happens on the impl thread and the main thread scrolling is used only as a fallback. - Animations are only handled by `cc::BrowserControlsOffsetManager`. Animation in this context means changing the browser controls offsets gradually without user action. There are two types of animations that are handled by this class:
diff --git a/docs/ui/create/examples/login_dialog.md b/docs/ui/create/examples/login_dialog.md index e8d63f7..10f9c15 100644 --- a/docs/ui/create/examples/login_dialog.md +++ b/docs/ui/create/examples/login_dialog.md
@@ -158,7 +158,7 @@ row for a given field in our form. The call to [`SetAssociatedLabel()`](https://source.chromium.org/chromium/chromium/src/+/main:ui/views/controls/textfield/textfield.h;l=250;drc=291180454e079aa5c3677dc3f3eaf619a1cf1d42) sets the accessible label relationship between the -[`Label`](https://source.chromium.org/chromium/chromium/src/+/master:ui/views/controls/label.h) and the [`Textfield`](https://source.chromium.org/chromium/chromium/src/+/main:ui/views/controls/textfield/textfield.h) +[`Label`](https://source.chromium.org/chromium/chromium/src/+/main:ui/views/controls/label.h) and the [`Textfield`](https://source.chromium.org/chromium/chromium/src/+/main:ui/views/controls/textfield/textfield.h) and copies the `Label`’s accessible name to the `Textfield`.
diff --git a/google_apis/gaia/gaia_constants.cc b/google_apis/gaia/gaia_constants.cc index b54f42a..09b5e18 100644 --- a/google_apis/gaia/gaia_constants.cc +++ b/google_apis/gaia/gaia_constants.cc
@@ -119,6 +119,8 @@ // OAuth2 scope for access to Drive. const char kDriveOAuth2Scope[] = "https://www.googleapis.com/auth/drive"; + +// The scope required for an access token in order to query ItemSuggest. const char kDriveReadOnlyOAuth2Scope[] = "https://www.googleapis.com/auth/drive.readonly";
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd index 146b9fe..1976425 100644 --- a/ios/chrome/app/strings/ios_strings.grd +++ b/ios/chrome/app/strings/ios_strings.grd
@@ -876,6 +876,9 @@ <message name="IDS_IOS_FIRST_RUN_SYNC_SCREEN_CONTENT_MINOR_MODE" desc="Content string on the sync screen presented to the user on First Run for minor and family link users [iOS only]"> Google may use your history to personalize Search and other Google services </message> + <message name="IDS_IOS_FIRST_RUN_SYNC_SCREEN_ADVANCE_SETTINGS" desc="Label of the button which open advance sync settings screen [iOS only]"> + Choose What to Sync + </message> <message name="IDS_IOS_FIRST_RUN_SYNC_SCREEN_PRIMARY_ACTION" desc="The primary action string on the sync screen presented to the user on First Run [iOS only]"> Turn on Sync </message> @@ -886,7 +889,7 @@ Sync your passwords, history, and more on all your devices </message> <message name="IDS_IOS_FIRST_RUN_SYNC_SCREEN_TITLE" desc="Title on the sync screen presented to the user on First Run [iOS only]"> - Keep Your Devices in Sync + Keep Your Devices in Sync </message> <message name="IDS_IOS_FIRST_RUN_WELCOME_SCREEN_ACCEPT_BUTTON" desc="The label on the continue button of the welcome screen presented to the user on First Run [iOS only]"> Accept and Continue
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_SYNC_SCREEN_ADVANCE_SETTINGS.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_SYNC_SCREEN_ADVANCE_SETTINGS.png.sha1 new file mode 100644 index 0000000..9ea4746 --- /dev/null +++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_SYNC_SCREEN_ADVANCE_SETTINGS.png.sha1
@@ -0,0 +1 @@ +7c24e40b2126f0c5990eb86b97e50e95020cc973 \ No newline at end of file
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet/bottom_sheet_presentation_controller.h b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet/bottom_sheet_presentation_controller.h index 462100a..a564c6f5 100644 --- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet/bottom_sheet_presentation_controller.h +++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet/bottom_sheet_presentation_controller.h
@@ -10,25 +10,11 @@ @class BottomSheetNavigationController; @class BottomSheetPresentationController; -// Presentation protocol for BottomSheetPresentationController. -@protocol BottomSheetPresentationControllerPresentationDelegate <NSObject> - -// Called when the bottom sheet view controller needs to be dismissed. -- (void)bottomSheetPresentationControllerDismissViewController: - (BottomSheetPresentationController*)controller; - -@end - // Presentation controller to present BottomSheetNavigationController from the // bottom of the screen. // Related to BottomSheetNavigationController. @interface BottomSheetPresentationController : UIPresentationController -// Presentation delegate. -@property(nonatomic, weak) - id<BottomSheetPresentationControllerPresentationDelegate> - presentationDelegate; - - (instancetype)initWithBottomSheetNavigationController: (BottomSheetNavigationController*)viewcontroller presentingViewController:
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet/bottom_sheet_presentation_controller.mm b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet/bottom_sheet_presentation_controller.mm index ac6708c..3af9436 100644 --- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet/bottom_sheet_presentation_controller.mm +++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet/bottom_sheet_presentation_controller.mm
@@ -73,16 +73,6 @@ [self.containerView addSubview:self.navigationController.backgroundDimmerView]; - // Add close button view. - UIButton* closeButton = - [AccessibilityCloseMenuButton buttonWithType:UIButtonTypeCustom]; - [closeButton addTarget:self - action:@selector(closeButtonAction:) - forControlEvents:UIControlEventTouchUpInside]; - closeButton.translatesAutoresizingMaskIntoConstraints = NO; - [self.containerView addSubview:closeButton]; - AddSameConstraints(self.containerView, closeButton); - // Add presented view controller. [self.containerView addSubview:self.presentedViewController.view]; CGRect presentedFrame = [self frameOfPresentedViewInContainerView]; @@ -135,12 +125,4 @@ [self.navigationController didUpdateControllerViewFrame]; } -#pragma mark - Private - -// Closes the bottom sheet. -- (void)closeButtonAction:(id)sender { - [self.presentationDelegate - bottomSheetPresentationControllerDismissViewController:self]; -} - @end
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm index 801c754..b41e51f 100644 --- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm +++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm
@@ -31,7 +31,6 @@ #endif @interface ConsistencyPromoSigninCoordinator () < - BottomSheetPresentationControllerPresentationDelegate, ConsistencyAccountChooserCoordinatorDelegate, ConsistencyDefaultAccountCoordinatorDelegate, IdentityManagerObserverBridgeDelegate, @@ -290,13 +289,6 @@ DCHECK(self.authenticationService->IsAuthenticated()); } -#pragma mark - BottomSheetPresentationControllerPresentationDelegate - -- (void)bottomSheetPresentationControllerDismissViewController: - (BottomSheetPresentationController*)controller { - [self dismissNavigationViewController]; -} - #pragma mark - ConsistencyAccountChooserCoordinatorDelegate - (void)consistencyAccountChooserCoordinatorChromeIdentitySelected: @@ -451,12 +443,9 @@ (UIViewController*)presentingViewController sourceViewController:(UIViewController*)source { DCHECK_EQ(self.navigationController, presentedViewController); - BottomSheetPresentationController* controller = - [[BottomSheetPresentationController alloc] - initWithBottomSheetNavigationController:self.navigationController - presentingViewController:presentingViewController]; - controller.presentationDelegate = self; - return controller; + return [[BottomSheetPresentationController alloc] + initWithBottomSheetNavigationController:self.navigationController + presentingViewController:presentingViewController]; } @end
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_promo_coordinator.mm b/ios/chrome/browser/ui/default_promo/default_browser_promo_coordinator.mm index 380f8ab..da1c36e1 100644 --- a/ios/chrome/browser/ui/default_promo/default_browser_promo_coordinator.mm +++ b/ios/chrome/browser/ui/default_promo/default_browser_promo_coordinator.mm
@@ -104,6 +104,7 @@ } - (void)confirmationAlertSecondaryAction { + LogUserInteractionWithFullscreenPromo(); if (IsInRemindMeLaterGroup()) { if (self.defaultBrowerPromoViewController.tertiaryActionAvailable) { // When the "Remind Me Later" button is visible, it is the secondary @@ -118,13 +119,11 @@ CANCEL]; base::RecordAction(base::UserMetricsAction( "IOS.DefaultBrowserFullscreenPromo.Dismissed")); - LogUserInteractionWithFullscreenPromo(); } } else { [self logDefaultBrowserFullscreenPromoHistogramForAction:CANCEL]; base::RecordAction( base::UserMetricsAction("IOS.DefaultBrowserFullscreenPromo.Dismissed")); - LogUserInteractionWithFullscreenPromo(); } [self.handler hidePromo]; }
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm index f2d34ff..cf53939 100644 --- a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm +++ b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm
@@ -389,7 +389,10 @@ LogNonModalPromoAction(NonModalPromoAction::kAppear, self.promoTypeForMetrics, UserInteractionWithNonModalPromoCount()); self.promoShownTime = base::TimeTicks::Now(); - [self startDismissPromoTimer]; + + if (!UIAccessibilityIsVoiceOverRunning()) { + [self startDismissPromoTimer]; + } } - (void)startDismissPromoTimer {
diff --git a/ios/chrome/browser/ui/first_run/first_run_coordinator.mm b/ios/chrome/browser/ui/first_run/first_run_coordinator.mm index 8e87f98..ea8ee289 100644 --- a/ios/chrome/browser/ui/first_run/first_run_coordinator.mm +++ b/ios/chrome/browser/ui/first_run/first_run_coordinator.mm
@@ -10,6 +10,8 @@ #include "base/notreached.h" #include "ios/chrome/browser/first_run/first_run_metrics.h" #include "ios/chrome/browser/main/browser.h" +#import "ios/chrome/browser/ui/commands/application_commands.h" +#import "ios/chrome/browser/ui/commands/command_dispatcher.h" #import "ios/chrome/browser/ui/first_run/first_run_screen_delegate.h" #import "ios/chrome/browser/ui/first_run/first_run_screen_provider.h" #import "ios/chrome/browser/ui/first_run/first_run_screen_type.h" @@ -100,6 +102,14 @@ [self.delegate willFinishPresentingScreens]; } +- (void)skipAllAndShowSyncSettings { + [self skipAll]; + id<ApplicationCommands> handler = HandlerForProtocol( + self.browser->GetCommandDispatcher(), ApplicationCommands); + [handler + showAdvancedSigninSettingsFromViewController:self.baseViewController]; +} + #pragma mark - Helper // Presents the screen of certain |type|.
diff --git a/ios/chrome/browser/ui/first_run/first_run_screen_delegate.h b/ios/chrome/browser/ui/first_run/first_run_screen_delegate.h index 1ff5525f..2c591cb 100644 --- a/ios/chrome/browser/ui/first_run/first_run_screen_delegate.h +++ b/ios/chrome/browser/ui/first_run/first_run_screen_delegate.h
@@ -14,6 +14,9 @@ // Called when user want to skip all screens after. - (void)skipAll; +// Called when the user click on the button to choose sync settings +- (void)skipAllAndShowSyncSettings; + @end #endif // IOS_CHROME_BROWSER_UI_FIRST_RUN_FIRST_RUN_SCREEN_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.mm b/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.mm index 7f00ef4..2eabbeb6 100644 --- a/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.mm +++ b/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.mm
@@ -364,7 +364,7 @@ _subtitleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; _subtitleLabel.numberOfLines = 0; - _subtitleLabel.textColor = [UIColor colorNamed:kTextSecondaryColor]; + _subtitleLabel.textColor = [UIColor colorNamed:kGrey800Color]; _subtitleLabel.text = self.subtitleText; _subtitleLabel.textAlignment = NSTextAlignmentCenter; _subtitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
diff --git a/ios/chrome/browser/ui/first_run/sync/sync_screen_coordinator.mm b/ios/chrome/browser/ui/first_run/sync/sync_screen_coordinator.mm index 8fafed14..09e10eb9 100644 --- a/ios/chrome/browser/ui/first_run/sync/sync_screen_coordinator.mm +++ b/ios/chrome/browser/ui/first_run/sync/sync_screen_coordinator.mm
@@ -107,7 +107,7 @@ } - (void)showSyncSettings { - // TODO(crbug.com/1189840): show sync settings. + [self.delegate skipAllAndShowSyncSettings]; } #pragma mark - Private
diff --git a/ios/chrome/browser/ui/first_run/sync/sync_screen_view_controller.mm b/ios/chrome/browser/ui/first_run/sync/sync_screen_view_controller.mm index f0c2ba74..e0247fc2 100644 --- a/ios/chrome/browser/ui/first_run/sync/sync_screen_view_controller.mm +++ b/ios/chrome/browser/ui/first_run/sync/sync_screen_view_controller.mm
@@ -12,6 +12,10 @@ #error "This file requires ARC support." #endif +namespace { +constexpr CGFloat kMarginBetweenContents = 12; +} // namespace + @implementation SyncScreenViewController @dynamic delegate; @@ -29,11 +33,44 @@ self.isTallBanner = NO; self.scrollToEndMandatory = YES; - // Add sync screen-specific content and its constraints. + // Add sync screen-specific content + UILabel* contentText = [self createContentText]; + [self.specificContentView addSubview:contentText]; + + UIButton* advanceSyncSettingsButton = [self createAdvanceSyncSettingsButton]; + [self.specificContentView addSubview:advanceSyncSettingsButton]; + + // Sync screen-specific constraints. + [NSLayoutConstraint activateConstraints:@[ + [contentText.topAnchor + constraintEqualToAnchor:self.specificContentView.topAnchor], + [contentText.centerXAnchor + constraintEqualToAnchor:self.specificContentView.centerXAnchor], + [contentText.widthAnchor + constraintLessThanOrEqualToAnchor:self.specificContentView.widthAnchor], + [advanceSyncSettingsButton.topAnchor + constraintEqualToAnchor:contentText.bottomAnchor + constant:kMarginBetweenContents], + [advanceSyncSettingsButton.centerXAnchor + constraintEqualToAnchor:self.specificContentView.centerXAnchor], + [advanceSyncSettingsButton.widthAnchor + constraintLessThanOrEqualToAnchor:self.specificContentView.widthAnchor], + [advanceSyncSettingsButton.bottomAnchor + constraintLessThanOrEqualToAnchor:self.specificContentView + .bottomAnchor], + ]]; + + [super viewDidLoad]; +} + +#pragma mark - Private + +// Creates and configures the text of sync screen +- (UILabel*)createContentText { UILabel* label = [[UILabel alloc] init]; label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote]; label.numberOfLines = 0; - label.textColor = [UIColor colorNamed:kTextSecondaryColor]; + label.textColor = [UIColor colorNamed:kGrey600Color]; label.textAlignment = NSTextAlignmentCenter; label.translatesAutoresizingMaskIntoConstraints = NO; @@ -45,22 +82,31 @@ } else { label.text = l10n_util::GetNSString(IDS_IOS_FIRST_RUN_SYNC_SCREEN_CONTENT); } + return label; +} - [self.specificContentView addSubview:label]; +// Creates and configures the sync settings button +- (UIButton*)createAdvanceSyncSettingsButton { + UIButton* button = [[UIButton alloc] init]; + button.translatesAutoresizingMaskIntoConstraints = NO; + button.titleLabel.adjustsFontSizeToFitWidth = YES; + [button.titleLabel + setFont:[UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]]; + [button setTitle:l10n_util::GetNSString( + IDS_IOS_FIRST_RUN_SYNC_SCREEN_ADVANCE_SETTINGS) + forState:UIControlStateNormal]; + [button setTitleColor:[UIColor colorNamed:kBlueColor] + forState:UIControlStateNormal]; - [NSLayoutConstraint activateConstraints:@[ - [label.topAnchor - constraintGreaterThanOrEqualToAnchor:self.specificContentView - .topAnchor], - [label.centerXAnchor - constraintEqualToAnchor:self.specificContentView.centerXAnchor], - [label.widthAnchor - constraintLessThanOrEqualToAnchor:self.specificContentView.widthAnchor], - [label.bottomAnchor - constraintEqualToAnchor:self.specificContentView.bottomAnchor], - ]]; + [button addTarget:self + action:@selector(showAdvanceSyncSettings) + forControlEvents:UIControlEventTouchUpInside]; + return button; +} - [super viewDidLoad]; +// Called when the sync settings button is tapped +- (void)showAdvanceSyncSettings { + [self.delegate showSyncSettings]; } @end
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.h index 809f8fc..7bfabc8 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.h +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.h
@@ -103,6 +103,9 @@ @property(nonatomic, weak) id<GridContextMenuProvider> menuProvider API_AVAILABLE(ios(13.0)); +// The item IDs of selected items for editing. +@property(nonatomic, readonly) NSArray<NSString*>* selectedItemIDsForEditing; + // Returns the layout of the grid for use in an animated transition. - (GridTransitionLayout*)transitionLayout; @@ -113,6 +116,10 @@ // Notifies the grid that it is about to be dismissed. - (void)prepareForDismissal; +// Selects all items in the grid for editing. No-op if |mode| is not +// TabGridModeSelection. +- (void)selectAllItemsForEditing; + @end #endif // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_GRID_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm index e42ef4e..ca70f8cb 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
@@ -646,6 +646,7 @@ self.items = [items mutableCopy]; self.selectedItemID = selectedItemID; + [self.selectedEditingItemIDs removeAllObjects]; [self.collectionView reloadData]; [self.collectionView selectItemAtIndexPath:CreateIndexPath(self.selectedIndex) animated:YES @@ -710,6 +711,7 @@ auto modelUpdates = ^{ [self.items removeObjectAtIndex:index]; self.selectedItemID = selectedItemID; + [self deselectItemWithIDForEditing:removedItemID]; [self.delegate gridViewController:self didChangeItemCount:self.items.count]; }; auto collectionViewUpdates = ^{ @@ -1038,9 +1040,9 @@ [self selectItemWithIDForEditing:itemID]; } [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]]; - } else { - [self.delegate gridViewController:self didSelectItemWithID:itemID]; } + + [self.delegate gridViewController:self didSelectItemWithID:itemID]; } // Animates the empty state into view. @@ -1111,6 +1113,22 @@ } } +#pragma mark - Public Editing Mode Selection +- (void)selectAllItemsForEditing { + if (_mode != TabGridModeSelection) { + return; + } + + for (TabSwitcherItem* item in self.items) { + [self selectItemWithIDForEditing:item.identifier]; + } + [self.collectionView reloadData]; +} + +- (NSArray<NSString*>*)selectedItemIDsForEditing { + return [self.selectedEditingItemIDs allObjects]; +} + #pragma mark - Private Editing Mode Selection - (BOOL)isItemWithIDSelectedForEditing:(NSString*)identifier {
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_bottom_toolbar.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_bottom_toolbar.h index bff53b6..c492e49 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_bottom_toolbar.h +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_bottom_toolbar.h
@@ -46,8 +46,6 @@ - (void)setDoneButtonTarget:(id)target action:(SEL)action; // Set |enabled| on the new tab button. - (void)setNewTabButtonEnabled:(BOOL)enabled; -// Set |enabled| on the selection mode buttons. -- (void)setSelectionModeButtonsEnabled:(BOOL)enabled; // Set |enabled| on the done button. - (void)setDoneButtonEnabled:(BOOL)enabled; // Set |enabled| on the closeAll button.
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_bottom_toolbar.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_bottom_toolbar.mm index 268d8c9..161fb0e 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_bottom_toolbar.mm +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_bottom_toolbar.mm
@@ -91,6 +91,8 @@ if (_mode == mode) return; _mode = mode; + // Reset selected tabs count when mode changes. + self.selectedTabsCount = 0; [self updateLayout]; } @@ -131,12 +133,6 @@ _closeAllOrUndoButton.enabled = enabled; } -- (void)setSelectionModeButtonsEnabled:(BOOL)enabled { - _addToButton.enabled = enabled; - _closeTabsButton.enabled = enabled; - _shareButton.enabled = enabled; -} - - (void)useUndoCloseAll:(BOOL)useUndo { _closeAllOrUndoButton.enabled = YES; if (useUndo) {
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.h index e2fc1a8..a569d5f 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.h +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.h
@@ -40,6 +40,8 @@ - (void)setNewTabButtonTarget:(id)target action:(SEL)action; // Sets target/action for tapping event on select tabs button. - (void)setSelectTabButtonTarget:(id)target action:(SEL)action; +// Sets target/action for tapping event on select all button. +- (void)setSelectAllButtonTarget:(id)target action:(SEL)action; // Sets target/action for tapping event on close all button. - (void)setCloseAllButtonTarget:(id)target action:(SEL)action; // Sets target/action for tapping event on done button.
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.mm index c750a21..42c816f 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.mm +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.mm
@@ -52,6 +52,8 @@ if (_mode == mode) return; _mode = mode; + // Reset selected tabs count when mode changes. + self.selectedTabsCount = 0; [self setItemsForTraitCollection:self.traitCollection]; } @@ -77,6 +79,11 @@ _selectTabsButton.action = action; } +- (void)setSelectAllButtonTarget:(id)target action:(SEL)action { + _selectAllButton.target = target; + _selectAllButton.action = action; +} + - (void)setDoneButtonTarget:(id)target action:(SEL)action { _doneButton.target = target; _doneButton.action = action;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm index aeabc27..105c9b1 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
@@ -579,6 +579,17 @@ _activePage = activePage; } +#pragma mark - TabGridMode + +- (void)setTabGridMode:(TabGridMode)mode { + _tabGridMode = mode; + + self.bottomToolbar.mode = self.tabGridMode; + self.regularTabsViewController.mode = self.tabGridMode; + self.incognitoTabsViewController.mode = self.tabGridMode; + self.topToolbar.mode = self.tabGridMode; +} + #pragma mark - LayoutSwitcherProvider - (id<LayoutSwitcher>)layoutSwitcher { @@ -1183,6 +1194,8 @@ [topToolbar setNewTabButtonTarget:self action:@selector(newTabButtonTapped:)]; [topToolbar setSelectTabButtonTarget:self action:@selector(selectButtonTapped:)]; + [topToolbar setSelectAllButtonTarget:self + action:@selector(selectAllButtonTapped:)]; // Configure and initialize the page control. [topToolbar.pageControl addTarget:self @@ -1655,6 +1668,14 @@ - (void)gridViewController:(GridViewController*)gridViewController didSelectItemWithID:(NSString*)itemID { + if (self.tabGridMode == TabGridModeSelection) { + NSUInteger selectedItemsCount = + [gridViewController.selectedItemIDsForEditing count]; + self.topToolbar.selectedTabsCount = selectedItemsCount; + self.bottomToolbar.selectedTabsCount = selectedItemsCount; + return; + } + // Update the model with the tab selection, but don't have the grid view // controller display the new selection, since there will be a transition // away from it immediately afterwards. @@ -1724,6 +1745,11 @@ - (void)gridViewController:(GridViewController*)gridViewController didChangeItemCount:(NSUInteger)count { + if (self.tabGridMode == TabGridModeSelection && count == 0) { + // Exit selection mode if there are no more tabs. + self.tabGridMode = TabGridModeNormal; + } + if (count > 0) { // Undo is only available when the tab grid is empty. self.undoCloseAllAvailable = NO; @@ -1770,8 +1796,6 @@ // mode. if (self.tabGridMode == TabGridModeSelection) { self.tabGridMode = TabGridModeNormal; - self.bottomToolbar.mode = self.tabGridMode; - self.topToolbar.mode = self.tabGridMode; // Records action when user exit the selection mode. base::RecordAction(base::UserMetricsAction("MobileTabGridSelectionDone")); return; @@ -1797,13 +1821,18 @@ - (void)selectButtonTapped:(id)sender { self.tabGridMode = TabGridModeSelection; - self.bottomToolbar.mode = self.tabGridMode; - self.regularTabsViewController.mode = self.tabGridMode; - self.incognitoTabsViewController.mode = self.tabGridMode; - self.topToolbar.mode = self.tabGridMode; base::RecordAction(base::UserMetricsAction("MobileTabGridSelectTabs")); } +- (void)selectAllButtonTapped:(id)sender { + base::RecordAction( + base::UserMetricsAction("MobileTabGridSelectionSelectAll")); + + GridViewController* gridViewController = + [self gridViewControllerForPage:self.currentPage]; + [gridViewController selectAllItemsForEditing]; +} + // Shows an action sheet that asks for confirmation when 'Close All' button is // tapped. - (void)closeAllButtonTappedShowConfirmation {
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 index 3c2dd60..e3ff8d6 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@ -c7cfe41e25c83155555587d40ff6ed7bf18420c2 \ No newline at end of file +041508f78b487f7a69244c22a519e2fcab7d4720 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 index d1b79f96..805d553e 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@ -b220701dd7713bc22811df4d2ded994d83153df2 \ No newline at end of file +c5ff51d65a91b4575edebd49a43882298bb3ec0b \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 index 3df96cd..d0ade49 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@ -c8e491f1052579a0818b050fb7f3ff150615bcf5 \ No newline at end of file +ff27837cf2ea405e5d3d8ac71414f3adad840b27 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1 index d994d6c..6b98e44 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@ -38b2778fcd202b2e58cf99895aca08601783481c \ No newline at end of file +516e93b2cfdb950fe8c5ee4bed359d3e1b3cb8d5 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 index 0aba5c8bd..874fea36 100644 --- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@ -baa21dd4e158c141157d19892de90dd484170740 \ No newline at end of file +6ce9802b2d7f4b294690dd7dacf0b523df3fcb63 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 index 83749e9..5984ab97 100644 --- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@ -bc49f00bfe18484b03cc72344abb914d15fa6040 \ No newline at end of file +e8d1d7b0db4659e2165614de15a6d58ad9432164 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 index 1857f3e..f2cc3f6 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@ -e3813193e42962052ce89abf222803f4a7021555 \ No newline at end of file +5b74a7623d1e4a5cf8a855c5a080bd77e7a6148b \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 index af21333..c9cd1c0 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@ -1a9d443bf0f860d3025fa9db2461e09f1c04dcd0 \ No newline at end of file +fa8b1ba488fc6967b38f7e36d9c044aa08d8b126 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 index 53884a53..3ac90e6c5 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@ -63a7bea8b203fbd2f0991560f78fa5d9c03c1916 \ No newline at end of file +1ccd98517d7c0589303cbacc934a34fb9ff1516b \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 index 75d8e04b..ca468ce 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@ -944167c0f04c492bbd10555e2bc6aeb93f3e25dd \ No newline at end of file +e8dd4551fdee459d7f6cf68100225785b75cea8b \ No newline at end of file
diff --git a/media/gpu/test/video_encoder/decoder_buffer_validator.cc b/media/gpu/test/video_encoder/decoder_buffer_validator.cc index 8a75a75b..1dd1f6a2 100644 --- a/media/gpu/test/video_encoder/decoder_buffer_validator.cc +++ b/media/gpu/test/video_encoder/decoder_buffer_validator.cc
@@ -142,13 +142,24 @@ LOG(ERROR) << "Failed parsing slice"; return false; } - // TODO(hiroh): Add more checks. + if (IsNewPicture(slice_hdr)) { // A new frame is found. Initialize |cur_pic|. num_frames++; if (!UpdateCurrentPicture(slice_hdr)) return false; } + + if (slice_hdr.disable_deblocking_filter_idc != 0) { + LOG(ERROR) << "Deblocking filter is not enabled"; + return false; + } + + if (slice_hdr.slice_type == H264SliceHeader::Type::kBSlice) { + LOG(ERROR) << "Found B slice"; + return false; + } + break; } case H264NALU::kSPS: { @@ -193,6 +204,20 @@ return false; } seen_pps_ = true; + + const H264PPS* pps = parser_.GetPPS(pps_id); + if ((profile_ == H264SPS::kProfileIDCMain || + profile_ == H264SPS::kProfileIDCHigh) && + !pps->entropy_coding_mode_flag) { + // CABAC must be selected if a profile is Main and High. + LOG(ERROR) << "The entropy coding is not CABAC"; + return false; + } + + // 8x8 transform should be enabled if a profile is High. However, we + // don't check it because it is not enabled due to a hardware limitation + // on AMD stoneyridge and picasso. + break; } default:
diff --git a/media/mojo/mojom/BUILD.gn b/media/mojo/mojom/BUILD.gn index 7e3d085..6bf108b7 100644 --- a/media/mojo/mojom/BUILD.gn +++ b/media/mojo/mojom/BUILD.gn
@@ -393,6 +393,10 @@ cpp = "::media::VideoEncodeAccelerator::Config" }, { + mojom = "media.mojom.InterLayerPredMode" + cpp = "::media::VideoEncodeAccelerator::Config::InterLayerPredMode" + }, + { mojom = "media.mojom.SpatialLayer" cpp = "::media::VideoEncodeAccelerator::Config::SpatialLayer" },
diff --git a/media/mojo/mojom/video_encode_accelerator.mojom b/media/mojo/mojom/video_encode_accelerator.mojom index 82cf95f..c825cc8 100644 --- a/media/mojo/mojom/video_encode_accelerator.mojom +++ b/media/mojo/mojom/video_encode_accelerator.mojom
@@ -80,6 +80,13 @@ kDisplay }; + // See media::VideoEncodeAccelerator::Config::InterLayerPredMode + enum InterLayerPredMode { + kOff, + kOn, + kOnKeyPic + }; + // See media::VideoEncodeAccelerator::Config::StorageType enum StorageType { kShmem, @@ -101,6 +108,7 @@ bool has_storage_type; // Whether or not config has storage type config ContentType content_type; array<SpatialLayer> spatial_layers; + InterLayerPredMode inter_layer_pred; bool require_low_delay; };
diff --git a/media/mojo/mojom/video_encode_accelerator_mojom_traits.cc b/media/mojo/mojom/video_encode_accelerator_mojom_traits.cc index 506a38e..af3010a 100644 --- a/media/mojo/mojom/video_encode_accelerator_mojom_traits.cc +++ b/media/mojo/mojom/video_encode_accelerator_mojom_traits.cc
@@ -210,6 +210,7 @@ NOTREACHED(); return media::mojom::VideoEncodeAcceleratorConfig_ContentType::kCamera; } + // static bool EnumTraits<media::mojom::VideoEncodeAcceleratorConfig_ContentType, media::VideoEncodeAccelerator::Config::ContentType>:: @@ -228,6 +229,48 @@ } // static +media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode +EnumTraits<media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode, + media::VideoEncodeAccelerator::Config::InterLayerPredMode>:: + ToMojom(media::VideoEncodeAccelerator::Config::InterLayerPredMode input) { + switch (input) { + case media::VideoEncodeAccelerator::Config::InterLayerPredMode::kOff: + return media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode:: + kOff; + case media::VideoEncodeAccelerator::Config::InterLayerPredMode::kOn: + return media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode::kOn; + case media::VideoEncodeAccelerator::Config::InterLayerPredMode::kOnKeyPic: + return media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode:: + kOnKeyPic; + } + NOTREACHED(); + return media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode::kOff; +} + +// static +bool EnumTraits<media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode, + media::VideoEncodeAccelerator::Config::InterLayerPredMode>:: + FromMojom( + media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode input, + media::VideoEncodeAccelerator::Config::InterLayerPredMode* output) { + switch (input) { + case media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode::kOff: + *output = media::VideoEncodeAccelerator::Config::InterLayerPredMode::kOff; + return true; + case media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode::kOn: + *output = media::VideoEncodeAccelerator::Config::InterLayerPredMode::kOn; + return true; + case media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode:: + kOnKeyPic: + *output = + media::VideoEncodeAccelerator::Config::InterLayerPredMode::kOnKeyPic; + return true; + } + NOTREACHED(); + return false; +} + +// static bool StructTraits<media::mojom::SpatialLayerDataView, media::VideoEncodeAccelerator::Config::SpatialLayer>:: Read(media::mojom::SpatialLayerDataView input, @@ -288,10 +331,14 @@ if (!input.ReadSpatialLayers(&spatial_layers)) return false; + media::VideoEncodeAccelerator::Config::InterLayerPredMode inter_layer_pred; + if (!input.ReadInterLayerPred(&inter_layer_pred)) + return false; + *output = media::VideoEncodeAccelerator::Config( input_format, input_visible_size, output_profile, input.initial_bitrate(), initial_framerate, gop_length, h264_output_level, is_constrained_h264, - storage_type, content_type, spatial_layers); + storage_type, content_type, spatial_layers, inter_layer_pred); output->require_low_delay = input.require_low_delay(); return true;
diff --git a/media/mojo/mojom/video_encode_accelerator_mojom_traits.h b/media/mojo/mojom/video_encode_accelerator_mojom_traits.h index 7cc9dc7..8016fc39 100644 --- a/media/mojo/mojom/video_encode_accelerator_mojom_traits.h +++ b/media/mojo/mojom/video_encode_accelerator_mojom_traits.h
@@ -178,6 +178,17 @@ }; template <> +struct EnumTraits<media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode, + media::VideoEncodeAccelerator::Config::InterLayerPredMode> { + static media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode ToMojom( + media::VideoEncodeAccelerator::Config::InterLayerPredMode input); + + static bool FromMojom( + media::mojom::VideoEncodeAcceleratorConfig_InterLayerPredMode, + media::VideoEncodeAccelerator::Config::InterLayerPredMode* output); +}; + +template <> struct EnumTraits<media::mojom::VideoEncodeAcceleratorConfig_ContentType, media::VideoEncodeAccelerator::Config::ContentType> { static media::mojom::VideoEncodeAcceleratorConfig_ContentType ToMojom( @@ -304,6 +315,11 @@ return input.spatial_layers; } + static media::VideoEncodeAccelerator::Config::InterLayerPredMode + inter_layer_pred(const media::VideoEncodeAccelerator::Config& input) { + return input.inter_layer_pred; + } + static bool require_low_delay( const media::VideoEncodeAccelerator::Config& input) { return input.require_low_delay;
diff --git a/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc b/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc index 57dc025..a6e49803c 100644 --- a/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc +++ b/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc
@@ -70,7 +70,8 @@ kBaseBitrateBps, kBaseFramerate, absl::nullopt, absl::nullopt, false, ::media::VideoEncodeAccelerator::Config::StorageType::kGpuMemoryBuffer, ::media::VideoEncodeAccelerator::Config::ContentType::kCamera, - input_spatial_layers); + input_spatial_layers, + ::media::VideoEncodeAccelerator::Config::InterLayerPredMode::kOnKeyPic); DVLOG(4) << input_config.AsHumanReadableString(); ::media::VideoEncodeAccelerator::Config output_config{};
diff --git a/media/video/video_encode_accelerator.cc b/media/video/video_encode_accelerator.cc index 361e53a..52a190e15 100644 --- a/media/video/video_encode_accelerator.cc +++ b/media/video/video_encode_accelerator.cc
@@ -55,7 +55,8 @@ bool is_constrained_h264, absl::optional<StorageType> storage_type, ContentType content_type, - const std::vector<SpatialLayer>& spatial_layers) + const std::vector<SpatialLayer>& spatial_layers, + InterLayerPredMode inter_layer_pred) : input_format(input_format), input_visible_size(input_visible_size), output_profile(output_profile), @@ -67,7 +68,8 @@ is_constrained_h264(is_constrained_h264), storage_type(storage_type), content_type(content_type), - spatial_layers(spatial_layers) {} + spatial_layers(spatial_layers), + inter_layer_pred(inter_layer_pred) {} VideoEncodeAccelerator::Config::~Config() = default; @@ -106,6 +108,21 @@ i, sl.width, sl.height, sl.bitrate_bps, sl.framerate, sl.max_qp, sl.num_of_temporal_layers); } + + switch (inter_layer_pred) { + case Config::InterLayerPredMode::kOff: + str += base::StringPrintf(", InterLayerPredMode::kOff"); + break; + case Config::InterLayerPredMode::kOn: + str += base::StringPrintf(", InterLayerPredMode::kOn"); + break; + case Config::InterLayerPredMode::kOnKeyPic: + str += base::StringPrintf(", InterLayerPredMode::kOnKeyPic"); + break; + default: + str += base::StringPrintf(", Unknown InterLayerPredMode"); + break; + } return str; } @@ -205,7 +222,8 @@ l.gop_length == r.gop_length && l.h264_output_level == r.h264_output_level && l.storage_type == r.storage_type && l.content_type == r.content_type && - l.spatial_layers == r.spatial_layers; + l.spatial_layers == r.spatial_layers && + l.inter_layer_pred == r.inter_layer_pred; } } // namespace media
diff --git a/media/video/video_encode_accelerator.h b/media/video/video_encode_accelerator.h index 3e0584a9..94cd76d1 100644 --- a/media/video/video_encode_accelerator.h +++ b/media/video/video_encode_accelerator.h
@@ -134,6 +134,11 @@ // Indicates if video content should be treated as a "normal" camera feed // or as generated (e.g. screen capture). enum class ContentType { kCamera, kDisplay }; + enum class InterLayerPredMode : int { + kOff = 0, // Inter-layer prediction is disabled. + kOn = 1, // Inter-layer prediction is enabled. + kOnKeyPic = 2 // Inter-layer prediction is enabled for key picture. + }; // Indicates the storage type of a video frame provided on Encode(). // kShmem if a video frame has a shared memory. // kGpuMemoryBuffer if a video frame has a GpuMemoryBuffer. @@ -168,7 +173,8 @@ bool is_constrained_h264 = false, absl::optional<StorageType> storage_type = absl::nullopt, ContentType content_type = ContentType::kCamera, - const std::vector<SpatialLayer>& spatial_layers = {}); + const std::vector<SpatialLayer>& spatial_layers = {}, + InterLayerPredMode inter_layer_pred = InterLayerPredMode::kOnKeyPic); ~Config(); @@ -228,6 +234,9 @@ // etc. of |spatial_layers|. std::vector<SpatialLayer> spatial_layers; + // Indicates the inter layer prediction mode for SVC encoding. + InterLayerPredMode inter_layer_pred; + // Currently it's Mac only! This flag forces Mac encoder to enforce low // latency mode. Initialize() will fail if the system can't do it for some // reason. See VTVideoEncodeAccelerator::require_low_delay_ for more
diff --git a/remoting/ios/app/view_utils.mm b/remoting/ios/app/view_utils.mm index c447397..fe41354 100644 --- a/remoting/ios/app/view_utils.mm +++ b/remoting/ios/app/view_utils.mm
@@ -4,11 +4,25 @@ #include "remoting/ios/app/view_utils.h" +namespace { +UIWindow* GetAnyKeyWindow() { +#if !defined(__IPHONE_13_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0 + return [UIApplication sharedApplication].keyWindow; +#else + NSArray<UIWindow*>* windows = [UIApplication sharedApplication].windows; + for (UIWindow* window in windows) { + if (window.isKeyWindow) + return window; + } + return nil; +#endif +} +} // namespace + namespace remoting { UIViewController* TopPresentingVC() { - UIViewController* topController = - UIApplication.sharedApplication.keyWindow.rootViewController; + UIViewController* topController = GetAnyKeyWindow().rootViewController; while (topController.presentedViewController) { topController = topController.presentedViewController;
diff --git a/remoting/resources/remoting_strings_az.xtb b/remoting/resources/remoting_strings_az.xtb index 6a9bdc9..d8d6f75 100644 --- a/remoting/resources/remoting_strings_az.xtb +++ b/remoting/resources/remoting_strings_az.xtb
@@ -71,7 +71,7 @@ <translation id="4281844954008187215">Xidmət Şərtləri</translation> <translation id="4405930547258349619">Kök Kitabxana</translation> <translation id="443560535555262820">Giriş Tərcihlərini Açın</translation> -<translation id="4450893287417543264">Daha göstərməyin</translation> +<translation id="4450893287417543264">Göstərilməsin</translation> <translation id="4512952200932940229">Copyright 2021 Chromium Müəllifləri. Bütün hüquqlar qorunur.</translation> <translation id="4513946894732546136">Geri əlaqə</translation> <translation id="4563926062592110512">Klient bağlantısı kəsildi: <ph name="CLIENT_USERNAME" />.</translation>
diff --git a/services/network/public/cpp/cross_origin_resource_policy.cc b/services/network/public/cpp/cross_origin_resource_policy.cc index 8b71b1bb..f3472a1 100644 --- a/services/network/public/cpp/cross_origin_resource_policy.cc +++ b/services/network/public/cpp/cross_origin_resource_policy.cc
@@ -140,6 +140,7 @@ const absl::optional<url::Origin>& request_initiator, mojom::RequestMode request_mode, absl::optional<url::Origin> request_initiator_origin_lock, + bool request_include_credentials, mojom::CrossOriginEmbedderPolicyValue embedder_policy) { // Browser-initiated requests are not subject to Cross-Origin-Resource-Policy. if (!request_initiator.has_value()) { @@ -153,11 +154,21 @@ return absl::nullopt; } - bool require_corp = - embedder_policy == mojom::CrossOriginEmbedderPolicyValue::kRequireCorp || - (embedder_policy == - mojom::CrossOriginEmbedderPolicyValue::kCredentialless && - request_mode == mojom::RequestMode::kNavigate); + bool require_corp; + switch (embedder_policy) { + case mojom::CrossOriginEmbedderPolicyValue::kNone: + require_corp = false; + break; + + case mojom::CrossOriginEmbedderPolicyValue::kCredentialless: + require_corp = request_mode == mojom::RequestMode::kNavigate || + request_include_credentials; + break; + + case mojom::CrossOriginEmbedderPolicyValue::kRequireCorp: + require_corp = true; + break; + } // COEP https://mikewest.github.io/corpp/#corp-check bool upgrade_to_same_origin = false; @@ -222,6 +233,7 @@ mojom::RequestMode request_mode, absl::optional<url::Origin> request_initiator_origin_lock, mojom::RequestDestination request_destination, + bool request_include_credentials, const CrossOriginEmbedderPolicy& embedder_policy, mojom::CrossOriginEmbedderPolicyReporter* reporter) { constexpr auto kBlockedDueToCoep = mojom::BlockedByResponseReason:: @@ -231,7 +243,8 @@ reporter) { const auto result = IsBlockedInternal( policy, request_url, request_initiator, request_mode, - request_initiator_origin_lock, embedder_policy.report_only_value); + request_initiator_origin_lock, request_include_credentials, + embedder_policy.report_only_value); UMA_HISTOGRAM_ENUMERATION( "NetworkService.CrossOriginResourcePolicy.ReportOnlyResult", ToCorpResult(result)); @@ -249,7 +262,8 @@ const auto result = IsBlockedInternal(policy, request_url, request_initiator, request_mode, - request_initiator_origin_lock, embedder_policy.value); + request_initiator_origin_lock, + request_include_credentials, embedder_policy.value); UMA_HISTOGRAM_ENUMERATION("NetworkService.CrossOriginResourcePolicy.Result", ToCorpResult(result)); if (reporter && @@ -296,8 +310,8 @@ return IsBlockedInternalWithReporting( policy, request_url, original_url, request_initiator, request_mode, - request_initiator_origin_lock, request_destination, embedder_policy, - reporter); + request_initiator_origin_lock, request_destination, + response.request_include_credentials, embedder_policy, reporter); } // static @@ -310,6 +324,7 @@ mojom::RequestMode request_mode, absl::optional<url::Origin> request_initiator_origin_lock, mojom::RequestDestination request_destination, + bool request_include_credentials, const CrossOriginEmbedderPolicy& embedder_policy, mojom::CrossOriginEmbedderPolicyReporter* reporter) { // From https://fetch.spec.whatwg.org/#cross-origin-resource-policy-header: @@ -321,8 +336,8 @@ return IsBlockedInternalWithReporting( policy, request_url, original_url, request_initiator, request_mode, - request_initiator_origin_lock, request_destination, embedder_policy, - reporter); + request_initiator_origin_lock, request_destination, + request_include_credentials, embedder_policy, reporter); } // static @@ -342,7 +357,8 @@ return IsBlockedInternalWithReporting( policy, request_url, original_url, request_initiator, mojom::RequestMode::kNavigate, request_initiator_origin_lock, - request_destination, embedder_policy, reporter); + request_destination, response.request_include_credentials, + embedder_policy, reporter); } // static
diff --git a/services/network/public/cpp/cross_origin_resource_policy.h b/services/network/public/cpp/cross_origin_resource_policy.h index db1d67ae..a32df33 100644 --- a/services/network/public/cpp/cross_origin_resource_policy.h +++ b/services/network/public/cpp/cross_origin_resource_policy.h
@@ -60,6 +60,7 @@ mojom::RequestMode request_mode, absl::optional<url::Origin> request_initiator_origin_lock, mojom::RequestDestination request_destination, + bool request_include_credentials, const CrossOriginEmbedderPolicy& embedder_policy, mojom::CrossOriginEmbedderPolicyReporter* reporter) WARN_UNUSED_RESULT;
diff --git a/services/network/public/mojom/url_response_head.mojom b/services/network/public/mojom/url_response_head.mojom index 13401eb..9518e78 100644 --- a/services/network/public/mojom/url_response_head.mojom +++ b/services/network/public/mojom/url_response_head.mojom
@@ -235,4 +235,9 @@ // covered by the wildcard in the preflight response. // TODO(crbug.com/1176753): Remove this once the investigation is done. bool has_authorization_covered_by_wildcard_on_preflight = false; + + // The request's |includeCredentials| value from the "HTTP-network fetch" + // algorithm. + // See: https://fetch.spec.whatwg.org/#concept-http-network-fetch + bool request_include_credentials = true; };
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc index 9f5850c..7a57ebf 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc
@@ -159,6 +159,7 @@ response->has_range_requested = request->extra_request_headers().HasHeader( net::HttpRequestHeaders::kRange); response->dns_aliases = request->response_info().dns_aliases; + response->request_include_credentials = request->allow_credentials(); } // A subclass of net::UploadBytesElementReader which owns @@ -1141,7 +1142,6 @@ DCHECK(!deferred_redirect_url_); deferred_redirect_url_ = std::make_unique<GURL>(redirect_info.new_url); - SetRequestCredentials(redirect_info.new_url); // Send the redirect response to the client, allowing them to inspect it and // optionally follow the redirect. @@ -1191,6 +1191,8 @@ return; } + SetRequestCredentials(redirect_info.new_url); + // We may need to clear out old Sec- prefixed request headers. We'll attempt // to do this before we re-add any. MaybeRemoveSecHeaders(url_request_.get(), redirect_info.new_url);
diff --git a/styleguide/c++/c++11.html b/styleguide/c++/c++11.html index 61ee252..619d1af 100644 --- a/styleguide/c++/c++11.html +++ b/styleguide/c++/c++11.html
@@ -307,7 +307,7 @@ <td><code>absl::Status</code></td> <td>Type for returning detailed errors.</td> <td><a href="https://source.chromium.org/chromium/chromium/src/+/main:third_party/abseil-cpp/absl/status/status.h">status.h</a></td> -<td>Approved for use inside a wrapper type. Use <a href="https://source.chromium.org/chromium/chromium/src/+/master:base/strings/abseil_string_conversions.h">abseil_string_conversions.h</a> to convert to and from <a href="https://source.chromium.org/chromium/chromium/src/+/master:third_party/abseil-cpp/absl/strings/string_view.h">absl::string_view</a> so the wrapper can expose <a href="https://source.chromium.org/chromium/chromium/src/+/master:base/strings/string_piece.h">base::StringPiece</a>. Use <a href="https://source.chromium.org/chromium/chromium/src/+/main:third_party/abseil-cpp/absl/strings/cord.h">absl::Cord</a> directly as minimally necessary to interface; do not expose in the wrapper type API.<br> +<td>Approved for use inside a wrapper type. Use <a href="https://source.chromium.org/chromium/chromium/src/+/main:base/strings/abseil_string_conversions.h">abseil_string_conversions.h</a> to convert to and from <a href="https://source.chromium.org/chromium/chromium/src/+/main:third_party/abseil-cpp/absl/strings/string_view.h">absl::string_view</a> so the wrapper can expose <a href="https://source.chromium.org/chromium/chromium/src/+/main:base/strings/string_piece.h">base::StringPiece</a>. Use <a href="https://source.chromium.org/chromium/chromium/src/+/main:third_party/abseil-cpp/absl/strings/cord.h">absl::Cord</a> directly as minimally necessary to interface; do not expose in the wrapper type API.<br> <a href="https://groups.google.com/a/chromium.org/g/cxx/c/ImdFCSZ-NMA">Discussion thread</a></td> </tr>
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json index 90c675b..a6e2334 100644 --- a/testing/buildbot/chromium.android.fyi.json +++ b/testing/buildbot/chromium.android.fyi.json
@@ -4689,7 +4689,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M91", - "revision": "version:91.0.4472.98" + "revision": "version:91.0.4472.99" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -4768,7 +4768,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M92", - "revision": "version:92.0.4515.45" + "revision": "version:92.0.4515.46" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -4926,7 +4926,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M91", - "revision": "version:91.0.4472.98" + "revision": "version:91.0.4472.99" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -5005,7 +5005,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M92", - "revision": "version:92.0.4515.45" + "revision": "version:92.0.4515.46" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json index 9add9e24..01b0be1e 100644 --- a/testing/buildbot/chromium.android.json +++ b/testing/buildbot/chromium.android.json
@@ -53852,7 +53852,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M91", - "revision": "version:91.0.4472.98" + "revision": "version:91.0.4472.99" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -53932,7 +53932,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M92", - "revision": "version:92.0.4515.45" + "revision": "version:92.0.4515.46" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -54092,7 +54092,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M91", - "revision": "version:91.0.4472.98" + "revision": "version:91.0.4472.99" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -54172,7 +54172,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M92", - "revision": "version:92.0.4515.45" + "revision": "version:92.0.4515.46" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -54397,7 +54397,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M91", - "revision": "version:91.0.4472.98" + "revision": "version:91.0.4472.99" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -54476,7 +54476,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M92", - "revision": "version:92.0.4515.45" + "revision": "version:92.0.4515.46" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -54634,7 +54634,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M91", - "revision": "version:91.0.4472.98" + "revision": "version:91.0.4472.99" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -54713,7 +54713,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M92", - "revision": "version:92.0.4515.45" + "revision": "version:92.0.4515.46" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -54938,7 +54938,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M91", - "revision": "version:91.0.4472.98" + "revision": "version:91.0.4472.99" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -55017,7 +55017,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M92", - "revision": "version:92.0.4515.45" + "revision": "version:92.0.4515.46" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -55175,7 +55175,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M91", - "revision": "version:91.0.4472.98" + "revision": "version:91.0.4472.99" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -55254,7 +55254,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M92", - "revision": "version:92.0.4515.45" + "revision": "version:92.0.4515.46" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl index 729d482..276c53c 100644 --- a/testing/buildbot/variants.pyl +++ b/testing/buildbot/variants.pyl
@@ -400,7 +400,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M92', - 'revision': 'version:92.0.4515.45', + 'revision': 'version:92.0.4515.46', } ], }, @@ -424,7 +424,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M91', - 'revision': 'version:91.0.4472.98', + 'revision': 'version:91.0.4472.99', } ], }, @@ -472,7 +472,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M92', - 'revision': 'version:92.0.4515.45', + 'revision': 'version:92.0.4515.46', } ], }, @@ -496,7 +496,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M91', - 'revision': 'version:91.0.4472.98', + 'revision': 'version:91.0.4472.99', } ], }, @@ -544,7 +544,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M92', - 'revision': 'version:92.0.4515.45', + 'revision': 'version:92.0.4515.46', } ], }, @@ -568,7 +568,7 @@ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M91', - 'revision': 'version:91.0.4472.98', + 'revision': 'version:91.0.4472.99', } ], },
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn index 617fdf6..59c92ace 100644 --- a/third_party/android_deps/BUILD.gn +++ b/third_party/android_deps/BUILD.gn
@@ -138,7 +138,7 @@ # This target does not come with most of its dependencies and is # only meant to be used by the resources shrinker. If you wish to use # this for other purposes, change buildCompileNoDeps in build.gradle. - visibility = [ "//build/android/gyp/resources_shrinker:*" ] + visibility = [ "//build/android/unused_resources:*" ] } # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. @@ -150,7 +150,7 @@ # This target does not come with most of its dependencies and is # only meant to be used by the resources shrinker. If you wish to use # this for other purposes, change buildCompileNoDeps in build.gradle. - visibility = [ "//build/android/gyp/resources_shrinker:*" ] + visibility = [ "//build/android/unused_resources:*" ] } # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. @@ -162,7 +162,7 @@ # This target does not come with most of its dependencies and is # only meant to be used by the resources shrinker. If you wish to use # this for other purposes, change buildCompileNoDeps in build.gradle. - visibility = [ "//build/android/gyp/resources_shrinker:*" ] + visibility = [ "//build/android/unused_resources:*" ] } # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
diff --git a/third_party/android_deps/README.md b/third_party/android_deps/README.md index b8dda00d..2cf4777 100644 --- a/third_party/android_deps/README.md +++ b/third_party/android_deps/README.md
@@ -71,4 +71,44 @@ dependencies and their pom.xml files, and use Python to process and generate the files. This approach was not as successful, as some information about the dependencies does not seem to be available purely from the POM file, which -resulted in expecting dependencies that gradle considered unnecessary. +resulted in expecting dependencies that gradle considered unnecessary. This is +especially true nowadays that pom.xml files for many dependencies are no longer +maintained by the package authors. + +#### Groovy Style Guide +The groovy code in `//third_party/android_deps/buildSrc/src/main/groovy` loosely +follows the [Groovy Style Guide][groovy_style_guide], and can be linted by each +dev with [npm-groovy-lint][npm_groovy_lint] via the +[VS Code extension][vs_code_groovy_lint]. The line length limit for groovy is +**120** characters. + +Here is a sample `.groovylintrc.json` file: + +``` +{ + "extends": "recommended", + "rules": { + "CatchException": "off", + "CompileStatic": "off", + "DuplicateMapLiteral": "off", + "DuplicateNumberLiteral": "off", + "DuplicateStringLiteral": "off", + "FactoryMethodName": "off", + "JUnitPublicProperty": "off", + "JavaIoPackageAccess": "off", + "MethodCount": "off", + "NestedForLoop": "off", + "ThrowRuntimeException": "off", + "formatting.Indentation": { + "spacesPerIndentLevel": 4 + } + } +} +``` + +This is a list of rule names: [Groovy Rule Index by Name][groovy_rule_index]. + +[groovy_style_guide]: https://groovy-lang.org/style-guide.html +[npm_groovy_lint]: https://github.com/nvuillam/npm-groovy-lint +[vs_code_groovy_lint]: https://marketplace.visualstudio.com/items?itemName=NicolasVuillamy.vscode-groovy-lint +[groovy_rule_index]: https://codenarc.org/codenarc-rule-index-by-name.html
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy index f7c0fb16..b0f3277 100644 --- a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy +++ b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
@@ -3,6 +3,7 @@ // found in the LICENSE file. import groovy.json.JsonOutput +import groovy.text.SimpleTemplateEngine import groovy.text.Template import groovy.transform.SourceURI import org.gradle.api.DefaultTask @@ -12,17 +13,15 @@ import java.nio.file.Path import java.nio.file.Paths -import java.time.LocalDate import java.util.concurrent.Executors import java.util.concurrent.ExecutorService import java.util.concurrent.Future -import java.util.concurrent.TimeUnit import java.util.regex.Pattern +import java.util.regex.Matcher /** - * Task to download dependencies specified in {@link ChromiumPlugin} and configure the - * Chromium build to integrate them. Used by declaring a new task in a {@code build.gradle} - * file: + * Task to download dependencies specified in {@link ChromiumPlugin} and configure the Chromium build to integrate them. + * Used by declaring a new task in a {@code build.gradle} file: * <pre> * task myTaskName(type: BuildConfigGenerator) { * repositoryPath 'build_files_and_repository_location/' @@ -30,47 +29,47 @@ * </pre> */ class BuildConfigGenerator extends DefaultTask { - private static final BUILD_GN_TOKEN_START = "# === Generated Code Start ===" - private static final BUILD_GN_TOKEN_END = "# === Generated Code End ===" - private static final BUILD_GN_GEN_PATTERN = Pattern.compile( - "${BUILD_GN_TOKEN_START}(.*)${BUILD_GN_TOKEN_END}", - Pattern.DOTALL) - private static final GEN_REMINDER = "# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.\n" - private static final DEPS_TOKEN_START = "# === ANDROID_DEPS Generated Code Start ===" - private static final DEPS_TOKEN_END = "# === ANDROID_DEPS Generated Code End ===" - private static final DEPS_GEN_PATTERN = Pattern.compile( - "${DEPS_TOKEN_START}(.*)${DEPS_TOKEN_END}", - Pattern.DOTALL) - private static final DOWNLOAD_DIRECTORY_NAME = "libs" - // The 3pp bot now adds an epoch to the version tag, this needs to be kept - // in sync with 3pp epoch at: + + private static final String BUILD_GN_TOKEN_START = '# === Generated Code Start ===' + private static final String BUILD_GN_TOKEN_END = '# === Generated Code End ===' + private static final Pattern BUILD_GN_GEN_PATTERN = Pattern.compile( + "${BUILD_GN_TOKEN_START}(.*)${BUILD_GN_TOKEN_END}", Pattern.DOTALL) + private static final String GEN_REMINDER = + '# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.\n' + private static final String DEPS_TOKEN_START = '# === ANDROID_DEPS Generated Code Start ===' + private static final String DEPS_TOKEN_END = '# === ANDROID_DEPS Generated Code End ===' + private static final Pattern DEPS_GEN_PATTERN = Pattern.compile( + "${DEPS_TOKEN_START}(.*)${DEPS_TOKEN_END}", Pattern.DOTALL) + private static final String DOWNLOAD_DIRECTORY_NAME = 'libs' + // The 3pp bot now adds an epoch to the version tag, this needs to be kept in sync with 3pp epoch at: + /* groovylint-disable-next-line LineLength */ // https://source.chromium.org/chromium/infra/infra/+/master:recipes/recipe_modules/support_3pp/resolved_spec.py?q=symbol:PACKAGE_EPOCH&ss=chromium - private static final THREEPP_EPOCH = "2" + private static final String THREEPP_EPOCH = '2' // Some libraries are hosted in Chromium's //third_party directory. This is a mapping between // them so they can be used instead of android_deps pulling in its own copy. - public static final def EXISTING_LIBS = [ - 'com_ibm_icu_icu4j': '//third_party/icu4j:icu4j_java', - 'com_almworks_sqlite4java_sqlite4java': '//third_party/sqlite4java:sqlite4java_java', - 'com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework': + static final Map<String, String> EXISTING_LIBS = [ + com_ibm_icu_icu4j: '//third_party/icu4j:icu4j_java', + com_almworks_sqlite4java_sqlite4java: '//third_party/sqlite4java:sqlite4java_java', + com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework: '//third_party/accessibility_test_framework:accessibility_test_framework_java', - 'junit_junit': '//third_party/junit:junit', - 'org_bouncycastle_bcprov_jdk15on': '//third_party/bouncycastle:bouncycastle_java', - 'org_hamcrest_hamcrest_core': '//third_party/hamcrest:hamcrest_core_java', - 'org_hamcrest_hamcrest_integration': '//third_party/hamcrest:hamcrest_integration_java', - 'org_hamcrest_hamcrest_library': '//third_party/hamcrest:hamcrest_library_java', + junit_junit: '//third_party/junit:junit', + org_bouncycastle_bcprov_jdk15on: '//third_party/bouncycastle:bouncycastle_java', + org_hamcrest_hamcrest_core: '//third_party/hamcrest:hamcrest_core_java', + org_hamcrest_hamcrest_integration: '//third_party/hamcrest:hamcrest_integration_java', + org_hamcrest_hamcrest_library: '//third_party/hamcrest:hamcrest_library_java', ] // Prefixes of autorolled libraries in //third_party/android_deps_autorolled. - public static final def AUTOROLLED_LIB_PREFIXES = [ ] + static final List<String> AUTOROLLED_LIB_PREFIXES = [] - public static final def AUTOROLLED_REPO_PATH = 'third_party/android_deps_autorolled' + static final String AUTOROLLED_REPO_PATH = 'third_party/android_deps_autorolled' - public static final def COPYRIGHT_HEADER = """\ + static final String COPYRIGHT_HEADER = '''\ # Copyright 2021 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. - """.stripIndent() + '''.stripIndent() /** * Directory where the artifacts will be downloaded and where files will be generated. @@ -81,86 +80,281 @@ @Input String repositoryPath - /** - * Relative path to the Chromium source root from the build.gradle file. - */ + /** Relative path to the Chromium source root from the build.gradle file. */ @Input String chromiumSourceRoot - /** - * Name of the cipd root package. - */ + /** Name of the cipd root package. */ @Input String cipdBucket - /** - * Skips license file import. - */ + /** Skips license file import. */ @Input boolean skipLicenses - /** - * Array with visibility for targets which are not listed in build.gradle - */ + /** Array with visibility for targets which are not listed in build.gradle */ @Input String[] internalTargetVisibility - /** - * Whether to ignore DEPS file. - */ + /** Whether to ignore DEPS file. */ @Input boolean ignoreDEPS - /** - * The URI of the file BuildConfigGenerator.groovy - */ + /** The URI of the file BuildConfigGenerator.groovy */ @Input @SourceURI URI sourceUri + static String translateTargetName(String targetName) { + if (isPlayServicesTarget(targetName)) { + return targetName.replaceFirst('com_', '').replaceFirst('android_gms_', '') + } + return targetName + } + + static boolean isPlayServicesTarget(String dependencyId) { + // Firebase has historically been treated as a part of play services, so it counts here for backwards + // compatibility. Datatransport is new as of 2019 and is used by many play services libraries. + return Pattern.matches('.*google.*(play_services|firebase|datatransport).*', dependencyId) + } + + static String makeOwners() { + // Make it easier to upgrade existing dependencies without full third_party review. + return 'file://third_party/android_deps/OWNERS\n' + } + + static String makeReadme(ChromiumDepGraph.DependencyDescription dependency) { + List<String> licenseStrings = [] + for (ChromiumDepGraph.LicenseSpec license : dependency.licenses) { + // Replace license names with ones that are whitelisted, see third_party/PRESUBMIT.py + switch (license.name) { + case 'The Apache License, Version 2.0': + case 'The Apache Software License, Version 2.0': + licenseStrings.add('Apache Version 2.0') + break + case 'GNU General Public License, version 2, with the Classpath Exception': + licenseStrings.add('GPL v2 with the classpath exception') + break + default: + licenseStrings.add(license.name) + } + } + String licenseString = String.join(', ', licenseStrings) + + boolean securityCritical = dependency.supportsAndroid && dependency.isShipped + String licenseFile = dependency.isShipped ? 'LICENSE' : 'NOT_SHIPPED' + + return """\ + Name: ${dependency.displayName} + Short Name: ${dependency.name} + URL: ${dependency.url} + Version: ${dependency.version} + License: ${licenseString} + License File: ${licenseFile} + Security Critical: ${securityCritical ? 'yes' : 'no'} + ${dependency.licenseAndroidCompatible ? 'License Android Compatible: yes' : ''} + Description: + ${dependency.description} + + Local Modifications: + No modifications. + """.stripIndent() + } + + static String makeCipdYaml(ChromiumDepGraph.DependencyDescription dependency, String cipdBucket, String repoPath) { + String cipdVersion = "${THREEPP_EPOCH}@${dependency.version}.${dependency.cipdSuffix}" + String cipdPath = "${cipdBucket}/${repoPath}" + // CIPD does not allow uppercase in names. + cipdPath += "/${DOWNLOAD_DIRECTORY_NAME}/$dependency.directoryName" + + // NOTE: The fetch_all.py script relies on the format of this file! See fetch_all.py:GetCipdPackageInfo(). + // NOTE: Keep the copyright year 2018 until this generated code is updated, avoiding annual churn of all + // cipd.yaml files. + return """\ + # Copyright 2018 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. + + # To create CIPD package run the following command. + # cipd create --pkg-def cipd.yaml -tag version:${cipdVersion} + package: ${cipdPath} + description: "${dependency.displayName}" + data: + - file: ${dependency.fileName} + """.stripIndent() + } + + static void validateLicenses(ChromiumDepGraph.DependencyDescription dependency) { + if (dependency.licenses.empty) { + throw new RuntimeException("Missing license for ${dependency.id}.") + } + + for (ChromiumDepGraph.LicenseSpec license : dependency.licenses) { + if (!license.path?.trim() && !license.url?.trim()) { + throw new RuntimeException("Missing license for ${dependency.id}. License Name was: ${license.name}") + } + } + } + + static void downloadLicenses(ChromiumDepGraph.DependencyDescription dependency, + String normalisedRepoPath, + ExecutorService downloadExecutor, + List<Future> downloadTasks) { + String depDir = "$normalisedRepoPath/${computeDepDir(dependency)}" + for (int i = 0; i < dependency.licenses.size(); ++i) { + ChromiumDepGraph.LicenseSpec license = dependency.licenses[i] + if (!license.path?.trim() && license.url?.trim()) { + String destFileSuffix = (dependency.licenses.size() > 1) ? "${i + 1}.tmp" : '' + File destFile = new File("${depDir}/LICENSE${destFileSuffix}") + downloadTasks.add(downloadExecutor.submit { + downloadFile(dependency.id, license.url, destFile) + if (destFile.text.contains('<html')) { + throw new RuntimeException('Found HTML in LICENSE file. Please add an ' + + "override to ChromiumDepGraph.groovy for ${dependency.id}.") + } + }) + } + } + } + + static void mergeLicenses(ChromiumDepGraph.DependencyDescription dependency, String normalisedRepoPath) { + String depDir = computeDepDir(dependency) + File outFile = new File("${normalisedRepoPath}/${depDir}/LICENSE") + + if (dependency.licenses.size() == 1) { + String licensePath0 = dependency.licenses.get(0).path?.trim() + if (licensePath0) { + outFile.write(new File("${normalisedRepoPath}/${licensePath0}").text) + } + return + } + + outFile.write('Third-Party Software Licenses\n') + for (int i = 0; i < dependency.licenses.size(); ++i) { + ChromiumDepGraph.LicenseSpec licenseSpec = dependency.licenses[i] + outFile.append("\n${i + 1}. ${licenseSpec.name}\n\n") + String licensePath = licenseSpec.path ? licenseSpec.path.trim() : "${depDir}/LICENSE${i + 1}.tmp" + outFile.append(new File("${normalisedRepoPath}/${licensePath}").text) + } + } + + static String make3ppPb(ChromiumDepGraph.DependencyDescription dependency, String cipdBucket, String repoPath) { + String pkgPrefix = "${cipdBucket}/${repoPath}/${DOWNLOAD_DIRECTORY_NAME}" + + return COPYRIGHT_HEADER + '\n' + GEN_REMINDER + """ + create { + source { + script { name: "fetch.py" } + patch_version: "${dependency.cipdSuffix}" + } + } + + upload { + pkg_prefix: "${pkgPrefix}" + universal: true + } + """.stripIndent() + } + + static String make3ppFetch(Template fetchTemplate, ChromiumDepGraph.DependencyDescription dependency) { + Map bindMap = [ + copyrightHeader: COPYRIGHT_HEADER, + dependency: dependency, + ] + return fetchTemplate.make(bindMap).toString() + } + + static String jsonDump(Object obj) { + return JsonOutput.prettyPrint(JsonOutput.toJson(obj)) + } + + static void printDump(Object obj) { + // Cannot reference logger directly due to this being a static method. + /* groovylint-disable-next-line UnnecessaryGetter */ + getLogger().warn(jsonDump(obj)) + } + + static HttpURLConnection connectAndFollowRedirects(String id, String sourceUrl) { + URL urlObj = new URL(sourceUrl) + HttpURLConnection connection + for (int i = 0; i < 10; ++i) { + // Several deps use this URL for their license, but it just points to license + // *template*. Generally the actual license can be found in the source code. + if (sourceUrl.contains('://opensource.org/licenses')) { + throw new RuntimeException('Found templated license URL for dependency ' + + id + ': ' + sourceUrl + + '. You will need to edit PROPERTY_OVERRIDES for this dep.') + } + connection = urlObj.openConnection() + switch (connection.responseCode) { + case HttpURLConnection.HTTP_MOVED_PERM: + case HttpURLConnection.HTTP_MOVED_TEMP: + String location = connection.getHeaderField('Location') + urlObj = new URL(urlObj, location) + continue + case HttpURLConnection.HTTP_OK: + return connection + default: + throw new RuntimeException("Url had statusCode=$connection.responseCode: $sourceUrl") + } + } + throw new RuntimeException("Url in redirect loop: $sourceUrl") + } + + static void downloadFile(String id, String sourceUrl, File destinationFile) { + destinationFile.withOutputStream { out -> + try { + out << connectAndFollowRedirects(id, sourceUrl).inputStream + } catch (Exception e) { + throw new RuntimeException("Failed to fetch license for $id url: $sourceUrl", e) + } + } + } + @TaskAction void main() { // Do not run task on subprojects. - if (project != project.getRootProject()) return + if (project != project.rootProject) { + return + } - skipLicenses = skipLicenses || project.hasProperty("skipLicenses") + skipLicenses = skipLicenses ?: project.hasProperty('skipLicenses') Path fetchTemplatePath = Paths.get(sourceUri).resolveSibling('3ppFetch.template') - def fetchTemplate = new groovy.text.SimpleTemplateEngine() - .createTemplate(fetchTemplatePath.toFile()) + Template fetchTemplate = new SimpleTemplateEngine().createTemplate(fetchTemplatePath.toFile()) - def subprojects = new HashSet<Project>() + Set<Project> subprojects = [] as Set subprojects.add(project) subprojects.addAll(project.subprojects) - def graph = new ChromiumDepGraph(projects: subprojects, logger: project.logger, - skipLicenses: skipLicenses) - def normalisedRepoPath = normalisePath(repositoryPath) + ChromiumDepGraph graph = new ChromiumDepGraph( + projects: subprojects, logger: project.logger, skipLicenses: skipLicenses) + String normalisedRepoPath = normalisePath(repositoryPath) // 1. Parse the dependency data graph.collectDependencies() // 2. Import artifacts into the local repository - def dependencyDirectories = [] - def downloadExecutor = Executors.newCachedThreadPool() - def downloadTasks = [] - def mergeLicensesDeps = [] + List<String> dependencyDirectories = [] + ExecutorService downloadExecutor = Executors.newCachedThreadPool() + List<Future> downloadTasks = [] + List<ChromiumDepGraph.DependencyDescription> mergeLicensesDeps = [] graph.dependencies.values().each { dependency -> if (excludeDependency(dependency) || computeJavaGroupForwardingTarget(dependency) != null) { return } - def dependencyForLogging = dependency.clone() + ChromiumDepGraph.DependencyDescription dependencyForLogging = dependency.clone() // jsonDump() throws StackOverflowError for ResolvedArtifact. dependencyForLogging.artifact = null logger.debug "Processing ${dependency.name}: \n${jsonDump(dependencyForLogging)}" - def depDir = computeDepDir(dependency) - def absoluteDepDir = "${normalisedRepoPath}/${depDir}" + String depDir = computeDepDir(dependency) + String absoluteDepDir = "${normalisedRepoPath}/${depDir}" dependencyDirectories.add(depDir) if (new File("${absoluteDepDir}/${dependency.fileName}").exists()) { - getLogger().quiet("${dependency.id} exists, skipping.") + logger.quiet("${dependency.id} exists, skipping.") return } @@ -170,25 +364,21 @@ } new File("${absoluteDepDir}/README.chromium").write(makeReadme(dependency)) - new File("${absoluteDepDir}/cipd.yaml").write(makeCipdYaml(dependency, cipdBucket, - repositoryPath)) + new File("${absoluteDepDir}/cipd.yaml").write(makeCipdYaml(dependency, cipdBucket, repositoryPath)) new File("${absoluteDepDir}/OWNERS").write(makeOwners()) // Enable 3pp flow for //third_party/android_deps only. // TODO(crbug.com/1132368): Enable 3pp flow for subprojects as well. - if (repositoryPath == "third_party/android_deps") { + if (repositoryPath == 'third_party/android_deps') { if (dependency.fileUrl) { - def absoluteDep3ppDir = "${absoluteDepDir}/3pp" + String absoluteDep3ppDir = "${absoluteDepDir}/3pp" new File(absoluteDep3ppDir).mkdirs() - new File("${absoluteDep3ppDir}/3pp.pb").write(make3ppPb(dependency, - cipdBucket, - repositoryPath)) - def fetchFile = new File("${absoluteDep3ppDir}/fetch.py") + new File("${absoluteDep3ppDir}/3pp.pb").write(make3ppPb(dependency, cipdBucket, repositoryPath)) + File fetchFile = new File("${absoluteDep3ppDir}/fetch.py") fetchFile.write(make3ppFetch(fetchTemplate, dependency)) fetchFile.setExecutable(true, false) } else { - throw new RuntimeException("Failed to generate 3pp files for ${dependency.id} " - + "due to empty file url.") + throw new RuntimeException("Failed to generate 3pp files for ${dependency.id} with empty fileUrl.") } } @@ -200,7 +390,7 @@ } downloadExecutor.shutdown() // Check for exceptions. - for (def task : downloadTasks) { + for (Future task : downloadTasks) { task.get() } @@ -214,153 +404,97 @@ updateDepsDeclaration(graph, cipdBucket, repositoryPath, "${normalisedRepoPath}/../../DEPS") } - dependencyDirectories.sort { path1, path2 -> return path1.compareTo(path2) } + dependencyDirectories.sort { path1, path2 -> return path1 <=> path2 } updateReadmeReferenceFile(dependencyDirectories, "${normalisedRepoPath}/additional_readme_paths.json") } - private static String computeDepDir(ChromiumDepGraph.DependencyDescription dependency) { - return "${DOWNLOAD_DIRECTORY_NAME}/${dependency.directoryName}" - } - - private void updateBuildTargetDeclaration(ChromiumDepGraph depGraph, String normalisedRepoPath) { - File buildFile = new File("${normalisedRepoPath}/BUILD.gn"); - def sb = new StringBuilder() - - // Comparator to sort the dependency in alphabetical order, with the visible ones coming - // before all the internal ones. - def dependencyComparator = { dependency1, dependency2 -> - def visibilityResult = Boolean.compare(dependency1.visible, dependency2.visible) - if (visibilityResult != 0) return -visibilityResult - - return dependency1.id.compareTo(dependency2.id) - } - - def fixedDependencies = depGraph.dependencies.values().findAll { - dependency -> dependency.usedInBuild - } - fixedDependencies.sort(dependencyComparator).each { dependency -> - appendBuildTarget(dependency, depGraph.dependencies, sb) - } - - sb.append("if (!limit_android_deps) {\n") - def buildWithChromiumDependencies = depGraph.dependencies.values().findAll { - dependency -> !dependency.usedInBuild - } - buildWithChromiumDependencies.sort(dependencyComparator).each { dependency -> - appendBuildTarget(dependency, depGraph.dependencies, sb) - } - sb.append("}\n") - - def out = "${BUILD_GN_TOKEN_START}\n${sb.toString()}\n${BUILD_GN_TOKEN_END}" - if (buildFile.exists()) { - def matcher = BUILD_GN_GEN_PATTERN.matcher(buildFile.getText()) - if (!matcher.find()) throw new IllegalStateException("BUILD.gn insertion point not found.") - out = matcher.replaceFirst(out) - } else { - out = "import(\"//build/config/android/rules.gni\")\n" + out - } - buildFile.write(out) - } - - public void appendBuildTarget(ChromiumDepGraph.DependencyDescription dependency, - Map<String, ChromiumDepGraph.DependencyDescription> allDependencies, - StringBuilder sb) { + void appendBuildTarget(ChromiumDepGraph.DependencyDescription dependency, + Map<String, ChromiumDepGraph.DependencyDescription> allDependencies, + StringBuilder sb) { if (excludeDependency(dependency) || !dependency.generateTarget) { return } - def targetName = translateTargetName(dependency.id) + "_java" - def javaGroupTarget = computeJavaGroupForwardingTarget(dependency) + String targetName = translateTargetName(dependency.id) + '_java' + String javaGroupTarget = computeJavaGroupForwardingTarget(dependency) if (javaGroupTarget != null) { assert dependency.extension == 'jar' || dependency.extension == 'aar' sb.append(""" - java_group("${targetName}") { - deps = [ \"${javaGroupTarget}\" ] - """.stripIndent()) - if (dependency.testOnly) sb.append(" testonly = true\n") - sb.append("}\n\n") + java_group("${targetName}") { + deps = [ "${javaGroupTarget}" ] + """.stripIndent()) + if (dependency.testOnly) { + sb.append(' testonly = true\n') + } + sb.append('}\n\n') return - } + } - def depsStr = "" - if (!dependency.children.isEmpty()) { - dependency.children.each { childDep -> - def dep = allDependencies[childDep] - if (dep.exclude) { - return - } - // Special case: If a child dependency is an existing lib, rather than skipping - // it, replace the child dependency with the existing lib. - def existingLib = EXISTING_LIBS.get(dep.id) - def depTargetName = translateTargetName(dep.id) + "_java" - if (existingLib != null) { - depsStr += "\"${existingLib}\"," - } else if (excludeDependency(dep)) { - def thirdPartyDir = (dep.id.startsWith("androidx")) ? "androidx" : "android_deps" - depsStr += "\"//third_party/${thirdPartyDir}:${depTargetName}\"," - } else if (dep.id == "com_google_android_material_material") { - // Material design is pulled in via doubledown, should - // use the variable instead of the real target. - depsStr += "\"\\\$material_design_target\"," - } else { - depsStr += "\":${depTargetName}\"," - } + String depsStr = '' + dependency.children?.each { childDep -> + ChromiumDepGraph.DependencyDescription dep = allDependencies[childDep] + if (dep.exclude) { + return + } + // Special case: If a child dependency is an existing lib, rather than skipping + // it, replace the child dependency with the existing lib. + String existingLib = EXISTING_LIBS.get(dep.id) + String depTargetName = translateTargetName(dep.id) + '_java' + if (existingLib) { + depsStr += "\"${existingLib}\"," + } else if (excludeDependency(dep)) { + String thirdPartyDir = (dep.id.startsWith('androidx')) ? 'androidx' : 'android_deps' + depsStr += "\"//third_party/${thirdPartyDir}:${depTargetName}\"," + } else if (dep.id == 'com_google_android_material_material') { + // Material design is pulled in via doubledown, should + // use the variable instead of the real target. + depsStr += '"$material_design_target",' + } else { + depsStr += "\":${depTargetName}\"," } } - def libPath = "${DOWNLOAD_DIRECTORY_NAME}/${dependency.directoryName}" + String libPath = "${DOWNLOAD_DIRECTORY_NAME}/${dependency.directoryName}" sb.append(GEN_REMINDER) if (dependency.extension == 'jar') { sb.append("""\ - java_prebuilt("${targetName}") { - jar_path = "${libPath}/${dependency.fileName}" - output_name = "${dependency.id}" - """.stripIndent()) + java_prebuilt("${targetName}") { + jar_path = "${libPath}/${dependency.fileName}" + output_name = "${dependency.id}" + """.stripIndent()) if (dependency.supportsAndroid) { - sb.append(" supports_android = true\n") + sb.append(' supports_android = true\n') } else { - // Save some time by not validating classpaths of desktop - // .jars. Also required to break a dependency cycle for - // errorprone. - sb.append(" enable_bytecode_checks = false\n") + // Save some time by not validating classpaths of desktop .jars. Also required to break a dependency + // cycle for errorprone. + sb.append(' enable_bytecode_checks = false\n') } } else if (dependency.extension == 'aar') { sb.append("""\ - android_aar_prebuilt("${targetName}") { - aar_path = "${libPath}/${dependency.fileName}" - info_path = "${libPath}/${dependency.id}.info" + android_aar_prebuilt("${targetName}") { + aar_path = "${libPath}/${dependency.fileName}" + info_path = "${libPath}/${dependency.id}.info" """.stripIndent()) } else { - throw new IllegalStateException("Dependency type should be JAR or AAR") - } + throw new IllegalStateException('Dependency type should be JAR or AAR') + } sb.append(generateBuildTargetVisibilityDeclaration(dependency)) - if (dependency.testOnly) sb.append(" testonly = true\n") - if (!depsStr.empty) sb.append(" deps = [${depsStr}]\n") + if (dependency.testOnly) { + sb.append(' testonly = true\n') + } + if (!depsStr.empty) { + sb.append(" deps = [${depsStr}]\n") + } addSpecialTreatment(sb, dependency.id, dependency.extension) - sb.append("}\n\n") - } + sb.append('}\n\n') + } - public static String translateTargetName(String targetName) { - if (isPlayServicesTarget(targetName)) { - return targetName.replaceFirst("com_", "").replaceFirst("android_gms_", "") - } - return targetName - } - - public static boolean isPlayServicesTarget(String dependencyId) { - // Firebase has historically been treated as a part of play services, so it counts here for - // backwards compatibility. Datatransport is new as of 2019 and is used by many play - // services libraries. - return Pattern.matches(".*google.*(play_services|firebase|datatransport).*", dependencyId) - } - - public String generateBuildTargetVisibilityDeclaration( - ChromiumDepGraph.DependencyDescription dependency) { - def sb = new StringBuilder() + String generateBuildTargetVisibilityDeclaration(ChromiumDepGraph.DependencyDescription dependency) { + StringBuilder sb = new StringBuilder() switch (dependency.id) { case 'com_google_android_material_material': sb.append(' # Material Design is pulled in via Doubledown, thus this target should not\n') @@ -382,10 +516,56 @@ return sb.toString() } - private String generateInternalTargetVisibilityLine() { - return 'visibility = ' + makeGnArray(internalTargetVisibility) + '\n' + boolean excludeDependency(ChromiumDepGraph.DependencyDescription dependency) { + if (dependency.exclude || EXISTING_LIBS.get(dependency.id)) { + return true + } + boolean isAndroidxRepository = (repositoryPath == 'third_party/androidx') + boolean isAndroidxDependency = (dependency.id.startsWith('androidx')) + if (isAndroidxRepository != isAndroidxDependency) { + return true + } + if (repositoryPath == AUTOROLLED_REPO_PATH) { + String targetName = translateTargetName(dependency.id) + '_java' + return !isTargetAutorolled(targetName) + } + // TODO(crbug.com/1184780): Remove this once org_robolectric_shadows_multidex is updated to a newer version + // which does not need jetify. + if (dependency.directoryName == 'org_robolectric_shadows_multidex') { + if (dependency.version != '4.3.1') { + throw new RuntimeException('Got a new version for org_robolectric_shadows_multidex. If this new ' + + "version doesn't need jetify, please move this dependency back to the " + + 'auto-generated section in //DEPS and //third_party/android_deps/BUILD.gn.') + } + return true + } + return false } + /** If |dependency| should be a java_group(), returns target to forward to. Returns null otherwise. */ + String computeJavaGroupForwardingTarget(ChromiumDepGraph.DependencyDescription dependency) { + String targetName = translateTargetName(dependency.id) + '_java' + return repositoryPath != AUTOROLLED_REPO_PATH && isTargetAutorolled(targetName) ? + "//${AUTOROLLED_REPO_PATH}:${targetName}" : null + } + + private static String makeGnArray(String[] values) { + StringBuilder sb = new StringBuilder() + sb.append('[') + for (String value : values) { + sb.append('"') + sb.append(value) + sb.append('",') + } + sb.replace(sb.length() - 1, sb.length(), ']') + return sb.toString() + } + + private static String computeDepDir(ChromiumDepGraph.DependencyDescription dependency) { + return "${DOWNLOAD_DIRECTORY_NAME}/${dependency.directoryName}" + } + + /* groovylint-disable-next-line MethodSize */ private static void addSpecialTreatment(StringBuilder sb, String dependencyId, String dependencyExtension) { addPreconditionsOverrideTreatment(sb, dependencyId) @@ -394,38 +574,41 @@ // accessibility_test_framework_java which requires_android. sb.append(' bypass_platform_checks = true\n') } - if (dependencyExtension == "aar" && - (dependencyId.startsWith('androidx') || - dependencyId.startsWith('com_android_support'))) { - // androidx and com_android_support libraries have duplicate resources such as 'primary_text_default_material_dark'. - sb.append(' resource_overlay = true\n') - } - switch(dependencyId) { + if (dependencyExtension == 'aar' && + (dependencyId.startsWith('androidx') || dependencyId.startsWith('com_android_support'))) { + // The androidx and com_android_support libraries have duplicate resources such as + // 'primary_text_default_material_dark'. + sb.append(' resource_overlay = true\n') + } + + switch (dependencyId) { case 'androidx_annotation_annotation': sb.append(' # https://crbug.com/989505\n') sb.append(' jar_excluded_patterns = ["META-INF/proguard/*"]\n') break case 'androidx_annotation_annotation_experimental': - sb.append("""\ + sb.append('''\ | # https://crbug.com/1213876 | deps = | [ "//third_party/android_deps:org_jetbrains_kotlin_kotlin_stdlib_java" ] - |""".stripMargin()) + |'''.stripMargin()) break case 'androidx_core_core': - sb.append('\n') - sb.append(' # Target has AIDL, but we do not support it yet: http://crbug.com/644439\n') - sb.append(' ignore_aidl = true\n') - sb.append('\n') - sb.append(' # Manifest and proguard config have just one entry: Adding (and -keep\'ing\n') - sb.append(' # android:appComponentFactory="androidx.core.app.CoreComponentFactory"\n') - sb.append(' # Chrome does not use this feature and it causes a scary stack trace to be\n') - sb.append(' # shown when incremental_install=true.\n') - sb.append(' ignore_manifest = true\n') - sb.append(' ignore_proguard_configs = true\n') + sb.with { + append('\n') + append(' # Target has AIDL, but we do not support it yet: http://crbug.com/644439\n') + append(' ignore_aidl = true\n') + append('\n') + append(' # Manifest and proguard config have just one entry: Adding (and -keep\'ing\n') + append(' # android:appComponentFactory="androidx.core.app.CoreComponentFactory"\n') + append(' # Chrome does not use this feature and it causes a scary stack trace to be\n') + append(' # shown when incremental_install=true.\n') + append(' ignore_manifest = true\n') + append(' ignore_proguard_configs = true\n') + } break case 'androidx_fragment_fragment': - sb.append("""\ + sb.append('''\ | deps += [ | "//third_party/android_deps/utils:java", | ] @@ -434,7 +617,7 @@ | use_classic_desugar = true | | bytecode_rewriter_target = "//build/android/bytecode:fragment_activity_replacer" - |""".stripMargin()) + |'''.stripMargin()) break case 'androidx_media_media': case 'androidx_versionedparcelable_versionedparcelable': @@ -486,7 +669,7 @@ // Necessary to not have duplicate classes after jetification. // They can be removed when we no longer jetify targets // that depend on com_android_support_support_compat. - sb.append("""\ + sb.append('''\ | jar_excluded_patterns = [ | "android/support/v4/graphics/drawable/IconCompatParcelizer.class", | "android/support/v4/os/ResultReceiver*", @@ -496,7 +679,7 @@ | "android/support/v4/os/IResultReceiver*", | ] | - |""".stripMargin()) + |'''.stripMargin()) break case 'com_android_support_transition': // Not specified in the POM, compileOnly dependency not supposed to be used unless @@ -510,13 +693,13 @@ // Necessary to not have identical classes after jetification. // They can be removed when we no longer jetify targets // that depend on com_android_support_versionedparcelable. - sb.append("""\ + sb.append('''\ | jar_excluded_patterns = [ | "android/support/v4/graphics/drawable/IconCompat.class", | "androidx/*", | ] | - |""".stripMargin()) + |'''.stripMargin()) break case 'com_google_ar_core': // Target .aar file contains .so libraries that need to be extracted, @@ -531,12 +714,14 @@ sb.append(' jar_excluded_patterns = ["*/ListenableFuture.class"]\n') break case 'com_google_guava_guava_android': - sb.append('\n') - sb.append(' # Add a dep to com_google_guava_listenablefuture_java\n') - sb.append(' # because androidx_concurrent_futures also depends on it and to avoid\n') - sb.append(' # defining ListenableFuture.class twice.\n') - sb.append(' deps += [":com_google_guava_listenablefuture_java"]\n') - sb.append(' jar_excluded_patterns += ["*/ListenableFuture.class"]\n') + sb.with { + append('\n') + append(' # Add a dep to com_google_guava_listenablefuture_java\n') + append(' # because androidx_concurrent_futures also depends on it and to avoid\n') + append(' # defining ListenableFuture.class twice.\n') + append(' deps += [":com_google_guava_listenablefuture_java"]\n') + append(' jar_excluded_patterns += ["*/ListenableFuture.class"]\n') + } break case 'com_google_code_findbugs_jsr305': case 'com_google_errorprone_error_prone_annotations': @@ -551,14 +736,14 @@ break case 'androidx_test_rules': // Target needs Android SDK deps which exist in third_party/android_sdk. - sb.append("""\ + sb.append('''\ | deps += [ | "//third_party/android_sdk:android_test_base_java", | "//third_party/android_sdk:android_test_mock_java", | "//third_party/android_sdk:android_test_runner_java", | ] | - |""".stripMargin()) + |'''.stripMargin()) break case 'androidx_test_espresso_espresso_contrib': case 'androidx_test_espresso_espresso_web': @@ -570,9 +755,9 @@ sb.append(' jar_excluded_patterns = [ "*xmlpull*" ]\n') break case 'androidx_preference_preference': - sb.append("""\ + sb.append('''\ | bytecode_rewriter_target = "//build/android/bytecode:fragment_activity_replacer" - |""".stripMargin()) + |'''.stripMargin()) // Replace broad library -keep rules with a more limited set in // chrome/android/java/proguard.flags instead. sb.append(' ignore_proguard_configs = true\n') @@ -592,25 +777,27 @@ sb.append(' ignore_manifest = true\n') break case 'com_google_protobuf_protobuf_javalite': - sb.append(' # Prebuilt protos in the runtime library.\n') - sb.append(' # If you want to use these protos, you should create a proto_java_library\n') - sb.append(' # target for them. See crbug.com/1103399 for discussion.\n') - sb.append(' jar_excluded_patterns = [\n') - sb.append(' "com/google/protobuf/Any*",\n') - sb.append(' "com/google/protobuf/Api*",\n') - sb.append(' "com/google/protobuf/Duration*",\n') - sb.append(' "com/google/protobuf/Empty*",\n') - sb.append(' "com/google/protobuf/FieldMask*",\n') - sb.append(' "com/google/protobuf/SourceContext*",\n') - sb.append(' "com/google/protobuf/Struct\\\\\\$1.class",\n') - sb.append(' "com/google/protobuf/Struct\\\\\\$Builder.class",\n') - sb.append(' "com/google/protobuf/Struct.class",\n') - sb.append(' "com/google/protobuf/StructOrBuilder.class",\n') - sb.append(' "com/google/protobuf/StructProto.class",\n') - sb.append(' "com/google/protobuf/Timestamp*",\n') - sb.append(' "com/google/protobuf/Type*",\n') - sb.append(' "com/google/protobuf/Wrappers*",\n') - sb.append(' ]') + sb.with { + append(' # Prebuilt protos in the runtime library.\n') + append(' # If you want to use these protos, you should create a proto_java_library\n') + append(' # target for them. See crbug.com/1103399 for discussion.\n') + append(' jar_excluded_patterns = [\n') + append(' "com/google/protobuf/Any*",\n') + append(' "com/google/protobuf/Api*",\n') + append(' "com/google/protobuf/Duration*",\n') + append(' "com/google/protobuf/Empty*",\n') + append(' "com/google/protobuf/FieldMask*",\n') + append(' "com/google/protobuf/SourceContext*",\n') + append(' "com/google/protobuf/Struct\\\\\\$1.class",\n') + append(' "com/google/protobuf/Struct\\\\\\$Builder.class",\n') + append(' "com/google/protobuf/Struct.class",\n') + append(' "com/google/protobuf/StructOrBuilder.class",\n') + append(' "com/google/protobuf/StructProto.class",\n') + append(' "com/google/protobuf/Timestamp*",\n') + append(' "com/google/protobuf/Type*",\n') + append(' "com/google/protobuf/Wrappers*",\n') + append(' ]') + } break case 'androidx_startup_startup_runtime': sb.append(' # Keeps emoji2 code. See http://crbug.com/1205141\n') @@ -655,27 +842,27 @@ sb.append(' # This target does not come with most of its dependencies and is\n') sb.append(' # only meant to be used by the resources shrinker. If you wish to use\n') sb.append(' # this for other purposes, change buildCompileNoDeps in build.gradle.\n') - sb.append(' visibility = [ "//build/android/gyp/resources_shrinker:*" ]\n') + sb.append(' visibility = [ "//build/android/unused_resources:*" ]\n') break case 'org_jetbrains_kotlinx_kotlinx_coroutines_android': - sb.append('requires_android = true') - break + sb.append('requires_android = true') + break } } private static void addPreconditionsOverrideTreatment(StringBuilder sb, String dependencyId) { - def targetName = translateTargetName(dependencyId) - switch(targetName) { - case "androidx_core_core": - case "com_google_guava_guava_android": - case "google_play_services_basement": - if (targetName == "com_google_guava_guava_android") { - // com_google_guava_guava_android is java_prebuilt(). - sb.append("bypass_platform_checks = true") - } - def libraryDep = "//third_party/android_deps/local_modifications/preconditions:" + - computePreconditionsStubLibraryForDep(dependencyId) - sb.append(""" + String targetName = translateTargetName(dependencyId) + switch (targetName) { + case 'androidx_core_core': + case 'com_google_guava_guava_android': + case 'google_play_services_basement': + if (targetName == 'com_google_guava_guava_android') { + // com_google_guava_guava_android is java_prebuilt(). + sb.append('bypass_platform_checks = true') + } + String libraryDep = '//third_party/android_deps/local_modifications/preconditions:' + + computePreconditionsStubLibraryForDep(dependencyId) + sb.append(""" | | jar_excluded_patterns = [] | if (!enable_java_asserts) { @@ -688,55 +875,107 @@ | ] | } |""".stripMargin()) - } + } } private static String computePreconditionsStubLibraryForDep(String dependencyId) { - def targetName = translateTargetName(dependencyId) + String targetName = translateTargetName(dependencyId) switch (targetName) { - case "androidx_core_core": - return "androidx_stub_preconditions_java" - case "com_google_guava_guava_android": - return "guava_stub_preconditions_java" - case "google_play_services_basement": - return "gms_stub_preconditions_java" + case 'androidx_core_core': + return 'androidx_stub_preconditions_java' + case 'com_google_guava_guava_android': + return 'guava_stub_preconditions_java' + case 'google_play_services_basement': + return 'gms_stub_preconditions_java' } return null } private static String computePreconditionsClassForDep(String dependencyId) { - def targetName = translateTargetName(dependencyId) + String targetName = translateTargetName(dependencyId) switch (targetName) { - case "androidx_core_core": - return "androidx/core/util/Preconditions.class" - case "com_google_guava_guava_android": - return "com/google/common/base/Preconditions.class" - case "google_play_services_basement": - return "com/google/android/gms/common/internal/Preconditions.class" + case 'androidx_core_core': + return 'androidx/core/util/Preconditions.class' + case 'com_google_guava_guava_android': + return 'com/google/common/base/Preconditions.class' + case 'google_play_services_basement': + return 'com/google/android/gms/common/internal/Preconditions.class' } return null } + private static void updateReadmeReferenceFile(List<String> directories, String readmePath) { + File refFile = new File(readmePath) + refFile.write(JsonOutput.prettyPrint(JsonOutput.toJson(directories)) + '\n') + } + + private void updateBuildTargetDeclaration(ChromiumDepGraph depGraph, String normalisedRepoPath) { + File buildFile = new File("${normalisedRepoPath}/BUILD.gn") + StringBuilder sb = new StringBuilder() + + // Comparator to sort the dependency in alphabetical order, with the visible ones coming + // before all the internal ones. + Closure dependencyComparator = { dependency1, dependency2 -> + int visibilityResult = Boolean.compare(dependency1.visible, dependency2.visible) + if (visibilityResult != 0) { + return -visibilityResult + } + return dependency1.id <=> dependency2.id + } + + List<ChromiumDepGraph.DependencyDescription> fixedDependencies = depGraph.dependencies.values().findAll { + dependency -> dependency.usedInBuild + } + fixedDependencies.sort(dependencyComparator).each { dependency -> + appendBuildTarget(dependency, depGraph.dependencies, sb) + } + + sb.append('if (!limit_android_deps) {\n') + List<ChromiumDepGraph.DependencyDescription> buildWithChromiumDependencies + buildWithChromiumDependencies = depGraph.dependencies.values().findAll { + dependency -> !dependency.usedInBuild + } + buildWithChromiumDependencies.sort(dependencyComparator).each { dependency -> + appendBuildTarget(dependency, depGraph.dependencies, sb) + } + sb.append('}\n') + + String out = "${BUILD_GN_TOKEN_START}\n$sb\n${BUILD_GN_TOKEN_END}" + if (buildFile.exists()) { + Matcher matcher = BUILD_GN_GEN_PATTERN.matcher(buildFile.text) + if (!matcher.find()) { + throw new IllegalStateException('BUILD.gn insertion point not found.') + } + out = matcher.replaceFirst(out) + } else { + out = 'import("//build/config/android/rules.gni")\n' + out + } + buildFile.write(out) + } + + private String generateInternalTargetVisibilityLine() { + return "visibility = ${makeGnArray(internalTargetVisibility)}\n" + } + private void updateDepsDeclaration(ChromiumDepGraph depGraph, String cipdBucket, String repoPath, String depsFilePath) { File depsFile = new File(depsFilePath) - def sb = new StringBuilder() + StringBuilder sb = new StringBuilder() // Note: The string we're inserting is nested 1 level, hence the 2 leading spaces. Same // applies to the multiline package declaration string below. - sb.append(" # Generated by //third_party/android_deps/fetch_all.py") + sb.append(' # Generated by //third_party/android_deps/fetch_all.py') // Comparator to sort the dependencies in alphabetical order. - def dependencyComparator = { dependency1, dependency2 -> - return dependency1.id.compareTo(dependency2.id) + Closure dependencyComparator = { dependency1, dependency2 -> + dependency1.id <=> dependency2.id } depGraph.dependencies.values().sort(dependencyComparator).each { dependency -> - if (excludeDependency(dependency) || - computeJavaGroupForwardingTarget(dependency) != null) { + if (excludeDependency(dependency) || computeJavaGroupForwardingTarget(dependency)) { return } - def depPath = "${DOWNLOAD_DIRECTORY_NAME}/${dependency.directoryName}" - def cipdPath = "${cipdBucket}/${repoPath}/${depPath}" + String depPath = "${DOWNLOAD_DIRECTORY_NAME}/${dependency.directoryName}" + String cipdPath = "${cipdBucket}/${repoPath}/${depPath}" sb.append("""\ | | 'src/${repoPath}/${depPath}': { @@ -752,56 +991,15 @@ |""".stripMargin()) } - def matcher = DEPS_GEN_PATTERN.matcher(depsFile.getText()) - if (!matcher.find()) throw new IllegalStateException("DEPS insertion point not found.") + Matcher matcher = DEPS_GEN_PATTERN.matcher(depsFile.text) + if (!matcher.find()) { + throw new IllegalStateException('DEPS insertion point not found.') + } depsFile.write(matcher.replaceFirst("${DEPS_TOKEN_START}\n${sb}\n ${DEPS_TOKEN_END}")) - } + } - private static void updateReadmeReferenceFile(List<String> directories, String readmePath) { - File refFile = new File(readmePath) - refFile.write(JsonOutput.prettyPrint(JsonOutput.toJson(directories)) + "\n") - } - - public boolean excludeDependency(ChromiumDepGraph.DependencyDescription dependency) { - if (dependency.exclude || EXISTING_LIBS.get(dependency.id) != null) { - return true - } - boolean isAndroidxRepository = (repositoryPath == "third_party/androidx") - boolean isAndroidxDependency = (dependency.id.startsWith("androidx")) - if (isAndroidxRepository != isAndroidxDependency) { - return true; - } - if (repositoryPath == AUTOROLLED_REPO_PATH) { - def targetName = translateTargetName(dependency.id) + "_java" - return !isTargetAutorolled(targetName) - } - // TODO(crbug.com/1184780): Remove this once org_robolectric_shadows_multidex - // is updated to a newer version which does not need jetify. - if (dependency.directoryName == "org_robolectric_shadows_multidex") { - if (dependency.version != "4.3.1") { - throw new RuntimeException("Got a new version for org_robolectric_shadows_multidex. " + - "If this new version don't need jetify, please move this dependency back to the " + - "auto-generated section in //DEPS and //third_party/android_deps/BUILD.gn.") - } - return true - } - return false - } - - /** - * If |dependency| should be a java_group(), returns target to forward to. Returns null - * otherwise. - */ - public String computeJavaGroupForwardingTarget(ChromiumDepGraph.DependencyDescription dependency) { - def targetName = translateTargetName(dependency.id) + "_java" - if (repositoryPath != AUTOROLLED_REPO_PATH && isTargetAutorolled(targetName)) { - return "//${AUTOROLLED_REPO_PATH}:${targetName}" - } - return null - } - - private boolean isTargetAutorolled(targetName) { - for (autorolledLibPrefix in AUTOROLLED_LIB_PREFIXES) { + private boolean isTargetAutorolled(String targetName) { + for (String autorolledLibPrefix in AUTOROLLED_LIB_PREFIXES) { if (targetName.startsWith(autorolledLibPrefix)) { return true } @@ -813,224 +1011,4 @@ return project.file("${chromiumSourceRoot}/${pathRelativeToChromiumRoot}").absolutePath } - private static String makeGnArray(String[] values) { - def sb = new StringBuilder(); - sb.append("["); - for (String value : values) { - sb.append("\""); - sb.append(value); - sb.append("\","); - } - sb.replace(sb.length() - 1, sb.length(), "]"); - return sb.toString(); - } - - static String makeOwners() { - // Make it easier to upgrade existing dependencies without full third_party review. - return "file://third_party/android_deps/OWNERS\n" - } - - static String makeReadme(ChromiumDepGraph.DependencyDescription dependency) { - def licenseStrings = [] - for (ChromiumDepGraph.LicenseSpec license : dependency.licenses) { - // Replace license names with ones that are whitelisted, see third_party/PRESUBMIT.py - switch (license.name) { - case "The Apache License, Version 2.0": - case "The Apache Software License, Version 2.0": - licenseStrings.add("Apache Version 2.0") - break - case "GNU General Public License, version 2, with the Classpath Exception": - licenseStrings.add("GPL v2 with the classpath exception") - break - default: - licenseStrings.add(license.name) - } - } - def licenseString = String.join(", ", licenseStrings) - - def securityCritical = dependency.supportsAndroid && dependency.isShipped - def licenseFile = dependency.isShipped? "LICENSE" : "NOT_SHIPPED" - - return """\ - Name: ${dependency.displayName} - Short Name: ${dependency.name} - URL: ${dependency.url} - Version: ${dependency.version} - License: ${licenseString} - License File: ${licenseFile} - Security Critical: ${securityCritical? "yes" : "no"} - ${dependency.licenseAndroidCompatible? "License Android Compatible: yes" : ""} - Description: - ${dependency.description} - - Local Modifications: - No modifications. - """.stripIndent() - } - - static String makeCipdYaml(ChromiumDepGraph.DependencyDescription dependency, String cipdBucket, - String repoPath) { - def cipdVersion = "${THREEPP_EPOCH}@${dependency.version}.${dependency.cipdSuffix}" - def cipdPath = "${cipdBucket}/${repoPath}" - // CIPD does not allow uppercase in names. - cipdPath += "/${DOWNLOAD_DIRECTORY_NAME}/" + dependency.directoryName - - // NOTE: the fetch_all.py script relies on the format of this file! - // See fetch_all.py:GetCipdPackageInfo(). - // NOTE: keep the copyright year 2018 until this generated code is - // updated, avoiding annual churn of all cipd.yaml files. - def str = """\ - # Copyright 2018 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. - - # To create CIPD package run the following command. - # cipd create --pkg-def cipd.yaml -tag version:${cipdVersion} - package: ${cipdPath} - description: "${dependency.displayName}" - data: - - file: ${dependency.fileName} - """.stripIndent() - - return str - } - - static void validateLicenses(ChromiumDepGraph.DependencyDescription dependency) { - if (dependency.licenses.isEmpty()) { - throw new RuntimeException("Missing license for ${dependency.id}.") - return - } - - for (ChromiumDepGraph.LicenseSpec license : dependency.licenses) { - if (!license.path?.trim() && !license.url?.trim()) { - def exceptionMessage = "Missing license for ${dependency.id}. " - + "License Name was: ${license.name}" - throw new RuntimeException(exceptionMessage) - } - } - } - - static void downloadLicenses(ChromiumDepGraph.DependencyDescription dependency, - String normalisedRepoPath, - ExecutorService downloadExecutor, - List<Future> downloadTasks) { - def depDir = normalisedRepoPath +"/" + computeDepDir(dependency) - for (int i = 0; i < dependency.licenses.size(); ++i) { - def license = dependency.licenses[i] - if (!license.path?.trim() && license.url?.trim()) { - def destFileSuffix = (dependency.licenses.size() > 1) ? "${i+1}.tmp" : "" - def destFile = new File("${depDir}/LICENSE${destFileSuffix}") - downloadTasks.add(downloadExecutor.submit { - downloadFile(dependency.id, license.url, destFile) - if (destFile.text.contains("<html")) { - throw new RuntimeException("Found HTML in LICENSE file. Please add an " - + "override to ChromiumDepGraph.groovy for ${dependency.id}.") - } - }) - } - } - } - - static void mergeLicenses(ChromiumDepGraph.DependencyDescription dependency, - String normalisedRepoPath) { - def depDir = computeDepDir(dependency) - def outFile = new File("${normalisedRepoPath}/${depDir}/LICENSE") - - if (dependency.licenses.size() == 1) { - def licensePath0 = dependency.licenses.get(0).path?.trim() - if (licensePath0) { - outFile.write(new File("${normalisedRepoPath}/${licensePath0}").text) - } - return - } - - outFile.write("Third-Party Software Licenses\n") - for (int i = 0; i < dependency.licenses.size(); ++i) { - def licenseSpec = dependency.licenses[i] - outFile.append("\n${i+1}. ${licenseSpec.name}\n\n") - def licensePath = (licenseSpec.path != null) - ? licenseSpec.path.trim() - : "${depDir}/LICENSE${i+1}.tmp" - outFile.append(new File("${normalisedRepoPath}/${licensePath}").text) - } - } - - static String make3ppPb(ChromiumDepGraph.DependencyDescription dependency, - String cipdBucket, String repoPath) { - def pkgPrefix = "${cipdBucket}/${repoPath}/${DOWNLOAD_DIRECTORY_NAME}" - - def str = COPYRIGHT_HEADER + "\n" + GEN_REMINDER - str += """ - create { - source { - script { name: "fetch.py" } - patch_version: "${dependency.cipdSuffix}" - } - } - - upload { - pkg_prefix: "${pkgPrefix}" - universal: true - } - """.stripIndent() - - return str - - } - - static String make3ppFetch(Template fetchTemplate, - ChromiumDepGraph.DependencyDescription dependency) { - def bindMap = [ - copyrightHeader: COPYRIGHT_HEADER, - dependency: dependency, - ] - return fetchTemplate.make(bindMap).toString() - } - - static String jsonDump(obj) { - return JsonOutput.prettyPrint(JsonOutput.toJson(obj)) - } - - static void printDump(obj) { - getLogger().warn(jsonDump(obj)) - } - - static HttpURLConnection connectAndFollowRedirects(String id, String sourceUrl) { - URL urlObj = new URL(sourceUrl) - HttpURLConnection connection - for (int i = 0; i < 10; ++i) { - // Several deps use this URL for their license, but it just points to license - // *template*. Generally the actual license can be found in the source code. - if (sourceUrl.contains("://opensource.org/licenses")) { - throw new RuntimeException("Found templated license URL for dependency " - + id + ": " + sourceUrl - + ". You will need to edit PROPERTY_OVERRIDES for this dep.") - } - connection = urlObj.openConnection() - switch (connection.getResponseCode()) { - case HttpURLConnection.HTTP_MOVED_PERM: - case HttpURLConnection.HTTP_MOVED_TEMP: - String location = connection.getHeaderField("Location"); - urlObj = new URL(urlObj, location); - continue - case HttpURLConnection.HTTP_OK: - return connection - default: - throw new RuntimeException( - "Url had statusCode=" + connection.getResponseCode() + ": " + sourceUrl) - } - } - throw new RuntimeException("Url in redirect loop: " + sourceUrl) - } - - static void downloadFile(String id, String sourceUrl, File destinationFile) { - destinationFile.withOutputStream { out -> - try { - out << connectAndFollowRedirects(id, sourceUrl).getInputStream() - } catch (Exception e) { - throw new RuntimeException("Failed to fetch license for " + id + " url: " + sourceUrl, e) - } - } - } - }
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy index 5cd5295..8470e45 100644 --- a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy +++ b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
@@ -11,328 +11,323 @@ import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.artifacts.ResolvedModuleVersion import org.gradle.api.artifacts.component.ComponentIdentifier -import org.gradle.api.artifacts.result.ArtifactResult -import org.gradle.api.artifacts.result.ResolvedArtifactResult import org.gradle.api.logging.Logger /** - * Parses the project dependencies and generates a graph of - * {@link ChromiumDepGraph.DependencyDescription} objects to make the data manipulation easier. + * Parses the project dependencies and generates a graph of {@link ChromiumDepGraph.DependencyDescription} objects to + * make the data manipulation easier. */ class ChromiumDepGraph { - final def dependencies = new HashMap<String, DependencyDescription>() - final def lowerVersionOverride = new HashSet<String>() - // Some libraries don't properly fill their POM with the appropriate licensing information. - // It is provided here from manual lookups. Note that licenseUrl must provide textual content - // rather than be an html page. - final def PROPERTY_OVERRIDES = [ - 'androidx_multidex_multidex': new PropertyOverride( + // Some libraries don't properly fill their POM with the appropriate licensing information. It is provided here from + // manual lookups. Note that licenseUrl must provide textual content rather than be an html page. + static final Map<String, PropertyOverride> PROPERTY_OVERRIDES = [ + androidx_multidex_multidex: new PropertyOverride( url: 'https://maven.google.com/androidx/multidex/multidex/2.0.0/multidex-2.0.0.aar'), - 'com_android_tools_desugar_jdk_libs': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/google/desugar_jdk_libs/master/LICENSE", - licenseName: "GNU General Public License, version 2, with the Classpath Exception", + com_android_tools_desugar_jdk_libs: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/google/desugar_jdk_libs/master/LICENSE', + licenseName: 'GNU General Public License, version 2, with the Classpath Exception', generateTarget: false), - 'com_android_tools_desugar_jdk_libs_configuration': new PropertyOverride( - licensePath: "licenses/desugar_jdk_libs_configuration.txt", - licenseName: "BSD 3-Clause", + com_android_tools_desugar_jdk_libs_configuration: new PropertyOverride( + licensePath: 'licenses/desugar_jdk_libs_configuration.txt', + licenseName: 'BSD 3-Clause', generateTarget: false), - 'backport_util_concurrent_backport_util_concurrent': new PropertyOverride( - licensePath: "licenses/CC01.0.txt", - licenseName: "CC0 1.0"), - 'classworlds_classworlds': new PropertyOverride( - description: "A class loader framework.", - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'com_github_kevinstern_software_and_algorithms': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/KevinStern/software-and-algorithms/master/LICENSE", - licenseName: "MIT License"), - 'com_google_android_datatransport_transport_api': new PropertyOverride( - description: "Interfaces for data logging in GmsCore SDKs."), - 'com_google_android_datatransport_transport_backend_cct': new PropertyOverride( + backport_util_concurrent_backport_util_concurrent: new PropertyOverride( + licensePath: 'licenses/CC01.0.txt', + licenseName: 'CC0 1.0'), + classworlds_classworlds: new PropertyOverride( + description: 'A class loader framework.', + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + com_github_kevinstern_software_and_algorithms: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/KevinStern/software-and-algorithms/master/LICENSE', + licenseName: 'MIT License'), + com_google_android_datatransport_transport_api: new PropertyOverride( + description: 'Interfaces for data logging in GmsCore SDKs.'), + com_google_android_datatransport_transport_backend_cct: new PropertyOverride( exclude: true), // We're not using datatransport functionality. - 'com_google_android_datatransport_transport_runtime': new PropertyOverride( + com_google_android_datatransport_transport_runtime: new PropertyOverride( exclude: true), // We're not using datatransport functionality. - 'com_google_android_gms_play_services_cloud_messaging': new PropertyOverride( - description: "Firebase Cloud Messaging library that interfaces with GmsCore."), - 'com_google_auto_auto_common': new PropertyOverride( - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_auto_service_auto_service': new PropertyOverride( - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_auto_service_auto_service_annotations': new PropertyOverride( - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_auto_value_auto_value_annotations': new PropertyOverride( - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_code_findbugs_jFormatString': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/spotbugs/spotbugs/master/spotbugs/licenses/LICENSE.txt", - licenseName: "GNU Lesser Public License"), - 'com_google_code_gson_gson': new PropertyOverride( - url: "https://github.com/google/gson", - licenseUrl: "https://raw.githubusercontent.com/google/gson/master/LICENSE", - licenseName: "Apache 2.0"), - 'com_google_errorprone_error_prone_annotation': new PropertyOverride( - url: "https://errorprone.info/", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_errorprone_error_prone_annotations': new PropertyOverride( - url: "https://errorprone.info/", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_firebase_firebase_annotations': new PropertyOverride( - description: "Common annotations for Firebase SKDs."), - 'com_google_firebase_firebase_common': new PropertyOverride( - description: "Common classes for Firebase SDKs."), - 'com_google_firebase_firebase_components': new PropertyOverride( - description: "Provides dependency management for Firebase SDKs."), - 'com_google_firebase_firebase_datatransport': new PropertyOverride( + com_google_android_gms_play_services_cloud_messaging: new PropertyOverride( + description: 'Firebase Cloud Messaging library that interfaces with GmsCore.'), + com_google_auto_auto_common: new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_auto_service_auto_service: new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_auto_service_auto_service_annotations: new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_auto_value_auto_value_annotations: new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_code_findbugs_jFormatString: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/spotbugs/spotbugs/master/spotbugs/licenses/LICENSE.txt', + licenseName: 'GNU Lesser Public License'), + com_google_code_gson_gson: new PropertyOverride( + url: 'https://github.com/google/gson', + licenseUrl: 'https://raw.githubusercontent.com/google/gson/master/LICENSE', + licenseName: 'Apache 2.0'), + com_google_errorprone_error_prone_annotation: new PropertyOverride( + url: 'https://errorprone.info/', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_errorprone_error_prone_annotations: new PropertyOverride( + url: 'https://errorprone.info/', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_firebase_firebase_annotations: new PropertyOverride( + description: 'Common annotations for Firebase SKDs.'), + com_google_firebase_firebase_common: new PropertyOverride( + description: 'Common classes for Firebase SDKs.'), + com_google_firebase_firebase_components: new PropertyOverride( + description: 'Provides dependency management for Firebase SDKs.'), + com_google_firebase_firebase_datatransport: new PropertyOverride( exclude: true), // We're not using datatransport functionality. - 'com_google_firebase_firebase_encoders_json': new PropertyOverride( - description: "JSON encoders used in Firebase SDKs."), - 'com_google_firebase_firebase_encoders': new PropertyOverride( - description: "Commonly used encoders for Firebase SKDs."), - 'com_google_firebase_firebase_iid_interop': new PropertyOverride( - description: "Interface library for Firebase IID SDK."), - 'com_google_firebase_firebase_iid': new PropertyOverride( - description: "Firebase IID SDK to get access to Instance IDs."), - 'com_google_firebase_firebase_installations_interop': new PropertyOverride( - description: "Interface library for Firebase Installations SDK."), - 'com_google_firebase_firebase_installations': new PropertyOverride( - description: "Firebase Installations SDK containing the client libraries to manage FIS."), - 'com_google_firebase_firebase_measurement_connector': new PropertyOverride( - description: "Bridge interfaces for Firebase analytics into GmsCore."), - 'com_google_firebase_firebase_messaging': new PropertyOverride( - description: "Firebase Cloud Messaging SDK to send and receive push messages via FCM."), - 'com_google_googlejavaformat_google_java_format': new PropertyOverride( - url: "https://github.com/google/google-java-format", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_guava_failureaccess': new PropertyOverride( - url: "https://github.com/google/guava", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_guava_guava': new PropertyOverride( - url: "https://github.com/google/guava", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_guava_guava_android': new PropertyOverride( - url: "https://github.com/google/guava", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_guava_listenablefuture': new PropertyOverride( - url: "https://github.com/google/guava", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'nekohtml_nekohtml': new PropertyOverride( - description: "NekoHTML is a simple HTML scanner and tag balancer."), - 'nekohtml_xercesMinimal': new PropertyOverride( - description: "Only contains necessary framework & Xerces2 classes", + com_google_firebase_firebase_encoders_json: new PropertyOverride( + description: 'JSON encoders used in Firebase SDKs.'), + com_google_firebase_firebase_encoders: new PropertyOverride( + description: 'Commonly used encoders for Firebase SKDs.'), + com_google_firebase_firebase_iid_interop: new PropertyOverride( + description: 'Interface library for Firebase IID SDK.'), + com_google_firebase_firebase_iid: new PropertyOverride( + description: 'Firebase IID SDK to get access to Instance IDs.'), + com_google_firebase_firebase_installations_interop: new PropertyOverride( + description: 'Interface library for Firebase Installations SDK.'), + com_google_firebase_firebase_installations: new PropertyOverride( + description: 'Firebase Installations SDK containing the client libraries to manage FIS.'), + com_google_firebase_firebase_measurement_connector: new PropertyOverride( + description: 'Bridge interfaces for Firebase analytics into GmsCore.'), + com_google_firebase_firebase_messaging: new PropertyOverride( + description: 'Firebase Cloud Messaging SDK to send and receive push messages via FCM.'), + com_google_googlejavaformat_google_java_format: new PropertyOverride( + url: 'https://github.com/google/google-java-format', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_guava_failureaccess: new PropertyOverride( + url: 'https://github.com/google/guava', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_guava_guava: new PropertyOverride( + url: 'https://github.com/google/guava', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_guava_guava_android: new PropertyOverride( + url: 'https://github.com/google/guava', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_guava_listenablefuture: new PropertyOverride( + url: 'https://github.com/google/guava', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + nekohtml_nekohtml: new PropertyOverride( + description: 'NekoHTML is a simple HTML scanner and tag balancer.'), + nekohtml_xercesMinimal: new PropertyOverride( + description: 'Only contains necessary framework & Xerces2 classes', url: 'http://nekohtml.sourceforge.net/index.html', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_ant_ant': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_ant_ant: new PropertyOverride( url: 'https://ant.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_ant_ant_launcher': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_ant_ant_launcher: new PropertyOverride( url: 'https://ant.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_ant_tasks': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_ant_tasks: new PropertyOverride( url: 'https://ant.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_artifact': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_artifact: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_artifact_manager': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_artifact_manager: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_error_diagnostics': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_error_diagnostics: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_model': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_model: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_plugin_registry': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_plugin_registry: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_profile': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_profile: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_project': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_project: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_repository_metadata': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_repository_metadata: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_settings': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_settings: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_wagon_wagon_file': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_wagon_wagon_file: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_wagon_wagon_http_lightweight': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_wagon_wagon_http_lightweight: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_wagon_wagon_http_shared': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_wagon_wagon_http_shared: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_wagon_wagon_provider_api': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_wagon_wagon_provider_api: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_codehaus_mojo_animal_sniffer_annotations': new PropertyOverride( - url: "http://www.mojohaus.org/animal-sniffer/animal-sniffer-annotations/", - licenseUrl: "https://raw.githubusercontent.com/mojohaus/animal-sniffer/master/animal-sniffer-annotations/pom.xml", - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_codehaus_plexus_plexus_container_default': new PropertyOverride( - url: "https://codehaus-plexus.github.io/", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_codehaus_plexus_plexus_interpolation': new PropertyOverride( - url: "https://codehaus-plexus.github.io/", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_codehaus_plexus_plexus_utils': new PropertyOverride( - url: "https://codehaus-plexus.github.io/", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_protobuf_protobuf_java': new PropertyOverride( - url: "https://github.com/protocolbuffers/protobuf/blob/master/java/README.md", - licenseUrl: "https://raw.githubusercontent.com/protocolbuffers/protobuf/master/LICENSE", - licenseName: "BSD"), - 'com_google_protobuf_protobuf_javalite': new PropertyOverride( - url: "https://github.com/protocolbuffers/protobuf/blob/master/java/lite.md", - licenseUrl: "https://raw.githubusercontent.com/protocolbuffers/protobuf/master/LICENSE", - licenseName: "BSD"), - 'com_google_protobuf_protobuf_lite': new PropertyOverride( - url: "https://github.com/protocolbuffers/protobuf/blob/master/java/lite.md", - licenseUrl: "https://raw.githubusercontent.com/protocolbuffers/protobuf/master/LICENSE", - licenseName: "BSD"), - 'com_google_ar_core': new PropertyOverride( - url: "https://github.com/google-ar/arcore-android-sdk", - licenseUrl: "https://raw.githubusercontent.com/google-ar/arcore-android-sdk/master/LICENSE", - licenseName: "Apache 2.0"), - 'commons_cli_commons_cli': new PropertyOverride( - licenseName: "Apache 2.0", - licenseUrl: "https://raw.githubusercontent.com/apache/commons-cli/master/LICENSE.txt"), - 'javax_annotation_javax_annotation_api': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_codehaus_mojo_animal_sniffer_annotations: new PropertyOverride( + url: 'http://www.mojohaus.org/animal-sniffer/animal-sniffer-annotations/', + /* groovylint-disable-next-line LineLength */ + licenseUrl: 'https://raw.githubusercontent.com/mojohaus/animal-sniffer/master/animal-sniffer-annotations/pom.xml', + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_codehaus_plexus_plexus_container_default: new PropertyOverride( + url: 'https://codehaus-plexus.github.io/', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_codehaus_plexus_plexus_interpolation: new PropertyOverride( + url: 'https://codehaus-plexus.github.io/', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_codehaus_plexus_plexus_utils: new PropertyOverride( + url: 'https://codehaus-plexus.github.io/', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_protobuf_protobuf_java: new PropertyOverride( + url: 'https://github.com/protocolbuffers/protobuf/blob/master/java/README.md', + licenseUrl: 'https://raw.githubusercontent.com/protocolbuffers/protobuf/master/LICENSE', + licenseName: 'BSD'), + com_google_protobuf_protobuf_javalite: new PropertyOverride( + url: 'https://github.com/protocolbuffers/protobuf/blob/master/java/lite.md', + licenseUrl: 'https://raw.githubusercontent.com/protocolbuffers/protobuf/master/LICENSE', + licenseName: 'BSD'), + com_google_protobuf_protobuf_lite: new PropertyOverride( + url: 'https://github.com/protocolbuffers/protobuf/blob/master/java/lite.md', + licenseUrl: 'https://raw.githubusercontent.com/protocolbuffers/protobuf/master/LICENSE', + licenseName: 'BSD'), + com_google_ar_core: new PropertyOverride( + url: 'https://github.com/google-ar/arcore-android-sdk', + licenseUrl: 'https://raw.githubusercontent.com/google-ar/arcore-android-sdk/master/LICENSE', + licenseName: 'Apache 2.0'), + commons_cli_commons_cli: new PropertyOverride( + licenseName: 'Apache 2.0', + licenseUrl: 'https://raw.githubusercontent.com/apache/commons-cli/master/LICENSE.txt'), + javax_annotation_javax_annotation_api: new PropertyOverride( isShipped: false, // Annotations are stripped by R8. - licenseName: "CDDLv1.1", - licensePath: "licenses/CDDLv1.1.txt"), - 'javax_annotation_jsr250_api': new PropertyOverride( + licenseName: 'CDDLv1.1', + licensePath: 'licenses/CDDLv1.1.txt'), + javax_annotation_jsr250_api: new PropertyOverride( isShipped: false, // Annotations are stripped by R8. - licenseName: "CDDLv1.0", - licensePath: "licenses/CDDLv1.0.txt"), - 'net_sf_kxml_kxml2': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/stefanhaustein/kxml2/master/license.txt", - licenseName: "MIT"), - 'org_checkerframework_checker_compat_qual': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt", - licenseName: "GPL v2 with the classpath exception"), - 'org_checkerframework_checker_qual': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt", - licenseName: "GPL v2 with the classpath exception"), - 'org_checkerframework_dataflow': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt", - licenseName: "GPL v2 with the classpath exception"), - 'org_checkerframework_dataflow_shaded': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt", - licenseName: "GPL v2 with the classpath exception"), - 'org_checkerframework_javacutil': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt", - licenseName: "GPL v2 with the classpath exception"), - 'org_ow2_asm_asm': new PropertyOverride( - licenseUrl: "https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt", - licenseName: "BSD"), - 'org_ow2_asm_asm_analysis': new PropertyOverride( - licenseUrl: "https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt", - licenseName: "BSD"), - 'org_ow2_asm_asm_commons': new PropertyOverride( - licenseUrl: "https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt", - licenseName: "BSD"), - 'org_ow2_asm_asm_tree': new PropertyOverride( - licenseUrl: "https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt", - licenseName: "BSD"), - 'org_ow2_asm_asm_util': new PropertyOverride( - licenseUrl: "https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt", - licenseName: "BSD"), - 'org_pcollections_pcollections': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/hrldcpr/pcollections/master/LICENSE", - licenseName: "The MIT License"), - 'org_plumelib_plume_util': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/plume-lib/plume-util/master/LICENSE", - licenseName: "MIT"), - 'org_plumelib_require_javadoc': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/plume-lib/require-javadoc/master/LICENSE", - licenseName: "MIT"), - 'org_plumelib_reflection_util': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/plume-lib/reflection-util/master/LICENSE", - licenseName: "MIT"), - 'org_robolectric_annotations': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_junit': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_pluginapi': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_plugins_maven_dependency_resolver': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_resources': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_robolectric': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_sandbox': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_shadowapi': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_shadows_framework': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_shadows_multidex': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT", - cipdSuffix: "cr1"), - 'org_robolectric_shadows_playservices': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_utils': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_utils_reflector': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), + licenseName: 'CDDLv1.0', + licensePath: 'licenses/CDDLv1.0.txt'), + net_sf_kxml_kxml2: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/stefanhaustein/kxml2/master/license.txt', + licenseName: 'MIT'), + org_checkerframework_checker_compat_qual: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt', + licenseName: 'GPL v2 with the classpath exception'), + org_checkerframework_checker_qual: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt', + licenseName: 'GPL v2 with the classpath exception'), + org_checkerframework_dataflow: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt', + licenseName: 'GPL v2 with the classpath exception'), + org_checkerframework_dataflow_shaded: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt', + licenseName: 'GPL v2 with the classpath exception'), + org_checkerframework_javacutil: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt', + licenseName: 'GPL v2 with the classpath exception'), + org_ow2_asm_asm: new PropertyOverride( + licenseUrl: 'https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt', + licenseName: 'BSD'), + org_ow2_asm_asm_analysis: new PropertyOverride( + licenseUrl: 'https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt', + licenseName: 'BSD'), + org_ow2_asm_asm_commons: new PropertyOverride( + licenseUrl: 'https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt', + licenseName: 'BSD'), + org_ow2_asm_asm_tree: new PropertyOverride( + licenseUrl: 'https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt', + licenseName: 'BSD'), + org_ow2_asm_asm_util: new PropertyOverride( + licenseUrl: 'https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt', + licenseName: 'BSD'), + org_pcollections_pcollections: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/hrldcpr/pcollections/master/LICENSE', + licenseName: 'The MIT License'), + org_plumelib_plume_util: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/plume-lib/plume-util/master/LICENSE', + licenseName: 'MIT'), + org_plumelib_require_javadoc: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/plume-lib/require-javadoc/master/LICENSE', + licenseName: 'MIT'), + org_plumelib_reflection_util: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/plume-lib/reflection-util/master/LICENSE', + licenseName: 'MIT'), + org_robolectric_annotations: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_junit: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_pluginapi: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_plugins_maven_dependency_resolver: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_resources: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_robolectric: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_sandbox: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_shadowapi: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_shadows_framework: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_shadows_multidex: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT', + cipdSuffix: 'cr1'), + org_robolectric_shadows_playservices: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_utils: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_utils_reflector: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), ] - // Local text versions of HTML licenses. This cannot replace PROPERTY_OVERRIDES because some - // libraries refer to license templates such as https://opensource.org/licenses/MIT - // - // Keys should be 'https'. customizeLicenses() will normalize URLs to https. - final def LICENSE_OVERRIDES = [ + // Local text versions of HTML licenses. This cannot replace PROPERTY_OVERRIDES because some libraries refer to + // license templates such as https://opensource.org/licenses/MIT. + // Keys should be 'https', since customizeLicenses() will normalize URLs to https. + static final Map<String, String> LICENSE_OVERRIDES = [ 'https://developer.android.com/studio/terms.html': 'licenses/Android_SDK_License-December_9_2016.txt', 'https://openjdk.java.net/legal/gplv2+ce.html': 'licenses/GNU_v2_with_Classpath_Exception_1991.txt', 'https://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web': 'licenses/SIL_Open_Font.txt', @@ -340,15 +335,42 @@ 'https://www.unicode.org/license.html': 'licenses/Unicode.txt', ] + final Map<String, DependencyDescription> dependencies = [:] + final Set<String> lowerVersionOverride = [] as Set + Project[] projects Logger logger boolean skipLicenses + static String makeModuleId(ResolvedModuleVersion module) { + // Does not include version because by default the resolution strategy for gradle is to use the newest version + // among the required ones. We want to be able to match it in the BUILD.gn file. + String moduleId = sanitize("${module.id.group}_${module.id.name}") + + // Add 'android' suffix for guava-android so that its module name is distinct from the module for guava. + if (module.id.name == 'guava' && 'android' in module.id.version) { + moduleId += '_android' + } + return moduleId + } + + static String makeModuleId(ResolvedArtifact artifact) { + // Does not include version because by default the resolution strategy for gradle is to use the newest version + // among the required ones. We want to be able to match it in the BUILD.gn file. + ComponentIdentifier componentId = artifact.id.componentIdentifier + String moduleId = sanitize("${componentId.group}_${componentId.module}") + + // Add 'android' suffix for guava-android so that its module name is distinct from the module for guava. + if (componentId.module == 'guava' && 'android' in componentId.version) { + moduleId += '_android' + } + return moduleId + } + void collectDependencies() { - Set<ResolvedConfiguration> deps = [] - Set<ResolvedConfiguration> buildDepsNoRecurse = [] - Set<ResolvedDependency> firstLevelModuleDependencies = [] - Map<String, List<ResolvedArtifact>> resolvedArtifacts = new HashMap<>() + Set<ResolvedConfiguration> deps = [] as Set + Set<ResolvedConfiguration> buildDepsNoRecurse = [] as Set + Map<String, List<ResolvedArtifact>> resolvedArtifacts = [:] String[] configNames = [ 'compile', 'compileListenableFuture', @@ -359,16 +381,16 @@ ] for (Project project : projects) { for (String configName : configNames) { - def config = project.configurations.getByName(configName).resolvedConfiguration + ResolvedConfiguration config = project.configurations.getByName(configName).resolvedConfiguration deps += config.firstLevelModuleDependencies if (configName == 'buildCompileNoDeps') { - buildDepsNoRecurse += config.firstLevelModuleDependencies + buildDepsNoRecurse += config.firstLevelModuleDependencies } if (!resolvedArtifacts.containsKey(configName)) { - resolvedArtifacts[configName] = [] + resolvedArtifacts[configName] = [] } resolvedArtifacts[configName].addAll(config.resolvedArtifacts) - } + } } resolvedArtifacts['compileListenableFuture'].each { artifact -> @@ -384,169 +406,136 @@ topLevelIds.each { id -> dependencies.get(id).visible = true } resolvedArtifacts['testCompile'].each { artifact -> - def id = makeModuleId(artifact) - def dep = dependencies.get(id) - assert dep != null : "No dependency collected for artifact ${artifact.name}" + String id = makeModuleId(artifact) + DependencyDescription dep = dependencies.get(id) + assert dep : "No dependency collected for artifact ${artifact.name}" dep.testOnly = true } resolvedArtifacts['androidTestCompile'].each { artifact -> - def dep = dependencies.get(makeModuleId(artifact)) - assert dep != null : "No dependency collected for artifact ${artifact.name}" + DependencyDescription dep = dependencies.get(makeModuleId(artifact)) + assert dep : "No dependency collected for artifact ${artifact.name}" dep.supportsAndroid = true dep.testOnly = true } resolvedArtifacts['buildCompile'].each { artifact -> - def id = makeModuleId(artifact) - def dep = dependencies.get(id) - assert dep != null : "No dependency collected for artifact ${artifact.name}" + String id = makeModuleId(artifact) + DependencyDescription dep = dependencies.get(id) + assert dep : "No dependency collected for artifact ${artifact.name}" dep.usedInBuild = true dep.testOnly = false } - def compileResolvedArtifacts = resolvedArtifacts['compile'] + List<ResolvedArtifact> compileResolvedArtifacts = resolvedArtifacts['compile'] compileResolvedArtifacts += resolvedArtifacts['compileListenableFuture'] compileResolvedArtifacts.each { artifact -> - def id = makeModuleId(artifact) - def dep = dependencies.get(id) - assert dep != null : "No dependency collected for artifact ${artifact.name}" + String id = makeModuleId(artifact) + DependencyDescription dep = dependencies.get(id) + assert dep : "No dependency collected for artifact ${artifact.name}" dep.supportsAndroid = true dep.testOnly = false dep.isShipped = true } buildDepsNoRecurse.each { resolvedDep -> - def id = makeModuleId(resolvedDep.module) - def dep = dependencies.get(id) - assert dep != null : "No dependency collected for artifact ${artifact.name}" + String id = makeModuleId(resolvedDep.module) + DependencyDescription dep = dependencies.get(id) + assert dep : "No dependency collected for artifact ${artifact.name}" dep.usedInBuild = true dep.testOnly = false } - PROPERTY_OVERRIDES.each { id, fallbackProperties -> - if (fallbackProperties?.isShipped != null) { - def dep = dependencies.get(id) - if (dep != null) { + if (fallbackProperties?.isShipped) { + DependencyDescription dep = dependencies.get(id) + if (dep) { dep.isShipped = fallbackProperties.isShipped } else { - logger.warn("PROPERTY_OVERRIDES has stale dep: " + id) + logger.warn('PROPERTY_OVERRIDES has stale dep: ' + id) } } } } - private void collectDependenciesInternal(ResolvedDependency dependency, boolean recurse = true) { - def id = makeModuleId(dependency.module) - if (dependencies.containsKey(id)) { - if (dependencies.get(id).version == dependency.module.id.version) return + private static String sanitize(String input) { + return input.replaceAll('[:.-]', '_') + } - // Default to using largest version for version conflict resolution. See - // crbug.com/1040958 + private void collectDependenciesInternal(ResolvedDependency dependency, boolean recurse = true) { + String id = makeModuleId(dependency.module) + if (dependencies.containsKey(id)) { + if (dependencies.get(id).version == dependency.module.id.version) { + return + } + + // Default to using largest version for version conflict resolution. See http://crbug.com/1040958. // https://docs.gradle.org/current/userguide/dependency_resolution.html#sec:version-conflict - def useLowerVersion = (id in lowerVersionOverride) - def versionIsLower = dependency.module.id.version < dependencies.get(id).version + boolean useLowerVersion = (id in lowerVersionOverride) + boolean versionIsLower = dependency.module.id.version < dependencies.get(id).version if (useLowerVersion != versionIsLower) { return } } - if (dependency.getModuleArtifacts().size() != 1) { + if (dependency.moduleArtifacts.size() != 1) { throw new IllegalStateException("The dependency ${id} does not have exactly one " + - "artifact: ${dependency.getModuleArtifacts()}") + "artifact: ${dependency.moduleArtifacts}") } - def artifact = dependency.getModuleArtifacts()[0] + ResolvedArtifact artifact = dependency.moduleArtifacts[0] if (artifact.extension != 'jar' && artifact.extension != 'aar') { throw new IllegalStateException("Type ${artifact.extension} of ${id} not supported.") } if (recurse) { - def childDependenciesWithArtifacts = [] + List<ResolvedDependency> childDependenciesWithArtifacts = [] - dependency.children.each { - childDependency-> - // Replace dependency which acts as a redirect - // (ex: org.jetbrains.kotlinx:kotlinx-coroutines-core) with + dependency.children.each { childDependency -> + // Replace dependency which acts as a redirect (ex: org.jetbrains.kotlinx:kotlinx-coroutines-core) with // dependencies it redirects to. - if (childDependency.getModuleArtifacts().isEmpty()) { - if (!childDependency.children.isEmpty()) { - childDependenciesWithArtifacts += childDependency.children - } else { - throw new IllegalStateException( - "The dependency ${id} has no children and no artifacts.") - } + if (childDependency.moduleArtifacts) { + childDependenciesWithArtifacts += childDependency } else { - childDependenciesWithArtifacts += childDependency + if (childDependency.children) { + childDependenciesWithArtifacts += childDependency.children + } else { + throw new IllegalStateException("The dependency ${id} has no children and no artifacts.") + } } - } + } - def childModules = [] - childDependenciesWithArtifacts.each { childDependency -> - childModules += makeModuleId(childDependency.module) - } - dependencies.put(id, buildDepDescription(id, dependency, artifact, childModules)) - childDependenciesWithArtifacts.each { - childDependency -> collectDependenciesInternal(childDependency) - } + List<String> childModules = [] + childDependenciesWithArtifacts.each { childDependency -> + childModules += makeModuleId(childDependency.module) + } + dependencies.put(id, buildDepDescription(id, dependency, artifact, childModules)) + childDependenciesWithArtifacts.each { + childDependency -> collectDependenciesInternal(childDependency) + } } else { - dependencies.put(id, buildDepDescription(id, dependency, artifact, [])) + dependencies.put(id, buildDepDescription(id, dependency, artifact, [])) } } - static String makeModuleId(ResolvedModuleVersion module) { - // Does not include version because by default the resolution strategy for gradle is to use - // the newest version among the required ones. We want to be able to match it in the - // BUILD.gn file. - def moduleId = sanitize("${module.id.group}_${module.id.name}") - - // Add 'android' suffix for guava-android so that its module name is distinct from the - // module for guava. - if (module.id.name == "guava" && module.id.version.contains("android")) { - moduleId += "_android" - } - return moduleId - } - - static String makeModuleId(ResolvedArtifact artifact) { - // Does not include version because by default the resolution strategy for gradle is to use - // the newest version among the required ones. We want to be able to match it in the - // BUILD.gn file. - def componentId = artifact.id.componentIdentifier - def moduleId = sanitize("${componentId.group}_${componentId.module}") - - // Add 'android' suffix for guava-android so that its module name is distinct from the - // module for guava. - if (componentId.module == "guava" && componentId.version.contains("android")) { - moduleId += "_android" - } - return moduleId - } - - private static String sanitize(String input) { - return input.replaceAll("[:.-]", "_") - } - - private buildDepDescription(String id, ResolvedDependency dependency, ResolvedArtifact artifact, - List<String> childModules) { - def pomUrl, pomContent + private DependencyDescription buildDepDescription( + String id, ResolvedDependency dependency, ResolvedArtifact artifact, List<String> childModules) { + String pomUrl + GPathResult pomContent (pomUrl, pomContent) = computePomFromArtifact(artifact) - def licenses = [] + List<LicenseSpec> licenses = [] if (!skipLicenses) { - licenses = resolveLicenseInformation(id, pomContent) + licenses = resolveLicenseInformation(pomContent) } // Build |fileUrl| by swapping '.pom' file extension with artifact file extension. - def fileUrl = pomUrl.substring(0, pomUrl.length() - 3) + artifact.extension + String fileUrl = pomUrl[0..-4] + artifact.extension checkDownloadable(fileUrl) // Get rid of irrelevant indent that might be present in the XML file. - def description = pomContent.description?.text()?.trim()?.replaceAll(/\s+/, " ") - def displayName = pomContent.name?.text() - if (!displayName) { - displayName = dependency.module.id.name - } + String description = pomContent.description?.text()?.trim()?.replaceAll(/\s+/, ' ') + String displayName = pomContent.name?.text() + displayName = displayName ?: dependency.module.id.name return customizeDep(new DependencyDescription( id: id, @@ -565,82 +554,67 @@ url: pomContent.url?.text(), displayName: displayName, exclude: false, - cipdSuffix: "cr0", + cipdSuffix: 'cr0', )) - } + } - private void customizeLicenses(DependencyDescription dep, - PropertyOverride fallbackProperties) { + private void customizeLicenses(DependencyDescription dep, PropertyOverride fallbackProperties) { for (LicenseSpec license : dep.licenses) { if (!license.url) { continue } - def normalizedLicenseUrl = license.url.replace('http://', 'https://') - def licenseOverridePath = LICENSE_OVERRIDES[normalizedLicenseUrl] + String normalizedLicenseUrl = license.url.replace('http://', 'https://') + String licenseOverridePath = LICENSE_OVERRIDES[normalizedLicenseUrl] if (licenseOverridePath) { license.url = '' license.path = licenseOverridePath } } - if (dep.id?.startsWith("com_google_android_")) { - logger.debug("Using Android license for ${dep.id}") + if (dep.id?.startsWith('com_google_android_')) { + logger.debug("Using Android license for $dep.id") dep.licenses.clear() dep.licenses.add(new LicenseSpec( - name: "Android Software Development Kit License", - path: "licenses/Android_SDK_License-December_9_2016.txt")) + name: 'Android Software Development Kit License', + path: 'licenses/Android_SDK_License-December_9_2016.txt')) } - if (fallbackProperties != null) { - if (fallbackProperties.licenseName == null) { - if (fallbackProperties.licensePath != null - || fallbackProperties.licenseUrl != null) { - def errorMsg = "PropertyOverride must specify 'licenseName' if either " - + "'licensePath' or licenseUrl' is specified." - throw new IllegalStateException(errorMsg) + if (fallbackProperties) { + if (fallbackProperties.licenseName) { + dep.licenses.clear() + LicenseSpec license = new LicenseSpec( + name : fallbackProperties.licenseName, + path: fallbackProperties.licensePath, + url: fallbackProperties.licenseUrl, + ) + dep.licenses.add(license) + } else { + if (fallbackProperties.licensePath || fallbackProperties.licenseUrl) { + throw new IllegalStateException('PropertyOverride must specify "licenseName" if either ' + + '"licensePath" or "licenseUrl" is specified.') } - return } - - dep.licenses.clear() - def license = new LicenseSpec( - name : fallbackProperties.licenseName, - path: fallbackProperties.licensePath, - url: fallbackProperties.licenseUrl, - ) - dep.licenses.add(license) } } - private customizeDep(DependencyDescription dep) { - if (dep.id?.startsWith("com_google_android_")) { - if (!dep.url) { - dep.url = "https://developers.google.com/android/guides/setup" - } - } else if (dep.id?.startsWith("com_google_firebase_")) { - // Some firebase dependencies don't set their URL. - if (!dep.url) { - dep.url = "https://firebase.google.com" - } + private DependencyDescription customizeDep(DependencyDescription dep) { + if (dep.id?.startsWith('com_google_android_')) { + // Many google dependencies don't set their URL, here is a good default. + dep.url = dep.url ?: 'https://developers.google.com/android/guides/setup' + } else if (dep.id?.startsWith('com_google_firebase_')) { + // Same as above for some firebase dependencies. + dep.url = dep.url ?: 'https://firebase.google.com' } - def fallbackProperties = PROPERTY_OVERRIDES.get(dep.id) - if (fallbackProperties != null) { - logger.debug("Using fallback properties for ${dep.id}") - if (fallbackProperties.description != null) { - dep.description = fallbackProperties.description - } - if (fallbackProperties.url != null) { - dep.url = fallbackProperties.url - } - if (fallbackProperties.cipdSuffix != null) { - dep.cipdSuffix = fallbackProperties.cipdSuffix - } - if (fallbackProperties.generateTarget != null) { - dep.generateTarget = fallbackProperties.generateTarget - } - if (fallbackProperties.exclude != null) { - dep.exclude = fallbackProperties.exclude + PropertyOverride fallbackProperties = PROPERTY_OVERRIDES.get(dep.id) + if (fallbackProperties) { + logger.debug("Using fallback properties for $dep.id") + dep.with { + description = fallbackProperties.description ?: description + url = fallbackProperties.url ?: url + cipdSuffix = fallbackProperties.cipdSuffix ?: cipdSuffix + generateTarget = fallbackProperties.generateTarget ?: generateTarget + exclude = fallbackProperties.exclude ?: exclude } } @@ -656,56 +630,55 @@ return dep } - private resolveLicenseInformation(String id, GPathResult pomContent) { - GPathResult licenses = pomContent?.licenses?.license - if (!licenses) { - return [] - } + private List<LicenseSpec> resolveLicenseInformation(GPathResult pomContent) { + GPathResult licenses = pomContent?.licenses?.license + if (!licenses) { + return [] + } - def out = [] - for (GPathResult license : licenses) { - out.add(new LicenseSpec( + List<LicenseSpec> out = [] + for (GPathResult license : licenses) { + out.add(new LicenseSpec( name: license.name.text(), url: license.url.text() )) - } - return out + } + return out } - private computePomFromArtifact(ResolvedArtifact artifact) { + private List computePomFromArtifact(ResolvedArtifact artifact) { for (Project project : projects) { for (ArtifactRepository repository : project.repositories.asList()) { - def repoUrl = repository.properties.get('url').toString() - // Some repo url may have trailing '/' and this breaks the file - // url generation below. So remove it if present. + String repoUrl = repository.properties.get('url') + // Some repo url may have trailing '/' and this breaks the file url generation below. So remove it if + // present. if (repoUrl.endsWith('/')) { - repoUrl = repoUrl.substring(0, repoUrl.length() - 1) + repoUrl = repoUrl[0..-2] } - def component = artifact.id.componentIdentifier + ComponentIdentifier component = artifact.id.componentIdentifier // Constructs the file url for pom. For example, with // * repoUrl as "https://maven.google.com" // * component.group as "android.arch.core" // * component.module as "common" // * component.version as "1.1.1" // - // The file url will be: - // https://maven.google.com/android/arch/core/common/1.1.1/common-1.1.1.pom - def fileUrl = String.format("%s/%s/%s/%s/%s-%s.pom", + // The file url will be: https://maven.google.com/android/arch/core/common/1.1.1/common-1.1.1.pom + String fileUrl = String.format('%s/%s/%s/%s/%s-%s.pom', repoUrl, component.group.replace('.', '/'), component.module, component.version, component.module, - // While maven central and maven.google.com use "version", - // https://androidx.dev uses "timestampedVersion" as part - // of the file url - component.hasProperty("timestampedVersion") ? component.timestampedVersion : component.version) + // While maven central and maven.google.com use "version", https://androidx.dev uses + // "timestampedVersion" as part of the file url + component.hasProperty('timestampedVersion') ? component.timestampedVersion : component.version) try { - def content = new XmlSlurper(false /* validating */, false /* namespaceAware */).parse(fileUrl) - logger.debug("Succeeded in resolving url ${fileUrl}") + GPathResult content = new XmlSlurper( + false /* validating */, false /* namespaceAware */).parse(fileUrl) + logger.debug("Succeeded in resolving url $fileUrl") return [fileUrl, content] - } catch (Exception ignored) { - logger.debug("Failed in resolving url ${fileUrl}") + } catch (any) { + logger.debug("Failed in resolving url $fileUrl") } } } @@ -714,27 +687,28 @@ private void checkDownloadable(String url) { try { - def inStream = new URL(url).openStream() - if (inStream != null) { - inStream.close(); - logger.debug("Succeeded in resolving url ${url}") + InputStream inStream = new URL(url).openStream() + if (inStream) { + inStream.close() + logger.debug("Succeeded in resolving url $url") return } - } catch (Exception ignored) {} - throw new RuntimeException("Resolved POM but could not resolve ${url}") + } catch (any) { + throw new RuntimeException("Resolved POM but could not resolve $url") + } } @AutoClone static class DependencyDescription { + String id ResolvedArtifact artifact String group, name, version, extension, displayName, description, url List<LicenseSpec> licenses String fileName, fileUrl - // The local directory name to store the files like artifact, license - // file, 3pp subdirectory, and etc. Must be lowercase since 3pp uses - // the directory name as part of the CIPD names. However CIPD does not - // allow uppercase in names. + // The local directory name to store the files like artifact, license file, 3pp subdirectory, and etc. Must be + // lowercase since 3pp uses the directory name as part of the CIPD names. However CIPD does not allow uppercase + // in names. String directoryName boolean supportsAndroid, visible, exclude, testOnly, isShipped, usedInBuild boolean generateTarget = true @@ -742,21 +716,27 @@ ComponentIdentifier componentId List<String> children String cipdSuffix + } static class LicenseSpec { - String name, url, path + + String name, url, path + } static class PropertyOverride { - String description - String url - String licenseName, licenseUrl, licensePath - String cipdSuffix - Boolean isShipped - // Set to true if this dependency is not needed. - Boolean exclude - // Set to false to skip creation of BUILD.gn target. - Boolean generateTarget + + String description + String url + String licenseName, licenseUrl, licensePath + String cipdSuffix + Boolean isShipped + // Set to true if this dependency is not needed. + Boolean exclude + // Set to false to skip creation of BUILD.gn target. + Boolean generateTarget + } + }
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy index 1204e7a4..7ff1e7e 100644 --- a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy +++ b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy
@@ -7,20 +7,21 @@ import org.gradle.api.artifacts.DependencyResolveDetails /** - * Plugin designed to define the configuration names to be used in the Gradle files to describe - * the dependencies that {@link ChromiumDepGraph} with pick up. + * Plugin designed to define the configuration names to be used in the Gradle files to describe the dependencies that + * {@link ChromiumDepGraph} with pick up. */ class ChromiumPlugin implements Plugin<Project> { + void apply(Project project) { - // The configurations here are going to be used in ChromiumDepGraph. Keep it up to date - // with the declarations below. + // The configurations here are going to be used in ChromiumDepGraph. Keep it up to date with the declarations + // below. project.configurations { /** Main type of configuration, use it for libraries that the APK depends on. */ compile /** - * Dedicated com_google_guava_listenablefuture configuration so that other libraries do - * not affect the resolved listenablefuture version. + * Dedicated com_google_guava_listenablefuture configuration so that other libraries do not affect the + * resolved listenablefuture version. */ compileListenableFuture @@ -30,27 +31,24 @@ /** Libraries that are only used during build. These support android. */ buildCompile - /** - * Libraries that are only used during build but should not - * automatically retrieve their dependencies. - */ + /** Libraries that are only used during build but should not automatically retrieve their dependencies. */ buildCompileNoDeps /** Libraries that are used for testing only and support android. */ androidTestCompile } - project.configurations.all { - resolutionStrategy.eachDependency { DependencyResolveDetails details -> - if (project.ext.hasProperty("versionOverrideMap") && - project.ext.versionOverrideMap != null) { - def module = details.requested.group + ":" + details.requested.name - def version = project.ext.versionOverrideMap[module] - if (version != null) { - details.useVersion version - } - } + project.configurations.all { + resolutionStrategy.eachDependency { DependencyResolveDetails details -> + if (project.ext.hasProperty('versionOverrideMap') && project.ext.versionOverrideMap) { + module = "${details.requested.group}:${details.requested.name}" + version = project.ext.versionOverrideMap[module] + if (version != null) { + details.useVersion version + } + } + } } - } - } + } + }
diff --git a/third_party/blink/common/OWNERS b/third_party/blink/common/OWNERS index 20eecf7..c39020a 100644 --- a/third_party/blink/common/OWNERS +++ b/third_party/blink/common/OWNERS
@@ -7,7 +7,6 @@ jbroman@chromium.org kinuko@chromium.org pfeldman@chromium.org -tkent@chromium.org # Any core owner can approve blink finch flags. per-file features.cc=file://third_party/blink/renderer/core/OWNERS
diff --git a/third_party/blink/common/mediastream/OWNERS b/third_party/blink/common/mediastream/OWNERS index 7653ee49..b3bb856f 100644 --- a/third_party/blink/common/mediastream/OWNERS +++ b/third_party/blink/common/mediastream/OWNERS
@@ -1,5 +1,4 @@ guidou@chromium.org -hbos@chromium.org per-file *_mojom_traits*.*=set noparent per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/OWNERS b/third_party/blink/public/OWNERS index 3dd00d1..f5a02b6 100644 --- a/third_party/blink/public/OWNERS +++ b/third_party/blink/public/OWNERS
@@ -14,7 +14,6 @@ mkwst@chromium.org pdr@chromium.org pfeldman@chromium.org -rbyers@chromium.org tkent@chromium.org # NOTE: keep this in sync with lsc-owners-override@chromium.org owners
diff --git a/third_party/blink/public/common/mediastream/OWNERS b/third_party/blink/public/common/mediastream/OWNERS index 7653ee49..b3bb856f 100644 --- a/third_party/blink/public/common/mediastream/OWNERS +++ b/third_party/blink/public/common/mediastream/OWNERS
@@ -1,5 +1,4 @@ guidou@chromium.org -hbos@chromium.org per-file *_mojom_traits*.*=set noparent per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/common/sampling_heap_profiler/OWNERS b/third_party/blink/public/common/sampling_heap_profiler/OWNERS deleted file mode 100644 index 87c96616..0000000 --- a/third_party/blink/public/common/sampling_heap_profiler/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -alph@chromium.org
diff --git a/third_party/blink/public/devtools_protocol/OWNERS b/third_party/blink/public/devtools_protocol/OWNERS index 5925879..30b2e7e7 100644 --- a/third_party/blink/public/devtools_protocol/OWNERS +++ b/third_party/blink/public/devtools_protocol/OWNERS
@@ -1,4 +1,3 @@ -alph@chromium.org caseq@chromium.org dgozman@chromium.org pfeldman@chromium.org
diff --git a/third_party/blink/public/mojom/background_sync/OWNERS b/third_party/blink/public/mojom/background_sync/OWNERS index 212d60b..1e3120b 100644 --- a/third_party/blink/public/mojom/background_sync/OWNERS +++ b/third_party/blink/public/mojom/background_sync/OWNERS
@@ -1,5 +1,3 @@ -iclelland@chromium.org -jkarlin@chromium.org peter@chromium.org rayankans@chromium.org
diff --git a/third_party/blink/public/mojom/crash/OWNERS b/third_party/blink/public/mojom/crash/OWNERS index faea832..08850f4 100644 --- a/third_party/blink/public/mojom/crash/OWNERS +++ b/third_party/blink/public/mojom/crash/OWNERS
@@ -1,5 +1,2 @@ per-file *.mojom=set noparent per-file *.mojom=file://ipc/SECURITY_OWNERS - -yuzus@chromium.org -
diff --git a/third_party/blink/public/mojom/fetch/fetch_api_response.mojom b/third_party/blink/public/mojom/fetch/fetch_api_response.mojom index 7d169ec8..a65b71f 100644 --- a/third_party/blink/public/mojom/fetch/fetch_api_response.mojom +++ b/third_party/blink/public/mojom/fetch/fetch_api_response.mojom
@@ -104,4 +104,9 @@ // Information related to an authentication challenge from the response, if // there was one. network.mojom.AuthChallengeInfo? auth_challenge_info; + + // The request's |includeCredentials| value from the "HTTP-network fetch" + // algorithm. + // See: https://fetch.spec.whatwg.org/#concept-http-network-fetch + bool request_include_credentials = true; };
diff --git a/third_party/blink/public/mojom/font_unique_name_lookup/OWNERS b/third_party/blink/public/mojom/font_unique_name_lookup/OWNERS index 78efc30..08850f4 100644 --- a/third_party/blink/public/mojom/font_unique_name_lookup/OWNERS +++ b/third_party/blink/public/mojom/font_unique_name_lookup/OWNERS
@@ -1,4 +1,2 @@ -drott@chromium.org - per-file *.mojom=set noparent per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/mojom/hyphenation/OWNERS b/third_party/blink/public/mojom/hyphenation/OWNERS index 185a39d..b89602e 100644 --- a/third_party/blink/public/mojom/hyphenation/OWNERS +++ b/third_party/blink/public/mojom/hyphenation/OWNERS
@@ -1,7 +1,4 @@ -eae@chromium.org -drott@chromium.org kojii@chromium.org -szager@chromium.org per-file *.mojom=set noparent per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/mojom/mediastream/OWNERS b/third_party/blink/public/mojom/mediastream/OWNERS index 96abc6a..2da2f538 100644 --- a/third_party/blink/public/mojom/mediastream/OWNERS +++ b/third_party/blink/public/mojom/mediastream/OWNERS
@@ -1,5 +1,4 @@ guidou@chromium.org -hbos@chromium.org per-file *.mojom=set noparent per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/mojom/payments/OWNERS b/third_party/blink/public/mojom/payments/OWNERS index 65c11cbe..cb9f41d 100644 --- a/third_party/blink/public/mojom/payments/OWNERS +++ b/third_party/blink/public/mojom/payments/OWNERS
@@ -1,12 +1,8 @@ - file://components/payments/OWNERS jinho.bang@samsung.com -mek@chromium.org rouslan@chromium.org per-file *.mojom=set noparent per-file *.mojom=file://ipc/SECURITY_OWNERS -# Emeritus -mathp@chromium.org
diff --git a/third_party/blink/public/mojom/webshare/OWNERS b/third_party/blink/public/mojom/webshare/OWNERS index ad55720..10c1a6f 100644 --- a/third_party/blink/public/mojom/webshare/OWNERS +++ b/third_party/blink/public/mojom/webshare/OWNERS
@@ -1,6 +1,5 @@ ericwilligers@chromium.org mgiuca@chromium.org -sammc@chromium.org # Changes to Mojo interfaces require a security review to avoid # introducing new sandbox escapes.
diff --git a/third_party/blink/public/platform/OWNERS b/third_party/blink/public/platform/OWNERS index b566b5ee..959f163 100644 --- a/third_party/blink/public/platform/OWNERS +++ b/third_party/blink/public/platform/OWNERS
@@ -12,9 +12,7 @@ # Video SurfaceLayer functionality. per-file web_video_frame*=file://media/OWNERS per-file webaudiosourceprovider_impl.h=file://media/OWNERS -per-file web_video_frame*=lethalantidote@chromium.org per-file web_surface_layer_bridge*=file://media/OWNERS -per-file web_surface_layer_bridge*=lethalantidote@chromium.org # Any core owner can also approve changes to runtime-enabled features. # Please make sure to get a review from someone with expertise on the feature
diff --git a/third_party/blink/public/platform/modules/mediastream/OWNERS b/third_party/blink/public/platform/modules/mediastream/OWNERS index b99ae05..2ad93e6 100644 --- a/third_party/blink/public/platform/modules/mediastream/OWNERS +++ b/third_party/blink/public/platform/modules/mediastream/OWNERS
@@ -1,3 +1 @@ file://third_party/blink/common/mediastream/OWNERS - -per-file media_stream_audio_processor*=aluebs@chromium.org
diff --git a/third_party/blink/public/platform/modules/webrtc/OWNERS b/third_party/blink/public/platform/modules/webrtc/OWNERS index ec89f98..29d9d1c7 100644 --- a/third_party/blink/public/platform/modules/webrtc/OWNERS +++ b/third_party/blink/public/platform/modules/webrtc/OWNERS
@@ -1,10 +1,8 @@ hbos@chromium.org -perkj@chromium.org tommi@chromium.org guidou@chromium.org olka@chromium.org orphis@chromium.org -sergeyu@chromium.org ilnik@chromium.org per-file rtc_video_*=file://media/gpu/OWNERS
diff --git a/third_party/blink/public/platform/web_url_response.h b/third_party/blink/public/platform/web_url_response.h index f650a0b..9a65a93 100644 --- a/third_party/blink/public/platform/web_url_response.h +++ b/third_party/blink/public/platform/web_url_response.h
@@ -351,6 +351,12 @@ BLINK_PLATFORM_EXPORT const absl::optional<net::AuthChallengeInfo>& AuthChallengeInfo() const; + // The request's |includeCredentials| value from the "HTTP-network fetch" + // algorithm. + // See: https://fetch.spec.whatwg.org/#concept-http-network-fetch + BLINK_PLATFORM_EXPORT void SetRequestIncludeCredentials(bool); + BLINK_PLATFORM_EXPORT bool RequestIncludeCredentials() const; + #if INSIDE_BLINK protected: // Permit subclasses to set arbitrary ResourceResponse pointer as
diff --git a/third_party/blink/public/web/OWNERS b/third_party/blink/public/web/OWNERS index 5dff49dd..6a719a5 100644 --- a/third_party/blink/public/web/OWNERS +++ b/third_party/blink/public/web/OWNERS
@@ -1,7 +1,6 @@ # Accessibility per-file web_ax_object.h=file://third_party/blink/renderer/modules/accessibility/OWNERS per-file web_ax_enums.h=file://third_party/blink/renderer/modules/accessibility/OWNERS -per-file web_ax_enums.h=dtseng@chromium.org # Page load metrics per-file web_performance.h=bmcquade@chromium.org
diff --git a/third_party/blink/public/web/modules/autofill/OWNERS b/third_party/blink/public/web/modules/autofill/OWNERS deleted file mode 100644 index d691287..0000000 --- a/third_party/blink/public/web/modules/autofill/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -jochen@chromium.org
diff --git a/third_party/blink/renderer/bindings/OWNERS b/third_party/blink/renderer/bindings/OWNERS index 33e84a44..5e4e3ad 100644 --- a/third_party/blink/renderer/bindings/OWNERS +++ b/third_party/blink/renderer/bindings/OWNERS
@@ -2,13 +2,10 @@ haraken@chromium.org japhet@chromium.org jbroman@chromium.org -jl@opera.com jochen@chromium.org kouhei@chromium.org -marja@chromium.org mkwst@chromium.org mlippautz@chromium.org -pfeldman@chromium.org vivekg@chromium.org yhirano@chromium.org yukishiino@chromium.org
diff --git a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h index 13643d1..b9157dc 100644 --- a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h +++ b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h
@@ -210,36 +210,34 @@ // // |try_block| must be the innermost v8::TryCatch and it's used to internally // capture an exception, which is rethrown in |exception_state|. -template <typename NVTTag, bool is_required, typename T> -bool ConvertDictionaryMember(v8::Isolate* isolate, - v8::Local<v8::Context> current_context, - v8::Local<v8::Object> v8_dictionary, - v8::Local<v8::Name> v8_member_name, - const char* dictionary_name, - const char* member_name, - T& value, - bool& presence, - v8::TryCatch& try_block, - ExceptionState& exception_state) { +template <typename IDLType, bool is_required, typename ValueType> +bool GetDictionaryMemberFromV8Object(v8::Isolate* isolate, + v8::Local<v8::Context> current_context, + v8::Local<v8::Object> v8_dictionary, + v8::Local<v8::Name> v8_member_name, + bool& presence, + ValueType& value, + v8::TryCatch& try_block, + ExceptionState& exception_state) { v8::Local<v8::Value> v8_value; if (!v8_dictionary->Get(current_context, v8_member_name).ToLocal(&v8_value)) { exception_state.RethrowV8Exception(try_block.Exception()); - try_block.Reset(); return false; } if (v8_value->IsUndefined()) { if (is_required) { exception_state.ThrowTypeError(ExceptionMessages::FailedToGet( - member_name, dictionary_name, "Required member is undefined.")); + exception_state.GetInnerMostContext().GetPropertyName(), + exception_state.GetInnerMostContext().GetClassName(), + "Required member is undefined.")); return false; } - presence = false; return true; } - value = NativeValueTraits<NVTTag>::NativeValue(isolate, v8_value, - exception_state); + value = NativeValueTraits<IDLType>::NativeValue(isolate, v8_value, + exception_state); if (exception_state.HadException()) { return false; }
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/blink_v8_bridge.py b/third_party/blink/renderer/bindings/scripts/bind_gen/blink_v8_bridge.py index 5856d41a..d3ad969 100644 --- a/third_party/blink/renderer/bindings/scripts/bind_gen/blink_v8_bridge.py +++ b/third_party/blink/renderer/bindings/scripts/bind_gen/blink_v8_bridge.py
@@ -252,7 +252,10 @@ if real_type.type_definition_object: typename = blink_class_name(real_type.type_definition_object) if real_type.is_enumeration: - return TypeInfo(typename, clear_member_var_fmt="") + return TypeInfo(typename, + ref_fmt="{}", + const_ref_fmt="{}", + clear_member_var_fmt="") return TypeInfo(typename, member_fmt="Member<{}>", ref_fmt="{}*", @@ -593,12 +596,22 @@ initializer_expr = None # VectorOf<T>::size() == 0 by default assignment_value = "{}()".format(type_info.value_t) elif default_value.idl_type.is_object: - dict_name = blink_class_name(idl_type.unwrap().type_definition_object) - value = _format("{}::Create(${isolate})", dict_name) - initializer_expr = value - initializer_deps = ["isolate"] - assignment_value = value - assignment_deps = ["isolate"] + dictionary = idl_type.unwrap().type_definition_object + # Currently "isolate" is the only possible dependency, so whenever + # .initializer_deps exists, it must be ["isolate"]. + if any((make_default_value_expr(member.idl_type, + member.default_value).initializer_deps) + for member in dictionary.members if member.default_value): + value = _format("{}::Create(${isolate})", + blink_class_name(dictionary)) + initializer_expr = value + initializer_deps = ["isolate"] + assignment_value = value + assignment_deps = ["isolate"] + else: + value = _format("{}::Create()", blink_class_name(dictionary)) + initializer_expr = value + assignment_value = value elif default_value.idl_type.is_boolean: value = "true" if default_value.value else "false" initializer_expr = value
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_context.py b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_context.py index ba53407..2e41629 100644 --- a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_context.py +++ b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_context.py
@@ -84,7 +84,14 @@ "named_property_deleter": None, "stringifier": None, - # Contents of union. + # Cache of a tuple of dictionary._DictionaryMember for the own + # members of the dictionary of which the Blink class is being + # generated. The cache is used in dictionary.py to save code + # generation time. + "dictionary_own_members": (), + # Cache of a tuple of union._UnionMember for the flattened member + # types of the union of which the Blink class is being generated. + # The cache is used in union.py to save code generation time. "union_members": (), # The names of the class being generated and its base class.
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_utils.py b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_utils.py index 7f16ff4..e2822ed 100644 --- a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_utils.py +++ b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_utils.py
@@ -89,8 +89,8 @@ elif idl_type.is_promise: header_include_headers.add( "third_party/blink/renderer/bindings/core/v8/script_promise.h") - elif (idl_type.is_sequence or idl_type.is_record - or idl_type.is_frozen_array or idl_type.is_variadic): + elif (idl_type.is_sequence or idl_type.is_frozen_array + or idl_type.is_record or idl_type.is_variadic): header_include_headers.add( "third_party/blink/renderer/platform/heap/heap_allocator.h") elif idl_type.is_string:
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py b/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py index f37ac415..f8143e00 100644 --- a/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py +++ b/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py
@@ -11,7 +11,7 @@ from .blink_v8_bridge import make_default_value_expr from .blink_v8_bridge import make_v8_to_blink_value from .blink_v8_bridge import native_value_tag -from .code_node import Likeliness +from .code_node import EmptyNode from .code_node import ListNode from .code_node import SequenceNode from .code_node import SymbolNode @@ -20,9 +20,9 @@ from .code_node_cxx import CxxClassDefNode from .code_node_cxx import CxxFuncDeclNode from .code_node_cxx import CxxFuncDefNode -from .code_node_cxx import CxxIfElseNode from .code_node_cxx import CxxLikelyIfNode from .code_node_cxx import CxxNamespaceNode +from .code_node_cxx import CxxUnlikelyIfNode from .codegen_accumulator import CodeGenAccumulator from .codegen_context import CodeGenContext from .codegen_expr import expr_from_exposure @@ -41,83 +41,193 @@ from .task_queue import TaskQueue -_DICT_MEMBER_PRESENCE_PREDICATES = { - "ScriptValue": "!{}.IsEmpty()", - "ScriptPromise": "!{}.IsEmpty()", -} +class _DictionaryMember(object): + """ + _DictionaryMember represents the properties that the code generator + directly needs while web_idl.DictionaryMember represents properties of IDL + dictionary member independent from ECMAScript binding. _DictionaryMember + is specific to not only ECMAScript binding but also Blink implementation + of IDL dictionary. + """ + + # Map from Blink member type to presence expression. + _MEMBER_TYPE_TO_PRESENCE_EXPR = { + "ScriptPromise": "!{}.IsEmpty()", + "ScriptValue": "!{}.IsEmpty()", + } + + def __init__(self, dict_member): + assert isinstance(dict_member, web_idl.DictionaryMember) + + self._identifier = dict_member.identifier + self._base_name = ( + dict_member.code_generator_info.property_implemented_as + or dict_member.identifier) + + self._api_has = name_style.api_func("has", self._base_name) + self._api_get = name_style.api_func(self._base_name) + self._api_get_or = name_style.api_func("get", self._base_name, "or") + self._api_set = name_style.api_func("set", self._base_name) + # C++ data member that shows the presence of the dictionary member. + self._presence_var = name_style.member_var("has", self._base_name) + # C++ data member that holds the value of the dictionary member. + self._value_var = name_style.member_var("member", self._base_name) + + # Migration adapters + self._api_has_non_null = name_style.api_func("has", self._base_name, + "non_null") + self._api_get_non_null = name_style.api_func(self._base_name, + "non_null") + + self._idl_type = dict_member.idl_type + self._type_info = blink_type_info(self._idl_type) + self._is_required = dict_member.is_required + if dict_member.default_value: + self._default_expr = make_default_value_expr( + self._idl_type, dict_member.default_value) + else: + self._default_expr = None + + self._exposure = dict_member.exposure + + @property + def identifier(self): + return self._identifier + + @property + def api_has(self): + return self._api_has + + @property + def api_get(self): + return self._api_get + + @property + def api_get_or(self): + return self._api_get_or + + @property + def api_set(self): + return self._api_set + + @property + def api_has_non_null(self): + return self._api_has_non_null + + @property + def api_get_non_null(self): + return self._api_get_non_null + + @property + def presence_var(self): + return self._presence_var + + @property + def value_var(self): + return self._value_var + + @property + def idl_type(self): + return self._idl_type + + @property + def type_info(self): + return self._type_info + + @property + def is_required(self): + return self._is_required + + @property + def presence_expr(self): + if self.is_always_present: + return "true" + expr = self._MEMBER_TYPE_TO_PRESENCE_EXPR.get(self.type_info.member_t) + if expr: + return expr.format(self.value_var) + return self.presence_var + + @property + def does_use_presence_var(self): + return not ( + self.is_always_present + or self.type_info.member_t in self._MEMBER_TYPE_TO_PRESENCE_EXPR) + + @property + def is_always_present(self): + return bool(self.is_required or self._default_expr) + + @property + def initializer_expr(self): + return (self._default_expr and self._default_expr.initializer_expr + or None) + + @property + def initializer_deps(self): + return (self._default_expr and self._default_expr.initializer_deps + or None) + + @property + def initializer_on_constructor(self): + # In order to avoid cyclic header inclusion of IDL dictionaries, put + # the initializer in *.cc if the type is a dictionary. + if (self.initializer_expr and (self.initializer_deps # + or self.idl_type.unwrap().is_dictionary + or self.idl_type.unwrap().is_union)): + return self.initializer_expr + return None + + @property + def initializer_on_member_decl(self): + # In order to avoid cyclic header inclusion of IDL dictionaries, put + # the initializer in *.cc if the type is a dictionary. + if (self.initializer_expr + and not (self.initializer_deps + or self.idl_type.unwrap().is_dictionary + or self.idl_type.unwrap().is_union)): + return self.initializer_expr + idl_type = self.idl_type.unwrap(typedef=True) + if idl_type.is_enumeration: + return "static_cast<{}::Enum>(0)".format(self.type_info.value_t) + return None + + @property + def exposure(self): + return self._exposure -def _blink_member_name(member): - assert isinstance(member, web_idl.DictionaryMember) +def bind_local_vars(code_node, cg_context): + assert isinstance(code_node, SymbolScopeNode) + assert isinstance(cg_context, CodeGenContext) - class BlinkMemberName(object): - def __init__(self, member): - blink_name = (member.code_generator_info.property_implemented_as - or member.identifier) - self.get_api = name_style.api_func(blink_name) - self.get_or_api = name_style.api_func(blink_name, "or") - self.set_api = name_style.api_func("set", blink_name) - self.has_api = name_style.api_func("has", blink_name) - # C++ data member that shows the presence of the IDL member. - self.presence_var = name_style.member_var("has", blink_name) - # C++ data member that holds the value of the IDL member. - self.value_var = name_style.member_var_f("member_{}", blink_name) - # Migration Adapters - self.get_non_null_api = name_style.api_func(blink_name, "non_null") - self.has_non_null_api = name_style.api_func( - "has", blink_name, "non_null") + S = SymbolNode + T = TextNode + F = lambda *args, **kwargs: T(_format(*args, **kwargs)) - return BlinkMemberName(member) + local_vars = [] + local_vars.extend([ + S("class_like_name", ("const char* const ${class_like_name} = " + "\"${class_like.identifier}\";")), + S("current_context", ("v8::Local<v8::Context> ${current_context} = " + "${isolate}->GetCurrentContext();")), + S("is_cross_origin_isolated", + ("const bool ${is_cross_origin_isolated} = " + "${execution_context}->CrossOriginIsolatedCapability();")), + S("is_direct_socket_enabled", + ("const bool ${is_direct_socket_enabled} = " + "${execution_context}->DirectSocketCapability();")), + S("is_in_secure_context", + ("const bool ${is_in_secure_context} = " + "${execution_context}->IsSecureContext();")), + S("v8_own_member_names", ("const auto& ${v8_own_member_names} = " + "GetV8OwnMemberNames(${isolate});")), + ]) -def _is_member_always_present(member): - assert isinstance(member, web_idl.DictionaryMember) - return member.is_required or member.default_value is not None - - -def _does_use_presence_flag(member): - assert isinstance(member, web_idl.DictionaryMember) - return (not _is_member_always_present(member) and blink_type_info( - member.idl_type).member_t not in _DICT_MEMBER_PRESENCE_PREDICATES) - - -def _member_presence_expr(member): - assert isinstance(member, web_idl.DictionaryMember) - if _is_member_always_present(member): - return "true" - if _does_use_presence_flag(member): - return _blink_member_name(member).presence_var - blink_type = blink_type_info(member.idl_type).member_t - assert blink_type in _DICT_MEMBER_PRESENCE_PREDICATES - _1 = _blink_member_name(member).value_var - return _format(_DICT_MEMBER_PRESENCE_PREDICATES[blink_type], _1) - - -def bind_member_iteration_local_vars(code_node): - local_vars = [ - SymbolNode( - "current_context", "v8::Local<v8::Context> ${current_context} = " - "${isolate}->GetCurrentContext();"), - SymbolNode( - "v8_member_names", "const auto* ${v8_member_names} = " - "GetV8MemberNames(${isolate}).data();"), - SymbolNode( - "is_cross_origin_isolated", - "const bool ${is_cross_origin_isolated} = " - "${execution_context}->CrossOriginIsolatedCapability();"), - SymbolNode( - "is_direct_socket_enabled", - "const bool ${is_direct_socket_enabled} = " - "${execution_context}->DirectSocketCapability();"), - SymbolNode( - "is_in_secure_context", "const bool ${is_in_secure_context} = " - "${execution_context}->IsSecureContext();"), - ] - - # Execution context - node = SymbolNode( - "execution_context", "ExecutionContext* ${execution_context} = " - "ToExecutionContext(${current_context});") + # execution_context + node = S("execution_context", + ("ExecutionContext* ${execution_context} = " + "ExecutionContext::From(${current_context});")) node.accumulate( CodeGenAccumulator.require_include_headers([ "third_party/blink/renderer/core/execution_context/execution_context.h" @@ -127,583 +237,555 @@ code_node.register_code_symbols(local_vars) -def _is_default_ctor_available(dictionary): - for member in dictionary.members: - if member.default_value is None: - continue - default_expr = make_default_value_expr(member.idl_type, - member.default_value) - if default_expr.initializer_deps: - return False - return True +def _constructor_needs_v8_isolate(dictionary): + assert isinstance(dictionary, web_idl.Dictionary) + + return any( + make_default_value_expr(member.idl_type, + member.default_value).initializer_deps + for member in dictionary.members if member.default_value) -def make_create_dict_funcs(cg_context): +def make_factory_methods(cg_context): assert isinstance(cg_context, CodeGenContext) - dictionary = cg_context.dictionary - name = "Create" - return_type = "${class_name}*" + T = TextNode decls = ListNode() defs = ListNode() - if _is_default_ctor_available(dictionary): - func_def = CxxFuncDefNode(name=name, + dictionary = cg_context.dictionary + + if not _constructor_needs_v8_isolate(dictionary): + func_def = CxxFuncDefNode(name="Create", arg_decls=[], - return_type=return_type, + return_type="${class_name}*", static=True) decls.append(func_def) func_def.set_base_template_vars(cg_context.template_bindings()) func_def.body.append( TextNode("return MakeGarbageCollected<${class_name}>();")) - func_def = CxxFuncDefNode(name=name, + func_def = CxxFuncDefNode(name="Create", arg_decls=["v8::Isolate* isolate"], - return_type=return_type, + return_type="${class_name}*", static=True) decls.append(func_def) func_def.set_base_template_vars(cg_context.template_bindings()) + func_def.body.add_template_vars({"isolate": "isolate"}) func_def.body.append( - TextNode("return MakeGarbageCollected<${class_name}>(isolate);")) + TextNode("return MakeGarbageCollected<${class_name}>(${isolate});")) - arg_decls = [ - "v8::Isolate* isolate", - "v8::Local<v8::Value> v8_value", - "ExceptionState& exception_state", - ] - func_decl = CxxFuncDeclNode(name=name, - arg_decls=arg_decls, - return_type=return_type, + func_decl = CxxFuncDeclNode(name="Create", + arg_decls=[ + "v8::Isolate* isolate", + "v8::Local<v8::Value> v8_value", + "ExceptionState& exception_state", + ], + return_type="${class_name}*", static=True) decls.append(func_decl) - func_def = CxxFuncDefNode(name=name, - class_name=cg_context.class_name, - arg_decls=arg_decls, - return_type=return_type) + func_def = CxxFuncDefNode(name="Create", + arg_decls=[ + "v8::Isolate* isolate", + "v8::Local<v8::Value> v8_value", + "ExceptionState& exception_state", + ], + return_type="${class_name}*", + class_name=cg_context.class_name) defs.append(func_def) func_def.set_base_template_vars(cg_context.template_bindings()) + body = func_def.body + body.add_template_vars({ + "isolate": "isolate", + "v8_value": "v8_value", + "exception_state": "exception_state", + }) + bind_local_vars(body, cg_context) - func_def.body.append( - TextNode("""\ -DCHECK(!v8_value.IsEmpty()); - -${class_name}* dictionary = Create(isolate); -dictionary->FillMembers(isolate, v8_value, exception_state); -if (exception_state.HadException()) { - return nullptr; -} -return dictionary;""")) + body.append( + T("${class_name}* dictionary = " + "MakeGarbageCollected<${class_name}>(${isolate});")) + if not dictionary.has_required_member: + body.append( + CxxLikelyIfNode(cond="${v8_value}->IsNullOrUndefined()", + body=T("return dictionary;"))) + # [PermissiveDictionaryConversion] + if "PermissiveDictionaryConversion" in dictionary.extended_attributes: + body.append( + CxxUnlikelyIfNode(cond="!${v8_value}->IsObject()", + body=[ + T("// [PermissiveDictionaryConversion]"), + T("return dictionary;"), + ])) + else: + body.append( + CxxUnlikelyIfNode(cond="!${v8_value}->IsObject()", + body=[ + T("${exception_state}.ThrowTypeError(" + "ExceptionMessages::ValueNotOfType(" + "${class_like_name}));"), + T("return nullptr;"), + ])) + body.extend([ + T("dictionary->FillMembersFromV8Object(" + "${isolate}, ${v8_value}.As<v8::Object>(), ${exception_state});"), + CxxUnlikelyIfNode(cond="${exception_state}.HadException()", + body=T("return nullptr;")), + T("return dictionary;"), + ]) return decls, defs -def make_dict_constructors(cg_context): +def make_constructors(cg_context): + assert isinstance(cg_context, CodeGenContext) + decls = ListNode() defs = ListNode() - dictionary = cg_context.dictionary - class_name = blink_class_name(dictionary) + member_initializer_list = [ + "{}({})".format(member.value_var, member.initializer_on_constructor) + for member in cg_context.dictionary_own_members + if member.initializer_on_constructor + ] - member_initializer_list = [] - should_construct_in_source = False - for member in dictionary.own_members: - if member.default_value is None: - continue - # In order to avoid cyclic header inclusion of IDL dictionaries, do not - # put dictionary member's initialization in the class definition. - does_initialize_with_dict = member.default_value.idl_type.is_object - if does_initialize_with_dict: - should_construct_in_source = True + if not _constructor_needs_v8_isolate(cg_context.dictionary): + func_decl = CxxFuncDeclNode(name=cg_context.class_name, + arg_decls=[], + return_type="", + explicit=True) + func_def = CxxFuncDefNode( + name=cg_context.class_name, + arg_decls=[], + return_type="", + class_name=cg_context.class_name, + member_initializer_list=member_initializer_list) + func_def.set_base_template_vars(cg_context.template_bindings()) + decls.append(func_decl) + defs.append(func_def) + defs.append(EmptyNode()) - default_expr = make_default_value_expr(member.idl_type, - member.default_value) - if (default_expr.initializer_deps == ["isolate"] - or does_initialize_with_dict): - _1 = _blink_member_name(member).value_var - _2 = default_expr.initializer_expr - member_initializer_list.append(_format("{_1}({_2})", _1=_1, _2=_2)) - - if _is_default_ctor_available(dictionary): - name = class_name - arg_decls = [] - return_type = "" - # In order to avoid cyclic header inclusion of IDL dictionaries, do not - # put dictionary member's initialization in the class definition. - if should_construct_in_source: - ctor_decl = CxxFuncDeclNode(name=name, - arg_decls=arg_decls, - return_type=return_type) - decls.append(ctor_decl) - ctor_def = CxxFuncDefNode( - name=name, - class_name=class_name, - arg_decls=arg_decls, - return_type=return_type, - member_initializer_list=member_initializer_list) - defs.append(ctor_def) - else: - ctor_decl = CxxFuncDeclNode(name=name, - arg_decls=arg_decls, - return_type=return_type, - default=True) - decls.append(ctor_decl) - - ctor_decl = CxxFuncDeclNode(name=class_name, + if cg_context.dictionary.inherited: + member_initializer_list = ["${base_class_name}(isolate)" + ] + member_initializer_list + func_decl = CxxFuncDeclNode(name=cg_context.class_name, arg_decls=["v8::Isolate* isolate"], return_type="", explicit=True) - decls.append(ctor_decl) - ctor_decl.set_base_template_vars(cg_context.template_bindings()) - - member_initializer_list.insert(0, "BaseClass(${isolate})") - ctor_def = CxxFuncDefNode(name=class_name, - class_name=class_name, + func_def = CxxFuncDefNode(name=cg_context.class_name, arg_decls=["v8::Isolate* isolate"], return_type="", + class_name=cg_context.class_name, member_initializer_list=member_initializer_list) - defs.append(ctor_def) - ctor_def.set_base_template_vars(cg_context.template_bindings()) - ctor_def.add_template_var("isolate", "isolate") - - return decls, defs - - -def make_dict_member_get(cg_context): - assert isinstance(cg_context, CodeGenContext) - - member = cg_context.dict_member - blink_member_name = _blink_member_name(member) - name = blink_member_name.get_api - idl_type = member.idl_type.unwrap(typedef=True) - blink_type = blink_type_info(idl_type) - const_ref_t = blink_type.const_ref_t - ref_t = blink_type.ref_t - - # Since Blink conventionally prefers non-const references to const - # references for the certain types, makes const member's getters return a - # non-const reference. For example, "Node* foo() const;" is preferable to - # "const Node* foo() const;". - if (idl_type.unwrap().is_interface - or idl_type.unwrap().is_callback_interface - or idl_type.unwrap().is_callback_function - or idl_type.unwrap().is_buffer_source_type): - const_ref_t = ref_t - - decls = ListNode() - defs = ListNode() - - func_def = CxxFuncDefNode(name=name, - arg_decls=[], - return_type=const_ref_t, - const=True) - decls.append(func_def) func_def.set_base_template_vars(cg_context.template_bindings()) - func_def.body.extend([ - TextNode(_format("DCHECK({}());", blink_member_name.has_api)), - TextNode(_format("return {};", blink_member_name.value_var)), - ]) - - if ref_t != const_ref_t: - func_def = CxxFuncDefNode(name=name, arg_decls=[], return_type=ref_t) - decls.append(func_def) - func_def.set_base_template_vars(cg_context.template_bindings()) - func_def.body.extend([ - TextNode(_format("DCHECK({}());", blink_member_name.has_api)), - TextNode(_format("return {};", blink_member_name.value_var)), - ]) - - if idl_type.is_numeric or idl_type.unwrap().is_string: - arg_type = blink_type.const_ref_t - return_type = blink_type.value_t - elif idl_type.unwrap().is_enumeration: - arg_type = ("absl::nullopt_t" - if idl_type.is_nullable else blink_type.value_t) - return_type = blink_type.value_t - elif idl_type.unwrap().is_dictionary: - arg_type = "nullptr_t" - return_type = blink_type.const_ref_t - elif idl_type.unwrap().type_definition_object: - arg_type = "nullptr_t" - return_type = blink_type.ref_t - elif idl_type.is_nullable and (idl_type.unwrap().is_sequence - or idl_type.unwrap().is_frozen_array - or idl_type.unwrap().is_record): - arg_type = "absl::nullopt_t" - return_type = blink_type.value_t - else: - arg_type = None - return_type = None - - if arg_type is not None or return_type is not None: - assert arg_type is not None and return_type is not None - - name = blink_member_name.get_or_api - arg_decls = [_format("{} fallback_value", arg_type)] - - def should_be_defined_in_source(member): - # sequence<Dictionary> and record<String, Dictionary> are - # implemented with HeapVector and Dictionary's definition is - # required while promise<Dictionary> doesn't require it. - def element_or_value_of(idl_type): - return (idl_type.unwrap().element_type - or idl_type.unwrap().value_type) - - idl_type = element_or_value_of(member.idl_type) - while idl_type and element_or_value_of(idl_type): - idl_type = element_or_value_of(idl_type) - return idl_type and idl_type.unwrap().is_dictionary - - # In order to avoid cyclic header inclusion of IDL dictionaries, - # whenever the function needs a dictionary's definition, do not put the - # function definition in the header file. - if should_be_defined_in_source(member): - func_decl = CxxFuncDeclNode(name=name, - arg_decls=arg_decls, - return_type=return_type, - const=True) - decls.append(func_decl) - func_def = CxxFuncDefNode(name=name, - class_name=cg_context.class_name, - arg_decls=arg_decls, - return_type=return_type, - const=True) - defs.append(func_def) - func_def.set_base_template_vars(cg_context.template_bindings()) - else: - func_def = CxxFuncDefNode(name=name, - arg_decls=arg_decls, - return_type=return_type, - const=True) - decls.append(func_def) - func_def.set_base_template_vars(cg_context.template_bindings()) - - body_node = TextNode( - _format("""\ -if ({has}()) {{ - return {get}(); -}} -return fallback_value;""", - has=blink_member_name.has_api, - get=blink_member_name.get_api)) - func_def.body.append(body_node) + func_def.add_template_vars({"isolate": "isolate"}) + decls.append(func_decl) + defs.append(func_def) return decls, defs -def make_dict_member_has(cg_context): - assert isinstance(cg_context, CodeGenContext) - - member = cg_context.dict_member - - decls = ListNode() - defs = ListNode() - - func_def = CxxFuncDefNode( - name=_blink_member_name(member).has_api, - arg_decls=[], - return_type="bool", - const=True) - decls.append(func_def) - func_def.set_base_template_vars(cg_context.template_bindings()) - body = func_def.body - - _1 = _member_presence_expr(member) - body.append(TextNode(_format("return {_1};", _1=_1))) - - return decls, defs - - -def make_dict_member_set(cg_context): +def make_accessor_functions(cg_context): assert isinstance(cg_context, CodeGenContext) T = TextNode + F = lambda *args, **kwargs: T(_format(*args, **kwargs)) - member = cg_context.dict_member - blink_member_name = _blink_member_name(member) - real_type = member.idl_type.unwrap(typedef=True) - type_info = blink_type_info(real_type) + def make_check_assigned_value(member): + idl_type = member.idl_type.unwrap(typedef=True) + if idl_type.is_object: + return F("DCHECK({}.IsObject());", member.value_var) + if (member.type_info.is_gc_type and not idl_type.is_nullable): + return F("DCHECK({});", member.value_var) + return None + + def make_api_has(member): + func_def = CxxFuncDefNode(name=member.api_has, + arg_decls=[], + return_type="bool", + const=True) + func_def.set_base_template_vars(cg_context.template_bindings()) + func_def.body.append(F("return {};", member.presence_expr)) + return func_def, None + + def make_api_get(member): + func_def = CxxFuncDefNode(name=member.api_get, + arg_decls=[], + return_type=member.type_info.member_ref_t, + const=True) + func_def.set_base_template_vars(cg_context.template_bindings()) + if not member.is_always_present: + func_def.body.append(F("DCHECK({}());", member.api_has)) + func_def.body.append(F("return {};", member.value_var)) + return func_def, None + + def make_api_get_or(member): + func_def = CxxFuncDefNode(name=member.api_get_or, + arg_decls=[ + "{} fallback_value".format( + member.type_info.member_ref_t) + ], + return_type=member.type_info.value_t, + const=True) + func_def.set_base_template_vars(cg_context.template_bindings()) + func_def.body.extend([ + CxxUnlikelyIfNode(cond="!{}()".format(member.api_has), + body=T("return fallback_value;")), + F("return {};", member.value_var), + ]) + return func_def, None + + def make_api_get_or_copy_and_move(member): + copy_func_decl = CxxFuncDeclNode(name=member.api_get_or, + arg_decls=[ + "{} fallback_value".format( + member.type_info.member_ref_t) + ], + return_type=member.type_info.value_t, + const=True) + copy_func_def = CxxFuncDefNode(name=member.api_get_or, + arg_decls=[ + "{} fallback_value".format( + member.type_info.member_ref_t) + ], + return_type=member.type_info.value_t, + class_name=cg_context.class_name, + const=True) + copy_func_def.set_base_template_vars(cg_context.template_bindings()) + copy_func_def.body.extend([ + CxxUnlikelyIfNode(cond="!{}()".format(member.api_has), + body=T("return fallback_value;")), + F("return {};", member.value_var), + ]) + + move_func_decl = CxxFuncDeclNode( + name=member.api_get_or, + arg_decls=["{}&& fallback_value".format(member.type_info.value_t)], + return_type=member.type_info.value_t, + const=True) + move_func_def = CxxFuncDefNode( + name=member.api_get_or, + arg_decls=["{}&& fallback_value".format(member.type_info.value_t)], + return_type=member.type_info.value_t, + class_name=cg_context.class_name, + const=True) + move_func_def.set_base_template_vars(cg_context.template_bindings()) + move_func_def.body.extend([ + CxxUnlikelyIfNode(cond="!{}()".format(member.api_has), + body=T("return std::move(fallback_value);")), + F("return {};", member.value_var), + ]) + + decls = ListNode([copy_func_decl, move_func_decl]) + defs = ListNode([copy_func_def, EmptyNode(), move_func_def]) + return decls, defs + + def make_api_get_or_string(member): + # getMemberOr(const char*) in addition to + # getMemberOr(const String&) to avoid creation of a temporary String + # object. + if not member.idl_type.unwrap(typedef=True).is_string: + return None, None + func_def = CxxFuncDefNode(name=member.api_get_or, + arg_decls=["const char* fallback_value"], + return_type=member.type_info.value_t, + const=True) + func_def.set_base_template_vars(cg_context.template_bindings()) + func_def.body.extend([ + CxxUnlikelyIfNode(cond="!{}()".format(member.api_has), + body=T("return fallback_value;")), + F("return {};", member.value_var), + ]) + return func_def, None + + def make_api_set(member, type_info=None): + if type_info is None: + type_info = member.type_info + func_def = CxxFuncDefNode( + name=member.api_set, + arg_decls=["{} value".format(type_info.member_ref_t)], + return_type="void") + func_def.set_base_template_vars(cg_context.template_bindings()) + func_def.body.append(F("{} = value;", member.value_var)) + if member.does_use_presence_var: + func_def.body.append(F("{} = true;", member.presence_var)) + func_def.body.append(make_check_assigned_value(member)) + return func_def, None + + def make_api_set_copy_and_move(member, type_info=None): + if type_info is None: + type_info = member.type_info + copy_func_decl = CxxFuncDeclNode( + name=member.api_set, + arg_decls=["{} value".format(type_info.member_ref_t)], + return_type="void") + copy_func_def = CxxFuncDefNode( + name=member.api_set, + arg_decls=["{} value".format(type_info.member_ref_t)], + return_type="void", + class_name=cg_context.class_name) + copy_func_def.set_base_template_vars(cg_context.template_bindings()) + copy_func_def.body.append(F("{} = value;", member.value_var)) + if member.does_use_presence_var: + copy_func_def.body.append(F("{} = true;", member.presence_var)) + copy_func_def.body.append(make_check_assigned_value(member)) + + move_func_decl = CxxFuncDeclNode( + name=member.api_set, + arg_decls=["{}&& value".format(type_info.value_t)], + return_type="void") + move_func_def = CxxFuncDefNode( + name=member.api_set, + arg_decls=["{}&& value".format(type_info.value_t)], + return_type="void", + class_name=cg_context.class_name) + move_func_def.set_base_template_vars(cg_context.template_bindings()) + move_func_def.body.append(F("{} = std::move(value);", + member.value_var)) + if member.does_use_presence_var: + move_func_def.body.append(F("{} = true;", member.presence_var)) + move_func_def.body.append(make_check_assigned_value(member)) + + decls = ListNode([copy_func_decl, move_func_decl]) + defs = ListNode([copy_func_def, EmptyNode(), move_func_def]) + return decls, defs + + def make_api_set_non_nullable(member): + # setMember(InnerType) in addition to + # setMember(absl::optional<InnerType>) for convenience. + if not (member.idl_type.does_include_nullable_type + and not member.type_info.has_null_value): + return None, None + return make_api_set(member, blink_type_info(member.idl_type.unwrap())) + + def make_api_set_copy_and_move_non_nullable(member): + # setMember(InnerType) in addition to + # setMember(absl::optional<InnerType>) for convenience. + if not (member.idl_type.does_include_nullable_type + and not member.type_info.has_null_value): + return None, None + return make_api_set_copy_and_move( + member, blink_type_info(member.idl_type.unwrap())) + + def make_api_set_enum(member): + # setMember(V8Enum::Enum) in addition to + # setMember(V8Enum) for convenience. + if not member.idl_type.unwrap().is_enumeration: + return None, None + type_info = blink_type_info(member.idl_type.unwrap()) + func_def = CxxFuncDefNode( + name=member.api_set, + arg_decls=["{}::Enum value".format(type_info.value_t)], + return_type="void") + func_def.set_base_template_vars(cg_context.template_bindings()) + func_def.body.append( + F("{} = {}(value);", member.value_var, type_info.value_t)) + if member.does_use_presence_var: + func_def.body.append(F("{} = true;", member.presence_var)) + func_def.body.append(make_check_assigned_value(member)) + return func_def, None decls = ListNode() defs = ListNode() - template_func_def = CxxFuncDefNode( - name=blink_member_name.set_api, - arg_decls=["T&& value"], - return_type="void", - template_params=["typename T"]) - decls.append(template_func_def) + def add(func_decl, func_def): + decls.append(func_decl) + defs.append(func_def) + defs.append(EmptyNode()) - # This setter with the explicit type declaration makes it possible to set - # the dictionary member with uniform initialization (especially aggregate - # initialization), e.g. setIntVector({3, 1, 4, 1, 5}). - move_func_decl = CxxFuncDeclNode( - name=blink_member_name.set_api, - arg_decls=[_format("{}&&", type_info.member_t)], - return_type="void") - decls.append(move_func_decl) + for member in cg_context.dictionary_own_members: + # Predicate + add(*make_api_has(member)) - move_func_def = CxxFuncDefNode( - name=blink_member_name.set_api, - arg_decls=[_format("{}&& value", type_info.member_t)], - return_type="void", - class_name=cg_context.class_name) - defs.append(move_func_def) + # Getter + add(*make_api_get(member)) + if member.is_always_present: + pass + elif member.type_info.is_move_effective: + add(*make_api_get_or_copy_and_move(member)) + else: + add(*make_api_get_or(member)) + add(*make_api_get_or_string(member)) - _1 = blink_member_name.value_var - template_func_def.body.append( - T(_format("{_1} = std::forward<T>(value);", _1=_1))) - move_func_def.body.append(T(_format("{_1} = value;", _1=_1))) + # Setter + if member.type_info.is_move_effective: + add(*make_api_set_copy_and_move(member)) + add(*make_api_set_copy_and_move_non_nullable(member)) + else: + add(*make_api_set(member)) + add(*make_api_set_non_nullable(member)) + add(*make_api_set_enum(member)) - if _does_use_presence_flag(member): - set_presense_expr = _format("{} = true;", - blink_member_name.presence_var) - template_func_def.body.append(T(set_presense_expr)) - move_func_def.body.append(T(set_presense_expr)) - - # Migration Adapter - if (real_type.is_nullable and - blink_type_info(real_type).typename.startswith("absl::optional")): - to_null_func_def = CxxFuncDefNode( - name=_format("{}ToNull", blink_member_name.set_api), - arg_decls=[], - return_type="void") - decls.append(to_null_func_def) - to_null_func_def.set_base_template_vars(cg_context.template_bindings()) - to_null_func_def.body.append( - T(_format("{}(absl::nullopt);", blink_member_name.set_api))) + decls.append(EmptyNode()) return decls, defs -def make_dict_member_vars(cg_context): +def make_backward_compatible_accessors(cg_context): assert isinstance(cg_context, CodeGenContext) - member = cg_context.dict_member + # TODO(crbug.com/1070871): Remove the accessors introduced just to be + # backward compatible. - default_value_initializer = "" - if member.default_value: - default_expr = make_default_value_expr(member.idl_type, - member.default_value) - # In order to avoid cyclic header inclusion of IDL dictionaries, do not - # put dictionary member's initialization in the class definition. - if (default_expr.initializer_expr is not None - and not default_expr.initializer_deps - and not member.default_value.idl_type.is_object): - default_value_initializer = _format("{{{}}}", - default_expr.initializer_expr) + T = TextNode + F = lambda *args, **kwargs: T(_format(*args, **kwargs)) - _1 = blink_type_info(member.idl_type).member_t - _2 = _blink_member_name(member).value_var - _3 = default_value_initializer - value_var_def = TextNode(_format("{_1} {_2}{_3};", _1=_1, _2=_2, _3=_3)) + decls = ListNode() - if _does_use_presence_flag(member): - _1 = _blink_member_name(member).presence_var - presense_var_def = TextNode(_format("bool {_1} = false;", _1=_1)) - else: - presense_var_def = None - - return value_var_def, presense_var_def - - -def make_dict_member_migration_adapters(cg_context): - assert isinstance(cg_context, CodeGenContext) - - member = cg_context.dict_member - blink_member_name = _blink_member_name(member) - idl_type = member.idl_type - blink_type = blink_type_info(idl_type) - real_type = idl_type.unwrap(typedef=True) - - if (not real_type.is_nullable - or blink_type_info(real_type.inner_type).has_null_value): - return None, None - - decls = ListNode([TextNode("// Migration Adapters")]) - defs = ListNode() - - # Accessors for non-null values, if the usual getter returns - # absl::optional<T>. - blink_inner_type = blink_type_info(real_type.inner_type) - get_api = blink_member_name.get_api - has_api = blink_member_name.has_api - get_non_null_api = blink_member_name.get_non_null_api - has_non_null_api = blink_member_name.has_non_null_api - - func_def = CxxFuncDefNode(name=get_non_null_api, - arg_decls=[], - return_type=blink_inner_type.const_ref_t, - const=True) - decls.extend([ - TextNode( - _format( - """\ -// Returns the value if this member has a non-null value. Call -// |{}| in advance to check the condition.""", has_non_null_api)), - func_def, - ]) - func_def.set_base_template_vars(cg_context.template_bindings()) - func_def.body.extend([ - TextNode(_format("DCHECK({}());", has_non_null_api)), - TextNode(_format("return {}().value();", get_api)), - ]) - - if blink_inner_type.ref_t != blink_inner_type.const_ref_t: - func_def = CxxFuncDefNode(name=get_non_null_api, + def make_api_has_non_null(member): + func_def = CxxFuncDefNode(name=member.api_has_non_null, arg_decls=[], - return_type=blink_inner_type.ref_t) - decls.append(func_def) + return_type="bool", + const=True) + func_def.set_base_template_vars(cg_context.template_bindings()) + func_def.body.append( + F("return {}() && {}().has_value();", member.api_has, + member.api_get)) + return func_def + + def make_api_get_non_null(member): + func_def = CxxFuncDefNode(name=member.api_get_non_null, + arg_decls=[], + return_type=blink_type_info( + member.idl_type.unwrap()).member_ref_t, + const=True) func_def.set_base_template_vars(cg_context.template_bindings()) func_def.body.extend([ - TextNode(_format("DCHECK({}());", has_non_null_api)), - TextNode(_format("return {}().value();", get_api)), + F("DCHECK({}());", member.api_has_non_null), + F("return {}().value();", member.api_get), ]) + return func_def - func_def = CxxFuncDefNode(name=has_non_null_api, - arg_decls=[], - return_type="bool", + def make_api_set_enum_string(member): + type_info = blink_type_info(member.idl_type.unwrap()) + func_def = CxxFuncDefNode(name=member.api_set, + arg_decls=["const String& value"], + return_type="void") + func_def.set_base_template_vars(cg_context.template_bindings()) + func_def.body.append( + F("{} = {}::Create(value).value();", member.value_var, + type_info.value_t)) + if member.does_use_presence_var: + func_def.body.append(F("{} = true;", member.presence_var)) + return func_def + + for member in cg_context.dictionary_own_members: + if member.idl_type.unwrap().is_enumeration: + decls.append(make_api_set_enum_string(member)) + + if (not member.idl_type.unwrap(nullable=False).is_nullable + or member.type_info.has_null_value): + continue + # The Blink type is absl::optional<T>. + decls.append(make_api_has_non_null(member)) + decls.append(make_api_get_non_null(member)) + + if decls: + decls.insert(0, TextNode("// Obsolete accessor functions")) + + return decls, None + + +def make_trace_function(cg_context): + assert isinstance(cg_context, CodeGenContext) + + func_decl = CxxFuncDeclNode(name="Trace", + arg_decls=["Visitor* visitor"], + return_type="void", + const=True, + override=True) + + func_def = CxxFuncDefNode(name="Trace", + arg_decls=["Visitor* visitor"], + return_type="void", + class_name=cg_context.class_name, const=True) - decls.extend([ - TextNode("""\ -// Returns true iff this member has a non-null value. Returns false if the -// value is missing or the null value."""), - func_def, + func_def.set_base_template_vars(cg_context.template_bindings()) + body = func_def.body + + for member in cg_context.dictionary_own_members: + body.append( + TextNode("TraceIfNeeded<{}>::Trace(visitor, {});".format( + member.type_info.member_t, member.value_var))) + body.append(TextNode("${base_class_name}::Trace(visitor);")) + + return func_decl, func_def + + +def make_blink_to_v8_function(cg_context): + assert isinstance(cg_context, CodeGenContext) + + S = SymbolNode + T = TextNode + F = lambda *args, **kwargs: T(_format(*args, **kwargs)) + + func_decl = CxxFuncDeclNode(name="FillV8ObjectWithMembers", + arg_decls=[ + "ScriptState* script_state", + "v8::Local<v8::Object> v8_dictionary", + ], + return_type="bool", + const=True, + override=True) + + func_def = CxxFuncDefNode(name="FillV8ObjectWithMembers", + arg_decls=[ + "ScriptState* script_state", + "v8::Local<v8::Object> v8_dictionary", + ], + return_type="bool", + class_name=cg_context.class_name, + const=True) + func_def.set_base_template_vars(cg_context.template_bindings()) + body = func_def.body + body.add_template_vars({ + "script_state": "script_state", + "v8_dictionary": "v8_dictionary", + }) + body.register_code_symbols([ + S("isolate", + "v8::Isolate* ${isolate} = ${script_state}->GetIsolate();"), + S("v8_value", "v8::Local<v8::Value> ${v8_value};"), + S("was_property_created", "bool ${was_property_created};"), ]) - func_def.set_base_template_vars(cg_context.template_bindings()) - func_def.body.append( - TextNode(_format("return {}() && {}().has_value();", has_api, - get_api))) + bind_local_vars(body, cg_context) - return decls, defs - - -def make_get_v8_dict_member_names_func(cg_context): - assert isinstance(cg_context, CodeGenContext) - - dictionary = cg_context.dictionary - name = "GetV8MemberNames" - arg_decls = ["v8::Isolate* isolate"] - return_type = "const base::span<const v8::Eternal<v8::Name>>" - - func_decl = CxxFuncDeclNode( - name=name, arg_decls=arg_decls, return_type=return_type, static=True) - func_def = CxxFuncDefNode( - name=name, - class_name=cg_context.class_name, - arg_decls=arg_decls, - return_type=return_type) - func_def.set_base_template_vars(cg_context.template_bindings()) - body = func_def.body - - if dictionary.own_members: - pattern = "static const char* const kKeyStrings[] = {{{_1}}};" - _1 = ", ".join( - _format("\"{}\"", member.identifier) - for member in dictionary.own_members) + if cg_context.dictionary.inherited: body.extend([ - TextNode(_format(pattern, _1=_1)), - TextNode("return V8PerIsolateData::From(isolate)" - "->FindOrCreateEternalNameCache(kKeyStrings, " - "kKeyStrings);"), + CxxUnlikelyIfNode( # + cond=T("!${base_class_name}::FillV8ObjectWithMembers" + "(${script_state}, ${v8_dictionary})"), + body=T("return false;")), + EmptyNode(), ]) - else: - body.append(TextNode("return {};")) - return func_decl, func_def - - -def make_fill_with_dict_members_func(cg_context): - assert isinstance(cg_context, CodeGenContext) - - dictionary = cg_context.dictionary - name = "FillWithMembers" - arg_decls = [ - "v8::Isolate* isolate", - "v8::Local<v8::Object> creation_context", - "v8::Local<v8::Object> v8_dictionary", - ] - return_type = "bool" - - func_decl = CxxFuncDeclNode( - name=name, - arg_decls=arg_decls, - return_type=return_type, - const=True, - override=True) - func_def = CxxFuncDefNode( - name=name, - class_name=cg_context.class_name, - arg_decls=arg_decls, - return_type=return_type, - const=True) - func_def.set_base_template_vars(cg_context.template_bindings()) - body = func_def.body - - if dictionary.inherited: - text = """\ -if (!BaseClass::FillWithMembers(isolate, creation_context, v8_dictionary)) { - return false; -}""" - body.append(TextNode(text)) - - body.append( - TextNode("return FillWithOwnMembers(" - "isolate, creation_context, v8_dictionary);")) - - return func_decl, func_def - - -def make_fill_with_own_dict_members_func(cg_context): - assert isinstance(cg_context, CodeGenContext) - - dictionary = cg_context.dictionary - own_members = dictionary.own_members - name = "FillWithOwnMembers" - arg_decls = [ - "v8::Isolate* isolate", - "v8::Local<v8::Object> creation_context", - "v8::Local<v8::Object> v8_dictionary", - ] - return_type = "bool" - - func_decl = CxxFuncDeclNode( - name=name, arg_decls=arg_decls, return_type=return_type, const=True) - func_def = CxxFuncDefNode( - name=name, - class_name=cg_context.class_name, - arg_decls=arg_decls, - return_type=return_type, - const=True) - func_def.set_base_template_vars(cg_context.template_bindings()) - body = func_def.body - body.add_template_var("isolate", "isolate") - body.add_template_var("creation_context", "creation_context") - body.add_template_var("v8_dictionary", "v8_dictionary") - bind_member_iteration_local_vars(body) - - for index, member in enumerate(own_members): - member_name = _blink_member_name(member) - v8_member_name = name_style.local_var_f("v8_member_{}", - member.identifier) - body.register_code_symbol( - make_blink_to_v8_value(v8_member_name, - "{}()".format(member_name.get_api), - member.idl_type, "${creation_context}")) + for index, member in enumerate(cg_context.dictionary_own_members): node = CxxLikelyIfNode( - cond="{}()".format(member_name.has_api), - body=TextNode( - _format( - "${v8_dictionary}->CreateDataProperty(" - "${current_context}, " - "${v8_member_names}[{index}].Get(${isolate}), " - "${{{v8_member_name}}}" - ").ToChecked();", - index=index, - v8_member_name=v8_member_name))) + cond="{}()".format(member.api_has), + body=[ + CxxUnlikelyIfNode( # + cond=_format( + "!ToV8Traits<{native_value_tag}>::" + "ToV8(${script_state}, {blink_value})" + ".ToLocal(&${v8_value})", + native_value_tag=native_value_tag(member.idl_type), + blink_value=member.value_var), + body=T("return false;")), + CxxUnlikelyIfNode( # + cond=_format( + "!${v8_dictionary}->CreateDataProperty(" + "${current_context}, " + "${v8_own_member_names}[{index}].Get(${isolate}), " + "${v8_value}).To(&${was_property_created})", + index=index), + body=T("return false;")), + ]) + conditional = expr_from_exposure(member.exposure) if not conditional.is_always_true: node = CxxLikelyIfNode(cond=conditional, body=node) + body.append(node) body.append(TextNode("return true;")) @@ -711,193 +793,154 @@ return func_decl, func_def -def make_fill_dict_members_func(cg_context): +def make_v8_to_blink_function(cg_context): assert isinstance(cg_context, CodeGenContext) + S = SymbolNode T = TextNode + F = lambda *args, **kwargs: T(_format(*args, **kwargs)) - dictionary = cg_context.dictionary - own_members = dictionary.own_members - required_own_members = list( - member for member in own_members if member.is_required) - name = "FillMembers" - arg_decls = [ - "v8::Isolate* isolate", - "v8::Local<v8::Value> v8_value", - "ExceptionState& exception_state", - ] - return_type = "void" + func_decl = CxxFuncDeclNode(name="FillMembersFromV8Object", + arg_decls=[ + "v8::Isolate* isolate", + "v8::Local<v8::Object> v8_dictionary", + "ExceptionState& exception_state", + ], + return_type="void") - func_decl = CxxFuncDeclNode( - name=name, arg_decls=arg_decls, return_type=return_type) - func_def = CxxFuncDefNode( - name=name, - class_name=cg_context.class_name, - arg_decls=arg_decls, - return_type=return_type) - func_def.set_base_template_vars(cg_context.template_bindings()) - - if required_own_members: - check_required_members_node = T("""\ -if (v8_value->IsNullOrUndefined()) { - exception_state.ThrowTypeError(ExceptionMessages::FailedToConstruct( - "${dictionary.identifier}", - "has required members, but null/undefined was passed.")); - return; -}""") - else: - check_required_members_node = T("""\ -if (v8_value->IsNullOrUndefined()) { - return; -}""") - - # [PermissiveDictionaryConversion] - if "PermissiveDictionaryConversion" in dictionary.extended_attributes: - permissive_conversion_node = T("""\ -if (!v8_value->IsObject()) { - // [PermissiveDictionaryConversion] - return; -}""") - else: - permissive_conversion_node = T("""\ -if (!v8_value->IsObject()) { - exception_state.ThrowTypeError( - ExceptionMessages::FailedToConstruct( - "${dictionary.identifier}", "The value is not of type Object")); - return; -}""") - - call_internal_func_node = T("""\ -FillMembersInternal(isolate, v8_value.As<v8::Object>(), exception_state);""") - - func_def.body.extend([ - check_required_members_node, - permissive_conversion_node, - call_internal_func_node, - ]) - - return func_decl, func_def - - -def make_fill_dict_members_internal_func(cg_context): - assert isinstance(cg_context, CodeGenContext) - - T = TextNode - - dictionary = cg_context.dictionary - own_members = dictionary.own_members - name = "FillMembersInternal" - arg_decls = [ - "v8::Isolate* isolate", - "v8::Local<v8::Object> v8_dictionary", - "ExceptionState& exception_state", - ] - return_type = "void" - func_decl = CxxFuncDeclNode( - name=name, arg_decls=arg_decls, return_type=return_type) - func_def = CxxFuncDefNode( - name=name, - class_name=cg_context.class_name, - arg_decls=arg_decls, - return_type=return_type) + func_def = CxxFuncDefNode(name="FillMembersFromV8Object", + arg_decls=[ + "v8::Isolate* isolate", + "v8::Local<v8::Object> v8_dictionary", + "ExceptionState& exception_state", + ], + return_type="void", + class_name=cg_context.class_name) func_def.set_base_template_vars(cg_context.template_bindings()) body = func_def.body - body.add_template_var("isolate", "isolate") - body.add_template_var("exception_state", "exception_state") - bind_member_iteration_local_vars(body) + body.add_template_vars({ + "isolate": "isolate", + "v8_dictionary": "v8_dictionary", + "exception_state": "exception_state", + }) body.register_code_symbols([ - SymbolNode("try_block", "v8::TryCatch ${try_block}(${isolate});"), - SymbolNode("v8_value", "v8::Local<v8::Value> ${v8_value};"), - SymbolNode("unused_presence_var", "bool ${unused_presence_var};"), + S("exception_context_scope", + ("ExceptionState::ContextScope ${exception_context_scope}(" + "ExceptionContext(" + "ExceptionContext::Context::kDictionaryMemberGet, " + "${class_like_name}, \"\"), " + "${exception_state});")), + S("fallback_presence_var", "bool ${fallback_presence_var};"), + S("is_optional", "constexpr bool ${is_optional} = false;"), + S("is_required", "constexpr bool ${is_required} = true;"), + S("try_block", "v8::TryCatch ${try_block}(${isolate});"), ]) + bind_local_vars(body, cg_context) - if dictionary.inherited: - text = """\ -BaseClass::FillMembersInternal(${isolate}, v8_dictionary, ${exception_state}); -if (${exception_state}.HadException()) { - return; -} -""" - body.append(T(text)) + if cg_context.dictionary.inherited: + body.extend([ + T("${base_class_name}::FillMembersFromV8Object" + "(${isolate}, ${v8_dictionary}, ${exception_state});"), + CxxUnlikelyIfNode(cond="${exception_state}.HadException()", + body=T("return;")), + EmptyNode(), + ]) - for key_index, member in enumerate(own_members): - body.append(make_fill_own_dict_member(key_index, member)) + for index, member in enumerate(cg_context.dictionary_own_members): + cond = _format( + "!bindings::GetDictionaryMemberFromV8Object" + "<{native_value_tag}, {is_required}>(" + "${isolate}, ${current_context}, " + "${v8_dictionary}, " + "${v8_own_member_names}[{index}].Get(${isolate}), " + "{presence_var}, {value_var}, " + "${try_block}, ${exception_state})", + native_value_tag=native_value_tag(member.idl_type), + is_required=("${is_required}" + if member.is_required else "${is_optional}"), + index=index, + presence_var=(member.presence_var if member.does_use_presence_var + else "${fallback_presence_var}"), + value_var=member.value_var) + node = SequenceNode([ + F(("${exception_context_scope}" + ".ChangePropertyNameAsOptimizationHack(\"{member_name}\");"), + member_name=member.identifier), + CxxUnlikelyIfNode(cond=cond, body=T("return;")), + ]) + + conditional = expr_from_exposure(member.exposure) + if not conditional.is_always_true: + node = CxxLikelyIfNode(cond=conditional, body=node) + + body.append(node) return func_decl, func_def -def make_fill_own_dict_member(key_index, member): - assert isinstance(key_index, int) - assert isinstance(member, web_idl.DictionaryMember) +def make_v8_own_member_names_function(cg_context): + assert isinstance(cg_context, CodeGenContext) - pattern = """\ -if (!bindings::ConvertDictionaryMember<{nvt_tag}, {is_required}>( - ${isolate}, - ${current_context}, - v8_dictionary, - ${v8_member_names}[{key_index}].Get(${isolate}), - "${{dictionary.identifier}}", - "{member_name}", - {value_var}, - {presence_var}, - ${try_block}, - ${exception_state})) {{ - return; -}}""" - if _does_use_presence_flag(member): - presence_var = _blink_member_name(member).presence_var - else: - presence_var = "${unused_presence_var}" - node = TextNode( - _format(pattern, - nvt_tag=native_value_tag(member.idl_type), - is_required="true" if member.is_required else "false", - key_index=key_index, - member_name=member.identifier, - value_var=_blink_member_name(member).value_var, - presence_var=presence_var)) + func_decl = CxxFuncDeclNode( + name="GetV8OwnMemberNames", + arg_decls=["v8::Isolate* isolate"], + return_type="const base::span<const v8::Eternal<v8::Name>>", + static=True) - conditional = expr_from_exposure(member.exposure) - if not conditional.is_always_true: - node = CxxLikelyIfNode(cond=conditional, body=node) + func_def = CxxFuncDefNode( + name="GetV8OwnMemberNames", + arg_decls=["v8::Isolate* isolate"], + return_type="const base::span<const v8::Eternal<v8::Name>>", + class_name=cg_context.class_name) + func_def.set_base_template_vars(cg_context.template_bindings()) + body = func_def.body + func_def.body.add_template_vars({"isolate": "isolate"}) - return node + if not cg_context.dictionary.own_members: + body.append(TextNode("return {};")) + return func_decl, func_def + + body.extend([ + ListNode([ + TextNode("static const char* const kOwnMemberNames[] = {"), + ListNode([ + TextNode("\"{}\",".format(member.identifier)) + for member in cg_context.dictionary.own_members + ]), + TextNode("};"), + ]), + TextNode("return V8PerIsolateData::From(${isolate})" + "->FindOrCreateEternalNameCache" + "(kOwnMemberNames, kOwnMemberNames);"), + ]) + + return func_decl, func_def -def make_dict_trace_func(cg_context): +def make_member_vars_def(cg_context): assert isinstance(cg_context, CodeGenContext) T = TextNode + F = lambda *args, **kwargs: T(_format(*args, **kwargs)) - dictionary = cg_context.dictionary - own_members = dictionary.own_members - name = "Trace" - arg_decls = ["Visitor* visitor"] - return_type = "void" + presence_vars = ListNode([ + F("bool {} = false;", member.presence_var) + for member in cg_context.dictionary_own_members + if member.does_use_presence_var + ]) - func_decl = CxxFuncDeclNode(name=name, - arg_decls=arg_decls, - return_type=return_type, - const=True, - override=True) - func_def = CxxFuncDefNode(name=name, - class_name=cg_context.class_name, - arg_decls=arg_decls, - return_type=return_type, - const=True) - func_def.set_base_template_vars(cg_context.template_bindings()) - body = func_def.body + value_vars = ListNode([ + F("{} {}{};", member.type_info.member_t, member.value_var, + ("{{{}}}".format(member.initializer_on_member_decl) + if member.initializer_on_member_decl else "")) + for member in cg_context.dictionary_own_members + ]) - def make_trace_member_node(member): - pattern = "TraceIfNeeded<{_1}>::Trace(visitor, {_2});" - _1 = blink_type_info(member.idl_type).member_t - _2 = _blink_member_name(member).value_var - return TextNode(_format(pattern, _1=_1, _2=_2)) - - body.extend(list(map(make_trace_member_node, own_members))) - body.append(TextNode("BaseClass::Trace(visitor);")) - - return func_decl, func_def + return ListNode([ + presence_vars, + EmptyNode(), + value_vars, + ]) def generate_dictionary(dictionary_identifier): @@ -908,21 +951,23 @@ path_manager = PathManager(dictionary) assert path_manager.api_component == path_manager.impl_component, ( - "We don't support partial dictionaries across components yet.") + "Partial dictionaries across components are not supported.") api_component = path_manager.api_component for_testing = dictionary.code_generator_info.for_testing - path_manager = PathManager(dictionary) - class_name = name_style.class_(blink_class_name(dictionary)) + # Class names + class_name = blink_class_name(dictionary) if dictionary.inherited: base_class_name = blink_class_name(dictionary.inherited) else: base_class_name = "bindings::DictionaryBase" - cg_context = CodeGenContext( - dictionary=dictionary, - class_name=class_name, - base_class_name=base_class_name) + cg_context = CodeGenContext(dictionary=dictionary, + dictionary_own_members=tuple( + map(_DictionaryMember, + dictionary.own_members)), + class_name=class_name, + base_class_name=base_class_name) # Filepaths header_path = path_manager.api_path(ext="h") @@ -940,96 +985,53 @@ header_blink_ns = CxxNamespaceNode(name_style.namespace("blink")) source_blink_ns = CxxNamespaceNode(name_style.namespace("blink")) - # Class definitions + # Class definition class_def = CxxClassDefNode(cg_context.class_name, base_class_names=[cg_context.base_class_name], export=component_export( api_component, for_testing)) class_def.set_base_template_vars(cg_context.template_bindings()) - class_def.top_section.append( - TextNode("using BaseClass = ${base_class_name};")) - # Create functions - create_decl, create_def = make_create_dict_funcs(cg_context) - - # Constructor and destructor - constructor_decls, constructor_defs = make_dict_constructors(cg_context) - destructor_decl = CxxFuncDeclNode( - name="~${class_name}", arg_decls=[], return_type="", default=True) - - # Fill with members (Blink -> V8 conversion) - (fill_with_members_decl, - fill_with_members_def) = make_fill_with_dict_members_func(cg_context) - (fill_with_own_members_decl, fill_with_own_members_def - ) = make_fill_with_own_dict_members_func(cg_context) - - # Fill members (V8 -> Blink conversion) - (fill_members_decl, - fill_members_def) = make_fill_dict_members_func(cg_context) - (fill_members_internal_decl, fill_members_internal_def - ) = make_fill_dict_members_internal_func(cg_context) - - # Misc. functions - (get_v8_member_names_decl, - get_v8_member_names_def) = make_get_v8_dict_member_names_func(cg_context) - trace_decl, trace_def = make_dict_trace_func(cg_context) - - member_accessor_decls = ListNode() - member_accessor_defs = ListNode() - member_value_var_defs = ListNode() - member_presense_var_defs = ListNode() - for member in cg_context.dictionary.own_members: - member_context = cg_context.make_copy(dict_member=member) - get_decls, get_defs = make_dict_member_get(member_context) - has_decls, has_defs = make_dict_member_has(member_context) - set_decls, set_defs = make_dict_member_set(member_context) - value_var_def, presense_var_def = make_dict_member_vars(member_context) - (migration_adapter_decls, migration_adapter_defs - ) = make_dict_member_migration_adapters(member_context) - member_accessor_decls.extend([ - TextNode(""), - get_decls, - has_decls, - set_decls, - migration_adapter_decls, - ]) - member_accessor_defs.extend([ - TextNode(""), - get_defs, - has_defs, - set_defs, - migration_adapter_defs, - ]) - member_value_var_defs.append(value_var_def) - member_presense_var_defs.append(presense_var_def) + # Implementation parts + factory_decls, factory_defs = make_factory_methods(cg_context) + ctor_decls, ctor_defs = make_constructors(cg_context) + accessor_decls, accessor_defs = make_accessor_functions(cg_context) + backward_compatible_accessor_decls, backward_compatible_accessor_defs = ( + make_backward_compatible_accessors(cg_context)) + trace_func_decls, trace_func_defs = make_trace_function(cg_context) + blink_to_v8_decls, blink_to_v8_defs = make_blink_to_v8_function(cg_context) + v8_to_blink_decls, v8_to_blink_defs = make_v8_to_blink_function(cg_context) + v8_names_decls, v8_names_defs = make_v8_own_member_names_function( + cg_context) + member_vars_def = make_member_vars_def(cg_context) # Header part (copyright, include directives, and forward declarations) header_node.extend([ make_copyright_header(), - TextNode(""), + EmptyNode(), enclose_with_header_guard( ListNode([ make_header_include_directives(header_node.accumulator), - TextNode(""), + EmptyNode(), header_blink_ns, ]), name_style.header_guard(header_path)), ]) header_blink_ns.body.extend([ make_forward_declarations(header_node.accumulator), - TextNode(""), + EmptyNode(), ]) source_node.extend([ make_copyright_header(), - TextNode(""), + EmptyNode(), TextNode("#include \"{}\"".format(header_path)), - TextNode(""), + EmptyNode(), make_header_include_directives(source_node.accumulator), - TextNode(""), + EmptyNode(), source_blink_ns, ]) source_blink_ns.body.extend([ make_forward_declarations(source_node.accumulator), - TextNode(""), + EmptyNode(), ]) # Assemble the parts. @@ -1039,68 +1041,68 @@ if dictionary.inherited else "third_party/blink/renderer/platform/bindings/dictionary_base.h"), component_export_header(api_component, for_testing), - "v8/include/v8.h", ]) source_node.accumulator.add_include_headers([ "third_party/blink/renderer/bindings/core/v8/generated_code_helper.h", "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h", + "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h", "third_party/blink/renderer/platform/bindings/exception_messages.h", "third_party/blink/renderer/platform/bindings/exception_state.h", "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h", ]) (header_forward_decls, header_include_headers, source_forward_decls, source_include_headers) = collect_forward_decls_and_include_headers( - map(lambda member: member.idl_type, dictionary.own_members)) + list(map(lambda member: member.idl_type, dictionary.own_members))) header_node.accumulator.add_class_decls(header_forward_decls) header_node.accumulator.add_include_headers(header_include_headers) source_node.accumulator.add_class_decls(source_forward_decls) source_node.accumulator.add_include_headers(source_include_headers) header_blink_ns.body.append(class_def) - class_def.public_section.extend([ - create_decl, - constructor_decls, - destructor_decl, - TextNode(""), - trace_decl, - TextNode(""), - member_accessor_decls, - ]) - class_def.protected_section.extend([ - fill_with_members_decl, - TextNode(""), - fill_members_internal_decl, - ]) - class_def.private_section.extend([ - get_v8_member_names_decl, - TextNode(""), - fill_with_own_members_decl, - TextNode(""), - fill_members_decl, - TextNode(""), - member_value_var_defs, - TextNode(""), - member_presense_var_defs, - ]) - source_blink_ns.body.extend([ - constructor_defs, - TextNode(""), - get_v8_member_names_def, - TextNode(""), - create_def, - TextNode(""), - fill_with_members_def, - TextNode(""), - fill_with_own_members_def, - TextNode(""), - fill_members_def, - TextNode(""), - fill_members_internal_def, - TextNode(""), - member_accessor_defs, - TextNode(""), - trace_def, - ]) + header_blink_ns.body.append(EmptyNode()) + + class_def.public_section.append(factory_decls) + class_def.public_section.append(EmptyNode()) + source_blink_ns.body.append(factory_defs) + source_blink_ns.body.append(EmptyNode()) + + class_def.public_section.append(ctor_decls) + class_def.public_section.append(EmptyNode()) + source_blink_ns.body.append(ctor_defs) + source_blink_ns.body.append(EmptyNode()) + + class_def.public_section.append(accessor_decls) + class_def.public_section.append(EmptyNode()) + source_blink_ns.body.append(accessor_defs) + source_blink_ns.body.append(EmptyNode()) + + class_def.public_section.append(backward_compatible_accessor_decls) + class_def.public_section.append(EmptyNode()) + source_blink_ns.body.append(backward_compatible_accessor_defs) + source_blink_ns.body.append(EmptyNode()) + + class_def.public_section.append(trace_func_decls) + class_def.public_section.append(EmptyNode()) + source_blink_ns.body.append(trace_func_defs) + source_blink_ns.body.append(EmptyNode()) + + class_def.protected_section.append(blink_to_v8_decls) + class_def.protected_section.append(EmptyNode()) + source_blink_ns.body.append(blink_to_v8_defs) + source_blink_ns.body.append(EmptyNode()) + + class_def.protected_section.append(v8_to_blink_decls) + class_def.protected_section.append(EmptyNode()) + source_blink_ns.body.append(v8_to_blink_defs) + source_blink_ns.body.append(EmptyNode()) + + class_def.private_section.append(v8_names_decls) + class_def.private_section.append(EmptyNode()) + source_blink_ns.body.append(v8_names_defs) + source_blink_ns.body.append(EmptyNode()) + + class_def.private_section.append(member_vars_def) + class_def.private_section.append(EmptyNode()) # Write down to the files. write_code_node_to_file(header_node, path_manager.gen_path_to(header_path))
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/task_queue.py b/third_party/blink/renderer/bindings/scripts/bind_gen/task_queue.py index 7532e2c..e06b401 100644 --- a/third_party/blink/renderer/bindings/scripts/bind_gen/task_queue.py +++ b/third_party/blink/renderer/bindings/scripts/bind_gen/task_queue.py
@@ -70,7 +70,7 @@ for index, task in enumerate(self._requested_tasks): func, args, kwargs = task report_progress(len(self._requested_tasks), index) - apply(func, args, kwargs) + func(*args, **kwargs) report_progress(len(self._requested_tasks), len(self._requested_tasks)) def _run_in_parallel(self, report_progress):
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/union.py b/third_party/blink/renderer/bindings/scripts/bind_gen/union.py index 7c735fa..23e1e744f 100644 --- a/third_party/blink/renderer/bindings/scripts/bind_gen/union.py +++ b/third_party/blink/renderer/bindings/scripts/bind_gen/union.py
@@ -42,6 +42,14 @@ class _UnionMember(object): + """ + _UnionMember represents the properties that the code generator directly + needs while web_idl.Union represents properties of IDL union independent + from ECMAScript binding. _UnionMember is specific to not only ECMAScript + binding but also Blink implementation of IDL union and its flattened member + types. + """ + def __init__(self, base_name): assert isinstance(base_name, str)
diff --git a/third_party/blink/renderer/core/animation/OWNERS b/third_party/blink/renderer/core/animation/OWNERS index e757120..ed9edb6 100644 --- a/third_party/blink/renderer/core/animation/OWNERS +++ b/third_party/blink/renderer/core/animation/OWNERS
@@ -5,6 +5,3 @@ # Legacy owners: alancutter@chromium.org -ericwilligers@chromium.org - -per-file compositor_*=loyso@chromium.org
diff --git a/third_party/blink/renderer/core/content_capture/OWNERS b/third_party/blink/renderer/core/content_capture/OWNERS index bc2e55f..ef41f5b 100644 --- a/third_party/blink/renderer/core/content_capture/OWNERS +++ b/third_party/blink/renderer/core/content_capture/OWNERS
@@ -1,4 +1,3 @@ pdr@chromium.org michaelbai@chromium.org -skyostil@chromium.org wangxianzhu@chromium.org
diff --git a/third_party/blink/renderer/core/css/OWNERS b/third_party/blink/renderer/core/css/OWNERS index 7c1cab45..ec2b6419 100644 --- a/third_party/blink/renderer/core/css/OWNERS +++ b/third_party/blink/renderer/core/css/OWNERS
@@ -1,4 +1,3 @@ -alancutter@chromium.org andruud@chromium.org ericwilligers@chromium.org futhark@chromium.org
diff --git a/third_party/blink/renderer/core/css/css_font_face_src_value.cc b/third_party/blink/renderer/core/css/css_font_face_src_value.cc index df689dac..2f6b536 100644 --- a/third_party/blink/renderer/core/css/css_font_face_src_value.cc +++ b/third_party/blink/renderer/core/css/css_font_face_src_value.cc
@@ -93,7 +93,8 @@ resource_request.SetIsAdResource(); ResourceLoaderOptions options(world_); options.initiator_info.name = fetch_initiator_type_names::kCSS; - options.initiator_info.referrer = referrer_.referrer; + if (referrer_.referrer != Referrer::ClientReferrerString()) + options.initiator_info.referrer = referrer_.referrer; FetchParameters params(std::move(resource_request), options); if (base::FeatureList::IsEnabled( features::kWebFontsCacheAwareTimeoutAdaption)) {
diff --git a/third_party/blink/renderer/core/css/css_image_value.cc b/third_party/blink/renderer/core/css/css_image_value.cc index a54fb823..c89fcaf5e 100644 --- a/third_party/blink/renderer/core/css/css_image_value.cc +++ b/third_party/blink/renderer/core/css/css_image_value.cc
@@ -71,7 +71,8 @@ options.initiator_info.name = initiator_name_.IsEmpty() ? fetch_initiator_type_names::kCSS : initiator_name_; - options.initiator_info.referrer = referrer_.referrer; + if (referrer_.referrer != Referrer::ClientReferrerString()) + options.initiator_info.referrer = referrer_.referrer; FetchParameters params(std::move(resource_request), options); if (cross_origin != kCrossOriginAttributeNotSet) {
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc index 078fc86..145c94e 100644 --- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc +++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -825,8 +825,9 @@ while (baseline == EDominantBaseline::kNoChange || baseline == EDominantBaseline::kResetSize) { parent = LayoutTreeBuilderTraversal::Parent(*parent); - baseline = parent ? parent->GetComputedStyle()->DominantBaseline() - : EDominantBaseline::kAuto; + baseline = parent && parent->GetComputedStyle() + ? parent->GetComputedStyle()->DominantBaseline() + : EDominantBaseline::kAuto; } } style.SetCssDominantBaseline(baseline);
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc index b83eb72..851dfc7 100644 --- a/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc +++ b/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc
@@ -275,4 +275,21 @@ GetDocument().IsUseCounted(WebFeature::kOverflowClipAlongEitherAxis)); } +// crbug.com/1216721 +TEST_F(StyleAdjusterTest, AdjustForSVGCrash) { + SetBodyInnerHTML(R"HTML( +<style> +.class1 { dominant-baseline: no-change; } +</style> +<svg> +<tref> +<tspan id="svgvar00005" /> +</svg> +<svg> +<use xlink:href="#svgvar00005" class="class1" /> + )HTML"); + UpdateAllLifecyclePhasesForTest(); + // Pass if we had no null pointer dereferences. +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/fetch/OWNERS b/third_party/blink/renderer/core/fetch/OWNERS index 0f06132..73144755 100644 --- a/third_party/blink/renderer/core/fetch/OWNERS +++ b/third_party/blink/renderer/core/fetch/OWNERS
@@ -1,3 +1,2 @@ -hiroshige@chromium.org horo@chromium.org yhirano@chromium.org
diff --git a/third_party/blink/renderer/core/fetch/fetch_response_data.cc b/third_party/blink/renderer/core/fetch/fetch_response_data.cc index 35c2aa5..6cd2613e 100644 --- a/third_party/blink/renderer/core/fetch/fetch_response_data.cc +++ b/third_party/blink/renderer/core/fetch/fetch_response_data.cc
@@ -174,6 +174,11 @@ return mime_type_; } +bool FetchResponseData::RequestIncludeCredentials() const { + return internal_response_ ? internal_response_->RequestIncludeCredentials() + : request_include_credentials_; +} + void FetchResponseData::SetURLList(const Vector<KURL>& url_list) { url_list_ = url_list; } @@ -208,6 +213,7 @@ new_response->alpn_negotiated_protocol_ = alpn_negotiated_protocol_; new_response->was_fetched_via_spdy_ = was_fetched_via_spdy_; new_response->has_range_requested_ = has_range_requested_; + new_response->request_include_credentials_ = request_include_credentials_; if (auth_challenge_info_) { new_response->auth_challenge_info_ = std::make_unique<net::AuthChallengeInfo>(*auth_challenge_info_); @@ -286,6 +292,7 @@ response->alpn_negotiated_protocol = alpn_negotiated_protocol_; response->was_fetched_via_spdy = was_fetched_via_spdy_; response->has_range_requested = has_range_requested_; + response->request_include_credentials = request_include_credentials_; for (const auto& header : HeaderList()->List()) response->headers.insert(header.first, header.second); response->parsed_headers = ParseHeaders( @@ -367,6 +374,7 @@ } SetAuthChallengeInfo(response.AuthChallengeInfo()); + SetRequestIncludeCredentials(response.RequestIncludeCredentials()); } FetchResponseData::FetchResponseData(Type type, @@ -393,6 +401,12 @@ } } +void FetchResponseData::SetRequestIncludeCredentials( + bool request_include_credentials) { + DCHECK(!internal_response_); + request_include_credentials_ = request_include_credentials; +} + void FetchResponseData::ReplaceBodyStreamBuffer(BodyStreamBuffer* buffer) { if (type_ == Type::kBasic || type_ == Type::kCors) { DCHECK(internal_response_);
diff --git a/third_party/blink/renderer/core/fetch/fetch_response_data.h b/third_party/blink/renderer/core/fetch/fetch_response_data.h index d5e1191..cf94881 100644 --- a/third_party/blink/renderer/core/fetch/fetch_response_data.h +++ b/third_party/blink/renderer/core/fetch/fetch_response_data.h
@@ -84,6 +84,7 @@ return cors_exposed_header_names_; } bool HasRangeRequested() const { return has_range_requested_; } + bool RequestIncludeCredentials() const; int64_t GetPadding() const { return padding_; } void SetPadding(int64_t padding) { padding_ = padding; } @@ -126,6 +127,7 @@ } void SetAuthChallengeInfo( const absl::optional<net::AuthChallengeInfo>& auth_challenge_info); + void SetRequestIncludeCredentials(bool request_include_credentials); // If the type is Default, replaces |buffer_|. // If the type is Basic or CORS, replaces |buffer_| and @@ -172,6 +174,11 @@ // |because this member is empty in most cases. std::unique_ptr<net::AuthChallengeInfo> auth_challenge_info_; + // The request's |includeCredentials| value from the "HTTP-network fetch" + // algorithm. + // See: https://fetch.spec.whatwg.org/#concept-http-network-fetch + bool request_include_credentials_ = true; + DISALLOW_COPY_AND_ASSIGN(FetchResponseData); };
diff --git a/third_party/blink/renderer/core/fetch/response.cc b/third_party/blink/renderer/core/fetch/response.cc index e2ce5c1d..f1e45a3 100644 --- a/third_party/blink/renderer/core/fetch/response.cc +++ b/third_party/blink/renderer/core/fetch/response.cc
@@ -390,6 +390,8 @@ WTF::AtomicString(fetch_api_response.alpn_negotiated_protocol)); response->SetWasFetchedViaSpdy(fetch_api_response.was_fetched_via_spdy); response->SetHasRangeRequested(fetch_api_response.has_range_requested); + response->SetRequestIncludeCredentials( + fetch_api_response.request_include_credentials); for (const auto& header : fetch_api_response.headers) response->HeaderList()->Append(header.key, header.value);
diff --git a/third_party/blink/renderer/core/inspector/OWNERS b/third_party/blink/renderer/core/inspector/OWNERS index b27e270f..f173073 100644 --- a/third_party/blink/renderer/core/inspector/OWNERS +++ b/third_party/blink/renderer/core/inspector/OWNERS
@@ -1,5 +1,4 @@ alexrudenko@chromium.org -alph@chromium.org caseq@chromium.org dgozman@chromium.org pfeldman@chromium.org
diff --git a/third_party/blink/renderer/core/layout/OWNERS b/third_party/blink/renderer/core/layout/OWNERS index ddf235e82..df28423 100644 --- a/third_party/blink/renderer/core/layout/OWNERS +++ b/third_party/blink/renderer/core/layout/OWNERS
@@ -1,7 +1,6 @@ atotic@chromium.org cbiesinger@chromium.org chrishtr@chromium.org -fmalita@chromium.org fs@opera.com pdr@chromium.org schenney@chromium.org
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni index ea12e59..a3902f07 100644 --- a/third_party/blink/renderer/core/layout/build.gni +++ b/third_party/blink/renderer/core/layout/build.gni
@@ -547,6 +547,8 @@ "ng/svg/ng_svg_text_layout_algorithm.h", "ng/svg/ng_svg_text_layout_attributes_builder.cc", "ng/svg/ng_svg_text_layout_attributes_builder.h", + "ng/svg/ng_svg_text_query.cc", + "ng/svg/ng_svg_text_query.h", "ng/svg/svg_inline_node_data.h", "ng/table/interface_casting.h", "ng/table/layout_ng_table.cc",
diff --git a/third_party/blink/renderer/core/layout/ng/svg/ng_svg_text_query.cc b/third_party/blink/renderer/core/layout/ng/svg/ng_svg_text_query.cc new file mode 100644 index 0000000..b3bf835 --- /dev/null +++ b/third_party/blink/renderer/core/layout/ng/svg/ng_svg_text_query.cc
@@ -0,0 +1,106 @@ +// Copyright 2021 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 "third_party/blink/renderer/core/layout/ng/svg/ng_svg_text_query.h" + +#include "third_party/blink/renderer/core/layout/layout_box.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.h" +#include "third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.h" + +namespace blink { + +namespace { + +unsigned NextCodePointOffset(StringView string, unsigned offset) { + ++offset; + if (offset < string.length() && U16_IS_LEAD(string[offset - 1]) && + U16_IS_TRAIL(string[offset])) + ++offset; + return offset; +} + +unsigned CodePointLength(StringView string) { + unsigned count = 0; + for (unsigned text_offset = 0; text_offset < string.length(); + text_offset = NextCodePointOffset(string, text_offset)) { + ++count; + } + return count; +} + +std::tuple<Vector<const NGFragmentItem*>, const NGFragmentItems*> +FragmentItemsInLogicalOrder(const LayoutObject& query_root) { + Vector<const NGFragmentItem*> item_list; + const NGFragmentItems* items = nullptr; + if (query_root.IsNGSVGText()) { + DCHECK_LE(To<LayoutBox>(query_root).PhysicalFragmentCount(), 1u); + for (const auto& fragment : To<LayoutBox>(query_root).PhysicalFragments()) { + if (!fragment.Items()) + continue; + items = fragment.Items(); + for (const auto& item : fragment.Items()->Items()) { + if (item.Type() == NGFragmentItem::kSvgText) + item_list.push_back(&item); + } + } + } else { + DCHECK(query_root.IsInLayoutNGInlineFormattingContext()); + NGInlineCursor cursor; + cursor.MoveToIncludingCulledInline(query_root); + items = &cursor.Items(); + for (; cursor; cursor.MoveToNextForSameLayoutObject()) { + const NGFragmentItem& item = *cursor.CurrentItem(); + if (item.Type() == NGFragmentItem::kSvgText) + item_list.push_back(&item); + } + } + // Sort |item_list| in the logical order. + std::sort(item_list.begin(), item_list.end(), + [](const NGFragmentItem* a, const NGFragmentItem* b) { + return a->StartOffset() < b->StartOffset(); + }); + return std::tie(item_list, items); +} + +} // namespace + +unsigned NGSvgTextQuery::NumberOfCharacters() const { + Vector<const NGFragmentItem*> item_list; + const NGFragmentItems* items; + std::tie(item_list, items) = FragmentItemsInLogicalOrder(query_root_); + + unsigned addressable_character_count = 0; + for (const auto* item : item_list) + addressable_character_count += CodePointLength(item->Text(*items)); + return addressable_character_count; +} + +float NGSvgTextQuery::SubStringLength(unsigned start_index, + unsigned length) const { + Vector<const NGFragmentItem*> item_list; + const NGFragmentItems* items; + std::tie(item_list, items) = FragmentItemsInLogicalOrder(query_root_); + + float total_length = 0.0f; + unsigned character_index = 0; + for (const auto* item : item_list) { + if (character_index >= start_index) { + if (start_index + length <= character_index) + break; + float inline_size = item->IsHorizontal() + ? item->SvgFragmentData()->rect.Width() + : item->SvgFragmentData()->rect.Height(); + total_length += + inline_size / + To<LayoutSVGInlineText>(item->GetLayoutObject())->ScalingFactor(); + } + character_index += CodePointLength(item->Text(*items)); + } + return total_length; +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/svg/ng_svg_text_query.h b/third_party/blink/renderer/core/layout/ng/svg/ng_svg_text_query.h new file mode 100644 index 0000000..92bad80 --- /dev/null +++ b/third_party/blink/renderer/core/layout/ng/svg/ng_svg_text_query.h
@@ -0,0 +1,31 @@ +// Copyright 2021 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 THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_SVG_NG_SVG_TEXT_QUERY_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_SVG_NG_SVG_TEXT_QUERY_H_ + +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" + +namespace blink { + +class LayoutObject; + +// An NG version of blink::SVGTextQuery, which is an implementation of SVG DOM +// functions to retrieve text geometry. +class NGSvgTextQuery { + STACK_ALLOCATED(); + + public: + explicit NGSvgTextQuery(LayoutObject& query_root) : query_root_(query_root) {} + + unsigned NumberOfCharacters() const; + float SubStringLength(unsigned start_index, unsigned length) const; + + private: + LayoutObject& query_root_; +}; + +} // namespace blink + +#endif
diff --git a/third_party/blink/renderer/core/loader/prerender_handle.cc b/third_party/blink/renderer/core/loader/prerender_handle.cc index 7c5bfdf..238db72 100644 --- a/third_party/blink/renderer/core/loader/prerender_handle.cc +++ b/third_party/blink/renderer/core/loader/prerender_handle.cc
@@ -32,7 +32,6 @@ #include "services/network/public/mojom/referrer_policy.mojom-blink.h" #include "third_party/blink/public/common/browser_interface_broker_proxy.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame_client.h" @@ -64,29 +63,14 @@ attributes->view_size = gfx::Size(document.GetFrame()->GetMainFrameViewportSize()); - HeapMojoRemote<mojom::blink::PrerenderProcessor> prerender_processor(context); HeapMojoRemote<mojom::blink::NoStatePrefetchProcessor> prefetch_processor( context); - // Run prerendering only when kPrerender2 is enabled and the origin of the - // prerendering URL is the same as the origin of the trigger context. - // TODO(https://crbug.com/1176054): This is a tentative behavior. We plan to - // support cross-origin prerendering later. - if (features::IsPrerender2Enabled() && - context->GetSecurityOrigin()->IsSameOriginWith( - SecurityOrigin::Create(url).get())) { - context->GetBrowserInterfaceBroker().GetInterface( - prerender_processor.BindNewPipeAndPassReceiver( - context->GetTaskRunner(TaskType::kMiscPlatformAPI))); - prerender_processor->Start(std::move(attributes)); - } else { - context->GetBrowserInterfaceBroker().GetInterface( - prefetch_processor.BindNewPipeAndPassReceiver( - context->GetTaskRunner(TaskType::kMiscPlatformAPI))); - prefetch_processor->Start(std::move(attributes)); - } + context->GetBrowserInterfaceBroker().GetInterface( + prefetch_processor.BindNewPipeAndPassReceiver( + context->GetTaskRunner(TaskType::kMiscPlatformAPI))); + prefetch_processor->Start(std::move(attributes)); return MakeGarbageCollected<PrerenderHandle>(PassKey(), context, url, - std::move(prerender_processor), std::move(prefetch_processor)); } @@ -94,20 +78,14 @@ PassKey pass_key, ExecutionContext* context, const KURL& url, - HeapMojoRemote<mojom::blink::PrerenderProcessor> remote_prerender_processor, HeapMojoRemote<mojom::blink::NoStatePrefetchProcessor> remote_fetch_processor) : url_(url), - remote_prerender_processor_(std::move(remote_prerender_processor)), remote_prefetch_processor_(std::move(remote_fetch_processor)) {} PrerenderHandle::~PrerenderHandle() = default; void PrerenderHandle::Cancel() { - if (remote_prerender_processor_.is_bound()) - remote_prerender_processor_->Cancel(); - remote_prerender_processor_.reset(); - if (remote_prefetch_processor_.is_bound()) remote_prefetch_processor_->Cancel(); remote_prefetch_processor_.reset(); @@ -118,7 +96,6 @@ } void PrerenderHandle::Trace(Visitor* visitor) const { - visitor->Trace(remote_prerender_processor_); visitor->Trace(remote_prefetch_processor_); }
diff --git a/third_party/blink/renderer/core/loader/prerender_handle.h b/third_party/blink/renderer/core/loader/prerender_handle.h index 2c0f3e5..a098df0 100644 --- a/third_party/blink/renderer/core/loader/prerender_handle.h +++ b/third_party/blink/renderer/core/loader/prerender_handle.h
@@ -69,7 +69,6 @@ PrerenderHandle(PassKey, ExecutionContext*, const KURL&, - HeapMojoRemote<mojom::blink::PrerenderProcessor>, HeapMojoRemote<mojom::blink::NoStatePrefetchProcessor>); ~PrerenderHandle(); @@ -82,7 +81,6 @@ private: const KURL url_; - HeapMojoRemote<mojom::blink::PrerenderProcessor> remote_prerender_processor_; HeapMojoRemote<mojom::blink::NoStatePrefetchProcessor> remote_prefetch_processor_;
diff --git a/third_party/blink/renderer/core/loader/prerender_test.cc b/third_party/blink/renderer/core/loader/prerender_test.cc index 28ff54f..7716904 100644 --- a/third_party/blink/renderer/core/loader/prerender_test.cc +++ b/third_party/blink/renderer/core/loader/prerender_test.cc
@@ -33,11 +33,9 @@ #include <memory> #include "base/memory/ptr_util.h" -#include "base/test/scoped_feature_list.h" #include "mojo/public/cpp/bindings/receiver_set.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/browser_interface_broker_proxy.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/prerender/prerender.mojom-blink.h" #include "third_party/blink/public/platform/web_cache.h" #include "third_party/blink/public/platform/web_string.h" @@ -68,22 +66,17 @@ bool IsPrefetchOnly() override { return false; } }; -class MockPrerenderProcessor : public mojom::blink::PrerenderProcessor, - public mojom::blink::NoStatePrefetchProcessor { +class MockNoStatePrefetchProcessor + : public mojom::blink::NoStatePrefetchProcessor { public: - explicit MockPrerenderProcessor( - mojo::PendingReceiver<mojom::blink::PrerenderProcessor> - pending_receiver) { - receiver_.Bind(std::move(pending_receiver)); - } - explicit MockPrerenderProcessor( + explicit MockNoStatePrefetchProcessor( mojo::PendingReceiver<mojom::blink::NoStatePrefetchProcessor> pending_receiver) { receiver_for_prefetch_.Bind(std::move(pending_receiver)); } - ~MockPrerenderProcessor() override = default; + ~MockNoStatePrefetchProcessor() override = default; - // mojom::blink::PrerenderProcessor implementation + // mojom::blink::NoStatePrefetchProcessor implementation void Start(mojom::blink::PrerenderAttributesPtr attributes) override { attributes_ = std::move(attributes); } @@ -99,21 +92,14 @@ private: mojom::blink::PrerenderAttributesPtr attributes_; - mojo::Receiver<mojom::blink::PrerenderProcessor> receiver_{this}; mojo::Receiver<mojom::blink::NoStatePrefetchProcessor> receiver_for_prefetch_{ this}; size_t cancel_count_ = 0; }; -class PrerenderTest : public testing::Test, - public testing::WithParamInterface<bool> { +class PrerenderTest : public testing::Test { public: - PrerenderTest() { - if (GetParam()) - feature_list_.InitAndEnableFeature(blink::features::kPrerender2); - } - ~PrerenderTest() override { if (web_view_helper_.GetWebView()) UnregisterMockPrerenderProcessor(); @@ -130,15 +116,9 @@ web_view_helper_.GetWebView()->SetNoStatePrefetchClient( &no_state_prefetch_client_); - if (features::IsPrerender2Enabled()) { - GetBrowserInterfaceBroker().SetBinderForTesting( - mojom::blink::PrerenderProcessor::Name_, - WTF::BindRepeating(&PrerenderTest::Bind, WTF::Unretained(this))); - } else { - GetBrowserInterfaceBroker().SetBinderForTesting( - mojom::blink::NoStatePrefetchProcessor::Name_, - WTF::BindRepeating(&PrerenderTest::Bind, WTF::Unretained(this))); - } + GetBrowserInterfaceBroker().SetBinderForTesting( + mojom::blink::NoStatePrefetchProcessor::Name_, + WTF::BindRepeating(&PrerenderTest::Bind, WTF::Unretained(this))); frame_test_helpers::LoadFrame( web_view_helper_.GetWebView()->MainFrameImpl(), @@ -146,17 +126,10 @@ } void Bind(mojo::ScopedMessagePipeHandle message_pipe_handle) { - if (features::IsPrerender2Enabled()) { - auto processor = std::make_unique<MockPrerenderProcessor>( - mojo::PendingReceiver<mojom::blink::PrerenderProcessor>( - std::move(message_pipe_handle))); - processors_.push_back(std::move(processor)); - } else { - auto processor = std::make_unique<MockPrerenderProcessor>( - mojo::PendingReceiver<mojom::blink::NoStatePrefetchProcessor>( - std::move(message_pipe_handle))); - processors_.push_back(std::move(processor)); - } + auto processor = std::make_unique<MockNoStatePrefetchProcessor>( + mojo::PendingReceiver<mojom::blink::NoStatePrefetchProcessor>( + std::move(message_pipe_handle))); + processors_.push_back(std::move(processor)); } void NavigateAway() { @@ -181,19 +154,14 @@ test::RunPendingTasks(); } - std::vector<std::unique_ptr<MockPrerenderProcessor>>& processors() { + std::vector<std::unique_ptr<MockNoStatePrefetchProcessor>>& processors() { return processors_; } private: void UnregisterMockPrerenderProcessor() { - if (features::IsPrerender2Enabled()) { - GetBrowserInterfaceBroker().SetBinderForTesting( - mojom::blink::PrerenderProcessor::Name_, {}); - } else { - GetBrowserInterfaceBroker().SetBinderForTesting( - mojom::blink::NoStatePrefetchProcessor::Name_, {}); - } + GetBrowserInterfaceBroker().SetBinderForTesting( + mojom::blink::NoStatePrefetchProcessor::Name_, {}); } BrowserInterfaceBrokerProxy& GetBrowserInterfaceBroker() { @@ -202,24 +170,19 @@ ->GetBrowserInterfaceBroker(); } - std::vector<std::unique_ptr<MockPrerenderProcessor>> processors_; - mojo::ReceiverSet<mojom::blink::PrerenderProcessor> receiver_set_; + std::vector<std::unique_ptr<MockNoStatePrefetchProcessor>> processors_; TestWebNoStatePrefetchClient no_state_prefetch_client_; frame_test_helpers::WebViewHelper web_view_helper_; - - base::test::ScopedFeatureList feature_list_; }; } // namespace -INSTANTIATE_TEST_SUITE_P(All, PrerenderTest, testing::Bool()); - -TEST_P(PrerenderTest, SinglePrerender) { +TEST_F(PrerenderTest, SinglePrerender) { Initialize("http://example.com/", "prerender/single_prerender.html"); ASSERT_EQ(processors().size(), 1u); - MockPrerenderProcessor& processor = *processors()[0]; + MockNoStatePrefetchProcessor& processor = *processors()[0]; EXPECT_EQ(KURL("http://example.com/prerender"), processor.Url()); EXPECT_EQ(mojom::blink::PrerenderTriggerType::kLinkRelPrerender, @@ -228,35 +191,35 @@ EXPECT_EQ(0u, processor.CancelCount()); } -TEST_P(PrerenderTest, CancelPrerender) { +TEST_F(PrerenderTest, CancelPrerender) { Initialize("http://example.com/", "prerender/single_prerender.html"); ASSERT_EQ(processors().size(), 1u); - MockPrerenderProcessor& processor = *processors()[0]; + MockNoStatePrefetchProcessor& processor = *processors()[0]; EXPECT_EQ(0u, processor.CancelCount()); ExecuteScript("removePrerender()"); EXPECT_EQ(1u, processor.CancelCount()); } -TEST_P(PrerenderTest, TwoPrerenders) { +TEST_F(PrerenderTest, TwoPrerenders) { Initialize("http://example.com/", "prerender/multiple_prerenders.html"); ASSERT_EQ(processors().size(), 2u); - MockPrerenderProcessor& first_processor = *processors()[0]; + MockNoStatePrefetchProcessor& first_processor = *processors()[0]; EXPECT_EQ(KURL("http://example.com/first"), first_processor.Url()); - MockPrerenderProcessor& second_processor = *processors()[1]; + MockNoStatePrefetchProcessor& second_processor = *processors()[1]; EXPECT_EQ(KURL("http://example.com/second"), second_processor.Url()); EXPECT_EQ(0u, first_processor.CancelCount()); EXPECT_EQ(0u, second_processor.CancelCount()); } -TEST_P(PrerenderTest, TwoPrerendersRemovingFirstThenNavigating) { +TEST_F(PrerenderTest, TwoPrerendersRemovingFirstThenNavigating) { Initialize("http://example.com/", "prerender/multiple_prerenders.html"); ASSERT_EQ(processors().size(), 2u); - MockPrerenderProcessor& first_processor = *processors()[0]; - MockPrerenderProcessor& second_processor = *processors()[1]; + MockNoStatePrefetchProcessor& first_processor = *processors()[0]; + MockNoStatePrefetchProcessor& second_processor = *processors()[1]; EXPECT_EQ(0u, first_processor.CancelCount()); EXPECT_EQ(0u, second_processor.CancelCount()); @@ -272,12 +235,12 @@ EXPECT_EQ(0u, second_processor.CancelCount()); } -TEST_P(PrerenderTest, TwoPrerendersAddingThird) { +TEST_F(PrerenderTest, TwoPrerendersAddingThird) { Initialize("http://example.com/", "prerender/multiple_prerenders.html"); ASSERT_EQ(processors().size(), 2u); - MockPrerenderProcessor& first_processor = *processors()[0]; - MockPrerenderProcessor& second_processor = *processors()[1]; + MockNoStatePrefetchProcessor& first_processor = *processors()[0]; + MockNoStatePrefetchProcessor& second_processor = *processors()[1]; EXPECT_EQ(0u, first_processor.CancelCount()); EXPECT_EQ(0u, second_processor.CancelCount()); @@ -285,17 +248,17 @@ ExecuteScript("addThirdPrerender()"); ASSERT_EQ(processors().size(), 3u); - MockPrerenderProcessor& third_processor = *processors()[2]; + MockNoStatePrefetchProcessor& third_processor = *processors()[2]; EXPECT_EQ(0u, first_processor.CancelCount()); EXPECT_EQ(0u, second_processor.CancelCount()); EXPECT_EQ(0u, third_processor.CancelCount()); } -TEST_P(PrerenderTest, MutateTarget) { +TEST_F(PrerenderTest, MutateTarget) { Initialize("http://example.com/", "prerender/single_prerender.html"); ASSERT_EQ(processors().size(), 1u); - MockPrerenderProcessor& processor = *processors()[0]; + MockNoStatePrefetchProcessor& processor = *processors()[0]; EXPECT_EQ(KURL("http://example.com/prerender"), processor.Url()); @@ -306,17 +269,17 @@ ExecuteScript("mutateTarget()"); ASSERT_EQ(processors().size(), 2u); - MockPrerenderProcessor& mutated_processor = *processors()[1]; + MockNoStatePrefetchProcessor& mutated_processor = *processors()[1]; EXPECT_EQ(KURL("http://example.com/mutated"), mutated_processor.Url()); EXPECT_EQ(1u, processor.CancelCount()); EXPECT_EQ(0u, mutated_processor.CancelCount()); } -TEST_P(PrerenderTest, MutateRel) { +TEST_F(PrerenderTest, MutateRel) { Initialize("http://example.com/", "prerender/single_prerender.html"); ASSERT_EQ(processors().size(), 1u); - MockPrerenderProcessor& processor = *processors()[0]; + MockNoStatePrefetchProcessor& processor = *processors()[0]; EXPECT_EQ(KURL("http://example.com/prerender"), processor.Url());
diff --git a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker_test.cc b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker_test.cc index 2214455..4b824be8 100644 --- a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker_test.cc +++ b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker_test.cc
@@ -42,8 +42,7 @@ // destruction sequence of WebView. frame_test_helpers::WebViewHelper helper; helper.Initialize(&web_frame_client, nullptr, ConfigureAndroidSettings); - helper.GetWebView()->EnableAutoResizeForTesting(gfx::Size(480, 800), - gfx::Size(480, 800)); + helper.Resize(gfx::Size(480, 800)); frame_test_helpers::LoadHTMLString( helper.GetWebView()->MainFrameImpl(), html, url_test_helpers::ToKURL("about:blank")); @@ -64,8 +63,7 @@ // destruction sequence of WebView. frame_test_helpers::WebViewHelper helper; helper.Initialize(&web_frame_client, nullptr, ConfigureAndroidSettings); - helper.GetWebView()->EnableAutoResizeForTesting(gfx::Size(480, 800), - gfx::Size(480, 800)); + helper.Resize(gfx::Size(480, 800)); url_test_helpers::RegisterMockedURLLoadFromBase( WebString::FromUTF8(kBaseUrl), blink::test::CoreTestDataPath(), WebString::FromUTF8(path));
diff --git a/third_party/blink/renderer/core/paint/OWNERS b/third_party/blink/renderer/core/paint/OWNERS index 51aafa9..543df8f 100644 --- a/third_party/blink/renderer/core/paint/OWNERS +++ b/third_party/blink/renderer/core/paint/OWNERS
@@ -1,11 +1,8 @@ # OWNERS specializing in painting code. chrishtr@chromium.org -enne@chromium.org fmalita@chromium.org fs@opera.com pdr@chromium.org schenney@chromium.org -senorblanco@chromium.org -trchen@chromium.org wangxianzhu@chromium.org
diff --git a/third_party/blink/renderer/core/probe/OWNERS b/third_party/blink/renderer/core/probe/OWNERS index a5b1e78..3e398966 100644 --- a/third_party/blink/renderer/core/probe/OWNERS +++ b/third_party/blink/renderer/core/probe/OWNERS
@@ -1,2 +1 @@ -alph@chromium.org caseq@chromium.org
diff --git a/third_party/blink/renderer/core/script/OWNERS b/third_party/blink/renderer/core/script/OWNERS index ec3c853..a6bb5f0 100644 --- a/third_party/blink/renderer/core/script/OWNERS +++ b/third_party/blink/renderer/core/script/OWNERS
@@ -1,4 +1,3 @@ dom@chromium.org hiroshige@chromium.org -jbroman@chromium.org kouhei@chromium.org
diff --git a/third_party/blink/renderer/core/svg/svg_text_content_element.cc b/third_party/blink/renderer/core/svg/svg_text_content_element.cc index c50de2b8..6d0eb1c 100644 --- a/third_party/blink/renderer/core/svg/svg_text_content_element.cc +++ b/third_party/blink/renderer/core/svg/svg_text_content_element.cc
@@ -26,6 +26,7 @@ #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/web_feature.h" #include "third_party/blink/renderer/core/layout/api/line_layout_item.h" +#include "third_party/blink/renderer/core/layout/ng/svg/ng_svg_text_query.h" #include "third_party/blink/renderer/core/layout/svg/svg_text_query.h" #include "third_party/blink/renderer/core/svg/svg_animated_length.h" #include "third_party/blink/renderer/core/svg/svg_enumeration_map.h" @@ -40,6 +41,15 @@ namespace blink { +namespace { + +bool IsNGTextOrInline(const LayoutObject* object) { + return object && (object->IsNGSVGText() || + object->IsInLayoutNGInlineFormattingContext()); +} + +} // namespace + template <> const SVGEnumerationMap& GetEnumerationMap<SVGLengthAdjustType>() { static const SVGEnumerationMap::Entry enum_items[] = { @@ -95,13 +105,21 @@ unsigned SVGTextContentElement::getNumberOfChars() { GetDocument().UpdateStyleAndLayoutForNode(this, DocumentUpdateReason::kJavaScript); - return SVGTextQuery(GetLayoutObject()).NumberOfCharacters(); + auto* layout_object = GetLayoutObject(); + if (IsNGTextOrInline(layout_object)) + return NGSvgTextQuery(*layout_object).NumberOfCharacters(); + return SVGTextQuery(layout_object).NumberOfCharacters(); } float SVGTextContentElement::getComputedTextLength() { GetDocument().UpdateStyleAndLayoutForNode(this, DocumentUpdateReason::kJavaScript); - return SVGTextQuery(GetLayoutObject()).TextLength(); + auto* layout_object = GetLayoutObject(); + if (IsNGTextOrInline(layout_object)) { + NGSvgTextQuery query(*layout_object); + return query.SubStringLength(0, query.NumberOfCharacters()); + } + return SVGTextQuery(layout_object).TextLength(); } float SVGTextContentElement::getSubStringLength( @@ -123,7 +141,10 @@ if (nchars > number_of_chars - charnum) nchars = number_of_chars - charnum; - return SVGTextQuery(GetLayoutObject()).SubStringLength(charnum, nchars); + auto* layout_object = GetLayoutObject(); + if (IsNGTextOrInline(layout_object)) + return NGSvgTextQuery(*layout_object).SubStringLength(charnum, nchars); + return SVGTextQuery(layout_object).SubStringLength(charnum, nchars); } SVGPointTearOff* SVGTextContentElement::getStartPositionOfChar( @@ -140,8 +161,13 @@ return nullptr; } - FloatPoint point = - SVGTextQuery(GetLayoutObject()).StartPositionOfCharacter(charnum); + FloatPoint point; + auto* layout_object = GetLayoutObject(); + if (IsNGTextOrInline(layout_object)) { + // TODO(1179585): Implement this. + } else { + point = SVGTextQuery(layout_object).StartPositionOfCharacter(charnum); + } return SVGPointTearOff::CreateDetached(point); } @@ -159,8 +185,13 @@ return nullptr; } - FloatPoint point = - SVGTextQuery(GetLayoutObject()).EndPositionOfCharacter(charnum); + FloatPoint point; + auto* layout_object = GetLayoutObject(); + if (IsNGTextOrInline(layout_object)) { + // TODO(1179585): Implement this. + } else { + point = SVGTextQuery(layout_object).EndPositionOfCharacter(charnum); + } return SVGPointTearOff::CreateDetached(point); } @@ -178,7 +209,13 @@ return nullptr; } - FloatRect rect = SVGTextQuery(GetLayoutObject()).ExtentOfCharacter(charnum); + FloatRect rect; + auto* layout_object = GetLayoutObject(); + if (IsNGTextOrInline(layout_object)) { + // TODO(1179585): Implement this. + } else { + rect = SVGTextQuery(layout_object).ExtentOfCharacter(charnum); + } return SVGRectTearOff::CreateDetached(rect); } @@ -196,7 +233,10 @@ return 0.0f; } - return SVGTextQuery(GetLayoutObject()).RotationOfCharacter(charnum); + auto* layout_object = GetLayoutObject(); + if (IsNGTextOrInline(layout_object)) + return 0.0f; // TODO(1179585): Implement this. + return SVGTextQuery(layout_object).RotationOfCharacter(charnum); } int SVGTextContentElement::getCharNumAtPosition( @@ -204,7 +244,10 @@ ExceptionState& exception_state) { GetDocument().UpdateStyleAndLayoutForNode(this, DocumentUpdateReason::kJavaScript); - return SVGTextQuery(GetLayoutObject()) + auto* layout_object = GetLayoutObject(); + if (IsNGTextOrInline(layout_object)) + return 0; // TODO(1179585): Implement this. + return SVGTextQuery(layout_object) .CharacterNumberAtPosition(point->Target()->Value()); }
diff --git a/third_party/blink/renderer/modules/OWNERS b/third_party/blink/renderer/modules/OWNERS index 5c0579d..4e16111 100644 --- a/third_party/blink/renderer/modules/OWNERS +++ b/third_party/blink/renderer/modules/OWNERS
@@ -6,5 +6,4 @@ # Reviewers for inspector-related code: dgozman@chromium.org -pfeldman@chromium.org caseq@chromium.org
diff --git a/third_party/blink/renderer/modules/animationworklet/OWNERS b/third_party/blink/renderer/modules/animationworklet/OWNERS index 759412f..e16b20d5 100644 --- a/third_party/blink/renderer/modules/animationworklet/OWNERS +++ b/third_party/blink/renderer/modules/animationworklet/OWNERS
@@ -2,5 +2,4 @@ majidvp@chromium.org # for Worker related changes -kinuko@chromium.org nhiroki@chromium.org
diff --git a/third_party/blink/renderer/modules/clipboard/OWNERS b/third_party/blink/renderer/modules/clipboard/OWNERS index a9eeb38..70f8c370 100644 --- a/third_party/blink/renderer/modules/clipboard/OWNERS +++ b/third_party/blink/renderer/modules/clipboard/OWNERS
@@ -5,4 +5,3 @@ garykac@chromium.org mek@chromium.org pwnall@chromium.org -jsbell@chromium.org
diff --git a/third_party/blink/renderer/modules/csspaint/OWNERS b/third_party/blink/renderer/modules/csspaint/OWNERS index cffe5fc..eb3a0a7 100644 --- a/third_party/blink/renderer/modules/csspaint/OWNERS +++ b/third_party/blink/renderer/modules/csspaint/OWNERS
@@ -1,3 +1,2 @@ flackr@chromium.org -ikilpatrick@chromium.org xidachen@chromium.org
diff --git a/third_party/blink/renderer/modules/device_orientation/OWNERS b/third_party/blink/renderer/modules/device_orientation/OWNERS index 72fc2ca..ff41043 100644 --- a/third_party/blink/renderer/modules/device_orientation/OWNERS +++ b/third_party/blink/renderer/modules/device_orientation/OWNERS
@@ -1,2 +1 @@ reillyg@chromium.org -timvolodine@chromium.org
diff --git a/third_party/blink/renderer/modules/document_metadata/OWNERS b/third_party/blink/renderer/modules/document_metadata/OWNERS deleted file mode 100644 index c48e48e..0000000 --- a/third_party/blink/renderer/modules/document_metadata/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -steimel@chromium.org
diff --git a/third_party/blink/renderer/modules/encoding/OWNERS b/third_party/blink/renderer/modules/encoding/OWNERS index 0c07f679..b47ce3e 100644 --- a/third_party/blink/renderer/modules/encoding/OWNERS +++ b/third_party/blink/renderer/modules/encoding/OWNERS
@@ -1,3 +1,2 @@ jsbell@chromium.org ricea@chromium.org -kbr@chromium.org
diff --git a/third_party/blink/renderer/modules/filesystem/OWNERS b/third_party/blink/renderer/modules/filesystem/OWNERS index b39c0796..c441c168 100644 --- a/third_party/blink/renderer/modules/filesystem/OWNERS +++ b/third_party/blink/renderer/modules/filesystem/OWNERS
@@ -1,5 +1,3 @@ mek@chromium.org jsbell@chromium.org -kinuko@chromium.org pwnall@chromium.org -nhiroki@chromium.org
diff --git a/third_party/blink/renderer/modules/gamepad/OWNERS b/third_party/blink/renderer/modules/gamepad/OWNERS index a53699ce..1aef6446 100644 --- a/third_party/blink/renderer/modules/gamepad/OWNERS +++ b/third_party/blink/renderer/modules/gamepad/OWNERS
@@ -1,3 +1,2 @@ -b.kelemen@samsung.com bajones@chromium.org mattreynolds@chromium.org
diff --git a/third_party/blink/renderer/modules/geolocation/OWNERS b/third_party/blink/renderer/modules/geolocation/OWNERS index 1ebe75b..7959d9d 100644 --- a/third_party/blink/renderer/modules/geolocation/OWNERS +++ b/third_party/blink/renderer/modules/geolocation/OWNERS
@@ -1,4 +1 @@ mattreynolds@chromium.org - -# Original (legacy) owner. -mcasas@chromium.org
diff --git a/third_party/blink/renderer/modules/imagecapture/OWNERS b/third_party/blink/renderer/modules/imagecapture/OWNERS index 5297710..94a0ef89 100644 --- a/third_party/blink/renderer/modules/imagecapture/OWNERS +++ b/third_party/blink/renderer/modules/imagecapture/OWNERS
@@ -1,5 +1,2 @@ reillyg@chromium.org rijubrata.bhaumik@intel.com - -# Original (legacy) owner. -mcasas@chromium.org
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/OWNERS b/third_party/blink/renderer/modules/mediacapturefromelement/OWNERS index b58e18d5..05cb9bd3 100644 --- a/third_party/blink/renderer/modules/mediacapturefromelement/OWNERS +++ b/third_party/blink/renderer/modules/mediacapturefromelement/OWNERS
@@ -1,5 +1 @@ guidou@chromium.org - -# Original (legacy) owner. -emircan@chromium.org -mcasas@chromium.org
diff --git a/third_party/blink/renderer/modules/mediarecorder/OWNERS b/third_party/blink/renderer/modules/mediarecorder/OWNERS index f24322a..072fd09f 100644 --- a/third_party/blink/renderer/modules/mediarecorder/OWNERS +++ b/third_party/blink/renderer/modules/mediarecorder/OWNERS
@@ -1,7 +1,5 @@ guidou@chromium.org handellm@google.com -peter@chromium.org # Original (legacy) owner. -emircan@chromium.org mcasas@chromium.org
diff --git a/third_party/blink/renderer/modules/mediasession/OWNERS b/third_party/blink/renderer/modules/mediasession/OWNERS index 3fc0474..e0bbeb1 100644 --- a/third_party/blink/renderer/modules/mediasession/OWNERS +++ b/third_party/blink/renderer/modules/mediasession/OWNERS
@@ -1,4 +1,3 @@ -foolip@chromium.org steimel@chromium.org per-file *_mojom_traits*.*=set noparent
diff --git a/third_party/blink/renderer/modules/netinfo/OWNERS b/third_party/blink/renderer/modules/netinfo/OWNERS index 3498144..0b9706f 100644 --- a/third_party/blink/renderer/modules/netinfo/OWNERS +++ b/third_party/blink/renderer/modules/netinfo/OWNERS
@@ -1,2 +1 @@ -jkarlin@chromium.org tbansal@chromium.org
diff --git a/third_party/blink/renderer/modules/payments/OWNERS b/third_party/blink/renderer/modules/payments/OWNERS index 88a70bc..d84162f7 100644 --- a/third_party/blink/renderer/modules/payments/OWNERS +++ b/third_party/blink/renderer/modules/payments/OWNERS
@@ -1,11 +1,6 @@ - file://components/payments/OWNERS jinho.bang@samsung.com -mek@chromium.org per-file *_type_converter*.*=set noparent per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS - -# Emeritus -mathp@chromium.org
diff --git a/third_party/blink/renderer/modules/sensor/OWNERS b/third_party/blink/renderer/modules/sensor/OWNERS index 461e969a..8120bc8 100644 --- a/third_party/blink/renderer/modules/sensor/OWNERS +++ b/third_party/blink/renderer/modules/sensor/OWNERS
@@ -1,4 +1,3 @@ raphael.kubo.da.costa@intel.com reillyg@chromium.org rijubrata.bhaumik@intel.com -timvolodine@chromium.org
diff --git a/third_party/blink/renderer/modules/service_worker/cross_origin_resource_policy_checker.cc b/third_party/blink/renderer/modules/service_worker/cross_origin_resource_policy_checker.cc index 992f3a5..48f38bf 100644 --- a/third_party/blink/renderer/modules/service_worker/cross_origin_resource_policy_checker.cc +++ b/third_party/blink/renderer/modules/service_worker/cross_origin_resource_policy_checker.cc
@@ -42,7 +42,8 @@ response.InternalURLList().back(), response.InternalURLList().front(), initiator_origin, corp_header_value, request_mode, initiator_origin, - request_destination, policy_, + request_destination, + response.GetResponse()->RequestIncludeCredentials(), policy_, reporter_ ? reporter_.get() : nullptr) .has_value(); }
diff --git a/third_party/blink/renderer/modules/shapedetection/OWNERS b/third_party/blink/renderer/modules/shapedetection/OWNERS index d522bc6..3931a46 100644 --- a/third_party/blink/renderer/modules/shapedetection/OWNERS +++ b/third_party/blink/renderer/modules/shapedetection/OWNERS
@@ -1,7 +1,4 @@ reillyg@chromium.org -# Original (legacy) owner. -mcasas@chromium.org - per-file *_type_converter*.*=set noparent per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/renderer/modules/speech/OWNERS b/third_party/blink/renderer/modules/speech/OWNERS index 5ecb2f4d..b997d55 100644 --- a/third_party/blink/renderer/modules/speech/OWNERS +++ b/third_party/blink/renderer/modules/speech/OWNERS
@@ -1,5 +1,2 @@ # Speech synthesis, and final approval for speech recognition dmazzoni@chromium.org - -# Speech recognition - send reviews here first, then to dmazzoni for approval. -tommi@chromium.org
diff --git a/third_party/blink/renderer/modules/vibration/OWNERS b/third_party/blink/renderer/modules/vibration/OWNERS deleted file mode 100644 index 02b971fe..0000000 --- a/third_party/blink/renderer/modules/vibration/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -mvanouwerkerk@chromium.org
diff --git a/third_party/blink/renderer/modules/wake_lock/README.md b/third_party/blink/renderer/modules/wake_lock/README.md index 4e61ece..99a018cc 100644 --- a/third_party/blink/renderer/modules/wake_lock/README.md +++ b/third_party/blink/renderer/modules/wake_lock/README.md
@@ -6,7 +6,7 @@ At the time of writing (October 2019), system wake lock requests are always denied, as allowing them depends on a proper permission model for the requests being figured out first. -The code required to implement the Wake Lock API is spread across multiple Chromium subsystems: Blink, `//content`, `//services` and `//chrome`. This document focuses on the Blink part, and the other subsystems are mentioned when necessary but without much detail. +The code required to implement the Wake Lock API is spread across multiple Chromium subsystems: Blink, `//content`, `//components`, `//services` and `//chrome`. This document focuses on the Blink part, and the other subsystems are mentioned when necessary but without much detail. ## High level overview @@ -34,7 +34,7 @@ * `content/browser/wake_lock` [implements](/content/browser/wake_lock/wake_lock_service_impl.cc) the [`WakeLockService`](../../../public/mojom/wake_lock/wake_lock.mojom) Mojo interface defined in Blink. It is responsible for communicating with Blink and connecting Blink to `services/device/wake_lock`. * `services/device/wake_lock` contains the [platform-specific parts of the implementation](../../../../../services/device/wake_lock/power_save_blocker) and implements the Wake Lock [Mojo interfaces]. -* `chrome/browser/wake_lock` contains the Chrome-specific side of permission management for Wake Locks. When the Blink implementation needs to either query or request permission for wake locks, the request bubbles up to this directory, where the decision is made based on the wake lock type (for testing purposes, `content_shell` always grants screen wake locks and denies system wake locks in [`shell_permission_manager.cc`](/content/shell/browser/web_test/web_test_message_filter.cc)). +* `components/permissions/contexts` contains the permission management for Wake Locks. When the Blink implementation needs to either query or request permission for wake locks, the request bubbles up to this directory, where the decision is made based on the wake lock type (for testing purposes, `content_shell` always grants screen wake locks and denies system wake locks in [`shell_permission_manager.cc`](/content/shell/browser/web_test/web_test_message_filter.cc)). [Mojo interfaces]: ../../../../../services/device/public/mojom/ [Wake Lock management]: https://w3c.github.io/screen-wake-lock/#managing-wake-locks @@ -48,7 +48,8 @@ * `*_test.cc` and `wake_lock_test_utils.{cc,h}` are built as part of the `blink_unittests` GN target, and attempt to have coverage over most of the code in this directory. * The unit tests in `services/device/wake_lock` test the service side of the API implementation. -* `chrome/browser/wake_lock` has unit tests for `WakeLockPermissionContext`, and browser tests for end-to-end behavior testing. +* `chrome/browser/wake_lock` has browser tests for end-to-end behavior testing. +* `components/permissions/contexts` has unit tests for `WakeLockPermissionContext`. * content_shell implements its own permission logic that mimics what is done in `//chrome` in [`shell_permission_manager.cc`](/content/shell/browser/shell_permission_manager.cc). [web platform tests]: ../../../web_tests/external/wpt/screen-wake-lock/ @@ -67,7 +68,7 @@ 1. `WakeLock::request()` performs all the validation steps described in [the spec](https://w3c.github.io/screen-wake-lock/#the-request-method). If all checks have passed, it creates a `ScriptPromiseResolver` and calls `WakeLock::DoRequest()`. 1. `WakeLock::DoRequest()` simply forwards its arguments to `WakeLock::ObtainPermission()`. It exists as a separate method just to make writing unit tests easier, as we'd otherwise be unable to use our own `ScriptPromiseResolver`s in tests. 1. `WakeLock::ObtainPermission()` connects to the [permission service](../../../public/mojom/permissions/permission.mojom) and asynchronously requests permission for a screen wake lock. -1. In the browser process, the permission request bubbles up through `//content` and reaches `//chrome`'s [`WakeLockPermissionContext`](/chrome/browser/wake_lock/wake_lock_permission_context.cc), where `WakeLockPermissionContext::GetPermissionStatusInternal()` always grants `CONTENT_SETTINGS_TYPE_WAKE_LOCK_SCREEN` permission requests. +1. In the browser process, the permission request bubbles up through `//content` and reaches [`WakeLockPermissionContext`](/components/permissions/contexts/wake_lock_permission_context.cc), where `WakeLockPermissionContext::GetPermissionStatusInternal()` always grants `CONTENT_SETTINGS_TYPE_WAKE_LOCK_SCREEN` permission requests. 1. Back in Blink, the permission request callback in this case is `WakeLock::DidReceivePermissionResponse()`. It performs some sanity checks such as verifying if the page visibility changed while waiting for the permission request to be processed. If any of the checks fail, or if the permission request was denied, the `ScriptPromiseResolver` instance created earlier by `WakeLock::request()` is rejected and we stop here. If everything went well, `WakeLockManager::AcquireWakeLock()` is called. 1. If there are no existing screen wake locks, `WakeLockManager::AcquireWakeLock()` will connect to the `WakeLockService` Mojo interface, invoke its `GetWakeLock()` method to obtain a `device::mojom::blink::WakeLock` and call its `RequestWakeLock()` method. 1. `WakeLockManager::AcquireWakeLock()` creates a new `WakeLockSentinel` instance, passing `this` as the `WakeLockSentinel`'s `WakeLockManager`. This new `WakeLockSentinel` is added to its set of [active locks]. @@ -136,4 +137,4 @@ * Screen wake lock request are always granted without prompting or user activation checks. This is based on the existing precedent of the `<video>` tag's use of `VideoWakeLock`s: they are always requested and granted transparently, so even if the Wake Lock API implementation in Chromium started requiring stricter checks, malicious actors could still embed a `<video>` tag and prevent the screen from turning off without any user interaction. -* System wake lock requests are always denied in `chrome/browser/wake_lock/wake_lock_permission_context.cc`. This means the entirety of the code is present and enabled in Blink, but all calls to `WakeLock.request('system')` currently return a promise that will be rejected with a `NotAllowedError`. Changing that requires figuring out a permission model for system wake lock requests, which, at the moment, is future work. +* System wake lock requests are always denied in `components/permissions/contexts/wake_lock_permission_context.cc`. This means the entirety of the code is present and enabled in Blink, but all calls to `WakeLock.request('system')` currently return a promise that will be rejected with a `NotAllowedError`. Changing that requires figuring out a permission model for system wake lock requests, which, at the moment, is future work.
diff --git a/third_party/blink/renderer/modules/webdatabase/OWNERS b/third_party/blink/renderer/modules/webdatabase/OWNERS index 8de7804..c0fe97d 100644 --- a/third_party/blink/renderer/modules/webdatabase/OWNERS +++ b/third_party/blink/renderer/modules/webdatabase/OWNERS
@@ -2,5 +2,4 @@ pwnall@chromium.org # Secondary -jsbell@chromium.org mek@chromium.org
diff --git a/third_party/blink/renderer/modules/webgl/OWNERS b/third_party/blink/renderer/modules/webgl/OWNERS index 2a09b59..5d487f4 100644 --- a/third_party/blink/renderer/modules/webgl/OWNERS +++ b/third_party/blink/renderer/modules/webgl/OWNERS
@@ -1,5 +1,4 @@ bajones@chromium.org kainino@chromium.org kbr@chromium.org -zmo@chromium.org jdarpinian@chromium.org
diff --git a/third_party/blink/renderer/modules/worklet/OWNERS b/third_party/blink/renderer/modules/worklet/OWNERS index 2129484b..bb3760e 100644 --- a/third_party/blink/renderer/modules/worklet/OWNERS +++ b/third_party/blink/renderer/modules/worklet/OWNERS
@@ -1,8 +1,4 @@ flackr@chromium.org -# For AnimationWorklet-related changes -majidvp@chromium.org - # For PaintWorklet-related changes -ikilpatrick@chromium.org xidachen@chromium.org
diff --git a/third_party/blink/renderer/platform/OWNERS b/third_party/blink/renderer/platform/OWNERS index 549b981e..37e78833 100644 --- a/third_party/blink/renderer/platform/OWNERS +++ b/third_party/blink/renderer/platform/OWNERS
@@ -15,7 +15,6 @@ mkwst@chromium.org noel@chromium.org pdr@chromium.org -pfeldman@chromium.org rtoy@chromium.org schenney@chromium.org senorblanco@chromium.org
diff --git a/third_party/blink/renderer/platform/animation/OWNERS b/third_party/blink/renderer/platform/animation/OWNERS index 8a44822..a7d6872 100644 --- a/third_party/blink/renderer/platform/animation/OWNERS +++ b/third_party/blink/renderer/platform/animation/OWNERS
@@ -1,3 +1 @@ file://third_party/blink/renderer/core/animation/OWNERS - -loyso@chromium.org
diff --git a/third_party/blink/renderer/platform/bindings/exception_context.h b/third_party/blink/renderer/platform/bindings/exception_context.h index dd74cc1..c07459d 100644 --- a/third_party/blink/renderer/platform/bindings/exception_context.h +++ b/third_party/blink/renderer/platform/bindings/exception_context.h
@@ -124,6 +124,13 @@ const char* GetPropertyName() const { return property_name_; } int16_t GetArgumentIndex() const { return argument_index_; } + // This is used for a performance hack to reduce the number of construction + // and destruction times of ExceptionContext when iterating over properties. + // Only the generated bindings code is allowed to use this hack. + void ChangePropertyNameAsOptimizationHack(const char* property_name) { + property_name_ = property_name; + } + private: Context context_ = Context::kEmpty; int16_t argument_index_ = 0;
diff --git a/third_party/blink/renderer/platform/bindings/exception_state.cc b/third_party/blink/renderer/platform/bindings/exception_state.cc index 1b3afbf..741d7fb3 100644 --- a/third_party/blink/renderer/platform/bindings/exception_state.cc +++ b/third_party/blink/renderer/platform/bindings/exception_state.cc
@@ -207,31 +207,35 @@ const String& m = message; switch (context.GetContext()) { - case ExceptionState::kConstructionContext: + case ExceptionContext::Context::kConstructorOperationInvoke: return ExceptionMessages::FailedToConstruct(c, m); - case ExceptionState::kExecutionContext: + case ExceptionContext::Context::kOperationInvoke: return ExceptionMessages::FailedToExecute(p, c, m); - case ExceptionState::kGetterContext: + case ExceptionContext::Context::kAttributeGet: return ExceptionMessages::FailedToGet(p, c, m); - case ExceptionState::kSetterContext: + case ExceptionContext::Context::kAttributeSet: return ExceptionMessages::FailedToSet(p, c, m); - case ExceptionState::kEnumerationContext: + case ExceptionContext::Context::kNamedPropertyEnumerate: return ExceptionMessages::FailedToEnumerate(c, m); - case ExceptionState::kQueryContext: + case ExceptionContext::Context::kNamedPropertyQuery: break; - case ExceptionState::kIndexedGetterContext: + case ExceptionContext::Context::kIndexedPropertyGet: return ExceptionMessages::FailedToGetIndexed(c, m); - case ExceptionState::kIndexedSetterContext: + case ExceptionContext::Context::kIndexedPropertySet: return ExceptionMessages::FailedToSetIndexed(c, m); - case ExceptionState::kIndexedDeletionContext: + case ExceptionContext::Context::kIndexedPropertyDelete: return ExceptionMessages::FailedToDeleteIndexed(c, m); - case ExceptionState::kNamedGetterContext: + case ExceptionContext::Context::kNamedPropertyGet: return ExceptionMessages::FailedToGetNamed(c, m); - case ExceptionState::kNamedSetterContext: + case ExceptionContext::Context::kNamedPropertySet: return ExceptionMessages::FailedToSetNamed(c, m); - case ExceptionState::kNamedDeletionContext: + case ExceptionContext::Context::kNamedPropertyDelete: return ExceptionMessages::FailedToDeleteNamed(c, m); - case ExceptionState::kUnknownContext: + case ExceptionContext::Context::kDictionaryMemberGet: + return ExceptionMessages::FailedToGet(p, c, m); + case ExceptionContext::Context::kDictionaryMemberSet: + return ExceptionMessages::FailedToSet(p, c, m); + case ExceptionContext::Context::kUnknown: break; default: NOTREACHED();
diff --git a/third_party/blink/renderer/platform/bindings/exception_state.h b/third_party/blink/renderer/platform/bindings/exception_state.h index 3f31d43..ec770c6 100644 --- a/third_party/blink/renderer/platform/bindings/exception_state.h +++ b/third_party/blink/renderer/platform/bindings/exception_state.h
@@ -99,6 +99,13 @@ ~ContextScope() { exception_state_.PopContextScope(); } + // This is used for a performance hack to reduce the number of construction + // and destruction times of ContextScope when iterating over properties. + // Only the generated bindings code is allowed to use this hack. + void ChangePropertyNameAsOptimizationHack(const char* property_name) { + context_.ChangePropertyNameAsOptimizationHack(property_name); + } + private: void SetParent(const ContextScope* parent) { parent_ = parent; } const ContextScope* GetParent() const { return parent_; } @@ -106,7 +113,7 @@ ExceptionState& exception_state_; const ContextScope* parent_ = nullptr; - const ExceptionContext context_; + ExceptionContext context_; friend class ExceptionState; }; @@ -208,6 +215,13 @@ return main_context_; } + // Returns the innermost context of the nested exception contexts. + const ExceptionContext& GetInnerMostContext() const { + if (context_stack_top_) + return context_stack_top_->GetContext(); + return main_context_; + } + // Deprecated APIs to get information about where this ExceptionState has // been created. ContextType Context() const { return GetContext().GetContext(); }
diff --git a/third_party/blink/renderer/platform/exported/mediastream/OWNERS b/third_party/blink/renderer/platform/exported/mediastream/OWNERS index b99ae05..2ad93e6 100644 --- a/third_party/blink/renderer/platform/exported/mediastream/OWNERS +++ b/third_party/blink/renderer/platform/exported/mediastream/OWNERS
@@ -1,3 +1 @@ file://third_party/blink/common/mediastream/OWNERS - -per-file media_stream_audio_processor*=aluebs@chromium.org
diff --git a/third_party/blink/renderer/platform/exported/web_url_response.cc b/third_party/blink/renderer/platform/exported/web_url_response.cc index a67bba83..9b3b99f 100644 --- a/third_party/blink/renderer/platform/exported/web_url_response.cc +++ b/third_party/blink/renderer/platform/exported/web_url_response.cc
@@ -550,6 +550,15 @@ return resource_response_->AuthChallengeInfo(); } +void WebURLResponse::SetRequestIncludeCredentials( + bool request_include_credentials) { + resource_response_->SetRequestIncludeCredentials(request_include_credentials); +} + +bool WebURLResponse::RequestIncludeCredentials() const { + return resource_response_->RequestIncludeCredentials(); +} + WebURLResponse::WebURLResponse(ResourceResponse& r) : resource_response_(&r) {} } // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/OWNERS b/third_party/blink/renderer/platform/graphics/OWNERS index cb4bc1b..f012bb9c 100644 --- a/third_party/blink/renderer/platform/graphics/OWNERS +++ b/third_party/blink/renderer/platform/graphics/OWNERS
@@ -22,4 +22,3 @@ per-file video_frame*=file://media/OWNERS per-file video_frame*=lethalantidote@chromium.org per-file surface_layer_bridge*=file://media/OWNERS -per-file surface_layer_bridge*=lethalantidote@chromium.org
diff --git a/third_party/blink/renderer/platform/json/OWNERS b/third_party/blink/renderer/platform/json/OWNERS deleted file mode 100644 index 8973bb94..0000000 --- a/third_party/blink/renderer/platform/json/OWNERS +++ /dev/null
@@ -1,2 +0,0 @@ -pfeldman@chromium.org -iclelland@chromium.org
diff --git a/third_party/blink/renderer/platform/json/json_parser.cc b/third_party/blink/renderer/platform/json/json_parser.cc index 56b11c0e..5ef104c0 100644 --- a/third_party/blink/renderer/platform/json/json_parser.cc +++ b/third_party/blink/renderer/platform/json/json_parser.cc
@@ -7,7 +7,6 @@ #include "base/notreached.h" #include "base/numerics/safe_conversions.h" #include "third_party/blink/renderer/platform/json/json_values.h" -#include "third_party/blink/renderer/platform/wtf/decimal.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" #include "third_party/blink/renderer/platform/wtf/text/string_to_number.h" @@ -478,9 +477,7 @@ bool ok; double value = CharactersToDouble(token_start.pos, cursor->pos - token_start.pos, &ok); - if (Decimal::FromDouble(value).IsInfinity()) - ok = false; - if (!ok) { + if (!ok || std::isinf(value)) { *cursor = token_start; return Error::kSyntaxError; }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_response.h b/third_party/blink/renderer/platform/loader/fetch/resource_response.h index 2cd78af..e0f5977 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_response.h +++ b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
@@ -520,6 +520,13 @@ auth_challenge_info_ = value; } + bool RequestIncludeCredentials() const { + return request_include_credentials_; + } + void SetRequestIncludeCredentials(bool request_include_credentials) { + request_include_credentials_ = request_include_credentials; + } + private: void UpdateHeaderParsedState(const AtomicString& name); @@ -710,6 +717,11 @@ KURL web_bundle_url_; absl::optional<net::AuthChallengeInfo> auth_challenge_info_; + + // The request's |includeCredentials| value from the "HTTP-network fetch" + // algorithm. + // See: https://fetch.spec.whatwg.org/#concept-http-network-fetch + bool request_include_credentials_ = true; }; } // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc index a398790..bdecf8a 100644 --- a/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc +++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc
@@ -893,6 +893,7 @@ } response->SetAuthChallengeInfo(head.auth_challenge_info); + response->SetRequestIncludeCredentials(head.request_include_credentials); const net::HttpResponseHeaders* headers = head.headers.get(); if (!headers)
diff --git a/third_party/blink/renderer/platform/p2p/OWNERS b/third_party/blink/renderer/platform/p2p/OWNERS index f73d253..e09cc12 100644 --- a/third_party/blink/renderer/platform/p2p/OWNERS +++ b/third_party/blink/renderer/platform/p2p/OWNERS
@@ -1,3 +1,2 @@ guidou@chromium.org -sergeyu@chromium.org steveanton@chromium.org
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc index 4f5b793..99a451c1 100644 --- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc +++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
@@ -199,12 +199,28 @@ return info; } +media::VideoEncodeAccelerator::Config::InterLayerPredMode +CopyFromWebRtcInterLayerPredMode( + const webrtc::InterLayerPredMode inter_layer_pred) { + switch (inter_layer_pred) { + case webrtc::InterLayerPredMode::kOff: + return media::VideoEncodeAccelerator::Config::InterLayerPredMode::kOff; + case webrtc::InterLayerPredMode::kOn: + return media::VideoEncodeAccelerator::Config::InterLayerPredMode::kOn; + case webrtc::InterLayerPredMode::kOnKeyPic: + return media::VideoEncodeAccelerator::Config::InterLayerPredMode:: + kOnKeyPic; + } +} + // Create VEA::Config::SpatialLayer from |codec_settings|. If some config of // |codec_settings| is not supported, returns false. bool CreateSpatialLayersConfig( const webrtc::VideoCodec& codec_settings, std::vector<media::VideoEncodeAccelerator::Config::SpatialLayer>* - spatial_layers) { + spatial_layers, + media::VideoEncodeAccelerator::Config::InterLayerPredMode* + inter_layer_pred) { if (codec_settings.codecType == webrtc::kVideoCodecVP8 && codec_settings.mode == webrtc::VideoCodecMode::kScreensharing && codec_settings.VP8().numberOfTemporalLayers > 1) { @@ -267,6 +283,8 @@ sl.num_of_temporal_layers = base::saturated_cast<uint8_t>(rtc_sl.numberOfTemporalLayers); } + *inter_layer_pred = CopyFromWebRtcInterLayerPredMode( + codec_settings.VP9().interLayerPred); } break; default: @@ -350,6 +368,8 @@ bool is_constrained_h264, const std::vector<media::VideoEncodeAccelerator::Config::SpatialLayer>& spatial_layers, + media::VideoEncodeAccelerator::Config::InterLayerPredMode + inter_layer_pred, SignaledValue init_event); webrtc::VideoEncoder::EncoderInfo GetEncoderInfo() const; @@ -571,6 +591,7 @@ bool is_constrained_h264, const std::vector<media::VideoEncodeAccelerator::Config::SpatialLayer>& spatial_layers, + media::VideoEncodeAccelerator::Config::InterLayerPredMode inter_layer_pred, SignaledValue init_event) { DVLOG(3) << __func__; DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -643,7 +664,7 @@ video_content_type_ == webrtc::VideoContentType::SCREENSHARE ? media::VideoEncodeAccelerator::Config::ContentType::kDisplay : media::VideoEncodeAccelerator::Config::ContentType::kCamera, - spatial_layers); + spatial_layers, inter_layer_pred); if (!video_encoder_->Initialize(config, this)) { LogAndNotifyError(FROM_HERE, "Error initializing video_encoder", media::VideoEncodeAccelerator::kInvalidArgumentError); @@ -1368,8 +1389,12 @@ std::vector<media::VideoEncodeAccelerator::Config::SpatialLayer> spatial_layers; - if (!CreateSpatialLayersConfig(*codec_settings, &spatial_layers)) + auto inter_layer_pred = + media::VideoEncodeAccelerator::Config::InterLayerPredMode::kOff; + if (!CreateSpatialLayersConfig(*codec_settings, &spatial_layers, + &inter_layer_pred)) { return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; + } // This wait is necessary because this task is completed in GPU process // asynchronously but WebRTC API is synchronous. @@ -1385,7 +1410,7 @@ scoped_refptr<Impl>(impl_), gfx::Size(codec_settings->width, codec_settings->height), codec_settings->startBitrate, profile_, is_constrained_h264_, - spatial_layers, + spatial_layers, inter_layer_pred, SignaledValue(&initialization_waiter, &initialization_retval))); // webrtc::VideoEncoder expects this call to be synchronous.
diff --git a/third_party/blink/renderer/platform/scheduler/OWNERS b/third_party/blink/renderer/platform/scheduler/OWNERS index a3f384b..436e2d4e 100644 --- a/third_party/blink/renderer/platform/scheduler/OWNERS +++ b/third_party/blink/renderer/platform/scheduler/OWNERS
@@ -1,7 +1,6 @@ # LON scheduling team altimin@chromium.org carlscab@google.com -rmcilroy@chromium.org skyostil@chromium.org # MTV web scheduling API team
diff --git a/third_party/blink/renderer/platform/testing/OWNERS b/third_party/blink/renderer/platform/testing/OWNERS index 81fbf4e9..ef21f5e 100644 --- a/third_party/blink/renderer/platform/testing/OWNERS +++ b/third_party/blink/renderer/platform/testing/OWNERS
@@ -1,4 +1,2 @@ danakj@chromium.org dcheng@chromium.org - -per-file testing_platform_support_with_web_rtc.*=hbos@chromium.org
diff --git a/third_party/blink/renderer/platform/video_capture/OWNERS b/third_party/blink/renderer/platform/video_capture/OWNERS index ea99024..3cec816 100644 --- a/third_party/blink/renderer/platform/video_capture/OWNERS +++ b/third_party/blink/renderer/platform/video_capture/OWNERS
@@ -1,5 +1,2 @@ guidou@chromium.org ilnik@chromium.org - -# Original (legacy) owner. -chfremer@chromium.org
diff --git a/third_party/blink/renderer/platform/wtf/OWNERS b/third_party/blink/renderer/platform/wtf/OWNERS index 024e0c8..c8abb4b 100644 --- a/third_party/blink/renderer/platform/wtf/OWNERS +++ b/third_party/blink/renderer/platform/wtf/OWNERS
@@ -1,4 +1,3 @@ -erik.corry@gmail.com haraken@chromium.org jochen@chromium.org thakis@chromium.org
diff --git a/third_party/blink/renderer/platform/wtf/text/OWNERS b/third_party/blink/renderer/platform/wtf/text/OWNERS index b3312871..02b3990 100644 --- a/third_party/blink/renderer/platform/wtf/text/OWNERS +++ b/third_party/blink/renderer/platform/wtf/text/OWNERS
@@ -1,5 +1,2 @@ per-file character_names.h=drott@chromium.org per-file text_codec*.*=jsbell@chromium.org -per-file text_codec*.*=jshin@chromium.org -per-file text_encoding*.*=jshin@chromium.org -per-file text_encoding*.*=jshin@chromium.org
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index a3bedadd..cca90a6 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -3990,7 +3990,6 @@ crbug.com/1179585 virtual/layout_ng_svg_text/external/wpt/svg/text/reftests/text-text-anchor-202.svg [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/external/wpt/svg/text/reftests/text-text-anchor-203.svg [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/external/wpt/svg/text/reftests/text-xml-space-001.svg [ Failure ] -crbug.com/1179585 virtual/layout_ng_svg_text/external/wpt/svg/text/scripted/getsubstringlength-emoji-ligatures.html [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/external/wpt/svg/types/scripted/SVGAnimatedEnumeration-SVGFEMorphologyElement.html [ Pass ] crbug.com/1179585 virtual/layout_ng_svg_text/external/wpt/svg/types/scripted/SVGAnimatedEnumeration-SVGMaskElement.html [ Pass ] crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/ems-display-none.svg [ Failure ] @@ -4033,14 +4032,12 @@ crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/focus-ring.svg [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/focus-ring-text.svg [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/font-face-not-in-document.svg [ Failure ] -crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/getSubStringLength.html [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/gradient-userSpaceOnUse-with-percentage.svg [ Pass Skip ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/mouse-move-on-svg-container.xhtml [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/mouse-move-on-svg-container-standalone.svg [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/path-textPath-simulation.svg [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pointer-events-text.svg [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pointer-events-text-css-transform.svg [ Failure ] -crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/selectSubString.html [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/svg-root-with-opacity.html [ Pass Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-dom-01-f.svg [ Failure Crash ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-hit-test.svg [ Failure ] @@ -4053,10 +4050,8 @@ crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/zoomed-mixed-scripts.html [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/dom/undefined-null.html [ Pass Crash ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-dom-lengthAdjust-attr.html [ Failure ] -crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-dom-textLength-attr.html [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-dom-transform-attr.html [ Pass Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-svgdom-lengthAdjust-prop.html [ Failure ] -crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-svgdom-textLength-prop.html [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/filters/feComponentTransfer-style-crash.xhtml [ Pass ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/filters/feDisplacementMap.svg [ Pass Timeout ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/filters/filter-on-filter-for-text.svg [ Failure ] @@ -4081,7 +4076,6 @@ crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/bidi-dir-rtl.html [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/bidi-embedded-direction.svg [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/bidi-getcharnumatpos.html [ Failure ] -crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/bidi-getsubstringlength.html [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/bidi-text-query.svg [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/columns-do-not-apply.html [ Pass Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/combining-character-queries.html [ Failure ] @@ -4126,9 +4120,7 @@ crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/surrogate-pair-attribute-positions.html [ Pass Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/surrogate-pair-queries.html [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/svgtextcontentelement-glyphqueries-rtl.html [ Failure ] -crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/svgtextcontentelement-methods-parameters.html [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/text-decorations-in-scaled-pattern.svg [ Failure ] -crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/text-getSubStringLength.html [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/text-outline-2.html [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/textPathBoundsBug.svg [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/textpath-outline.svg [ Failure ] @@ -4167,7 +4159,6 @@ crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/text-text-03-b.svg [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/text-tselect-02-f.svg [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/zoom/page/zoom-zoom-coords.xhtml [ Failure ] -crbug.com/1179585 virtual/layout_ng_svg_text/svg/zoom/text/lowdpi-zoom-text.html [ Failure ] crbug.com/1179585 virtual/layout_ng_svg_text/svg/zoom/zoomed-text-in-clippath.html [ Failure ] # Ignore older OSes during SVGTextNG development crbug.com/1179585 [ Mac10.12 ] virtual/layout_ng_svg_text/* [ Skip ] @@ -4216,8 +4207,6 @@ crbug.com/688670 external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-removed.html [ Pass Failure ] -crbug.com/849670 http/tests/devtools/service-workers/service-worker-v8-cache.js [ Pass Timeout Failure ] - crbug.com/664858 virtual/threaded-prefer-compositing/fast/scroll-behavior/overflow-scroll-animates.html [ Skip ] crbug.com/664858 virtual/threaded-prefer-compositing/fast/scroll-behavior/overflow-scroll-root-frame-animates.html [ Skip ] crbug.com/664858 virtual/threaded-prefer-compositing/fast/scroll-behavior/smooth-scroll/horizontal-smooth-scroll-in-rtl.html [ Skip ]
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/cache-storage.tentative.https_document-expected.txt b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/cache-storage.tentative.https_document-expected.txt deleted file mode 100644 index 3e622076..0000000 --- a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/cache-storage.tentative.https_document-expected.txt +++ /dev/null
@@ -1,17 +0,0 @@ -This is a testharness.js-based test. -PASS [document] unsafe-none => unsafe-none -FAIL [document] unsafe-none => credentialless assert_equals: expected "error" but got "retrieved" -PASS [document] unsafe-none => credentialless (omit) -PASS [document] unsafe-none => credentialless + CORP -PASS [document] unsafe-none => require-corp -PASS [document] unsafe-none => require-corp (omit) -PASS [document] unsafe-none => require-corp + CORP -PASS [document] credentialless => unsafe-none -PASS [document] credentialless => credentialless -PASS [document] credentialless => require-corp -PASS [document] credentialless => require-corp + CORP -PASS [document] require_corp => unsafe-none -PASS [document] require_corp => credentialless -PASS [document] require_corp => require-corp -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/cache-storage.tentative.https_service_worker-expected.txt b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/cache-storage.tentative.https_service_worker-expected.txt deleted file mode 100644 index ebf6505..0000000 --- a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/cache-storage.tentative.https_service_worker-expected.txt +++ /dev/null
@@ -1,17 +0,0 @@ -This is a testharness.js-based test. -PASS [service_worker] unsafe-none => unsafe-none -FAIL [service_worker] unsafe-none => credentialless assert_equals: expected "error" but got "retrieved" -PASS [service_worker] unsafe-none => credentialless (omit) -PASS [service_worker] unsafe-none => credentialless + CORP -PASS [service_worker] unsafe-none => require-corp -PASS [service_worker] unsafe-none => require-corp (omit) -PASS [service_worker] unsafe-none => require-corp + CORP -PASS [service_worker] credentialless => unsafe-none -PASS [service_worker] credentialless => credentialless -PASS [service_worker] credentialless => require-corp -PASS [service_worker] credentialless => require-corp + CORP -PASS [service_worker] require_corp => unsafe-none -PASS [service_worker] require_corp => credentialless -PASS [service_worker] require_corp => require-corp -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coop-same-origin-allow-popups-document-write-expected.txt b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coop-same-origin-allow-popups-document-write-expected.txt new file mode 100644 index 0000000..4b6c977 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coop-same-origin-allow-popups-document-write-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL coop-same-origin-allow-popups-document-write assert_equals: opener exists expected "true" but got "false" +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coop-same-origin-allow-popups-document-write.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coop-same-origin-allow-popups-document-write.html new file mode 100644 index 0000000..c577972 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/coop-same-origin-allow-popups-document-write.html
@@ -0,0 +1,61 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/common/utils.js"></script> +<script src="./resources/dispatcher.js"></script> +<script> + +/* + Regression test for: https://crbug.com/1216244 + From a window using Cross-Origin-Opener-Policy:same-origin-allow-popup, open + a new blank window and navigate it cross-origin using document.write and a + meta refresh. The openee/opener relationship must hold. +*/ + +const directory = '/html/cross-origin-opener-policy'; +const executor_path = directory + '/resources/executor.html?pipe='; +const coep_soap = + "|header(Cross-Origin-Opener-Policy,same-origin-allow-popups)"; +const same_origin = get_host_info().HTTPS_ORIGIN; +const cross_origin = get_host_info().HTTPS_REMOTE_ORIGIN; + +promise_test(async t => { + // This window: + const this_window_token = token(); + + // The opener, using COEP:same-origin-allow-popups: + const opener_token = token(); + const opener_url = same_origin + executor_path + coep_soap + + `&uuid=${opener_token}`; + const opener = window.open(opener_url); + + // Open a blank window, then use document.write and a meta refresh to navigate + // cross-origin. + const openee_token = token(); + const openee_url = cross_origin + executor_path + `&uuid=${openee_token}`; + send(opener_token, ` + openee = window.open(); + openee.document.write(\` + <meta http-equiv="refresh" content="0; url=${openee_url}"> + \`); + openee.document.close(); + `); + + // Check the openee is loaded and has access to its opener. + send(openee_token, ` + send("${this_window_token}", opener != null) + send("${this_window_token}", opener.closed); + `); + assert_equals(await receive(this_window_token), "true", "opener exists"); + assert_equals(await receive(this_window_token), "false", "opener not closed"); + + // Check the opener has still access to its openee. + send(opener_token, ` + send("${this_window_token}", openee != null) + send("${this_window_token}", openee.closed); + `); + assert_equals(await receive(this_window_token), "true", "openee exists"); + assert_equals(await receive(this_window_token), "false", "openee not closed"); +}); +</script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/console-cross-origin-iframe-logging-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console-cross-origin-iframe-logging-expected.txt deleted file mode 100644 index f38ecbd..0000000 --- a/third_party/blink/web_tests/http/tests/devtools/console-cross-origin-iframe-logging-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -Tests that cross origin errors are logged with source url and line number. - -console-cross-origin…frame-logging.js:33 Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('http://example.test:8000') does not match the recipient window's origin ('http://other.origin.example.test:8000'). -(anonymous) @ console-cross-origin…frame-logging.js:33 -
diff --git a/third_party/blink/web_tests/http/tests/devtools/console-cross-origin-iframe-logging.js b/third_party/blink/web_tests/http/tests/devtools/console-cross-origin-iframe-logging.js deleted file mode 100644 index 8f3af2ef..0000000 --- a/third_party/blink/web_tests/http/tests/devtools/console-cross-origin-iframe-logging.js +++ /dev/null
@@ -1,41 +0,0 @@ -// 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. - -(async function() { - TestRunner.addResult(`Tests that cross origin errors are logged with source url and line number.\n`); - await TestRunner.loadModule('console'); await TestRunner.loadTestModule('console_test_runner'); - await TestRunner.navigatePromise("http://example.test:8000/devtools/resources/empty.html"); - // NOTE: evaluateInPageAsync() waits on the promise at the end of block before - // resolving the promise it returned. Other forms of the evaluate including - // evaluateInPagePromise() do not do this. - await TestRunner.evaluateInPageAsync(` - const frame = document.createElement('iframe'); - frame.src = 'http://other.origin.example.test:8000/devtools/resources/cross-origin-iframe.html'; - document.body.appendChild(frame); - new Promise(f => frame.onload = f); - `); - - ConsoleTestRunner.addConsoleSniffer(finish); - Common.settingForTest('monitoringXHREnabled').set(true); - await TestRunner.evaluateInPagePromise(` - // Should fail. - try { - var host = frames[0].location.host; - } catch (e) {} - - // Should fail. - try { - frames[0].location.reload(); - } catch (e) {} - - // Should fail. - frames[0].postMessage("fail", "http://example.test:8000"); - `); - - async function finish() { - Common.settingForTest('monitoringXHREnabled').set(false); - await ConsoleTestRunner.dumpConsoleMessages(); - TestRunner.completeTest(); - } -})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/network/network-initiator-expected.txt b/third_party/blink/web_tests/http/tests/devtools/network/network-initiator-expected.txt index 94bc559b..d3a0b830 100644 --- a/third_party/blink/web_tests/http/tests/devtools/network/network-initiator-expected.txt +++ b/third_party/blink/web_tests/http/tests/devtools/network/network-initiator-expected.txt
@@ -6,17 +6,20 @@ http://127.0.0.1:8000/devtools/network/resources/network-initiator-frame.html 4 53 http://127.0.0.1:8000/devtools/network/resources/resource.php?type=image&random=1&size=200: parser http://127.0.0.1:8000/devtools/network/resources/initiator.css undefined undefined -size=300 NOT FOUND +http://127.0.0.1:8000/devtools/network/resources/resource.php?type=image&random=1&size=300: parser + http://127.0.0.1:8000/devtools/network/resources/network-initiator-frame.html 36 1 http://127.0.0.1:8000/devtools/network/resources/resource.php?type=image&random=1&size=400: script - loadData http://127.0.0.1:8000/devtools/network/resources/network-initiator-frame.html 13 8 + loadData http://127.0.0.1:8000/devtools/network/resources/network-initiator-frame.html 25 8 http://127.0.0.1:8000/devtools/network/resources/style.css: parser - http://127.0.0.1:8000/devtools/network/resources/network-initiator-frame.html 8 35 + http://127.0.0.1:8000/devtools/network/resources/network-initiator-frame.html 10 35 http://127.0.0.1:8000/devtools/network/resources/empty.html: parser - http://127.0.0.1:8000/devtools/network/resources/network-initiator-frame.html 17 45 + http://127.0.0.1:8000/devtools/network/resources/network-initiator-frame.html 29 45 http://127.0.0.1:8000/devtools/network/resources/module1.js: script - http://127.0.0.1:8000/devtools/network/resources/network-initiator-frame.html 19 16 + http://127.0.0.1:8000/devtools/network/resources/network-initiator-frame.html 31 16 http://127.0.0.1:8000/devtools/network/resources/module2.js: script http://127.0.0.1:8000/devtools/network/resources/module1.js 2 20 http://127.0.0.1:8000/devtools/network/resources/example.ttf: parser http://127.0.0.1:8000/devtools/network/resources/initiator.css undefined undefined +http://127.0.0.1:8000/devtools/network/resources/example2.ttf: parser + http://127.0.0.1:8000/devtools/network/resources/network-initiator-frame.html 36 1
diff --git a/third_party/blink/web_tests/http/tests/devtools/network/network-initiator.js b/third_party/blink/web_tests/http/tests/devtools/network/network-initiator.js index 64445e1..0dd4cbe 100644 --- a/third_party/blink/web_tests/http/tests/devtools/network/network-initiator.js +++ b/third_party/blink/web_tests/http/tests/devtools/network/network-initiator.js
@@ -68,6 +68,7 @@ dumpInitiator('module1.js'); dumpInitiator('module2.js'); dumpInitiator('example.ttf'); + dumpInitiator('example2.ttf'); TestRunner.completeTest(); } })();
diff --git a/third_party/blink/web_tests/http/tests/devtools/network/resources/network-initiator-frame.html b/third_party/blink/web_tests/http/tests/devtools/network/resources/network-initiator-frame.html index 1d82447..bf4fadae 100644 --- a/third_party/blink/web_tests/http/tests/devtools/network/resources/network-initiator-frame.html +++ b/third_party/blink/web_tests/http/tests/devtools/network/resources/network-initiator-frame.html
@@ -4,9 +4,21 @@ <body> <img src="resource.php?type=image&random=1&size=100"> <div class="image-background">This div has background image set from CSS.</div> +<div class="image-background-3">This div has background image set from inline CSS.</div> <div style="font-family: Example">This div will have font set from CSS.</div> +<div style="font-family: Example2">This div will have font set from inline CSS.</div> <div id="div-without-class">This div will have background image set from JavaScript.</div> <style>@import "style.css";</style> +<style> + .image-background-3 { + background-image: url("resource.php?type=image&random=1&size=300"); + } + @font-face { + font-family: Example2; + src: url("example2.ttf"); + } +</style> + <script> function loadData() { var xhr = new XMLHttpRequest();
diff --git a/third_party/blink/web_tests/http/tests/devtools/service-workers/service-worker-v8-cache-expected.txt b/third_party/blink/web_tests/http/tests/devtools/service-workers/service-worker-v8-cache-expected.txt index 0ff3c63..66a27eb 100644 --- a/third_party/blink/web_tests/http/tests/devtools/service-workers/service-worker-v8-cache-expected.txt +++ b/third_party/blink/web_tests/http/tests/devtools/service-workers/service-worker-v8-cache-expected.txt
@@ -40,6 +40,7 @@ columnNumber : 0 consumedCacheSize : <number> lineNumber : 0 + notStreamedReason : "script has code-cache available" streamed : <boolean> url : .../devtools/resources/v8-cache-script.js }
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/postmessage/console-cross-origin-iframe-logging-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/postmessage/console-cross-origin-iframe-logging-expected.txt new file mode 100644 index 0000000..1db2bd5 --- /dev/null +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/postmessage/console-cross-origin-iframe-logging-expected.txt
@@ -0,0 +1,19 @@ +Tests that postMessage origin mismatches are logged with source url and line number if both frames are in the same process. +Trying to access iframe's location +DOMException +Trying to reload iframe +TypeError +Trying to postMessage iframe +{ + callFrames : [ + [0] : { + columnNumber : 33 + functionName : + lineNumber : 1 + scriptId : <string> + url : + } + ] +} +Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('http://example.test:8000') does not match the recipient window's origin ('http://other.origin.example.test:8000'). +
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/postmessage/console-cross-origin-iframe-logging.js b/third_party/blink/web_tests/http/tests/inspector-protocol/postmessage/console-cross-origin-iframe-logging.js new file mode 100644 index 0000000..f5d342c --- /dev/null +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/postmessage/console-cross-origin-iframe-logging.js
@@ -0,0 +1,42 @@ +// Copyright 2021 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. + +(async function(testRunner) { + const {page, session, dp} = await testRunner.startURL( + 'http://example.test:8000/inspector-protocol/resources/empty.html', + `Tests that postMessage origin mismatches are logged with source url and line number if both frames are in the same process.`); + + const onEntryAddedHandler = event => { + testRunner.log(event.params.entry.stackTrace); + testRunner.log(event.params.entry.text); + testRunner.completeTest(); + }; + dp.Log.onEntryAdded(onEntryAddedHandler); + await dp.Log.enable(); + + await session.evaluateAsync(` + const frame = document.createElement('iframe'); + frame.src = + 'http://other.origin.example.test:8000/inspector-protocol/resources/empty.html'; + document.body.appendChild(frame); + window.myframe = frame; + new Promise(f => frame.onload = f); + `); + + testRunner.log(`Trying to access iframe's location`); + const {result: result1} = await dp.Runtime.evaluate( + {expression: 'window.myframe.contentWindow.location.host'}); + testRunner.log(result1.exceptionDetails.exception.className); + + testRunner.log(`Trying to reload iframe`); + const {result: result2} = await dp.Runtime.evaluate( + {expression: 'window.myframe.location.reload()'}); + testRunner.log(result2.exceptionDetails.exception.className); + + testRunner.log(`Trying to postMessage iframe`); + await session.evaluate(` + window.myframe.contentWindow.postMessage("fail", + "http://example.test:8000"); + `); +})
diff --git a/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/html/cross-origin-embedder-policy/credentialless/cache-storage.tentative.https_dedicated_worker-expected.txt b/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/html/cross-origin-embedder-policy/credentialless/cache-storage.tentative.https_dedicated_worker-expected.txt index 2217e45..a4269f98 100644 --- a/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/html/cross-origin-embedder-policy/credentialless/cache-storage.tentative.https_dedicated_worker-expected.txt +++ b/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/html/cross-origin-embedder-policy/credentialless/cache-storage.tentative.https_dedicated_worker-expected.txt
@@ -1,13 +1,13 @@ This is a testharness.js-based test. PASS [dedicated_worker] unsafe-none => unsafe-none -FAIL [dedicated_worker] unsafe-none => credentialless assert_equals: expected "error" but got "retrieved" +PASS [dedicated_worker] unsafe-none => credentialless PASS [dedicated_worker] unsafe-none => credentialless (omit) PASS [dedicated_worker] unsafe-none => credentialless + CORP PASS [dedicated_worker] unsafe-none => require-corp PASS [dedicated_worker] unsafe-none => require-corp (omit) PASS [dedicated_worker] unsafe-none => require-corp + CORP PASS [dedicated_worker] credentialless => unsafe-none -PASS [dedicated_worker] credentialless => credentialless +FAIL [dedicated_worker] credentialless => credentialless assert_equals: expected "retrieved" but got "error" PASS [dedicated_worker] credentialless => require-corp PASS [dedicated_worker] credentialless => require-corp + CORP PASS [dedicated_worker] require_corp => unsafe-none
diff --git a/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/html/cross-origin-embedder-policy/credentialless/shared-worker.tentative.https-expected.txt b/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/html/cross-origin-embedder-policy/credentialless/shared-worker.tentative.https-expected.txt deleted file mode 100644 index 19699d9..0000000 --- a/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/html/cross-origin-embedder-policy/credentialless/shared-worker.tentative.https-expected.txt +++ /dev/null
@@ -1,8 +0,0 @@ -This is a testharness.js-based test. -PASS shared-worker -PASS fetch same-origin -PASS fetch same-origin + credentialless worker -PASS fetch cross-origin -FAIL fetch cross-origin + credentialless worker assert_equals: coep:none => expected (undefined) undefined but got (string) "cross_origin" -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/virtual/plz-service-worker/http/tests/devtools/service-workers/service-worker-v8-cache-expected.txt b/third_party/blink/web_tests/virtual/plz-service-worker/http/tests/devtools/service-workers/service-worker-v8-cache-expected.txt deleted file mode 100644 index 66a27eb..0000000 --- a/third_party/blink/web_tests/virtual/plz-service-worker/http/tests/devtools/service-workers/service-worker-v8-cache-expected.txt +++ /dev/null
@@ -1,70 +0,0 @@ -Tests V8 cache information of Service Worker Cache Storage in timeline - ---- Trace events while installing ------------- -v8.compile Properties: -{ - data : { - columnNumber : 0 - lineNumber : 0 - notStreamedReason : "worker top-level scripts are not streamable" - streamed : <boolean> - url : .../devtools/service-workers/resources/v8-cache-worker.js - } - endTime : <number> - startTime : <number> - type : "v8.compile" -} -Text details for v8.compile: v8-cache-worker.js:1 -v8.compile Properties: -{ - data : { - columnNumber : 0 - lineNumber : 0 - notStreamedReason : "script has code-cache available" - producedCacheSize : <number> - streamed : <boolean> - url : .../devtools/resources/v8-cache-script.js - } - endTime : <number> - startTime : <number> - type : "v8.compile" -} -Text details for v8.compile: v8-cache-script.js:1 ------------------------------------------------ ---- Trace events while executing scripts ------ -v8.compile Properties: -{ - data : { - cacheConsumeOptions : "code" - cacheRejected : false - columnNumber : 0 - consumedCacheSize : <number> - lineNumber : 0 - notStreamedReason : "script has code-cache available" - streamed : <boolean> - url : .../devtools/resources/v8-cache-script.js - } - endTime : <number> - startTime : <number> - type : "v8.compile" -} -Text details for v8.compile: v8-cache-script.js:1 -v8.compile Properties: -{ - data : { - cacheConsumeOptions : "code" - cacheRejected : false - columnNumber : 0 - consumedCacheSize : <number> - lineNumber : 0 - notStreamedReason : "already used streamed data" - streamed : <boolean> - url : .../devtools/resources/v8-cache-script.js - } - endTime : <number> - startTime : <number> - type : "v8.compile" -} -Text details for v8.compile: v8-cache-script.js:1 ------------------------------------------------ -
diff --git a/third_party/blink/web_tests/wpt_internal/prerender/activate-from-iframe.html b/third_party/blink/web_tests/wpt_internal/prerender/activate-from-iframe.html new file mode 100644 index 0000000..7fb0041 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/prerender/activate-from-iframe.html
@@ -0,0 +1,24 @@ +<!DOCTYPE html> +<!-- +This file cannot be upstreamed to WPT until: +* startPrerendering() usage is replaced with a WebDriver API +--> +<title>Activate a prerendered page from iframe via window.parent.location</title> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + +promise_test(async t => { + const resultChannel = new BroadcastChannel('result-channel'); + t.add_cleanup(_ => resultChannel.close()); + + const url = `resources/activate-from-iframe.html?mode=triggering`; + window.open(url, '_blank', 'noopener'); + + const gotMessage = await new Promise(r => resultChannel.onmessage = r); + assert_equals(gotMessage.data, 'activated'); +}, `Navigation on the main frame initiated by an iframe via ` + + `window.parent.location should activate a prerendered page`); + +</script>
diff --git a/third_party/blink/web_tests/wpt_internal/prerender/resources/activate-from-iframe.html b/third_party/blink/web_tests/wpt_internal/prerender/resources/activate-from-iframe.html new file mode 100644 index 0000000..6d9dfc1 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/prerender/resources/activate-from-iframe.html
@@ -0,0 +1,79 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="utils.js"></script> +<script type="module"> + +// Used to notify an iframe to activate of activation ready. +const readyChannel = new BroadcastChannel('ready-channel'); + +// Used to notify the main test page of the test result. +const resultChannel = new BroadcastChannel('result-channel'); + +// URL to be prerendered. +const prerenderingUrl = new URL(document.URL); +prerenderingUrl.searchParams.set('mode', 'prerendering'); + +const params = new URLSearchParams(location.search); + +try { + // The `mode` param indicates the purpose of the loaded frame. + switch (params.get('mode')) { + // The main frame to trigger prerendering. + case 'triggering': { + assert_false(document.prerendering); + + // Create an iframe to activate a prerendered page instead of the main + // frame. + const iframeUrl = new URL(document.URL); + iframeUrl.searchParams.set('mode', 'activating-from-iframe'); + const iframe = document.createElement('iframe'); + iframe.src = iframeUrl; + document.body.appendChild(iframe); + + startPrerendering(prerenderingUrl.toString()); + break; + } + + // The iframe to initiate navigation on the main frame to activate the + // prerendered page. + case 'activating-from-iframe': { + assert_false(document.prerendering); + + // Wait for activation ready. + const gotMessage = await new Promise(r => readyChannel.onmessage = r); + assert_equals(gotMessage.data, 'activation ready'); + + // Initiate navigation on the main frame. This should activate the + // prerendered page. + window.parent.location = prerenderingUrl.toString(); + + // Close this iframe. This shouldn't cancel activation. + window.close(); + break; + } + + // The main frame of the page being prerendered. + case 'prerendering': { + assert_true(document.prerendering); + + // Inform the activator iframe that this page is ready for activation. + readyChannel.postMessage('activation ready'); + + // Wait until this page gets activated. + await new Promise(r => document.onprerenderingchange = r); + assert_false(document.prerendering); + + // Inform the main test page that activation succeeded. + resultChannel.postMessage('activated'); + break; + } + } +} catch (e) { + resultChannel.postMessage(e.toString()); +} finally { + readyChannel.close(); + resultChannel.close(); +} + +</script>
diff --git a/tools/clang/rewrite_raw_ptr_fields/manual-paths-to-ignore.txt b/tools/clang/rewrite_raw_ptr_fields/manual-paths-to-ignore.txt index 9369c1a..9398aefb 100644 --- a/tools/clang/rewrite_raw_ptr_fields/manual-paths-to-ignore.txt +++ b/tools/clang/rewrite_raw_ptr_fields/manual-paths-to-ignore.txt
@@ -58,6 +58,11 @@ cc/ content/browser/scheduler/responsiveness/ +# Performance related exclusion based on tab_search:top100:2020 profiler data +base/observer_list.h +base/containers/linked_list.h +base/containers/circular_deque.h + # Exclude code that only runs inside a renderer process - renderer # processes are excluded for now from the MiraclePtr project scope, # because they are sensitive to performance regressions (to a much higher
diff --git a/tools/memory/partition_allocator/profile_allocations.py b/tools/memory/partition_allocator/profile_allocations.py index dffd3137..da5ef8c 100755 --- a/tools/memory/partition_allocator/profile_allocations.py +++ b/tools/memory/partition_allocator/profile_allocations.py
@@ -90,7 +90,8 @@ } size_counts = [] for allocator in allocators: - if 'malloc/thread_cache/buckets_alloc/' not in allocator: + if ('malloc/partitions/allocator/thread_cache/buckets_alloc/' not in + allocator): continue size = int(allocator[allocator.rindex('/') + 1:]) count = int(allocators[allocator]['attrs']['count']['value'], 16) @@ -119,11 +120,12 @@ plt.title('Allocation count vs Size - %s - %s' % (data['name'], data['labels'])) plt.stem(data['data']['size'], data['data']['count']) - plt.xscale('log') - plt.yscale('log') + plt.xscale('log', base=2) + plt.yscale('log', base=10) plt.xlabel('Size (log)') plt.ylabel('Allocations (log)') plt.savefig('%s_%d_count.png' % (output_prefix, pid), bbox_inches='tight') + plt.close() # CDF. plt.figure(figsize=(16, 8)) @@ -138,12 +140,13 @@ plt.step(data['data']['size'], cdf, color='black', where='post') plt.ylim(ymin=0, ymax=100) plt.xlim(xmin=10, xmax=1e6) - plt.xscale('log') + plt.xscale('log', base=2) plt.xlabel('Size (log)') plt.ylabel('CDF (%)') plt.savefig('%s_%d_cdf.png' % (output_prefix, pid), bbox_inches='tight', dpi=300) + plt.close() def _CreateArgumentParser():
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index 964625e..b39941f 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -15873,6 +15873,14 @@ </description> </action> +<action name="MobileTabGridSelectionSelectAll"> + <owner>mrefaat@chromium.org</owner> + <owner>michaeldo@chromium.org</owner> + <description> + User in the iOS tab grid used the Select All button to select all items. + </description> +</action> + <action name="MobileTabGridSelectRegularPanel"> <owner>edchin@chromium.org</owner> <owner>marq@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index c7f21e9c..e911782 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -1119,6 +1119,19 @@ <int value="8" label="With signed in, no match"/> </enum> +<enum name="AccuracyTipInteraction"> + <int value="0" label="kNoAction"/> + <int value="1" label="kLostFocus"/> + <int value="2" label="kClosed"/> + <int value="3" label="kIgnorePressed"/> + <int value="4" label="kLearnMorePressed"/> +</enum> + +<enum name="AccuracyTipStatus"> + <int value="0" label="kNone"/> + <int value="1" label="kMisinformation"/> +</enum> + <enum name="ActionAfterDoubleTap"> <int value="0" label="Navigated Back"/> <int value="1" label="Stopped Navigation"/> @@ -19592,6 +19605,30 @@ </int> </enum> +<enum name="DnsProxy.ProcessEvent"> + <int value="0" label="Proxy subprocess launch succeeded"/> + <int value="1" label="Proxy subprocess launch failed"/> + <int value="2" label="Attempt to kill proxy subprocess failed"/> + <int value="3" label="Proxy subprocess crash detected"/> + <int value="4" label="Proxy subprocess stop detected"/> + <int value="5" label="Proxy subprocess continuation detected"/> + <int value="6" label="Untracked proxy subprocess crash detected"/> + <int value="7" label="Failed to preserve process capability"/> + <int value="8" label="Patchpanel DBus client not initialized"/> + <int value="9" label="Patchpanel DBus service not available"/> + <int value="10" label="Patchpanel DBus service reset detected"/> + <int value="11" label="Patchpanel DBus service shutdown detected"/> + <int value="12" label="Failed to acquire private connected namespace"/> + <int value="13" label="Failed to start DNS redirection"/> + <int value="14" label="Shill DBus service not available"/> + <int value="15" label="Shill DBus service reset detected"/> + <int value="16" label="Shill DBus service shutdown detected"/> + <int value="17" label="Failed to send system proxy IP address to shill"/> + <int value="18" label="Chrome Features DBus service not available"/> + <int value="19" label="Resolver failed to listen on UDP"/> + <int value="20" label="Resolver failed to listen on TCP"/> +</enum> + <enum name="DockedAction"> <int value="0" label="None"/> <int value="1" label="Dock"/>
diff --git a/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml b/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml index 515b658..002b202 100644 --- a/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml
@@ -1032,6 +1032,18 @@ </summary> </histogram> +<histogram name="Accessibility.Performance.HandleAXEvents" units="ms" + expires_after="M95"> + <owner>dmazzoni@chromium.org</owner> + <owner>delphick@chromium.org</owner> + <owner>chrome-a11y-core@google.com</owner> + <summary> + Tracks time spent on the browser main thread handling accessibility events + sent from the renderer process. Logged each time the method is run without + returning early. + </summary> +</histogram> + <histogram name="Accessibility.Performance.ProcessDeferredAccessibilityEvents" units="ms" expires_after="M93"> <owner>dmazzoni@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/network/histograms.xml b/tools/metrics/histograms/histograms_xml/network/histograms.xml index 323f405..6f9c47d4 100644 --- a/tools/metrics/histograms/histograms_xml/network/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/network/histograms.xml
@@ -571,6 +571,24 @@ </token> </histogram> +<histogram name="Network.DnsProxy.{ProcessType}.Event" + enum="DnsProxy.ProcessEvent" expires_after="2021-12-01"> + <owner>garrick@chromium.org</owner> + <owner>cros-network-metrics@google.com</owner> + <summary> + Diagnostic metric for Chrome OS DNS proxy tracking critical process events, + which are emitted when conditions arise that cause fatal or disruptive + conditions to the service. This metric is used to monitor the stability of + the service and provide detailed insight into failures. + </summary> + <token key="ProcessType"> + <variant name="ARCProxy"/> + <variant name="Controller"/> + <variant name="DefaultProxy"/> + <variant name="SystemProxy"/> + </token> +</histogram> + <histogram name="Network.Shill.Cellular.3GPPRegistrationDelayedDrop" enum="NetworkCellular3GPPRegistrationDelayedDrop" expires_after="2021-12-01">
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml index a5dee8e8..6b8649c 100644 --- a/tools/metrics/histograms/histograms_xml/others/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -12360,6 +12360,33 @@ </summary> </histogram> +<histogram name="Privacy.AccuracyTip.AccuracyTipInteraction" + enum="AccuracyTipInteraction" expires_after="M96"> + <owner>dullweber@chromium.org</owner> + <owner>eokoyomon@chromium.org</owner> + <summary> + The interaction performed on the accuracy tip UI. Recorded when UI is + closed. + </summary> +</histogram> + +<histogram name="Privacy.AccuracyTip.AccuracyTipTimeOpen" units="ms" + expires_after="M96"> + <owner>dullweber@chromium.org</owner> + <owner>eokoyomon@chromium.org</owner> + <summary> + The amount of time the accuracy tip UI was shown. Recorded when UI is + closed. + </summary> +</histogram> + +<histogram name="Privacy.AccuracyTip.PageStatus" enum="AccuracyTipStatus" + expires_after="M96"> + <owner>dullweber@chromium.org</owner> + <owner>eokoyomon@chromium.org</owner> + <summary>The accuracy status of a page. Recorded on each page load.</summary> +</histogram> + <histogram name="Privacy.ConsentAuditor.ConsentGiven.Feature" enum="ConsentAuditorFeature" expires_after="M85"> <owner>markusheintz@google.com</owner> @@ -18597,6 +18624,17 @@ </summary> </histogram> +<histogram name="Webapp.SystemApps.BadNavigate.Type" units="App ID" + expires_after="2021-09-01"> + <owner>calamity@chromium.org</owner> + <owner>qjw@chromium.org</owner> + <summary> + Records the type of System Web App that caused a bad navigation. This is + used to trigger a Perfetto trace to provide more debugging information. See + crbug.com/1201820 for more details. + </summary> +</histogram> + <histogram name="Webapp.SystemApps.FreshInstallDuration" units="ms" expires_after="2021-11-14"> <owner>calamity@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/password/histograms.xml b/tools/metrics/histograms/histograms_xml/password/histograms.xml index bb8368f3..0b4050f4 100644 --- a/tools/metrics/histograms/histograms_xml/password/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/password/histograms.xml
@@ -1391,7 +1391,7 @@ </histogram> <histogram name="PasswordManager.iOS.InfoBar.PasswordSave" enum="Boolean" - expires_after="2021-07-01"> + expires_after="2022-06-01"> <owner>djean@chromium.org</owner> <owner>sczs@google.com</owner> <summary> @@ -1400,7 +1400,7 @@ </histogram> <histogram name="PasswordManager.iOS.InfoBar.PasswordUpdate" enum="Boolean" - expires_after="2021-09-05"> + expires_after="2022-06-01"> <owner>djean@chromium.org</owner> <owner>sczs@google.com</owner> <summary>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml index afcbf39..5f60c62 100644 --- a/tools/metrics/ukm/ukm.xml +++ b/tools/metrics/ukm/ukm.xml
@@ -7400,6 +7400,19 @@ </metric> </event> +<event name="InputMethod.Assistive.Match"> + <owner>jiwan@chromium.org</owner> + <summary> + Recorded when an assistive action could be triggered according to the + surrounding text. + </summary> + <metric name="Type" enum="IMEAssistiveAction"> + <summary> + The type of the assistive suggestion which is triggered. + </summary> + </metric> +</event> + <event name="InputMethod.NonCompliantApi"> <owner>shend@chromium.org</owner> <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 8ee5cde..828c0bee 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -9,8 +9,8 @@ "remote_path": "perfetto_binaries/trace_processor_shell/mac/bae8193de6c017394901163b7817157342914679/trace_processor_shell" }, "linux": { - "hash": "7cc4323bf742e9f853f3687d1e37ce2f62b7c9d0", - "remote_path": "perfetto_binaries/trace_processor_shell/linux/2d7bf1e435f95bbe0b2f7973c5ec978bd72ec41b/trace_processor_shell" + "hash": "a5cb01af2dfe7c3a0fb51427d187501776f672c3", + "remote_path": "perfetto_binaries/trace_processor_shell/linux/5465d9e0184a6fe3e5660cf2b123386569f7d379/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/ui/base/ime/chromeos/input_method_ukm.cc b/ui/base/ime/chromeos/input_method_ukm.cc index 2ff9a22..0960915a 100644 --- a/ui/base/ime/chromeos/input_method_ukm.cc +++ b/ui/base/ime/chromeos/input_method_ukm.cc
@@ -18,4 +18,12 @@ .Record(ukm::UkmRecorder::Get()); } +void RecordUkmAssistiveMatch(ukm::SourceId source, const int64_t type) { + if (source == ukm::kInvalidSourceId) + return; + + ukm::builders::InputMethod_Assistive_Match(source).SetType(type).Record( + ukm::UkmRecorder::Get()); +} + } // namespace ui
diff --git a/ui/base/ime/chromeos/input_method_ukm.h b/ui/base/ime/chromeos/input_method_ukm.h index b44d3033..e50a74a 100644 --- a/ui/base/ime/chromeos/input_method_ukm.h +++ b/ui/base/ime/chromeos/input_method_ukm.h
@@ -18,6 +18,12 @@ COMPONENT_EXPORT(UI_BASE_IME_CHROMEOS) void RecordUkmNonCompliantApi(ukm::SourceId source, int64_t operation); +// Records an event in UKM, under the InputMethod.Assistive.Match metric. +// Ignores invalid sources. +// `type` is a value in the chromeos.AssistiveType enum. +COMPONENT_EXPORT(UI_BASE_IME_CHROMEOS) +void RecordUkmAssistiveMatch(ukm::SourceId source, int64_t type); + } // namespace ui #endif // UI_BASE_IME_CHROMEOS_INPUT_METHOD_UKM_H_
diff --git a/ui/base/template_expressions.cc b/ui/base/template_expressions.cc index 48630ab1..ef85ac40 100644 --- a/ui/base/template_expressions.cc +++ b/ui/base/template_expressions.cc
@@ -213,13 +213,12 @@ namespace ui { void TemplateReplacementsFromDictionaryValue( - const base::DictionaryValue& dictionary, + const base::Value& dictionary, TemplateReplacements* replacements) { - for (base::DictionaryValue::Iterator it(dictionary); !it.IsAtEnd(); - it.Advance()) { - std::string str_value; - if (it.value().GetAsString(&str_value)) - (*replacements)[it.key()] = str_value; + for (const auto& pair : dictionary.DictItems()) { + const std::string* value = pair.second.GetIfString(); + if (value) + (*replacements)[pair.first] = pair.second.GetString(); } }
diff --git a/ui/base/template_expressions.h b/ui/base/template_expressions.h index 470d54b..e63e951 100644 --- a/ui/base/template_expressions.h +++ b/ui/base/template_expressions.h
@@ -15,7 +15,7 @@ #include "base/strings/string_piece.h" namespace base { -class DictionaryValue; +class Value; } namespace ui { @@ -28,7 +28,7 @@ // TODO(dschuyler): remove this function by using TemplateReplacements directly. COMPONENT_EXPORT(UI_BASE) void TemplateReplacementsFromDictionaryValue( - const base::DictionaryValue& dictionary, + const base::Value& dictionary, TemplateReplacements* replacements); // Replace $i18n*{foo} in the format string with the value for the foo key in
diff --git a/ui/base/webui/jstemplate_builder.cc b/ui/base/webui/jstemplate_builder.cc index db4026e..536fbcb9 100644 --- a/ui/base/webui/jstemplate_builder.cc +++ b/ui/base/webui/jstemplate_builder.cc
@@ -23,7 +23,7 @@ // Appends a script tag with a variable name |templateData| that has the JSON // assigned to it. -void AppendJsonHtml(const base::DictionaryValue* json, std::string* output) { +void AppendJsonHtml(const base::Value* json, std::string* output) { std::string javascript_string; AppendJsonJS(json, &javascript_string, /*from_js_module=*/false); @@ -72,7 +72,7 @@ // Appends the code that processes the JsTemplate with the JSON. You should // call AppendJsTemplateSourceHtml and AppendLoadTimeData before calling this. -void AppendJsTemplateProcessHtml(const base::DictionaryValue* json, +void AppendJsTemplateProcessHtml(const base::Value* json, const base::StringPiece& template_id, std::string* output) { std::string jstext; @@ -94,7 +94,7 @@ } // namespace std::string GetI18nTemplateHtml(const base::StringPiece& html_template, - const base::DictionaryValue* json) { + const base::Value* json) { ui::TemplateReplacements replacements; ui::TemplateReplacementsFromDictionaryValue(*json, &replacements); std::string output = @@ -107,7 +107,7 @@ } std::string GetTemplatesHtml(const base::StringPiece& html_template, - const base::DictionaryValue* json, + const base::Value* json, const base::StringPiece& template_id) { ui::TemplateReplacements replacements; ui::TemplateReplacementsFromDictionaryValue(*json, &replacements); @@ -120,7 +120,7 @@ return output; } -void AppendJsonJS(const base::DictionaryValue* json, +void AppendJsonJS(const base::Value* json, std::string* output, bool from_js_module) { // Convert the template data to a json string.
diff --git a/ui/base/webui/jstemplate_builder.h b/ui/base/webui/jstemplate_builder.h index 97a4a558..7d2613b 100644 --- a/ui/base/webui/jstemplate_builder.h +++ b/ui/base/webui/jstemplate_builder.h
@@ -19,7 +19,7 @@ #include "base/strings/string_piece.h" namespace base { -class DictionaryValue; +class Value; } namespace webui { @@ -29,19 +29,19 @@ // full page with support for i18n Templates. COMPONENT_EXPORT(UI_BASE) std::string GetI18nTemplateHtml(const base::StringPiece& html_template, - const base::DictionaryValue* json); + const base::Value* json); // A helper function that generates a string of HTML to be loaded. The // string includes the HTML and the javascript code necessary to generate the // full page with support for both i18n Templates and JsTemplates. COMPONENT_EXPORT(UI_BASE) std::string GetTemplatesHtml(const base::StringPiece& html_template, - const base::DictionaryValue* json, + const base::Value* json, const base::StringPiece& template_id); // Assigns the given json data into |loadTimeData|, without a <script> tag. COMPONENT_EXPORT(UI_BASE) -void AppendJsonJS(const base::DictionaryValue* json, +void AppendJsonJS(const base::Value* json, std::string* output, bool from_js_module);
diff --git a/ui/base/webui/web_ui_util.cc b/ui/base/webui/web_ui_util.cc index c6cd6de..2480fb2 100644 --- a/ui/base/webui/web_ui_util.cc +++ b/ui/base/webui/web_ui_util.cc
@@ -184,11 +184,12 @@ } void SetLoadTimeDataDefaults(const std::string& app_locale, - base::DictionaryValue* localized_strings) { - localized_strings->SetString("fontfamily", GetFontFamily()); - localized_strings->SetString("fontsize", GetFontSize()); - localized_strings->SetString("language", l10n_util::GetLanguage(app_locale)); - localized_strings->SetString("textdirection", GetTextDirection()); + base::Value* localized_strings) { + localized_strings->SetStringKey("fontfamily", GetFontFamily()); + localized_strings->SetStringKey("fontsize", GetFontSize()); + localized_strings->SetStringKey("language", + l10n_util::GetLanguage(app_locale)); + localized_strings->SetStringKey("textdirection", GetTextDirection()); } void SetLoadTimeDataDefaults(const std::string& app_locale,
diff --git a/ui/base/webui/web_ui_util.h b/ui/base/webui/web_ui_util.h index a0c1ca2..8ac209a 100644 --- a/ui/base/webui/web_ui_util.h +++ b/ui/base/webui/web_ui_util.h
@@ -67,7 +67,7 @@ // application locale (i.e. g_browser_process->GetApplicationLocale()). COMPONENT_EXPORT(UI_BASE) void SetLoadTimeDataDefaults(const std::string& app_locale, - base::DictionaryValue* localized_strings); + base::Value* localized_strings); COMPONENT_EXPORT(UI_BASE) void SetLoadTimeDataDefaults(const std::string& app_locale, ui::TemplateReplacements* replacements);
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp index c7c5efc..4004814 100644 --- a/ui/chromeos/file_manager_strings.grdp +++ b/ui/chromeos/file_manager_strings.grdp
@@ -377,137 +377,9 @@ An error occured during installation of your Linux application. </message> - <message name="IDS_FILE_BROWSER_GALLERY_NO_IMAGES" desc="In the Gallery, the message that there are no images in this directory."> - No images in this directory. - </message> - <message name="IDS_FILE_BROWSER_GALLERY_ITEMS_SELECTED" desc="In the Gallery, the message showing the selected item count."> - <ph name="COUNT">$1<ex>3</ex></ph> items selected - </message> - - <message name="IDS_FILE_BROWSER_GALLERY_THUMBNAIL" desc="In the Gallery, the title on the button that switches to the thumbnail view."> - Thumbnail view - </message> - <message name="IDS_FILE_BROWSER_GALLERY_SLIDE" desc="In the Gallery, the title on the button that switches to the slide view."> - Slide view - </message> - <message name="IDS_FILE_BROWSER_GALLERY_SLIDESHOW" desc="In the Gallery, the title on the button that starts the slide show."> - Slideshow - </message> - <message name="IDS_FILE_BROWSER_GALLERY_DELETE" desc="In the Gallery, the title on the button that deletes selected images."> - Delete - </message> - - <message name="IDS_FILE_BROWSER_GALLERY_EDIT" desc="In the Gallery, the title on the button shows/hides editing buttons."> - Edit - </message> - <message name="IDS_FILE_BROWSER_GALLERY_PRINT" desc="In the Gallery, the title on the button showing the print dialog for the current image or video."> - Print - </message> - <message name="IDS_FILE_BROWSER_GALLERY_SHARE" desc="In the Gallery, the title on the button that lets the user share a photo."> - Share - </message> - <message name="IDS_FILE_BROWSER_GALLERY_EXIT" desc="In the Gallery, the title on the button that exits edit mode."> - Exit - </message> - <message name="IDS_FILE_BROWSER_GALLERY_ENTER_WHEN_DONE" desc="In the Gallery, the text in a floating panel that prompts the user to press enter when they are done with the changes to a photo."> - Press Enter when done - </message> - <message name="IDS_FILE_BROWSER_GALLERY_AUTOFIX" desc="In the Gallery, the title on the button that applies the Auto-Fix filter to a photo."> - Auto-fix - </message> - <message name="IDS_FILE_BROWSER_GALLERY_FIXED" desc="In the Gallery, the text in a floating panel that notifies that the photo has been automatically fixed."> - Fixed - </message> - <message name="IDS_FILE_BROWSER_GALLERY_CROP" desc="In the Gallery, the title on the button that cuts out a rectangular fragment from a photo"> - Crop - </message> - <message name="IDS_FILE_BROWSER_GALLERY_ASPECT_RATIO_1_1" desc="Button label to fix aspect to 1:1 ratio when cropping."> - 1x1 - </message> - <message name="IDS_FILE_BROWSER_GALLERY_ASPECT_RATIO_6_4" desc="Button label to fix aspect to 6:4 ratio when cropping."> - 6x4 - </message> - <message name="IDS_FILE_BROWSER_GALLERY_ASPECT_RATIO_7_5" desc="Button label to fix aspect to 7:5 ratio when cropping."> - 7x5 - </message> - <message name="IDS_FILE_BROWSER_GALLERY_ASPECT_RATIO_16_9" desc="Button label to fix aspect to 16:9 ratio when cropping."> - 16x9 - </message> - <message name="IDS_FILE_BROWSER_GALLERY_RESIZE" desc="In the Gallery, the title on the button that resize a photo."> - Resize - </message> - <message name="IDS_FILE_BROWSER_GALLERY_FIXRATIO" desc="Togglable Button to fix the ratio of width/height input field when resizing."> - Fix ratio - </message> - <message name="IDS_FILE_BROWSER_GALLERY_HEIGHT" desc="Input field to set the new size for height."> - Height - </message> - <message name="IDS_FILE_BROWSER_GALLERY_INVALIDVALUE" desc="In the Gallery, the message informing that the input value is invalid."> - Input value is invalid. - </message> - <message name="IDS_FILE_BROWSER_GALLERY_WIDTH" desc="Input field to set the new size for width."> - Width - </message> - <message name="IDS_FILE_BROWSER_GALLERY_EXPOSURE" desc="In the Gallery, the title on the button that applies the Brightness/Contrast filter to a photo."> - Brightness - </message> - <message name="IDS_FILE_BROWSER_GALLERY_BRIGHTNESS" desc="In the Gallery, the text next to the Brightness slider."> - Brightness - </message> - <message name="IDS_FILE_BROWSER_GALLERY_CONTRAST" desc="In the Gallery, the text next to the Contrast slider."> - Contrast - </message> - <message name="IDS_FILE_BROWSER_GALLERY_ROTATE_LEFT" desc="In the Gallery, the title on the button that rotates the photo 90 degrees to the left (counterclockwise)."> - Left - </message> - <message name="IDS_FILE_BROWSER_GALLERY_ROTATE_RIGHT" desc="In the Gallery, the title on the button that rotates the photo 90 degrees to the right (clockwise)."> - Right - </message> - <message name="IDS_FILE_BROWSER_GALLERY_UNDO" desc="In the Gallery, the text on the button that reverts the most recent operation."> - Undo - </message> - <message name="IDS_FILE_BROWSER_GALLERY_REDO" desc="In the Gallery, the text on the button that performs again the operation that has been recently reverted."> - Redo - </message> - <message name="IDS_FILE_BROWSER_GALLERY_DONE" desc="In the Gallery, the text on the button that commit image edit."> - DONE - </message> - <message name="IDS_FILE_BROWSER_GALLERY_FILE_EXISTS" desc="In the Gallery, the message informing that file already exists when attempting to rename."> - File already exists - </message> - <message name="IDS_FILE_BROWSER_GALLERY_SAVED" meaning="saved to disk" desc="In the Gallery, the message informing that editing saved successfully."> - Saved - </message> - <message name="IDS_FILE_BROWSER_GALLERY_SAVE_FAILED" desc="In the Gallery, the message informing that it failed to save editing image."> - Failed to save image. - </message> - <message name="IDS_FILE_BROWSER_GALLERY_OVERWRITE_ORIGINAL" desc="In the Gallery, text on the button to overwrite original file along with the edited copy."> - Overwrite original - </message> - <message name="IDS_FILE_BROWSER_GALLERY_OVERWRITE_BUBBLE" desc="In the Gallery, text in the bubble informing user about saving and overwriting original file."> - Your edits are saved automatically.<ph name="BREAKS"><br><br><ex><br><br></ex></ph>To keep a copy of the original image, uncheck "Overwrite original" - </message> - <message name="IDS_FILE_BROWSER_GALLERY_OVERWRITE_BUBBLE_CLOSE" desc="In the Gallery, close button on the bubble notifying user of overwriting original file."> - Close - </message> - <message name="IDS_FILE_BROWSER_GALLERY_READONLY_AND_NON_WRITABLE_FORMAT_WARNING" desc="In the Gallery, message informing that all edits will be saved to Downloads directory since the directory is readonly and the file is non-writable format of Gallery."> - Cannot save to <ph name="FOLDER_NAME">$1<ex>readonly-dir</ex></ph>. All edits will be saved to <ph name="DOWNLOADS_FOLDER">$2<ex>Downloads/fallbackdir</ex></ph> in the Downloads folder. - </message> - <message name="IDS_FILE_BROWSER_GALLERY_READONLY_WARNING" desc="In the Gallery, message informing that directory is readonly, and edited files will be saved to Downloads directory."> - Cannot save to <ph name="FOLDER_NAME">$1<ex>readonly-dir</ex></ph>. Edited images will be saved in the Downloads folder. - </message> - <message name="IDS_FILE_BROWSER_GALLERY_NON_WRITABLE_FORMAT_WARNING" desc="In the Gallery, message informing that Gallery cannot overwrite to the original file and all edits will be saved to a copy."> - All edits will be saved to <ph name="DOWNLOADS_FOLDER">$1<ex>Downloads/fallbackdir</ex></ph>. - </message> - <message name="IDS_FILE_BROWSER_GALLERY_IMAGE_ERROR" desc="In the Gallery, message informing that the image failed to load."> - This file could not be displayed. - </message> <message name="IDS_FILE_BROWSER_AUDIO_ERROR" desc="In the Audio Player, message informing that the file could not be played."> This file could not be played. </message> - <message name="IDS_FILE_BROWSER_GALLERY_IMAGE_OFFLINE" desc="In the Gallery, message informing that the image could be displayed in the offline mode."> - This image is not available offline. - </message> <message name="IDS_FILE_BROWSER_AUDIO_OFFLINE" desc="In the Audio Player, message informing that the audio file could not be played in the offline mode."> This file is not available offline. </message>
diff --git a/ui/display/manager/json_converter_unittest.cc b/ui/display/manager/json_converter_unittest.cc index 559e836..8ddeba20 100644 --- a/ui/display/manager/json_converter_unittest.cc +++ b/ui/display/manager/json_converter_unittest.cc
@@ -28,7 +28,7 @@ layout.placement_list[1].position = DisplayPlacement::LEFT; layout.placement_list[1].offset = 30; - base::DictionaryValue value; + base::Value value(base::Value::Type::DICTIONARY); DisplayLayoutToJson(layout, &value); const char data[] =
diff --git a/ui/file_manager/BUILD.gn b/ui/file_manager/BUILD.gn index 6642c9e..9ff8bc7 100644 --- a/ui/file_manager/BUILD.gn +++ b/ui/file_manager/BUILD.gn
@@ -43,8 +43,6 @@ "file_manager/foreground/js:closure_compile", "file_manager/foreground/js/metadata:closure_compile", "file_manager/foreground/js/ui:closure_compile", - "gallery/js:closure_compile", - "gallery/js/image_editor:closure_compile", "image_loader:closure_compile", "integration_tests:closure_compile", "integration_tests/file_manager:closure_compile", @@ -61,9 +59,6 @@ "file_manager/foreground/js:js_test_gen_html_modules", "file_manager/foreground/js/metadata:js_test_gen_html_modules", "file_manager/foreground/js/ui:js_test_gen_html_modules", - "gallery/js:js_test_gen_html", - "gallery/js/image_editor:js_test_gen_html", - "gallery/js/image_editor:js_test_gen_html_modules", "image_loader:js_test_gen_html_modules", "video_player/js:js_test_gen_html_modules", ]
diff --git a/ui/file_manager/file_manager/background/js/background_common_scripts.js b/ui/file_manager/file_manager/background/js/background_common_scripts.js index 9de4133a..7729d56 100644 --- a/ui/file_manager/file_manager/background/js/background_common_scripts.js +++ b/ui/file_manager/file_manager/background/js/background_common_scripts.js
@@ -4,7 +4,7 @@ /** * @fileoverview Scripts which are commonly used as parts of background scripts - * in Files app, Gallery app, Video Player app, and Audio Player app. + * in Files app, Video Player app, and Audio Player app. * The purpose of this file is to share common files between Files app and its * companion apps to save the size. * Note that adding a script in this file results in being loaded in each app.
diff --git a/ui/file_manager/file_manager/background/js/test_util_base.js b/ui/file_manager/file_manager/background/js/test_util_base.js index ade3c93..1ee63237 100644 --- a/ui/file_manager/file_manager/background/js/test_util_base.js +++ b/ui/file_manager/file_manager/background/js/test_util_base.js
@@ -67,7 +67,6 @@ */ const kTestingExtensionIds = [ 'oobinhbdbiehknkpbpejbbpdbkdjmoco', // File Manager test extension. - 'ejhcmmdhhpdhhgmifplfmjobgegbibkn', // Gallery test extension. 'ljoplibgfehghmibaoaepfagnmbbfiga', // Video Player test extension. 'ddabbgbggambiildohfagdkliahiecfl', // Audio Player test extension. ];
diff --git a/ui/file_manager/file_manager/externs/background/background_base.js b/ui/file_manager/file_manager/externs/background/background_base.js index ef475be1..6fc0b47 100644 --- a/ui/file_manager/file_manager/externs/background/background_base.js +++ b/ui/file_manager/file_manager/externs/background/background_base.js
@@ -9,11 +9,11 @@ /** * Interface exposed in window.background in the background page. Used for - * Audio, Video and Gallery. + * Audio and Video. * * Files app uses a larger interface: `FileBrowserBackgroundFull`. * Interface exposed in window.background in the background page. Used for - * Audio, Video and Gallery. + * Audio and Video. * * Files app uses a larger interface: `FileBrowserBackgroundFull`. *
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js index bc803fd..b468b57 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -1200,8 +1200,8 @@ } const message = entries.length === 1 ? - strf('GALLERY_CONFIRM_DELETE_ONE', entries[0].name) : - strf('GALLERY_CONFIRM_DELETE_SOME', entries.length); + strf('CONFIRM_DELETE_ONE', entries[0].name) : + strf('CONFIRM_DELETE_SOME', entries.length); if (!dialog) { dialog = fileManager.ui.deleteConfirmDialog;
diff --git a/ui/file_manager/file_manager_resources.grd b/ui/file_manager/file_manager_resources.grd index e58629b..cb2ba9c 100644 --- a/ui/file_manager/file_manager_resources.grd +++ b/ui/file_manager/file_manager_resources.grd
@@ -67,14 +67,6 @@ <include name="IDR_FILE_MANAGER_ICON_128" file="file_manager/common/images/icon128.png" type="BINDATA" /> <include name="IDR_FILE_MANAGER_ICON_192" file="file_manager/common/images/icon192.png" type="BINDATA" /> <include name="IDR_FILE_MANAGER_ICON_256" file="file_manager/common/images/icon256.png" type="BINDATA" /> - <include name="IDR_FILE_MANAGER_GALLERY_ICON_16" file="gallery/images/icon16.png" type="BINDATA" /> - <include name="IDR_FILE_MANAGER_GALLERY_ICON_32" file="gallery/images/icon32.png" type="BINDATA" /> - <include name="IDR_FILE_MANAGER_GALLERY_ICON_48" file="gallery/images/icon48.png" type="BINDATA" /> - <include name="IDR_FILE_MANAGER_GALLERY_ICON_64" file="gallery/images/icon64.png" type="BINDATA" /> - <include name="IDR_FILE_MANAGER_GALLERY_ICON_96" file="gallery/images/icon96.png" type="BINDATA" /> - <include name="IDR_FILE_MANAGER_GALLERY_ICON_128" file="gallery/images/icon128.png" type="BINDATA" /> - <include name="IDR_FILE_MANAGER_GALLERY_ICON_192" file="gallery/images/icon192.png" type="BINDATA" /> - <include name="IDR_FILE_MANAGER_GALLERY_ICON_256" file="gallery/images/icon256.png" type="BINDATA" /> <!-- Resources used for non-flattened HTML files. --> <include name="IDR_FILE_MANAGER_DRIVE_WELCOME_STYLE" file="file_manager/foreground/css/drive_welcome.css" type="BINDATA" /> @@ -112,17 +104,6 @@ <include name="IDR_AUDIO_PLAYER_ICON_192" file="audio_player/icons/audio-player-192.png" type="BINDATA" /> <include name="IDR_AUDIO_PLAYER_ICON_256" file="audio_player/icons/audio-player-256.png" type="BINDATA" /> - <!-- The Gallery app pages and scripts. --> - <include name="IDR_GALLERY_MANIFEST" file="gallery/manifest.json" type="BINDATA" /> - <include name="IDR_GALLERY" file="gallery/gallery.html" allowexternalscript="true" flattenhtml="true" type="BINDATA" /> - <include name="IDR_GALLERY_JS" file="gallery/js/gallery_scripts.js" flattenhtml="true" type="BINDATA" /> - <include name="IDR_GALLERY_BACKGROUND_JS" file="gallery/js/background_scripts.js" flattenhtml="true" type="BINDATA" /> - <include name="IDR_GALLERY_METADATA_WORKER_JS" file="gallery/js/metadata_worker.js" type="BINDATA" /> - - <!-- Custom cursors (which grit cannot inline). --> - <include name="IDR_FILE_MANAGER_IMG_GALLERY_CURSOR_CROP" file="gallery/images/100/cursor_crop.png" type="BINDATA" /> - <include name="IDR_FILE_MANAGER_IMG_GALLERY_2X_CURSOR_CROP" file="gallery/images/200/cursor_crop.png" type="BINDATA" /> - <!-- Image loader extension manifest and scripts. --> <if expr="chromeos"> <include name="IDR_IMAGE_LOADER_MANIFEST" file="image_loader/manifest.json" type="BINDATA" />
diff --git a/ui/file_manager/gallery/OWNERS b/ui/file_manager/gallery/OWNERS deleted file mode 100644 index 13c6f49f..0000000 --- a/ui/file_manager/gallery/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -tapted@chromium.org
diff --git a/ui/file_manager/gallery/css/gallery.css b/ui/file_manager/gallery/css/gallery.css deleted file mode 100644 index 1e0e03c..0000000 --- a/ui/file_manager/gallery/css/gallery.css +++ /dev/null
@@ -1,1468 +0,0 @@ -/* Copyright (c) 2014 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -body { - background-color: black; - font-size: 84%; - margin: 0; - user-select: none; -} - -button { - cursor: pointer; -} - -button:focus { - outline: 1px solid rgb(77, 144, 254); -} - -/* TODO(fukino): This style for the bubble is old. Apply new design and remove - * these styles. */ -.bubble { - background: #FFF; - border-radius: 2px; - cursor: default; - outline: 1px solid rgba(0, 0, 0, 0.2); - padding: 16px; -} - -.bubble .pointer { - background: -webkit-image-set( - url(../../file_manager/foreground/images/common/bubble_point_white.png) 1x, - url(../../file_manager/foreground/images/common/2x/bubble_point_white.png) 2x); - display: block; - height: 11px; - left: 24px; - margin: 0 0 0 -5px; - outline: none; - position: absolute; - width: 17px; -} - -html[dir='rtl'] .bubble .pointer { - left: auto; - margin: 0 -5px 0 0; - right: 24px; -} - -.bubble .pointer:not(.bottom) { - top: -11px; -} - -.bubble .pointer.bottom { - bottom: -11px; - transform: rotate(180deg); -} - -.bubble .close-x { - background: -webkit-image-set( - url(../../file_manager/foreground/images/common/close_x_gray.png) 1x, - url(../../file_manager/foreground/images/common/2x/close_x_gray.png) 2x); - height: 21px; - opacity: 0.3; - position: absolute; - right: 3px; - top: 3px; - width: 21px; -} - -html[dir='rtl'] .bubble .close-x { - left: 3px; - right: auto; -} - -.bubble .close-x:hover { - opacity: 0.7; -} - -paper-ripple { - color: black; -} - -.gallery, -.gallery > .content { - bottom: 0; - left: 0; - overflow: hidden; - position: absolute; - right: 0; - top: 0; -} - -/* Common background for both mosaic and slide mode. */ -.gallery > .content { - background-color: black; -} - -/* Image container and canvas elements */ - -.gallery .image-container { - cursor: none; /* Only visible when the toolbar is active */ - height: 100%; - position: absolute; - width: 100%; -} - -.gallery .video-container { - /* - * Videos don't use transforms (so the controls do not morph). Use flex to - * center within the container. Note that this makes transitions janky, but - * transitions with video controls look silly, so they're disabled. - */ - align-items: center; - display: flex; - justify-content: center; -} - -.gallery[tools] .image-container[cursor='default'] { - cursor: default; -} - -.gallery[tools] .image-container[cursor='move'] { - cursor: move; -} - -.gallery[tools] .image-container[cursor='crop'] { - cursor: -webkit-image-set( - url(../images/100/cursor_crop.png) 1x, - url(../images/200/cursor_crop.png) 2x) 15 15, auto; -} - -/** - * If large cursor or high contrast is enabled, falls back to default cursor - * since they don't support css custom cursor. - */ -.gallery[tools][large-cursor] .image-container[cursor='crop'], -.gallery[tools][high-contrast] .image-container[cursor='crop'] { - cursor: default; -} - -.gallery[tools] .image-container[cursor='n-resize'], -.gallery[tools] .image-container[cursor='s-resize'] { - cursor: ns-resize; -} - -.gallery[tools] .image-container[cursor='e-resize'], -.gallery[tools] .image-container[cursor='w-resize'] { - cursor: ew-resize; -} - -.gallery[tools] .image-container[cursor='nw-resize'], -.gallery[tools] .image-container[cursor='se-resize'] { - cursor: nwse-resize; -} - -.gallery[tools] .image-container[cursor='ne-resize'], -.gallery[tools] .image-container[cursor='sw-resize'] { - cursor: nesw-resize; -} - -.gallery .image-container > .image { - left: 0; - pointer-events: none; - position: absolute; - /* Duration and timing function are set in Javascript. */ - transition-property: transform, opacity; -} - -.gallery .image-container > .image[fade] { - opacity: 0; -} - -.gallery .video-container > .video { - max-height: 100%; - max-width: 100%; - - /* - * Since r614513, <video> elements take focus on click which causes yellow - * lines to appear unless the outline is disabled. See crbug/917503. - */ - outline: none; -} - -/* - * Move the video element away from the top and bottom edges when the ribbon is - * visible so that it doesn't overlap the scrubber when a video is sufficiently - * tall. - */ -.gallery[tools] .video-container > .video { - box-sizing: border-box; - padding: 48px 0; -} - -@media print { - /* Do not print anything but the image content. */ - .gallery > :not(.content) { - display: none !important; - } - - /* Center the printed image. */ - .gallery .image-container { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - display: -webkit-box; - } - - /* Print the full resolution image instead. */ - .gallery .image-container > .image { - height: auto !important; - max-height: 100%; - max-width: 100%; - opacity: 1 !important; - position: static !important; - transform: none !important; - visibility: visible !important; - width: auto !important; - } -} - -/* Toolbar */ - -.gallery > .toolbar { - -webkit-box-align: stretch; - -webkit-box-orient: horizontal; - -webkit-box-pack: start; - background-color: rgb(40, 42, 45); - display: flex; - height: 48px; - left: 0; - opacity: 0; - overflow: hidden; - padding: 0; - pointer-events: none; - position: absolute; - right: 0; - transition: opacity 300ms ease; -} - -.gallery > .toolbar.top { - top: 0; -} - -.gallery > .toolbar.bottom { - bottom: 0; - height: 48px; - overflow: visible; /* For overwrite original bubble. */ -} - -.gallery > .toolbar.bottom > .slide-mode-toolbar { - left: 0; - opacity: 1; - position: absolute; - visibility: visible; - width: 100%; -} - -.gallery > .toolbar.bottom > .edit-mode-toolbar { - left: 0; - opacity: 0; - position: absolute; - visibility: hidden; - width: 100%; -} - -.gallery[editing] > .toolbar.bottom > .slide-mode-toolbar { - opacity: 0; - visibility: hidden; -} - -.gallery[editing] > .toolbar.bottom > .edit-mode-toolbar { - opacity: 1; - visibility: visible; -} - -.gallery[tools]:not([slideshow]) > .toolbar { - opacity: 0.9; - pointer-events: auto; -} - -.gallery:not([tools]) > files-tooltip { - opacity: 0; -} - -/* Hide immediately when entering the slideshow. */ -.gallery[tools][slideshow] > .toolbar { - transition-duration: 0ms; -} - -.gallery[tools][slideshow] > files-tooltip { - transition-duration: 0ms; -} - -.gallery[tools][locked] > .toolbar { - pointer-events: none; -} - -.gallery .arrow-box { - pointer-events: none; -} - -.gallery .arrow-box .arrow { - align-items: center; - display: flex; - height: 100%; - position: absolute; - z-index: 100; -} - -.gallery .arrow-box .arrow.left { - left: 16px; -} - -.gallery .arrow-box .arrow.right { - right: 16px; -} - -/* The arrow icons are in nested divs so that their opacity can be manipulated - * independently from their parent (which can be dimmed when the crop frame - * overlaps it) */ -.gallery .arrow div { - cursor: pointer; - height: 48px; - opacity: 0; - width: 48px; -} - -.gallery[tools]:not([editing]) .arrow-box[active] .arrow div { - opacity: 0.4; - pointer-events: auto; -} - -.gallery[tools]:not([editing]) .arrow-box[active] .arrow div:hover { - opacity: 1; -} - -.gallery .arrow.left div { - background-image: -webkit-image-set( - url(../images/100/slideshow_previous.png) 1x, - url(../images/200/slideshow_previous.png) 2x); -} - -.gallery .arrow.right div { - background-image: -webkit-image-set( - url(../images/100/slideshow_next.png) 1x, - url(../images/200/slideshow_next.png) 2x); -} - -/* Special behavior on mouse drag. - Redundant .gallery attributes included to make the rules more specific */ - -/* Everything but the image container should become mouse-transparent */ -.gallery[tools][editing][mousedrag] * { - pointer-events: none; -} - -.gallery[tools][editing][mousedrag] .image-container { - pointer-events: auto; -} - -/* The editor marks elements with 'dimmed' attribute to get them out of the way - of the crop frame */ -.gallery[tools][editing] [dimmed], -.gallery[tools][editing] [dimmed] * { - pointer-events: none; -} - -.gallery[tools][editing] [dimmed] { - opacity: 0.2; -} - -/* Filename */ - -.filename-spacer { - flex: 1 0 auto; - margin-inline-start: 16px; -} - -/* Bubble */ - -.gallery .toolbar .bubble { - bottom: 65px; - font-size: 85%; - left: 20px; - position: absolute; - width: 220px; -} - -html[dir='rtl'] .gallery .toolbar .bubble { - left: auto; - right: 20px; -} - -.gallery:not([editing]) .toolbar .bubble { - display: none; -} - -/* Middle spacer */ - -.gallery .middle-spacer { - align-items: center; - display: flex; - flex: 1 0 auto; - flex-direction: column; -} - -/* Toolbar buttons */ - -.gallery .button-spacer { - align-items: center; - display: flex; - flex: none; - justify-content: flex-end; -} - -/* Thumbnails */ - -.gallery .ribbon-spacer { - height: 48px; - position: relative; -} - -.gallery .toolbar .ribbon { - display: flex; - justify-content: center; - overflow: hidden; - padding: 4px 0; - position: absolute; - top: 0; - transition: opacity 180ms linear, visibility 0ms linear; - white-space: nowrap; - width: 100%; - z-index: 0; -} - -.gallery .toolbar .ribbon:focus { - outline: none; -} - -.gallery[editing] .toolbar .ribbon { - opacity: 0; - transition-delay: 0ms, 180ms; - visibility: hidden; -} - -.gallery .ribbon-image { - background-color: black; - cursor: pointer; - display: block; - flex: none; - height: 40px; - margin: 0 2px; - overflow: hidden; - position: relative; - transition: all 180ms linear; - width: 71px; -} - -.ribbon-image[vanishing='smooth'] { - border-left-width: 0; - border-right-width: 0; - margin-left: 0; - margin-right: 0; - width: 0; -} - -.gallery .toolbar .ribbon.fade-left { - justify-content: flex-end; -} - -.gallery .toolbar .ribbon.fade-right { - justify-content: flex-start; -} - -.gallery .image-wrapper { - height: 100%; - overflow: hidden; - position: absolute; - width: 100%; -} - -.gallery .image-wrapper > img { - position: absolute; -} - -.gallery .image-wrapper > img:not(.cached) { - animation: fadeIn 500ms ease-in; -} - -@keyframes fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -.gallery .ribbon-image > .indicator { - background-image: -webkit-image-set( - url(../images/100/slide_view.png) 1x, - url(../images/200/slide_view.png) 2x); - background-position: center; - background-repeat: no-repeat; - height: 100%; - opacity: 0; - position: absolute; - transition: opacity 500ms ease-in; - width: 100%; -} - -.gallery .ribbon-image > .indicator.loading { - opacity: 0.2; -} - -.gallery .ribbon-image[selected] > .selection-frame { - border: 2px solid rgb(27, 168, 243); - box-sizing: border-box; - height: 100%; - position: absolute; - top: 0; - width: 100%; -} - -/* Editor toolbar. */ - -.gallery .edit-mode-toolbar .options, -.gallery .edit-mode-toolbar .exit-button-spacer { - align-items: center; - display: flex; - height: 100%; - opacity: 0; - pointer-events: none; - position: absolute; - top: 0; - visibility: hidden; - width: 100%; -} - -.gallery .edit-mode-toolbar .options > :not([disabled]), -.gallery .edit-mode-toolbar .exit-button-spacer > :not([disabled]) { - pointer-events: auto; -} - -.gallery .edit-mode-toolbar .exit-button-spacer { - justify-content: flex-end; -} - -.gallery .edit-mode-toolbar .exit-button-spacer cr-button { - color: white; - font-weight: bold; - margin-inline-end: 8px; - text-transform: uppercase; -} - -.gallery .edit-mode-toolbar .edit-bar-spacer { - display: block; - height: 100%; - opacity: 0; - transition: opacity 180ms linear, visibility 0ms linear 180ms; - visibility: hidden; -} - -.gallery[editing] .edit-mode-toolbar .options, -.gallery[editing] .edit-mode-toolbar .exit-button-spacer { - opacity: 1; - visibility: visible; -} - -.edit-mode-toolbar .overwrite-original { - margin-inline-start: 16px; -} - -.edit-mode-toolbar .overwrite-original[disabled] { - opacity: 0.2; -} - -.gallery .edit-mode-toolbar .saved { - cursor: inherit; - margin-inline-start: 14px; - opacity: 0; - pointer-events: none; - transition-duration: 120ms; - transition-property: opacity, transform; - transition-timing-function: linear; -} - -.gallery[editing] .edit-mode-toolbar .saved { - color: white; - opacity: 0.5; -} - -.gallery .edit-mode-toolbar .saved[highlighted] { - opacity: 1; - transform: scaleX(1.1) scaleY(1.1) rotate(0); -} - -/* Editor buttons. */ - -.gallery .toolbar .edit-main { - align-items: center; - color: white; - display: flex; - height: 48px; - justify-content: center; - overflow: visible; -} - -.gallery .toolbar .edit-main .container { - display: flex; -} - -.gallery[editing] .edit-bar-spacer { - opacity: 1.0; - pointer-events: auto; - transition-delay: 100ms, 100ms; - visibility: visible; -} - -.gallery > .toolbar cr-button, -.gallery > .toolbar button { - border-radius: 2px; - margin: 0 8px; -} - -.gallery > .toolbar .icon-button { - background-position: center; - background-repeat: no-repeat; - height: 32px; - min-width: 32px; - width: 32px; -} - -.gallery > .toolbar button { - background-color: transparent; - border: 0; - padding: 0; - position: relative; -} - -.gallery > .toolbar cr-button:focus, -.gallery > .toolbar button:focus { - background-color: rgba(255, 255, 255, 0.2); -} - -.gallery > .toolbar button:focus { - outline: none; -} - -.gallery > .toolbar button.using-mouse:focus { - background-color: transparent; -} - -.gallery > .toolbar button > .icon { - background-position: center; - background-repeat: no-repeat; - height: 100%; - position: relative; - width: 100%; - z-index: 2; -} - -.gallery > .toolbar button > files-toggle-ripple { - position: absolute; - top: 0; - z-index: 1; -} - -.gallery > .toolbar .icon-button > files-toggle-ripple { - height: 28px; - left: 2px; - top: 2px; - width: 28px; -} - -.gallery > .toolbar button.edit > .icon { - background-image: -webkit-image-set( - url(../images/100/edit.png) 1x, - url(../images/200/edit.png) 2x); -} - -.gallery > .toolbar button.print > .icon { - background-image: -webkit-image-set( - url(../images/100/print.png) 1x, - url(../images/200/print.png) 2x); -} - -.gallery > .toolbar button.delete > .icon { - background-image: -webkit-image-set( - url(../images/100/delete.png) 1x, - url(../images/200/delete.png) 2x); -} - -.gallery > .toolbar .icon.slide-mode { - background-image: -webkit-image-set( - url(../images/100/slide_view.png) 1x, - url(../images/200/slide_view.png) 2x); -} - -.gallery > .toolbar .icon.thumbnail-mode { - background-image: -webkit-image-set( - url(../images/100/mosaic_view.png) 1x, - url(../images/200/mosaic_view.png) 2x); -} - -.gallery > .toolbar button.slideshow > .icon { - background-image: -webkit-image-set( - url(../images/100/slideshow.png) 1x, - url(../images/200/slideshow.png) 2x); -} - -.gallery > .toolbar button.share > .icon { - background-image: -webkit-image-set( - url(../images/100/share.png) 1x, - url(../images/200/share.png) 2x); -} - -.gallery[mode='slide'] .icon.slide-mode, -.gallery[mode='thumbnail'] .icon.thumbnail-mode, -cr-button[disabled], -button[disabled] { - display: none; -} - -/* Button in edit toolbar. */ - -.toolbar button.edit-toolbar { - height: 28px; - margin: 0 10px; - min-width: 28px; /* Reset. */ - outline: none; - width: 28px; -} - -.toolbar button.edit-toolbar[disabled], -.gallery[tools][locked] > button.edit-toolbar { - display: block; - opacity: 0.5; - pointer-events: none; -} - -.toolbar button.edit-toolbar[hidden] { - display: none; -} - -/* Do not show focus state if it's made by mouse since it makes difficult to see - * the toggle animation. */ -.toolbar button.edit-toolbar:focus.using-mouse { - background-color: transparent; -} - -/* Edit main toolbar. */ -.gallery > .toolbar .edit-main button { - border-radius: 1px; -} - -.edit-main button.autofix .icon { - background-image: -webkit-image-set( - url(../images/100/auto_fix.png) 1x, - url(../images/200/auto_fix.png) 2x); -} - -.edit-main button.crop .icon { - background-image: -webkit-image-set( - url(../images/100/crop.png) 1x, - url(../images/200/crop.png) 2x); -} - -.edit-main button.resize .icon { - background-image: -webkit-image-set( - url(../images/100/resize.png) 1x, - url(../images/200/resize.png) 2x); -} - -.edit-main button.exposure .icon { - background-image: -webkit-image-set( - url(../images/100/brightness.png) 1x, - url(../images/200/brightness.png) 2x); -} - -.edit-main button.rotate_right .icon { - background-image: -webkit-image-set( - url(../images/100/rotate_right.png) 1x, - url(../images/200/rotate_right.png) 2x); -} - -.edit-main button.rotate_left .icon { - background-image: -webkit-image-set( - url(../images/100/rotate_left.png) 1x, - url(../images/200/rotate_left.png) 2x); -} - -.edit-main button.undo .icon { - background-image: -webkit-image-set( - url(../images/100/undo.png) 1x, - url(../images/200/undo.png) 2x); -} - -.edit-main button.redo .icon { - background-image: -webkit-image-set( - url(../images/100/redo.png) 1x, - url(../images/200/redo.png) 2x); -} - -/* Edit modal. */ -.edit-modal { - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - bottom: 48px; - display: block; - height: 48px; - pointer-events: none; - position: absolute; - width: 100%; -} - -.gallery[editor-mode='resize'] .edit-modal { - height: 56px; -} - -.edit-modal-wrapper[hidden] { - display: none; -} - -.edit-modal-wrapper { - background-color: rgb(20, 22, 25); - color: white; - height: 100%; - opacity: 0.9; - pointer-events: auto; - position: relative; -} - -.edit-modal-wrapper .container, -.edit-modal-wrapper .action-buttons { - align-items: center; - display: flex; - height: 100%; - width: 100%; -} - -.edit-modal-wrapper .container { - justify-content: center; - left: 0; - position: absolute; - top: 0; -} - -.edit-modal-wrapper .action-buttons { - justify-content: flex-end; -} - -.edit-modal button.edit-toolbar { - background: transparent; - border: 0 none; - border-image: none; - border-radius: 2px; - box-sizing: border-box; - color: white; - min-width: 0; - padding: 0; - position: relative; -} - -.edit-modal button.edit-toolbar:focus { - background-color: rgba(255, 255, 255, 0.12); -} - -.edit-modal button.edit-toolbar .icon { - display: none; -} - -.edit-modal button.edit-toolbar .label { - display: block; -} - -/* Action buttons */ -.edit-modal-wrapper .action-buttons button { - align-items: center; - display: flex; - height: 32px; - justify-content: center; - margin-inline-end: 8px; - outline: none; - width: 70px; -} - -.edit-modal-wrapper .action-buttons button .label { - font-weight: bold; -} - -/* Crop aspect ratio buttons */ -.edit-modal button.crop-aspect-ratio { - border: solid 1px white; - border-radius: 2px; - height: 18px; - margin: 0 12px; - outline: none; - position: relative; - transition: background 200ms ease; - width: 36px; -} - -.edit-modal button.crop-aspect-ratio.selected { - background: white; -} - -.edit-modal button.crop-aspect-ratio:focus:not(.using-mouse) { - outline: 1px solid rgb(77, 144, 254); -} - -.edit-modal button.crop-aspect-ratio .label { - font-size: 12px; - height: 12px; - pointer-events: none; -} - -.edit-modal button.crop-aspect-ratio.selected .label { - color: rgb(20, 22, 25); -} - -.edit-modal button.crop-aspect-ratio paper-ripple { - display: none; -} - -/* Range UI */ -.edit-modal .range { - align-items: center; - display: flex; -} - -.edit-modal .range:not(:last-child) { - margin-inline-end: 16px; -} - -.edit-modal .range > .icon { - display: block; - height: 16px; - margin-inline-end: 8px; - width: 16px; -} - -cr-slider { - width: 172px; -} - -.edit-modal .range.brightness > .icon { - background-image: -webkit-image-set( - url(../images/100/brightness.png) 1x, - url(../images/200/brightness.png) 2x); -} - -.edit-modal .range.contrast > .icon { - background-image: -webkit-image-set( - url(../images/100/contrast.png) 1x, - url(../images/200/contrast.png) 2x); -} - -/* Crop frame */ - -.gallery .crop-overlay { - -webkit-box-orient: vertical; - display: -webkit-box; - height: 100%; - pointer-events: none; - position: absolute; - width: 100%; -} - -.gallery .crop-overlay .shadow { - background-color: rgba(0, 0, 0, 0.65); -} - -.gallery .crop-overlay .middle-box { - -webkit-box-flex: 1; - -webkit-box-orient: horizontal; - display: -webkit-box; -} - -.gallery .crop-frame { - -webkit-box-flex: 1; - display: -webkit-box; - position: relative; -} - -.gallery .crop-frame div { - background-color: rgba(255, 255, 255, 1); - box-shadow: 0 0 3px rgba(0, 0, 0, 0.75); - position: absolute; -} - -.gallery .crop-frame .horizontal { - height: 1px; - left: 7px; - right: 7px; -} - -.gallery .crop-frame .horizontal.top { - top: 0; -} - -.gallery .crop-frame .horizontal.bottom { - bottom: 0; -} - -.gallery .crop-frame .vertical { - bottom: 7px; - top: 7px; - width: 1px; -} - -.gallery .crop-frame .vertical.left { - left: 0; -} - -.gallery .crop-frame .vertical.right { - right: 0; -} - -.gallery .crop-frame .corner { - border-radius: 6px; - height: 13px; - width: 13px; -} - -.gallery .crop-frame .corner.left { - left: -6px; -} - -.gallery .crop-frame .corner.right { - right: -6px; -} - -.gallery .crop-frame .corner.top { - top: -6px; -} - -.gallery .crop-frame .corner.bottom { - bottom: -6px; -} - -/* Input UI */ -.edit-modal .input { - align-items: flex-end; - display: flex; - width: 90px; -} - -.edit-modal .input > .unit_label { - color: rgba(255, 255, 255, 0.54); - margin-bottom: 2px; -} - -.edit-modal .lockicon { - background-repeat: no-repeat; - background-size: contain; - height: 16px; - margin: 16px; - margin-top: auto; - width: 16px; -} - -.edit-modal .lockicon:focus { - outline: none; -} - -.edit-modal .lockicon[locked] { - background-image: -webkit-image-set( - url(../images/100/ratio_locked.png) 1x, - url(../images/200/ratio_locked.png) 2x); -} - -.edit-modal .lockicon:not([locked]) { - background-image: -webkit-image-set( - url(../images/100/ratio_unlocked.png) 1x, - url(../images/200/ratio_unlocked.png) 2x); - opacity: 0.54; -} - -/* Prompt/notification panel */ - -.gallery .prompt-wrapper { - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - display: -webkit-box; - height: 100%; - pointer-events: none; - position: absolute; - width: 100%; -} - -.gallery .prompt-wrapper[pos=top] { - -webkit-box-align: start; -} - -.gallery .prompt-wrapper[pos=center] { - -webkit-box-align: center; -} - -.gallery .prompt-wrapper[pos=center] .back-button { - display: none; -} - -.dimmable { - opacity: 1; - transition: opacity 220ms ease; -} - -.gallery .prompt { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - background-color: rgba(0, 0, 0, 0.8); - color: white; - display: -webkit-box; - font-size: 120%; - height: 40px; - opacity: 0; - padding: 0 20px; - position: relative; - top: 5px; - transition: all 180ms ease; -} - -.gallery .prompt[state='fadein'] { - opacity: 1; - top: 0; -} - -.gallery .prompt[state='fadeout'] { - opacity: 0; - top: 0; -} - -.gallery .prompt-wrapper[pos=top] .prompt { - padding-right: 10px; -} - -.gallery .share-menu { - -webkit-box-align: stretch; - -webkit-box-orient: vertical; - -webkit-box-pack: start; - background-color: white; - border: 1px solid #7f7f7f; - border-radius: 1px; - bottom: 60px; - display: -webkit-box; - opacity: 1.0; - padding: 8px; - position: absolute; - right: 10px; - transition: opacity 500ms ease-in-out; -} - -.gallery .share-menu .bubble-point { - background-image: -webkit-image-set( - url(../images/100/bubble_point.png) 1x, - url(../images/200/bubble_point.png) 2x); - background-position: center top; - background-repeat: no-repeat; - bottom: -8px; - height: 8px; - padding: 0; - position: absolute; - right: 20px; - width: 20px; -} - -.gallery .share-menu[hidden] { - bottom: -100%; /* Offscreen so that 'dimmed' attribute does not show it. */ - opacity: 0; - pointer-events: none; -} - -.gallery .share-menu > .item { - background-color: rgba(0, 0, 0, 0); - background-position: 5px center; - background-repeat: no-repeat; - cursor: pointer; - padding: 5px; - padding-left: 26px; -} - -.gallery .share-menu > .item:hover { - background-color: rgba(240, 240, 240, 1); -} - -.gallery .share-menu > div > img { - display: block; - margin-right: 5px; -} - -/* Load spinner and error banner. */ - -.gallery .spinner { - background-image: url(chrome://resources/images/throbber_medium.svg); - background-size: 100%; - height: 32px; - left: 50%; - margin-left: -16px; - margin-top: -16px; - position: absolute; - top: 50%; - width: 32px; -} - -.gallery:not([spinner]) .spinner { - display: none; -} - -#progress-bar { - position: absolute; - top: 48px; /* toolbar height */ - width: 100%; -} - -.gallery .error-banner { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - background-color: rgba(24, 24, 24, 1); - background-image: -webkit-image-set( - url(../images/100/error.png) 1x, - url(../images/200/error.png) 2x); - background-position: 25px center; - background-repeat: no-repeat; - color: white; - display: -webkit-box; - height: 54px; - padding-left: 70px; - padding-right: 35px; -} - -.gallery:not([error]) .error-banner { - display: none; -} - -.toast-stage { - bottom: 0; - left: 0; - pointer-events: none; - position: absolute; - right: 0; - top: 0; -} - -.gallery[tools] .toast-stage { - bottom: 48px; - top: 48px; -} - -files-toast { - pointer-events: auto; -} - -/* In order to do mode animated transitions smoothly we keep both mosaic and - image-container but transparent. */ -.gallery:not([mode='slide']) .image-container { - pointer-events: none; -} - -.gallery:not([mode='slide']) .ribbon, -.gallery:not([mode='slide']) .arrow-box { - opacity: 0; - pointer-events: none; -} - -/* Temporary. Remove this along with the delete confirmation dialog - when Undo delete is implemented. */ -.cr-dialog-shield { - background-color: black; -} - -/* Slideshow controls */ - -.slideshow-toolbar { - bottom: 16px; - display: none; - justify-content: center; - left: 0; - padding-bottom: 6px; - pointer-events: none; - position: absolute; - right: 0; -} - -.gallery[tools][slideshow] .slideshow-toolbar { - display: flex; -} - -.slideshow-toolbar > div { - background-position: center; - background-repeat: no-repeat; - cursor: pointer; - height: 48px; - opacity: 0.8; - pointer-events: auto; - width: 48px; -} - -.slideshow-toolbar > div:hover { - opacity: 1; -} - -.slideshow-toolbar > .slideshow-play { - background-image: -webkit-image-set( - url(../images/100/slideshow_play.png) 1x, - url(../images/200/slideshow_play.png) 2x); -} - -.gallery[slideshow='playing'] .slideshow-toolbar > .slideshow-play { - background-image: -webkit-image-set( - url(../images/100/slideshow_pause.png) 1x, - url(../images/200/slideshow_pause.png) 2x); -} - -.slideshow-toolbar > .slideshow-end { - background-image: -webkit-image-set( - url(../images/100/slideshow_close.png) 1x, - url(../images/200/slideshow_close.png) 2x); - margin-inline-start: 4px; -} - -.debug-me .load-target-content-metadata::before, -.debug-me .load-target-external-metadata::before, -.debug-me .load-target-file-entry::before { - bottom: 0; - content: ''; - display: block; - left: 0; - position: absolute; - right: 0; - top: 0; - z-index: 1; -} - -.debug-me .load-target-content-metadata::before { - background-color: rgba(255, 0, 0, 0.3); -} - -.debug-me .load-target-external-metadata::before { - background-color: rgba(0, 255, 0, 0.3); -} - -.debug-me .load-target-file-entry::before { - background-color: rgba(0, 0, 255, 0.3); -} - -/** - * Thumbnail view. - */ -.thumbnail-view { - display: block; - height: 100%; - outline: none; - overflow-y: scroll; -} - -.thumbnail-view ul { - margin: 0; - padding-bottom: 0; - padding-inline-end: 4px; - padding-inline-start: 0; - padding-top: 52px; /* Toolbar height (48px) + Margin (4px) */ -} - -.thumbnail-view .thumbnail { - display: block; - margin-bottom: 4px; - margin-inline-start: 4px; - position: relative; -} - -html[dir='ltr'] .thumbnail-view .thumbnail { - float: left; -} - -html[dir='rtl'] .thumbnail-view .thumbnail { - float: right; -} - -.thumbnail-view .thumbnail.selected:focus { - outline: none; -} - -.thumbnail-view::-webkit-scrollbar { - width: 0; -} - -.thumbnail-view .scrollbar { - bottom: 4px; - opacity: 1; - position: absolute; - top: 52px; /* Toolbar height (48px) + Margin (4px) */ - transition: opacity 1s; - width: 8px; -} - -.thumbnail-view .scrollbar.transparent { - opacity: 0; - pointer-events: none; -} - -html[dir='ltr'] .thumbnail-view .scrollbar { - right: 0; -} - -html[dir='rtl'] .thumbnail-view .scrollbar { - left: 0; -} - -.thumbnail-view .scrollbar .thumb { - background-color: white; - border-radius: 2px; - margin-inline-end: 4px; - opacity: 0.4; - width: 4px; -} - -.thumbnail-view .thumbnail > .frame { - bottom: 0; - left: 0; - position: absolute; - right: 0; - top: 0; -} - -/* - * Overlay a play icon over video. The video thumbnail is already hosted using - * background-image, so this puts the overlay in the selection div. - */ -.thumbnail-view .thumbnail > .video + .frame.selection { - background-image: -webkit-image-set(url(../images/play_arrow.svg) 1x); - background-position: center; - background-repeat: no-repeat; - background-size: 48px 48px; -} - -.thumbnail-view .thumbnail > .video + .frame.selection:hover { - background-image: -webkit-image-set(url(../images/play_arrow_hover.svg) 1x); -} - -.thumbnail-view .thumbnail > .frame.image { - background-repeat: no-repeat; - background-size: cover; -} - -.thumbnail-view .thumbnail.selected > .frame.selection { - border: solid 2px rgb(27, 168, 243); -} - -.thumbnail-view .thumbnail.error { - display: none; -} - -.thumbnail-view .thumbnail.transparent { - opacity: 0; -} - -.thumbnail-view .animation-thumbnail { - background-repeat: no-repeat; - background-size: cover; - display: none; - position: absolute; -} - -.thumbnail-view .animation-thumbnail.animating { - display: block; -}
diff --git a/ui/file_manager/gallery/gallery.html b/ui/file_manager/gallery/gallery.html deleted file mode 100644 index 561c8ff..0000000 --- a/ui/file_manager/gallery/gallery.html +++ /dev/null
@@ -1,162 +0,0 @@ -<!-- - -- Copyright 2014 The Chromium Authors. All rights reserved. - -- Use of this source code is governed by a BSD-style license that can be - -- found in the LICENSE file. - --> -<html i18n-values="dir:textdirection"> -<head> - <script src="chrome://resources/polymer/v1_0/html-imports/html-imports.min.js"></script> - <link rel="import" href="chrome://resources/html/polymer.html"> - <link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> - <link rel="stylesheet" href="chrome://resources/chromeos/colors/cros_colors.generated.css"> - <link rel="stylesheet" href="../file_manager/foreground/css/list.css"> - <link rel="stylesheet" href="../file_manager/foreground/css/common.css"> - <link rel="stylesheet" href="../file_manager/foreground/css/file_types.css"> - <link rel="stylesheet" href="css/gallery.css"> - - <script src="chrome://resources/js/assert.js"></script> - <script src="chrome://resources/js/load_time_data.js"></script> - <script src="chrome://resources/js/util.js"></script> - - <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html"> - <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html"> - <link rel="import" href="chrome://resources/cr_elements/cr_slider/cr_slider.html"> - <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html"> - <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> - <link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html"> - <link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html"> - - <script src="js/gallery_scripts.js"></script> -</head> -<body> - <custom-style> - <style> - cr-checkbox { - --cr-checkbox-checked-box-color: white; - --cr-checkbox-label-color: white; - --cr-checkbox-label-padding-start: 6px; - --cr-checkbox-mark-color: black; - --cr-checkbox-ripple-checked-color: white; - --cr-checkbox-ripple-size: 38px; - --cr-checkbox-ripple-unchecked-color: white; - --cr-checkbox-size: 14px; - --cr-checkbox-unchecked-box-color: white; - } - cr-slider { - --cr-slider-active-color: white; - --cr-slider-container-color: rgba(255, 255, 255, 0.2); - --cr-slider-knob-color-rgb: 255, 255, 255; - --cr-slider-secondary-color: transparent; - } - paper-progress { - --paper-progress-container-color: rgba(0, 0, 0, 0.3); - --paper-progress-active-color: #1AC222; - } - cr-button { - --ink-color: black; - border: none; - box-shadow: none; - } - - cr-input { - --cr-input-background-color: transparent; - --cr-input-border-bottom: 1px solid var(--google-grey-800); - --cr-input-border-radius: 0; - --cr-input-color: white; - --cr-input-padding-end: 0; - --cr-input-padding-start: 0; - --cr-input-error-display: none; - --cr-input-focus-color: white; - } - - #rename-input { - font-size: 16px; - margin: 8px 0; - min-width: 160px; - text-overflow: ellipsis; - } - </style> - </custom-style> - <div class="gallery"> - <div id="content" class="content"> - <div class="thumbnail-view"></div> - </div> - <div id="top-toolbar" class="toolbar top tool dimmable"> - <div class="filename-spacer"> - <cr-input id="rename-input" spellcheck="false"></cr-input> - </div> - <div class="button-spacer"> - <!-- Use button element for edit button to implement toggle button with - -- custom ripple effect. --> - <button class="edit icon-button" i18n-values="aria-label:GALLERY_EDIT" - has-tooltip - disabled> - <div class="icon"></div> - </button> - <button class="print icon-button" - i18n-values="aria-label:GALLERY_PRINT" - has-tooltip - disabled> - <div class="icon"></div> - </button> - <button class="delete icon-button" - i18n-values="aria-label:GALLERY_DELETE" - has-tooltip - disabled> - <div class="icon"></div> - </button> - <!-- Use button element for mode button to implement toggle switch - -- button. --> - <button class="mode icon-button" - i18n-values="aria-label:GALLERY_THUMBNAIL" - has-tooltip - disabled> - <div class="icon slide-mode"></div> - <div class="icon thumbnail-mode"></div> - </button> - <button class="slideshow icon-button" - i18n-values="aria-label:GALLERY_SLIDESHOW" - has-tooltip - disabled> - <div class="icon"></div> - </button> - <button class="share icon-button" - i18n-values="aria-label:GALLERY_SHARE" - has-tooltip - disabled> - <div class="icon"></div> - </button> - </div> - </div> - <paper-progress id="progress-bar" min="0" max="100" hidden></paper-progress> - <div id="bottom-toolbar" class="toolbar bottom tool dimmable"> - <div class="slide-mode-toolbar"> - <div class="ribbon-spacer"></div> - </div> - <div class="edit-mode-toolbar"> - <div class="options"> - <cr-checkbox class="overwrite-original" - i18n-content="GALLERY_OVERWRITE_ORIGINAL"> - </cr-checkbox> - <div class="saved" i18n-content="GALLERY_SAVED" hidden></div> - </div> - <div class="edit-bar-spacer"></div> - <div class="exit-button-spacer"> - <cr-button class="exit" i18n-content="GALLERY_EXIT"></cr-button> - </div> - </div> - <div class="bubble" hidden> - <div class="content"></div> - <span class="pointer bottom"></span> - <div class="close-x" i18n-values="aria-label:GALLERY_OVERWRITE_BUBBLE_CLOSE" - has-tooltip></div> - </div> - </div> - <div class="prompt-wrapper" pos="center"> - <div class="error-banner"></div> - </div> - <div class="toast-stage"> - </div> - </div> -</body> -</html>
diff --git a/ui/file_manager/gallery/images/100/auto_fix.png b/ui/file_manager/gallery/images/100/auto_fix.png deleted file mode 100644 index c6a5df9b..0000000 --- a/ui/file_manager/gallery/images/100/auto_fix.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/brightness.png b/ui/file_manager/gallery/images/100/brightness.png deleted file mode 100644 index b187b00..0000000 --- a/ui/file_manager/gallery/images/100/brightness.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/bubble_point.png b/ui/file_manager/gallery/images/100/bubble_point.png deleted file mode 100644 index a4a44e0..0000000 --- a/ui/file_manager/gallery/images/100/bubble_point.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/contrast.png b/ui/file_manager/gallery/images/100/contrast.png deleted file mode 100644 index eb3a157..0000000 --- a/ui/file_manager/gallery/images/100/contrast.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/crop.png b/ui/file_manager/gallery/images/100/crop.png deleted file mode 100644 index 7a484b10..0000000 --- a/ui/file_manager/gallery/images/100/crop.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/cursor_crop.png b/ui/file_manager/gallery/images/100/cursor_crop.png deleted file mode 100644 index 6084188..0000000 --- a/ui/file_manager/gallery/images/100/cursor_crop.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/delete.png b/ui/file_manager/gallery/images/100/delete.png deleted file mode 100644 index dba537da..0000000 --- a/ui/file_manager/gallery/images/100/delete.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/edit.png b/ui/file_manager/gallery/images/100/edit.png deleted file mode 100644 index 1aa1df9b..0000000 --- a/ui/file_manager/gallery/images/100/edit.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/error.png b/ui/file_manager/gallery/images/100/error.png deleted file mode 100644 index bfae452..0000000 --- a/ui/file_manager/gallery/images/100/error.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/mosaic_view.png b/ui/file_manager/gallery/images/100/mosaic_view.png deleted file mode 100644 index 7d11864..0000000 --- a/ui/file_manager/gallery/images/100/mosaic_view.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/print.png b/ui/file_manager/gallery/images/100/print.png deleted file mode 100644 index d081b58..0000000 --- a/ui/file_manager/gallery/images/100/print.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/ratio_locked.png b/ui/file_manager/gallery/images/100/ratio_locked.png deleted file mode 100644 index f4e75f7..0000000 --- a/ui/file_manager/gallery/images/100/ratio_locked.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/ratio_unlocked.png b/ui/file_manager/gallery/images/100/ratio_unlocked.png deleted file mode 100644 index b0bc2fa78..0000000 --- a/ui/file_manager/gallery/images/100/ratio_unlocked.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/redo.png b/ui/file_manager/gallery/images/100/redo.png deleted file mode 100644 index d1881e47..0000000 --- a/ui/file_manager/gallery/images/100/redo.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/resize.png b/ui/file_manager/gallery/images/100/resize.png deleted file mode 100644 index 49289189..0000000 --- a/ui/file_manager/gallery/images/100/resize.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/rotate_left.png b/ui/file_manager/gallery/images/100/rotate_left.png deleted file mode 100644 index ae04831..0000000 --- a/ui/file_manager/gallery/images/100/rotate_left.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/rotate_right.png b/ui/file_manager/gallery/images/100/rotate_right.png deleted file mode 100644 index e1f7c5a..0000000 --- a/ui/file_manager/gallery/images/100/rotate_right.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/share.png b/ui/file_manager/gallery/images/100/share.png deleted file mode 100644 index e7032a63..0000000 --- a/ui/file_manager/gallery/images/100/share.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/slide_view.png b/ui/file_manager/gallery/images/100/slide_view.png deleted file mode 100644 index 0820889..0000000 --- a/ui/file_manager/gallery/images/100/slide_view.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/slideshow.png b/ui/file_manager/gallery/images/100/slideshow.png deleted file mode 100644 index 90affd6..0000000 --- a/ui/file_manager/gallery/images/100/slideshow.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/slideshow_close.png b/ui/file_manager/gallery/images/100/slideshow_close.png deleted file mode 100644 index 89fa710..0000000 --- a/ui/file_manager/gallery/images/100/slideshow_close.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/slideshow_next.png b/ui/file_manager/gallery/images/100/slideshow_next.png deleted file mode 100644 index 30b0b5a..0000000 --- a/ui/file_manager/gallery/images/100/slideshow_next.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/slideshow_pause.png b/ui/file_manager/gallery/images/100/slideshow_pause.png deleted file mode 100644 index d5ad8b5..0000000 --- a/ui/file_manager/gallery/images/100/slideshow_pause.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/slideshow_play.png b/ui/file_manager/gallery/images/100/slideshow_play.png deleted file mode 100644 index e19071ad..0000000 --- a/ui/file_manager/gallery/images/100/slideshow_play.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/slideshow_previous.png b/ui/file_manager/gallery/images/100/slideshow_previous.png deleted file mode 100644 index 35e2d4d..0000000 --- a/ui/file_manager/gallery/images/100/slideshow_previous.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/100/undo.png b/ui/file_manager/gallery/images/100/undo.png deleted file mode 100644 index 3c8bd33a..0000000 --- a/ui/file_manager/gallery/images/100/undo.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/auto_fix.png b/ui/file_manager/gallery/images/200/auto_fix.png deleted file mode 100644 index 4e93aff..0000000 --- a/ui/file_manager/gallery/images/200/auto_fix.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/brightness.png b/ui/file_manager/gallery/images/200/brightness.png deleted file mode 100644 index 54e3d9ad..0000000 --- a/ui/file_manager/gallery/images/200/brightness.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/bubble_point.png b/ui/file_manager/gallery/images/200/bubble_point.png deleted file mode 100644 index 161e4c0..0000000 --- a/ui/file_manager/gallery/images/200/bubble_point.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/contrast.png b/ui/file_manager/gallery/images/200/contrast.png deleted file mode 100644 index cc9ad25..0000000 --- a/ui/file_manager/gallery/images/200/contrast.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/crop.png b/ui/file_manager/gallery/images/200/crop.png deleted file mode 100644 index e9c2e95..0000000 --- a/ui/file_manager/gallery/images/200/crop.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/cursor_crop.png b/ui/file_manager/gallery/images/200/cursor_crop.png deleted file mode 100644 index 6202fa9..0000000 --- a/ui/file_manager/gallery/images/200/cursor_crop.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/delete.png b/ui/file_manager/gallery/images/200/delete.png deleted file mode 100644 index 6d1aafb..0000000 --- a/ui/file_manager/gallery/images/200/delete.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/edit.png b/ui/file_manager/gallery/images/200/edit.png deleted file mode 100644 index 4570fe71..0000000 --- a/ui/file_manager/gallery/images/200/edit.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/error.png b/ui/file_manager/gallery/images/200/error.png deleted file mode 100644 index 200baf5..0000000 --- a/ui/file_manager/gallery/images/200/error.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/mosaic_view.png b/ui/file_manager/gallery/images/200/mosaic_view.png deleted file mode 100644 index 04f11fb..0000000 --- a/ui/file_manager/gallery/images/200/mosaic_view.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/print.png b/ui/file_manager/gallery/images/200/print.png deleted file mode 100644 index 5f2e0f9..0000000 --- a/ui/file_manager/gallery/images/200/print.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/ratio_locked.png b/ui/file_manager/gallery/images/200/ratio_locked.png deleted file mode 100644 index 024f417..0000000 --- a/ui/file_manager/gallery/images/200/ratio_locked.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/ratio_unlocked.png b/ui/file_manager/gallery/images/200/ratio_unlocked.png deleted file mode 100644 index bf3f2ddd..0000000 --- a/ui/file_manager/gallery/images/200/ratio_unlocked.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/redo.png b/ui/file_manager/gallery/images/200/redo.png deleted file mode 100644 index fe8f1af..0000000 --- a/ui/file_manager/gallery/images/200/redo.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/resize.png b/ui/file_manager/gallery/images/200/resize.png deleted file mode 100644 index 6992b6a..0000000 --- a/ui/file_manager/gallery/images/200/resize.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/rotate_left.png b/ui/file_manager/gallery/images/200/rotate_left.png deleted file mode 100644 index 4164fed..0000000 --- a/ui/file_manager/gallery/images/200/rotate_left.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/rotate_right.png b/ui/file_manager/gallery/images/200/rotate_right.png deleted file mode 100644 index cc2850b56..0000000 --- a/ui/file_manager/gallery/images/200/rotate_right.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/share.png b/ui/file_manager/gallery/images/200/share.png deleted file mode 100644 index 8cfa9c6..0000000 --- a/ui/file_manager/gallery/images/200/share.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/slide_view.png b/ui/file_manager/gallery/images/200/slide_view.png deleted file mode 100644 index 1acaebb..0000000 --- a/ui/file_manager/gallery/images/200/slide_view.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/slideshow.png b/ui/file_manager/gallery/images/200/slideshow.png deleted file mode 100644 index 88f3d387..0000000 --- a/ui/file_manager/gallery/images/200/slideshow.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/slideshow_close.png b/ui/file_manager/gallery/images/200/slideshow_close.png deleted file mode 100644 index 72c9bba..0000000 --- a/ui/file_manager/gallery/images/200/slideshow_close.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/slideshow_next.png b/ui/file_manager/gallery/images/200/slideshow_next.png deleted file mode 100644 index a75aa463..0000000 --- a/ui/file_manager/gallery/images/200/slideshow_next.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/slideshow_pause.png b/ui/file_manager/gallery/images/200/slideshow_pause.png deleted file mode 100644 index faadb613..0000000 --- a/ui/file_manager/gallery/images/200/slideshow_pause.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/slideshow_play.png b/ui/file_manager/gallery/images/200/slideshow_play.png deleted file mode 100644 index a5689d7..0000000 --- a/ui/file_manager/gallery/images/200/slideshow_play.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/slideshow_previous.png b/ui/file_manager/gallery/images/200/slideshow_previous.png deleted file mode 100644 index a369a59..0000000 --- a/ui/file_manager/gallery/images/200/slideshow_previous.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/200/undo.png b/ui/file_manager/gallery/images/200/undo.png deleted file mode 100644 index 9ba6c0e1..0000000 --- a/ui/file_manager/gallery/images/200/undo.png +++ /dev/null Binary files differ
diff --git a/ui/file_manager/gallery/images/play_arrow.svg b/ui/file_manager/gallery/images/play_arrow.svg deleted file mode 100644 index 5ad386dda..0000000 --- a/ui/file_manager/gallery/images/play_arrow.svg +++ /dev/null
@@ -1,4 +0,0 @@ -<svg fill="#ffffff" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> - <path d="M8 5v14l11-7z" fill-opacity="0.6"/> - <path d="M0 0h24v24H0z" fill="none"/> -</svg>
diff --git a/ui/file_manager/gallery/images/play_arrow_hover.svg b/ui/file_manager/gallery/images/play_arrow_hover.svg deleted file mode 100644 index 34eea98..0000000 --- a/ui/file_manager/gallery/images/play_arrow_hover.svg +++ /dev/null
@@ -1,4 +0,0 @@ -<svg fill="#ffffff" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> - <path d="M8 5v14l11-7z" fill-opacity="0.9"/> - <path d="M0 0h24v24H0z" fill="none"/> -</svg>
diff --git a/ui/file_manager/gallery/js/BUILD.gn b/ui/file_manager/gallery/js/BUILD.gn deleted file mode 100644 index f2b2556..0000000 --- a/ui/file_manager/gallery/js/BUILD.gn +++ /dev/null
@@ -1,247 +0,0 @@ -# Copyright 2018 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("//third_party/closure_compiler/compile_js.gni") -import("//third_party/closure_compiler/js_unit_tests.gni") -import("//ui/file_manager/base/gn/js_test_gen_html.gni") - -js_type_check("closure_compile_module") { - uses_legacy_modules = true - deps = [ - ":background", - ":dimmable_ui_controller", - ":entry_list_watcher", - ":error_banner", - ":gallery", - ":gallery_constants", - ":gallery_data_model", - ":gallery_item", - ":gallery_util", - ":metadata_worker", - ":ribbon", - ":slide_mode", - ":thumbnail_mode", - ] -} - -js_library("background") { - deps = [ - "//ui/file_manager/file_manager/background/js:app_window_wrapper", - "//ui/file_manager/file_manager/background/js:background_base", - "//ui/file_manager/file_manager/common/js:util", - ] -} - -js_library("dimmable_ui_controller") { - deps = [ - ":gallery_constants", - "image_editor:image_editor_prompt", - "//ui/webui/resources/js:assert", - ] - externs_list = [ - # We need both chrome_extensions.js and accessibility_features.js to support - # chrome.accessibilityFeatures, since accessibility_features.js uses the - # ChromeSetting type (only defined in chrome_extensions.js). - "$externs_path/accessibility_features.js", - "$externs_path/chrome.js", - "$externs_path/chrome_extensions.js", - ] -} - -js_unittest("dimmable_ui_controller_unittest") { - deps = [ - ":dimmable_ui_controller", - "//ui/webui/resources/js:webui_resource_test", - ] -} - -js_library("entry_list_watcher") { - deps = [ - "//ui/file_manager/file_manager/externs:file_manager_private", - "//ui/webui/resources/js:assert", - "//ui/webui/resources/js/cr/ui:array_data_model", - ] -} - -js_unittest("entry_list_watcher_unittest") { - deps = [ - ":entry_list_watcher", - "//ui/file_manager/file_manager/common/js:mock_entry", - "//ui/file_manager/file_manager/common/js:test_error_reporting", - ] -} - -js_library("error_banner") { - deps = [ "//ui/file_manager/file_manager/common/js:util" ] -} - -js_library("gallery") { - deps = [ - ":gallery_constants", - ":gallery_item", - ":thumbnail_mode", - "//ui/file_manager/file_manager/common/js:app_util", - "//ui/file_manager/file_manager/common/js:filtered_volume_manager", - "//ui/file_manager/file_manager/common/js:util", - "//ui/file_manager/file_manager/foreground/js/ui:files_confirm_dialog", - "//ui/file_manager/gallery/js:slide_mode", - "//ui/webui/resources/js:i18n_template_no_process", - ] -} - -js_library("gallery_constants") { -} - -js_library("gallery_data_model") { - deps = [ - ":entry_list_watcher", - ":gallery_item", - "//ui/file_manager/file_manager/common/js:util", - "//ui/file_manager/file_manager/foreground/js/metadata:thumbnail_model", - "//ui/webui/resources/js:assert", - "//ui/webui/resources/js/cr/ui:array_data_model", - ] -} - -js_unittest("gallery_data_model_unittest") { - deps = [ - ":gallery_data_model", - ":mock_gallery_item", - "//ui/file_manager/file_manager/common/js:test_error_reporting", - ] -} - -js_library("gallery_item") { - deps = [ - ":gallery_metrics", - ":gallery_util", - "image_editor:image_encoder", - "image_editor:image_util", - "//ui/file_manager/file_manager/common/js:util", - "//ui/file_manager/file_manager/foreground/js/metadata:metadata_model", - "//ui/file_manager/file_manager/foreground/js/metadata:thumbnail_model", - ] - externs_list = [ "//ui/file_manager/file_manager/externs/entry_location.js" ] -} - -js_unittest("gallery_item_unittest") { - deps = [ - ":gallery_item", - ":mock_gallery_item", - "//ui/file_manager/file_manager/common/js:test_error_reporting", - ] -} - -js_library("gallery_metrics") { - deps = [ "//ui/file_manager/file_manager/common/js:metrics_base" ] -} - -js_library("gallery_util") { - deps = [ - "//ui/file_manager/file_manager/common/js:file_type", - "//ui/file_manager/file_manager/common/js:util", - "//ui/file_manager/file_manager/common/js:volume_manager_types", - "//ui/file_manager/file_manager/externs:volume_manager", - ] -} - -js_unittest("gallery_util_unittest") { - deps = [ - ":gallery_util", - "//ui/file_manager/file_manager/common/js:mock_entry", - "//ui/file_manager/file_manager/common/js:test_error_reporting", - ] -} - -js_library("metadata_worker") { - deps = [ - "//ui/file_manager/file_manager/foreground/js/metadata:metadata_dispatcher", - ] -} - -js_library("mock_gallery_item") { - deps = [ - ":gallery_item", - "//ui/file_manager/file_manager/common/js:mock_entry", - "//ui/file_manager/file_manager/foreground/js/metadata:mock_metadata", - ] -} - -js_library("ribbon") { - deps = [ - ":gallery_data_model", - ":gallery_item", - "image_editor:image_util", - "//ui/file_manager/file_manager/foreground/js:thumbnail_loader", - "//ui/file_manager/file_manager/foreground/js/metadata:thumbnail_model", - "//ui/webui/resources/js/cr/ui:array_data_model", - "//ui/webui/resources/js/cr/ui:list_selection_model", - ] -} - -js_unittest("ribbon_unittest") { - deps = [ ":ribbon" ] -} - -js_library("slide_mode") { - deps = [ - ":dimmable_ui_controller", - ":error_banner", - ":gallery_constants", - ":gallery_data_model", - ":gallery_item", - ":gallery_metrics", - ":ribbon", - "image_editor:image_adjust", - "image_editor:image_editor", - "image_editor:image_transform", - "image_editor:image_util", - "image_editor:image_view", - "image_editor:viewport", - "//third_party/polymer/v1_0/components-chromium/paper-progress:paper-progress-extracted", - "//ui/file_manager/file_manager/common/js:util", - ] - externs_list = - [ "//ui/file_manager/file_manager/externs/gallery_foreground.js" ] -} - -js_unittest("slide_mode_unittest") { - deps = [ - ":slide_mode", - "//ui/file_manager/file_manager/common/js:test_error_reporting", - ] -} - -js_library("thumbnail_mode") { - deps = [ - ":error_banner", - ":gallery_constants", - ":gallery_data_model", - ":gallery_item", - "image_editor:image_editor", - "//ui/file_manager/file_manager/foreground/js:thumbnail_loader", - "//ui/file_manager/file_manager/foreground/js/metadata:thumbnail_model", - "//ui/webui/resources/js/cr/ui:list_selection_model", - ] -} - -js_test_gen_html("js_test_gen_html") { - deps = [ - ":dimmable_ui_controller_unittest", - ":entry_list_watcher_unittest", - ":gallery_data_model_unittest", - ":gallery_item_unittest", - ":gallery_util_unittest", - ":ribbon_unittest", - ":slide_mode_unittest", - ] -} - -group("closure_compile") { - testonly = true - deps = [ - ":closure_compile_module", - ":js_test_gen_html_type_check_auto", - ] -}
diff --git a/ui/file_manager/gallery/js/background.js b/ui/file_manager/gallery/js/background.js deleted file mode 100644 index c9a901a..0000000 --- a/ui/file_manager/gallery/js/background.js +++ /dev/null
@@ -1,84 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * Configuration of the Gallery window. - * @type {!Object} - * @const - */ -const windowCreateOptions = { - id: 'gallery', - outerBounds: { - minWidth: 860, - minHeight: 554, - }, - frame: { - color: '#1E2023', - }, - hidden: true -}; - -/** - * Backgound object. This is necessary for AppWindowWrapper. - * @type {!BackgroundBase} - */ -window.background = new BackgroundBaseImpl(); - -/** - * Gallery app window wrapper. - * @type {!SingletonAppWindowWrapper} - */ -const galleryWrapper = - new SingletonAppWindowWrapper('gallery.html', windowCreateOptions); - -/** - * Opens gallery window. - * @param {!Array<string>} urls List of URL to show. - * @return {!Promise} Promise to be fulfilled on success, or rejected on error. - */ -function openGalleryWindow(urls) { - return new Promise(function(fulfill, reject) { - util.URLsToEntries(urls) - .then(function(result) { - fulfill(util.entriesToURLs(result.entries)); - }) - .catch(reject); - }) - .then(function(urls) { - if (urls.length === 0) { - return Promise.reject('No file to open.'); - } - - // Opens a window. - return galleryWrapper.launch({urls: urls}, false) - .then(function() { - const galleryWrapperDocument = - galleryWrapper.rawAppWindow.contentWindow.document; - if (galleryWrapperDocument.readyState == 'complete') { - return galleryWrapper; - } - - return new Promise(function(fulfill, reject) { - galleryWrapperDocument.addEventListener( - 'DOMContentLoaded', fulfill.bind(null, galleryWrapper)); - }); - }); - }) - .then(function(galleryWrapper) { - // If the window is minimized, we need to restore it first. - if (galleryWrapper.rawAppWindow.isMinimized()) { - galleryWrapper.rawAppWindow.restore(); - } - - galleryWrapper.rawAppWindow.show(); - - return galleryWrapper.rawAppWindow.contentWindow.appID; - }) - .catch(function(error) { - console.error('Launch failed: ' + (error.stack || error)); - return Promise.reject(error); - }); -} - -window.background.setLaunchHandler(openGalleryWindow);
diff --git a/ui/file_manager/gallery/js/background_scripts.js b/ui/file_manager/gallery/js/background_scripts.js deleted file mode 100644 index 1710b3f..0000000 --- a/ui/file_manager/gallery/js/background_scripts.js +++ /dev/null
@@ -1,7 +0,0 @@ -// Copyright 2016 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 src="gallery_metrics.js"> -// <include src="test_util.js"> -// <include src="background.js">
diff --git a/ui/file_manager/gallery/js/dimmable_ui_controller.js b/ui/file_manager/gallery/js/dimmable_ui_controller.js deleted file mode 100644 index a1fdbea..0000000 --- a/ui/file_manager/gallery/js/dimmable_ui_controller.js +++ /dev/null
@@ -1,391 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * Dimmable UI Controller. - * @param {!HTMLElement} container - * @constructor - * @struct - */ -function DimmableUIController(container) { - /** - * @private {!HTMLElement} - * @const - */ - this.container_ = container; - - /** - * @private {NodeList} - */ - this.tools_ = null; - - /** - * @private {number} - */ - this.timeoutId_ = 0; - - /** - * @private {boolean} - */ - this.isCursorInTools_ = false; - - /** - * @private {GalleryMode|undefined} - */ - this.mode_ = undefined; - - /** - * @private {GallerySubMode|undefined} - */ - this.subMode_ = undefined; - - /** - * @private {boolean} - */ - this.spokenFeedbackEnabled_ = false; - - /** - * @private {boolean} - */ - this.loading_ = false; - - /** - * @private {boolean} - */ - this.renaming_ = false; - - /** - * @private {boolean} - */ - this.disabled_ = false; - - /** - * @private {number} - */ - this.madeVisibleAt_ = 0; - - this.container_.addEventListener('click', this.onClick_.bind(this)); - this.container_.addEventListener('mousemove', this.onMousemove_.bind(this)); - this.container_.addEventListener( - 'touchstart', this.onTouchOperation_.bind(this)); - this.container_.addEventListener( - 'touchmove', this.onTouchOperation_.bind(this)); - this.container_.addEventListener( - 'touchend', this.onTouchOperation_.bind(this)); - this.container_.addEventListener( - 'touchcancel', this.onTouchOperation_.bind(this)); - - chrome.accessibilityFeatures.spokenFeedback.onChange.addListener( - this.onGetOrChangedSpokenFeedbackConfiguration_.bind(this)); - chrome.accessibilityFeatures.spokenFeedback.get({}, - this.onGetOrChangedSpokenFeedbackConfiguration_.bind(this)); -} - -/** - * Default timeout. - * @const {number} - */ -DimmableUIController.DEFAULT_TIMEOUT = 3000; // ms - -/** - * We don't allow user to change visibility of tools shorter than this interval. - * This is necessary not to hide tools immediately after they become visible by - * touchstart event when user taps UI to make them visible. - * @const {number} - */ -DimmableUIController.MIN_OPERATION_INTERVAL = 500; // ms - -/** - * Returns true if this controller should be disabled. - * @param {GalleryMode|undefined} mode - * @param {GallerySubMode|undefined} subMode - * @param {boolean} loading - * @param {boolean} spokenFeedbackEnabled - * @param {boolean} renaming - * @return {boolean} - */ -DimmableUIController.shouldBeDisabled = function( - mode, subMode, loading, spokenFeedbackEnabled, renaming) { - return spokenFeedbackEnabled || mode === undefined || subMode === undefined || - mode === GalleryMode.THUMBNAIL || - (mode === GalleryMode.SLIDE && subMode === GallerySubMode.EDIT) || - (mode === GalleryMode.SLIDE && subMode === GallerySubMode.BROWSE && - (loading || renaming)); -}; - -/** - * Sets current mode of Gallery. - * @param {GalleryMode} mode - * @param {GallerySubMode} subMode - */ -DimmableUIController.prototype.setCurrentMode = function(mode, subMode) { - if (this.mode_ === mode && this.subMode_ === subMode) { - return; - } - - this.mode_ = mode; - this.subMode_ = subMode; - this.updateAvailability_(); -}; - -/** - * Sets whether user is renaming an image or not. - * @param {boolean} renaming - */ -DimmableUIController.prototype.setRenaming = function(renaming) { - if (this.renaming_ === renaming) { - return; - } - - this.renaming_ = renaming; - this.updateAvailability_(); -}; - -/** - * Sets whether gallery is currently loading an image or not. - * @param {boolean} loading - */ -DimmableUIController.prototype.setLoading = function(loading) { - if (this.loading_ === loading) { - return; - } - - this.loading_ = loading; - this.updateAvailability_(); -}; - -/** - * Handles click event. - * @param {!Event} event An event. - * @private - */ -DimmableUIController.prototype.onClick_ = function(event) { - if (this.disabled_ || - (event.target && - this.isPartOfTools_(/** @type {!HTMLElement} */ (event.target)))) { - return; - } - - this.toggle_(); -}; - -/** - * Handles mousemove event. - * @private - */ -DimmableUIController.prototype.onMousemove_ = function() { - if (this.disabled_) { - return; - } - - this.kick(); -}; - -/** - * Handles touch event. - * @private - */ -DimmableUIController.prototype.onTouchOperation_ = function() { - if (this.disabled_) { - return; - } - - this.kick(); -}; - -/** - * Handles mouseover event. - * @private - */ -DimmableUIController.prototype.onMouseover_ = function() { - if (this.disabled_) { - return; - } - - this.isCursorInTools_ = true; -}; - -/** - * Handles mouseout event. - * @private - */ -DimmableUIController.prototype.onMouseout_ = function() { - if (this.disabled_) { - return; - } - - this.isCursorInTools_ = false; -}; - -/** - * Returns true if element is a part of tools. - * @param {!HTMLElement} element A html element. - * @return {boolean} True if element is a part of tools. - * @private - */ -DimmableUIController.prototype.isPartOfTools_ = function(element) { - for (var i = 0; i < this.tools_.length; i++) { - if (this.tools_[i].contains(element)) { - return true; - } - } - return false; -}; - -/** - * Toggles visibility of UI. - * @private - */ -DimmableUIController.prototype.toggle_ = function() { - if (this.isToolsVisible_()) { - this.show_(false); - } else { - this.kick(); - } -}; - -/** - * Returns true if UI is visible. - * @return {boolean} True if UI is visible. - * @private - */ -DimmableUIController.prototype.isToolsVisible_ = function() { - return this.container_.hasAttribute('tools'); -}; - -/** - * Shows UI. - * @param {boolean} show True to show UI. - * @private - */ -DimmableUIController.prototype.show_ = function(show) { - if (this.isToolsVisible_() === show) { - return; - } - - if (show) { - this.madeVisibleAt_ = Date.now(); - this.container_.setAttribute('tools', true); - } else { - if (Date.now() - this.madeVisibleAt_ < - DimmableUIController.MIN_OPERATION_INTERVAL) { - return; - } - - this.container_.removeAttribute('tools'); - this.clearTimeout_(); - } -}; - -/** - * Clears current timeout. - * @private - */ -DimmableUIController.prototype.clearTimeout_ = function() { - if (!this.timeoutId_) { - return; - } - - clearTimeout(this.timeoutId_); - this.timeoutId_ = 0; -}; - -/** - * Extends current timeout. - * @param {number=} opt_timeout Timeout. - * @private - */ -DimmableUIController.prototype.extendTimeout_ = function(opt_timeout) { - this.clearTimeout_(); - - var timeout = opt_timeout || DimmableUIController.DEFAULT_TIMEOUT; - this.timeoutId_ = setTimeout(this.onTimeout_.bind(this), timeout); -}; - -/** - * Handles timeout. - * @private - */ -DimmableUIController.prototype.onTimeout_ = function() { - // If mouse cursor is on tools, extend timeout. - if (this.isCursorInTools_) { - this.extendTimeout_(); - return; - } - - this.show_(false /* hide */); -}; - -/** - * Updates availability of this controller with spoken feedback configuration. - * @param {Object} details - * @private - */ -DimmableUIController.prototype.onGetOrChangedSpokenFeedbackConfiguration_ = - function(details) { - this.spokenFeedbackEnabled_ = !!details.value; - this.updateAvailability_(); -}; - -/** - * Sets tools which are controlled by this controller. - * This method must not be called more than once for an instance. - * @param {!NodeList} tools Tools. - */ -DimmableUIController.prototype.setTools = function(tools) { - assert(this.tools_ === null); - - this.tools_ = tools; - - for (var i = 0; i < this.tools_.length; i++) { - this.tools_[i].addEventListener('mouseover', this.onMouseover_.bind(this)); - this.tools_[i].addEventListener('mouseout', this.onMouseout_.bind(this)); - } -}; - -/** - * Shows UI and set timeout. - * @param {number=} opt_timeout Timeout. - */ -DimmableUIController.prototype.kick = function(opt_timeout) { - if (this.disabled_) { - return; - } - - this.show_(true); - this.extendTimeout_(opt_timeout); -}; - -/** - * Updates availability. - * @private - */ -DimmableUIController.prototype.updateAvailability_ = function() { - var disabled = DimmableUIController.shouldBeDisabled( - this.mode_, this.subMode_, this.loading_, this.spokenFeedbackEnabled_, - this.renaming_); - - if (this.disabled_ === disabled) { - return; - } - - this.disabled_ = disabled; - - if (this.disabled_) { - this.isCursorInTools_ = false; - this.show_(true); - this.clearTimeout_(); - } else { - this.kick(); - } -}; - -/** - * Sets cursor's state as out of tools. Mouseout event is not dispatched for - * some cases even when mouse cursor goes out of elements. This method is used - * to handle these cases manually. - */ -DimmableUIController.prototype.setCursorOutOfTools = function() { - this.isCursorInTools_ = false; -};
diff --git a/ui/file_manager/gallery/js/dimmable_ui_controller_unittest.js b/ui/file_manager/gallery/js/dimmable_ui_controller_unittest.js deleted file mode 100644 index 396949d3..0000000 --- a/ui/file_manager/gallery/js/dimmable_ui_controller_unittest.js +++ /dev/null
@@ -1,40 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -function testShouldBeDisabled() { - // Disabled when mode is not set. - assertTrue(DimmableUIController.shouldBeDisabled( - undefined /* mode */, undefined /* subMode */, false /* loading */, - false /* spokenFeedbackEnabled */, false /* renaming */)); - - // Disabled in thumbnail mode. - assertTrue(DimmableUIController.shouldBeDisabled( - GalleryMode.THUMBNAIL, GallerySubMode.BROWSE, false /* loading */, - false /* spokenFeedbackEnabled */, false /* renaming */)); - - // Disabled in edit mode. - assertTrue(DimmableUIController.shouldBeDisabled( - GalleryMode.SLIDE, GallerySubMode.EDIT, false /* loading*/, - false /* spokenFeedbackEnabled */, false /* renaming */)); - - // Shouldn't be disabled while browsing in slide mode. - assertFalse(DimmableUIController.shouldBeDisabled( - GalleryMode.SLIDE, GallerySubMode.BROWSE, false /* loading */, - false /* spokenFeedbackEnabled */, false /* renaming */)); - - // Disabled while loading an image in slide mode. - assertTrue(DimmableUIController.shouldBeDisabled( - GalleryMode.SLIDE, GallerySubMode.BROWSE, true /* loading */, - false /* spokenFeedbackEnabled */, false /* renaming */)); - - // Disabled when spoken feedback is enabled. - assertTrue(DimmableUIController.shouldBeDisabled( - GalleryMode.SLIDE, GallerySubMode.BROWSE, false /* loading */, - true /* spokenFeedbackEnabled */, false /* renaming */)); - - // Disabled when user is renaming an image. - assertTrue(DimmableUIController.shouldBeDisabled( - GalleryMode.SLIDE, GallerySubMode.BROWSE, false /* loading */, - false /* spokenFeedbackEnabled */, true /* renaming */)); -}
diff --git a/ui/file_manager/gallery/js/entry_list_watcher.js b/ui/file_manager/gallery/js/entry_list_watcher.js deleted file mode 100644 index 8d79d5b..0000000 --- a/ui/file_manager/gallery/js/entry_list_watcher.js +++ /dev/null
@@ -1,141 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * Watcher for entry lists. - * It watches entries and remove the item if the entry is removed from file - * system. - * @param {!cr.ui.ArrayDataModel} list - * @constructor - * @struct - */ -function EntryListWatcher(list) { - /** - * Watched list. - * @type {!cr.ui.ArrayDataModel} - * @const - * @private - */ - this.list_ = list; - - /** - * Set of watched URL. - * Key is watched entry URL. Value is always true. - * @type {!Object<boolean>} - * @private - */ - this.watchers_ = {}; - - this.list_.addEventListener('splice', this.onSplice_.bind(this)); - chrome.fileManagerPrivate.onDirectoryChanged.addListener( - this.onDirectoryChanged_.bind(this)); - - this.onSplice_(null); -} - -/** - * Obtains entry from ArrayDataModel's item. - * @param {*} item Item in ArrayDataModel. - * @return {!FileEntry} - */ -EntryListWatcher.prototype.getEntry = function(item) { - return /** @type {!FileEntry} */ (item); -}; - -/** - * @param {Event} event - * @private - */ -EntryListWatcher.prototype.onSplice_ = function(event) { - // TODO(mtomasz, hirono): Remove operations on URLs as they won't work after - // switching to isolated entries. - - // Mark all existing watchers as candidates to be removed. - var diff = {}; - for (var url in this.watchers_) { - diff[url] = -1; - } - - // Obtains the set of new watcher. - this.watchers_ = {}; - for (var i = 0; i < this.list_.length; i++) { - var entry = this.getEntry(this.list_.item(i)); - var parentURL = entry.toURL().replace(/[^\/]+\/?$/, ''); - this.watchers_[parentURL] = true; - } - - // Mark new watchers to be added, and existing watchers to be kept. - for (var url in this.watchers_) { - diff[url] = (diff[url] || 0) + 1; - } - - // Check the number in diff. - // -1: watcher exists in the old set, but does not exists in the new set. - // 0: watcher exists in both sets. - // 1: watcher does not exists in the old set, but exists in the new set. - var reportError = function() { - if (chrome.runtime.lastError) { - console.error(chrome.runtime.lastError.name); - } - }; - for (var url in diff) { - switch (diff[url]) { - case 1: - window.webkitResolveLocalFileSystemURL(url, function(entry) { - reportError(); - chrome.fileManagerPrivate.addFileWatch(entry, reportError); - }); - break; - case -1: - window.webkitResolveLocalFileSystemURL(url, function(entry) { - reportError(); - chrome.fileManagerPrivate.removeFileWatch(entry, reportError); - }); - break; - case 0: - break; - default: - assertNotReached(); - break; - } - } -}; - -/** - * @param {!chrome.fileManagerPrivate.FileWatchEvent} event - * @private - */ -EntryListWatcher.prototype.onDirectoryChanged_ = function(event) { - // Add '/' to the tail for checking if the each entry's URL is child URL of - // the URL or not by using entryURL.indexOf(thisUrl) === 0. - var url = event.entry.toURL().replace(/\/?$/, '/'); - var promiseList = []; - var removedEntryURL = {}; - for (var i = 0; i < this.list_.length; i++) { - var entry = this.getEntry(this.list_.item(i)); - // Remove trailing '/' to prevent calling getMetadata in the case where the - // event.entry and the entry are same. - var entryURL = entry.toURL().replace(/\/$/, ''); - if (entry.toURL().indexOf(url) !== 0) { - continue; - } - // Look for non-existing files by using getMetadata. - // getMetadata returns NOT_FOUND error for non-existing files. - promiseList.push(new Promise(entry.getMetadata.bind(entry)).catch( - function(url) { - removedEntryURL[url] = true; - }.bind(null, entry.toURL()))); - } - Promise.all(promiseList).then(function() { - var i = 0; - while (i < this.list_.length) { - var url = this.getEntry(this.list_.item(i)).toURL(); - if (removedEntryURL[url]) { - this.list_.splice(i, 1); - } else { - i++; - } - } - }.bind(this)); -};
diff --git a/ui/file_manager/gallery/js/entry_list_watcher_unittest.js b/ui/file_manager/gallery/js/entry_list_watcher_unittest.js deleted file mode 100644 index 28c473b..0000000 --- a/ui/file_manager/gallery/js/entry_list_watcher_unittest.js +++ /dev/null
@@ -1,170 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -var chrome; -var mockFileSystem; - -/** - * @constructor - * @struct - */ -function MockAPIEvent() { - /** - * @type {!Array<!Function>} - * @const - */ - this.listeners_ = []; -} - -/** - * @param {!Function} callback - */ -MockAPIEvent.prototype.addListener = function(callback) { - this.listeners_.push(callback); -}; - -/** - * @param {!Function} callback - */ -MockAPIEvent.prototype.removeListener = function(callback) { - var index = this.listeners_.indexOf(callback); - if (index < 0) { - throw new Error('Tried to remove an unregistered listener.'); - } - this.listeners_.splice(index, 1); -}; - -/** - * @param {...*} var_args - */ -MockAPIEvent.prototype.dispatch = function(var_args) { - for (var i = 0; i < this.listeners_.length; i++) { - this.listeners_[i].apply(null, arguments); - } -}; - -function webkitResolveLocalFileSystemURL(url, callback) { - var paths = Object.keys(mockFileSystem.entries); - for (var i = 0; i < paths.length; i++) { - var entry = mockFileSystem.entries[paths[i]]; - if (url === entry.toURL()) { - delete chrome.runtime['lastError']; - callback(entry); - return; - } - } - chrome.runtime.lastError = { - name: 'Not found.' - }; - callback(null); -} - -/** - * Use a map to track watched URLs in the test. This is not normally part of the - * fileManagerPrivate API. - * - * @typedef {Object<string, boolean>} - */ -chrome.fileManagerPrivate.watchedURLs; - -/** - * Creates a mock for window.chrome. TODO(tapted): Make this an ES6 class to - * avoid the confusing use of |this| below. - * @constructor - */ -function MockChrome() { - this.fileManagerPrivate = { - onDirectoryChanged: new MockAPIEvent(), - addFileWatch: function(entry, callback) { - this.watchedURLs[entry.toURL()] = true; - callback(); - }, - removeFileWatch: function(entry, callback) { - delete this.watchedURLs[entry.toURL()]; - callback(); - }, - watchedURLs: {} - }; - this.runtime = {}; // For lastError. -} - -/** - * Replace the real chrome APIs with a mock while suppressing closure errors. - * This is in its own function to limit the scope of suppressions. - * - * @param {Object} mockChrome - * @suppress {checkTypes|const} - */ -function replaceChromeWithMock() { - chrome = new MockChrome(); -} - -function setUp() { - replaceChromeWithMock(); - mockFileSystem = new MockFileSystem('volumeId', 'filesystem://rootURL'); - mockFileSystem.entries['/'] = MockDirectoryEntry.create(mockFileSystem, '/'); - mockFileSystem.entries['/A.txt'] = - MockFileEntry.create(mockFileSystem, '/A.txt'); - mockFileSystem.entries['/B.txt'] = - MockFileEntry.create(mockFileSystem, '/B.txt'); - mockFileSystem.entries['/C/'] = - MockDirectoryEntry.create(mockFileSystem, '/C/'); - mockFileSystem.entries['/C/D.txt'] = - MockFileEntry.create(mockFileSystem, '/C/D.txt'); -} - -function testAddWatcher() { - var list = new cr.ui.ArrayDataModel([ - mockFileSystem.entries['/A.txt'] - ]); - var watcher = new EntryListWatcher(list); - assertArrayEquals( - ['filesystem://rootURL/'], - Object.keys(chrome.fileManagerPrivate.watchedURLs)); - list.push(mockFileSystem.entries['/C/D.txt']); - assertArrayEquals( - ['filesystem://rootURL/', 'filesystem://rootURL/C/'], - Object.keys(chrome.fileManagerPrivate.watchedURLs).sort()); -} - -function testRemoveWatcher() { - var list = new cr.ui.ArrayDataModel([ - mockFileSystem.entries['/A.txt'], - mockFileSystem.entries['/C/D.txt'] - ]); - var watcher = new EntryListWatcher(list); - assertArrayEquals( - ['filesystem://rootURL/', 'filesystem://rootURL/C/'], - Object.keys(chrome.fileManagerPrivate.watchedURLs).sort()); - list.splice(1, 1); - assertArrayEquals( - ['filesystem://rootURL/'], - Object.keys(chrome.fileManagerPrivate.watchedURLs)); - -} - -function testEntryRemoved(callback) { - var list = new cr.ui.ArrayDataModel([ - mockFileSystem.entries['/A.txt'], - mockFileSystem.entries['/B.txt'] - ]); - - var watcher = new EntryListWatcher(list); - var splicedPromise = new Promise(function(fulfill) { - list.addEventListener('splice', fulfill); - }); - - var deletedB = mockFileSystem.entries['/B.txt']; - delete mockFileSystem.entries['/B.txt']; - assertArrayEquals( - ['filesystem://rootURL/'], - Object.keys(chrome.fileManagerPrivate.watchedURLs)); - /** @type{MockAPIEvent} */ (chrome.fileManagerPrivate.onDirectoryChanged) - .dispatch({entry: mockFileSystem.entries['/']}); - - reportPromise(splicedPromise.then(function(event) { - assertEquals(1, event.removed.length); - assertEquals(deletedB, event.removed[0]); - }), callback); -}
diff --git a/ui/file_manager/gallery/js/error_banner.js b/ui/file_manager/gallery/js/error_banner.js deleted file mode 100644 index 3a23a4f7..0000000 --- a/ui/file_manager/gallery/js/error_banner.js +++ /dev/null
@@ -1,28 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @param {Element} container Content container. - * @constructor - */ -function ErrorBanner(container) { - this.container_ = container; - this.errorBanner_ = this.container_.querySelector('.error-banner'); -} - -/** - * Shows an error message. - * @param {string} message Message. - */ -ErrorBanner.prototype.show = function(message) { - this.errorBanner_.textContent = str(message); - this.container_.setAttribute('error', true); -}; - -/** - * Hides an error message. - */ -ErrorBanner.prototype.clear = function() { - this.container_.removeAttribute('error'); -};
diff --git a/ui/file_manager/gallery/js/gallery.js b/ui/file_manager/gallery/js/gallery.js deleted file mode 100644 index 26d0f5b..0000000 --- a/ui/file_manager/gallery/js/gallery.js +++ /dev/null
@@ -1,1136 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Override metadata worker's path. -ContentMetadataProvider.configure('/js/metadata_worker.js'); - -/** - * Gallery for viewing and editing image files. - * - * @param {!VolumeManager} volumeManager - * @constructor - * @struct - */ -function Gallery(volumeManager) { - /** - * @type {{appWindow: chrome.app.window.AppWindow, readonlyDirName: string, - * displayStringFunction: function(), loadTimeData: Object}} - * @private - */ - this.context_ = { - appWindow: chrome.app.window.current(), - readonlyDirName: '', - displayStringFunction: function() { - return ''; - }, - loadTimeData: {}, - }; - this.container_ = queryRequiredElement('.gallery'); - this.document_ = document; - this.volumeManager_ = volumeManager; - /** - * @private {!MetadataModel} - * @const - */ - this.metadataModel_ = MetadataModel.create(volumeManager); - /** - * @private {!ThumbnailModel} - * @const - */ - this.thumbnailModel_ = new ThumbnailModel(this.metadataModel_); - this.selectedEntry_ = null; - this.onExternallyUnmountedBound_ = this.onExternallyUnmounted_.bind(this); - this.initialized_ = false; - - this.dataModel_ = new GalleryDataModel(this.metadataModel_); - const downloadVolumeInfo = this.volumeManager_.getCurrentProfileVolumeInfo( - VolumeManagerCommon.VolumeType.DOWNLOADS); - downloadVolumeInfo.resolveDisplayRoot().then(function(entry) { - this.dataModel_.fallbackSaveDirectory = entry; - }.bind(this)).catch(function(error) { - console.error( - 'Failed to obtain the fallback directory: ' + (error.stack || error)); - }); - this.selectionModel_ = new cr.ui.ListSelectionModel(); - - /** - * @type {(SlideMode|ThumbnailMode)} - * @private - */ - this.currentMode_ = null; - - /** - * @type {boolean} - * @private - */ - this.changingMode_ = false; - - // ----------------------------------------------------------------- - // Initializes the UI. - - // Initialize the dialog label. - cr.ui.dialogs.BaseDialog.OK_LABEL = str('GALLERY_OK_LABEL'); - cr.ui.dialogs.BaseDialog.CANCEL_LABEL = str('GALLERY_CANCEL_LABEL'); - - const content = getRequiredElement('content'); - content.addEventListener('click', this.onContentClick_.bind(this)); - - this.topToolbar_ = getRequiredElement('top-toolbar'); - this.bottomToolbar_ = getRequiredElement('bottom-toolbar'); - - this.filenameSpacer_ = queryRequiredElement('.filename-spacer', - this.topToolbar_); - - /** - * @private {HTMLInputElement} - * @const - */ - this.filenameEdit_ = /** @type {HTMLInputElement} */ - (queryRequiredElement('#rename-input', this.filenameSpacer_)); - - this.filenameCanvas_ = document.createElement('canvas'); - this.filenameCanvasContext_ = this.filenameCanvas_.getContext('2d'); - - // Set font style of canvas context to same font style with rename field. - const filenameEditComputedStyle = window.getComputedStyle(this.filenameEdit_); - this.filenameCanvasContext_.font = filenameEditComputedStyle.font; - - this.filenameEdit_.addEventListener('blur', - this.onFilenameEditBlur_.bind(this)); - this.filenameEdit_.addEventListener('focus', - this.onFilenameFocus_.bind(this)); - this.filenameEdit_.addEventListener('input', - this.resizeRenameField_.bind(this)); - this.filenameEdit_.addEventListener('keydown', - this.onFilenameEditKeydown_.bind(this)); - - this.prompt_ = new ImageEditorPrompt(this.container_, strf); - - this.errorBanner_ = new ErrorBanner(this.container_); - - /** - * @private {!HTMLElement} - * @const - */ - this.modeSwitchButton_ = queryRequiredElement('button.mode', - this.topToolbar_); - GalleryUtil.decorateMouseFocusHandling(this.modeSwitchButton_); - this.modeSwitchButton_.addEventListener('click', - this.onModeSwitchButtonClicked_.bind(this)); - - /** - * @private {!DimmableUIController} - * @const - */ - this.dimmableUIController_ = new DimmableUIController(this.container_); - - this.thumbnailMode_ = new ThumbnailMode( - assertInstanceof(document.querySelector('.thumbnail-view'), HTMLElement), - this.errorBanner_, this.dataModel_, this.selectionModel_, - this.onThumbnailActivated_.bind(this)); - this.thumbnailMode_.hide(); - - this.slideMode_ = new SlideMode(this.container_, - content, - this.topToolbar_, - this.bottomToolbar_, - this.prompt_, - this.errorBanner_, - this.dataModel_, - this.selectionModel_, - this.metadataModel_, - this.thumbnailModel_, - this.context_, - this.volumeManager_, - this.toggleMode_.bind(this), - str, - this.dimmableUIController_); - - /** - * @private {!HTMLElement} - * @const - */ - this.deleteButton_ = queryRequiredElement('button.delete', this.topToolbar_); - GalleryUtil.decorateMouseFocusHandling(this.deleteButton_); - this.deleteButton_.addEventListener('click', this.delete_.bind(this)); - - /** - * @private {!HTMLElement} - * @const - */ - this.slideshowButton_ = queryRequiredElement( - 'button.slideshow', this.topToolbar_); - GalleryUtil.decorateMouseFocusHandling(this.slideshowButton_); - - /** - * @private {!HTMLElement} - * @const - */ - this.shareButton_ = queryRequiredElement('button.share', this.topToolbar_); - GalleryUtil.decorateMouseFocusHandling(this.shareButton_); - this.shareButton_.addEventListener( - 'click', this.onShareButtonClick_.bind(this)); - - this.dataModel_.addEventListener('splice', this.onSplice_.bind(this)); - this.dataModel_.addEventListener('content', this.onContentChange_.bind(this)); - - this.selectionModel_.addEventListener('change', this.onSelection_.bind(this)); - this.slideMode_.addEventListener('useraction', this.onUserAction_.bind(this)); - - // ----------------------------------------------------------------- - // Initialize listeners. - - this.keyDownBound_ = this.onKeyDown_.bind(this); - this.document_.body.addEventListener('keydown', this.keyDownBound_); - - // TODO(hirono): Add observer to handle thumbnail update. - this.volumeManager_.addEventListener( - 'externally-unmounted', this.onExternallyUnmountedBound_); - // The 'pagehide' event is called when the app window is closed. - window.addEventListener('pagehide', this.onPageHide_.bind(this)); - - window.addEventListener('resize', this.resizeRenameField_.bind(this)); - - // We must call this method after elements of all tools have been attached to - // the DOM. - this.dimmableUIController_.setTools(document.querySelectorAll('.tool')); - - /** - * @private {function(!Event)} - * @const - */ - this.onSubModeChangedBound_ = this.onSubModeChanged_.bind(this); - - chrome.accessibilityFeatures.largeCursor.onChange.addListener( - this.onGetOrChangedAccessibilityConfiguration_.bind( - this, 'large-cursor')); - chrome.accessibilityFeatures.largeCursor.get({}, - this.onGetOrChangedAccessibilityConfiguration_.bind( - this, 'large-cursor')); - - chrome.accessibilityFeatures.highContrast.onChange.addListener( - this.onGetOrChangedAccessibilityConfiguration_.bind( - this, 'high-contrast')); - chrome.accessibilityFeatures.highContrast.get({}, - this.onGetOrChangedAccessibilityConfiguration_.bind( - this, 'high-contrast')); -} - -/** - * First time tools fade-out timeout in milliseconds. - * @const {number} - * @private - */ -Gallery.FIRST_FADE_TIMEOUT_ = 1000; - -/** - * Time until mosaic is initialized in the background. Used to make gallery - * in the slide mode load faster. In milliseconds. - * @const {number} - * @private - */ -Gallery.MOSAIC_BACKGROUND_INIT_DELAY_ = 1000; - -/** - * Updates attributes of container element when accessibility configuration has - * been changed. - * @param {string} name - * @param {Object} details - * @private - */ -Gallery.prototype.onGetOrChangedAccessibilityConfiguration_ = function( - name, details) { - if (details.value) { - this.container_.setAttribute(name, true); - } else { - this.container_.removeAttribute(name); - } -}; - -/** - * Closes gallery when a volume containing the selected item is unmounted. - * @param {!Event} event The unmount event. - * @private - */ -Gallery.prototype.onExternallyUnmounted_ = function(event) { - if (!this.selectedEntry_) { - return; - } - - event = /** @type {!ExternallyUnmountedEvent} */ (event); - if (this.volumeManager_.getVolumeInfo(this.selectedEntry_) === event.detail) { - window.close(); - } -}; - -/** - * Unloads the Gallery. - * @private - */ -Gallery.prototype.onPageHide_ = function() { - this.volumeManager_.removeEventListener( - 'externally-unmounted', this.onExternallyUnmountedBound_); - this.volumeManager_.dispose(); -}; - -/** - * Loads the content. - * - * @param {!Array<!Entry>} selectedEntries Array of selected entries. - */ -Gallery.prototype.load = function(selectedEntries) { - GalleryUtil.createEntrySet(selectedEntries).then(function(allEntries) { - this.loadInternal_(allEntries, selectedEntries); - }.bind(this)); -}; - -/** - * Loads the content. - * - * @param {!Array<!FileEntry>} entries Array of entries. - * @param {!Array<!FileEntry>} selectedEntries Array of selected entries. - * @private - */ -Gallery.prototype.loadInternal_ = function(entries, selectedEntries) { - // Add the entries to data model. - let items = []; - for (let i = 0; i < entries.length; i++) { - const locationInfo = this.volumeManager_.getLocationInfo(entries[i]); - if (!locationInfo) { // Skip the item, since gone. - return; - } - items.push(new GalleryItem( - entries[i], - locationInfo, - null, - null, - true)); - } - this.dataModel_.splice(0, this.dataModel_.length); - this.updateThumbnails_(); // Remove the caches. - - GalleryDataModel.prototype.splice.apply( - this.dataModel_, [0, 0].concat(items)); - - // Apply the selection. - const selectedSet = {}; - for (let i = 0; i < selectedEntries.length; i++) { - selectedSet[selectedEntries[i].toURL()] = true; - } - for (let i = 0; i < items.length; i++) { - if (!selectedSet[items[i].getEntry().toURL()]) { - continue; - } - this.selectionModel_.setIndexSelected(i, true); - } - this.onSelection_(); - - // If items are empty, stop initialization. - if (items.length === 0) { - this.dataModel_.splice(0, this.dataModel_.length); - return; - } - - // Sort the selected image first - const containsInSelection = function(galleryItem) { - return selectedEntries.indexOf(galleryItem.getEntry()) >= 0; - }; - const notContainsInSelection = function(galleryItem) { - return !containsInSelection(galleryItem); - }; - items = items.filter(containsInSelection) - .concat(items.filter(notContainsInSelection)); - - // Load entries. - // Use the self variable capture-by-closure because it is faster than bind. - const self = this; - const thumbnailModel = new ThumbnailModel(this.metadataModel_); - const loadNext = function(index) { - // Extract chunk. - if (index >= items.length) { - return; - } - const item = items[index]; - const entry = item.getEntry(); - const metadataPromise = - self.metadataModel_.get([entry], GalleryItem.PREFETCH_PROPERTY_NAMES); - const thumbnailPromise = thumbnailModel.get([entry]); - return Promise.all([metadataPromise, thumbnailPromise]).then( - function(metadataLists) { - // Add items to the model. - item.setMetadataItem(metadataLists[0][0]); - item.setThumbnailMetadataItem(metadataLists[1][0]); - - const event = new Event('content'); - event.item = item; - event.oldEntry = entry; - event.thumbnailChanged = true; - self.dataModel_.dispatchEvent(event); - - // Continue to load chunks. - return loadNext(/* index */ index + 1); - }); - }; - // init modes before loading images. - if (!this.initialized_) { - // Determine the initial mode. - const shouldShowThumbnail = selectedEntries.length > 1 || - (this.context_.pageState && - this.context_.pageState.gallery === 'thumbnail'); - this.setCurrentMode_( - shouldShowThumbnail ? this.thumbnailMode_ : this.slideMode_); - - // Do the initialization for each mode. - if (shouldShowThumbnail) { - this.thumbnailMode_.show(); - this.thumbnailMode_.focus(); - } else { - this.slideMode_.enter( - null, - function() { - // Flash the toolbar briefly to show it is there. - self.dimmableUIController_.kick(Gallery.FIRST_FADE_TIMEOUT_); - }, - function() {}); - } - this.initialized_ = true; - } - loadNext(/* index */ 0).catch(function(error) { - console.error(error.stack || error); - }); -}; - -/** - * @return {boolean} True if some tool is currently active. - */ -Gallery.prototype.hasActiveTool = function() { - return (this.currentMode_ && this.currentMode_.hasActiveTool()) || - this.isRenaming_(); -}; - -/** -* External user action event handler. -* @private -*/ -Gallery.prototype.onUserAction_ = function() { - // Show the toolbar and hide it after the default timeout. - this.dimmableUIController_.kick(); -}; - -/** - * Returns the current mode. - * @return {GalleryMode} - */ -Gallery.prototype.getCurrentMode = function() { - switch (/** @type {(SlideMode|ThumbnailMode)} */ (this.currentMode_)) { - case this.slideMode_: - return GalleryMode.SLIDE; - case this.thumbnailMode_: - return GalleryMode.THUMBNAIL; - default: - assertNotReached(); - } -}; - -/** - * Returns sub mode of current mode. If current mode is not set yet, null is - * returned. - * @return {GallerySubMode} - */ -Gallery.prototype.getCurrentSubMode = function() { - assert(this.currentMode_); - return this.currentMode_.getSubMode(); -}; - -/** - * Sets the current mode, update the UI. - * @param {!(SlideMode|ThumbnailMode)} mode Current mode. - * @private - */ -Gallery.prototype.setCurrentMode_ = function(mode) { - if (mode !== this.slideMode_ && mode !== this.thumbnailMode_) { - console.error('Invalid Gallery mode'); - } - - if (this.currentMode_) { - this.currentMode_.removeEventListener( - 'sub-mode-change', this.onSubModeChangedBound_); - } - this.currentMode_ = mode; - this.currentMode_.addEventListener( - 'sub-mode-change', this.onSubModeChangedBound_); - - this.dimmableUIController_.setCurrentMode( - this.getCurrentMode(), this.getCurrentSubMode()); - - this.container_.setAttribute('mode', this.currentMode_.getName()); - this.updateSelectionAndState_(); - this.updateModeButtonAttribute_(); -}; - -/** - * Handles sub-mode-change event. - * @private - */ -Gallery.prototype.onSubModeChanged_ = function() { - this.dimmableUIController_.setCurrentMode( - this.getCurrentMode(), this.getCurrentSubMode()); -}; - -/** - * Handles click event of mode switch button. - * @param {!Event} event An event. - * @private - */ -Gallery.prototype.onModeSwitchButtonClicked_ = function(event) { - this.toggleMode_(undefined /* callback */, event); -}; - -/** - * Callback from ThumbnailMode: changes to slide mode, possibly autoplaying the - * selected item. - * @private - */ -Gallery.prototype.onThumbnailActivated_ = function() { - if (this.modeSwitchButton_.disabled) { - return; - } - - this.changeCurrentMode_(this.slideMode_, true /* activate */); -}; - -/** - * Update attributes of slide/thumbnail toggle button - * @private - */ -Gallery.prototype.updateModeButtonAttribute_ = function() { - if (this.currentMode_ === this.slideMode_) { - this.modeSwitchButton_.setAttribute('aria-label', str('GALLERY_THUMBNAIL')); - } else { - this.modeSwitchButton_.setAttribute('aria-label', str('GALLERY_SLIDE')); - } -}; - -/** - * Change current mode. - * @param {!(SlideMode|ThumbnailMode)} mode Target mode. - * @param {boolean} activate Whether to activate a selected item (if any). - * @param {Event=} opt_event Event that caused this call. - * @return {!Promise} Resolved when mode has been changed. - * @private - */ -Gallery.prototype.changeCurrentMode_ = function(mode, activate, opt_event) { - return new Promise(function(fulfill, reject) { - // Do not re-enter while changing the mode. - if (this.currentMode_ === mode || this.changingMode_) { - fulfill(); - return; - } - - if (opt_event) { - this.onUserAction_(); - } - - this.changingMode_ = true; - - const onModeChanged = function() { - this.changingMode_ = false; - fulfill(); - }.bind(this); - - const thumbnailIndex = Math.max(0, this.selectionModel_.selectedIndex); - const thumbnailRect = ImageRect.createFromBounds( - this.thumbnailMode_.getThumbnailRect(thumbnailIndex)); - - if (mode === this.thumbnailMode_) { - this.setCurrentMode_(this.thumbnailMode_); - this.slideMode_.leave( - thumbnailRect, - function() { - // Show thumbnail mode and perform animation. - this.thumbnailMode_.show(); - const fromRect = this.slideMode_.getSelectedImageRect(); - if (fromRect) { - this.thumbnailMode_.performEnterAnimation( - thumbnailIndex, fromRect); - } - this.thumbnailMode_.focus(); - - onModeChanged(); - }.bind(this)); - this.bottomToolbar_.hidden = true; - } else { - this.setCurrentMode_(this.slideMode_); - this.slideMode_.enter( - thumbnailRect, - function() { - // Animate to zoomed position. - this.thumbnailMode_.hide(); - if (activate) { - this.slideMode_.activateContent(); - } - }.bind(this), - onModeChanged); - this.bottomToolbar_.hidden = false; - } - }.bind(this)); -}; - -/** - * Mode toggle event handler. - * @param {function()=} opt_callback Callback. - * @param {Event=} opt_event Event that caused this call. - * @private - */ -Gallery.prototype.toggleMode_ = function(opt_callback, opt_event) { - // If it's in editing, leave edit mode. - if (this.slideMode_.isEditing()) { - this.slideMode_.toggleEditor(); - } - - const targetMode = this.currentMode_ === this.slideMode_ ? - this.thumbnailMode_ : - this.slideMode_; - - const activate = false; - this.changeCurrentMode_(targetMode, activate, opt_event).then(function() { - if (opt_callback) { - opt_callback(); - } - }); -}; - -/** - * Deletes the selected items. - * @private - */ -Gallery.prototype.delete_ = function() { - this.onUserAction_(); - - // Clone the sorted selected indexes array. - const indexesToRemove = this.selectionModel_.selectedIndexes.slice(); - if (!indexesToRemove.length) { - return; - } - - /* TODO(dgozman): Implement Undo delete, Remove the confirmation dialog. */ - - const itemsToRemove = this.getSelectedItems(); - const plural = itemsToRemove.length > 1; - const param = plural ? itemsToRemove.length : itemsToRemove[0].getFileName(); - - function deleteNext() { - if (!itemsToRemove.length) { - return; // All deleted. - } - - const entry = itemsToRemove.pop().getEntry(); - entry.remove(deleteNext, function() { - console.error('Error deleting: ' + entry.name); - deleteNext(); - }); - } - - // Prevent the Gallery from handling Esc and Enter. - this.document_.body.removeEventListener('keydown', this.keyDownBound_); - const restoreListener = function() { - this.document_.body.addEventListener('keydown', this.keyDownBound_); - }.bind(this); - - const confirm = new FilesConfirmDialog(this.container_); - confirm.setOkLabel(str('DELETE_BUTTON_LABEL')); - confirm.show(strf(plural ? - 'GALLERY_CONFIRM_DELETE_SOME' : 'GALLERY_CONFIRM_DELETE_ONE', param), - function() { - restoreListener(); - this.selectionModel_.unselectAll(); - this.selectionModel_.leadIndex = -1; - // Remove items from the data model, starting from the highest index. - while (indexesToRemove.length) { - this.dataModel_.splice(indexesToRemove.pop(), 1); - } - // Delete actual files. - deleteNext(); - }.bind(this), - function() { - // Restore the listener after a timeout so that ESC is processed. - setTimeout(restoreListener, 0); - }, - null); -}; - -/** - * @return {!Array<GalleryItem>} Current selection. - */ -Gallery.prototype.getSelectedItems = function() { - return this.selectionModel_.selectedIndexes.map( - this.dataModel_.item.bind(this.dataModel_)); -}; - -/** - * @return {!Array<Entry>} Array of currently selected entries. - */ -Gallery.prototype.getSelectedEntries = function() { - return this.selectionModel_.selectedIndexes.map(function(index) { - return this.dataModel_.item(index).getEntry(); - }.bind(this)); -}; - -/** - * @return {?GalleryItem} Current single selection. - */ -Gallery.prototype.getSingleSelectedItem = function() { - const items = this.getSelectedItems(); - if (items.length > 1) { - console.error('Unexpected multiple selection'); - return null; - } - return items[0]; -}; - -/** - * Selection change event handler. - * @private - */ -Gallery.prototype.onSelection_ = function() { - this.updateSelectionAndState_(); -}; - -/** - * Data model splice event handler. - * @private - */ -Gallery.prototype.onSplice_ = function() { - this.selectionModel_.adjustLength(this.dataModel_.length); - this.selectionModel_.selectedIndexes = - this.selectionModel_.selectedIndexes.filter(function(index) { - return 0 <= index && index < this.dataModel_.length; - }.bind(this)); - - // Disable mode switch button if there is no image. - this.modeSwitchButton_.disabled = this.dataModel_.length === 0; -}; - -/** - * Content change event handler. - * @param {!Event} event Event. - * @private -*/ -Gallery.prototype.onContentChange_ = function(event) { - this.updateSelectionAndState_(); -}; - -/** - * Keydown handler. - * - * @param {!Event} event - * @private - */ -Gallery.prototype.onKeyDown_ = function(event) { - const keyString = util.getKeyModifiers(event) + event.key; - - // Handle debug shortcut keys. - switch (keyString) { - case 'Ctrl-Shift-I': // Ctrl+Shift+I - chrome.fileManagerPrivate.openInspector( - chrome.fileManagerPrivate.InspectionType.NORMAL); - break; - case 'Ctrl-Shift-J': // Ctrl+Shift+J - chrome.fileManagerPrivate.openInspector( - chrome.fileManagerPrivate.InspectionType.CONSOLE); - break; - case 'Ctrl-Shift-C': // Ctrl+Shift+C - chrome.fileManagerPrivate.openInspector( - chrome.fileManagerPrivate.InspectionType.ELEMENT); - break; - case 'Ctrl-Shift-B': // Ctrl+Shift+B - chrome.fileManagerPrivate.openInspector( - chrome.fileManagerPrivate.InspectionType.BACKGROUND); - break; - } - - // Show UIs when user types any key. - this.dimmableUIController_.kick(); - - // Handle mode specific shortcut keys. - if (this.currentMode_.onKeyDown(event)) { - event.preventDefault(); - return; - } - - // Handle application wide shortcut keys. - switch (keyString) { - case 'Backspace': - // The default handler would call history.back and close the Gallery. - // Except while typing into text. - if (!event.target.classList.contains('text')) { - event.preventDefault(); - } - break; - - case 'm': // 'm' switches between Slide and Mosaic mode. - if (!this.modeSwitchButton_.disabled) { - this.toggleMode_(undefined, event); - } - break; - - case 'v': - case 'MediaPlayPause': - if (!this.slideshowButton_.disabled) { - this.slideMode_.startSlideshow( - SlideMode.SLIDESHOW_INTERVAL_FIRST, event); - } - break; - - case 'Delete': - case 'Shift-3': // Shift+'3' (Delete key might be missing). - case 'd': - if (!this.deleteButton_.disabled) { - this.delete_(); - } - break; - - case 'Escape': - case 'BrowserBack': - window.close(); - break; - } -}; - -// Name box and rename support. - -/** - * Updates the UI related to the selected item and the persistent state. - * - * @private - */ -Gallery.prototype.updateSelectionAndState_ = function() { - const numSelectedItems = this.selectionModel_.selectedIndexes.length; - let selectedEntryURL = null; - - // If it's selecting something, update the variable values. - if (numSelectedItems) { - // Enable slideshow button. - this.slideshowButton_.disabled = false; - - // Delete button is available when all images are NOT readOnly. - this.deleteButton_.disabled = !this.selectionModel_.selectedIndexes - .every(function(i) { - return !this.dataModel_.item(i).getLocationInfo().isReadOnly; - }, this); - - // Obtains selected item. - const selectedItem = - this.dataModel_.item(this.selectionModel_.selectedIndex); - this.selectedEntry_ = selectedItem.getEntry(); - selectedEntryURL = this.selectedEntry_.toURL(); - - // Update cache. - selectedItem.touch(); - this.dataModel_.evictCache(); - - // Filename Edit field shows for anything selected. - this.filenameEdit_.hidden = false; - - // Update the title and the display name. - if (numSelectedItems === 1) { - document.title = this.selectedEntry_.name; - this.filenameEdit_.disabled = selectedItem.getLocationInfo().isReadOnly; - this.filenameEdit_.value = - ImageUtil.getDisplayNameFromName(this.selectedEntry_.name); - this.resizeRenameField_(); - - this.shareButton_.disabled = !selectedItem.getLocationInfo().isDriveBased; - } else { - if (this.context_.curDirEntry) { - // If the Gallery was opened on search results the search query will not - // be recorded in the app state and the relaunch will just open the - // gallery in the curDirEntry directory. - document.title = this.context_.curDirEntry.name; - } else { - document.title = ''; - } - this.filenameEdit_.disabled = true; - this.filenameEdit_.value = - strf('GALLERY_ITEMS_SELECTED', numSelectedItems); - this.resizeRenameField_(); - - this.shareButton_.disabled = true; - } - } else { - document.title = ''; - this.filenameEdit_.hidden = true; - this.filenameEdit_.disabled = true; - this.filenameEdit_.value = ''; - this.resizeRenameField_(); - - this.deleteButton_.disabled = true; - this.slideshowButton_.disabled = true; - this.shareButton_.disabled = true; - } - - appUtil.updateAppState( - null, // Keep the current directory. - selectedEntryURL, // Update the selection. - { - gallery: - (this.currentMode_ === this.thumbnailMode_ ? 'thumbnail' : 'slide') - }); -}; - -/** - * Click event handler on filename edit box - * @private - */ -Gallery.prototype.onFilenameFocus_ = function() { - ImageUtil.setAttribute(this.filenameSpacer_, 'renaming', true); - this.dimmableUIController_.setRenaming(true); - - this.filenameEdit_.originalValue = this.filenameEdit_.value; - setTimeout(this.filenameEdit_.select.bind(this.filenameEdit_), 0); - this.onUserAction_(); -}; - -/** - * Blur event handler on filename edit box. - * - * @param {!Event} event Blur event. - * @private - */ -Gallery.prototype.onFilenameEditBlur_ = function(event) { - const item = this.getSingleSelectedItem(); - if (item) { - const oldEntry = item.getEntry(); - - item.rename(this.filenameEdit_.value) - .then( - function() { - const event = new Event('content'); - event.item = item; - event.oldEntry = oldEntry; - event.thumbnailChanged = false; - this.dataModel_.dispatchEvent(event); - }.bind(this), - function(error) { - if (error === 'NOT_CHANGED') { - return Promise.resolve(); - } - this.filenameEdit_.value = - ImageUtil.getDisplayNameFromName(item.getEntry().name); - this.resizeRenameField_(); - this.filenameEdit_.focus(); - if (typeof error === 'string') { - this.prompt_.showStringAt('center', error, 5000); - } else { - return Promise.reject(error); - } - }.bind(this)) - .catch(function(error) { - console.error(error.stack || error); - }); - } - - ImageUtil.setAttribute(this.filenameSpacer_, 'renaming', false); - this.dimmableUIController_.setRenaming(false); - this.onUserAction_(); -}; - -/** - * Minimum width of rename field. - * @const {number} - * @private - */ -Gallery.MIN_WIDTH_RENAME_FIELD_ = 160; // px - -/** - * End padding for rename field. - * @const {number} - * @private - */ -Gallery.END_PADDING_RENAME_FIELD_ = 20; // px - -/** - * Resize rename field depending on its content. - * @private - */ -Gallery.prototype.resizeRenameField_ = function() { - const size = - this.filenameCanvasContext_.measureText(this.filenameEdit_.value); - - const width = Math.min( - Math.max( - size.width + Gallery.END_PADDING_RENAME_FIELD_, - Gallery.MIN_WIDTH_RENAME_FIELD_), - window.innerWidth / 2); - - this.filenameEdit_.style.width = width + 'px'; -}; - -/** - * Keydown event handler on filename edit box - * @param {!Event} event A keyboard event. - * @private - */ -Gallery.prototype.onFilenameEditKeydown_ = function(event) { - event = assertInstanceof(event, KeyboardEvent); - switch (event.keyCode) { - case 27: // Escape - this.filenameEdit_.value = this.filenameEdit_.originalValue; - this.resizeRenameField_(); - this.filenameEdit_.blur(); - break; - - case 13: // Enter - this.filenameEdit_.blur(); - break; - } - event.stopPropagation(); -}; - -/** - * @return {boolean} True if file renaming is currently in progress. - * @private - */ -Gallery.prototype.isRenaming_ = function() { - return this.filenameSpacer_.hasAttribute('renaming'); -}; - -/** - * Content area click handler. - * @private - */ -Gallery.prototype.onContentClick_ = function() { - this.filenameEdit_.blur(); -}; - -/** - * Share button handler. - * @private - */ -Gallery.prototype.onShareButtonClick_ = function() { - const item = this.getSingleSelectedItem(); - if (!item) { - return; - } - chrome.fileManagerPrivate.getEntryProperties( - [item.getEntry()], ['shareUrl'], results => { - if (chrome.runtime.lastError) { - console.error(chrome.runtime.lastError.message); - return; - } - if (results.length != 1) { - console.error( - 'getEntryProperties for shareUrl should return 1 entry ' + - '(returned ' + results.length + ')'); - return; - } - if (results[0].shareUrl === undefined) { - console.error('getEntryProperties shareUrl is undefined'); - return; - } - util.visitURL(assert(results[0].shareUrl)); - }); -}; - -/** - * Updates thumbnails. - * @private - */ -Gallery.prototype.updateThumbnails_ = function() { - if (this.currentMode_ === this.slideMode_) { - this.slideMode_.updateThumbnails(); - } -}; - -/** - * Singleton gallery. - * @type {Gallery} - */ -let gallery = null; - -/** - * (Re-)loads entries. - */ -function reload() { - initializePromise.then(function() { - util.URLsToEntries(window.appState.urls, function(entries) { - gallery.load(entries); - }); - }); -} - -/** - * Promise to initialize the load time data. - * @type {!Promise} - */ -const loadTimeDataPromise = new Promise(function(fulfill, reject) { - chrome.fileManagerPrivate.getStrings(function(strings) { - window.loadTimeData.data = strings; - fulfill(true); - }); -}); - -/** - * Promise to initialize volume manager. - * @type {!Promise} - */ -const volumeManagerPromise = new Promise(function(fulfill, reject) { - const volumeManager = new FilteredVolumeManager( - AllowedPaths.ANY_PATH, false, appUtil.getVolumeManager()); - volumeManager.ensureInitialized(fulfill.bind(null, volumeManager)); -}); - -/** - * Promise to initialize both the volume manager and the load time data, and - * then create the gallery. - * @type {Promise} - */ -let initializePromise = null; - -/** - * Initializes the gallery: setup the gallery |initializePromise| and invoke - * it to create the gallery. Calls reload() to populate the gallery entries. - */ -function initializeGallery() { - const htmlImportsPromise = new Promise(resolve => { - window.HTMLImports.whenReady(resolve); - }); - const promise = htmlImportsPromise.then(() => { - return Promise.all([loadTimeDataPromise, volumeManagerPromise]); - }); - - /** - * Define the initializePromise, which runs |promise| and then creates the - * Gallery. Define that as a 'createGallery' function here so that name is - * shown in the error stack if .catch((error)) fires. - */ - initializePromise = promise.then(function createGallery(results) { - const isReady = window.document.readyState !== 'loading'; - assert(isReady, 'Gallery DOM document is still loading'); - i18nTemplate.process(window.document, window.loadTimeData); - const volumeManager = results[1]; - gallery = new Gallery(volumeManager); - window.gallery = gallery; // for debug. - }).catch((error) => { - console.error('gallery ' + (error.stack ? error.stack : error)); - }); - - /** - * Initialize the gallery, and reload its entries. Then expose reload() on - * the global window (for background page use). - */ - initializePromise.then(reload).then(() => { - window.reload = reload; // can be called from background page. - }); -} - -/** - * Ensure the gallery.html DOM is loaded before attempting to initialize the - * gallery from script: crbug.com/882606 - */ -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initializeGallery); -} else { - initializeGallery(); -}
diff --git a/ui/file_manager/gallery/js/gallery_constants.js b/ui/file_manager/gallery/js/gallery_constants.js deleted file mode 100644 index a57813f..0000000 --- a/ui/file_manager/gallery/js/gallery_constants.js +++ /dev/null
@@ -1,16 +0,0 @@ -// 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. - -/** - * Modes in Gallery. - * @enum {string} - */ -var GalleryMode = {SLIDE: 'slide', THUMBNAIL: 'thumbnail'}; - -/** - * Sub modes in Gallery. - * @enum {string} - * TODO(yawano): Remove sub modes by extracting them as modes. - */ -var GallerySubMode = {BROWSE: 'browse', EDIT: 'edit', SLIDESHOW: 'slideshow'};
diff --git a/ui/file_manager/gallery/js/gallery_data_model.js b/ui/file_manager/gallery/js/gallery_data_model.js deleted file mode 100644 index da8e42e8..0000000 --- a/ui/file_manager/gallery/js/gallery_data_model.js +++ /dev/null
@@ -1,148 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * Data model for gallery. - */ -class GalleryDataModel extends cr.ui.ArrayDataModel { - /** - * @param {!MetadataModel} metadataModel - * @param {!EntryListWatcher=} opt_watcher Entry list watcher. - */ - constructor(metadataModel, opt_watcher) { - super([]); - - /** - * File system metadata. - * @private {!MetadataModel} - * @const - */ - this.metadataModel_ = metadataModel; - - /** - * Directory where the image is saved if the image is located in a read-only - * volume. - * @public {DirectoryEntry} - */ - this.fallbackSaveDirectory = null; - - // Start to watch file system entries. - var watcher = opt_watcher ? opt_watcher : new EntryListWatcher(this); - watcher.getEntry = function(item) { - return item.getEntry(); - }; - - this.addEventListener('splice', this.onSplice_.bind(this)); - } - - /** - * Saves new image. - * - * @param {!VolumeManager} volumeManager Volume manager instance. - * @param {!GalleryItem} item Original gallery item. - * @param {!HTMLCanvasElement} canvas Canvas containing new image. - * @param {boolean} overwrite Set true to overwrite original if it's possible. - * @return {!Promise} Promise to be fulfilled with when the operation - * completes. - */ - saveItem(volumeManager, item, canvas, overwrite) { - var oldEntry = item.getEntry(); - var oldLocationInfo = item.getLocationInfo(); - var oldIsOriginal = item.isOriginal(); - return new Promise(function(fulfill, reject) { - item.saveToFile( - volumeManager, this.metadataModel_, - assert(this.fallbackSaveDirectory), canvas, overwrite, - function(success) { - if (!success) { - reject('Failed to save the image.'); - return; - } - - // Current entry is updated. - // Dispatch an event. - var event = new Event('content'); - event.item = item; - event.oldEntry = oldEntry; - event.thumbnailChanged = true; - this.dispatchEvent(event); - - if (!util.isSameEntry(oldEntry, item.getEntry())) { - Promise - .all([ - this.metadataModel_.get( - [oldEntry], GalleryItem.PREFETCH_PROPERTY_NAMES), - new ThumbnailModel(this.metadataModel_).get([oldEntry]) - ]) - .then(function(itemLists) { - // New entry is added and the item now tracks it. - // Add another item for the old entry. - var anotherItem = new GalleryItem( - oldEntry, oldLocationInfo, itemLists[0][0], - itemLists[1][0], oldIsOriginal); - // The item must be added behind the existing item so that - // it does not change the index of the existing item. - // TODO(hirono): Update the item index of the selection - // model correctly. - this.splice(this.indexOf(item) + 1, 0, anotherItem); - }.bind(this)) - .then(fulfill, reject); - } else { - fulfill(); - } - }.bind(this)); - }.bind(this)); - } - - /** - * Evicts image caches in the items. - */ - evictCache() { - // Sort the item by the last accessed date. - var sorted = this.slice().sort(function(a, b) { - return b.getLastAccessedDate() - a.getLastAccessedDate(); - }); - - // Evict caches. - var contentCacheCount = 0; - var screenCacheCount = 0; - for (var i = 0; i < sorted.length; i++) { - if (sorted[i].contentImage) { - if (++contentCacheCount > GalleryDataModel.MAX_FULL_IMAGE_CACHE_) { - if (sorted[i].contentImage.parentNode) { - console.error('The content image has a parent node.'); - } else { - // Force to free the buffer of the canvas by assigning zero size. - sorted[i].contentImage.width = 0; - sorted[i].contentImage.height = 0; - sorted[i].contentImage = null; - } - } - } - } - } - - /** - * Handles entry delete. - * @param {!Event} event - * @private - */ - onSplice_(event) { - if (!event.removed || !event.removed.length) { - return; - } - var removedURLs = event.removed.map(function(item) { - return item.getEntry().toURL(); - }); - this.metadataModel_.notifyEntriesRemoved(removedURLs); - } -} - -/** - * Maximum number of full size image cache. - * @type {number} - * @const - * @private - */ -GalleryDataModel.MAX_FULL_IMAGE_CACHE_ = 3;
diff --git a/ui/file_manager/gallery/js/gallery_data_model_unittest.js b/ui/file_manager/gallery/js/gallery_data_model_unittest.js deleted file mode 100644 index 5a13304..0000000 --- a/ui/file_manager/gallery/js/gallery_data_model_unittest.js +++ /dev/null
@@ -1,77 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -var model; -var fileSystem; -var item; - -function setUp() { - // Avoid creating the default EntryListWatcher, since it needs access to - // fileManagerPrivate, which is not in the unit test environment. - let mockEntryListWatcher = /** @type{!EntryListWatcher} */ ({}); - model = new GalleryDataModel(new MockMetadataModel({}), mockEntryListWatcher); - - fileSystem = new MockFileSystem('volumeId'); - model.fallbackSaveDirectory = fileSystem.root; -} - -function testSaveItemOverwrite(callback) { - var item = new MockGalleryItem( - MockFileEntry.create(fileSystem, '/test.jpg'), null, {}, null, - false /* isOriginal */); - - // Mocking the saveToFile method. - item.saveToFile = function( - volumeManager, - metadataModel, - fallbackDir, - canvas, - overwrite, - callback) { - callback(true); - }; - model.push(item); - reportPromise( - model - .saveItem( - {}, item, document.createElement('canvas'), true /* overwrite */) - .then(function() { - assertEquals(1, model.length); - }), - callback); -} - -function testSaveItemToNewFile(callback) { - var item = new MockGalleryItem( - MockFileEntry.create(fileSystem, '/test.webp'), null, {}, null, - true /* isOriginal */); - - // Mocking the saveToFile method. In this case, Gallery saves to a new file - // since it cannot overwrite to webp image file. - /** @suppress {accessControls} */ - item.saveToFile = function( - volumeManager, - metadataModel, - fallbackDir, - canvas, - overwrite, - callback) { - // Gallery item track new file. - item.entry_ = MockFileEntry.create(fileSystem, '/test (1).png'); - item.original_ = false; - callback(true); - }; - model.push(item); - reportPromise( - model.saveItem({}, item, document.createElement('canvas'), - false /* not overwrite */). - then(function() { - assertEquals(2, model.length); - assertEquals('test (1).png', model.item(0).getFileName()); - assertFalse(model.item(0).isOriginal()); - assertEquals('test.webp', model.item(1).getFileName()); - assertTrue(model.item(1).isOriginal()); - }), - callback); -}
diff --git a/ui/file_manager/gallery/js/gallery_item.js b/ui/file_manager/gallery/js/gallery_item.js deleted file mode 100644 index d1e9892..0000000 --- a/ui/file_manager/gallery/js/gallery_item.js +++ /dev/null
@@ -1,489 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * Object representing an image item (a photo). - * - * @param {!FileEntry} entry Image entry. - * @param {!EntryLocation} locationInfo Entry location information. - * @param {MetadataItem} metadataItem - * @param {ThumbnailMetadataItem} thumbnailMetadataItem - * @param {boolean} original Whether the entry is original or edited. - * @constructor - * @struct - */ -function GalleryItem( - entry, locationInfo, metadataItem, thumbnailMetadataItem, original) { - /** - * @private {!FileEntry} - */ - this.entry_ = entry; - - /** - * @private {!EntryLocation} - */ - this.locationInfo_ = locationInfo; - - /** - * @private {MetadataItem} - */ - this.metadataItem_ = metadataItem; - - /** - * @private {ThumbnailMetadataItem} - */ - this.thumbnailMetadataItem_ = metadataItem; - - // TODO(yawano): Change this.contentImage and this.screenImage to private - // fields and provide utility methods for them (e.g. revokeFullImageCache). - /** - * The content cache is used for prefetching the next image when going through - * the images sequentially. The real life photos can be large (18Mpix = 72Mb - * pixel array) so we want only the minimum amount of caching. - * @type {(HTMLCanvasElement|HTMLImageElement)} - */ - this.contentImage = null; - - /** - * Last accessed date to be used for selecting items whose cache are evicted. - * @type {number} - * @private - */ - this.lastAccessed_ = Date.now(); - - /** - * @type {boolean} - * @private - */ - this.original_ = original; -} - -/** - * Types of metadata Gallery uses (to query the metadata cache). - * @const - * @type {!Array<string>} - */ -GalleryItem.PREFETCH_PROPERTY_NAMES = - ['imageWidth', 'imageHeight', 'imageRotation', 'size', 'present']; - -/** - * @return {!FileEntry} Image entry. - */ -GalleryItem.prototype.getEntry = function() { - return this.entry_; -}; - -/** - * @return {!EntryLocation} Entry location information. - */ -GalleryItem.prototype.getLocationInfo = function() { - return this.locationInfo_; -}; - -/** - * @return {MetadataItem} Metadata. - */ -GalleryItem.prototype.getMetadataItem = function() { - return this.metadataItem_; -}; - -/** - * @param {!MetadataItem} metadata - */ -GalleryItem.prototype.setMetadataItem = function(metadata) { - this.metadataItem_ = metadata; -}; - -/** - * @return {ThumbnailMetadataItem} Thumbnail metadata item. - */ -GalleryItem.prototype.getThumbnailMetadataItem = function() { - return this.thumbnailMetadataItem_; -}; - -/** - * @param {!ThumbnailMetadataItem} item Thumbnail metadata item. - */ -GalleryItem.prototype.setThumbnailMetadataItem = function(item) { - this.thumbnailMetadataItem_ = item; -}; - -/** - * @return {string} File name. - */ -GalleryItem.prototype.getFileName = function() { - return this.entry_.name; -}; - -/** - * @return {boolean} True if this image has not been created in this session. - */ -GalleryItem.prototype.isOriginal = function() { - return this.original_; -}; - -/** - * Sets an item as original. - */ -GalleryItem.prototype.setAsOriginal = function() { - this.original_ = true; -}; - -/** - * Obtains the last accessed date. - * @return {number} Last accessed date. - */ -GalleryItem.prototype.getLastAccessedDate = function() { - return this.lastAccessed_; -}; - -/** - * Updates the last accessed date. - */ -GalleryItem.prototype.touch = function() { - this.lastAccessed_ = Date.now(); -}; - -// TODO: Localize? -/** - * @type {string} Suffix for a edited copy file name. - * @const - */ -GalleryItem.COPY_SIGNATURE = ' - Edited'; - -/** - * Regular expression to match '... - Edited'. - * @type {!RegExp} - * @const - */ -GalleryItem.REGEXP_COPY_0 = - new RegExp('^(.+)' + GalleryItem.COPY_SIGNATURE + '$'); - -/** - * Regular expression to match '... - Edited (N)'. - * @type {!RegExp} - * @const - */ -GalleryItem.REGEXP_COPY_N = - new RegExp('^(.+)' + GalleryItem.COPY_SIGNATURE + ' \\((\\d+)\\)$'); - -/** - * Creates a name for an edited copy of the file. - * - * @param {!DirectoryEntry} dirEntry Entry. - * @param {string} newMimeType Mime type of new image. - * @param {function(string)} callback Callback. - * @private - */ -GalleryItem.prototype.createCopyName_ = function( - dirEntry, newMimeType, callback) { - var name = this.getFileName(); - - // If the item represents a file created during the current Gallery session - // we reuse it for subsequent saves instead of creating multiple copies. - if (!this.original_) { - callback(name); - return; - } - - var baseName = name.replace(/\.[^\.\/]+$/, ''); - var ext = newMimeType === 'image/jpeg' ? '.jpg' : '.png'; - - function tryNext(tries) { - // All the names are used. Let's overwrite the last one. - if (tries == 0) { - setTimeout(callback, 0, baseName + ext); - return; - } - - // If the file name contains the copy signature add/advance the sequential - // number. - var matchN = GalleryItem.REGEXP_COPY_N.exec(baseName); - var match0 = GalleryItem.REGEXP_COPY_0.exec(baseName); - if (matchN && matchN[1] && matchN[2]) { - var copyNumber = parseInt(matchN[2], 10) + 1; - baseName = matchN[1] + GalleryItem.COPY_SIGNATURE + - ' (' + copyNumber + ')'; - } else if (match0 && match0[1]) { - baseName = match0[1] + GalleryItem.COPY_SIGNATURE + ' (1)'; - } else { - baseName += GalleryItem.COPY_SIGNATURE; - } - - dirEntry.getFile(baseName + ext, {create: false, exclusive: false}, - tryNext.bind(null, tries - 1), - callback.bind(null, baseName + ext)); - } - - tryNext(10); -}; - -/** - * @return {boolean} True if this item can be edited by the ImageEditor. - */ -GalleryItem.prototype.isEditable = function() { - return FileType.isImage(this.entry_) || FileType.isRaw(this.entry_); -}; - -/** - * Returns true if the original format is writable format of Gallery. - * @return {boolean} True if the original format is writable format. - */ -GalleryItem.prototype.isWritableFormat = function() { - var type = FileType.getType(this.entry_); - return type.type === 'image' && - (type.subtype === 'JPEG' || type.subtype === 'PNG'); -}; - -/** - * Returns true if the entry of item is writable. - * @param {!VolumeManager} volumeManager Volume manager. - * @return {boolean} True if the entry of item is writable. - */ -GalleryItem.prototype.isWritableFile = function(volumeManager) { - return this.isWritableFormat() && - !this.locationInfo_.isReadOnly && - !GalleryUtil.isOnMTPVolume(this.entry_, volumeManager); -}; - -/** - * Returns mime type for saving an edit of this item. - * @return {string} Mime type. - * @private - */ -GalleryItem.prototype.getNewMimeType_ = function() { - return this.getFileName().match(/\.jpe?g$/i) || FileType.isRaw(this.entry_) ? - 'image/jpeg' : 'image/png'; -}; - -/** - * Return copy name of this item. - * @param {!DirectoryEntry} dirEntry Parent directory entry of copied item. - * @return {!Promise<string>} A promise which will be fulfilled with copy name. - */ -GalleryItem.prototype.getCopyName = function(dirEntry) { - return new Promise(this.createCopyName_.bind( - this, dirEntry, this.getNewMimeType_())); -}; - -/** - * Writes the new item content to either the existing or a new file. - * - * @param {!VolumeManager} volumeManager Volume manager instance. - * @param {!MetadataModel} metadataModel - * @param {!DirectoryEntry} fallbackDir Fallback directory in case the current - * directory is read only. - * @param {!HTMLCanvasElement} canvas Source canvas. - * @param {boolean} overwrite Set true to overwrite original if it's possible. - * @param {function(boolean)} callback Callback accepting true for success. - */ -GalleryItem.prototype.saveToFile = function( - volumeManager, metadataModel, fallbackDir, canvas, overwrite, callback) { - metrics.startInterval(ImageUtil.getMetricName('SaveTime')); - var saveResultRecorded = false; - - Promise.all([this.getEntryToWrite_(overwrite, fallbackDir, volumeManager), - this.getBlobForSave_(canvas, metadataModel)]).then(function(results) { - // Write content to the entry. - var fileEntry = results[0]; - var blob = results[1]; - - // Create writer. - return new Promise(function(resolve, reject) { - fileEntry.createWriter(resolve, reject); - }).then(function(fileWriter) { - // Truncates the file to 0 byte if it overwrites existing file. - return new Promise(function(resolve, reject) { - if (util.isSameEntry(fileEntry, this.entry_)) { - fileWriter.onerror = reject; - fileWriter.onwriteend = resolve; - fileWriter.truncate(0); - } else { - resolve(null); - } - }.bind(this)).then(function() { - // Writes the blob of new image. - return new Promise(function(resolve, reject) { - fileWriter.onerror = reject; - fileWriter.onwriteend = resolve; - fileWriter.write(blob); - }); - }).catch(function(error) { - // Disable all callbacks on the first error. - fileWriter.onerror = null; - fileWriter.onwriteend = null; - - return Promise.reject(error); - }); - }.bind(this)).then(function() { - var locationInfo = volumeManager.getLocationInfo(fileEntry); - if (!locationInfo) { - // Reuse old location info if it fails to obtain location info. - locationInfo = this.locationInfo_; - } - - metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 1, 2); - saveResultRecorded = true; - metrics.recordInterval(ImageUtil.getMetricName('SaveTime')); - - this.entry_ = fileEntry; - this.locationInfo_ = locationInfo; - - // Updates the metadata. - metadataModel.notifyEntriesChanged([this.entry_]); - Promise.all([ - metadataModel.get([this.entry_], GalleryItem.PREFETCH_PROPERTY_NAMES), - new ThumbnailModel(metadataModel).get([this.entry_]) - ]).then(function(metadataLists) { - this.metadataItem_ = metadataLists[0][0]; - this.thumbnailMetadataItem_ = metadataLists[1][0]; - callback(true); - }.bind(this), function() { - callback(false); - }); - }.bind(this)); - }.bind(this)).catch(function(error) { - console.error('Error saving from gallery', this.entry_.name, error); - - if (!saveResultRecorded) { - metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 0, 2); - } - - callback(false); - }.bind(this)); -}; - -/** - * Returns file entry to write. - * @param {boolean} overwrite True to overwrite original file. - * @param {!DirectoryEntry} fallbackDirectory Directory to fallback if current - * directory is not writable. - * @param {!VolumeManager} volumeManager - * @return {!Promise<!FileEntry>} - * @private - */ -GalleryItem.prototype.getEntryToWrite_ = function( - overwrite, fallbackDirectory, volumeManager) { - return new Promise(function(resolve, reject) { - // Since in-place editing is not supported on MTP volume, the Gallery app - // handles MTP volume as read only volume. - if (this.locationInfo_.isReadOnly || - GalleryUtil.isOnMTPVolume(this.entry_, volumeManager)) { - resolve(fallbackDirectory); - } else { - this.entry_.getParent(resolve, reject); - } - }.bind(this)).then(function(directory) { - return new Promise(function(resolve) { - // Find file name. - if (overwrite && - !this.locationInfo_.isReadOnly && - this.isWritableFormat()) { - resolve(this.getFileName()); - return; - } - - this.createCopyName_( - directory, this.getNewMimeType_(), function(copyName) { - this.original_ = false; - resolve(copyName); - }.bind(this)); - }.bind(this)).then(function(name) { - // Get File entry and return. - return new Promise(directory.getFile.bind( - directory, name, { create: true, exclusive: false })); - }); - }.bind(this)); -}; - -/** - * Returns blob to be saved. - * @param {!HTMLCanvasElement} canvas - * @param {!MetadataModel} metadataModel - * @return {!Promise<!Blob>} - * @private - */ -GalleryItem.prototype.getBlobForSave_ = function(canvas, metadataModel) { - return metadataModel.get( - [this.entry_], - ['mediaMimeType', 'contentMimeType', 'ifd', 'exifLittleEndian'] - ).then(function(metadataItems) { - // Create the blob of new image. - var metadataItem = metadataItems[0]; - metadataItem.modificationTime = new Date(); - metadataItem.mediaMimeType = this.getNewMimeType_(); - var metadataEncoder = ImageEncoder.encodeMetadata( - metadataItem, canvas, /* quality for thumbnail*/ 0.8); - // Contrary to what one might think 1.0 is not a good default. Opening - // and saving an typical photo taken with consumer camera increases - // its file size by 50-100%. Experiments show that 0.9 is much better. - // It shrinks some photos a bit, keeps others about the same size, but - // does not visibly lower the quality. - return ImageEncoder.getBlob(canvas, metadataEncoder, 0.9); - }.bind(this)); -}; - -/** - * Renames the item. - * - * @param {string} displayName New display name (without the extension). - * @return {!Promise} Promise fulfilled with when renaming completes, or - * rejected with the error message. - */ -GalleryItem.prototype.rename = function(displayName) { - var newFileName = this.entry_.name.replace( - ImageUtil.getDisplayNameFromName(this.entry_.name), displayName); - - if (newFileName === this.entry_.name) { - return Promise.reject('NOT_CHANGED'); - } - - if (/^\s*$/.test(displayName)) { - return Promise.reject(str('ERROR_WHITESPACE_NAME')); - } - - var parentDirectoryPromise = new Promise( - this.entry_.getParent.bind(this.entry_)); - return parentDirectoryPromise.then(function(parentDirectory) { - var nameValidatingPromise = - util.validateFileName(parentDirectory, newFileName, true); - return nameValidatingPromise.then(function() { - var existingFilePromise = new Promise(parentDirectory.getFile.bind( - parentDirectory, newFileName, {create: false, exclusive: false})); - return existingFilePromise.then(function() { - return Promise.reject(str('GALLERY_FILE_EXISTS')); - }, function() { - return new Promise( - this.entry_.moveTo.bind(this.entry_, parentDirectory, newFileName)); - }.bind(this)); - }.bind(this)); - }.bind(this)).then(function(entry) { - this.entry_ = entry; - }.bind(this)); -}; - -/** - * The threshold size of an image in pixels, which we always use thumbnail - * image for slide-in animation above this. This is a hack to avoid an UI - * unresponsiveness when switching between images. - * @type {number} - * @const - */ -GalleryItem.HEAVY_RENDERING_THRESHOLD_PIXELS = 4000 * 3000; - -/** - * Whether the image requires long rendering time. - * - * @return {boolean} - */ -GalleryItem.prototype.requireLongRenderingTime = function() { - // Check for undefined values. - if (!this.metadataItem_ || !this.metadataItem_.imageHeight || - !this.metadataItem_.imageWidth) { - return false; - } - var numPixels = this.metadataItem_.imageHeight * - this.metadataItem_.imageWidth; - return numPixels > GalleryItem.HEAVY_RENDERING_THRESHOLD_PIXELS; -};
diff --git a/ui/file_manager/gallery/js/gallery_item_unittest.js b/ui/file_manager/gallery/js/gallery_item_unittest.js deleted file mode 100644 index 602a102..0000000 --- a/ui/file_manager/gallery/js/gallery_item_unittest.js +++ /dev/null
@@ -1,291 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * Mock of ImageUtil and metrics. - */ -var ImageUtil = { - getMetricName: function() {}, -}; -var metrics = { - recordEnum: function() {}, - recordInterval: function() {}, - startInterval: function() {} -}; - -/** - * Load time data. - */ -loadTimeData.data = { - DRIVE_DIRECTORY_LABEL: '', - DOWNLOADS_DIRECTORY_LABEL: '' -}; - -function setUp() { - // Replace the real ImageEncoder with a mock. - ImageEncoder = /** @lends{ImageEncoder} */ ( - {encodeMetadata: function() {}, getBlob: function() {}}); -} - -function getMockMetadataModel() { - return new MockMetadataModel({size: 200}); -} - -/** - * Creates a mock result for GalleryItem#saveToFile. - * - * @param{!GalleryItem} item - * @param{!MetadataModel} metadataModel - * @param{boolean} overwrite - * @return {Promise} - */ -function makeMockSavePromise(item, metadataModel, overwrite) { - let canvas = - assertInstanceof(document.createElement('canvas'), HTMLCanvasElement); - let mockVolumeManager = /**@type{!VolumeManager} */ ({ - getLocationInfo: function() { - return {}; - }, - getVolumeInfo: function() { - return {}; - } - }); - return new Promise(item.saveToFile.bind( - item, mockVolumeManager, metadataModel, - /** @type{!DirectoryEntry} */ ({}), // fallbackDir. - canvas, overwrite /* overwrite */)); -} - -/** - * Tests for GalleryItem#saveToFile. - */ -function testSaveToFile(callback) { - var fileSystem = new MockFileSystem('volumeId'); - fileSystem.populate(['/test.jpg']); - var entry = /** @type {FileEntry} */ (fileSystem.entries['/test.jpg']); - entry.createWriter = function(callback) { - let mockWriter = /**@lends {FileWriter} */ ({ - write: function() { - Promise.resolve().then(function() { - mockWriter.onwriteend(); - }); - }, - truncate: function() { - mockWriter.write(); - } - }); - callback(mockWriter); - }; - var entryChanged = false; - var metadataModel = getMockMetadataModel(); - metadataModel.notifyEntriesChanged = function() { - entryChanged = true; - }; - - - var item = - new MockGalleryItem(entry, null, {size: 100}, null, true /*original */); - assertEquals(100, item.getMetadataItem().size); - assertFalse(entryChanged); - reportPromise( - makeMockSavePromise(item, metadataModel, true).then(function() { - assertEquals(200, item.getMetadataItem().size); - assertTrue(entryChanged); - }), - callback); -} - -/** - * Tests for GalleryItem#saveToFile. In this test case, fileWriter.write fails - * with an error. - */ -function testSaveToFileWriteFailCase(callback) { - var fileSystem = new MockFileSystem('volumeId'); - fileSystem.populate(['/test.jpg']); - var entry = /** @type {FileEntry} */ (fileSystem.entries['/test.jpg']); - - entry.createWriter = function(callback) { - let mockWriter = /**@lends {FileWriter} */ ({ - write: function() { - Promise.resolve().then(function() { - mockWriter.onerror(new Error()); - }); - }, - truncate: function() { - Promise.resolve().then(function() { - mockWriter.onwriteend(); - }); - } - }); - callback(mockWriter); - }; - - var item = - new MockGalleryItem(entry, null, {size: 100}, null, true /*original */); - reportPromise( - makeMockSavePromise(item, getMockMetadataModel(), true) - .then(function(result) { - assertFalse(result); - }), - callback); -} - -/** - * Tests for GalleryItem#saveToFile. In this test case, ImageEncoder.getBlob - * fails with an error. This test case confirms that no write operation runs - * when it fails to get a blob of new image. - */ -function testSaveToFileGetBlobFailCase(callback) { - ImageEncoder.getBlob = function() { - throw new Error(); - }; - - var fileSystem = new MockFileSystem('volumeId'); - fileSystem.populate(['/test.jpg']); - var entry = /** @type {FileEntry} */ (fileSystem.entries['/test.jpg']); - - var writeOperationRun = false; - entry.createWriter = function(callback) { - let mockWriter = /**@lends {FileWriter} */ ({ - write: function() { - Promise.resolve().then(function() { - writeOperationRun = true; - mockWriter.onwriteend(); - }); - }, - truncate: function() { - Promise.resolve().then(function() { - writeOperationRun = true; - mockWriter.onwriteend(); - }); - } - }); - callback(mockWriter); - }; - - var item = - new MockGalleryItem(entry, null, {size: 100}, null, true /*original */); - reportPromise( - makeMockSavePromise(item, getMockMetadataModel(), true) - .then(function(result) { - assertFalse(result); - assertFalse(writeOperationRun); - }), - callback); -} - -function testSaveToFileRaw(callback) { - var fileSystem = new MockFileSystem('volumeId'); - fileSystem.populate(['/test.arw']); - fileSystem.entries['/'].getFile = function(name, options, success, error) { - if (options.create) { - assertEquals('test - Edited.jpg', name); - fileSystem.populate(['/test - Edited.jpg']); - var entry = fileSystem.entries['/test - Edited.jpg']; - entry.createWriter = function(callback) { - let mockWriter = /**@lends {FileWriter} */ ({ - write: function() { - Promise.resolve().then(function() { - mockWriter.onwriteend(); - }); - }, - truncate: function() { - mockWriter.write(); - } - }); - callback(mockWriter); - }; - } - MockDirectoryEntry.prototype.getFile.apply(this, arguments); - }; - var entryChanged = false; - var metadataModel = getMockMetadataModel(); - metadataModel.notifyEntriesChanged = function() { - entryChanged = true; - }; - - var entry = /** @type {!FileEntry} */ (fileSystem.entries['/test.arw']); - var item = - new MockGalleryItem(entry, null, {size: 100}, null, true /*original */); - assertEquals(100, item.getMetadataItem().size); - assertFalse(entryChanged); - reportPromise( - makeMockSavePromise(item, metadataModel, false).then(function(success) { - assertTrue(success); - assertEquals(200, item.getMetadataItem().size); - assertTrue(entryChanged); - assertFalse(item.isOriginal()); - }), - callback); -} - -function testIsWritableFile() { - var downloads = new MockFileSystem('downloads'); - var removable = new MockFileSystem('removable'); - var mtp = new MockFileSystem('mtp'); - - var volumeTypes = { - downloads: VolumeManagerCommon.VolumeType.DOWNLOADS, - removable: VolumeManagerCommon.VolumeType.REMOVABLE, - mtp: VolumeManagerCommon.VolumeType.MTP - }; - - // Mock volume manager. - var volumeManager = { - getVolumeInfo: function(entry) { - return { - volumeType: volumeTypes[entry.filesystem.name] - }; - } - }; - - // Jpeg file on downloads. - assertTrue( - MockGalleryItem - .makeWithPath('/test.jpg', downloads, false /* not read only */) - .isWritableFile(volumeManager)); - - // Png file on downloads. - assertTrue( - MockGalleryItem - .makeWithPath('/test.png', downloads, false /* not read only */) - .isWritableFile(volumeManager)); - - // Webp file on downloads. - assertFalse( - MockGalleryItem - .makeWithPath('/test.webp', downloads, false /* not read only */) - .isWritableFile(volumeManager)); - - // Jpeg file on non-writable volume. - assertFalse( - MockGalleryItem.makeWithPath('/test.jpg', removable, true /* read only */) - .isWritableFile(volumeManager)); - - // Jpeg file on mtp volume. - assertFalse( - MockGalleryItem.makeWithPath('/test.jpg', mtp, false /* not read only */) - .isWritableFile(volumeManager)); -} - -function testIsEditableFile() { - var downloads = new MockFileSystem('downloads'); - var getGalleryItem = function(path, isReadOnly) { - return MockGalleryItem.makeWithPath(path, downloads, isReadOnly); - }; - - // Images and raw files are editable, even if read-only (a copy is made). - assertTrue(getGalleryItem('/test.jpg', false).isEditable()); - assertTrue(getGalleryItem('/test.png', false).isEditable()); - assertTrue(getGalleryItem('/test.webp', false).isEditable()); - assertTrue(getGalleryItem('/test.arw', false).isEditable()); - assertTrue(getGalleryItem('/test.jpg', true).isEditable()); - - // Video files are not editable. - assertFalse(getGalleryItem('/test.avi', false).isEditable()); - assertFalse(getGalleryItem('/test.mkv', false).isEditable()); - assertFalse(getGalleryItem('/test.mp4', false).isEditable()); - assertFalse(getGalleryItem('/test.mov', false).isEditable()); - assertFalse(getGalleryItem('/test.webm', false).isEditable()); -}
diff --git a/ui/file_manager/gallery/js/gallery_metrics.js b/ui/file_manager/gallery/js/gallery_metrics.js deleted file mode 100644 index 6ab9cde..0000000 --- a/ui/file_manager/gallery/js/gallery_metrics.js +++ /dev/null
@@ -1,14 +0,0 @@ -// Copyright 2018 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. - -var metrics = metricsBase; - -/** - * @private - */ -metrics.convertName_ = function(name) { - // Historically, Gallery incorrectly uses the Files App prefix. Keep doing - // that to preserve continuity. - return 'FileBrowser.' + name; -};
diff --git a/ui/file_manager/gallery/js/gallery_scripts.js b/ui/file_manager/gallery/js/gallery_scripts.js deleted file mode 100644 index 2801379..0000000 --- a/ui/file_manager/gallery/js/gallery_scripts.js +++ /dev/null
@@ -1,110 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// The include directives are put into Javascript-style comments to prevent -// parsing errors in non-flattened mode. The flattener still sees them. -// Note that this makes the flattener to comment out the first line of the -// included file but that's all right since any javascript file should start -// with a copyright comment anyway. - -// <include src="../../file_manager/common/js/metrics_base.js"> -// <include src="gallery_metrics.js"> -// <include src="../../file_manager/foreground/js/metrics_start.js"> - -// <include src="../../file_manager/common/js/lru_cache.js"> -// <include src="../../file_manager/common/js/power.js"> -// <include src="../../file_manager/common/js/storage_adapter.js"> -// <include src="../../file_manager/common/js/xfm.js"> -// <include src="../../image_loader/load_image_request.js"> -// <include src="../../image_loader/image_loader_client.js"> - -// <include src="../../../webui/resources/js/cr.js"> -// <include src="../../../webui/resources/js/assert.js"> -// <include src="../../../webui/resources/js/util.js"> -// <include src="../../../webui/resources/js/event_tracker.js"> -// <include src="../../../webui/resources/js/i18n_template_no_process.js"> - -// <include src="../../../webui/resources/js/cr/ui.js"> -// <include src="../../../webui/resources/js/cr/event_target.js"> -// <include src="../../../webui/resources/js/cr/ui/array_data_model.js"> -// <include src="../../../webui/resources/js/cr/ui/dialogs.js"> -// <include src="../../../webui/resources/js/cr/ui/list_item.js"> -// <include src="../../../webui/resources/js/cr/ui/list_selection_model.js"> -// <include src="../../../webui/resources/js/cr/ui/list_single_selection_model.js"> -// <include src="../../../webui/resources/js/cr/ui/list_selection_controller.js"> -// <include src="../../../webui/resources/js/cr/ui/list.js"> -// <include src="../../../webui/resources/js/cr/ui/grid.js"> - -(function() { -// 'strict mode' is invoked for this scope. - -// Base classes. -// <include src="../../file_manager/foreground/js/metadata/metadata_cache_set.js"> -// <include src="../../file_manager/foreground/js/metadata/metadata_provider.js"> -// <include src="../../file_manager/foreground/js/metadata/metadata_request.js"> - -// <include src="../../file_manager/common/js/async_util.js"> -// <include src="../../file_manager/common/js/file_type.js"> -// <include src="../../file_manager/common/js/app_util.js"> - -/* TODO(tapted): Remove the util.js dependency */ -// <include src="../../file_manager/common/js/util.js"> - -// <include src="../../file_manager/common/js/volume_manager_types.js"> -// <include -// src="../../file_manager/foreground/js/metadata/content_metadata_provider.js"> -// <include src="../../file_manager/foreground/js/metadata/exif_constants.js"> -// <include -// src="../../file_manager/foreground/js/metadata/external_metadata_provider.js"> -// <include -// src="../../file_manager/foreground/js/metadata/file_system_metadata_provider.js"> -// <include -// src="../../file_manager/foreground/js/metadata/metadata_cache_item.js"> -// <include src="../../file_manager/foreground/js/metadata/metadata_item.js"> -// <include src="../../file_manager/foreground/js/metadata/metadata_model.js"> -// <include -// src="../../file_manager/foreground/js/metadata/multi_metadata_provider.js"> -// <include src="../../file_manager/foreground/js/metadata/thumbnail_model.js"> -// <include src="../../file_manager/foreground/js/thumbnail_loader.js"> -// <include -// src="../../file_manager/foreground/js/ui/file_manager_dialog_base.js"> -// <include src="../../file_manager/foreground/js/ui/files_alert_dialog.js"> -// <include src="../../file_manager/foreground/js/ui/files_confirm_dialog.js"> -// <include src="../../file_manager/common/js/filtered_volume_manager.js"> - -// <include src="image_editor/image_util.js"> -// <include src="image_editor/viewport.js"> -// <include src="image_editor/image_buffer.js"> -// <include src="image_editor/image_loader.js"> -// <include src="image_editor/image_view.js"> -// <include src="image_editor/commands.js"> -// <include src="image_editor/image_editor_prompt.js"> -// <include src="image_editor/image_editor_mode.js"> -// <include src="image_editor/image_editor_toolbar.js"> -// <include src="image_editor/image_editor.js"> -// <include src="image_editor/image_transform.js"> -// <include src="image_editor/image_adjust.js"> -// <include src="image_editor/image_resize.js"> -// <include src="image_editor/filter.js"> -// <include src="image_editor/image_encoder.js"> -// <include src="image_editor/exif_encoder.js"> - -// <include src="dimmable_ui_controller.js"> -// <include src="entry_list_watcher.js"> -// <include src="error_banner.js"> -// <include src="gallery_constants.js"> -// <include src="gallery_data_model.js"> -// <include src="gallery_item.js"> -// <include src="gallery_util.js"> -// <include src="ribbon.js"> -// <include src="slide_mode.js"> -// <include src="thumbnail_mode.js"> -// <include src="gallery.js"> - -// Exports -window.ImageUtil = ImageUtil; -window.metrics = metrics; -window.Gallery = Gallery; - -})();
diff --git a/ui/file_manager/gallery/js/gallery_util.js b/ui/file_manager/gallery/js/gallery_util.js deleted file mode 100644 index c04acd1..0000000 --- a/ui/file_manager/gallery/js/gallery_util.js +++ /dev/null
@@ -1,81 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -var GalleryUtil = {}; - -/** - * Obtains the entry set from the entries passed from onLaunched events. - * If an single entry is specified, the function returns all entries in the same - * directory. Otherwise the function returns the passed entries. - * - * The function also filters non-image items and hidden items. - * - * @param {!Array<!FileEntry>} originalEntries Entries passed from onLaunched - * events. - * @return {!Promise} Promise to be fulfilled with entry array. - */ -GalleryUtil.createEntrySet = function(originalEntries) { - var entriesPromise; - if (originalEntries.length === 1) { - var parentPromise = - new Promise(originalEntries[0].getParent.bind(originalEntries[0])); - entriesPromise = parentPromise.then(function(parent) { - var reader = parent.createReader(); - var readEntries = function() { - return new Promise(reader.readEntries.bind(reader)) - .then(function(entries) { - if (entries.length === 0) { - return []; - } - return readEntries().then(function(nextEntries) { - return entries.concat(nextEntries); - }); - }); - }; - return readEntries(); - }).then(function(entries) { - return entries.filter(function(entry) { - return originalEntries[0].toURL() === entry.toURL() || - entry.name[0] !== '.'; - }); - }); - } else { - entriesPromise = Promise.resolve(originalEntries); - } - - return entriesPromise.then(function(entries) { - return entries.filter(function(entry) { - // Currently the gallery doesn't support mime types, so checking by - // file extensions is enough. - return FileType.isImage(entry) || FileType.isRaw(entry) || - FileType.isVideo(entry); - }).sort(function(a, b) { - return util.compareName(a, b); - }); - }); -}; - -/** - * Returns true if entry is on MTP volume. - * @param {!Entry} entry An entry. - * @param {!VolumeManager} volumeManager Volume manager. - * @return True if entry is on MTP volume. - */ -GalleryUtil.isOnMTPVolume = function(entry, volumeManager) { - var volumeInfo = volumeManager.getVolumeInfo(entry); - return volumeInfo && - volumeInfo.volumeType === VolumeManagerCommon.VolumeType.MTP; -}; - -/** - * Decorates an element to handle mouse focus specific logic. The element - * becomes to have using-mouse class when it is focused by mouse. - * @param {!HTMLElement} element - */ -GalleryUtil.decorateMouseFocusHandling = function(element) { - element.addEventListener('mousedown', - element.classList.toggle.bind(element.classList, 'using-mouse', true)); - element.addEventListener('blur', - element.classList.toggle.bind(element.classList, 'using-mouse', false)); -};
diff --git a/ui/file_manager/gallery/js/gallery_util_unittest.js b/ui/file_manager/gallery/js/gallery_util_unittest.js deleted file mode 100644 index 7453aea..0000000 --- a/ui/file_manager/gallery/js/gallery_util_unittest.js +++ /dev/null
@@ -1,84 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -var fileSystem; - -function setUp() { - fileSystem = new MockFileSystem('fake-media-volume'); - var filenames = [ - '/DCIM/photos0/IMG00001.jpg', '/DCIM/photos0/IMG00002.jpg', - '/DCIM/photos0/IMG00003.jpg', '/DCIM/photos0/MyNote.txt', - '/DCIM/photos0/MyVideo.avi', '/DCIM/photos0/.HiddenFile.jpg', - '/DCIM/photos1/IMG00001.jpg', '/DCIM/photos1/IMG00002.jpg', - '/DCIM/photos1/IMG00003.jpg' - ]; - fileSystem.populate(filenames); -} - -function testcreateEntrySet(callback) { - var emptySelectionPromise = GalleryUtil.createEntrySet([ - ]).then(function(results) { - assertEquals(0, results.length); - }); - - var singleSelectionPromise = - GalleryUtil - .createEntrySet([fileSystem.entries['/DCIM/photos0/IMG00002.jpg']]) - .then(function(results) { - assertEquals(4, results.length); - assertEquals('/DCIM/photos0/IMG00001.jpg', results[0].fullPath); - assertEquals('/DCIM/photos0/IMG00002.jpg', results[1].fullPath); - assertEquals('/DCIM/photos0/IMG00003.jpg', results[2].fullPath); - assertEquals('/DCIM/photos0/MyVideo.avi', results[3].fullPath); - }); - - // If a hidden file is selected directly by users, includes it. - var singleHiddenSelectionPromise = - GalleryUtil - .createEntrySet([fileSystem.entries['/DCIM/photos0/.HiddenFile.jpg']]) - .then(function(results) { - assertEquals(5, results.length); - assertEquals('/DCIM/photos0/.HiddenFile.jpg', results[0].fullPath); - assertEquals('/DCIM/photos0/IMG00001.jpg', results[1].fullPath); - assertEquals('/DCIM/photos0/IMG00002.jpg', results[2].fullPath); - assertEquals('/DCIM/photos0/IMG00003.jpg', results[3].fullPath); - assertEquals('/DCIM/photos0/MyVideo.avi', results[4].fullPath); - }); - - var multipleSelectionPromise = GalleryUtil.createEntrySet([ - fileSystem.entries['/DCIM/photos0/IMG00001.jpg'], - fileSystem.entries['/DCIM/photos0/IMG00002.jpg'], - ]).then(function(results) { - assertEquals(2, results.length); - assertEquals('/DCIM/photos0/IMG00001.jpg', results[0].fullPath); - assertEquals('/DCIM/photos0/IMG00002.jpg', results[1].fullPath); - }); - - var multipleSelectionReverseOrderPromise = GalleryUtil.createEntrySet([ - fileSystem.entries['/DCIM/photos0/IMG00002.jpg'], - fileSystem.entries['/DCIM/photos0/IMG00001.jpg'], - ]).then(function(results) { - assertEquals(2, results.length); - assertEquals('/DCIM/photos0/IMG00001.jpg', results[0].fullPath); - assertEquals('/DCIM/photos0/IMG00002.jpg', results[1].fullPath); - }); - - var multipleHiddenSelectionPromise = GalleryUtil.createEntrySet([ - fileSystem.entries['/DCIM/photos0/IMG00001.jpg'], - fileSystem.entries['/DCIM/photos0/.HiddenFile.jpg'] - ]).then(function(results) { - assertEquals(2, results.length); - assertEquals('/DCIM/photos0/.HiddenFile.jpg', results[0].fullPath); - assertEquals('/DCIM/photos0/IMG00001.jpg', results[1].fullPath); - }); - - reportPromise(Promise.all([ - emptySelectionPromise, - singleSelectionPromise, - singleHiddenSelectionPromise, - multipleSelectionPromise, - multipleSelectionReverseOrderPromise, - multipleHiddenSelectionPromise - ]), callback); -}
diff --git a/ui/file_manager/gallery/js/image_editor/BUILD.gn b/ui/file_manager/gallery/js/image_editor/BUILD.gn deleted file mode 100644 index 94171dd3..0000000 --- a/ui/file_manager/gallery/js/image_editor/BUILD.gn +++ /dev/null
@@ -1,292 +0,0 @@ -# Copyright 2018 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("//third_party/closure_compiler/compile_js.gni") -import("//third_party/closure_compiler/js_unit_tests.gni") -import("//ui/file_manager/base/gn/js_test_gen_html.gni") -import("//ui/webui/resources/tools/js_modulizer.gni") - -js_type_check("closure_compile_jsmodules") { - deps = [ - ":exif_encoder.m", - ":image_encoder.m", - ":image_util.m", - ":test_util.m", - ] -} - -js_type_check("closure_compile_module") { - uses_legacy_modules = true - deps = [ - ":commands", - ":exif_encoder", - ":filter", - ":image_adjust", - ":image_buffer", - ":image_editor", - ":image_editor_mode", - ":image_editor_prompt", - ":image_editor_toolbar", - ":image_encoder", - ":image_loader", - ":image_resize", - ":image_transform", - ":image_util", - ":image_view", - ":test_util", - ":viewport", - ] -} - -js_library("commands") { - deps = [ - ":filter", - ":image_editor_prompt", - ":image_util", - ":image_view", - ":viewport", - ] -} - -js_library("exif_encoder") { - deps = [ - ":image_encoder", - "//ui/file_manager/file_manager/foreground/js/metadata:exif_constants", - "//ui/file_manager/file_manager/foreground/js/metadata:metadata_item", - ] - externs_list = [ "//ui/file_manager/file_manager/externs/exif_entry.js" ] -} - -js_library("exif_encoder.m") { - sources = [ - "$root_gen_dir/ui/file_manager/gallery/js/image_editor/exif_encoder.m.js", - ] - deps = [ - ":image_encoder.m", - "//ui/file_manager/file_manager/externs:exif_entry.m", - "//ui/file_manager/file_manager/foreground/js/metadata:exif_constants.m", - "//ui/file_manager/file_manager/foreground/js/metadata:metadata_item.m", - "//ui/webui/resources/js:assert.m", - ] - - extra_deps = [ ":modulize" ] -} - -js_unittest("exif_encoder_unittest.m") { - deps = [ - ":exif_encoder.m", - ":image_encoder.m", - ":test_util.m", - "//chrome/test/data/webui:chai_assert", - "//ui/file_manager/file_manager/externs:metadata_worker_window.m", - "//ui/file_manager/file_manager/foreground/js/metadata:byte_reader.m", - "//ui/file_manager/file_manager/foreground/js/metadata:exif_parser.m", - "//ui/file_manager/file_manager/foreground/js/metadata:metadata_item.m", - ] -} - -js_library("filter") { - deps = [ ":image_util" ] -} - -js_library("image_adjust") { - deps = [ - ":commands", - ":image_editor", - "//ui/webui/resources/js:assert", - ] -} - -js_library("image_buffer") { -} - -js_library("image_editor") { - deps = [ - ":image_buffer", - ":image_editor_mode", - ":image_editor_toolbar", - ":image_resize", - ":image_util", - ":image_view", - ":viewport", - "//ui/webui/resources/js/cr:event_target", - ] -} - -js_library("image_editor_mode") { - deps = [ - ":commands", - ":image_buffer", - ":image_editor_toolbar", - ":image_view", - ":viewport", - ] -} - -js_library("image_editor_prompt") { - deps = [ "//ui/webui/resources/js:assert" ] -} - -js_library("image_editor_toolbar") { - deps = [ - ":image_util", - "//ui/file_manager/file_manager/common/js:util", - "//ui/file_manager/gallery/js/:gallery_util", - ] - externs_list = [ "//ui/file_manager/file_manager/externs/paper_elements.js" ] -} - -js_library("image_encoder") { - deps = [ - ":image_util", - "//ui/file_manager/file_manager/foreground/js/metadata:metadata_item", - "//ui/webui/resources/js:assert", - ] -} - -js_library("image_encoder.m") { - sources = [ - "$root_gen_dir/ui/file_manager/gallery/js/image_editor/image_encoder.m.js", - ] - deps = [ - ":image_util.m", - "//ui/file_manager/file_manager/foreground/js/metadata:metadata_item.m", - "//ui/webui/resources/js:assert.m", - ] - - extra_deps = [ ":modulize" ] -} - -js_unittest("image_encoder_unittest.m") { - deps = [ - ":image_encoder.m", - ":test_util.m", - "//chrome/test/data/webui:chai_assert", - "//ui/file_manager/file_manager/common/js:test_error_reporting.m", - "//ui/file_manager/file_manager/foreground/js/metadata:metadata_item.m", - ] - externs_list = - [ "//ui/file_manager/file_manager/externs/metadata_worker_window.js" ] -} - -js_library("image_loader") { - deps = [ - ":image_util", - "//ui/file_manager/file_manager/common/js:file_type", - "//ui/file_manager/file_manager/common/js:metrics_base", - "//ui/file_manager/file_manager/common/js:util", - "//ui/file_manager/file_manager/foreground/js/metadata:metadata_model", - "//ui/file_manager/gallery/js/:gallery_item", - "//ui/file_manager/image_loader:image_loader_client", - ] -} - -js_library("image_resize") { - deps = [ - ":image_editor_mode", - "//ui/file_manager/file_manager/foreground/js/ui:files_alert_dialog", - ] -} - -js_library("image_transform") { - deps = [ - ":commands", - ":image_buffer", - ":image_editor_mode", - ":image_editor_toolbar", - ":image_util", - ":viewport", - ] -} - -js_library("image_util") { - deps = [ "//ui/webui/resources/js:assert" ] -} - -js_library("image_util.m") { - sources = [ - "$root_gen_dir/ui/file_manager/gallery/js/image_editor/image_util.m.js", - ] - deps = [ "//ui/webui/resources/js:assert.m" ] - - extra_deps = [ ":modulize" ] -} - -js_library("image_view") { - deps = [ - ":image_buffer", - ":image_loader", - ":image_util", - ":viewport", - "//ui/file_manager/file_manager/common/js:metrics_base", - "//ui/file_manager/file_manager/foreground/js:thumbnail_loader", - "//ui/file_manager/gallery/js/:gallery_item", - "//ui/webui/resources/js:assert", - ] -} - -js_unittest("image_view_unittest") { - deps = [ - ":image_view", - "//ui/file_manager/gallery/js/:mock_gallery_item", - "//ui/webui/resources/js:webui_resource_test", - ] -} - -js_library("test_util") { -} - -js_library("test_util.m") { - sources = - [ "$root_gen_dir/ui/file_manager/gallery/js/image_editor/test_util.m.js" ] - deps = [ "//ui/webui/resources/js:assert.m" ] - - extra_deps = [ ":modulize" ] -} - -js_library("viewport") { - deps = [ - ":image_util", - "//ui/webui/resources/js/cr:event_target", - ] -} - -js_test_gen_html("js_test_gen_html_modules") { - deps = [ - ":exif_encoder_unittest.m", - ":image_encoder_unittest.m", - ] - js_module = true - - closure_flags = - strict_error_checking_closure_args + [ - "js_module_root=./gen/ui", - "js_module_root=../../ui", - "browser_resolver_prefix_replacements=\"chrome://test/=./\"", - ] -} - -js_test_gen_html("js_test_gen_html") { - deps = [ ":image_view_unittest" ] - mocks = [ "//ui/file_manager/file_manager/foreground/js/metadata/metadata_dispatcher_mock_deps.js" ] -} - -group("closure_compile") { - testonly = true - deps = [ - ":closure_compile_jsmodules", - ":closure_compile_module", - ":js_test_gen_html_modules_type_check_auto", - ":js_test_gen_html_type_check_auto", - ] -} - -js_modulizer("modulize") { - input_files = [ - "exif_encoder.js", - "image_encoder.js", - "image_util.js", - "test_util.js", - ] -}
diff --git a/ui/file_manager/gallery/js/image_editor/commands.js b/ui/file_manager/gallery/js/image_editor/commands.js deleted file mode 100644 index 8da532a..0000000 --- a/ui/file_manager/gallery/js/image_editor/commands.js +++ /dev/null
@@ -1,497 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/* eslint-disable no-var */ - -/** - * Command queue is the only way to modify images. - * Supports undo/redo. - * Command execution is asynchronous (callback-based). - * - * @param {!Document} document Document to create canvases in. - * @param {!HTMLCanvasElement|!HTMLImageElement} image The canvas with the - * original image. - * @param {function(function())} saveFunction Function to save the image. - * @constructor - * @struct - */ -function CommandQueue(document, image, saveFunction) { - this.document_ = document; - this.undo_ = []; - this.redo_ = []; - this.subscribers_ = []; - - /** - * @type {HTMLCanvasElement|HTMLImageElement} - * @private - */ - this.currentImage_ = image; - - /** - * @type {HTMLCanvasElement|HTMLImageElement} - * @private - */ - this.baselineImage_ = image; - - /** - * @type {HTMLCanvasElement|HTMLImageElement} - * @private - */ - this.previousImage_ = null; - - this.saveFunction_ = saveFunction; - this.busy_ = false; - this.UIContext_ = {}; -} - -/** - * Attach the UI elements to the command queue. - * Once the UI is attached the results of image manipulations are displayed. - * - * @param {!ImageView} imageView The ImageView object to display the results. - * @param {!ImageEditorPrompt} prompt Prompt to use with this CommandQueue. - * @param {function()} updateUndoRedo Function to update undo and redo buttons - * state. - * @param {function(boolean)} lock Function to enable/disable buttons etc. - */ -CommandQueue.prototype.attachUI = function( - imageView, prompt, updateUndoRedo, lock) { - this.UIContext_ = { - imageView: imageView, - prompt: prompt, - updateUndoRedo: updateUndoRedo, - lock: lock - }; -}; - -/** - * Execute the action when the queue is not busy. - * @param {function()} callback Callback. - */ -CommandQueue.prototype.executeWhenReady = function(callback) { - if (this.isBusy()) { - this.subscribers_.push(callback); - } else { - setTimeout(callback, 0); - } -}; - -/** - * @return {boolean} True if the command queue is busy. - */ -CommandQueue.prototype.isBusy = function() { - return this.busy_; -}; - -/** - * Set the queue state to busy. Lock the UI. - * @private - */ -CommandQueue.prototype.setBusy_ = function() { - if (this.busy_) { - throw new Error('CommandQueue already busy'); - } - - this.busy_ = true; - - if (this.UIContext_.lock) { - this.UIContext_.lock(true); - } - - ImageUtil.trace.resetTimer('command-busy'); -}; - -/** - * Set the queue state to not busy. Unlock the UI and execute pending actions. - * @private - */ -CommandQueue.prototype.clearBusy_ = function() { - if (!this.busy_) { - throw new Error('Inconsistent CommandQueue already not busy'); - } - - this.busy_ = false; - - // Execute the actions requested while the queue was busy. - while (this.subscribers_.length) { - this.subscribers_.shift()(); - } - - if (this.UIContext_.lock) { - this.UIContext_.lock(false); - } - - ImageUtil.trace.reportTimer('command-busy'); -}; - -/** - * Commit the image change: save and unlock the UI. - * @param {boolean} showUndoAction True to show undo action in the toast. - * @param {number=} opt_delay Delay in ms (to avoid disrupting the animation). - * @private - */ -CommandQueue.prototype.commit_ = function(showUndoAction, opt_delay) { - setTimeout(this.saveFunction_.bind(null, this.clearBusy_.bind(this)), - opt_delay || 0); -}; - -/** - * Internal function to execute the command in a given context. - * - * @param {!Command} command The command to execute. - * @param {!Object} uiContext The UI context. - * @param {function(number=)} callback Completion callback. - * @private - */ -CommandQueue.prototype.doExecute_ = function(command, uiContext, callback) { - if (!this.currentImage_) { - throw new Error('Cannot operate on null image'); - } - - command.execute( - this.document_, - this.currentImage_, - /** - * @type {function(HTMLCanvasElement, number=)} - */ - (function(result, opt_delay) { - this.previousImage_ = this.currentImage_; - this.currentImage_ = result; - callback(opt_delay); - }.bind(this)), - uiContext); -}; - -/** - * Executes the command. - * - * @param {!Command} command Command to execute. - * @param {boolean=} opt_keep_redo True if redo stack should not be cleared. - */ -CommandQueue.prototype.execute = function(command, opt_keep_redo) { - this.setBusy_(); - - if (!opt_keep_redo) { - this.redo_ = []; - } - - this.undo_.push(command); - - this.doExecute_(command, this.UIContext_, - this.commit_.bind(this, true /* Show undo action */)); -}; - -/** - * @return {boolean} True if Undo is applicable. - */ -CommandQueue.prototype.canUndo = function() { - return this.undo_.length != 0; -}; - -/** - * Undo the most recent command. - */ -CommandQueue.prototype.undo = function() { - if (!this.canUndo()) { - throw new Error('Cannot undo'); - } - - this.setBusy_(); - - var command = this.undo_.pop(); - this.redo_.push(command); - - var self = this; - - function complete() { - var delay = command.revertView( - self.currentImage_, self.UIContext_.imageView); - self.commit_(false /* Do not show undo action */, delay); - } - - if (this.previousImage_) { - // First undo after an execute call. - this.currentImage_ = this.previousImage_; - this.previousImage_ = null; - - complete(); - // TODO(kaznacheev) Consider recalculating previousImage_ right here - // by replaying the commands in the background. - } else { - this.currentImage_ = this.baselineImage_; - - var replay = function(index) { - if (index < self.undo_.length) { - self.doExecute_(self.undo_[index], {}, replay.bind(null, index + 1)); - } else { - complete(); - } - }; - - replay(0); - } -}; - -/** - * @return {boolean} True if Redo is applicable. - */ -CommandQueue.prototype.canRedo = function() { - return this.redo_.length != 0; -}; - -/** - * Repeat the command that was recently un-done. - */ -CommandQueue.prototype.redo = function() { - if (!this.canRedo()) { - throw new Error('Cannot redo'); - } - - this.execute(this.redo_.pop(), true); -}; - -/** - * Closes internal buffers. Call to ensure, that internal buffers are freed - * as soon as possible. - */ -CommandQueue.prototype.close = function() { - // Free memory used by the undo buffer. - this.currentImage_ = null; - this.previousImage_ = null; - this.baselineImage_ = null; -}; - -/** - * Command object encapsulates an operation on an image and a way to visualize - * its result. - * - * @param {string} name Command name. - * @constructor - * @struct - */ -function Command(name) { - this.name_ = name; -} - -/** - * @return {string} String representation of the command. - */ -Command.prototype.toString = function() { - return 'Command ' + this.name_; -}; - -/** - * Execute the command and visualize its results. - * - * The two actions are combined into one method because sometimes it is nice - * to be able to show partial results for slower operations. - * - * @param {!Document} document Document on which to execute command. - * @param {!HTMLCanvasElement|!HTMLImageElement} srcImage Image to execute on. - * Do NOT modify this object. - * @param {function(HTMLCanvasElement, number=)} callback Callback to call on - * completion. - * @param {!Object} uiContext Context to work in. - */ -Command.prototype.execute = function(document, srcImage, callback, uiContext) { - console.error('Command.prototype.execute not implemented'); -}; - -/** - * Visualize reversion of the operation. - * - * @param {!HTMLCanvasElement|!HTMLCanvasElement} image previous image. - * @param {!ImageView} imageView ImageView to revert. - * @return {number} Animation duration in ms. - */ -Command.prototype.revertView = function(image, imageView) { - imageView.replace(image); - return 0; -}; - -/** - * Creates canvas to render on. - * - * @param {!Document} document Document to create canvas in. - * @param {!HTMLCanvasElement|!HTMLImageElement} srcImage to copy optional - * dimensions from. - * @param {number=} opt_width new canvas width. - * @param {number=} opt_height new canvas height. - * @return {!HTMLCanvasElement} Newly created canvas. - * @private - */ -Command.prototype.createCanvas_ = function( - document, srcImage, opt_width, opt_height) { - var result = assertInstanceof(document.createElement('canvas'), - HTMLCanvasElement); - result.width = opt_width || srcImage.width; - result.height = opt_height || srcImage.height; - return result; -}; - - -/** - * Rotate command - * @param {number} rotate90 Rotation angle in 90 degree increments (signed). - * @constructor - * @extends {Command} - * @struct - */ -Command.Rotate = function(rotate90) { - Command.call(this, 'rotate(' + rotate90 * 90 + 'deg)'); - this.rotate90_ = rotate90; -}; - -Command.Rotate.prototype = { __proto__: Command.prototype }; - -/** @override */ -Command.Rotate.prototype.execute = function( - document, srcImage, callback, uiContext) { - var result = this.createCanvas_( - document, - srcImage, - (this.rotate90_ & 1) ? srcImage.height : srcImage.width, - (this.rotate90_ & 1) ? srcImage.width : srcImage.height); - ImageUtil.drawImageTransformed( - result, srcImage, 1, 1, this.rotate90_ * Math.PI / 2); - var delay; - if (uiContext.imageView) { - delay = uiContext.imageView.replaceAndAnimate(result, null, this.rotate90_); - } - setTimeout(callback, 0, result, delay); -}; - -/** @override */ -Command.Rotate.prototype.revertView = function(image, imageView) { - return imageView.replaceAndAnimate(image, null, -this.rotate90_); -}; - - -/** - * Crop command. - * - * @param {!ImageRect} imageRect Crop rectangle in image coordinates. - * @constructor - * @extends {Command} - * @struct - */ -Command.Crop = function(imageRect) { - Command.call(this, 'crop' + imageRect.toString()); - this.imageRect_ = imageRect; -}; - -Command.Crop.prototype = { __proto__: Command.prototype }; - -/** @override */ -Command.Crop.prototype.execute = function( - document, srcCanvas, callback, uiContext) { - var result = this.createCanvas_( - document, srcCanvas, this.imageRect_.width, this.imageRect_.height); - var ctx = assertInstanceof(result.getContext('2d'), CanvasRenderingContext2D); - ImageRect.drawImage(ctx, srcCanvas, null, this.imageRect_); - var delay; - if (uiContext.imageView) { - delay = uiContext.imageView.replaceAndAnimate(result, this.imageRect_, 0); - } - setTimeout(callback, 0, result, delay); -}; - -/** @override */ -Command.Crop.prototype.revertView = function(image, imageView) { - return imageView.animateAndReplace(image, this.imageRect_); -}; - - -/** - * Filter command. - * - * @param {string} name Command name. - * @param {function(!ImageData,!ImageData,number,number)} filter Filter - * function. - * @param {?string} message Message to display when done. - * @constructor - * @extends {Command} - * @struct - */ -Command.Filter = function(name, filter, message) { - Command.call(this, name); - this.filter_ = filter; - this.message_ = message; -}; - -Command.Filter.prototype = { __proto__: Command.prototype }; - -/** @override */ -Command.Filter.prototype.execute = function( - document, srcImage, callback, uiContext) { - var result = this.createCanvas_(document, srcImage); - var self = this; - var previousRow = 0; - - function onProgressVisible(updatedRow, rowCount) { - if (updatedRow == rowCount) { - uiContext.imageView.replace(result); - if (self.message_) { - uiContext.prompt.show(self.message_, 2000); - } - callback(result); - } else { - var viewport = uiContext.imageView.viewport_; - - var imageStrip = ImageRect.createFromBounds(viewport.getImageBounds()); - imageStrip.top = previousRow; - imageStrip.height = updatedRow - previousRow; - - var screenStrip = ImageRect.createFromBounds( - viewport.getImageBoundsOnScreen()); - screenStrip.top = Math.round(viewport.imageToScreenY(previousRow)); - screenStrip.height = - Math.round(viewport.imageToScreenY(updatedRow)) - screenStrip.top; - - uiContext.imageView.paintDeviceRect(result, imageStrip); - previousRow = updatedRow; - } - } - - function onProgressInvisible(updatedRow, rowCount) { - if (updatedRow == rowCount) { - callback(result); - } - } - - filter.applyByStrips(result, srcImage, this.filter_, - uiContext.imageView ? onProgressVisible : onProgressInvisible); -}; - -/** - * Resize Command - * @param {number} inputWidth width user input - * @param {number} inputHeight height user input - * @constructor - * @extends {Command} - * @struct - */ -Command.Resize = function(inputWidth, inputHeight) { - Command.call(this, 'resize(x:' + inputWidth + ',y:' + inputHeight + ')'); - this.newWidth_ = inputWidth; - this.newHeight_ = inputHeight; -}; - -Command.Resize.prototype = {__proto__: Command.prototype}; - -/** @override */ -Command.Resize.prototype.execute = function( - document, srcImage, callback, uiContext) { - var result = this.createCanvas_( - document, srcImage, this.newWidth_, this.newHeight_); - - var scaleX = this.newWidth_ / srcImage.width; - var scaleY = this.newHeight_ / srcImage.height; - ImageUtil.drawImageTransformed(result, srcImage, scaleX, scaleY, 0); - - if (uiContext.imageView) { - uiContext.imageView.replace(result); - } - callback(result); -};
diff --git a/ui/file_manager/gallery/js/image_editor/exif_encoder.js b/ui/file_manager/gallery/js/image_editor/exif_encoder.js deleted file mode 100644 index e845e8e..0000000 --- a/ui/file_manager/gallery/js/image_editor/exif_encoder.js +++ /dev/null
@@ -1,654 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/* eslint-disable no-var */ - -// clang-format off -// #import {ImageEncoder} from './image_encoder.m.js'; -// #import * as wrappedExif from '../../../file_manager/foreground/js/metadata/exif_constants.m.js'; const {Exif} = wrappedExif; -// #import {MetadataItem} from '../../../file_manager/foreground/js/metadata/metadata_item.m.js'; -// #import {assert} from 'chrome://resources/js/assert.m.js' -// #import {ExifEntry} from '../../../file_manager/externs/exif_entry.m.js'; -// clang-format on - -/** - * The Exif metadata encoder. - * Uses the metadata format as defined by ExifParser. - * @param {!MetadataItem} originalMetadata Metadata to encode. - * @constructor - * @extends {ImageEncoder.MetadataEncoder} - * @struct - */ -/* #export */ function ExifEncoder(originalMetadata) { - ImageEncoder.MetadataEncoder.apply(this, arguments); - /** - * Image File Directory obtained from EXIF header. - * @private {!Object} - * @const - */ - this.ifd_ = /** @type {!Object} */( - JSON.parse(JSON.stringify(originalMetadata.ifd || {}))); - - /** - * Note use little endian if the original metadata does not have the - * information. - * @private {boolean} - * @const - */ - this.exifLittleEndian_ = !!originalMetadata.exifLittleEndian; - - /** - * Modification time to be stored in EXIF header. - * @private {!Date} - * @const - */ - this.modificationTime_ = assert(originalMetadata.modificationTime); -} - -ExifEncoder.prototype = {__proto__: ImageEncoder.MetadataEncoder.prototype}; - -ImageEncoder.registerMetadataEncoder(ExifEncoder, 'image/jpeg'); - -/** - * Software name of the Gallery app. - * @type {string} - * @const - */ -ExifEncoder.SOFTWARE = 'Chrome OS Gallery App\0'; - -/** - * Maximum size of exif data. - * @const {number} - */ -ExifEncoder.MAXIMUM_EXIF_DATA_SIZE = 0x10000; - -/** - * Size of metadata for thumbnail. - * @const {number} - */ -ExifEncoder.THUMBNAIL_METADATA_SIZE = 76; - -/** - * @param {!HTMLCanvasElement} canvas - * @override - */ -ExifEncoder.prototype.setImageData = function(canvas) { - ImageEncoder.MetadataEncoder.prototype.setImageData.call(this, canvas); - - var image = this.ifd_.image; - if (!image) { - image = this.ifd_.image = {}; - } - - // Only update width/height in this directory if they are present. - if (image[Exif.Tag.IMAGE_WIDTH] && image[Exif.Tag.IMAGE_HEIGHT]) { - image[Exif.Tag.IMAGE_WIDTH].value = canvas.width; - image[Exif.Tag.IMAGE_HEIGHT].value = canvas.height; - } - - var exif = this.ifd_.exif; - if (!exif) { - exif = this.ifd_.exif = {}; - } - ExifEncoder.findOrCreateTag(image, Exif.Tag.EXIFDATA); - ExifEncoder.findOrCreateTag(exif, Exif.Tag.X_DIMENSION).value = canvas.width; - ExifEncoder.findOrCreateTag(exif, Exif.Tag.Y_DIMENSION).value = canvas.height; - - // Always save in default orientation. - ExifEncoder.findOrCreateTag(image, Exif.Tag.ORIENTATION).value = 1; - - // Update software name. - var softwareTag = ExifEncoder.findOrCreateTag(image, Exif.Tag.SOFTWARE, 2); - softwareTag.value = ExifEncoder.SOFTWARE; - softwareTag.componentCount = ExifEncoder.SOFTWARE.length; - - // Update modification date time. - var padNumWithZero = function(num, length) { - var str = num.toString(); - while (str.length < length) { - str = '0' + str; - } - return str; - }; - - var dateTimeTag = ExifEncoder.findOrCreateTag(image, Exif.Tag.DATETIME, 2); - dateTimeTag.value = - padNumWithZero(this.modificationTime_.getFullYear(), 4) + ':' + - padNumWithZero(this.modificationTime_.getMonth() + 1, 2) + ':' + - padNumWithZero(this.modificationTime_.getDate(), 2) + ' ' + - padNumWithZero(this.modificationTime_.getHours(), 2) + ':' + - padNumWithZero(this.modificationTime_.getMinutes(), 2) + ':' + - padNumWithZero(this.modificationTime_.getSeconds(), 2) + '\0'; - dateTimeTag.componentCount = 20; -}; - -/** - * @override - */ -ExifEncoder.prototype.setThumbnailData = function(canvas, quality) { - if (canvas) { - // Empirical formula with reasonable behavior: - // 10K for 1Mpix, 30K for 5Mpix, 50K for 9Mpix and up. - var pixelCount = this.imageWidth * this.imageHeight; - var maxEncodedSize = 5000 * Math.min(10, 1 + pixelCount / 1000000); - var DATA_URL_PREFIX = 'data:image/jpeg;base64,'; - var BASE64_BLOAT = 4 / 3; - var maxDataURLLength = - DATA_URL_PREFIX.length + Math.ceil(maxEncodedSize * BASE64_BLOAT); - for (; quality > 0.2; quality *= 0.8) { - ImageEncoder.MetadataEncoder.prototype.setThumbnailData.call( - this, canvas, quality); - // If the obtained thumbnail URL is too long, reset the URL and try again - // with less quality value. - if (this.thumbnailDataUrl.length > maxDataURLLength) { - this.thumbnailDataUrl = ''; - continue; - } - break; - } - } - if (this.thumbnailDataUrl) { - var thumbnail = this.ifd_.thumbnail; - if (!thumbnail) { - thumbnail = this.ifd_.thumbnail = {}; - } - - ExifEncoder.findOrCreateTag(thumbnail, Exif.Tag.IMAGE_WIDTH).value = - canvas.width; - - ExifEncoder.findOrCreateTag(thumbnail, Exif.Tag.IMAGE_HEIGHT).value = - canvas.height; - - // The values for these tags will be set in ExifWriter.encode. - ExifEncoder.findOrCreateTag(thumbnail, Exif.Tag.JPG_THUMB_OFFSET); - ExifEncoder.findOrCreateTag(thumbnail, Exif.Tag.JPG_THUMB_LENGTH); - - // Always save in default orientation. - ExifEncoder.findOrCreateTag(thumbnail, Exif.Tag.ORIENTATION).value = 1; - - // When thumbnail is compressed with JPEG, compression must be set as 6. - ExifEncoder.findOrCreateTag(this.ifd_.image, Exif.Tag.COMPRESSION).value = - 6; - } else { - if (this.ifd_.thumbnail) { - delete this.ifd_.thumbnail; - } - } -}; - -/** - * @override - */ -ExifEncoder.prototype.findInsertionRange = function(encodedImage) { - function getWord(pos) { - if (pos + 2 > encodedImage.length) { - throw 'Reading past the buffer end @' + pos; - } - return encodedImage.charCodeAt(pos) << 8 | encodedImage.charCodeAt(pos + 1); - } - - if (getWord(0) != Exif.Mark.SOI) { - throw new Error('Jpeg data starts from 0x' + getWord(0).toString(16)); - } - - var sectionStart = 2; - - // Default: an empty range right after SOI. - // Will be returned in absence of APP0 or Exif sections. - var range = {from: sectionStart, to: sectionStart}; - - for (;;) { - var tag = getWord(sectionStart); - - if (tag == Exif.Mark.SOS) { - break; - } - - var nextSectionStart = sectionStart + 2 + getWord(sectionStart + 2); - if (nextSectionStart <= sectionStart || - nextSectionStart > encodedImage.length) { - throw new Error('Invalid section size in jpeg data'); - } - - if (tag == Exif.Mark.APP0) { - // Assert that we have not seen the Exif section yet. - if (range.from != range.to) { - throw new Error('APP0 section found after EXIF section'); - } - // An empty range right after the APP0 segment. - range.from = range.to = nextSectionStart; - } else if (tag == Exif.Mark.EXIF) { - // A range containing the existing EXIF section. - range.from = sectionStart; - range.to = nextSectionStart; - } - sectionStart = nextSectionStart; - } - - return range; -}; - -/** - * @override - */ -ExifEncoder.prototype.encode = function() { - var HEADER_SIZE = 10; - - // Allocate the largest theoretically possible size. - var bytes = new Uint8Array(ExifEncoder.MAXIMUM_EXIF_DATA_SIZE); - - // Serialize header - var hw = new ByteWriter(bytes.buffer, 0, HEADER_SIZE); - hw.writeScalar(Exif.Mark.EXIF, 2); - hw.forward('size', 2); - hw.writeString('Exif\0\0'); // Magic string. - - // First serialize the content of the exif section. - // Use a ByteWriter starting at HEADER_SIZE offset so that tell() positions - // can be directly mapped to offsets as encoded in the dictionaries. - var bw = new ByteWriter(bytes.buffer, HEADER_SIZE); - - if (this.exifLittleEndian_) { - bw.setByteOrder(ByteWriter.ByteOrder.LITTLE_ENDIAN); - bw.writeScalar(Exif.Align.LITTLE, 2); - } else { - bw.setByteOrder(ByteWriter.ByteOrder.BIG_ENDIAN); - bw.writeScalar(Exif.Align.BIG, 2); - } - - bw.writeScalar(Exif.Tag.TIFF, 2); - - bw.forward('image-dir', 4); // The pointer should point right after itself. - bw.resolveOffset('image-dir'); - - ExifEncoder.encodeDirectory(bw, this.ifd_.image, - [Exif.Tag.EXIFDATA, Exif.Tag.GPSDATA], 'thumb-dir'); - - if (this.ifd_.exif) { - bw.resolveOffset(Exif.Tag.EXIFDATA); - ExifEncoder.encodeDirectory(bw, this.ifd_.exif); - } else { - if (Exif.Tag.EXIFDATA in this.ifd_.image) { - throw new Error('Corrupt exif dictionary reference'); - } - } - - if (this.ifd_.gps) { - bw.resolveOffset(Exif.Tag.GPSDATA); - ExifEncoder.encodeDirectory(bw, this.ifd_.gps); - } else { - if (Exif.Tag.GPSDATA in this.ifd_.image) { - throw new Error('Missing gps dictionary reference'); - } - } - - // Since thumbnail data can be large, check enough space has left for - // thumbnail data. - var thumbnailDecoded = this.ifd_.thumbnail ? - ImageEncoder.decodeDataURL(this.thumbnailDataUrl) : ''; - if (this.ifd_.thumbnail && - (ExifEncoder.MAXIMUM_EXIF_DATA_SIZE - bw.tell() >= - thumbnailDecoded.length + ExifEncoder.THUMBNAIL_METADATA_SIZE)) { - bw.resolveOffset('thumb-dir'); - ExifEncoder.encodeDirectory( - bw, - this.ifd_.thumbnail, - [Exif.Tag.JPG_THUMB_OFFSET, Exif.Tag.JPG_THUMB_LENGTH]); - - bw.resolveOffset(Exif.Tag.JPG_THUMB_OFFSET); - bw.resolve(Exif.Tag.JPG_THUMB_LENGTH, thumbnailDecoded.length); - bw.writeString(thumbnailDecoded); - } else { - bw.resolve('thumb-dir', 0); - } - - bw.checkResolved(); - - var totalSize = HEADER_SIZE + bw.tell(); - hw.resolve('size', totalSize - 2); // The marker is excluded. - hw.checkResolved(); - - var subarray = new Uint8Array(totalSize); - for (var i = 0; i != totalSize; i++) { - subarray[i] = bytes[i]; - } - return subarray.buffer; -}; - -/* - * Static methods. - */ - -/** - * Write the contents of an IFD directory. - * @param {!ByteWriter} bw ByteWriter to use. - * @param {!Object<!Exif.Tag, ExifEntry>} directory A directory map as created - * by ExifParser. - * @param {Array=} opt_resolveLater An array of tag ids for which the values - * will be resolved later. - * @param {string=} opt_nextDirPointer A forward key for the pointer to the next - * directory. If omitted the pointer is set to 0. - */ -ExifEncoder.encodeDirectory = function( - bw, directory, opt_resolveLater, opt_nextDirPointer) { - - var longValues = []; - - bw.forward('dir-count', 2); - var count = 0; - - for (var key in directory) { - var tag = directory[/** @type {!Exif.Tag} */ (parseInt(key, 10))]; - bw.writeScalar(/** @type {number}*/ (tag.id), 2); - bw.writeScalar(tag.format, 2); - bw.writeScalar(tag.componentCount, 4); - - var width = ExifEncoder.getComponentWidth(tag) * tag.componentCount; - - if (opt_resolveLater && (opt_resolveLater.indexOf(tag.id) >= 0)) { - // The actual value depends on further computations. - if (tag.componentCount != 1 || width > 4) { - throw new Error('Cannot forward the pointer for ' + tag.id); - } - bw.forward(tag.id, width); - } else if (width <= 4) { - // The value fits into 4 bytes, write it immediately. - ExifEncoder.writeValue(bw, tag); - } else { - // The value does not fit, forward the 4 byte offset to the actual value. - width = 4; - bw.forward(tag.id, width); - longValues.push(tag); - } - bw.skip(4 - width); // Align so that the value take up exactly 4 bytes. - count++; - } - - bw.resolve('dir-count', count); - - if (opt_nextDirPointer) { - bw.forward(opt_nextDirPointer, 4); - } else { - bw.writeScalar(0, 4); - } - - // Write out the long values and resolve pointers. - for (var i = 0; i != longValues.length; i++) { - var longValue = longValues[i]; - bw.resolveOffset(longValue.id); - ExifEncoder.writeValue(bw, longValue); - } -}; - -/** - * @param {ExifEntry} tag EXIF tag object. - * @return {number} Width in bytes of the data unit associated with this tag. - * TODO(kaznacheev): Share with ExifParser? - */ -ExifEncoder.getComponentWidth = function(tag) { - switch (tag.format) { - case 1: // Byte - case 2: // String - case 7: // Undefined - return 1; - - case 3: // Short - return 2; - - case 4: // Long - case 9: // Signed Long - return 4; - - case 5: // Rational - case 10: // Signed Rational - return 8; - - default: // ??? - console.warn('Unknown tag format 0x' + - Number(tag.id).toString(16) + ': ' + tag.format); - return 4; - } -}; - -/** - * Writes out the tag value. - * @param {!ByteWriter} bw Writer to use. - * @param {ExifEntry} tag Tag, which value to write. - */ -ExifEncoder.writeValue = function(bw, tag) { - if (tag.format === 2) { // String - if (tag.componentCount !== tag.value.length) { - throw new Error( - 'String size mismatch for 0x' + Number(tag.id).toString(16)); - } - - if (tag.value.charAt(tag.value.length - 1) !== '\0') { - throw new Error('String must end with null character.'); - } - - bw.writeString(/** @type {string} */ (tag.value)); - } else { // Scalar or rational - var width = ExifEncoder.getComponentWidth(tag); - - var writeComponent = function(value, signed) { - if (width == 8) { - bw.writeScalar(value[0], 4, signed); - bw.writeScalar(value[1], 4, signed); - } else { - bw.writeScalar(value, width, signed); - } - }; - - var signed = (tag.format == 9 || tag.format == 10); - if (tag.componentCount == 1) { - writeComponent(tag.value, signed); - } else { - for (var i = 0; i != tag.componentCount; i++) { - writeComponent(tag.value[i], signed); - } - } - } -}; - -/** - * Finds a tag. If not exist, creates a tag. - * - * @param {!Object<!Exif.Tag, ExifEntry>} directory EXIF directory. - * @param {!Exif.Tag} id Tag id. - * @param {number=} opt_format Tag format - * (used in {@link ExifEncoder#getComponentWidth}). - * @param {number=} opt_componentCount Number of components in this tag. - * @return {ExifEntry} - * Tag found or created. - */ -ExifEncoder.findOrCreateTag = function(directory, id, opt_format, - opt_componentCount) { - if (!(id in directory)) { - directory[id] = { - id: id, - format: opt_format || 3, // Short - componentCount: opt_componentCount || 1, - value: 0 - }; - } - return directory[id]; -}; - -/** - * ByteWriter class. - * @param {!ArrayBuffer} arrayBuffer Underlying buffer to use. - * @param {number} offset Offset at which to start writing. - * @param {number=} opt_length Maximum length to use. - * @constructor - * @struct - */ -/* #export */ function ByteWriter(arrayBuffer, offset, opt_length) { - var length = opt_length || (arrayBuffer.byteLength - offset); - this.view_ = new DataView(arrayBuffer, offset, length); - this.littleEndian_ = false; - this.pos_ = 0; - this.forwards_ = {}; -} - -/** - * Byte order. - * @enum {number} - */ -ByteWriter.ByteOrder = { - // Little endian byte order. - LITTLE_ENDIAN: 0, - // Big endian byte order. - BIG_ENDIAN: 1 -}; - -/** - * Set the byte ordering for future writes. - * @param {ByteWriter.ByteOrder} order ByteOrder to use - * {ByteWriter.LITTLE_ENDIAN} or {ByteWriter.BIG_ENDIAN}. - */ -ByteWriter.prototype.setByteOrder = function(order) { - this.littleEndian_ = (order === ByteWriter.ByteOrder.LITTLE_ENDIAN); -}; - -/** - * @return {number} the current write position. - */ -ByteWriter.prototype.tell = function() { - return this.pos_; -}; - -/** - * Skips desired amount of bytes in output stream. - * @param {number} count Byte count to skip. - */ -ByteWriter.prototype.skip = function(count) { - this.validateWrite(count); - this.pos_ += count; -}; - -/** - * Check if the buffer has enough room to read 'width' bytes. Throws an error - * if it has not. - * @param {number} width Amount of bytes to check. - */ -ByteWriter.prototype.validateWrite = function(width) { - if (this.pos_ + width > this.view_.byteLength) { - throw new Error('Writing past the end of the buffer'); - } -}; - -/** - * Writes scalar value to output stream. - * @param {number} value Value to write. - * @param {number} width Desired width of written value. - * @param {boolean=} opt_signed True if value represents signed number. - */ -ByteWriter.prototype.writeScalar = function(value, width, opt_signed) { - var method; - // The below switch is so verbose for two reasons: - // 1. V8 is faster on method names which are 'symbols'. - // 2. Method names are discoverable by full text search. - switch (width) { - case 1: - method = opt_signed ? 'setInt8' : 'setUint8'; - break; - - case 2: - method = opt_signed ? 'setInt16' : 'setUint16'; - break; - - case 4: - method = opt_signed ? 'setInt32' : 'setUint32'; - break; - - case 8: - method = opt_signed ? 'setInt64' : 'setUint64'; - break; - - default: - throw new Error('Invalid width: ' + width); - break; - } - - this.validateWrite(width); - this.view_[method](this.pos_, value, this.littleEndian_); - this.pos_ += width; -}; - -/** - * Writes string. - * @param {string} str String to write. - */ -ByteWriter.prototype.writeString = function(str) { - this.validateWrite(str.length); - for (var i = 0; i != str.length; i++) { - this.view_.setUint8(this.pos_++, str.charCodeAt(i)); - } -}; - -/** - * Allocate the space for 'width' bytes for the value that will be set later. - * To be followed by a 'resolve' call with the same key. - * @param {(string|Exif.Tag)} key A key to identify the value. - * @param {number} width Width of the value in bytes. - */ -ByteWriter.prototype.forward = function(key, width) { - if (key in this.forwards_) { - throw new Error('Duplicate forward key ' + key); - } - this.validateWrite(width); - this.forwards_[key] = { - pos: this.pos_, - width: width - }; - this.pos_ += width; -}; - -/** - * Set the value previously allocated with a 'forward' call. - * @param {(string|Exif.Tag)} key A key to identify the value. - * @param {number} value value to write in pre-allocated space. - */ -ByteWriter.prototype.resolve = function(key, value) { - if (!(key in this.forwards_)) { - throw new Error('Undeclared forward key ' + key.toString(16)); - } - var forward = this.forwards_[key]; - var curPos = this.pos_; - this.pos_ = forward.pos; - this.writeScalar(value, forward.width); - this.pos_ = curPos; - delete this.forwards_[key]; -}; - -/** - * A shortcut to resolve the value to the current write position. - * @param {(string|Exif.Tag)} key A key to identify pre-allocated position. - */ -ByteWriter.prototype.resolveOffset = function(key) { - this.resolve(key, this.tell()); -}; - -/** - * Check if every forward has been resolved, throw and error if not. - */ -ByteWriter.prototype.checkResolved = function() { - for (var key in this.forwards_) { - throw new Error('Unresolved forward pointer ' + - ByteWriter.prettyKeyFormat(key)); - } -}; - -/** - * If key is a number, format it in hex style. - * @param {!(string|Exif.Tag)} key A key. - * @return {string} Formatted representation. - */ -ByteWriter.prettyKeyFormat = function(key) { - if (typeof key === 'number') { - return '0x' + key.toString(16); - } else { - return key; - } -};
diff --git a/ui/file_manager/gallery/js/image_editor/exif_encoder_unittest.m.js b/ui/file_manager/gallery/js/image_editor/exif_encoder_unittest.m.js deleted file mode 100644 index a75e90b0..0000000 --- a/ui/file_manager/gallery/js/image_editor/exif_encoder_unittest.m.js +++ /dev/null
@@ -1,202 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import {assertEquals, assertTrue} from 'chrome://test/chai_assert.js'; -import {MetadataParserLogger} from '../../../file_manager/externs/metadata_worker_window.m.js'; -import {ByteReader} from '../../../file_manager/foreground/js/metadata/byte_reader.m.js'; -import {ExifParser} from '../../../file_manager/foreground/js/metadata/exif_parser.m.js'; -import {MetadataItem} from '../../../file_manager/foreground/js/metadata/metadata_item.m.js'; -import {ExifEncoder} from './exif_encoder.m.js'; -import {ImageEncoder} from './image_encoder.m.js'; -import {getSampleCanvas} from './test_util.m.js'; - -/** - * @implements {MetadataParserLogger} - * @final - */ -class NoLogger { - constructor() { - this.verbose = false; - } - - error() {} - log() {} - vlog() {} -} - -/** - * Test case for ordinal exif encoding and decoding. - */ -export function testExifEncodeAndDecode() { - const canvas = getSampleCanvas(); - const data = canvas.toDataURL('image/jpeg'); - - const metadata = /** @type {!MetadataItem} */ ({ - mediaMimeType: 'image/jpeg', - modificationTime: new Date(2015, 0, 7, 15, 30, 6), - ifd: { - image: { - // Manufacture - 271: {id: 0x10f, format: 2, componentCount: 12, value: 'Manufacture\0'}, - // Device model - 272: {id: 0x110, format: 2, componentCount: 12, value: 'DeviceModel\0'}, - // GPS Pointer - 34853: { - id: 0x8825, - format: 4, - componentCount: 1, - value: 0 // The value is set by the encoder. - } - }, - exif: { - // Lens model - 42036: {id: 0xa434, format: 2, componentCount: 10, value: 'LensModel\0'} - }, - gps: { - // GPS latitude ref - 1: {id: 0x1, format: 2, componentCount: 2, value: 'N\0'} - } - } - }); - - const encoder = ImageEncoder.encodeMetadata(metadata, canvas, 1); - - // Assert that ExifEncoder is returned. - assertTrue(encoder instanceof ExifEncoder); - - const encodedResult = encoder.encode(); - - // Decode encoded exif data. - const exifParser = new ExifParser(new NoLogger()); - const parsedMetadata = {}; - const byteReader = new ByteReader(encodedResult); - byteReader.readString(2 + 2); // Skip marker and size. - exifParser.parseExifSection(parsedMetadata, encodedResult, byteReader); - - // Check ifd.image. - assertEquals(1, parsedMetadata.ifd.image[0x112].value); // Orientation - - // Since thumbnail is compressed with JPEG, compression must be 6. - assertEquals(6, parsedMetadata.ifd.image[0x102].value); - - // Check ifd.exif. - assertEquals(1920, parsedMetadata.ifd.exif[0xA002].value); // PixelXDimension - assertEquals(1080, parsedMetadata.ifd.exif[0xA003].value); // PixelYDimension - - // These fields should be copied correctly. - // Manufacture - assertEquals('Manufacture\0', parsedMetadata.ifd.image[0x10F].value); - // Device model - assertEquals('DeviceModel\0', parsedMetadata.ifd.image[0x110].value); - // Lens model - assertEquals('LensModel\0', parsedMetadata.ifd.exif[0xa434].value); - // GPS latitude ref - assertEquals('N\0', parsedMetadata.ifd.gps[0x1].value); - - // Software should be set as the Gallery app - assertEquals( - 'Chrome OS Gallery App\0', parsedMetadata.ifd.image[0x131].value); - - // Datetime should be updated. - assertEquals('2015:01:07 15:30:06\0', parsedMetadata.ifd.image[0x132].value); - - // Thumbnail image - assertTrue(!!parsedMetadata.thumbnailTransform); - assertTrue(!!parsedMetadata.thumbnailURL); -} - -/** - * Helper function to encode and measure expected thumbnail size. - * @return {number} Expected thumbnail size. - */ -function measureExpectedThumbnailSize_() { - const canvas = getSampleCanvas(); - const metadata = /** @type {!MetadataItem} */ ({ - mediaMimeType: 'image/jpeg', - modificationTime: new Date(2015, 0, 7, 15, 30, 6) - }); - - const encoder = ImageEncoder.encodeMetadata(metadata, canvas, 1); - /** @suppress {accessControls} */ - function getThumbnailDataUrlForTest() { - return encoder.thumbnailDataUrl; - } - return ImageEncoder.decodeDataURL(getThumbnailDataUrlForTest()).length; -} - -/** - * Helper function for testing that exif encoder drops thumbnail data if there - * isn't enough space. - * - * @param {number} largeFieldValueSize Size of the large value. - * @param {boolean} expectThumbnail True if thumbnail is expected to be written. - */ -function largeExifDataTestHelper_(largeFieldValueSize, expectThumbnail) { - const canvas = getSampleCanvas(); - - // Generate a long string. - let longString = '0'.repeat(largeFieldValueSize - 1); - longString += '\0'; - - const metadata = /** @type{!MetadataItem} */ ({ - mediaMimeType: 'image/jpeg', - modificationTime: new Date(2015, 0, 7, 15, 30, 6), - ifd: { - image: { - // Manufacture - 271: { - id: 0x10f, - format: 2, - componentCount: longString.length, - value: longString - } - } - } - }); - - const encoder = ImageEncoder.encodeMetadata(metadata, canvas, 1); - - // For failure case, an error is thrown. - const encodedResult = encoder.encode(); - - // Decode encoded exif data and check thumbnail is written or not. - const exifParser = new ExifParser(new NoLogger()); - const parsedMetadata = {}; - const byteReader = new ByteReader(encodedResult); - byteReader.readString(2 + 2); // Skip marker and size. - exifParser.parseExifSection(parsedMetadata, encodedResult, byteReader); - - assertEquals(expectThumbnail, !!parsedMetadata.thumbnailURL); -} - -/** - * Test case when other exif data is small as the thumbnail can fit in. - */ -export function testLargeExifDataSmallCase() { - // 158 bytes: other exif data except value of the large field. - largeExifDataTestHelper_( - ExifEncoder.MAXIMUM_EXIF_DATA_SIZE - measureExpectedThumbnailSize_() - - ExifEncoder.THUMBNAIL_METADATA_SIZE - 158 - 1, - true); -} - -/** - * Test case when other exif data is large as the thumbnail can just fit in. - */ -export function testLargeExifDataBoundaryCase() { - largeExifDataTestHelper_( - ExifEncoder.MAXIMUM_EXIF_DATA_SIZE - measureExpectedThumbnailSize_() - - ExifEncoder.THUMBNAIL_METADATA_SIZE - 158, - true); -} - -/** - * Test case when other exif data is large as the thumbnail cannot fit in. - */ -export function testLargeExifDataExceedsCase() { - largeExifDataTestHelper_( - ExifEncoder.MAXIMUM_EXIF_DATA_SIZE - measureExpectedThumbnailSize_() - - ExifEncoder.THUMBNAIL_METADATA_SIZE - 158 + 1, - false); -}
diff --git a/ui/file_manager/gallery/js/image_editor/filter.js b/ui/file_manager/gallery/js/image_editor/filter.js deleted file mode 100644 index 703d38a..0000000 --- a/ui/file_manager/gallery/js/image_editor/filter.js +++ /dev/null
@@ -1,630 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * A namespace for image filter utilities. - */ -var filter = {}; - -/** - * Create a filter from name and options. - * - * @param {string} name Maps to a filter method name. - * @param {!Object} options A map of filter-specific options. - * @return {function(!ImageData,!ImageData,number,number)} created function. - */ -filter.create = function(name, options) { - var filterFunc = filter[name](options); - return function() { - var time = Date.now(); - filterFunc.apply(null, arguments); - var dst = arguments[0]; - var mPixPerSec = dst.width * dst.height / 1000 / (Date.now() - time); - ImageUtil.trace.report(name, Math.round(mPixPerSec * 10) / 10 + 'Mps'); - }; -}; - -/** - * Apply a filter to a image by splitting it into strips. - * - * To be used with large images to avoid freezing up the UI. - * - * @param {!HTMLCanvasElement} dstCanvas Destination canvas. - * @param {!HTMLCanvasElement|!HTMLImageElement} srcImage Source image. - * @param {function(!ImageData,!ImageData,number,number)} filterFunc Filter. - * @param {function(number, number)} progressCallback Progress callback. - * @param {number=} opt_maxPixelsPerStrip Pixel number to process at once. - */ -filter.applyByStrips = function( - dstCanvas, srcImage, filterFunc, progressCallback, opt_maxPixelsPerStrip) { - // 1 Mpix is a reasonable default. - var maxPixelsPerStrip = opt_maxPixelsPerStrip || 1000000; - - var dstContext = dstCanvas.getContext('2d'); - var srcContext = ImageUtil.ensureCanvas(srcImage).getContext('2d'); - var source = srcContext.getImageData(0, 0, srcImage.width, srcImage.height); - - var stripCount = Math.ceil(srcImage.width * srcImage.height / - maxPixelsPerStrip); - - var strip = srcContext.getImageData(0, 0, - srcImage.width, Math.ceil(srcImage.height / stripCount)); - - var offset = 0; - - function filterStrip() { - // If the strip overlaps the bottom of the source image we cannot shrink it - // and we cannot fill it partially (since canvas.putImageData always draws - // the entire buffer). - // Instead we move the strip up several lines (converting those lines - // twice is a small price to pay). - if (offset > source.height - strip.height) { - offset = source.height - strip.height; - } - - filterFunc(strip, source, 0, offset); - dstContext.putImageData(strip, 0, offset); - - offset += strip.height; - - if (offset < source.height) { - setTimeout(filterStrip, 0); - } else { - ImageUtil.trace.reportTimer('filter-commit'); - } - - progressCallback(offset, source.height); - } - - ImageUtil.trace.resetTimer('filter-commit'); - filterStrip(); -}; - -/** - * Return a color histogram for an image. - * - * @param {!(HTMLCanvasElement|ImageData)} source Image data to analyze. - * @return {{r: !Array<number>, g: !Array<number>, b: !Array<number>}} - * histogram. - */ -filter.getHistogram = function(source) { - var imageData; - if (source.constructor.name == 'HTMLCanvasElement') { - imageData = source.getContext('2d'). - getImageData(0, 0, source.width, source.height); - } else { - imageData = source; - } - - var r = []; - var g = []; - var b = []; - - for (var i = 0; i != 256; i++) { - r.push(0); - g.push(0); - b.push(0); - } - - var data = imageData.data; - var maxIndex = 4 * imageData.width * imageData.height; - for (var index = 0; index != maxIndex;) { - r[data[index++]]++; - g[data[index++]]++; - b[data[index++]]++; - index++; - } - - return { r: r, g: g, b: b }; -}; - -/** - * Compute the function for every integer value from 0 up to maxArg. - * - * Rounds and clips the results to fit the [0..255] range. - * Useful to speed up pixel manipulations. - * - * @param {number} maxArg Maximum argument value (inclusive). - * @param {function(number): number} func Function to precompute. - * @return {!Uint8Array} Computed results. - */ -filter.precompute = function(maxArg, func) { - var results = new Uint8Array(maxArg + 1); - for (var arg = 0; arg <= maxArg; arg++) { - results[arg] = Math.max(0, Math.min(0xFF, Math.round(func(arg)))); - } - return results; -}; - -/** - * Convert pixels by applying conversion tables to each channel individually. - * - * @param {!Uint8Array} rMap Red channel conversion table. - * @param {!Uint8Array} gMap Green channel conversion table. - * @param {!Uint8Array} bMap Blue channel conversion table. - * @param {!ImageData} dst Destination image data. Can be smaller than the - * source, must completely fit inside the source. - * @param {!ImageData} src Source image data. - * @param {!number} offsetX Horizontal offset of dst relative to src. - * @param {!number} offsetY Vertical offset of dst relative to src. - */ -filter.mapPixels = function(rMap, gMap, bMap, dst, src, offsetX, offsetY) { - var dstData = dst.data; - var dstWidth = dst.width; - var dstHeight = dst.height; - - var srcData = src.data; - var srcWidth = src.width; - var srcHeight = src.height; - - if (offsetX < 0 || offsetX + dstWidth > srcWidth || offsetY < 0 || - offsetY + dstHeight > srcHeight) { - throw new Error('Invalid offset'); - } - - var dstIndex = 0; - for (var y = 0; y != dstHeight; y++) { - var srcIndex = (offsetX + (offsetY + y) * srcWidth) * 4; - for (var x = 0; x != dstWidth; x++) { - dstData[dstIndex++] = rMap[srcData[srcIndex++]]; - dstData[dstIndex++] = gMap[srcData[srcIndex++]]; - dstData[dstIndex++] = bMap[srcData[srcIndex++]]; - dstIndex++; - srcIndex++; - } - } -}; - -/** - * Number of digits after period(in binary form) to preserve. - * @type {number} - * @const - */ -filter.FIXED_POINT_SHIFT = 16; - -/** - * Maximum value that can be represented in fixed point without overflow. - * @type {number} - * @const - */ -filter.MAX_FLOAT_VALUE = 0x7FFFFFFF >> filter.FIXED_POINT_SHIFT; - -/** - * Converts floating point to fixed. - * @param {number} x Number to convert. - * @return {number} Converted number. - */ -filter.floatToFixedPoint = function(x) { - // Math.round on negative arguments causes V8 to deoptimize the calling - // function, so we are using >> 0 instead. - return (x * (1 << filter.FIXED_POINT_SHIFT)) >> 0; -}; - -/** - * Perform an image convolution with a symmetrical 5x5 matrix: - * - * 0 0 w3 0 0 - * 0 w2 w1 w2 0 - * w3 w1 w0 w1 w3 - * 0 w2 w1 w2 0 - * 0 0 w3 0 0 - * - * @param {!Array<number>} weights See the picture above. - * @param {!ImageData} dst Destination image data. Can be smaller than the - * source, must completely fit inside the source. - * @param {!ImageData} src Source image data. - * @param {number} offsetX Horizontal offset of dst relative to src. - * @param {number} offsetY Vertical offset of dst relative to src. - */ -filter.convolve5x5 = function(weights, dst, src, offsetX, offsetY) { - var w0 = filter.floatToFixedPoint(weights[0]); - var w1 = filter.floatToFixedPoint(weights[1]); - var w2 = filter.floatToFixedPoint(weights[2]); - var w3 = filter.floatToFixedPoint(weights[3]); - - var dstData = dst.data; - var dstWidth = dst.width; - var dstHeight = dst.height; - var dstStride = dstWidth * 4; - - var srcData = src.data; - var srcWidth = src.width; - var srcHeight = src.height; - var srcStride = srcWidth * 4; - var srcStride2 = srcStride * 2; - - if (offsetX < 0 || offsetX + dstWidth > srcWidth || offsetY < 0 || - offsetY + dstHeight > srcHeight) { - throw new Error('Invalid offset'); - } - - // Javascript is not very good at inlining constants. - // We inline manually and assert that the constant is equal to the variable. - if (filter.FIXED_POINT_SHIFT != 16) { - throw new Error('Wrong fixed point shift'); - } - - var margin = 2; - - var startX = Math.max(0, margin - offsetX); - var endX = Math.min(dstWidth, srcWidth - margin - offsetX); - - var startY = Math.max(0, margin - offsetY); - var endY = Math.min(dstHeight, srcHeight - margin - offsetY); - - for (var y = startY; y != endY; y++) { - var dstIndex = y * dstStride + startX * 4; - var srcIndex = (y + offsetY) * srcStride + (startX + offsetX) * 4; - - for (var x = startX; x != endX; x++) { - for (var c = 0; c != 3; c++) { - var sum = w0 * srcData[srcIndex] + - w1 * (srcData[srcIndex - 4] + - srcData[srcIndex + 4] + - srcData[srcIndex - srcStride] + - srcData[srcIndex + srcStride]) + - w2 * (srcData[srcIndex - srcStride - 4] + - srcData[srcIndex + srcStride - 4] + - srcData[srcIndex - srcStride + 4] + - srcData[srcIndex + srcStride + 4]) + - w3 * (srcData[srcIndex - 8] + - srcData[srcIndex + 8] + - srcData[srcIndex - srcStride2] + - srcData[srcIndex + srcStride2]); - if (sum < 0) { - dstData[dstIndex++] = 0; - } else if (sum > 0xFF0000) { - dstData[dstIndex++] = 0xFF; - } else { - dstData[dstIndex++] = sum >> 16; - } - srcIndex++; - } - srcIndex++; - dstIndex++; - } - } -}; - -/** - * Compute the average color for the image. - * - * @param {!ImageData} imageData Image data to analyze. - * @return {{r: number, g: number, b: number}} average color. - */ -filter.getAverageColor = function(imageData) { - var data = imageData.data; - var width = imageData.width; - var height = imageData.height; - - var total = 0; - var r = 0; - var g = 0; - var b = 0; - - var maxIndex = 4 * width * height; - for (var i = 0; i != maxIndex;) { - total++; - r += data[i++]; - g += data[i++]; - b += data[i++]; - i++; - } - if (total == 0) return { r: 0, g: 0, b: 0 }; - return { r: r / total, g: g / total, b: b / total }; -}; - -/** - * Compute the average color with more weight given to pixes at the center. - * - * @param {!ImageData} imageData Image data to analyze. - * @return {{r: number, g: number, b: number}} weighted average color. - */ -filter.getWeightedAverageColor = function(imageData) { - var data = imageData.data; - var width = imageData.width; - var height = imageData.height; - - var total = 0; - var r = 0; - var g = 0; - var b = 0; - - var center = Math.floor(width / 2); - var maxDist = center * Math.sqrt(2); - maxDist *= 2; // Weaken the effect of distance - - var i = 0; - for (var x = 0; x != width; x++) { - for (var y = 0; y != height; y++) { - var dist = Math.sqrt( - (x - center) * (x - center) + (y - center) * (y - center)); - var weight = (maxDist - dist) / maxDist; - - total += weight; - r += data[i++] * weight; - g += data[i++] * weight; - b += data[i++] * weight; - i++; - } - } - if (total == 0) return { r: 0, g: 0, b: 0 }; - return { r: r / total, g: g / total, b: b / total }; -}; - -/** - * Copy part of src image to dst, applying matrix color filter on-the-fly. - * - * The copied part of src should completely fit into dst (there is no clipping - * on either side). - * - * @param {!Array<number>} matrix 3x3 color matrix. - * @param {!ImageData} dst Destination image data. - * @param {!ImageData} src Source image data. - * @param {number} offsetX X offset in source to start processing. - * @param {number} offsetY Y offset in source to start processing. - */ -filter.colorMatrix3x3 = function(matrix, dst, src, offsetX, offsetY) { - var c11 = filter.floatToFixedPoint(matrix[0]); - var c12 = filter.floatToFixedPoint(matrix[1]); - var c13 = filter.floatToFixedPoint(matrix[2]); - var c21 = filter.floatToFixedPoint(matrix[3]); - var c22 = filter.floatToFixedPoint(matrix[4]); - var c23 = filter.floatToFixedPoint(matrix[5]); - var c31 = filter.floatToFixedPoint(matrix[6]); - var c32 = filter.floatToFixedPoint(matrix[7]); - var c33 = filter.floatToFixedPoint(matrix[8]); - - var dstData = dst.data; - var dstWidth = dst.width; - var dstHeight = dst.height; - - var srcData = src.data; - var srcWidth = src.width; - var srcHeight = src.height; - - if (offsetX < 0 || offsetX + dstWidth > srcWidth || offsetY < 0 || - offsetY + dstHeight > srcHeight) { - throw new Error('Invalid offset'); - } - - // Javascript is not very good at inlining constants. - // We inline manually and assert that the constant is equal to the variable. - if (filter.FIXED_POINT_SHIFT != 16) { - throw new Error('Wrong fixed point shift'); - } - - var dstIndex = 0; - for (var y = 0; y != dstHeight; y++) { - var srcIndex = (offsetX + (offsetY + y) * srcWidth) * 4; - for (var x = 0; x != dstWidth; x++) { - var r = srcData[srcIndex++]; - var g = srcData[srcIndex++]; - var b = srcData[srcIndex++]; - srcIndex++; - - var rNew = r * c11 + g * c12 + b * c13; - var gNew = r * c21 + g * c22 + b * c23; - var bNew = r * c31 + g * c32 + b * c33; - - if (rNew < 0) { - dstData[dstIndex++] = 0; - } else if (rNew > 0xFF0000) { - dstData[dstIndex++] = 0xFF; - } else { - dstData[dstIndex++] = rNew >> 16; - } - - if (gNew < 0) { - dstData[dstIndex++] = 0; - } else if (gNew > 0xFF0000) { - dstData[dstIndex++] = 0xFF; - } else { - dstData[dstIndex++] = gNew >> 16; - } - - if (bNew < 0) { - dstData[dstIndex++] = 0; - } else if (bNew > 0xFF0000) { - dstData[dstIndex++] = 0xFF; - } else { - dstData[dstIndex++] = bNew >> 16; - } - - dstIndex++; - } - } -}; - -/** - * Return a convolution filter function bound to specific weights. - * - * @param {!Array<number>} weights Weights for the convolution matrix - * (not normalized). - * @return {function(!ImageData,!ImageData,number,number)} Convolution filter. - */ -filter.createConvolutionFilter = function(weights) { - // Normalize the weights to sum to 1. - var total = 0; - for (var i = 0; i != weights.length; i++) { - total += weights[i] * (i ? 4 : 1); - } - - var normalized = []; - for (i = 0; i != weights.length; i++) { - normalized.push(weights[i] / total); - } - for (; i < 4; i++) { - normalized.push(0); - } - - var maxWeightedSum = 0xFF * - Math.abs(normalized[0]) + - Math.abs(normalized[1]) * 4 + - Math.abs(normalized[2]) * 4 + - Math.abs(normalized[3]) * 4; - if (maxWeightedSum > filter.MAX_FLOAT_VALUE) { - throw new Error('convolve5x5 cannot convert the weights to fixed point'); - } - - return filter.convolve5x5.bind(null, normalized); -}; - -/** - * Creates matrix filter. - * @param {!Array<number>} matrix Color transformation matrix. - * @return {function(!ImageData,!ImageData,number,number)} Matrix filter. - */ -filter.createColorMatrixFilter = function(matrix) { - for (var r = 0; r != 3; r++) { - var maxRowSum = 0; - for (var c = 0; c != 3; c++) { - maxRowSum += 0xFF * Math.abs(matrix[r * 3 + c]); - } - if (maxRowSum > filter.MAX_FLOAT_VALUE) { - throw new Error( - 'colorMatrix3x3 cannot convert the matrix to fixed point'); - } - } - return filter.colorMatrix3x3.bind(null, matrix); -}; - -/** - * Return a blur filter. - * @param {{radius: number, strength: number}} options Blur options. - * @return {function(!ImageData,!ImageData,number,number)} Blur filter. - */ -filter.blur = function(options) { - if (options.radius == 1) { - return filter.createConvolutionFilter([1, options.strength]); - } else if (options.radius == 2) { - return filter.createConvolutionFilter( - [1, options.strength, options.strength]); - } else { - return filter.createConvolutionFilter( - [1, options.strength, options.strength, options.strength]); - } -}; - -/** - * Return a sharpen filter. - * @param {{radius: number, strength: number}} options Sharpen options. - * @return {function(!ImageData,!ImageData,number,number)} Sharpen filter. - */ -filter.sharpen = function(options) { - if (options.radius == 1) { - return filter.createConvolutionFilter([5, -options.strength]); - } else if (options.radius == 2) { - return filter.createConvolutionFilter( - [10, -options.strength, -options.strength]); - } else { - return filter.createConvolutionFilter( - [15, -options.strength, -options.strength, -options.strength]); - } -}; - -/** - * Return an exposure filter. - * @param {{brightness: number, contrast: number}} options exposure options. - * @return {function(!ImageData,!ImageData,number,number)} Exposure filter. - */ -filter.exposure = function(options) { - var pixelMap = filter.precompute( - 255, - function(value) { - if (options.brightness > 0) { - value *= (1 + options.brightness); - } else { - value += (0xFF - value) * options.brightness; - } - return 0x80 + - (value - 0x80) * Math.tan((options.contrast + 1) * Math.PI / 4); - }); - - return filter.mapPixels.bind(null, pixelMap, pixelMap, pixelMap); -}; - -/** - * Return a color autofix filter. - * @param {{histogram: - * {r: !Array<number>, g: !Array<number>, b: !Array<number>}}} options - * Histogram for autofix. - * @return {function(!ImageData,!ImageData,number,number)} Autofix filter. - */ -filter.autofix = function(options) { - return filter.mapPixels.bind(null, - filter.autofix.stretchColors(options.histogram.r), - filter.autofix.stretchColors(options.histogram.g), - filter.autofix.stretchColors(options.histogram.b)); -}; - -/** - * Return a conversion table that stretches the range of colors used - * in the image to 0..255. - * @param {!Array<number>} channelHistogram Histogram to calculate range. - * @return {!Uint8Array} Color mapping array. - */ -filter.autofix.stretchColors = function(channelHistogram) { - var range = filter.autofix.getRange(channelHistogram); - return filter.precompute( - 255, - function(x) { - return (x - range.first) / (range.last - range.first) * 255; - } - ); -}; - -/** - * Return a range that encloses non-zero elements values in a histogram array. - * @param {!Array<number>} channelHistogram Histogram to analyze. - * @return {{first: number, last: number}} Channel range in histogram. - */ -filter.autofix.getRange = function(channelHistogram) { - var first = 0; - while (first < channelHistogram.length && channelHistogram[first] == 0) { - first++; - } - - var last = channelHistogram.length - 1; - while (last >= 0 && channelHistogram[last] == 0) { - last--; - } - - if (first >= last) { // Stretching does not make sense - return {first: 0, last: channelHistogram.length - 1}; - } else { - return {first: first, last: last}; - } -}; - -/** - * Minimum channel offset that makes visual difference. If autofix calculated - * offset is less than SENSITIVITY, probably autofix is not needed. - * Reasonable empirical value. - * @type {number} - * @const - */ -filter.autofix.SENSITIVITY = 8; - -/** - * @param {!Array<number>} channelHistogram Histogram to analyze. - * @return {boolean} True if stretching this range to 0..255 would make - * a visible difference. - */ -filter.autofix.needsStretching = function(channelHistogram) { - var range = filter.autofix.getRange(channelHistogram); - return (range.first >= filter.autofix.SENSITIVITY || - range.last <= 255 - filter.autofix.SENSITIVITY); -}; - -/** - * @param {{r: !Array<number>, g: !Array<number>, b: !Array<number>}} - * histogram - * @return {boolean} True if the autofix would make a visible difference. - */ -filter.autofix.isApplicable = function(histogram) { - return filter.autofix.needsStretching(histogram.r) || - filter.autofix.needsStretching(histogram.g) || - filter.autofix.needsStretching(histogram.b); -};
diff --git a/ui/file_manager/gallery/js/image_editor/image_adjust.js b/ui/file_manager/gallery/js/image_editor/image_adjust.js deleted file mode 100644 index fee64d8f..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_adjust.js +++ /dev/null
@@ -1,277 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * The base class for simple filters that only modify the image content - * but do not modify the image dimensions. - * @param {string} name - * @param {string} title - * @constructor - * @struct - * @extends {ImageEditorMode} - */ -ImageEditorMode.Adjust = function(name, title) { - ImageEditorMode.call(this, name, title); - - /** - * @type {boolean} - * @const - */ - this.implicitCommit = true; - - /** - * @type {?string} - * @private - */ - this.doneMessage_ = null; - - /** - * @type {number} - * @private - */ - this.viewportGeneration_ = 0; - - /** - * @type {?function(!ImageData,!ImageData,number,number)} - * @private - */ - this.filter_ = null; - - /** - * @type {HTMLCanvasElement} - * @private - */ - this.canvas_ = null; - - /** - * @private {ImageData} - */ - this.previewImageData_ = null; - - /** - * @private {ImageData} - */ - this.originalImageData_ = null; -}; - -ImageEditorMode.Adjust.prototype = { - __proto__: ImageEditorMode.prototype -}; - -/** - * Gets command to do filter. - * - * @return {Command.Filter} Filter command. - */ -ImageEditorMode.Adjust.prototype.getCommand = function() { - if (!this.filter_) { - return null; - } - - return new Command.Filter(this.name, this.filter_, this.doneMessage_); -}; - -/** @override */ -ImageEditorMode.Adjust.prototype.cleanUpUI = function() { - ImageEditorMode.prototype.cleanUpUI.apply(this, arguments); - this.hidePreview(); -}; - -/** - * Hides preview. - */ -ImageEditorMode.Adjust.prototype.hidePreview = function() { - if (this.canvas_) { - this.canvas_.parentNode.removeChild(this.canvas_); - this.canvas_ = null; - } -}; - -/** @override */ -ImageEditorMode.Adjust.prototype.cleanUpCaches = function() { - this.filter_ = null; - this.previewImageData_ = null; -}; - -/** @override */ -ImageEditorMode.Adjust.prototype.reset = function() { - ImageEditorMode.prototype.reset.call(this); - this.hidePreview(); - this.cleanUpCaches(); -}; - -/** @override */ -ImageEditorMode.Adjust.prototype.update = function(options) { - ImageEditorMode.prototype.update.apply(this, arguments); - this.updatePreviewImage_(options); -}; - -/** - * Copy the source image data for the preview. - * Use the cached copy if the viewport has not changed. - * @param {Object} options Options that describe the filter. It it is null, it - * does not update current filter. - * @private - */ -ImageEditorMode.Adjust.prototype.updatePreviewImage_ = function(options) { - assert(this.getViewport()); - - var isPreviewImageInvalidated = false; - - // Update filter. - if (options) { - // We assume filter names are used in the UI directly. - // This will have to change with i18n. - this.filter_ = this.createFilter(options); - isPreviewImageInvalidated = true; - } - - // Update canvas size and/or transformation. - if (!this.previewImageData_ || - this.viewportGeneration_ !== this.getViewport().getCacheGeneration()) { - this.viewportGeneration_ = this.getViewport().getCacheGeneration(); - - if (!this.canvas_) { - this.canvas_ = this.getImageView().createOverlayCanvas(); - } - - this.getImageView().setupDeviceBuffer(this.canvas_); - var canvas = this.getImageView().getImageCanvasWith( - this.canvas_.width, this.canvas_.height); - var context = canvas.getContext('2d'); - this.originalImageData_ = context.getImageData(0, 0, - this.canvas_.width, this.canvas_.height); - this.previewImageData_ = context.getImageData(0, 0, - this.canvas_.width, this.canvas_.height); - - isPreviewImageInvalidated = true; - } else { - this.getImageView().setTransform_( - assert(this.canvas_), assert(this.getViewport())); - } - - // Update preview image with applying filter. - if (isPreviewImageInvalidated) { - assert(this.originalImageData_); - assert(this.previewImageData_); - - ImageUtil.trace.resetTimer('preview'); - this.filter_(this.previewImageData_, this.originalImageData_, 0, 0); - ImageUtil.trace.reportTimer('preview'); - - this.canvas_.getContext('2d').putImageData(this.previewImageData_, 0, 0); - } -}; - -/** @override */ -ImageEditorMode.Adjust.prototype.draw = function() { - this.updatePreviewImage_(null); -}; - -/* - * Own methods - */ - -/** - * Creates a filter. - * @param {!Object} options A map of filter-specific options. - * @return {function(!ImageData,!ImageData,number,number)} Created function. - */ -ImageEditorMode.Adjust.prototype.createFilter = function(options) { - return filter.create(this.name, options); -}; - -/** - * A base class for color filters that are scale independent. - * @constructor - * @param {string} name The mode name. - * @param {string} title The mode title. - * @extends {ImageEditorMode.Adjust} - * @struct - */ -ImageEditorMode.ColorFilter = function(name, title) { - ImageEditorMode.Adjust.call(this, name, title); -}; - -ImageEditorMode.ColorFilter.prototype = { - __proto__: ImageEditorMode.Adjust.prototype -}; - -/** - * Gets a histogram from a thumbnail. - * @return {{r: !Array<number>, g: !Array<number>, b: !Array<number>}} - * histogram. - */ -ImageEditorMode.ColorFilter.prototype.getHistogram = function() { - return filter.getHistogram(this.getImageView().getThumbnail()); -}; - -/** - * Exposure/contrast filter. - * @constructor - * @extends {ImageEditorMode.ColorFilter} - * @struct - */ -ImageEditorMode.Exposure = function() { - ImageEditorMode.ColorFilter.call(this, 'exposure', 'GALLERY_EXPOSURE'); -}; - -ImageEditorMode.Exposure.prototype = { - __proto__: ImageEditorMode.ColorFilter.prototype -}; - -/** @override */ -ImageEditorMode.Exposure.prototype.createTools = function(toolbar) { - toolbar.addRange('brightness', 'GALLERY_BRIGHTNESS', -1, 0, 1, 100); - toolbar.addRange('contrast', 'GALLERY_CONTRAST', -1, 0, 1, 100); -}; - -/** - * Autofix. - * @constructor - * @struct - * @extends {ImageEditorMode.ColorFilter} - */ -ImageEditorMode.Autofix = function() { - ImageEditorMode.ColorFilter.call(this, 'autofix', 'GALLERY_AUTOFIX'); - this.doneMessage_ = 'GALLERY_FIXED'; -}; - -ImageEditorMode.Autofix.prototype = { - __proto__: ImageEditorMode.ColorFilter.prototype -}; - -/** @override */ -ImageEditorMode.Autofix.prototype.isApplicable = function() { - return this.getImageView().hasValidImage() && - filter.autofix.isApplicable(this.getHistogram()); -}; - -/** - * Applies autofix. - */ -ImageEditorMode.Autofix.prototype.apply = function() { - this.update({histogram: this.getHistogram()}); -}; - -/** - * Instant Autofix. - * @constructor - * @extends {ImageEditorMode.Autofix} - * @struct - */ -ImageEditorMode.InstantAutofix = function() { - ImageEditorMode.Autofix.call(this); - this.instant = true; -}; - -ImageEditorMode.InstantAutofix.prototype = { - __proto__: ImageEditorMode.Autofix.prototype -}; - -/** @override */ -ImageEditorMode.InstantAutofix.prototype.setUp = function() { - ImageEditorMode.Autofix.prototype.setUp.apply(this, arguments); - this.apply(); -};
diff --git a/ui/file_manager/gallery/js/image_editor/image_buffer.js b/ui/file_manager/gallery/js/image_editor/image_buffer.js deleted file mode 100644 index eac65363..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_buffer.js +++ /dev/null
@@ -1,193 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * A stack of overlays that display itself and handle mouse events. - * TODO(kaznacheev) Consider disbanding this class and moving - * the functionality to individual objects that display anything or handle - * mouse events. - * @constructor - * @struct - */ -function ImageBuffer() { - this.overlays_ = []; -} - -/** - * @typedef {function(number, number, boolean)} - */ -ImageBuffer.DragHandler; - -/** - * Add an overlay to a buffer. - * @param {!ImageBuffer.Overlay} overlay An overlay added to a buffer. - */ -ImageBuffer.prototype.addOverlay = function(overlay) { - var zIndex = overlay.getZIndex(); - // Store the overlays in the ascending Z-order. - var i; - for (i = 0; i != this.overlays_.length; i++) { - if (zIndex < this.overlays_[i].getZIndex()) break; - } - this.overlays_.splice(i, 0, overlay); -}; - -/** - * Remove an overlay from a buffer. - * @param {!ImageBuffer.Overlay} overlay An overlay removed from a buffer. - */ -ImageBuffer.prototype.removeOverlay = function(overlay) { - for (var i = 0; i != this.overlays_.length; i++) { - if (this.overlays_[i] == overlay) { - this.overlays_.splice(i, 1); - return; - } - } - throw new Error('Cannot remove overlay ' + overlay); -}; - -/** - * Draws overlays in the ascending Z-order. - */ -ImageBuffer.prototype.draw = function() { - for (var i = 0; i != this.overlays_.length; i++) { - this.overlays_[i].draw(); - } -}; - -/** - * Searches for a cursor style in the descending Z-order. - * @param {number} x X coordinate for cursor. - * @param {number} y Y coordinate for cursor. - * @param {boolean} mouseDown If mouse button is down. - * @return {string} A value for style.cursor CSS property. - */ -ImageBuffer.prototype.getCursorStyle = function(x, y, mouseDown) { - for (var i = this.overlays_.length - 1; i >= 0; i--) { - var style = this.overlays_[i].getCursorStyle(x, y, mouseDown); - if (style) return style; - } - return 'default'; -}; - -/** - * Searches for a click handler in the descending Z-order. - * @param {number} x X coordinate for click event. - * @param {number} y Y coordinate for click event. - * @return {boolean} True if handled. - */ -ImageBuffer.prototype.onClick = function(x, y) { - for (var i = this.overlays_.length - 1; i >= 0; i--) { - if (this.overlays_[i].onClick(x, y)) return true; - } - return false; -}; - -/** - * Searches for a drag handler in the descending Z-order. - * @param {number} x Event X coordinate. - * @param {number} y Event Y coordinate. - * @param {boolean} touch True if it's a touch event, false if mouse. - * @return {?ImageBuffer.DragHandler} A function to be called on mouse drag. - */ -ImageBuffer.prototype.getDragHandler = function(x, y, touch) { - for (var i = this.overlays_.length - 1; i >= 0; i--) { - var handler = this.overlays_[i].getDragHandler(x, y, touch); - if (handler) { - return handler; - } - } - return null; -}; - -/** - * Searches for an action for the double tap enumerating - * layers in the descending Z-order. - * @param {number} x X coordinate of the event. - * @param {number} y Y coordinate of the event. - * @return {!ImageBuffer.DoubleTapAction} Action to perform as result. - */ -ImageBuffer.prototype.getDoubleTapAction = function(x, y) { - for (var i = this.overlays_.length - 1; i >= 0; i--) { - var action = this.overlays_[i].getDoubleTapAction(x, y); - if (action != ImageBuffer.DoubleTapAction.NOTHING) { - return action; - } - } - return ImageBuffer.DoubleTapAction.NOTHING; -}; - -/** - * Possible double tap actions. - * @enum {number} - */ -ImageBuffer.DoubleTapAction = { - NOTHING: 0, - COMMIT: 1, - CANCEL: 2 -}; - -/** - * ImageBuffer.Overlay is a pluggable extension that modifies the outlook - * and the behavior of the ImageBuffer instance. - * @constructor - * @struct - */ -ImageBuffer.Overlay = function() {}; - -/** - * Get Z index of this overlay. - * @return {number} Z index of this overlay. - */ -ImageBuffer.Overlay.prototype.getZIndex = function() { - return 0; -}; - -/** - * Draw an overlay. - */ -ImageBuffer.Overlay.prototype.draw = function() {}; - -/** - * Get cursor style. - * @param {number} x X coordinate for cursor. - * @param {number} y Y coordinate for cursor. - * @param {boolean} mouseDown If mouse button is down. - * @return {?string} A value for style.cursor CSS property or null for - * default. - */ -ImageBuffer.Overlay.prototype.getCursorStyle = function(x, y, mouseDown) { - return null; -}; - -/** - * Handle click. - * @param {number} x X coordinate. - * @param {number} y Y coordinate. - * @return {boolean} True if handled. - */ -ImageBuffer.Overlay.prototype.onClick = function(x, y) { - return false; -}; - -/** - * Returns a drag handler. - * @param {number} x Event X coordinate. - * @param {number} y Event Y coordinate. - * @param {boolean} touch True if it's a touch event, false if mouse. - * @return {?ImageBuffer.DragHandler} A function to be called on mouse drag. - */ -ImageBuffer.Overlay.prototype.getDragHandler = function(x, y, touch) { - return null; -}; - -/** - * Returns an action for a double tap. - * @param {number} x X coordinate. - * @param {number} y Y coordinate. - * @return {ImageBuffer.DoubleTapAction} Double tap action. - */ -ImageBuffer.Overlay.prototype.getDoubleTapAction = function(x, y) { - return ImageBuffer.DoubleTapAction.NOTHING; -};
diff --git a/ui/file_manager/gallery/js/image_editor/image_editor.js b/ui/file_manager/gallery/js/image_editor/image_editor.js deleted file mode 100644 index 68dcb9a4..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_editor.js +++ /dev/null
@@ -1,938 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/* eslint-disable no-var */ - -/** - * ImageEditor is the top level object that holds together and connects - * everything needed for image editing. - */ -class ImageEditor extends cr.EventTarget { - /** - * @param {!Viewport} viewport The viewport. - * @param {!ImageView} imageView The ImageView containing the images to edit. - * @param {!ImageEditorPrompt} prompt Prompt instance. - * @param {!{image: !HTMLElement, root: !HTMLElement, toolbar: !HTMLElement, - * mode: !HTMLElement}} DOMContainers Various DOM containers required for the - * editor. - * @param {!Array<!ImageEditorMode>} modes Available editor modes. - * @param {function(string, ...string)} displayStringFunction String - * formatting function. - * - * TODO(yawano): Remove displayStringFunction from arguments. - */ - constructor( - viewport, imageView, prompt, DOMContainers, modes, - displayStringFunction) { - super(); - - this.rootContainer_ = DOMContainers.root; - this.container_ = DOMContainers.image; - this.modes_ = modes; - this.displayStringFunction_ = displayStringFunction; - - /** - * @private {ImageEditorMode} - */ - this.currentMode_ = null; - - /** - * @private {HTMLElement} - */ - this.currentTool_ = null; - - /** - * @private {boolean} - */ - this.settingUpNextMode_ = false; - - ImageUtil.removeChildren(this.container_); - - this.viewport_ = viewport; - - this.imageView_ = imageView; - - this.buffer_ = new ImageBuffer(); - this.buffer_.addOverlay(this.imageView_); - - this.panControl_ = new ImageEditor.MouseControl( - this.rootContainer_, this.container_, this.getBuffer()); - this.panControl_.setDoubleTapCallback(this.onDoubleTap_.bind(this)); - - this.mainToolbar_ = - new ImageEditorToolbar(DOMContainers.toolbar, displayStringFunction); - - this.modeToolbar_ = new ImageEditorToolbar( - DOMContainers.mode, displayStringFunction, - this.onOptionsChange.bind(this), true /* done button */); - this.modeToolbar_.addEventListener( - 'done-clicked', this.onDoneClicked_.bind(this)); - this.modeToolbar_.addEventListener( - 'cancel-clicked', this.onCancelClicked_.bind(this)); - - this.prompt_ = prompt; - - this.commandQueue_ = null; - - // ----------------------------------------------------------------- - // Populate the toolbar. - - /** - * @type {!Array<string>} - * @private - */ - this.actionNames_ = []; - - this.mainToolbar_.clear(); - - // Create action buttons. - for (var i = 0; i != this.modes_.length; i++) { - var mode = this.modes_[i]; - var button = this.createToolButton_( - mode.name, mode.title, this.enterMode.bind(this, mode), mode.instant); - mode.bind( - button, this.getBuffer(), this.getViewport(), this.getImageView()); - this.registerAction_(mode.name); - } - - /** - * @type {!HTMLElement} - * @private - */ - this.undoButton_ = this.createToolButton_( - 'undo', 'GALLERY_UNDO', this.undo.bind(this), true /* instant */); - this.registerAction_('undo'); - - /** - * @type {!HTMLElement} - * @private - */ - this.redoButton_ = this.createToolButton_( - 'redo', 'GALLERY_REDO', this.redo.bind(this), true /* instant */); - this.registerAction_('redo'); - - /** - * @private {!HTMLElement} - * @const - */ - this.exitButton_ = /** @type {!HTMLElement} */ - (queryRequiredElement('.edit-mode-toolbar cr-button.exit')); - this.exitButton_.addEventListener('click', this.onExitClicked_.bind(this)); - } - - /** - * Handles click event of exit button. - * @private - */ - onExitClicked_() { - var event = new Event('exit-clicked'); - this.dispatchEvent(event); - } - - /** - * Creates a toolbar button. - * @param {string} name Button name. - * @param {string} title Button title. - * @param {function(Event)} handler onClick handler. - * @param {boolean} isInstant True if this tool (mode) is instant. - * @return {!HTMLElement} A created button. - * @private - */ - createToolButton_(name, title, handler, isInstant) { - var button = this.mainToolbar_.addButton( - title, - isInstant ? ImageEditorToolbar.ButtonType.ICON : - ImageEditorToolbar.ButtonType.ICON_TOGGLEABLE, - handler, name /* opt_className */); - return button; - } - - /** - * @return {boolean} True if no user commands are to be accepted. - */ - isLocked() { - return !this.commandQueue_ || this.commandQueue_.isBusy(); - } - - /** - * @return {boolean} True if the command queue is busy. - */ - isBusy() { - return this.commandQueue_ && this.commandQueue_.isBusy(); - } - - /** - * Reflect the locked state of the editor in the UI. - * @param {boolean} on True if locked. - */ - lockUI(on) { - ImageUtil.setAttribute(this.rootContainer_, 'locked', on); - } - - /** - * Report the tool use to the metrics subsystem. - * @param {string} name Action name. - */ - recordToolUse(name) { - metrics.recordEnum( - ImageUtil.getMetricName('Tool'), name, this.actionNames_); - } - - /** - * Content update handler. - * @private - */ - calculateModeApplicativity_() { - for (var i = 0; i != this.modes_.length; i++) { - var mode = this.modes_[i]; - ImageUtil.setAttribute( - assert(mode.button_), 'disabled', !mode.isApplicable()); - } - } - - /** - * Open the editing session for a new image. - * - * @param {!GalleryItem} item Gallery item. - * @param {!ImageView.Effect} effect Transition effect object. - * @param {function(function())} saveFunction Image save function. - * @param {function()} displayCallback Display callback. - * @param {function(!ImageView.LoadType, number, *=)} loadCallback Load - * callback. - */ - openSession(item, effect, saveFunction, displayCallback, loadCallback) { - if (this.commandQueue_) { - throw new Error('Session not closed'); - } - - this.lockUI(true); - - var self = this; - this.imageView_.load( - item, effect, displayCallback, function(loadType, delay, error) { - self.lockUI(false); - - // Always handle an item as original for new session. - item.setAsOriginal(); - - self.commandQueue_ = new CommandQueue( - assert(self.container_.ownerDocument), - assert(self.imageView_.getEditableImage()), saveFunction); - self.commandQueue_.attachUI( - self.getImageView(), self.getPrompt(), - self.updateUndoRedo.bind(self), self.lockUI.bind(self)); - self.updateUndoRedo(); - loadCallback(loadType, delay, error); - }); - } - - /** - * Close the current image editing session. - * @param {function()} callback Callback. - */ - closeSession(callback) { - this.getPrompt().hide(); - if (this.imageView_.isLoading()) { - if (this.commandQueue_) { - console.warn('Inconsistent image editor state'); - this.commandQueue_ = null; - } - this.imageView_.cancelLoad(); - this.lockUI(false); - callback(); - return; - } - if (!this.commandQueue_) { - // Session is already closed. - callback(); - return; - } - - this.executeWhenReady(callback); - this.commandQueue_.close(); - this.commandQueue_ = null; - } - - /** - * Commit the current operation and execute the action. - * - * @param {function()} callback Callback. - */ - executeWhenReady(callback) { - if (this.commandQueue_) { - this.leaveMode(false /* not to switch mode */); - this.commandQueue_.executeWhenReady(callback); - } else { - if (!this.imageView_.isLoading()) { - console.warn('Inconsistent image editor state'); - } - callback(); - } - } - - /** - * @return {boolean} True if undo queue is not empty. - */ - canUndo() { - return !!this.commandQueue_ && this.commandQueue_.canUndo(); - } - - /** - * Undo the recently executed command. - */ - undo() { - if (this.isLocked()) { - return; - } - this.recordToolUse('undo'); - - // First undo click should dismiss the uncommitted modifications. - if (this.currentMode_ && this.currentMode_.isUpdated()) { - this.modeToolbar_.reset(); - this.currentMode_.reset(); - return; - } - - this.getPrompt().hide(); - this.leaveModeInternal_(false, false /* not to switch mode */); - this.commandQueue_.undo(); - this.updateUndoRedo(); - this.calculateModeApplicativity_(); - } - - /** - * Redo the recently un-done command. - */ - redo() { - if (this.isLocked()) { - return; - } - this.recordToolUse('redo'); - this.getPrompt().hide(); - this.leaveModeInternal_(false, false /* not to switch mode */); - this.commandQueue_.redo(); - this.updateUndoRedo(); - this.calculateModeApplicativity_(); - } - - /** - * Update Undo/Redo buttons state. - */ - updateUndoRedo() { - var canUndo = this.commandQueue_ && this.commandQueue_.canUndo(); - var canRedo = this.commandQueue_ && this.commandQueue_.canRedo(); - ImageUtil.setAttribute(this.undoButton_, 'disabled', !canUndo); - ImageUtil.setAttribute(this.redoButton_, 'disabled', !canRedo); - } - - /** - * @return {HTMLCanvasElement|HTMLImageElement} The current image. - */ - getImage() { - return this.getImageView().getEditableImage(); - } - - /** - * @return {!ImageBuffer} ImageBuffer instance. - */ - getBuffer() { - return this.buffer_; - } - - /** - * @return {!ImageView} ImageView instance. - */ - getImageView() { - return this.imageView_; - } - - /** - * @return {!Viewport} Viewport instance. - */ - getViewport() { - return this.viewport_; - } - - /** - * @return {!ImageEditorPrompt} Prompt instance. - */ - getPrompt() { - return this.prompt_; - } - - /** - * Handle the toolbar controls update. - * @param {Object} options A map of options. - */ - onOptionsChange(options) { - ImageUtil.trace.resetTimer('update'); - if (this.currentMode_) { - this.currentMode_.update(options); - } - ImageUtil.trace.reportTimer('update'); - } - - /** - * Register the action name. Required for metrics reporting. - * @param {string} name Button name. - * @private - */ - registerAction_(name) { - this.actionNames_.push(name); - } - - /** - * @return {ImageEditorMode} The current mode. - */ - getMode() { - return this.currentMode_; - } - - /** - * The user clicked on the mode button. - * - * @param {!ImageEditorMode} mode The new mode. - */ - enterMode(mode) { - if (this.isLocked()) { - return; - } - - if (this.currentMode_ === mode) { - // Currently active editor tool clicked, commit if modified. - this.leaveModeInternal_( - this.currentMode_.updated_, false /* not to switch mode */); - return; - } - - // Guard not to call setUpMode_ more than once. - if (this.settingUpNextMode_) { - return; - } - this.settingUpNextMode_ = true; - - this.recordToolUse(mode.name); - - this.leaveMode(true /* to switch mode */); - - // The above call could have caused a commit which might have initiated - // an asynchronous command execution. Wait for it to complete, then proceed - // with the mode set up. - this.commandQueue_.executeWhenReady(function() { - this.setUpMode_(mode); - this.settingUpNextMode_ = false; - }.bind(this)); - } - - /** - * Set up the new editing mode. - * - * @param {!ImageEditorMode} mode The mode. - * @private - */ - setUpMode_(mode) { - this.currentTool_ = mode.button_; - this.currentMode_ = mode; - this.rootContainer_.setAttribute('editor-mode', mode.name); - - // Activate toggle ripple if button is toggleable. - var filesToggleRipple = - this.currentTool_.querySelector('files-toggle-ripple'); - if (filesToggleRipple) { - // Current mode must NOT be instant for toggleable button. - assert(!this.currentMode_.instant); - filesToggleRipple.activated = true; - } - - // Scale the screen so that it doesn't overlap the toolbars. We should scale - // the screen before setup of current mode is called to make the current - // mode able to set up with new screen size. - if (!this.currentMode_.instant) { - this.getViewport().setScreenTop( - ImageEditorToolbar.HEIGHT + mode.paddingTop); - this.getViewport().setScreenBottom( - ImageEditorToolbar.HEIGHT * 2 + mode.paddingBottom); - this.getImageView().applyViewportChange(); - } - - this.currentMode_.setUp(); - - this.calculateModeApplicativity_(); - if (this.currentMode_.instant) { // Instant tool. - this.leaveModeInternal_(true, false /* not to switch mode */); - return; - } - - this.exitButton_.hidden = true; - - this.modeToolbar_.clear(); - this.currentMode_.createTools(this.modeToolbar_); - this.modeToolbar_.show(true); - } - - /** - * Handles click event of Done button. - * @param {!Event} event An event. - * @private - */ - onDoneClicked_(event) { - this.leaveModeInternal_(true /* commit */, false /* not to switch mode */); - } - - /** - * Handles click event of Cancel button. - * @param {!Event} event An event. - * @private - */ - onCancelClicked_(event) { - this.leaveModeInternal_( - false /* not commit */, false /* not to switch mode */); - } - - /** - * The user clicked on 'OK' or 'Cancel' or on a different mode button. - * @param {boolean} commit True if commit is required. - * @param {boolean} leaveToSwitchMode True if it leaves to change mode. - * @private - */ - leaveModeInternal_(commit, leaveToSwitchMode) { - if (!this.currentMode_) { - return; - } - - // If the current mode is 'Resize', and commit is required, - // leaving mode should be stopped when an input value is not valid. - if (commit && this.currentMode_.name === 'resize') { - var resizeMode = /** @type {!ImageEditorMode.Resize} */ - (this.currentMode_); - if (!resizeMode.isInputValid()) { - resizeMode.showAlertDialog(); - return; - } - } - - this.modeToolbar_.show(false); - this.rootContainer_.removeAttribute('editor-mode'); - - // If it leaves to switch mode, do not restore screen size since the next - // mode might change screen size. We should avoid to show intermediate - // animation which tries to restore screen size. - if (!leaveToSwitchMode) { - this.getViewport().setScreenTop(ImageEditorToolbar.HEIGHT); - this.getViewport().setScreenBottom(ImageEditorToolbar.HEIGHT); - this.getImageView().applyViewportChange(); - } - - this.currentMode_.cleanUpUI(); - - if (commit) { - var self = this; - var command = this.currentMode_.getCommand(); - if (command) { // Could be null if the user did not do anything. - this.commandQueue_.execute(command); - this.updateUndoRedo(); - } - } - - var filesToggleRipple = - this.currentTool_.querySelector('files-toggle-ripple'); - if (filesToggleRipple) { - filesToggleRipple.activated = false; - } - - this.exitButton_.hidden = false; - - this.currentMode_.cleanUpCaches(); - this.currentMode_ = null; - this.currentTool_ = null; - } - - /** - * Leave the mode, commit only if required by the current mode. - * @param {boolean} leaveToSwitchMode True if it leaves to switch mode. - */ - leaveMode(leaveToSwitchMode) { - this.leaveModeInternal_( - !!this.currentMode_ && this.currentMode_.updated_ && - this.currentMode_.implicitCommit, - leaveToSwitchMode); - } - - /** - * Enter the editor mode with the given name. - * - * @param {string} name Mode name. - * @private - */ - enterModeByName_(name) { - for (var i = 0; i !== this.modes_.length; i++) { - var mode = this.modes_[i]; - if (mode.name === name) { - if (!mode.button_.hasAttribute('disabled')) { - this.enterMode(mode); - } - return; - } - } - console.error('Mode "' + name + '" not found.'); - } - - /** - * Key down handler. - * @param {!Event} event The keydown event. - * @return {boolean} True if handled. - */ - onKeyDown(event) { - if (this.currentMode_ && this.currentMode_.isConsumingKeyEvents()) { - return false; - } - - switch (util.getKeyModifiers(event) + event.key) { - case 'Escape': - case 'Enter': - if (this.getMode()) { - this.leaveModeInternal_( - event.key === 'Enter', false /* not to switch mode */); - return true; - } - break; - - case 'Ctrl-z': // Ctrl+Z - if (this.commandQueue_.canUndo()) { - this.undo(); - return true; - } - break; - - case 'Ctrl-y': // Ctrl+Y - if (this.commandQueue_.canRedo()) { - this.redo(); - return true; - } - break; - - case 'a': - this.enterModeByName_('autofix'); - return true; - - case 'b': - this.enterModeByName_('exposure'); - return true; - - case 'c': - this.enterModeByName_('crop'); - return true; - - case 'l': - this.enterModeByName_('rotate_left'); - return true; - - case 'r': - this.enterModeByName_('rotate_right'); - return true; - } - return false; - } - - /** - * Double tap handler. - * @param {number} x X coordinate of the event. - * @param {number} y Y coordinate of the event. - * @private - */ - onDoubleTap_(x, y) { - if (this.getMode()) { - var action = this.buffer_.getDoubleTapAction(x, y); - if (action === ImageBuffer.DoubleTapAction.COMMIT) { - this.leaveModeInternal_(true, false /* not to switch mode */); - } else if (action === ImageBuffer.DoubleTapAction.CANCEL) { - this.leaveModeInternal_(false, false /* not to switch mode */); - } - } - } - - /** - * Called when the user starts editing image. - */ - onStartEditing() { - this.calculateModeApplicativity_(); - } -} - -/** - * A helper object for panning the ImageBuffer. - */ -ImageEditor.MouseControl = class { - /** - * @param {!HTMLElement} rootContainer The top-level container. - * @param {!HTMLElement} container The container for mouse events. - * @param {!ImageBuffer} buffer Image buffer. - */ - constructor(rootContainer, container, buffer) { - this.rootContainer_ = rootContainer; - this.container_ = container; - this.buffer_ = buffer; - - var handlers = { - 'touchstart': this.onTouchStart, - 'touchend': this.onTouchEnd, - 'touchcancel': this.onTouchCancel, - 'touchmove': this.onTouchMove, - 'mousedown': this.onMouseDown, - 'mouseup': this.onMouseUp - }; - - for (var eventName in handlers) { - container.addEventListener( - eventName, handlers[eventName].bind(this), false); - } - - // Mouse move handler has to be attached to the window to receive events - // from outside of the window. See: http://crbug.com/155705 - window.addEventListener('mousemove', this.onMouseMove.bind(this), false); - - /** - * @type {?ImageBuffer.DragHandler} - * @private - */ - this.dragHandler_ = null; - - /** - * @type {boolean} - * @private - */ - this.dragHappened_ = false; - - /** - * @type {?{x: number, y: number, time:number}} - * @private - */ - this.touchStartInfo_ = null; - - /** - * @type {?{x: number, y: number, time:number}} - * @private - */ - this.previousTouchStartInfo_ = null; - } - - /** - * Returns an event's position. - * - * @param {!(MouseEvent|Touch)} e Pointer position. - * @return {!Object} A pair of x,y in page coordinates. - * @private - */ - static getPosition_(e) { - return {x: e.pageX, y: e.pageY}; - } - - /** - * Returns touch position or null if there is more than one touch position. - * - * @param {!TouchEvent} e Event. - * @return {Object?} A pair of x,y in page coordinates. - * @private - */ - getTouchPosition_(e) { - if (e.targetTouches.length == 1) { - return ImageEditor.MouseControl.getPosition_(e.targetTouches[0]); - } else { - return null; - } - } - - /** - * Touch start handler. - * @param {!TouchEvent} e Event. - */ - onTouchStart(e) { - var position = this.getTouchPosition_(e); - if (position) { - this.touchStartInfo_ = {x: position.x, y: position.y, time: Date.now()}; - this.dragHandler_ = - this.buffer_.getDragHandler(position.x, position.y, true /* touch */); - this.dragHappened_ = false; - } - } - - /** - * Touch end handler. - * @param {!TouchEvent} e Event. - */ - onTouchEnd(e) { - if (!this.dragHappened_ && this.touchStartInfo_ && - Date.now() - this.touchStartInfo_.time <= - ImageEditor.MouseControl.MAX_TAP_DURATION_) { - this.buffer_.onClick(this.touchStartInfo_.x, this.touchStartInfo_.y); - if (this.previousTouchStartInfo_ && - Date.now() - this.previousTouchStartInfo_.time < - ImageEditor.MouseControl.MAX_DOUBLE_TAP_DURATION_) { - var prevTouchCircle = new Circle( - this.previousTouchStartInfo_.x, this.previousTouchStartInfo_.y, - ImageEditor.MouseControl.MAX_DISTANCE_FOR_DOUBLE_TAP_); - if (prevTouchCircle.inside( - this.touchStartInfo_.x, this.touchStartInfo_.y)) { - this.doubleTapCallback_( - this.touchStartInfo_.x, this.touchStartInfo_.y); - } - } - this.previousTouchStartInfo_ = this.touchStartInfo_; - } else { - this.previousTouchStartInfo_ = null; - } - this.onTouchCancel(); - } - - /** - * Default double tap handler. - * @param {number} x X coordinate of the event. - * @param {number} y Y coordinate of the event. - * @private - */ - doubleTapCallback_(x, y) {} - - /** - * Sets callback to be called when double tap detected. - * @param {function(number, number)} callback New double tap callback. - */ - setDoubleTapCallback(callback) { - this.doubleTapCallback_ = callback; - } - - /** - * Touch cancel handler. - */ - onTouchCancel() { - this.dragHandler_ = null; - this.dragHappened_ = false; - this.touchStartInfo_ = null; - this.lockMouse_(false); - } - - /** - * Touch move handler. - * @param {!TouchEvent} e Event. - */ - onTouchMove(e) { - var position = this.getTouchPosition_(e); - if (!position) { - return; - } - - if (this.touchStartInfo_ && !this.dragHappened_) { - var tapCircle = new Circle( - this.touchStartInfo_.x, this.touchStartInfo_.y, - ImageEditor.MouseControl.MAX_MOVEMENT_FOR_TAP_); - this.dragHappened_ = !tapCircle.inside(position.x, position.y); - } - if (this.dragHandler_ && this.dragHappened_) { - this.dragHandler_(position.x, position.y, e.shiftKey); - this.lockMouse_(true); - } - } - - /** - * Mouse down handler. - * @param {!MouseEvent} e Event. - */ - onMouseDown(e) { - var position = ImageEditor.MouseControl.getPosition_(e); - - this.dragHandler_ = - this.buffer_.getDragHandler(position.x, position.y, false /* mouse */); - this.dragHappened_ = false; - this.updateCursor_(position); - } - - /** - * Mouse up handler. - * @param {!MouseEvent} e Event. - */ - onMouseUp(e) { - var position = ImageEditor.MouseControl.getPosition_(e); - - if (!this.dragHappened_) { - this.buffer_.onClick(position.x, position.y); - } - this.dragHandler_ = null; - this.dragHappened_ = false; - this.lockMouse_(false); - } - - /** - * Mouse move handler. - * @param {!Event} e Event. - */ - onMouseMove(e) { - e = assertInstanceof(e, MouseEvent); - var position = ImageEditor.MouseControl.getPosition_(e); - - if (this.dragHandler_ && !e.which) { - // mouseup must have happened while the mouse was outside our window. - this.dragHandler_ = null; - this.lockMouse_(false); - } - - this.updateCursor_(position); - if (this.dragHandler_) { - this.dragHandler_(position.x, position.y, e.shiftKey); - this.dragHappened_ = true; - this.lockMouse_(true); - } - } - - /** - * Update the UI to reflect mouse drag state. - * @param {boolean} on True if dragging. - * @private - */ - lockMouse_(on) { - ImageUtil.setAttribute(this.rootContainer_, 'mousedrag', on); - } - - /** - * Update the cursor. - * - * @param {!Object} position An object holding x and y properties. - * @private - */ - updateCursor_(position) { - var oldCursor = this.container_.getAttribute('cursor'); - var newCursor = this.buffer_.getCursorStyle( - position.x, position.y, !!this.dragHandler_); - if (newCursor != oldCursor) { // Avoid flicker. - this.container_.setAttribute('cursor', newCursor); - } - } -}; - -/** - * Maximum movement for touch to be detected as a tap (in pixels). - * @private - * @const - */ -ImageEditor.MouseControl.MAX_MOVEMENT_FOR_TAP_ = 8; - -/** - * Maximum time for touch to be detected as a tap (in milliseconds). - * @private - * @const - */ -ImageEditor.MouseControl.MAX_TAP_DURATION_ = 500; - -/** - * Maximum distance from the first tap to the second tap to be considered - * as a double tap. - * @private - * @const - */ -ImageEditor.MouseControl.MAX_DISTANCE_FOR_DOUBLE_TAP_ = 32; - -/** - * Maximum time for touch to be detected as a double tap (in milliseconds). - * @private - * @const - */ -ImageEditor.MouseControl.MAX_DOUBLE_TAP_DURATION_ = 1000;
diff --git a/ui/file_manager/gallery/js/image_editor/image_editor_mode.js b/ui/file_manager/gallery/js/image_editor/image_editor_mode.js deleted file mode 100644 index b7914ac7..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_editor_mode.js +++ /dev/null
@@ -1,211 +0,0 @@ -// 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. - -/** - * ImageEditorMode represents a modal state dedicated to a specific operation. - * Inherits from ImageBuffer. Overlay to simplify the drawing of mode-specific - * tools. - * - * @param {string} name The mode name. - * @param {string} title The mode title. - * @constructor - * @struct - * @extends {ImageBuffer.Overlay} - */ -function ImageEditorMode(name, title) { - this.name = name; - this.title = title; - this.message_ = 'GALLERY_ENTER_WHEN_DONE'; - - /** - * @type {boolean} - */ - this.implicitCommit = false; - - /** - * @type {boolean} - */ - this.instant = false; - - /** - * @type {number} - */ - this.paddingTop = 0; - - /** - * @type {number} - */ - this.paddingBottom = 0; - - /** - * @type {Viewport} - */ - this.viewport_ = null; - - /** - * @type {HTMLElement} - */ - this.button_ = null; - - /** - * @type {ImageBuffer} - * @private - */ - this.buffer_ = null; - - /** - * @type {boolean} - */ - this.updated_ = false; - - /** - * @type {ImageView} - * @private - */ - this.imageView_ = null; -} - -ImageEditorMode.prototype = { - __proto__: ImageBuffer.Overlay.prototype -}; - -/** - * @return {Viewport} Viewport instance. - */ -ImageEditorMode.prototype.getViewport = function() { - return this.viewport_; -}; - -/** - * @return {ImageView} ImageView instance. - */ -ImageEditorMode.prototype.getImageView = function() { - return this.imageView_; -}; - -/** - * @return {string} The mode-specific message to be displayed when entering. - */ -ImageEditorMode.prototype.getMessage = function() { - return this.message_; -}; - -/** - * @return {boolean} True if the mode is applicable in the current context. - */ -ImageEditorMode.prototype.isApplicable = function() { - return true; -}; - -/** - * Called once after creating the mode button. - * - * @param {!HTMLElement} button The mode button. - * @param {!ImageBuffer} buffer - * @param {!Viewport} viewport - * @param {!ImageView} imageView - */ - -ImageEditorMode.prototype.bind = function(button, buffer, viewport, imageView) { - this.button_ = button; - this.buffer_ = buffer; - this.viewport_ = viewport; - this.imageView_ = imageView; -}; - -/** - * Called before entering the mode. - */ -ImageEditorMode.prototype.setUp = function() { - this.buffer_.addOverlay(this); - this.updated_ = false; -}; - -/** - * Create mode-specific controls here. - * @param {!ImageEditorToolbar} toolbar The toolbar to populate. - */ -ImageEditorMode.prototype.createTools = function(toolbar) {}; - -/** - * Called before exiting the mode. - */ -ImageEditorMode.prototype.cleanUpUI = function() { - this.buffer_.removeOverlay(this); -}; - -/** - * Called after exiting the mode. - */ -ImageEditorMode.prototype.cleanUpCaches = function() {}; - -/** - * Called when any of the controls changed its value. - * @param {Object} options A map of options. - */ -ImageEditorMode.prototype.update = function(options) { - this.markUpdated(); -}; - -/** - * Mark the editor mode as updated. - */ -ImageEditorMode.prototype.markUpdated = function() { - this.updated_ = true; -}; - -/** - * @return {boolean} True if the mode controls changed. - */ -ImageEditorMode.prototype.isUpdated = function() { - return this.updated_; -}; - -/** - * @return {boolean} True if a key event should be consumed by the mode. - */ -ImageEditorMode.prototype.isConsumingKeyEvents = function() { - return false; -}; - -/** - * Resets the mode to a clean state. - */ -ImageEditorMode.prototype.reset = function() { - this.updated_ = false; -}; - -/** - * @return {Command} Command. - */ -ImageEditorMode.prototype.getCommand = function() { - return null; -}; - -/** - * One-click editor tool, requires no interaction, just executes the command. - * - * @param {string} name The mode name. - * @param {string} title The mode title. - * @param {!Command} command The command to execute on click. - * @constructor - * @extends {ImageEditorMode} - * @struct - */ -ImageEditorMode.OneClick = function(name, title, command) { - ImageEditorMode.call(this, name, title); - this.instant = true; - this.command_ = command; -}; - -ImageEditorMode.OneClick.prototype = { - __proto__: ImageEditorMode.prototype -}; - -/** - * @override - */ -ImageEditorMode.OneClick.prototype.getCommand = function() { - return this.command_; -};
diff --git a/ui/file_manager/gallery/js/image_editor/image_editor_prompt.js b/ui/file_manager/gallery/js/image_editor/image_editor_prompt.js deleted file mode 100644 index 349b9d60..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_editor_prompt.js +++ /dev/null
@@ -1,155 +0,0 @@ -// 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. - -/** - * A prompt panel for the editor. - * - * @param {!HTMLElement} container Container element. - * @param {function(string, ...string)} displayStringFunction A formatting - * function. - * @constructor - * @struct - */ -function ImageEditorPrompt(container, displayStringFunction) { - this.container_ = container; - this.displayStringFunction_ = displayStringFunction; - - /** - * @type {HTMLDivElement} - * @private - */ - this.wrapper_ = null; - - /** - * @type {HTMLDivElement} - * @private - */ - this.prompt_ = null; - - /** - * @type {number} - * @private - */ - this.timer_ = 0; -} - -/** - * Reset the prompt. - */ -ImageEditorPrompt.prototype.reset = function() { - this.cancelTimer(); - if (this.wrapper_) { - this.container_.removeChild(this.wrapper_); - this.wrapper_ = null; - this.prompt_ = null; - } -}; - -/** - * Cancel the delayed action. - */ -ImageEditorPrompt.prototype.cancelTimer = function() { - if (this.timer_) { - clearTimeout(this.timer_); - this.timer_ = 0; - } -}; - -/** - * Schedule the delayed action. - * @param {function()} callback Callback. - * @param {number} timeout Timeout. - */ -ImageEditorPrompt.prototype.setTimer = function(callback, timeout) { - this.cancelTimer(); - var self = this; - this.timer_ = setTimeout(function() { - self.timer_ = 0; - callback(); - }, timeout); -}; - -/** - * Show the prompt. - * - * @param {string} text The prompt text. - * @param {number=} opt_timeout Timeout in ms. - * @param {...Object} var_args varArgs for the formatting function. - */ -ImageEditorPrompt.prototype.show = function(text, opt_timeout, var_args) { - var args = [text].concat(Array.prototype.slice.call(arguments, 2)); - var message = this.displayStringFunction_.apply(null, args); - this.showStringAt('center', message, opt_timeout); -}; - -/** - * Show the position at the specific position. - * - * @param {string} pos The 'pos' attribute value. - * @param {string} text The prompt text. - * @param {number} timeout Timeout in ms. - * @param {...Object} var_args varArgs for the formatting function. - */ -ImageEditorPrompt.prototype.showAt = function(pos, text, timeout, var_args) { - var args = [text].concat(Array.prototype.slice.call(arguments, 3)); - var message = this.displayStringFunction_.apply(null, args); - this.showStringAt(pos, message, timeout); -}; - -/** - * Show the string in the prompt - * - * @param {string} pos The 'pos' attribute value. - * @param {string} text The prompt text. - * @param {number=} opt_timeout Timeout in ms. - */ -ImageEditorPrompt.prototype.showStringAt = function(pos, text, opt_timeout) { - this.reset(); - if (!text) { - return; - } - - var document = this.container_.ownerDocument; - this.wrapper_ = - assertInstanceof(document.createElement('div'), HTMLDivElement); - this.wrapper_.className = 'prompt-wrapper'; - this.wrapper_.setAttribute('pos', pos); - this.container_.appendChild(this.wrapper_); - - this.prompt_ = - assertInstanceof(document.createElement('div'), HTMLDivElement); - this.prompt_.className = 'prompt'; - - // Create an extra wrapper which opacity can be manipulated separately. - var tool = document.createElement('div'); - tool.className = 'dimmable'; - this.wrapper_.appendChild(tool); - tool.appendChild(this.prompt_); - - this.prompt_.textContent = text; - - var close = document.createElement('div'); - close.className = 'close'; - close.addEventListener('click', this.hide.bind(this)); - this.prompt_.appendChild(close); - - setTimeout( - this.prompt_.setAttribute.bind(this.prompt_, 'state', 'fadein'), 0); - - if (opt_timeout) { - this.setTimer(this.hide.bind(this), opt_timeout); - } -}; - -/** - * Hide the prompt. - */ -ImageEditorPrompt.prototype.hide = function() { - if (!this.prompt_) { - return; - } - this.prompt_.setAttribute('state', 'fadeout'); - // Allow some time for the animation to play out. - this.setTimer(this.reset.bind(this), 500); -};
diff --git a/ui/file_manager/gallery/js/image_editor/image_editor_toolbar.js b/ui/file_manager/gallery/js/image_editor/image_editor_toolbar.js deleted file mode 100644 index f349606..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_editor_toolbar.js +++ /dev/null
@@ -1,374 +0,0 @@ -// 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. - -/* eslint-disable no-var */ - -/** - * A toolbar for the ImageEditor. - */ -class ImageEditorToolbar extends cr.EventTarget { - /** - * @param {!HTMLElement} parent The parent element. - * @param {function(string)} displayStringFunction A string formatting - * function. - * @param {function(Object)=} opt_updateCallback The callback called when - * controls change. - * @param {boolean=} opt_showActionButtons True to show action buttons. - */ - constructor( - parent, displayStringFunction, opt_updateCallback, - opt_showActionButtons) { - super(); - - this.wrapper_ = parent; - this.displayStringFunction_ = displayStringFunction; - - /** - * @type {?function(Object)} - * @private - */ - this.updateCallback_ = opt_updateCallback || null; - - /** - * @private {!HTMLElement} - */ - this.container_ = - /** @type {!HTMLElement} */ (document.createElement('div')); - this.container_.classList.add('container'); - this.wrapper_.appendChild(this.container_); - - // Create action buttons. - if (opt_showActionButtons) { - var actionButtonsLayer = document.createElement('div'); - actionButtonsLayer.classList.add('action-buttons'); - - this.cancelButton_ = ImageEditorToolbar.createButton_( - 'GALLERY_CANCEL_LABEL', - ImageEditorToolbar.ButtonType.LABEL_UPPER_CASE, - this.onCancelClicked_.bind(this), 'cancel'); - actionButtonsLayer.appendChild(this.cancelButton_); - - this.doneButton_ = ImageEditorToolbar.createButton_( - 'GALLERY_DONE', ImageEditorToolbar.ButtonType.LABEL_UPPER_CASE, - this.onDoneClicked_.bind(this), 'done'); - actionButtonsLayer.appendChild(this.doneButton_); - - this.wrapper_.appendChild(actionButtonsLayer); - } - } - - /** - * Handles click event of done button. - * @private - */ - onDoneClicked_() { - (/** @type {!PaperRipple}*/ ( - this.doneButton_.querySelector('paper-ripple'))) - .simulatedRipple(); - - var event = new Event('done-clicked'); - this.dispatchEvent(event); - } - - /** - * Handles click event of cancel button. - * @private - */ - onCancelClicked_() { - (/**@type{PaperRipple}*/ (this.cancelButton_.querySelector('paper-ripple'))) - .simulatedRipple(); - - var event = new Event('cancel-clicked'); - this.dispatchEvent(event); - } - - /** - * Returns the parent element. - * @return {!HTMLElement} - */ - getElement() { - return this.container_; - } - - /** - * Clear the toolbar. - */ - clear() { - ImageUtil.removeChildren(this.container_); - } - - /** - * Add a control. - * @param {!HTMLElement} element The control to add. - * @return {!HTMLElement} The added element. - */ - add(element) { - this.container_.appendChild(element); - return element; - } - - /** - * Create a button. - * - * @param {string} title String ID of button title. - * @param {ImageEditorToolbar.ButtonType} type Button type. - * @param {function(Event)} handler onClick handler. - * @param {string=} opt_class Extra class name. - * @return {!HTMLElement} The created button. - * @private - */ - static createButton_(title, type, handler, opt_class) { - var button = /** @type {!HTMLElement} */ (document.createElement('button')); - if (opt_class) { - button.classList.add(opt_class); - } - button.classList.add('edit-toolbar'); - - if (type === ImageEditorToolbar.ButtonType.ICON || - type === ImageEditorToolbar.ButtonType.ICON_TOGGLEABLE) { - var icon = document.createElement('div'); - icon.classList.add('icon'); - - button.appendChild(icon); - - if (type === ImageEditorToolbar.ButtonType.ICON) { - var filesRipple = document.createElement('files-ripple'); - button.appendChild(filesRipple); - } else { - var filesToggleRipple = document.createElement('files-toggle-ripple'); - button.appendChild(filesToggleRipple); - } - } else if ( - type === ImageEditorToolbar.ButtonType.LABEL || - type === ImageEditorToolbar.ButtonType.LABEL_UPPER_CASE) { - var label = document.createElement('span'); - label.classList.add('label'); - label.textContent = - type === ImageEditorToolbar.ButtonType.LABEL_UPPER_CASE ? - strf(title).toLocaleUpperCase() : - strf(title); - - button.appendChild(label); - - var paperRipple = document.createElement('paper-ripple'); - button.appendChild(paperRipple); - } else { - assertNotReached(); - } - - button.label = strf(title); - button.setAttribute('aria-label', strf(title)); - - GalleryUtil.decorateMouseFocusHandling(button); - - button.addEventListener('click', handler, false); - button.addEventListener('keydown', function(event) { - // Stop propagation of Enter key event to prevent it from being captured - // by image editor. - if (event.key === 'Enter') { - event.stopPropagation(); - } - }); - - return button; - } - - /** - * Add a button. - * - * @param {string} title Button title. - * @param {ImageEditorToolbar.ButtonType} type Button type. - * @param {function(Event)} handler onClick handler. - * @param {string=} opt_class Extra class name. - * @return {!HTMLElement} The added button. - */ - addButton(title, type, handler, opt_class) { - var button = - ImageEditorToolbar.createButton_(title, type, handler, opt_class); - this.add(button); - return button; - } - - /** - * Add a input field. - * - * @param {string} name Input name - * @param {string} title Input title - * @param {function(Event)} handler onInput and onChange handler - * @param {string|number} value Default value - * @param {string=} opt_unit Unit for an input field - * @return {!HTMLElement} Input Element - */ - addInput(name, title, handler, value, opt_unit) { - var input = /** @type {!HTMLElement} */ (document.createElement('div')); - input.classList.add('input', name); - - var text = document.createElement('cr-input'); - text.setAttribute('label', strf(title)); - text.classList.add('text', name); - text.value = value; - - // We should listen to not only 'change' event, but also 'input' because we - // want to update values as soon as the user types characters. - text.addEventListener('input', handler, false); - text.addEventListener('change', handler, false); - input.appendChild(text); - - if (opt_unit) { - var unitLabel = document.createElement('span'); - unitLabel.textContent = opt_unit; - unitLabel.classList.add('unit_label'); - input.appendChild(unitLabel); - } - - input.name = name; - input.getValue = function(text) { - return text.value; - }.bind(this, text); - input.setValue = function(text, value) { - text.value = value; - }.bind(this, text); - - this.add(input); - - return input; - } - - /** - * Add a range control (scalar value picker). - * - * @param {string} name An option name. - * @param {string} title An option title. - * @param {number} min Min value of the option. - * @param {number} value Default value of the option. - * @param {number} max Max value of the options. - * @param {number=} opt_scale A number to multiply by when setting - * min/value/max in DOM. - * @param {boolean=} opt_showNumeric True if numeric value should be - * displayed. - * @return {!HTMLElement} Range element. - */ - addRange(name, title, min, value, max, opt_scale, opt_showNumeric) { - var range = /** @type {!HTMLElement} */ (document.createElement('div')); - range.classList.add('range', name); - - var icon = document.createElement('icon'); - icon.classList.add('icon'); - range.appendChild(icon); - - var label = document.createElement('span'); - label.textContent = strf(title); - label.classList.add('label'); - range.appendChild(label); - - var scale = opt_scale || 1; - var slider = document.createElement('cr-slider'); - slider.min = Math.ceil(min * scale); - slider.max = Math.floor(max * scale); - slider.value = value * scale; - const handler = () => { - if (this.updateCallback_) { - this.updateCallback_(this.getOptions()); - } - }; - slider.addEventListener('cr-slider-value-changed', handler); - setTimeout(handler); - range.appendChild(slider); - - range.name = name; - range.getValue = () => slider.value / scale; - - // Swallow the left and right keys, so they are not handled by other - // listeners. - range.addEventListener('keydown', function(e) { - if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') { - e.stopPropagation(); - } - }); - - this.add(range); - - return range; - } - - /** - * @return {!Object} options A map of options. - */ - getOptions() { - var values = {}; - - for (var child = this.container_.firstChild; child; - child = child.nextSibling) { - if (child.name) { - values[child.name] = child.getValue(); - } - } - - return values; - } - - /** - * Reset the toolbar. - */ - reset() { - for (var child = this.wrapper_.firstChild; child; - child = child.nextSibling) { - if (child.reset) { - child.reset(); - } - } - } - - /** - * Show/hide the toolbar. - * @param {boolean} on True if show. - */ - show(on) { - if (!this.wrapper_.firstChild) { - return; // Do not show empty toolbar; - } - - this.wrapper_.hidden = !on; - - // Focus the first input on the toolbar. - if (on) { - var input = this.container_.querySelector( - // Crop aspect ratio buttons should not be focused immediately - // crbug.com/655943 - [ - 'button:not(.crop-aspect-ratio)', - 'cr-button', - 'input', - 'cr-slider', - 'cr-input', - ].join(', ')); - if (input) { - input.focus(); - // Fix for crbug/914741 set selection to the end (> 32-bit int) - // Note the input element lives in Shadow DOM. - if (input.select && input.tagName) { - assert(input.tagName === 'CR-INPUT'); - input.select(12, 12); - } - } - } - } -} - -/** - * Height of the toolbar. - * @const {number} - */ -ImageEditorToolbar.HEIGHT = 48; // px - -/** - * Button type. - * @enum {string} - */ -ImageEditorToolbar.ButtonType = { - ICON: 'icon', - ICON_TOGGLEABLE: 'icon_toggleable', - LABEL: 'label', - LABEL_UPPER_CASE: 'label_upper_case' -};
diff --git a/ui/file_manager/gallery/js/image_editor/image_encoder.js b/ui/file_manager/gallery/js/image_editor/image_encoder.js deleted file mode 100644 index c0bf285..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_encoder.js +++ /dev/null
@@ -1,272 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/* eslint-disable no-var */ - -// clang-format off -// #import * as wrappedImageUtil from './image_util.m.js'; const {ImageUtil} = wrappedImageUtil; -// #import {MetadataItem} from '../../../file_manager/foreground/js/metadata/metadata_item.m.js'; -// #import {assert, assertInstanceof} from 'chrome://resources/js/assert.m.js' -// clang-format on - -/** - * A namespace class for image encoding functions. All methods are static. - * @constructor - */ -/* #export */ function ImageEncoder() {} - -/** - * The value 360 px is enough in the Files app grid view for HiDPI devices. - * @const {number} - */ -ImageEncoder.MAX_THUMBNAIL_DIMENSION = 360; - -/** - * Tries to create thumbnail if the image width or height longer than the size. - * @const {number} - */ -ImageEncoder.MIN_IMAGE_DIMENSION_FOR_THUMBNAIL = - ImageEncoder.MAX_THUMBNAIL_DIMENSION * 4; - -/** - * Metadata encoders. - * @type {!Object<function(new:ImageEncoder.MetadataEncoder,!MetadataItem)>} - * @const - */ -ImageEncoder.metadataEncoders = {}; - -/** - * Registers metadata encoder. - * @param {function(new:ImageEncoder.MetadataEncoder,!MetadataItem)} constructor - * Constructor of a metadata encoder. - * @param {string} mimeType Mime type of the metadata encoder. - */ -ImageEncoder.registerMetadataEncoder = function(constructor, mimeType) { - ImageEncoder.metadataEncoders[mimeType] = constructor; -}; - -/** - * Create a metadata encoder. - * - * The encoder will own and modify a copy of the original metadata. - * - * @param {!MetadataItem} metadata Original metadata. - * @return {!ImageEncoder.MetadataEncoder} Created metadata encoder. - */ -ImageEncoder.createMetadataEncoder = function(metadata) { - var constructor = - ImageEncoder.metadataEncoders[metadata.mediaMimeType || ""] || - ImageEncoder.MetadataEncoder; - return new constructor(metadata); -}; - -/** - * Create a metadata encoder object holding a copy of metadata - * modified according to the properties of the supplied image. - * - * @param {!MetadataItem} metadata Original metadata. - * @param {!HTMLCanvasElement} canvas Canvas to use for metadata. - * @param {number} thumbnailQuality Encoding quality of a thumbnail. - * @return {!ImageEncoder.MetadataEncoder} Encoder with encoded metadata. - * - * TODO(yawano): rename to a better name, e.g. prepareMetadataEncoder. - */ -ImageEncoder.encodeMetadata = function(metadata, canvas, thumbnailQuality) { - var encoder = ImageEncoder.createMetadataEncoder(metadata); - encoder.setImageData(canvas); - encoder.setThumbnailData(ImageEncoder.createThumbnail(canvas), - thumbnailQuality); - return encoder; -}; - -/** - * Return a blob with the encoded image with metadata inserted. - * @param {!HTMLCanvasElement} canvas The canvas with the image to be encoded. - * @param {!ImageEncoder.MetadataEncoder} metadataEncoder Encoder to use. - * @param {number} imageQuality (0..1], Encoding quality of an image. - * @return {!Blob} encoded data. - */ -ImageEncoder.getBlob = function(canvas, metadataEncoder, imageQuality) { - ImageUtil.trace.resetTimer('dataurl'); - // WebKit does not support canvas.toBlob yet so canvas.toDataURL is - // the only way to use the Chrome built-in image encoder. - var dataURL = canvas.toDataURL(metadataEncoder.mimeType, imageQuality); - ImageUtil.trace.reportTimer('dataurl'); - - var encodedImage = ImageEncoder.decodeDataURL(dataURL); - - var encodedMetadata = metadataEncoder.encode(); - - var slices = []; - - // TODO(kaznacheev): refactor |stringToArrayBuffer| and |encode| to return - // arrays instead of array buffers. - function appendSlice(arrayBuffer) { - slices.push(new DataView(arrayBuffer)); - } - - ImageUtil.trace.resetTimer('blob'); - if (encodedMetadata.byteLength != 0) { - var metadataRange = metadataEncoder.findInsertionRange(encodedImage); - appendSlice(ImageEncoder.stringToArrayBuffer( - encodedImage, 0, metadataRange.from)); - - appendSlice(metadataEncoder.encode()); - - appendSlice(ImageEncoder.stringToArrayBuffer( - encodedImage, metadataRange.to, encodedImage.length)); - } else { - appendSlice(ImageEncoder.stringToArrayBuffer( - encodedImage, 0, encodedImage.length)); - } - var blob = new Blob(slices, {type: metadataEncoder.mimeType}); - ImageUtil.trace.reportTimer('blob'); - return blob; -}; - -/** - * Decode a dataURL into a binary string containing the encoded image. - * - * Why return a string? Calling atob and having the rest of the code deal - * with a string is several times faster than decoding base64 in Javascript. - * - * @param {string} dataURL Data URL to decode. - * @return {string} A binary string (char codes are the actual byte values). - */ -ImageEncoder.decodeDataURL = function(dataURL) { - // Skip the prefix ('data:image/<type>;base64,') - var base64string = dataURL.substring(dataURL.indexOf(',') + 1); - return window.atob(base64string); -}; - -/** - * Return a thumbnail for an image. - * @param {!HTMLCanvasElement} canvas Original image. - * @return {HTMLCanvasElement} Thumbnail canvas. - */ -ImageEncoder.createThumbnail = function(canvas) { - if (canvas.width < ImageEncoder.MIN_IMAGE_DIMENSION_FOR_THUMBNAIL && - canvas.height < ImageEncoder.MIN_IMAGE_DIMENSION_FOR_THUMBNAIL) { - return null; - } - - var ratio = Math.min(ImageEncoder.MAX_THUMBNAIL_DIMENSION / canvas.width, - ImageEncoder.MAX_THUMBNAIL_DIMENSION / canvas.height); - var thumbnailCanvas = assertInstanceof( - canvas.ownerDocument.createElement('canvas'), HTMLCanvasElement); - thumbnailCanvas.width = Math.round(canvas.width * ratio); - thumbnailCanvas.height = Math.round(canvas.height * ratio); - - var context = thumbnailCanvas.getContext('2d'); - context.drawImage(canvas, - 0, 0, canvas.width, canvas.height, - 0, 0, thumbnailCanvas.width, thumbnailCanvas.height); - - return thumbnailCanvas; -}; - -/** - * Converts string to an array buffer. - * @param {string} string A string. - * @param {number} from Start index. - * @param {number} to End index. - * @return {!ArrayBuffer} A created array buffer is returned. - */ -ImageEncoder.stringToArrayBuffer = function(string, from, to) { - var size = to - from; - var array = new Uint8Array(size); - for (var i = 0; i != size; i++) { - array[i] = string.charCodeAt(from + i); - } - return array.buffer; -}; - -/** - * A base class for a metadata encoder. - * - * Serves as a default metadata encoder for images that none of the metadata - * parsers recognized. - * - * @param {!MetadataItem} originalMetadata Starting metadata. - * @constructor - * @struct - */ -ImageEncoder.MetadataEncoder = function(originalMetadata) { - var mimeType = ImageEncoder.MetadataEncoder.getMimeType_(originalMetadata); - - /** - * Chrome can only encode JPEG and PNG. Force PNG mime type so that we - * can save to file and generate a thumbnail. - * @public {string} - */ - this.mimeType = mimeType === 'image/jpeg' ? 'image/jpeg' : 'image/png'; - - /** - * @protected {string} - */ - this.thumbnailDataUrl = ''; - - /** - * @protected {number} - */ - this.imageWidth = 0; - - /** - * @protected {number} - */ - this.imageHeight = 0; -}; - -/** - * Gets mime type from metadata. It reads media.mimeType at first, and if it - * fails, it falls back to external.contentMimeType. If both fields are - * undefined, it means that metadata is broken. Then it throws an exception. - * - * @param {!MetadataItem} metadata Metadata. - * @return {string} Mime type. - * @private - */ -ImageEncoder.MetadataEncoder.getMimeType_ = function(metadata) { - return assert(metadata.mediaMimeType || metadata.contentMimeType); -}; - -/** - * Sets an image data. - * @param {!HTMLCanvasElement} canvas Canvas or anything with width and height - * properties. - */ -ImageEncoder.MetadataEncoder.prototype.setImageData = function(canvas) { - this.imageWidth = canvas.width; - this.imageHeight = canvas.height; -}; - -/** - * @param {HTMLCanvasElement} canvas Canvas to use as thumbnail. Note that it - * can be null. - * @param {number} quality Thumbnail quality. - */ -ImageEncoder.MetadataEncoder.prototype.setThumbnailData = - function(canvas, quality) { - this.thumbnailDataUrl = - canvas ? canvas.toDataURL(this.mimeType, quality) : ''; -}; - -/** - * Returns a range where the metadata is (or should be) located. - * @param {string} encodedImage An encoded image. - * @return {{from:number, to:number}} An object with from and to properties. - */ -ImageEncoder.MetadataEncoder.prototype.findInsertionRange = function( - encodedImage) { - return {from: 0, to: 0}; -}; - -/** - * Returns serialized metadata ready to write to an image file. - * The return type is optimized for passing to Blob.append. - * @return {!ArrayBuffer} Serialized metadata. - */ -ImageEncoder.MetadataEncoder.prototype.encode = function() { - return new Uint8Array(0).buffer; -};
diff --git a/ui/file_manager/gallery/js/image_editor/image_encoder_unittest.m.js b/ui/file_manager/gallery/js/image_editor/image_encoder_unittest.m.js deleted file mode 100644 index 5a3ea420..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_encoder_unittest.m.js +++ /dev/null
@@ -1,99 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/* eslint-disable no-var */ - -import {assertEquals, assertThrows} from 'chrome://test/chai_assert.js'; -import {reportPromise} from '../../../file_manager/common/js/test_error_reporting.m.js'; -import {MetadataItem} from '../../../file_manager/foreground/js/metadata/metadata_item.m.js'; -import {ImageEncoder} from './image_encoder.m.js'; -import {getSampleCanvas} from './test_util.m.js'; - -/** - * Encodes an image to data URL by using ImageEncoder.getBlob. - */ -function encodeAnImageAsDataURL(canvas, metadata, imageQuality) { - return new Promise(function(resolve, reject) { - // Thumbnail quality is fixed to 1.0 in this test. - var encoder = ImageEncoder.encodeMetadata(metadata, canvas, 1.0); - var blob = ImageEncoder.getBlob(canvas, encoder, imageQuality); - - var fileReader = new FileReader(); - fileReader.onerror = function() { - reject(fileReader.error); - }; - fileReader.onloadend = function() { - resolve(fileReader.result); - }; - fileReader.readAsDataURL(blob); - }); -} - -/** - * Test case for png image. - * Png image should be saved as a png image. - */ -export function testPngImage(callback) { - var canvas = getSampleCanvas(); - - var metadata = {mediaMimeType: 'image/png'}; - - reportPromise( - encodeAnImageAsDataURL(canvas, metadata, 0.9).then(function(result) { - assertEquals(canvas.toDataURL('image/png'), result); - }), - callback); -} - -/** - * Test case for jpeg image. - * Jpeg image should be saved as a jpeg image. Since we don't include - * exif_encoder.js in this test, no metadata is added to the blob. - */ -export function testJpegImage(callback) { - var canvas = getSampleCanvas(); - - var metadata = {mediaMimeType: 'image/jpeg'}; - - reportPromise( - encodeAnImageAsDataURL(canvas, metadata, 0.9).then(function(result) { - assertEquals(canvas.toDataURL('image/jpeg', 0.9), result); - }), - callback); -} - - -/** - * Test case of webp image. - * Image should be saved as a image/png since chrome doesn't support to - * encode other than image/jpeg or image/png. - */ -export function testWebpImage(callback) { - var canvas = getSampleCanvas(); - - var metadata = {mediaMimeType: 'image/webp'}; - - reportPromise( - encodeAnImageAsDataURL(canvas, metadata, 0.9).then(function(result) { - assertEquals(canvas.toDataURL('image/png'), result); - }), - callback); -} - -/** - * Test case for broken metadata. - */ -export function testWithBrokenMetadata() { - var canvas = getSampleCanvas(); - - var metadata = /** @type {!MetadataItem} */ ({ - // No mimetype field. - }); - - // An exception should be thrown if metadata is broken. - const quality = 0.5; - assertThrows(function() { - var encoder = ImageEncoder.encodeMetadata(metadata, canvas, quality); - }); -}
diff --git a/ui/file_manager/gallery/js/image_editor/image_loader.js b/ui/file_manager/gallery/js/image_editor/image_loader.js deleted file mode 100644 index 0c3748e..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_loader.js +++ /dev/null
@@ -1,361 +0,0 @@ -// 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. - -/** - * ImageLoader loads an image from a given Entry into a canvas in two steps: - * 1. Loads the image into an HTMLImageElement. - * 2. Copies pixels from HTMLImageElement to HTMLCanvasElement. This is done - * stripe-by-stripe to avoid freezing up the UI. The transform is taken into - * account. - * - * @param {!HTMLDocument} document Owner document. - * @param {!MetadataModel} metadataModel - * @constructor - * @struct - */ -ImageUtil.ImageLoader = function(document, metadataModel) { - this.document_ = document; - - /** - * @private {!MetadataModel} - * @const - */ - this.metadataModel_ = metadataModel; - - this.generation_ = 0; - - /** - * @type {number} - * @private - */ - this.timeout_ = 0; - - /** - * Cleanup function for Blink EXIF changes in m81. See comments in - * convertImage_() and https://crbug.com/1043561. - * - * @type {function()} - */ - this.onCancelForExifWorkaround_ = () => {}; - - /** - * @type {?function(!HTMLCanvasElement, string=)} - * @private - */ - this.callback_ = null; - - /** - * @type {FileEntry} - * @private - */ - this.entry_ = null; -}; - -/** - * Loads media. - * TODO(mtomasz): Simplify, or even get rid of this class and merge with the - * ThumbnaiLoader class. - * - * @param {!GalleryItem} item Item representing the media to be loaded. - * @param {function(!HTMLCanvasElement, string=)} callback Callback to be - * called when loaded. The second optional argument is an error identifier. - * @param {number=} opt_delay Load delay in milliseconds, useful to let the - * animations play out before the computation heavy media loading starts. - */ -ImageUtil.ImageLoader.prototype.load = function(item, callback, opt_delay) { - var entry = item.getEntry(); - - this.cancel(); - this.entry_ = entry; - this.callback_ = callback; - - // The transform fetcher is not cancellable so we need a generation counter. - var generation = ++this.generation_; - - if (FileType.isVideo(entry)) { - var targetVideo = assertInstanceof( - this.document_.createElement('video'), HTMLVideoElement); - targetVideo.controls = true; - targetVideo.controlsList = 'nodownload'; - targetVideo.classList.add('video'); - } else { - var targetImage = - assertInstanceof(this.document_.createElement('img'), HTMLImageElement); - - /** - * @param {!HTMLImageElement} image Image to be transformed. - * @param {Object=} opt_transform Transformation. - */ - var onTransform = function(image, opt_transform) { - if (generation === this.generation_) { - this.convertImage_(image, opt_transform); - } - }; - onTransform = onTransform.bind(this); - } - - /** - * @param {string=} opt_error Error. - */ - var onError = function(opt_error) { - targetImage.onerror = null; - targetImage.onload = null; - var tmpCallback = this.callback_; - this.callback_ = null; - var emptyCanvas = assertInstanceof(this.document_.createElement('canvas'), - HTMLCanvasElement); - emptyCanvas.width = 0; - emptyCanvas.height = 0; - tmpCallback(emptyCanvas, opt_error); - }; - onError = onError.bind(this); - - var loadImage = function(url) { - if (generation !== this.generation_) { - return; - } - - metrics.startInterval(ImageUtil.getMetricName('LoadTime')); - this.timeout_ = 0; - - targetImage.onload = function() { - targetImage.onerror = null; - targetImage.onload = null; - if (generation !== this.generation_) { - return; - } - this.metadataModel_.get([entry], ['contentImageTransform']).then( - function(metadataItems) { - onTransform(targetImage, metadataItems[0].contentImageTransform); - }.bind(this)); - }.bind(this); - - // The error callback has an optional error argument, which in case of a - // general error should not be specified - targetImage.onerror = onError.bind(null, 'GALLERY_IMAGE_ERROR'); - - targetImage.src = url; - }.bind(this); - - var loadVideo = function(url) { - if (generation !== this.generation_) { - return; - } - - metrics.startInterval(ImageUtil.getMetricName('LoadTime')); - this.timeout_ = 0; - - var source = assertInstanceof( - this.document_.createElement('source'), HTMLSourceElement); - source.src = url; - targetVideo.appendChild(source); - - // Start the <video> element loading immediately. - setTimeout(this.callback_, 0, targetVideo); - this.callback_ = null; - - // Start a task to set the poster property using the thumbnail so that there - // is an image visible before the user clicks play. Ignore errors - the - // poster just won't be set. If the video load also fails, the standard - // controls show a broken play icon. - var thumbnailMetadata = item.getThumbnailMetadataItem(); - if (!thumbnailMetadata) { - return; - } - - var posterLoader = new ThumbnailLoader( - entry, undefined /* opt_loaderType */, thumbnailMetadata); - posterLoader.loadAsDataUrl(ThumbnailLoader.FillMode.FIT) - .then(function(result) { - targetVideo.poster = result.data; - }.bind(this)) - .catch(function(error) {}.bind(this)); - }.bind(this); - - // Loads the media. If already loaded, then forces a reload. - var startLoad = function() { - if (generation !== this.generation_) { - return; - } - - if (FileType.isVideo(entry)) { - loadVideo(entry.toURL() + '?nocache=' + Date.now()); - return; - } - - // Obtain target URL. - if (FileType.isRaw(entry)) { - var timestamp = - item.getMetadataItem() && - item.getMetadataItem().modificationTime && - item.getMetadataItem().modificationTime.getTime(); - let request = LoadImageRequest.createFullImageRequest({ - url: entry.toURL(), - cache: true, - timestamp: timestamp, - priority: 0 // Use highest priority to show main image. - }); - ImageLoaderClient.getInstance().load(request, function(result) { - if (generation !== this.generation_) { - return; - } - if (result.status === 'success') { - loadImage(result.data); - } else { - onError('GALLERY_IMAGE_ERROR'); - } - }.bind(this)); - return; - } - - // Load the image directly. The query parameter is workaround for - // crbug.com/379678, which force to update the contents of the image. - loadImage(entry.toURL() + '?nocache=' + Date.now()); - }.bind(this); - - if (opt_delay) { - this.timeout_ = setTimeout(startLoad, opt_delay); - } else { - startLoad(); - } -}; - -/** - * @return {boolean} True if an image is loading. - */ -ImageUtil.ImageLoader.prototype.isBusy = function() { - return !!this.callback_; -}; - -/** - * @param {Entry} entry Image entry. - * @return {boolean} True if loader loads this image. - */ -ImageUtil.ImageLoader.prototype.isLoading = function(entry) { - return this.isBusy() && util.isSameEntry(this.entry_, entry); -}; - -/** - * @param {function(!HTMLCanvasElement, string=)} callback To be called when the - * image loaded. - */ -ImageUtil.ImageLoader.prototype.setCallback = function(callback) { - this.callback_ = callback; -}; - -/** - * Stops loading image. - */ -ImageUtil.ImageLoader.prototype.cancel = function() { - if (!this.callback_) { - return; - } - this.callback_ = null; - if (this.timeout_) { - this.onCancelForExifWorkaround_(); - clearTimeout(this.timeout_); - this.timeout_ = 0; - } - this.generation_++; // Silence the transform fetcher if it is in progress. -}; - -/** - * @param {!HTMLImageElement} image Image to be transformed. - * @param {!Object} transform transformation description to apply to the image. - * @private - */ -ImageUtil.ImageLoader.prototype.convertImage_ = function(image, transform) { - if (!transform || - (transform.rotate90 === 0 && - transform.scaleX === 1 && - transform.scaleY === 1)) { - setTimeout(this.callback_, 0, image); - this.callback_ = null; - return; - } - - // Helper to suppress JSC_USELESS_CODE below. Like goog.reflect.sinkValue(). - function sinkValue(value) {} - - // If the <img> is not in the DOM (and style calculated for it). Chrome m81+ - // provides width/height that already accounts for EXIF orientations. - image.style.imageOrientation = 'none'; - image.style.visibility = 'hidden'; - document.body.appendChild(image); - sinkValue(image.clientWidth); // Ensure style resolves. - - var canvas = this.document_.createElement('canvas'); - - if (transform.rotate90 & 1) { // Rotated +/-90deg, swap the dimensions. - canvas.width = image.height; - canvas.height = image.width; - } else { - canvas.width = image.width; - canvas.height = image.height; - } - - // Before getting context, apply style to ignore EXIF orientation from source - // pixel data. The <canvas> also needs to be in the DOM when getContext() is - // called for this to take effect. - canvas.style.imageOrientation = 'none'; - canvas.style.visibility = 'hidden'; - document.body.appendChild(canvas); - sinkValue(canvas.clientWidth); // Ensure style resolves. - - // Remove image and canvas from the DOM once the content is drawn. Note the - // canvas may be added back later. Doing this earlier causes the elements to - // "forget' their image-orientation style attribute. - this.onCancelForExifWorkaround_ = () => { - image.remove(); - canvas.remove(); - canvas.style.visibility = 'unset'; - this.onCancelForExifWorkaround_ = () => {}; - }; - - var context = canvas.getContext('2d'); - context.save(); - context.translate(canvas.width / 2, canvas.height / 2); - context.rotate(transform.rotate90 * Math.PI / 2); - context.scale(transform.scaleX, transform.scaleY); - - var stripCount = Math.ceil(image.width * image.height / (1 << 21)); - var step = Math.max(16, Math.ceil(image.height / stripCount)) & 0xFFFFF0; - - this.copyStrip_(context, image, 0, step); -}; - -/** - * @param {!CanvasRenderingContext2D} context Context to draw. - * @param {!HTMLImageElement} image Image to draw. - * @param {number} firstRow Number of the first pixel row to draw. - * @param {number} rowCount Count of pixel rows to draw. - * @private - */ -ImageUtil.ImageLoader.prototype.copyStrip_ = function( - context, image, firstRow, rowCount) { - var lastRow = Math.min(firstRow + rowCount, image.height); - - context.drawImage( - image, 0, firstRow, image.width, lastRow - firstRow, - -image.width / 2, firstRow - image.height / 2, - image.width, lastRow - firstRow); - - if (lastRow === image.height) { - context.restore(); - if (this.entry_.toURL().substr(0, 5) !== 'data:') { // Ignore data urls. - metrics.recordInterval(ImageUtil.getMetricName('LoadTime')); - } - this.onCancelForExifWorkaround_(); - setTimeout(this.callback_, 0, context.canvas); - this.callback_ = null; - } else { - var self = this; - this.timeout_ = setTimeout( - function() { - self.timeout_ = 0; - self.copyStrip_(context, image, lastRow, rowCount); - }, 0); - } -}; -
diff --git a/ui/file_manager/gallery/js/image_editor/image_resize.js b/ui/file_manager/gallery/js/image_editor/image_resize.js deleted file mode 100644 index 03fc8ca..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_resize.js +++ /dev/null
@@ -1,286 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * Resize Mode - * - * @extends {ImageEditorMode} - * @constructor - * @struct - */ -ImageEditorMode.Resize = function() { - ImageEditorMode.call(this, 'resize', 'GALLERY_RESIZE'); - - /** @private {number} */ - this.imageWidth_ = 0; - - /** @private {number} */ - this.imageHeight_ = 0; - - /** @private {number} */ - this.maxValidWidth_ = 0; - - /** @private {number} */ - this.maxValidHeight_ = 0; - - /** @private {number} */ - this.widthInputValue_ = 0; - - /** @private {number} */ - this.heightInputValue_ = 0; - - /** @private {HTMLElement} */ - this.widthInputElement_ = null; - - /** @private {HTMLElement} */ - this.heightInputElement_ = null; - - /** @private {HTMLElement} */ - this.lockIcon_ = null; - - /** @private {number} */ - this.ratio_ = 0; - - /** @private {boolean} */ - this.fixedRatio_ = true; - - /** @private {boolean} */ - this.showingAlertDialog_ = false; - - /** @private {FilesAlertDialog} */ - this.alertDialog_ = null; -}; - -/** @const {number} */ -ImageEditorMode.Resize.MINIMUM_VALID_VALUE = 1; - -/** @const {number} */ -ImageEditorMode.Resize.DEFAULT_MAX_VALID_VALUE = 10000; - -ImageEditorMode.Resize.prototype = { - __proto__: ImageEditorMode.prototype -}; - -/** @override */ -ImageEditorMode.Resize.prototype.setUp = function() { - ImageEditorMode.prototype.setUp.apply(this, arguments); - - this.setDefault_(); -}; - -/** @override */ -ImageEditorMode.Resize.prototype.createTools = function(toolbar) { - this.widthInputElement_ = toolbar.addInput('width', 'GALLERY_WIDTH', - this.onInputChanged_.bind(this, 'width'), - this.widthInputValue_, 'px'); - - this.lockIcon_ = toolbar.addButton( - 'GALLERY_FIXRATIO', ImageEditorToolbar.ButtonType.ICON_TOGGLEABLE, - this.onLockIconClicked_.bind(this), 'lockicon'); - if (this.fixedRatio_) { - this.lockIcon_.setAttribute('locked', ''); - } - - this.heightInputElement_ = toolbar.addInput('height', 'GALLERY_HEIGHT', - this.onInputChanged_.bind(this, 'height'), - this.heightInputValue_, 'px'); -}; - -/** - * Handlers change events of width/height input element. - * @param {string} name - * @param {Event} event - * @private - */ -ImageEditorMode.Resize.prototype.onInputChanged_ = function(name, event) { - if (name !== 'height' && name !== 'width') { - return; - } - - this.updateInputValues_(); - - function adjustToRatio() { - switch (name) { - case 'width': - var newHeight = Math.ceil(this.widthInputValue_ / this.ratio_); - if (this.isInputValidByName_('height', newHeight)) { - this.heightInputValue_ = newHeight; - this.setHeightInputValue_(); - } - break; - case 'height': - var newWidth = Math.ceil(this.heightInputValue_ * this.ratio_); - if(this.isInputValidByName_('width', newWidth)) { - this.widthInputValue_ = newWidth; - this.setWidthInputValue_(); - } - break; - } - } - - if (this.fixedRatio_ && this.isInputValidByName_(name)) { - adjustToRatio.call(this); - } -}; - -/** - * Handlers change events of lock icon. - * @param {Event} event An event. - * @private - */ -ImageEditorMode.Resize.prototype.onLockIconClicked_ = function(event) { - var toggled = !this.fixedRatio_; - if(toggled) { - this.initializeInputValues_(); - this.lockIcon_.setAttribute('locked', ''); - } else { - this.lockIcon_.removeAttribute('locked'); - } - - this.fixedRatio_ = toggled; -}; - -/** - * Set default values. - * @private - */ -ImageEditorMode.Resize.prototype.setDefault_ = function() { - var viewport = this.getViewport(); - assert(viewport); - - var rect = viewport.getImageBounds(); - this.imageWidth_ = rect.width; - this.imageHeight_ = rect.height; - - this.initializeInputValues_(); - - this.ratio_ = this.imageWidth_ / this.imageHeight_; - - this.maxValidWidth_ = Math.max( - this.imageWidth_, ImageEditorMode.Resize.DEFAULT_MAX_VALID_VALUE); - this.maxValidHeight_ = Math.max( - this.imageHeight_, ImageEditorMode.Resize.DEFAULT_MAX_VALID_VALUE); -}; - -/** - * Initialize width/height input values. - * @private - */ -ImageEditorMode.Resize.prototype.initializeInputValues_ = function() { - this.widthInputValue_ = this.imageWidth_; - this.setWidthInputValue_(); - - this.heightInputValue_ = this.imageHeight_; - this.setHeightInputValue_(); -}; - -/** - * Update input values to local variales. - * @private - */ -ImageEditorMode.Resize.prototype.updateInputValues_ = function() { - if (this.widthInputElement_) { - this.widthInputValue_ = parseInt(this.widthInputElement_.getValue(), 10); - } - if (this.heightInputElement_) { - this.heightInputValue_ = parseInt(this.heightInputElement_.getValue(), 10); - } -}; - -/** - * Apply local variables' change to width input element. - * @private - */ -ImageEditorMode.Resize.prototype.setWidthInputValue_ = function() { - if (this.widthInputElement_) { - this.widthInputElement_.setValue(this.widthInputValue_); - } -}; - -/** - * Apply local variables' change to height input element. - * @private - */ -ImageEditorMode.Resize.prototype.setHeightInputValue_ = function() { - if (this.heightInputElement_) { - this.heightInputElement_.setValue(this.heightInputValue_); - } -}; - -/** - * Check if the given name of input has a valid value. - * - * @param {string} name Name of input to check. - * @param {number=} opt_value Value to be checked. Local input's variable will - be checked if undefined. - * @return {boolean} True if the input - * @private - */ -ImageEditorMode.Resize.prototype.isInputValidByName_ = function( - name, opt_value) { - console.assert(name === 'height' || name === 'width'); - - var limit = name === 'width' ? this.maxValidWidth_ : this.maxValidHeight_; - var value = opt_value || - (name === 'width' ? this.widthInputValue_ : this.heightInputValue_); - - return ImageEditorMode.Resize.MINIMUM_VALID_VALUE <= value && value <= limit; -}; - -/** - * Check if width/height input values are valid. - * @return {boolean} true if both values are valid. - */ -ImageEditorMode.Resize.prototype.isInputValid = function() { - return this.isInputValidByName_('width') && - this.isInputValidByName_('height'); -}; - -/** - * Show alert dialog only if input value is invalid. - */ -ImageEditorMode.Resize.prototype.showAlertDialog = function() { - if (this.isInputValid() || this.showingAlertDialog_) { - return; - } - - this.alertDialog_ = this.alertDialog_ || - new FilesAlertDialog(/** @type {!HTMLElement} */ (document.body)); - this.showingAlertDialog_ = true; - this.alertDialog_.showWithTitle('', strf('GALLERY_INVALIDVALUE'), - function() { - this.initializeInputValues_(); - this.showingAlertDialog_ = false; - }.bind(this), null, null); -}; - -/** - * @return {boolean} True when showing an alert dialog. - * @override - */ -ImageEditorMode.Resize.prototype.isConsumingKeyEvents = function() { - return this.showingAlertDialog_; -}; - -/** @override */ -ImageEditorMode.Resize.prototype.cleanUpUI = function() { - ImageEditorMode.prototype.cleanUpUI.apply(this, arguments); - - if(this.showingAlertDialog_) { - this.alertDialog_.hide(); - this.showingAlertDialog_ = false; - } -}; - -/** @override */ -ImageEditorMode.Resize.prototype.reset = function() { - ImageEditorMode.prototype.reset.call(this); - - this.setDefault_(); -}; - -/** @override */ -ImageEditorMode.Resize.prototype.getCommand = function() { - return new Command.Resize(this.widthInputValue_, this.heightInputValue_); -};
diff --git a/ui/file_manager/gallery/js/image_editor/image_transform.js b/ui/file_manager/gallery/js/image_editor/image_transform.js deleted file mode 100644 index 7865797..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_transform.js +++ /dev/null
@@ -1,755 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * Crop mode. - * - * @extends {ImageEditorMode} - * @constructor - * @struct - */ -ImageEditorMode.Crop = function() { - ImageEditorMode.call(this, 'crop', 'GALLERY_CROP'); - - this.paddingTop = ImageEditorMode.Crop.MOUSE_GRAB_RADIUS; - this.paddingBottom = ImageEditorMode.Crop.MOUSE_GRAB_RADIUS; - - /** - * @type {HTMLElement} - * @private - */ - this.domOverlay_ = null; - - /** - * @type {HTMLElement} - * @private - */ - this.shadowTop_ = null; - - /** - * @type {HTMLElement} - * @private - */ - this.middleBox_ = null; - - /** - * @type {HTMLElement} - * @private - */ - this.shadowLeft_ = null; - - /** - * @type {HTMLElement} - * @private - */ - this.cropFrame_ = null; - - /** - * @type {HTMLElement} - * @private - */ - this.shadowRight_ = null; - - /** - * @type {HTMLElement} - * @private - */ - this.shadowBottom_ = null; - - /** - * @type {?function()} - * @private - */ - this.onViewportResizedBound_ = null; - - /** - * @type {DraggableRect} - * @private - */ - this.cropRect_ = null; -}; - -ImageEditorMode.Crop.prototype = { - __proto__: ImageEditorMode.prototype -}; - -/** - * Sets the mode up. - * @override - */ -ImageEditorMode.Crop.prototype.setUp = function() { - ImageEditorMode.prototype.setUp.apply(this, arguments); - - var container = this.getImageView().container_; - var doc = container.ownerDocument; - - this.domOverlay_ = /** @type {!HTMLElement} */ (doc.createElement('div')); - this.domOverlay_.className = 'crop-overlay'; - container.appendChild(this.domOverlay_); - - this.shadowTop_ = /** @type {!HTMLElement} */ (doc.createElement('div')); - this.shadowTop_.className = 'shadow'; - this.domOverlay_.appendChild(this.shadowTop_); - - this.middleBox_ = /** @type {!HTMLElement} */ (doc.createElement('div')); - this.middleBox_.className = 'middle-box'; - this.domOverlay_.appendChild(this.middleBox_); - - this.shadowLeft_ = /** @type {!HTMLElement} */ (doc.createElement('div')); - this.shadowLeft_.className = 'shadow'; - this.middleBox_.appendChild(this.shadowLeft_); - - this.cropFrame_ = /** @type {!HTMLElement} */ (doc.createElement('div')); - this.cropFrame_.className = 'crop-frame'; - this.middleBox_.appendChild(this.cropFrame_); - - this.shadowRight_ = /** @type {!HTMLElement} */ (doc.createElement('div')); - this.shadowRight_.className = 'shadow'; - this.middleBox_.appendChild(this.shadowRight_); - - this.shadowBottom_ = /** @type {!HTMLElement} */ (doc.createElement('div')); - this.shadowBottom_.className = 'shadow'; - this.domOverlay_.appendChild(this.shadowBottom_); - - var cropFrame = this.cropFrame_; - function addCropFrame(className) { - var div = doc.createElement('div'); - div.className = className; - cropFrame.appendChild(div); - } - - addCropFrame('left top corner'); - addCropFrame('top horizontal'); - addCropFrame('right top corner'); - addCropFrame('left vertical'); - addCropFrame('right vertical'); - addCropFrame('left bottom corner'); - addCropFrame('bottom horizontal'); - addCropFrame('right bottom corner'); - - this.onViewportResizedBound_ = this.onViewportResized_.bind(this); - this.getViewport().addEventListener('resize', this.onViewportResizedBound_); - - this.createDefaultCrop(); -}; - -/** - * @override - */ -ImageEditorMode.Crop.prototype.createTools = function(toolbar) { - var aspects = { - GALLERY_ASPECT_RATIO_1_1: 1 / 1, - GALLERY_ASPECT_RATIO_6_4: 6 / 4, - GALLERY_ASPECT_RATIO_7_5: 7 / 5, - GALLERY_ASPECT_RATIO_16_9: 16 / 9 - }; - - // TODO(fukino): The loop order is not guaranteed. Fix it! - for (var name in aspects) { - var button = toolbar.addButton( - name, ImageEditorToolbar.ButtonType.LABEL, - this.onCropAspectRatioClicked_.bind(this, toolbar, aspects[name]), - 'crop-aspect-ratio'); - - // Prevent from cropping by Enter key if the button is focused. - button.addEventListener('keydown', function(event) { - var key = util.getKeyModifiers(event) + event.key; - if (key === 'Enter') { - event.stopPropagation(); - } - }); - } -}; - -/** - * Handles click events of crop aspect ratio buttons. - * @param {!ImageEditorToolbar} toolbar Toolbar. - * @param {number} aspect Aspect ratio. - * @param {Event} event An event. - * @private - */ -ImageEditorMode.Crop.prototype.onCropAspectRatioClicked_ = function( - toolbar, aspect, event) { - var button = event.target; - - if (button.classList.contains('selected')) { - button.classList.remove('selected'); - this.cropRect_.fixedAspectRatio = null; - } else { - var selectedButtons = - toolbar.getElement().querySelectorAll('button.selected'); - for (var i = 0; i < selectedButtons.length; i++) { - selectedButtons[i].classList.remove('selected'); - } - button.classList.add('selected'); - var clipRect = this.viewport_.screenToImageRect( - this.viewport_.getImageBoundsOnScreenClipped()); - this.cropRect_.fixedAspectRatio = aspect; - this.cropRect_.forceAspectRatio(aspect, clipRect); - this.markUpdated(); - this.positionDOM(); - } -}; - -/** - * Handles resizing of the viewport and updates the crop rectangle. - * @private - */ -ImageEditorMode.Crop.prototype.onViewportResized_ = function() { - this.positionDOM(); -}; - -/** - * Resets the mode. - */ -ImageEditorMode.Crop.prototype.reset = function() { - ImageEditorMode.prototype.reset.call(this); - this.createDefaultCrop(); -}; - -/** - * Updates the position of DOM elements. - */ -ImageEditorMode.Crop.prototype.positionDOM = function() { - var screenCrop = this.viewport_.imageToScreenRect(this.cropRect_.getRect()); - - this.shadowLeft_.style.width = screenCrop.left + 'px'; - this.shadowTop_.style.height = screenCrop.top + 'px'; - this.shadowRight_.style.width = window.innerWidth - screenCrop.right + 'px'; - this.shadowBottom_.style.height = - window.innerHeight - screenCrop.bottom + 'px'; -}; - -/** - * Removes the overlay elements from the document. - */ -ImageEditorMode.Crop.prototype.cleanUpUI = function() { - ImageEditorMode.prototype.cleanUpUI.apply(this, arguments); - this.domOverlay_.parentNode.removeChild(this.domOverlay_); - this.domOverlay_ = null; - this.getViewport().removeEventListener( - 'resize', this.onViewportResizedBound_); - this.onViewportResizedBound_ = null; -}; - -/** - * @const - * @type {number} - */ -ImageEditorMode.Crop.MOUSE_GRAB_RADIUS = 6; - -/** - * @const - * @type {number} - */ -ImageEditorMode.Crop.TOUCH_GRAB_RADIUS = 20; - -/** - * Gets command to do the crop depending on the current state. - * - * @return {!Command.Crop} Crop command. - */ -ImageEditorMode.Crop.prototype.getCommand = function() { - var cropImageRect = this.cropRect_.getRect(); - return new Command.Crop(cropImageRect); -}; - -/** - * Creates default (initial) crop. - */ -ImageEditorMode.Crop.prototype.createDefaultCrop = function() { - var viewport = this.getViewport(); - assert(viewport); - - var rect = viewport.screenToImageRect( - viewport.getImageBoundsOnScreenClipped()); - rect = rect.inflate( - -Math.round(rect.width / 6), -Math.round(rect.height / 6)); - - this.cropRect_ = new DraggableRect(rect, viewport); - - this.positionDOM(); -}; - -/** - * Obtains the cursor style depending on the mouse state. - * - * @param {number} x X coordinate for cursor. - * @param {number} y Y coordinate for cursor. - * @param {boolean} mouseDown If mouse button is down. - * @return {string} A value for style.cursor CSS property. - */ -ImageEditorMode.Crop.prototype.getCursorStyle = function(x, y, mouseDown) { - return this.cropRect_.getCursorStyle(x, y, mouseDown); -}; - -/** - * Obtains handler function depending on the mouse state. - * - * @param {number} x Event X coordinate. - * @param {number} y Event Y coordinate. - * @param {boolean} touch True if it's a touch event, false if mouse. - * @return {?function(number,number,boolean)} A function to be called on mouse - * drag. It takes x coordinate value, y coordinate value, and shift key - * flag. - */ -ImageEditorMode.Crop.prototype.getDragHandler = function(x, y, touch) { - var cropDragHandler = this.cropRect_.getDragHandler(x, y, touch); - if (!cropDragHandler) { - return null; - } - - return function(x, y, shiftKey) { - cropDragHandler(x, y, shiftKey); - this.markUpdated(); - this.positionDOM(); - }.bind(this); -}; - -/** - * Obtains the double tap action depending on the coordinate. - * - * @param {number} x X coordinate of the event. - * @param {number} y Y coordinate of the event. - * @return {!ImageBuffer.DoubleTapAction} Action to perform as result. - */ -ImageEditorMode.Crop.prototype.getDoubleTapAction = function(x, y) { - return this.cropRect_.getDoubleTapAction(x, y); -}; - -/** - * A draggable rectangle over the image. - * - * @param {!ImageRect} rect Initial size of the image. - * @param {!Viewport} viewport Viewport. - * @constructor - * @struct - */ -function DraggableRect(rect, viewport) { - /** - * The bounds are not held in a regular rectangle (with width/height). - * left/top/right/bottom held instead for convenience. - * - * @type {{left: number, right: number, top: number, bottom: number}} - * @private - */ - this.bounds_ = { - left: rect.left, - right: rect.left + rect.width, - top: rect.top, - bottom: rect.top + rect.height - }; - - /** - * Viewport. - * - * @type {!Viewport} - * @private - * @const - */ - this.viewport_ = viewport; - - /** - * Drag mode. - * - * @type {Object} - * @private - */ - this.dragMode_ = null; - - /** - * Fixed aspect ratio. - * The aspect ratio is not fixed when null. - * @type {?number} - */ - this.fixedAspectRatio = null; -} - -// Static members to simplify reflective access to the bounds. -/** - * @const - * @type {string} - */ -DraggableRect.LEFT = 'left'; - -/** - * @const - * @type {string} - */ -DraggableRect.RIGHT = 'right'; - -/** - * @const - * @type {string} - */ -DraggableRect.TOP = 'top'; - -/** - * @const - * @type {string} - */ -DraggableRect.BOTTOM = 'bottom'; - -/** - * @const - * @type {string} - */ -DraggableRect.NONE = 'none'; - -/** - * Obtains the left position. - * @return {number} Position. - */ -DraggableRect.prototype.getLeft = function() { - return this.bounds_[DraggableRect.LEFT]; -}; - -/** - * Obtains the right position. - * @return {number} Position. - */ -DraggableRect.prototype.getRight = function() { - return this.bounds_[DraggableRect.RIGHT]; -}; - -/** - * Obtains the top position. - * @return {number} Position. - */ -DraggableRect.prototype.getTop = function() { - return this.bounds_[DraggableRect.TOP]; -}; - -/** - * Obtains the bottom position. - * @return {number} Position. - */ -DraggableRect.prototype.getBottom = function() { - return this.bounds_[DraggableRect.BOTTOM]; -}; - -/** - * Obtains the geometry of the rectangle. - * @return {!ImageRect} Geometry of the rectangle. - */ -DraggableRect.prototype.getRect = function() { - return ImageRect.createFromBounds(this.bounds_); -}; - -/** - * Obtains the drag mode depending on the coordinate. - * - * @param {number} x X coordinate for cursor. - * @param {number} y Y coordinate for cursor. - * @param {boolean=} opt_touch Whether the operation is done by touch or not. - * @return {{xSide: string, ySide:string, whole:boolean, newCrop:boolean}} - * Drag mode. - */ -DraggableRect.prototype.getDragMode = function(x, y, opt_touch) { - var touch = opt_touch || false; - - var result = { - xSide: DraggableRect.NONE, - ySide: DraggableRect.NONE, - whole: false, - newCrop: false - }; - - var bounds = this.bounds_; - var R = this.viewport_.screenToImageSize( - touch ? ImageEditorMode.Crop.TOUCH_GRAB_RADIUS : - ImageEditorMode.Crop.MOUSE_GRAB_RADIUS); - - var circle = new Circle(x, y, R); - - var xBetween = ImageUtil.between(bounds.left, x, bounds.right); - var yBetween = ImageUtil.between(bounds.top, y, bounds.bottom); - - if (circle.inside(bounds.left, bounds.top)) { - result.xSide = DraggableRect.LEFT; - result.ySide = DraggableRect.TOP; - } else if (circle.inside(bounds.left, bounds.bottom)) { - result.xSide = DraggableRect.LEFT; - result.ySide = DraggableRect.BOTTOM; - } else if (circle.inside(bounds.right, bounds.top)) { - result.xSide = DraggableRect.RIGHT; - result.ySide = DraggableRect.TOP; - } else if (circle.inside(bounds.right, bounds.bottom)) { - result.xSide = DraggableRect.RIGHT; - result.ySide = DraggableRect.BOTTOM; - } else if (yBetween && Math.abs(x - bounds.left) <= R) { - result.xSide = DraggableRect.LEFT; - } else if (yBetween && Math.abs(x - bounds.right) <= R) { - result.xSide = DraggableRect.RIGHT; - } else if (xBetween && Math.abs(y - bounds.top) <= R) { - result.ySide = DraggableRect.TOP; - } else if (xBetween && Math.abs(y - bounds.bottom) <= R) { - result.ySide = DraggableRect.BOTTOM; - } else if (xBetween && yBetween) { - result.whole = true; - } else { - result.newcrop = true; - result.xSide = DraggableRect.RIGHT; - result.ySide = DraggableRect.BOTTOM; - } - - return result; -}; - -/** - * Obtains the cursor style depending on the coordinate. - * - * @param {number} x X coordinate for cursor. - * @param {number} y Y coordinate for cursor. - * @param {boolean} mouseDown If mouse button is down. - * @return {string} Cursor style. - */ -DraggableRect.prototype.getCursorStyle = function(x, y, mouseDown) { - var mode; - if (mouseDown) { - mode = this.dragMode_; - } else { - mode = this.getDragMode( - this.viewport_.screenToImageX(x), this.viewport_.screenToImageY(y)); - } - if (mode.whole) { - return 'move'; - } - if (mode.newcrop) { - return 'crop'; - } - - var xSymbol = ''; - switch (mode.xSide) { - case 'left': xSymbol = 'w'; break; - case 'right': xSymbol = 'e'; break; - } - var ySymbol = ''; - switch (mode.ySide) { - case 'top': ySymbol = 'n'; break; - case 'bottom': ySymbol = 's'; break; - } - return ySymbol + xSymbol + '-resize'; -}; - -/** - * Obtains the drag handler depending on the coordinate. - * - * @param {number} initialScreenX X coordinate for cursor in the screen. - * @param {number} initialScreenY Y coordinate for cursor in the screen. - * @param {boolean} touch Whether the operation is done by touch or not. - * @return {?function(number,number,boolean)} Drag handler that takes x - * coordinate value, y coordinate value, and shift key flag. - */ -DraggableRect.prototype.getDragHandler = function( - initialScreenX, initialScreenY, touch) { - // Check if the initial coordinate is in the image rect. - var boundsOnScreen = this.viewport_.getImageBoundsOnScreenClipped(); - var handlerRadius = touch ? ImageEditorMode.Crop.TOUCH_GRAB_RADIUS : - ImageEditorMode.Crop.MOUSE_GRAB_RADIUS; - var draggableAreas = [ - boundsOnScreen, - new Circle(boundsOnScreen.left, boundsOnScreen.top, handlerRadius), - new Circle(boundsOnScreen.right, boundsOnScreen.top, handlerRadius), - new Circle(boundsOnScreen.left, boundsOnScreen.bottom, handlerRadius), - new Circle(boundsOnScreen.right, boundsOnScreen.bottom, handlerRadius) - ]; - - if (!draggableAreas.some( - (area) => area.inside(initialScreenX, initialScreenY))) { - return null; - } - - // Convert coordinates. - var initialX = this.viewport_.screenToImageX(initialScreenX); - var initialY = this.viewport_.screenToImageY(initialScreenY); - var initialWidth = this.bounds_.right - this.bounds_.left; - var initialHeight = this.bounds_.bottom - this.bounds_.top; - var clipRect = this.viewport_.screenToImageRect(boundsOnScreen); - - // Obtain the drag mode. - this.dragMode_ = this.getDragMode(initialX, initialY, touch); - - if (this.dragMode_.whole) { - // Calc constant values during the operation. - var mouseBiasX = this.bounds_.left - initialX; - var mouseBiasY = this.bounds_.top - initialY; - var maxX = clipRect.left + clipRect.width - initialWidth; - var maxY = clipRect.top + clipRect.height - initialHeight; - - // Returns a handler. - return function(newScreenX, newScreenY) { - var newX = this.viewport_.screenToImageX(newScreenX); - var newY = this.viewport_.screenToImageY(newScreenY); - var clamppedX = ImageUtil.clamp(clipRect.left, newX + mouseBiasX, maxX); - var clamppedY = ImageUtil.clamp(clipRect.top, newY + mouseBiasY, maxY); - this.bounds_.left = clamppedX; - this.bounds_.right = clamppedX + initialWidth; - this.bounds_.top = clamppedY; - this.bounds_.bottom = clamppedY + initialHeight; - }.bind(this); - } else { - // Calc constant values during the operation. - var mouseBiasX = this.bounds_[this.dragMode_.xSide] - initialX; - var mouseBiasY = this.bounds_[this.dragMode_.ySide] - initialY; - var maxX = clipRect.left + clipRect.width; - var maxY = clipRect.top + clipRect.height; - - // Returns a handler. - return function(newScreenX, newScreenY, shiftKey) { - var newX = this.viewport_.screenToImageX(newScreenX); - var newY = this.viewport_.screenToImageY(newScreenY); - - // Check new crop. - if (this.dragMode_.newcrop) { - this.dragMode_.newcrop = false; - this.bounds_.left = this.bounds_.right = initialX; - this.bounds_.top = this.bounds_.bottom = initialY; - mouseBiasX = 0; - mouseBiasY = 0; - } - - // Update X coordinate. - if (this.dragMode_.xSide !== DraggableRect.NONE) { - this.bounds_[this.dragMode_.xSide] = - ImageUtil.clamp(clipRect.left, newX + mouseBiasX, maxX); - if (this.bounds_.left > this.bounds_.right) { - var left = this.bounds_.left; - var right = this.bounds_.right; - this.bounds_.left = right - 1; - this.bounds_.right = left + 1; - this.dragMode_.xSide = - this.dragMode_.xSide == 'left' ? 'right' : 'left'; - } - } - - // Update Y coordinate. - if (this.dragMode_.ySide !== DraggableRect.NONE) { - this.bounds_[this.dragMode_.ySide] = - ImageUtil.clamp(clipRect.top, newY + mouseBiasY, maxY); - if (this.bounds_.top > this.bounds_.bottom) { - var top = this.bounds_.top; - var bottom = this.bounds_.bottom; - this.bounds_.top = bottom - 1; - this.bounds_.bottom = top + 1; - this.dragMode_.ySide = - this.dragMode_.ySide === 'top' ? 'bottom' : 'top'; - } - } - - // Update aspect ratio. - if (this.fixedAspectRatio) { - this.forceAspectRatio(this.fixedAspectRatio, clipRect); - } else if (shiftKey) { - this.forceAspectRatio(initialWidth / initialHeight, clipRect); - } - }.bind(this); - } -}; - -/** - * Obtains double tap action depending on the coordinate. - * - * @param {number} x X coordinate for cursor. - * @param {number} y Y coordinate for cursor. - * @return {!ImageBuffer.DoubleTapAction} Double tap action. - */ -DraggableRect.prototype.getDoubleTapAction = function(x, y) { - var clipRect = this.viewport_.getImageBoundsOnScreenClipped(); - if (clipRect.inside(x, y)) { - return ImageBuffer.DoubleTapAction.COMMIT; - } else { - return ImageBuffer.DoubleTapAction.NOTHING; - } -}; - -/** - * Forces the aspect ratio. - * - * @param {number} aspectRatio Aspect ratio. - * @param {!Object} clipRect Clip rect. - */ -DraggableRect.prototype.forceAspectRatio = function(aspectRatio, clipRect) { - // Get current rectangle scale. - var width = this.bounds_.right - this.bounds_.left; - var height = this.bounds_.bottom - this.bounds_.top; - var currentScale; - if (!this.dragMode_) { - currentScale = ((width / aspectRatio) + height) / 2; - } else if (this.dragMode_.xSide === 'none') { - currentScale = height; - } else if (this.dragMode_.ySide === 'none') { - currentScale = width / aspectRatio; - } else { - currentScale = Math.max(width / aspectRatio, height); - } - - // Get maximum width/height scale. - var maxWidth; - var maxHeight; - var center = (this.bounds_.left + this.bounds_.right) / 2; - var middle = (this.bounds_.top + this.bounds_.bottom) / 2; - var xSide = this.dragMode_ ? this.dragMode_.xSide : 'none'; - var ySide = this.dragMode_ ? this.dragMode_.ySide : 'none'; - switch (xSide) { - case 'left': - maxWidth = this.bounds_.right - clipRect.left; - break; - case 'right': - maxWidth = clipRect.left + clipRect.width - this.bounds_.left; - break; - case 'none': - maxWidth = Math.min( - clipRect.left + clipRect.width - center, - center - clipRect.left) * 2; - break; - } - switch (ySide) { - case 'top': - maxHeight = this.bounds_.bottom - clipRect.top; - break; - case 'bottom': - maxHeight = clipRect.top + clipRect.height - this.bounds_.top; - break; - case 'none': - maxHeight = Math.min( - clipRect.top + clipRect.height - middle, - middle - clipRect.top) * 2; - break; - } - - // Obtains target scale. - var targetScale = Math.min( - currentScale, - maxWidth / aspectRatio, - maxHeight); - - // Update bounds. - var newWidth = targetScale * aspectRatio; - var newHeight = targetScale; - switch (xSide) { - case 'left': - this.bounds_.left = this.bounds_.right - newWidth; - break; - case 'right': - this.bounds_.right = this.bounds_.left + newWidth; - break; - case 'none': - this.bounds_.left = center - newWidth / 2; - this.bounds_.right = center + newWidth / 2; - break; - } - switch (ySide) { - case 'top': - this.bounds_.top = this.bounds_.bottom - newHeight; - break; - case 'bottom': - this.bounds_.bottom = this.bounds_.top + newHeight; - break; - case 'none': - this.bounds_.top = middle - newHeight / 2; - this.bounds_.bottom = middle + newHeight / 2; - break; - } -};
diff --git a/ui/file_manager/gallery/js/image_editor/image_util.js b/ui/file_manager/gallery/js/image_editor/image_util.js deleted file mode 100644 index aa72761..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_util.js +++ /dev/null
@@ -1,506 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview - * @suppress {uselessCode} Temporary suppress because of the line exporting. - */ - -/* eslint-disable no-var */ - -// clang-format off -// #import {assert, assertInstanceof} from 'chrome://resources/js/assert.m.js'; -// clang-format on - -// Namespace object for the utilities. -var ImageUtil = {}; - -/** - * Performance trace. - */ -ImageUtil.trace = (function() { - /** - * Performance trace. - * @constructor - * @struct - */ - function PerformanceTrace() { - this.lines_ = {}; - this.timers_ = {}; - this.container_ = null; - } - - PerformanceTrace.prototype.bindToDOM = function(container) { - this.container_ = container; - }; - - PerformanceTrace.prototype.report = function(key, value) { - if (!(key in this.lines_)) { - if (this.container_) { - var div = this.lines_[key] = document.createElement('div'); - this.container_.appendChild(div); - } else { - this.lines_[key] = {}; - } - } - this.lines_[key].textContent = key + ': ' + value; - if (ImageUtil.trace.log) this.dumpLine(key); - }; - - PerformanceTrace.prototype.resetTimer = function(key) { - this.timers_[key] = Date.now(); - }; - - PerformanceTrace.prototype.reportTimer = function(key) { - this.report(key, (Date.now() - this.timers_[key]) + 'ms'); - }; - - PerformanceTrace.prototype.dump = function() { - for (var key in this.lines_) { - this.dumpLine(key); - } - }; - - PerformanceTrace.prototype.dumpLine = function(key) { - console.log('trace.' + this.lines_[key].textContent); - }; - - return new PerformanceTrace(); -})(); - -/** - * @param {number} min Minimum value. - * @param {number} value Value to adjust. - * @param {number} max Maximum value. - * @return {number} The closest to the |value| number in span [min, max]. - */ -ImageUtil.clamp = function(min, value, max) { - return Math.max(min, Math.min(max, value)); -}; - -/** - * @param {number} min Minimum value. - * @param {number} value Value to check. - * @param {number} max Maximum value. - * @return {boolean} True if value is between. - */ -ImageUtil.between = function(min, value, max) { - return (value - min) * (value - max) <= 0; -}; - -/** - * Rectangle class. - * - * @param {number} left Left. - * @param {number} top Top. - * @param {number} width Width. - * @param {number} height Height. - * @constructor - * @struct - */ -/* #export */ function ImageRect(left, top, width, height) { - this.left = left; - this.top = top; - this.width = width; - this.height = height; -} - -/** - * Creates an image rect with an image or a canvas. - * @param {!(HTMLImageElement|HTMLCanvasElement)} image An image or a canvas. - * @return {!ImageRect} - */ -ImageRect.createFromImage = function(image) { - return new ImageRect(0, 0, image.width, image.height); -}; - -/** - * Clone an image rect. - * @param {!ImageRect} imageRect An image rect. - * @return {!ImageRect} - */ -ImageRect.clone = function(imageRect) { - return new ImageRect(imageRect.left, imageRect.top, imageRect.width, - imageRect.height); -}; - -/** - * Creates an image rect with a bound. - * @param {{left: number, top: number, right: number, bottom: number}} bound - * A bound. - * @return {!ImageRect} - */ -ImageRect.createFromBounds = function(bound) { - return new ImageRect(bound.left, bound.top, - bound.right - bound.left, bound.bottom - bound.top); -}; - -/** - * Creates an image rect with width and height. - * @param {number} width Width. - * @param {number} height Height. - * @return {!ImageRect} - */ -ImageRect.createFromWidthAndHeight = function(width, height) { - return new ImageRect(0, 0, width, height); -}; - -ImageRect.prototype = /** @struct */ ({ - // TODO(yawano): Change getters to methods (e.g. getRight()). - - /** - * Obtains the x coordinate of right edge. The most right pixels in the - * rectangle are (x = right - 1) and the pixels (x = right) are not included - * in the rectangle. - * @return {number} - */ - get right() { - return this.left + this.width; - }, - - /** - * Obtains the y coordinate of bottom edge. The most bottom pixels in the - * rectangle are (y = bottom - 1) and the pixels (y = bottom) are not included - * in the rectangle. - * @return {number} - */ - get bottom() { - return this.top + this.height; - } -}); - -/** - * @param {number} factor Factor to scale. - * @return {!ImageRect} A rectangle with every dimension scaled. - */ -ImageRect.prototype.scale = function(factor) { - return new ImageRect( - this.left * factor, - this.top * factor, - this.width * factor, - this.height * factor); -}; - -/** - * @param {number} dx Difference in X. - * @param {number} dy Difference in Y. - * @return {!ImageRect} A rectangle shifted by (dx,dy), same size. - */ -ImageRect.prototype.shift = function(dx, dy) { - return new ImageRect(this.left + dx, this.top + dy, this.width, this.height); -}; - -/** - * @param {number} x Coordinate of the left top corner. - * @param {number} y Coordinate of the left top corner. - * @return {!ImageRect} A rectangle with left==x and top==y, same size. - */ -ImageRect.prototype.moveTo = function(x, y) { - return new ImageRect(x, y, this.width, this.height); -}; - -/** - * @param {number} dx Difference in X. - * @param {number} dy Difference in Y. - * @return {!ImageRect} A rectangle inflated by (dx, dy), same center. - */ -ImageRect.prototype.inflate = function(dx, dy) { - return new ImageRect( - this.left - dx, this.top - dy, this.width + 2 * dx, this.height + 2 * dy); -}; - -/** - * @param {number} x Coordinate of the point. - * @param {number} y Coordinate of the point. - * @return {boolean} True if the point lies inside the rectangle. - */ -ImageRect.prototype.inside = function(x, y) { - return this.left <= x && x < this.left + this.width && - this.top <= y && y < this.top + this.height; -}; - -/** - * @param {!ImageRect} rect Rectangle to check. - * @return {boolean} True if this rectangle intersects with the |rect|. - */ -ImageRect.prototype.intersects = function(rect) { - return (this.left + this.width) > rect.left && - (rect.left + rect.width) > this.left && - (this.top + this.height) > rect.top && - (rect.top + rect.height) > this.top; -}; - -/** - * @param {!ImageRect} rect Rectangle to check. - * @return {boolean} True if this rectangle containing the |rect|. - */ -ImageRect.prototype.contains = function(rect) { - return (this.left <= rect.left) && - (rect.left + rect.width) <= (this.left + this.width) && - (this.top <= rect.top) && - (rect.top + rect.height) <= (this.top + this.height); -}; - -/** - * @return {boolean} True if rectangle is empty. - */ -ImageRect.prototype.isEmpty = function() { - return this.width === 0 || this.height === 0; -}; - -/** - * Clamp the rectangle to the bounds by moving it. - * Decrease the size only if necessary. - * @param {!ImageRect} bounds Bounds. - * @return {!ImageRect} Calculated rectangle. - */ -ImageRect.prototype.clamp = function(bounds) { - var rect = ImageRect.clone(this); - - if (rect.width > bounds.width) { - rect.left = bounds.left; - rect.width = bounds.width; - } else if (rect.left < bounds.left) { - rect.left = bounds.left; - } else if (rect.left + rect.width > - bounds.left + bounds.width) { - rect.left = bounds.left + bounds.width - rect.width; - } - - if (rect.height > bounds.height) { - rect.top = bounds.top; - rect.height = bounds.height; - } else if (rect.top < bounds.top) { - rect.top = bounds.top; - } else if (rect.top + rect.height > - bounds.top + bounds.height) { - rect.top = bounds.top + bounds.height - rect.height; - } - - return rect; -}; - -/** - * @return {string} String representation. - */ -ImageRect.prototype.toString = function() { - return '(' + this.left + ',' + this.top + '):' + - '(' + (this.left + this.width) + ',' + (this.top + this.height) + ')'; -}; -/* - * Useful shortcuts for drawing (static functions). - */ - -/** - * Draw the image in context with appropriate scaling. - * @param {!CanvasRenderingContext2D} context Context to draw. - * @param {!(HTMLCanvasElement|HTMLImageElement)} image Image to draw. - * @param {ImageRect=} opt_dstRect Rectangle in the canvas (whole canvas by - * default). - * @param {ImageRect=} opt_srcRect Rectangle in the image (whole image by - * default). - */ -ImageRect.drawImage = function(context, image, opt_dstRect, opt_srcRect) { - opt_dstRect = opt_dstRect || - ImageRect.createFromImage(assert(context.canvas)); - opt_srcRect = opt_srcRect || ImageRect.createFromImage(image); - if (opt_dstRect.isEmpty() || opt_srcRect.isEmpty()) { - return; - } - context.drawImage(image, - opt_srcRect.left, opt_srcRect.top, opt_srcRect.width, opt_srcRect.height, - opt_dstRect.left, opt_dstRect.top, opt_dstRect.width, opt_dstRect.height); -}; - -/** - * Draw a box around the rectangle. - * @param {!CanvasRenderingContext2D} context Context to draw. - * @param {!ImageRect} rect Rectangle. - */ -ImageRect.outline = function(context, rect) { - context.strokeRect( - rect.left - 0.5, rect.top - 0.5, rect.width + 1, rect.height + 1); -}; - -/** - * Fill the rectangle. - * @param {!CanvasRenderingContext2D} context Context to draw. - * @param {!ImageRect} rect Rectangle. - */ -ImageRect.fill = function(context, rect) { - context.fillRect(rect.left, rect.top, rect.width, rect.height); -}; - -/** - * Fills the space between the two rectangles. - * @param {!CanvasRenderingContext2D} context Context to draw. - * @param {!ImageRect} inner Inner rectangle. - * @param {!ImageRect} outer Outer rectangle. - */ -ImageRect.fillBetween = function(context, inner, outer) { - var innerRight = inner.left + inner.width; - var innerBottom = inner.top + inner.height; - var outerRight = outer.left + outer.width; - var outerBottom = outer.top + outer.height; - if (inner.top > outer.top) { - context.fillRect( - outer.left, outer.top, outer.width, inner.top - outer.top); - } - if (inner.left > outer.left) { - context.fillRect( - outer.left, inner.top, inner.left - outer.left, inner.height); - } - if (inner.width < outerRight) { - context.fillRect( - innerRight, inner.top, outerRight - innerRight, inner.height); - } - if (inner.height < outerBottom) { - context.fillRect( - outer.left, innerBottom, outer.width, outerBottom - innerBottom); - } -}; - -/** - * Circle class. - * @param {number} x X coordinate of circle center. - * @param {number} y Y coordinate of circle center. - * @param {number} r Radius. - * @constructor - */ -function Circle(x, y, r) { - this.x = x; - this.y = y; - this.squaredR = r * r; -} - -/** - * Check if the point is inside the circle. - * @param {number} x X coordinate of the point. - * @param {number} y Y coordinate of the point. - * @return {boolean} True if the point is inside. - */ -Circle.prototype.inside = function(x, y) { - x -= this.x; - y -= this.y; - return x * x + y * y <= this.squaredR; -}; - -/** - * Copy an image applying scaling and rotation. - * - * @param {!HTMLCanvasElement} dst Destination. - * @param {!(HTMLCanvasElement|HTMLImageElement)} src Source. - * @param {number} scaleX Y scale transformation. - * @param {number} scaleY X scale transformation. - * @param {number} angle (in radians). - */ -ImageUtil.drawImageTransformed = function(dst, src, scaleX, scaleY, angle) { - var context = dst.getContext('2d'); - context.save(); - context.translate(context.canvas.width / 2, context.canvas.height / 2); - context.rotate(angle); - context.scale(scaleX, scaleY); - context.drawImage(src, -src.width / 2, -src.height / 2); - context.restore(); -}; - -/** - * Adds or removes an attribute to/from an HTML element. - * @param {!HTMLElement} element To be applied to. - * @param {string} attribute Name of attribute. - * @param {boolean} on True if add, false if remove. - */ -ImageUtil.setAttribute = function(element, attribute, on) { - if (on) { - element.setAttribute(attribute, ''); - } else { - element.removeAttribute(attribute); - } -}; - -/** - * Adds or removes CSS class to/from an HTML element. - * @param {!HTMLElement} element To be applied to. - * @param {string} className Name of CSS class. - * @param {boolean} on True if add, false if remove. - */ -ImageUtil.setClass = function(element, className, on) { - var cl = element.classList; - if (on) { - cl.add(className); - } else { - cl.remove(className); - } -}; - -/** - * @param {!HTMLElement} element To remove children from. - */ -ImageUtil.removeChildren = function(element) { - element.textContent = ''; -}; - -/** - * @param {string} name File name (with extension). - * @return {string} File name without extension. - */ -ImageUtil.getDisplayNameFromName = function(name) { - var index = name.lastIndexOf('.'); - if (index !== -1) { - return name.substr(0, index); - } else { - return name; - } -}; - -/** - * @param {string} name File name. - * @return {string} File extension. - */ -ImageUtil.getExtensionFromFullName = function(name) { - var index = name.lastIndexOf('.'); - if (index !== -1) { - return name.substring(index); - } else { - return ''; - } -}; - -/** - * @param {string} name Local name. - * @return {string} Full name. - */ -ImageUtil.getMetricName = function(name) { - return 'PhotoEditor.' + name; -}; - -/** - * Ensures argument is canvas. If it's not, creates new canvas and copy. - * - * @param {!HTMLCanvasElement|!HTMLImageElement} imgOrCanvas image or canvas - * element - * @return {!HTMLCanvasElement} canvas. - */ -ImageUtil.ensureCanvas = function(imgOrCanvas) { - if(imgOrCanvas.tagName === 'canvas') { - return assertInstanceof(imgOrCanvas, HTMLCanvasElement); - } - var canvas = assertInstanceof(document.createElement('canvas'), - HTMLCanvasElement); - canvas.width = imgOrCanvas.width; - canvas.height = imgOrCanvas.height; - var context = canvas.getContext('2d'); - context.drawImage(imgOrCanvas, 0, 0); - return canvas; -}; - -/** - * Used for metrics reporting, keep in sync with the histogram description. - * @type {Array<string>} - * @const - */ -ImageUtil.FILE_TYPES = ['jpg', 'png', 'gif', 'bmp', 'webp']; - -// eslint-disable-next-line semi,no-extra-semi -/* #export */ {ImageUtil};
diff --git a/ui/file_manager/gallery/js/image_editor/image_view.js b/ui/file_manager/gallery/js/image_editor/image_view.js deleted file mode 100644 index 9fd0ba0..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_view.js +++ /dev/null
@@ -1,1028 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * The overlay displaying the image. - * - * @param {!HTMLElement} container The container element. - * @param {!Viewport} viewport The viewport. - * @param {!MetadataModel} metadataModel - * @constructor - * @extends {ImageBuffer.Overlay} - * @struct - */ -function ImageView(container, viewport, metadataModel) { - ImageBuffer.Overlay.call(this); - - this.container_ = container; - - /** - * The viewport. - * @type {!Viewport} - * @private - */ - this.viewport_ = viewport; - - this.document_ = assertInstanceof(container.ownerDocument, HTMLDocument); - this.contentGeneration_ = 0; - this.displayedContentGeneration_ = 0; - - this.imageLoader_ = - new ImageUtil.ImageLoader(this.document_, metadataModel); - // We have a separate image loader for prefetch which does not get cancelled - // when the selection changes. - this.prefetchLoader_ = - new ImageUtil.ImageLoader(this.document_, metadataModel); - - this.contentCallbacks_ = []; - - /** - * The content image or canvas element. - * @type {(HTMLCanvasElement|HTMLImageElement|HTMLVideoElement)} - * @private - */ - this.contentImage_ = null; - - /** - * True if the image is a preview (not full res). - * @type {boolean} - * @private - */ - this.preview_ = false; - - /** - * Cached thumbnail image. - * @type {HTMLCanvasElement} - * @private - */ - this.thumbnailCanvas_ = null; - - /** - * The content revision number. - * @type {number} - * @private - */ - this.contentRevision_ = -1; - - /** - * The last load time. - * @type {?number} - * @private - */ - this.lastLoadTime_ = null; - - /** - * Gallery item which is loaded. - * @type {GalleryItem} - * @private - */ - this.contentItem_ = null; - - /** - * Timer to unload. - * @type {?number} - * @private - */ - this.unloadTimer_ = null; -} - -/** - * Duration of transition between modes in ms. - * @type {number} - * @const - */ -ImageView.MODE_TRANSITION_DURATION = 350; - -/** - * If the user flips though images faster than this interval we do not apply - * the slide-in/slide-out transition. - * @type {number} - * @const - */ -ImageView.FAST_SCROLL_INTERVAL = 300; - - -/** - * Image load types. - * @enum {number} - */ -ImageView.LoadType = { - // Full resolution image loaded from cache. - CACHED_FULL: 0, - // Screen resolution preview loaded from cache. - CACHED_SCREEN: 1, - // Image read from file. - IMAGE_FILE: 2, - // Error occurred. - ERROR: 3, - // The file contents is not available offline. - OFFLINE: 4 -}; - -/** - * Target of image load. - * @enum {string} - */ -ImageView.LoadTarget = { - CACHED_MAIN_IMAGE: 'cachedMainImage', - THUMBNAIL: 'thumbnail', - MAIN_IMAGE: 'mainImage' -}; - -/** - * Obtains prefered load type from GalleryItem. - * - * @param {!GalleryItem} item - * @param {!ImageView.Effect} effect - * @return {ImageView.LoadTarget} Load target. - */ -ImageView.getLoadTarget = function(item, effect) { - if (item.contentImage && !item.requireLongRenderingTime()) { - return ImageView.LoadTarget.CACHED_MAIN_IMAGE; - } - - // Only show thumbnails if there is no effect or the effect is Slide or - // ZoomToScreen. - var thumbnailLoader = new ThumbnailLoader( - item.getEntry(), - ThumbnailLoader.LoaderType.CANVAS, - item.getThumbnailMetadataItem()); - if ((effect instanceof ImageView.Effect.None || - effect instanceof ImageView.Effect.Slide || - effect instanceof ImageView.Effect.ZoomToScreen) && - thumbnailLoader.getLoadTarget() !== - ThumbnailLoader.LoadTarget.FILE_ENTRY) { - return ImageView.LoadTarget.THUMBNAIL; - } - - return ImageView.LoadTarget.MAIN_IMAGE; -}; - -ImageView.prototype = {__proto__: ImageBuffer.Overlay.prototype}; - -/** - * @override - */ -ImageView.prototype.getZIndex = function() { - return -1; -}; - -/** - * @override - */ -ImageView.prototype.draw = function() { - if (!this.contentImage_) { // Do nothing if the image content is not set. - return; - } - this.setTransform_( - this.contentImage_, - this.viewport_, - new ImageView.Effect.None(), - 0); -}; - -/** - * Applies the viewport change that does not affect the screen cache size (zoom - * change or offset change) with animation. - */ -ImageView.prototype.applyViewportChange = function() { - if (this.contentImage_) { - this.setTransform_( - this.contentImage_, - this.viewport_, - new ImageView.Effect.None(), - ImageView.Effect.DEFAULT_DURATION); - } -}; - -/** - * @return {number} The cache generation. - */ -ImageView.prototype.getCacheGeneration = function() { - return this.contentGeneration_; -}; - -/** - * Invalidates the caches to force redrawing the screen canvas. - */ -ImageView.prototype.invalidateCaches = function() { - this.contentGeneration_++; -}; - -/** - * @return {!HTMLCanvasElement|!HTMLImageElement} The content image(or canvas). - */ -ImageView.prototype.getEditableImage = function() { - assert( - this.contentImage_ instanceof HTMLCanvasElement || - this.contentImage_ instanceof HTMLImageElement); - return assert(this.contentImage_); -}; - -/** - * @return {!HTMLCanvasElement|!HTMLImageElement|!HTMLVideoElement} The content - * image, canvas or video. - */ -ImageView.prototype.getMedia = function() { - return assert(this.contentImage_); -}; - -/** - * @return {boolean} True if the a valid image is currently loaded. - */ -ImageView.prototype.hasValidImage = function() { - return !!(!this.preview_ && this.contentImage_ && this.contentImage_.width); -}; - -/** - * @return {!HTMLCanvasElement} The cached thumbnail image. - */ -ImageView.prototype.getThumbnail = function() { - assert(this.thumbnailCanvas_); - return this.thumbnailCanvas_; -}; - -/** - * @return {number} The content revision number. - */ -ImageView.prototype.getContentRevision = function() { - return this.contentRevision_; -}; - -/** - * Copies an image fragment to a content image. - * - * @param {!HTMLCanvasElement} canvas Canvas containing whole image. The canvas - * may not be full resolution (scaled). - * @param {!ImageRect} imageRect Rectangle region of the canvas to be rendered. - */ -ImageView.prototype.paintDeviceRect = function(canvas, imageRect) { - // Map the rectangle in full resolution image to the rectangle in the device - // canvas. - // TODO(ryoh): Shold we prepare a device-res canvas to show? - var deviceBounds = this.viewport_.getDeviceBounds(); - var scaleX = deviceBounds.width / canvas.width; - var scaleY = deviceBounds.height / canvas.height; - var deviceRect = new ImageRect( - imageRect.left * scaleX, - imageRect.top * scaleY, - imageRect.width * scaleX, - imageRect.height * scaleY); - - var canvas = ImageUtil.ensureCanvas(assert(this.getEditableImage())); - if (canvas !== this.contentImage_) { - this.replaceContent_(canvas); - } - ImageRect.drawImage( - /** @type {!CanvasRenderingContext2D} */ ( - this.contentImage_.getContext('2d')), - canvas, deviceRect, imageRect); -}; - -/** - * Creates an overlay canvas with properties similar to the screen canvas. - * Useful for showing quick feedback when editing. - * - * @return {!HTMLCanvasElement} Overlay canvas. - */ -ImageView.prototype.createOverlayCanvas = function() { - var canvas = assertInstanceof(this.document_.createElement('canvas'), - HTMLCanvasElement); - canvas.className = 'image'; - this.container_.appendChild(canvas); - return canvas; -}; - -/** - * Sets up the canvas as a buffer in the device resolution. - * - * @param {!HTMLCanvasElement} canvas The buffer canvas. - * @return {boolean} True if the canvas needs to be rendered. - */ -ImageView.prototype.setupDeviceBuffer = function(canvas) { - // Set the canvas position and size in device pixels. - var deviceRect = this.viewport_.getDeviceBounds(); - var needRepaint = false; - if (canvas.width !== deviceRect.width) { - canvas.width = deviceRect.width; - needRepaint = true; - } - if (canvas.height !== deviceRect.height) { - canvas.height = deviceRect.height; - needRepaint = true; - } - this.setTransform_(canvas, this.viewport_); - return needRepaint; -}; - -/** - * Gets screen image canvas with specified size. - * @param {number} width - * @param {number} height - * @return {!HTMLCanvasElement} A scaled canvas. - */ -ImageView.prototype.getImageCanvasWith = function(width, height) { - // Resize if these sizes are different. - var resizeCanvas = assertInstanceof(document.createElement('canvas'), - HTMLCanvasElement); - resizeCanvas.width = width; - resizeCanvas.height = height; - - var context = resizeCanvas.getContext('2d'); - context.drawImage(this.contentImage_, - 0, 0, this.contentImage_.width, this.contentImage_.height, - 0, 0, resizeCanvas.width, resizeCanvas.height); - return resizeCanvas; -}; - -/** - * @return {boolean} True if the image is currently being loaded. - */ -ImageView.prototype.isLoading = function() { - return this.imageLoader_.isBusy(); -}; - -/** - * Cancels the current image loading operation. The callbacks will be ignored. - */ -ImageView.prototype.cancelLoad = function() { - this.imageLoader_.cancel(); -}; - -/** - * Loads and display a new image. - * - * Loads the thumbnail first, then replaces it with the main image. - * Takes into account the image orientation encoded in the metadata. - * - * @param {!GalleryItem} item Gallery item to be loaded. - * @param {!ImageView.Effect} effect Transition effect object. - * @param {function()} displayCallback Called when the image is displayed - * (possibly as a preview). - * @param {function(!ImageView.LoadType, number, *=)} loadCallback Called when - * the image is fully loaded. The first parameter is the load type. - */ -ImageView.prototype.load = - function(item, effect, displayCallback, loadCallback) { - var entry = item.getEntry(); - - if (!(effect instanceof ImageView.Effect.None)) { - // Skip effects when reloading repeatedly very quickly. - var time = Date.now(); - if (this.lastLoadTime_ && - (time - this.lastLoadTime_) < ImageView.FAST_SCROLL_INTERVAL) { - effect = new ImageView.Effect.None(); - } - this.lastLoadTime_ = time; - } - - metrics.startInterval(ImageUtil.getMetricName('DisplayTime')); - - var self = this; - - this.contentItem_ = item; - this.contentRevision_ = -1; - - switch (ImageView.getLoadTarget(item, effect)) { - case ImageView.LoadTarget.CACHED_MAIN_IMAGE: - displayMainImage( - ImageView.LoadType.CACHED_FULL, - false /* no preview */, - assert(item.contentImage)); - break; - - case ImageView.LoadTarget.THUMBNAIL: - var thumbnailLoader = new ThumbnailLoader( - entry, - ThumbnailLoader.LoaderType.CANVAS, - item.getThumbnailMetadataItem()); - thumbnailLoader.loadDetachedImage(function(success) { - displayThumbnail( - ImageView.LoadType.IMAGE_FILE, - success ? thumbnailLoader.getImage() : null); - }); - break; - - case ImageView.LoadTarget.MAIN_IMAGE: - loadMainImage( - ImageView.LoadType.IMAGE_FILE, - entry, - false /* no preview*/, - 0 /* delay */); - break; - - default: - assertNotReached(); - } - - /** - * @param {!ImageView.LoadType} loadType A load type. - * @param {(HTMLCanvasElement|HTMLImageElement)} canvas A canvas. - */ - function displayThumbnail(loadType, canvas) { - if (canvas) { - var width = item.getMetadataItem().imageWidth; - var height = item.getMetadataItem().imageHeight; - self.replace( - canvas, - effect, - width, - height, - true /* preview */); - if (displayCallback) { - displayCallback(); - } - } - loadMainImage(loadType, entry, !!canvas, - (effect && canvas) ? effect.getSafeInterval() : 0); - } - - /** - * @param {!ImageView.LoadType} loadType Load type. - * @param {Entry} contentEntry A content entry. - * @param {boolean} previewShown A preview is shown or not. - * @param {number} delay Load delay. - */ - function loadMainImage(loadType, contentEntry, previewShown, delay) { - if (self.prefetchLoader_.isLoading(contentEntry)) { - // The image we need is already being prefetched. Initiating another load - // would be a waste. Hijack the load instead by overriding the callback. - self.prefetchLoader_.setCallback( - displayMainImage.bind(null, loadType, previewShown)); - - // Swap the loaders so that the self.isLoading works correctly. - var temp = self.prefetchLoader_; - self.prefetchLoader_ = self.imageLoader_; - self.imageLoader_ = temp; - return; - } - self.prefetchLoader_.cancel(); // The prefetch was doing something useless. - - self.imageLoader_.load( - item, - displayMainImage.bind(null, loadType, previewShown), - delay); - } - - /** - * @param {!ImageView.LoadType} loadType Load type. - * @param {boolean} previewShown A preview is shown or not. - * @param {!(HTMLCanvasElement|HTMLImageElement)} content A content. - * @param {string=} opt_error Error message. - */ - function displayMainImage(loadType, previewShown, content, opt_error) { - if (opt_error) { - loadType = ImageView.LoadType.ERROR; - } - - // If we already displayed the preview we should not replace the content if - // the full content failed to load. - var animationDuration = 0; - if (!(previewShown && loadType === ImageView.LoadType.ERROR)) { - var replaceEffect = previewShown ? null : effect; - animationDuration = replaceEffect ? replaceEffect.getSafeInterval() : 0; - self.replace(content, replaceEffect); - if (!previewShown && displayCallback) displayCallback(); - } - - if (loadType !== ImageView.LoadType.ERROR && - loadType !== ImageView.LoadType.CACHED_SCREEN) { - metrics.recordInterval(ImageUtil.getMetricName('DisplayTime')); - } - metrics.recordEnum(ImageUtil.getMetricName('LoadMode'), - loadType, Object.keys(ImageView.LoadType).length); - - if (loadType === ImageView.LoadType.ERROR && - !navigator.onLine && !item.getMetadataItem().present) { - loadType = ImageView.LoadType.OFFLINE; - } - if (loadCallback) loadCallback(loadType, animationDuration, opt_error); - } -}; - -/** - * Prefetches an image. - * @param {!GalleryItem} item The image item. - * @param {number=} opt_delay Image load delay in ms. - */ -ImageView.prototype.prefetch = function(item, opt_delay) { - if (item.contentImage || this.prefetchLoader_.isLoading(item.getEntry())) { - return; - } - this.prefetchLoader_.load(item, function(canvas) { - if (canvas.width && canvas.height && !item.contentImage) { - item.contentImage = canvas; - } - }, opt_delay); -}; - -/** - * Unloads content. - * @param {ImageRect=} opt_zoomToRect Target rectangle for zoom-out-effect. - */ -ImageView.prototype.unload = function(opt_zoomToRect) { - if (this.unloadTimer_) { - clearTimeout(this.unloadTimer_); - this.unloadTimer_ = null; - } - if (opt_zoomToRect && this.contentImage_) { - var effect = this.createZoomEffect(opt_zoomToRect); - this.setTransform_(this.contentImage_, this.viewport_, effect); - this.contentImage_.setAttribute('fade', true); - this.unloadTimer_ = setTimeout(function() { - this.unloadTimer_ = null; - this.unload(null /* force unload */); - }.bind(this), effect.getSafeInterval()); - return; - } - this.container_.textContent = ''; - this.contentImage_ = null; -}; - -/** - * @param {!(HTMLCanvasElement|HTMLImageElement|HTMLVideoElement)} content The - * image, canvas or video element. - * @param {number=} opt_width Image width. - * @param {number=} opt_height Image height. - * @param {boolean=} opt_preview True if the image is a preview (not full res). - * @private - */ -ImageView.prototype.replaceContent_ = function( - content, opt_width, opt_height, opt_preview) { - if (this.contentImage_ && this.contentImage_.parentNode === this.container_) { - this.container_.removeChild(this.contentImage_); - } - - this.contentImage_ = content; - this.preview_ = opt_preview || false; - this.container_.appendChild(content); - ImageUtil.setAttribute(this.contentImage_, 'fade', false); - this.invalidateCaches(); - this.viewport_.setImageSize( - opt_width || this.contentImage_.width, - opt_height || this.contentImage_.height); - this.draw(); - - // Use the video-container class (in addition to image-container). This is - // currently just to center the content without needing a transform. - ImageUtil.setClass( - this.container_, 'video-container', content instanceof HTMLVideoElement); - - // If this is not a thumbnail, cache the content and the screen-scale image. - if (this.hasValidImage()) { - // Insert the full resolution canvas into DOM so that it can be printed. - this.contentImage_.classList.add('image'); - this.setTransform_(this.contentImage_, this.viewport_, null, 0); - - // Keep video out of the contentItem media cache. - if (!(content instanceof HTMLVideoElement)) { - assert( - content instanceof HTMLCanvasElement || - content instanceof HTMLImageElement); - this.contentItem_.contentImage = content; - } - this.updateThumbnail_(this.contentImage_); - - this.contentRevision_++; - for (var i = 0; i !== this.contentCallbacks_.length; i++) { - try { - this.contentCallbacks_[i](); - } catch (e) { - console.error(e); - } - } - } -}; - -/** - * Adds a listener for content changes. - * @param {function()} callback Callback. - */ -ImageView.prototype.addContentCallback = function(callback) { - this.contentCallbacks_.push(callback); -}; - -/** - * Updates the cached thumbnail image. - * - * @param {!HTMLCanvasElement|!HTMLImageElement|!HTMLVideoElement} image The - * source image, canvas or video. - * @private - */ -ImageView.prototype.updateThumbnail_ = function(image) { - // Ignore video. TODO(tapted): Support updating from the poster? - if (image instanceof HTMLVideoElement) { - return; - } - - ImageUtil.trace.resetTimer('thumb'); - var pixelCount = 10000; - var downScale = - Math.max(1, Math.sqrt(image.width * image.height / pixelCount)); - - this.thumbnailCanvas_ = /** @type {!HTMLCanvasElement} */ ( - image.ownerDocument.createElement('canvas')); - this.thumbnailCanvas_.width = Math.round(image.width / downScale); - this.thumbnailCanvas_.height = Math.round(image.height / downScale); - ImageRect.drawImage( - /** @type {!CanvasRenderingContext2D} */ ( - this.thumbnailCanvas_.getContext('2d')), - image); - ImageUtil.trace.reportTimer('thumb'); -}; - -/** - * Replaces the displayed image, possibly with slide-in animation. - * - * @param {!(HTMLCanvasElement|HTMLImageElement)} newContentImage - * The image element. - * @param {ImageView.Effect=} opt_effect Transition effect object. - * @param {number=} opt_width Image width. - * @param {number=} opt_height Image height. - * @param {boolean=} opt_preview True if the image is a preview (not full res). - */ -ImageView.prototype.replace = function( - newContentImage, opt_effect, opt_width, opt_height, opt_preview) { - var oldContentImage = this.contentImage_; - var oldViewport = this.viewport_.clone(); - this.replaceContent_(newContentImage, opt_width, opt_height, opt_preview); - - // Don't use transitions on video elements for now. Animating the video - // controls looks silly and positioning the video with display:flex causes - // jank. TODO(tapted): Support this (maybe) after input from UX. - if (!opt_effect || oldContentImage instanceof HTMLVideoElement) { - return; - } - - assert(this.contentImage_); - this.viewport_.resetView(); - - if (oldContentImage) { - this.container_.insertBefore(oldContentImage, this.container_.firstChild); - ImageUtil.setAttribute(newContentImage, 'fade', true); - } - - this.setTransform_( - newContentImage, this.viewport_, opt_effect, 0 /* instant */); - - // We need to call requestAnimationFrame twice here. The first call is for - // commiting the styles of beggining of transition that are assigned above. - // The second call is for assigning and commiting the styles of end of - // transition, which triggers transition animation. - requestAnimationFrame(function() { - requestAnimationFrame(function() { - this.setTransform_( - newContentImage, - this.viewport_, - null, - opt_effect ? opt_effect.getDuration() : undefined); - if (oldContentImage) { - ImageUtil.setAttribute(newContentImage, 'fade', false); - ImageUtil.setAttribute(oldContentImage, 'fade', true); - var reverse = opt_effect.getReverse(); - if (reverse) { - this.setTransform_(oldContentImage, oldViewport, reverse); - setTimeout(function() { - if (oldContentImage.parentNode && - this.contentImage_ !== oldContentImage) { - oldContentImage.parentNode.removeChild(oldContentImage); - } - }.bind(this), reverse.getSafeInterval()); - } else { - if (oldContentImage.parentNode && - this.contentImage_ !== oldContentImage) { - oldContentImage.parentNode.removeChild(oldContentImage); - } - } - } - }.bind(this)); - }.bind(this)); -}; - -/** - * @param {!HTMLCanvasElement|!HTMLImageElement|!HTMLVideoElement} element The - * element to transform. - * @param {!Viewport} viewport Viewport to be used for calculating - * transformation. - * @param {ImageView.Effect=} opt_effect The effect to apply. - * @param {number=} opt_duration Transition duration. - */ -ImageView.prototype.setTransform_ = function( - element, viewport, opt_effect, opt_duration) { - if (!opt_effect) { - opt_effect = new ImageView.Effect.None(); - } - if (typeof opt_duration !== 'number') { - opt_duration = opt_effect.getDuration(); - } - element.style.transitionDuration = opt_duration + 'ms'; - element.style.transitionTimingFunction = opt_effect.getTiming(); - element.style.transform = opt_effect.transform(element, viewport); -}; - -/** - * Creates zoom effect object. - * @param {!ImageRect} screenRect Target rectangle in screen coordinates. - * @return {!ImageView.Effect} Zoom effect object. - */ -ImageView.prototype.createZoomEffect = function(screenRect) { - return new ImageView.Effect.ZoomToScreen( - screenRect, - ImageView.MODE_TRANSITION_DURATION); -}; - -/** - * Visualizes crop or rotate operation. Hide the old image instantly, animate - * the new image to visualize the operation. - * - * @param {!HTMLCanvasElement} canvas New content canvas. - * @param {ImageRect} imageCropRect The crop rectangle in image coordinates. - * Null for rotation operations. - * @param {number} rotate90 Rotation angle in 90 degree increments. - * @return {number} Animation duration. - */ -ImageView.prototype.replaceAndAnimate = function( - canvas, imageCropRect, rotate90) { - assert(this.contentImage_); - - var oldImageBounds = { - width: this.viewport_.getImageBounds().width, - height: this.viewport_.getImageBounds().height - }; - var oldScreenImage = this.contentImage_; - this.replaceContent_(canvas); - var newScreenImage = this.contentImage_; - var effect = rotate90 ? - new ImageView.Effect.Rotate(rotate90 > 0) : - new ImageView.Effect.Zoom( - oldImageBounds.width, oldImageBounds.height, assert(imageCropRect)); - - this.setTransform_(newScreenImage, this.viewport_, effect, 0 /* instant */); - - // Let the layout fire, then animate back to non-transformed state. - setTimeout( - this.setTransform_.bind( - this, newScreenImage, this.viewport_, null, effect.getDuration()), - 0); - - return effect.getSafeInterval(); -}; - -/** - * Visualizes "undo crop". Shrink the current image to the given crop rectangle - * while fading in the new image. - * - * @param {!HTMLCanvasElement} canvas New content canvas. - * @param {!ImageRect} imageCropRect The crop rectangle in image coordinates. - * @return {number} Animation duration. - */ -ImageView.prototype.animateAndReplace = function(canvas, imageCropRect) { - var oldScreenImage = assert(this.contentImage_); - oldScreenImage.style.zIndex = 1000; - this.replaceContent_(canvas); - this.container_.appendChild(oldScreenImage); - var newScreenImage = this.contentImage_; - ImageUtil.setAttribute(newScreenImage, 'fade', true); - var effect = new ImageView.Effect.Zoom( - this.viewport_.getImageBounds().width, - this.viewport_.getImageBounds().height, - imageCropRect); - // Animate to the transformed state. - requestAnimationFrame(function() { - requestAnimationFrame(function() { - this.setTransform_(oldScreenImage, this.viewport_, effect); - ImageUtil.setAttribute(newScreenImage, 'fade', false); - }.bind(this)); - }.bind(this)); - - setTimeout(function() { - if (oldScreenImage.parentNode) { - oldScreenImage.parentNode.removeChild(oldScreenImage); - } - oldScreenImage.style.zIndex = ''; - }, effect.getSafeInterval()); - - return effect.getSafeInterval(); -}; - -/* Transition effects */ - -/** - * Base class for effects. - * - * @param {number} duration Duration in ms. - * @param {string=} opt_timing CSS transition timing function name. - * @constructor - * @struct - */ -ImageView.Effect = function(duration, opt_timing) { - this.duration_ = duration; - this.timing_ = opt_timing || 'linear'; -}; - -/** - * Default duration of an effect. - * @type {number} - * @const - */ -ImageView.Effect.DEFAULT_DURATION = 180; - -/** - * Effect margin. - * @type {number} - * @const - */ -ImageView.Effect.MARGIN = 100; - -/** - * @return {number} Effect duration in ms. - */ -ImageView.Effect.prototype.getDuration = function() { - return this.duration_; -}; - -/** - * @return {number} Delay in ms since the beginning of the animation after which - * it is safe to perform CPU-heavy operations without disrupting the animation. - */ -ImageView.Effect.prototype.getSafeInterval = function() { - return this.getDuration() + ImageView.Effect.MARGIN; -}; - -/** - * Reverses the effect. - * @return {ImageView.Effect} Reversed effect. Null is returned if this - * is not supported in the effect. - */ -ImageView.Effect.prototype.getReverse = function() { - return null; -}; - -/** - * @return {string} CSS transition timing function name. - */ -ImageView.Effect.prototype.getTiming = function() { - return this.timing_; -}; - -/** - * Obtains the CSS transformation string of the effect. - * @param {!HTMLCanvasElement|!HTMLImageElement|HTMLVideoElement} element Canvas - * or image/video element to be applied the transformation. - * @param {!Viewport} viewport Current viewport. - * @return {string} CSS transformation description. - */ -ImageView.Effect.prototype.transform = function(element, viewport) { - throw new Error('Not implemented.'); -}; - -/** - * Default effect. - * - * @constructor - * @extends {ImageView.Effect} - * @struct - */ -ImageView.Effect.None = function() { - ImageView.Effect.call(this, 0, 'easy-out'); -}; - -/** - * Inherits from ImageView.Effect. - */ -ImageView.Effect.None.prototype = { __proto__: ImageView.Effect.prototype }; - -/** - * @override - */ -ImageView.Effect.None.prototype.transform = function(element, viewport) { - return viewport.getTransformation(element.width, element.height); -}; - -/** - * Slide effect. - * - * @param {number} direction -1 for left, 1 for right. - * @param {boolean=} opt_slow True if slow (as in slideshow). - * @constructor - * @extends {ImageView.Effect} - * @struct - */ -ImageView.Effect.Slide = function Slide(direction, opt_slow) { - ImageView.Effect.call(this, - opt_slow ? 800 : ImageView.Effect.DEFAULT_DURATION, 'ease-out'); - this.direction_ = direction; - this.slow_ = opt_slow; - this.shift_ = opt_slow ? 100 : 40; - if (this.direction_ < 0) this.shift_ = -this.shift_; -}; - -ImageView.Effect.Slide.prototype = { __proto__: ImageView.Effect.prototype }; - -/** - * @override - */ -ImageView.Effect.Slide.prototype.getReverse = function() { - return new ImageView.Effect.Slide(-this.direction_, this.slow_); -}; - -/** - * @override - */ -ImageView.Effect.Slide.prototype.transform = function(element, viewport) { - return viewport.getTransformation( - element.width, element.height, this.shift_); -}; - -/** - * Zoom effect. - * - * Animates the original rectangle to the target rectangle. - * - * @param {number} previousImageWidth Width of the full resolution image. - * @param {number} previousImageHeight Height of the full resolution image. - * @param {!ImageRect} imageCropRect Crop rectangle in the full resolution - * image. - * @param {number=} opt_duration Duration of the effect. - * @constructor - * @extends {ImageView.Effect} - * @struct - */ -ImageView.Effect.Zoom = function( - previousImageWidth, previousImageHeight, imageCropRect, opt_duration) { - ImageView.Effect.call(this, - opt_duration || ImageView.Effect.DEFAULT_DURATION, 'ease-out'); - this.previousImageWidth_ = previousImageWidth; - this.previousImageHeight_ = previousImageHeight; - this.imageCropRect_ = imageCropRect; -}; - -ImageView.Effect.Zoom.prototype = { __proto__: ImageView.Effect.prototype }; - -/** - * @override - */ -ImageView.Effect.Zoom.prototype.transform = function(element, viewport) { - return viewport.getCroppingTransformation( - element.width, - element.height, - this.previousImageWidth_, - this.previousImageHeight_, - this.imageCropRect_); -}; - -/** - * Effect to zoom to a screen rectangle. - * - * @param {!ImageRect} screenRect Rectangle in the application window's - * coordinate. - * @param {number=} opt_duration Duration of effect. - * @constructor - * @extends {ImageView.Effect} - * @struct - */ -ImageView.Effect.ZoomToScreen = function(screenRect, opt_duration) { - ImageView.Effect.call(this, opt_duration || - ImageView.Effect.DEFAULT_DURATION); - this.screenRect_ = screenRect; -}; - -ImageView.Effect.ZoomToScreen.prototype = { - __proto__: ImageView.Effect.prototype -}; - -/** - * @override - */ -ImageView.Effect.ZoomToScreen.prototype.transform = function( - element, viewport) { - return viewport.getScreenRectTransformation( - element.width, - element.height, - this.screenRect_); -}; - -/** - * Rotation effect. - * - * @param {boolean} orientation Orientation of rotation. True is for clockwise - * and false is for counterclockwise. - * @constructor - * @extends {ImageView.Effect} - * @struct - */ -ImageView.Effect.Rotate = function(orientation) { - ImageView.Effect.call(this, ImageView.Effect.DEFAULT_DURATION); - this.orientation_ = orientation; -}; - -ImageView.Effect.Rotate.prototype = { __proto__: ImageView.Effect.prototype }; - -/** - * @override - */ -ImageView.Effect.Rotate.prototype.transform = function(element, viewport) { - return viewport.getRotatingTransformation( - element.width, element.height, this.orientation_ ? -1 : 1); -};
diff --git a/ui/file_manager/gallery/js/image_editor/image_view_unittest.js b/ui/file_manager/gallery/js/image_editor/image_view_unittest.js deleted file mode 100644 index 70f8d565..0000000 --- a/ui/file_manager/gallery/js/image_editor/image_view_unittest.js +++ /dev/null
@@ -1,125 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// eslint-disable-next-line no-var -var metrics = { - recordEnum: function() {}, - recordInterval: function() {}, - startInterval: function() {} -}; - - -function testImageView() { - const mockFileSystem = new MockFileSystem('volumeId'); - const mockEntry = MockFileEntry.create(mockFileSystem, '/test.jpg'); - - // Item has full size cache. - const itemWithFullCache = new MockGalleryItem(mockEntry, null, {}); - itemWithFullCache.contentImage = - assertInstanceof(document.createElement('canvas'), HTMLCanvasElement); - assertEquals( - ImageView.LoadTarget.CACHED_MAIN_IMAGE, - ImageView.getLoadTarget(itemWithFullCache, new ImageView.Effect.None())); - - // Item with content thumbnail. - const itemWithContentThumbnail = - new MockGalleryItem(mockEntry, null, {thumbnail: {url: 'url'}}); - assertEquals( - ImageView.LoadTarget.THUMBNAIL, - ImageView.getLoadTarget( - itemWithContentThumbnail, new ImageView.Effect.None())); - - // Item with external thumbnail. - const itemWithExternalThumbnail = - new MockGalleryItem(mockEntry, null, {external: {thumbnailUrl: 'url'}}); - assertEquals( - ImageView.LoadTarget.THUMBNAIL, - ImageView.getLoadTarget( - itemWithExternalThumbnail, new ImageView.Effect.None())); - - // Item with external thumbnail but present locally. - const itemWithExternalThumbnailPresent = new MockGalleryItem( - mockEntry, null, {external: {thumbnailUrl: 'url', present: true}}); - assertEquals( - ImageView.LoadTarget.MAIN_IMAGE, - ImageView.getLoadTarget( - itemWithExternalThumbnailPresent, new ImageView.Effect.None())); - - // Item with external thumbnail shown by slide effect. - const itemWithExternalThumbnailSlide = - new MockGalleryItem(mockEntry, null, {external: {thumbnailUrl: 'url'}}); - assertEquals( - ImageView.LoadTarget.THUMBNAIL, - ImageView.getLoadTarget( - itemWithExternalThumbnailSlide, new ImageView.Effect.Slide(1))); - - // Item with external thumbnail shown by zoom to screen effect. - const itemWithExternalThumbnailZoomToScreen = - new MockGalleryItem(mockEntry, null, {external: {thumbnailUrl: 'url'}}); - assertEquals( - ImageView.LoadTarget.THUMBNAIL, - ImageView.getLoadTarget( - itemWithExternalThumbnailZoomToScreen, - new ImageView.Effect.ZoomToScreen(new ImageRect(0, 0, 100, 100)))); - - // Item with external thumbnail shown by zoom effect. - const itemWithExternalThumbnailZoom = - new MockGalleryItem(mockEntry, null, {external: {thumbnailUrl: 'url'}}); - assertEquals( - ImageView.LoadTarget.MAIN_IMAGE, - ImageView.getLoadTarget( - itemWithExternalThumbnailZoom, - new ImageView.Effect.Zoom(0, 0, new ImageRect(0, 0, 1, 1)))); - - // Item without cache/thumbnail. - const itemWithoutCacheOrThumbnail = new MockGalleryItem(mockEntry, null, {}); - assertEquals( - ImageView.LoadTarget.MAIN_IMAGE, - ImageView.getLoadTarget( - itemWithoutCacheOrThumbnail, new ImageView.Effect.None)); -} - -function testLoadVideo(callback) { - const container = - assertInstanceof(document.createElement('div'), HTMLElement); - // We re-use the image-container for video, it starts with this class. - container.classList.add('image-container'); - - const viewport = new Viewport(window); - - class MockMetadataProvider extends MetadataProvider { - constructor() { - super([]); - } - - /** @override */ - async get() { - return []; - } - } - - const metadataModel = new MetadataModel(new MockMetadataProvider()); - const imageView = new ImageView(container, viewport, metadataModel); - - const downloads = new MockFileSystem('file:///downloads'); - const getGalleryItem = function(path) { - return new MockGalleryItem( - MockFileEntry.create(downloads, path), null, {size: 100}); - }; - const item = getGalleryItem('/test.webm'); - const effect = new ImageView.Effect.None(); - const displayDone = function() { - assertTrue(container.classList.contains('image-container')); - assertTrue(container.classList.contains('video-container')); - const video = container.firstElementChild; - assertTrue(video instanceof HTMLVideoElement); - const source = video.firstElementChild; - assertTrue(source instanceof HTMLSourceElement); - assertTrue(source.src.startsWith( - 'filesystem:file:///downloads/test.webm?nocache=')); - callback(); - }; - const loadDone = function() {}; - imageView.load(item, effect, displayDone, loadDone); -}
diff --git a/ui/file_manager/gallery/js/image_editor/test_util.js b/ui/file_manager/gallery/js/image_editor/test_util.js deleted file mode 100644 index 0494c42..0000000 --- a/ui/file_manager/gallery/js/image_editor/test_util.js +++ /dev/null
@@ -1,28 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/* eslint-disable no-var */ - -// #import {assert, assertInstanceof} from 'chrome://resources/js/assert.m.js' - -/* #ignore */ 'use strict'; - -/** - * Creates a sample canvas. - * @return {!HTMLCanvasElement} - */ -/* #export */ function getSampleCanvas() { - var canvas = - assertInstanceof(document.createElement('canvas'), HTMLCanvasElement); - canvas.width = 1920; - canvas.height = 1080; - - var ctx = canvas.getContext('2d'); - ctx.fillStyle = '#000000'; - for (var i = 0; i < 10; i++) { - ctx.fillRect(i * 30, i * 30, 20, 20); - } - - return canvas; -}
diff --git a/ui/file_manager/gallery/js/image_editor/viewport.js b/ui/file_manager/gallery/js/image_editor/viewport.js deleted file mode 100644 index 24545dd..0000000 --- a/ui/file_manager/gallery/js/image_editor/viewport.js +++ /dev/null
@@ -1,649 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * Formats string by replacing place holder with actual values. - * @param {string} str String includes placeholder '$n'. n starts from 1. - * @param {...*} var_args Values inserted into the place holders. - * @return {string} - */ -function formatString(str, var_args) { - var args = arguments; - return str.replace(/\$[1-9]/g, function(placeHolder) { - return args[placeHolder[1]]; - }); -} - -/** - * Viewport class controls the way the image is displayed (scale, offset etc). - * Screen size is same with window size by default. Change screen size and - * position by changing screenTop and screenBottom. - */ -class Viewport extends cr.EventTarget { - /** - * @param {!Window} targetWindow A window which this viewport is attached to. - */ - constructor(targetWindow) { - super(); - - /** - * Window - * @private {!Window} - */ - this.window_ = targetWindow; - - /** - * Size of the full resolution image. - * @type {!ImageRect} - * @private - */ - this.imageBounds_ = new ImageRect(0, 0, 0, 0); - - /** - * Bounds of the image element on screen without zoom and offset. - * @type {ImageRect} - * @private - */ - this.imageElementBoundsOnScreen_ = null; - - /** - * Bounds of the image with zoom and offset. - * @type {ImageRect} - * @private - */ - this.imageBoundsOnScreen_ = null; - - /** - * Image bounds that is clipped with the screen bounds. - * @type {ImageRect} - * @private - */ - this.imageBoundsOnScreenClipped_ = null; - - /** - * Scale from the full resolution image to the screen displayed image. This - * is not zoom operated by users. - * @type {number} - * @private - */ - this.scale_ = 1; - - /** - * Zoom ratio specified by user operations. - * @type {number} - * @private - */ - this.zoom_ = 1; - - /** - * Offset specified by user operations. - * @type {number} - * @private - */ - this.offsetX_ = 0; - - /** - * Offset specified by user operations. - * @type {number} - * @private - */ - this.offsetY_ = 0; - - /** - * Integer Rotation value. - * The rotation angle is this.rotation_ * 90. - * @type {number} - * @private - */ - this.rotation_ = 0; - - /** - * Generation of the screen size image cache. - * This is incremented every time when the size of image cache is changed. - * @type {number} - * @private - */ - this.generation_ = 0; - - /** - * Top margin of the screen. - * @private {number} - */ - this.screenTop_ = 0; - - /** - * Bottom margin of the screen. - * @private {number} - */ - this.screenBottom_ = 0; - - /** - * Window width. - * @private {number} - */ - this.windowWidth_ = this.window_.innerWidth; - - /** - * Window height. - * @private {number} - */ - this.windowHeight_ = this.window_.innerHeight; - - this.window_.addEventListener('resize', this.onWindowResize_.bind(this)); - - this.update_(); - } - - /** - * Sets image size. - * @param {number} width Image width. - * @param {number} height Image height. - */ - setImageSize(width, height) { - this.imageBounds_ = ImageRect.createFromWidthAndHeight(width, height); - this.update_(); - } - - /** - * Handles window resize event. - */ - onWindowResize_(event) { - this.windowWidth_ = event.target.innerWidth; - this.windowHeight_ = event.target.innerHeight; - this.update_(); - - // Dispatches resize event of viewport. - var resizeEvent = new Event('resize'); - this.dispatchEvent(resizeEvent); - } - - /** - * Sets screen top. - * @param {number} top Top. - */ - setScreenTop(top) { - this.screenTop_ = top; - this.update_(); - } - - /** - * Sets screen bottom. - * @param {number} bottom Bottom. - */ - setScreenBottom(bottom) { - this.screenBottom_ = bottom; - this.update_(); - } - - /** - * Sets zoom value directly. - * @param {number} zoom New zoom value. - */ - setZoom(zoom) { - var zoomMin = Viewport.ZOOM_RATIOS[0]; - var zoomMax = Viewport.ZOOM_RATIOS[Viewport.ZOOM_RATIOS.length - 1]; - var adjustedZoom = Math.max(zoomMin, Math.min(zoom, zoomMax)); - this.zoom_ = adjustedZoom; - this.update_(); - } - - /** - * Returns the value of zoom. - * @return {number} Zoom value. - */ - getZoom() { - return this.zoom_; - } - - /** - * Sets the nearest larger value of ZOOM_RATIOS. - */ - zoomIn() { - var zoom = Viewport.ZOOM_RATIOS[0]; - for (var i = 0; i < Viewport.ZOOM_RATIOS.length; i++) { - zoom = Viewport.ZOOM_RATIOS[i]; - if (zoom > this.zoom_) { - break; - } - } - this.setZoom(zoom); - } - - /** - * Sets the nearest smaller value of ZOOM_RATIOS. - */ - zoomOut() { - var zoom = Viewport.ZOOM_RATIOS[Viewport.ZOOM_RATIOS.length - 1]; - for (var i = Viewport.ZOOM_RATIOS.length - 1; i >= 0; i--) { - zoom = Viewport.ZOOM_RATIOS[i]; - if (zoom < this.zoom_) { - break; - } - } - this.setZoom(zoom); - } - - /** - * Obtains whether the picture is zoomed or not. - * @return {boolean} - */ - isZoomed() { - return this.zoom_ !== 1; - } - - /** - * Sets the rotation value. - * @param {number} rotation New rotation value. - */ - setRotation(rotation) { - this.rotation_ = rotation; - this.update_(); - } - - /** - * Obtains the rotation value. - * @return {number} Current rotation value. - */ - getRotation() { - return this.rotation_; - } - - /** - * Returns image scale so that it matches screen size as long as it does not - * exceed maximum size. - * - * @param {number} width Width of image. - * @param {number} height Height of image. - * @param {number} maxWidth Max width of image. - * @param {number} maxHeight Max height of image. - * @return {number} The ratio of the full resotion image size and the - * calculated displayed image size. - * @private - */ - getFittingScaleForImageSize_(width, height, maxWidth, maxHeight) { - return Math.min( - maxWidth / width, maxHeight / height, - this.getScreenBounds().width / width, - this.getScreenBounds().height / height); - } - - /** - * Returns offset X. - * @return {number} X-offset of the viewport. - */ - getOffsetX() { - return this.offsetX_; - } - - /** - * Returns offset Y. - * @return {number} Y-offset of the viewport. - */ - getOffsetY() { - return this.offsetY_; - } - - /** - * Set the image offset in the viewport. - * @param {number} x X-offset. - * @param {number} y Y-offset. - */ - setOffset(x, y) { - if (this.offsetX_ == x && this.offsetY_ == y) { - return; - } - this.offsetX_ = x; - this.offsetY_ = y; - this.update_(); - } - - /** - * Returns image bounds. - * @return {!ImageRect} The image bounds in image coordinates. - */ - getImageBounds() { - return this.imageBounds_; - } - - /** - * Returns screen bounds. - * @return {!ImageRect} The screen bounds in screen coordinates. - */ - getScreenBounds() { - return new ImageRect( - 0, this.screenTop_, this.windowWidth_, - this.windowHeight_ - this.screenTop_ - this.screenBottom_); - } - - /** - * Returns device bounds. - * @return {!ImageRect} The size of screen cache canvas. - */ - getDeviceBounds() { - return ImageRect.createFromWidthAndHeight( - this.imageElementBoundsOnScreen_.width * window.devicePixelRatio, - this.imageElementBoundsOnScreen_.height * window.devicePixelRatio); - } - - /** - * A counter that is incremented with each viewport state change. - * Clients that cache anything that depends on the viewport state should keep - * track of this counter. - * @return {number} counter. - */ - getCacheGeneration() { - return this.generation_; - } - - /** - * Returns image bounds in screen coordinates. - * @return {!ImageRect} The image bounds in screen coordinates. - */ - getImageBoundsOnScreen() { - assert(this.imageBoundsOnScreen_); - return this.imageBoundsOnScreen_; - } - - /** - * The image bounds on screen, which is clipped with the screen size. - * @return {!ImageRect} - */ - getImageBoundsOnScreenClipped() { - assert(this.imageBoundsOnScreenClipped_); - return this.imageBoundsOnScreenClipped_; - } - - /** - * Returns size in image coordinates. - * @param {number} size Size in screen coordinates. - * @return {number} Size in image coordinates. - */ - screenToImageSize(size) { - return size / this.scale_; - } - - /** - * Returns X in image coordinates. - * @param {number} x X in screen coordinates. - * @return {number} X in image coordinates. - */ - screenToImageX(x) { - return Math.round((x - this.imageBoundsOnScreen_.left) / this.scale_); - } - - /** - * Returns Y in image coordinates. - * @param {number} y Y in screen coordinates. - * @return {number} Y in image coordinates. - */ - screenToImageY(y) { - return Math.round((y - this.imageBoundsOnScreen_.top) / this.scale_); - } - - /** - * Returns a rectangle in image coordinates. - * @param {!ImageRect} rect Rectangle in screen coordinates. - * @return {!ImageRect} Rectangle in image coordinates. - */ - screenToImageRect(rect) { - return new ImageRect( - this.screenToImageX(rect.left), this.screenToImageY(rect.top), - this.screenToImageSize(rect.width), - this.screenToImageSize(rect.height)); - } - - /** - * Returns size in screen coordinates. - * @param {number} size Size in image coordinates. - * @return {number} Size in screen coordinates. - */ - imageToScreenSize(size) { - return size * this.scale_; - } - - /** - * Returns X in screen coordinates. - * @param {number} x X in image coordinates. - * @return {number} X in screen coordinates. - */ - imageToScreenX(x) { - return Math.round(this.imageBoundsOnScreen_.left + x * this.scale_); - } - - /** - * Returns Y in screen coordinates. - * @param {number} y Y in image coordinates. - * @return {number} Y in screen coordinates. - */ - imageToScreenY(y) { - return Math.round(this.imageBoundsOnScreen_.top + y * this.scale_); - } - - /** - * Returns a rectangle in screen coordinates. - * @param {!ImageRect} rect Rectangle in image coordinates. - * @return {!ImageRect} Rectangle in screen coordinates. - */ - imageToScreenRect(rect) { - return new ImageRect( - this.imageToScreenX(rect.left), this.imageToScreenY(rect.top), - Math.round(this.imageToScreenSize(rect.width)), - Math.round(this.imageToScreenSize(rect.height))); - } - - /** - * Returns a rectangle with given geometry. - * @param {number} width Width of the rectangle. - * @param {number} height Height of the rectangle. - * @param {number} offsetX X-offset of center position of the rectangle. - * @param {number} offsetY Y-offset of center position of the rectangle. - * @return {!ImageRect} Rectangle with given geometry. - * @private - */ - getCenteredRect_(width, height, offsetX, offsetY) { - var screenBounds = this.getScreenBounds(); - return new ImageRect( - ~~((screenBounds.width - width) / 2) + offsetX, - ~~((screenBounds.height - height) / 2) + screenBounds.top + offsetY, - width, height); - } - - /** - * Resets zoom and offset. - */ - resetView() { - this.zoom_ = 1; - this.offsetX_ = 0; - this.offsetY_ = 0; - this.rotation_ = 0; - this.update_(); - } - - /** - * Recalculate the viewport parameters. - * @private - */ - update_() { - // Update scale. - this.scale_ = this.getFittingScaleForImageSize_( - this.imageBounds_.width, this.imageBounds_.height, - this.imageBounds_.width, this.imageBounds_.height); - - // Limit offset values. - var zoomedWidht; - var zoomedHeight; - if (this.rotation_ % 2 == 0) { - zoomedWidht = ~~(this.imageBounds_.width * this.scale_ * this.zoom_); - zoomedHeight = ~~(this.imageBounds_.height * this.scale_ * this.zoom_); - } else { - var scale = this.getFittingScaleForImageSize_( - this.imageBounds_.height, this.imageBounds_.width, - this.imageBounds_.height, this.imageBounds_.width); - zoomedWidht = ~~(this.imageBounds_.height * scale * this.zoom_); - zoomedHeight = ~~(this.imageBounds_.width * scale * this.zoom_); - } - var dx = Math.max(zoomedWidht - this.getScreenBounds().width, 0) / 2; - var dy = Math.max(zoomedHeight - this.getScreenBounds().height, 0) / 2; - this.offsetX_ = ImageUtil.clamp(-dx, this.offsetX_, dx); - this.offsetY_ = ImageUtil.clamp(-dy, this.offsetY_, dy); - - // Image bounds on screen. - this.imageBoundsOnScreen_ = this.getCenteredRect_( - zoomedWidht, zoomedHeight, this.offsetX_, this.offsetY_); - - // Image bounds of element (that is not applied zoom and offset) on screen. - var oldBounds = this.imageElementBoundsOnScreen_; - this.imageElementBoundsOnScreen_ = this.getCenteredRect_( - ~~(this.imageBounds_.width * this.scale_), - ~~(this.imageBounds_.height * this.scale_), 0, 0); - if (!oldBounds || - this.imageElementBoundsOnScreen_.width != oldBounds.width || - this.imageElementBoundsOnScreen_.height != oldBounds.height) { - this.generation_++; - } - - // Image bounds on screen clipped with the screen bounds. - var left = Math.max(this.imageBoundsOnScreen_.left, 0); - var top = Math.max(this.imageBoundsOnScreen_.top, this.screenTop_); - var right = - Math.min(this.imageBoundsOnScreen_.right, this.getScreenBounds().width); - var bottom = Math.min( - this.imageBoundsOnScreen_.bottom, - this.getScreenBounds().height + this.screenTop_); - this.imageBoundsOnScreenClipped_ = - new ImageRect(left, top, right - left, bottom - top); - } - - /** - * Clones the viewport. - * @return {!Viewport} New instance. - */ - clone() { - var viewport = new Viewport(this.window_); - viewport.imageBounds_ = ImageRect.createFromBounds(this.imageBounds_); - viewport.scale_ = this.scale_; - viewport.zoom_ = this.zoom_; - viewport.offsetX_ = this.offsetX_; - viewport.offsetY_ = this.offsetY_; - viewport.screenTop_ = this.screenTop_; - viewport.screenBottom_ = this.screenBottom_; - viewport.windowWidth_ = this.windowWidth_; - viewport.windowHeight_ = this.windowHeight_; - viewport.rotation_ = this.rotation_; - viewport.generation_ = this.generation_; - viewport.update_(); - return viewport; - } - - /** - * Obtains CSS transformation string that matches the image dimension with - * |screenRect|. - * @param {number} width Width of image. - * @param {number} height Height of image. - * @param {!ImageRect} screenRect Rectangle in window coordinate system. The - * origin of the coordinate system is located at the left upper of the - * window. - */ - getScreenRectTransformation(width, height, screenRect) { - var dx = screenRect.left + (screenRect.width - width) / 2; - var dy = screenRect.top + (screenRect.height - height) / 2; - - return formatString( - 'translate($1px,$2px) scale($3,$4)', dx, dy, screenRect.width / width, - screenRect.height / height); - } - - /** - * Obtains CSS transformation string that places the cropped image at the - * original position in the whole image. - * @param {number} width Width of cropped image. - * @param {number} height Width of cropped image. - * @param {number} wholeWidthMax Max width value that is used for layouting - * whole image. - * @param {number} wholeHeightMax Max height value that is used for layouting - * whole image. - * @param {!ImageRect} cropRect Crop rectangle in the whole image. The origin - * is left upper of the whole image. - */ - getCroppingTransformation( - width, height, wholeWidthMax, wholeHeightMax, cropRect) { - var fittingScale = this.getFittingScaleForImageSize_( - wholeWidthMax, wholeHeightMax, wholeWidthMax, wholeHeightMax); - var wholeWidth = wholeWidthMax * fittingScale; - var wholeHeight = wholeHeightMax * fittingScale; - var wholeRect = this.getCenteredRect_(wholeWidth, wholeHeight, 0, 0); - return this.getScreenRectTransformation( - width, height, - new ImageRect( - wholeRect.left + cropRect.left * fittingScale, - wholeRect.top + cropRect.top * fittingScale, - cropRect.width * fittingScale, cropRect.height * fittingScale)); - } - - /** - * Obtains CSS transformation for the screen image. - * @param {number} width Width of image. - * @param {number} height Height of image. - * @param {number=} opt_dx Amount of horizontal shift. - * @return {string} Transformation description. - */ - getTransformation(width, height, opt_dx) { - return this.getTransformationInternal_( - width, height, this.rotation_, this.zoom_, - this.offsetX_ + (opt_dx || 0), this.offsetY_); - } - - /** - * Obtains CSS transformation that makes the rotated image fit the original - * image. The new rotated image that the transformation is applied to looks - * the same with original image. - * - * @param {number} width Width of image. - * @param {number} height Height of image. - * @param {number} rotation Number of clockwise 90 degree rotation. The - * rotation angle of the image is rotation * 90. - * @return {string} Transformation description. - */ - getRotatingTransformation(width, height, rotation) { - return this.getTransformationInternal_(width, height, rotation, 1, 0, 0); - } - - /** - * Obtains CSS transformation that placed the image in the application window. - * @param {number} width Width of image. - * @param {number} height Height of image. - * @param {number} rotation Number of clockwise 90 degree rotation. The - * rotation angle of the image is rotation * 90. - * @param {number} zoom Zoom rate. - * @param {number} offsetX Horizontal offset. - * @param {number} offsetY Vertical offset. - * @private - */ - getTransformationInternal_(width, height, rotation, zoom, offsetX, offsetY) { - var rotatedWidth = rotation % 2 ? height : width; - var rotatedHeight = rotation % 2 ? width : height; - var rotatedMaxWidth = - rotation % 2 ? this.imageBounds_.height : this.imageBounds_.width; - var rotatedMaxHeight = - rotation % 2 ? this.imageBounds_.width : this.imageBounds_.height; - - // Scale. - var fittingScale = this.getFittingScaleForImageSize_( - rotatedWidth, rotatedHeight, rotatedMaxWidth, rotatedMaxHeight); - - // Offset for centering. - var rect = this.getCenteredRect_(width, height, offsetX, offsetY); - return formatString( - 'translate($1px,$2px) scale($3) rotate($4deg)', rect.left, rect.top, - fittingScale * zoom, rotation * 90); - } -} - -/** - * Zoom ratios. - * - * @type {Array<number>} - * @const - */ -Viewport.ZOOM_RATIOS = [1, 1.5, 2, 3];
diff --git a/ui/file_manager/gallery/js/metadata_worker.js b/ui/file_manager/gallery/js/metadata_worker.js deleted file mode 100644 index dec52ec..0000000 --- a/ui/file_manager/gallery/js/metadata_worker.js +++ /dev/null
@@ -1,8 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Load the worker script of the Files app. -importScripts( - 'chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/' + - 'foreground/js/metadata/metadata_dispatcher.js');
diff --git a/ui/file_manager/gallery/js/mock_gallery_item.js b/ui/file_manager/gallery/js/mock_gallery_item.js deleted file mode 100644 index 3046810..0000000 --- a/ui/file_manager/gallery/js/mock_gallery_item.js +++ /dev/null
@@ -1,42 +0,0 @@ -// Copyright 2018 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. - -/** - * Helper to construct testing GalleryItem objects. - * - * @param {!FileEntry} entry - * @param {EntryLocation} locationInfo - * @param {Object} metadataItem - * @param {ThumbnailMetadataItem=} opt_thumbnailMetadataItem - * @param {boolean=} opt_original Whether the entry is original or edited. - * @constructor - * @extends GalleryItem - */ -function MockGalleryItem( - entry, locationInfo, metadataItem, opt_thumbnailMetadataItem, - opt_original) { - let entryLocation = locationInfo || /** @type {!EntryLocation} */ ({}); - GalleryItem.call( - this, entry, entryLocation, /** @type {MetadataItem} */ (metadataItem), - opt_thumbnailMetadataItem || null, opt_original || false); -} - -/** - * Helper to construct a MockGalleryItem with a given |path| and dummy metadata. - * - * @param {!string} path - * @param {!FileSystem} fileSystem - * @param {boolean} isReadOnly - * @return MockGalleryItem - */ -MockGalleryItem.makeWithPath = function(path, fileSystem, isReadOnly) { - return new MockGalleryItem( - MockFileEntry.create(fileSystem, path), - /** @type {EntryLocation} */ ({isReadOnly: isReadOnly}), {size: 100}, - null, true /* original */); -}; - -MockGalleryItem.prototype = { - __proto__: GalleryItem.prototype -};
diff --git a/ui/file_manager/gallery/js/ribbon.js b/ui/file_manager/gallery/js/ribbon.js deleted file mode 100644 index 7f780d1..0000000 --- a/ui/file_manager/gallery/js/ribbon.js +++ /dev/null
@@ -1,525 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * Scrollable thumbnail ribbon at the bottom of the Gallery in the Slide mode. - * - * @param {!Document} document Document. - * @param {!Window} targetWindow A window which this ribbon is attached to. - * @param {!cr.ui.ArrayDataModel} dataModel Data model. - * @param {!cr.ui.ListSelectionModel} selectionModel Selection model. - * @param {!ThumbnailModel} thumbnailModel - * @extends {HTMLDivElement} - * @constructor - * @struct - */ -function Ribbon( - document, targetWindow, dataModel, selectionModel, thumbnailModel) { - if (this instanceof Ribbon) { - return Ribbon.call(/** @type {Ribbon} */ (document.createElement('div')), - document, targetWindow, dataModel, selectionModel, thumbnailModel); - } - - this.__proto__ = Ribbon.prototype; - this.className = 'ribbon'; - this.setAttribute('role', 'listbox'); - this.tabIndex = 0; - - /** - * @private {!Window} - * @const - */ - this.targetWindow_ = targetWindow; - - /** - * @private {!cr.ui.ArrayDataModel} - * @const - */ - this.dataModel_ = dataModel; - - /** - * @private {!cr.ui.ListSelectionModel} - * @const - */ - this.selectionModel_ = selectionModel; - - /** - * @private {!ThumbnailModel} - * @const - */ - this.thumbnailModel_ = thumbnailModel; - - /** - * @type {!Object} - * @private - */ - this.renderCache_ = {}; - - /** - * @type {number} - * @private - */ - this.firstVisibleIndex_ = 0; - - /** - * @type {number} - * @private - */ - this.lastVisibleIndex_ = -1; - - /** - * @type {?function(!Event)} - * @private - */ - this.onContentBound_ = null; - - /** - * @type {?function(!Event)} - * @private - */ - this.onSpliceBound_ = null; - - /** - * @type {?function(!Event)} - * @private - */ - this.onSelectionBound_ = null; - - /** - * @type {?number} - * @private - */ - this.removeTimeout_ = null; - - /** - * @private {number} - */ - this.thumbnailElementId_ = 0; - - this.targetWindow_.addEventListener( - 'resize', this.onWindowResize_.bind(this)); - - return this; -} - -/** - * Inherit from HTMLDivElement. - */ -Ribbon.prototype.__proto__ = HTMLDivElement.prototype; - -/** - * Margin of thumbnails. - * @const {number} - */ -Ribbon.MARGIN = 2; // px - -/** - * Width of thumbnail on the ribbon. - * @const {number} - */ -Ribbon.THUMBNAIL_WIDTH = 71; // px - -/** - * Height of thumbnail on the ribbon. - * @const {number} - */ -Ribbon.THUMBNAIL_HEIGHT = 40; // px - -/** - * Returns number of items in the viewport. - * @return {number} Number of items in the viewport. - */ -Ribbon.prototype.getItemCount_ = function() { - return Math.ceil(this.targetWindow_.innerWidth / - (Ribbon.THUMBNAIL_WIDTH + Ribbon.MARGIN * 2)); -}; - -/** - * Handles resize event of target window. - */ -Ribbon.prototype.onWindowResize_ = function() { - this.redraw(); -}; - -/** - * Force redraw the ribbon. - */ -Ribbon.prototype.redraw = function() { - this.onSelection_(); -}; - -/** - * Clear all cached data to force full redraw on the next selection change. - */ -Ribbon.prototype.reset = function() { - this.renderCache_ = {}; - this.firstVisibleIndex_ = 0; - this.lastVisibleIndex_ = -1; // Zero thumbnails -}; - -/** - * Enable the ribbon. - */ -Ribbon.prototype.enable = function() { - this.onContentBound_ = this.onContentChange_.bind(this); - this.dataModel_.addEventListener('content', this.onContentBound_); - - this.onSpliceBound_ = this.onSplice_.bind(this); - this.dataModel_.addEventListener('splice', this.onSpliceBound_); - - this.onSelectionBound_ = this.onSelection_.bind(this); - this.selectionModel_.addEventListener('change', this.onSelectionBound_); - - this.reset(); - this.redraw(); -}; - -/** - * Disable ribbon. - */ -Ribbon.prototype.disable = function() { - this.dataModel_.removeEventListener('content', this.onContentBound_); - this.dataModel_.removeEventListener('splice', this.onSpliceBound_); - this.selectionModel_.removeEventListener('change', this.onSelectionBound_); - - this.removeVanishing_(); - this.textContent = ''; -}; - -/** - * Data model splice handler. - * @param {!Event} event Event. - * @private - */ -Ribbon.prototype.onSplice_ = function(event) { - if (event.removed.length === 0 && event.added.length === 0) { - return; - } - - if (event.removed.length > 0 && event.added.length > 0) { - console.error('Replacing is not implemented.'); - return; - } - - if (event.added.length > 0) { - for (let i = 0; i < event.added.length; i++) { - const index = this.dataModel_.indexOf(event.added[i]); - if (index === -1) { - continue; - } - const element = this.renderThumbnail_(index); - const nextItem = this.dataModel_.item(index + 1); - const nextElement = - nextItem && this.renderCache_[nextItem.getEntry().toURL()]; - this.insertBefore(element, /** @type {!HTMLElement} */ (nextElement)); - } - return; - } - - const persistentNodes = - this.querySelectorAll('.ribbon-image:not([vanishing])'); - if (this.lastVisibleIndex_ < this.dataModel_.length) { // Not at the end. - const lastNode = persistentNodes[persistentNodes.length - 1]; - if (lastNode.nextSibling) { - // Pull back a vanishing node from the right. - lastNode.nextSibling.removeAttribute('vanishing'); - } else { - // Push a new item at the right end. - this.appendChild(this.renderThumbnail_(this.lastVisibleIndex_)); - } - } else { - // No items to the right, move the window to the left. - this.lastVisibleIndex_--; - if (this.firstVisibleIndex_) { - this.firstVisibleIndex_--; - const firstNode = persistentNodes[0]; - if (firstNode.previousSibling) { - // Pull back a vanishing node from the left. - firstNode.previousSibling.removeAttribute('vanishing'); - } else { - // Push a new item at the left end. - if (this.firstVisibleIndex_ < this.dataModel_.length) { - const newThumbnail = this.renderThumbnail_(this.firstVisibleIndex_); - newThumbnail.style.marginLeft = -(this.clientHeight - 2) + 'px'; - this.insertBefore(newThumbnail, this.firstChild); - setTimeout(function() { - newThumbnail.style.marginLeft = '0'; - }, 0); - } - } - } - } - - let removed = false; - for (let i = 0; i < event.removed.length; i++) { - const removedDom = this.renderCache_[event.removed[i].getEntry().toURL()]; - if (removedDom) { - removedDom.removeAttribute('selected'); - removedDom.setAttribute('vanishing', 'smooth'); - removed = true; - } - } - - if (removed) { - this.scheduleRemove_(); - } - - this.onSelection_(); -}; - -/** - * Selection change handler. - * @private - */ -Ribbon.prototype.onSelection_ = function() { - const indexes = this.selectionModel_.selectedIndexes; - if (indexes.length === 0) { - return; // Ignore temporary empty selection. - } - const selectedIndex = indexes[0]; - - const length = this.dataModel_.length; - const fullItems = Math.min(this.getItemCount_(), length); - const right = Math.floor((fullItems - 1) / 2); - - let lastIndex = selectedIndex + right; - lastIndex = Math.max(lastIndex, fullItems - 1); - lastIndex = Math.min(lastIndex, length - 1); - - const firstIndex = lastIndex - fullItems + 1; - - if (this.firstVisibleIndex_ !== firstIndex || - this.lastVisibleIndex_ !== lastIndex) { - - if (this.lastVisibleIndex_ === -1) { - this.firstVisibleIndex_ = firstIndex; - this.lastVisibleIndex_ = lastIndex; - } - - this.removeVanishing_(); - - this.textContent = ''; - const startIndex = Math.min(firstIndex, this.firstVisibleIndex_); - // All the items except the first one treated equally. - for (let index = startIndex + 1; - index <= Math.max(lastIndex, this.lastVisibleIndex_); ++index) { - // Only add items that are in either old or the new viewport. - if (this.lastVisibleIndex_ < index && index < firstIndex || - lastIndex < index && index < this.firstVisibleIndex_) { - continue; - } - - const box = this.renderThumbnail_(index); - box.style.marginLeft = Ribbon.MARGIN + 'px'; - this.appendChild(box); - - if (index < firstIndex || index > lastIndex) { - // If the node is not in the new viewport we only need it while - // the animation is playing out. - box.setAttribute('vanishing', 'slide'); - } - } - - const slideCount = this.childNodes.length + 1 - fullItems; - const margin = Ribbon.THUMBNAIL_WIDTH * slideCount; - const startBox = this.renderThumbnail_(startIndex); - - if (startIndex === firstIndex) { - // Sliding to the right. - startBox.style.marginLeft = -margin + 'px'; - - if (this.firstChild) { - this.insertBefore(startBox, this.firstChild); - } else { - this.appendChild(startBox); - } - - setTimeout(function() { - startBox.style.marginLeft = Ribbon.MARGIN + 'px'; - }, 0); - } else { - // Sliding to the left. Start item will become invisible and should be - // removed afterwards. - startBox.setAttribute('vanishing', 'slide'); - startBox.style.marginLeft = Ribbon.MARGIN + 'px'; - - if (this.firstChild) { - this.insertBefore(startBox, this.firstChild); - } else { - this.appendChild(startBox); - } - - setTimeout(function() { - startBox.style.marginLeft = -margin + 'px'; - }, 0); - } - - this.firstVisibleIndex_ = firstIndex; - this.lastVisibleIndex_ = lastIndex; - - this.scheduleRemove_(); - } - - ImageUtil.setClass( - this, - 'fade-left', - firstIndex > 0 && selectedIndex !== firstIndex); - ImageUtil.setClass( - this, - 'fade-right', - lastIndex < length - 1 && selectedIndex !== lastIndex); - - const oldSelected = this.querySelector('[selected]'); - if (oldSelected) { - oldSelected.removeAttribute('selected'); - } - - const newSelected = - this.renderCache_[this.dataModel_.item(selectedIndex).getEntry().toURL()]; - if (newSelected) { - newSelected.setAttribute('selected', true); - this.setAttribute('aria-activedescendant', newSelected.id); - this.focus(); - } -}; - -/** - * Schedule the removal of thumbnails marked as vanishing. - * @private - */ -Ribbon.prototype.scheduleRemove_ = function() { - if (this.removeTimeout_) { - clearTimeout(this.removeTimeout_); - } - - this.removeTimeout_ = setTimeout(function() { - this.removeTimeout_ = null; - this.removeVanishing_(); - }.bind(this), 200); -}; - -/** - * Remove all thumbnails marked as vanishing. - * @private - */ -Ribbon.prototype.removeVanishing_ = function() { - if (this.removeTimeout_) { - clearTimeout(this.removeTimeout_); - this.removeTimeout_ = 0; - } - const vanishingNodes = this.querySelectorAll('[vanishing]'); - for (let i = 0; i != vanishingNodes.length; i++) { - vanishingNodes[i].removeAttribute('vanishing'); - this.removeChild(vanishingNodes[i]); - } -}; - -/** - * Create a DOM element for a thumbnail. - * - * @param {number} index Item index. - * @return {!Element} Newly created element. - * @private - */ -Ribbon.prototype.renderThumbnail_ = function(index) { - const item = assertInstanceof(this.dataModel_.item(index), GalleryItem); - const url = item.getEntry().toURL(); - - const cached = this.renderCache_[url]; - if (cached) { - const img = cached.querySelector('img'); - if (img) { - img.classList.add('cached'); - } - return cached; - } - - const thumbnail = - assertInstanceof(this.ownerDocument.createElement('div'), HTMLDivElement); - thumbnail.id = `thumbnail-${this.thumbnailElementId_++}`; - thumbnail.className = 'ribbon-image'; - thumbnail.setAttribute('role', 'listitem'); - thumbnail.addEventListener('click', function() { - const index = this.dataModel_.indexOf(item); - this.selectionModel_.unselectAll(); - this.selectionModel_.setIndexSelected(index, true); - }.bind(this)); - - util.createChild(thumbnail, 'indicator loading'); - util.createChild(thumbnail, 'image-wrapper'); - util.createChild(thumbnail, 'selection-frame'); - - this.setThumbnailImage_(thumbnail, item); - - // TODO: Implement LRU eviction. - // Never evict the thumbnails that are currently in the DOM because we rely - // on this cache to find them by URL. - this.renderCache_[url] = thumbnail; - return thumbnail; -}; - -/** - * Set the thumbnail image. - * - * @param {!Element} thumbnail Thumbnail element. - * @param {!GalleryItem} item Gallery item. - * @private - */ -Ribbon.prototype.setThumbnailImage_ = function(thumbnail, item) { - thumbnail.setAttribute('title', item.getFileName()); - - if (!item.getThumbnailMetadataItem()) { - return; - } - - const hideIndicator = function() { - thumbnail.querySelector('.indicator').classList.toggle('loading', false); - }; - - this.thumbnailModel_.get([item.getEntry()]).then(function(metadataList) { - const loader = new ThumbnailLoader( - item.getEntry(), ThumbnailLoader.LoaderType.IMAGE, metadataList[0]); - const box = - assertInstanceof(thumbnail.querySelector('.image-wrapper'), Element); - // Pass 0.35 as auto fill threshold. This value allows to fill 4:3 and 3:2 - // photos in 16:9 box (ratio factors for them are ~1.34 and ~1.18 - // respectively). - loader.load( - box, ThumbnailLoader.FillMode.AUTO, hideIndicator /* onSuccess */, - 0.35 /* autoFillThreshold */, Ribbon.THUMBNAIL_WIDTH /* boxWidth */, - Ribbon.THUMBNAIL_HEIGHT /* boxHeight */); - }); -}; - -/** - * Content change handler. - * - * @param {!Event} event Event. - * @private - */ -Ribbon.prototype.onContentChange_ = function(event) { - const url = event.item.getEntry().toURL(); - if (event.oldEntry.toURL() !== url) { - this.remapCache_(event.oldEntry.toURL(), url); - } - - const thumbnail = this.renderCache_[url]; - if (thumbnail && event.item) { - this.setThumbnailImage_(thumbnail, event.item); - } -}; - -/** - * Update the thumbnail element cache. - * - * @param {string} oldUrl Old url. - * @param {string} newUrl New url. - * @private - */ -Ribbon.prototype.remapCache_ = function(oldUrl, newUrl) { - if (oldUrl != newUrl && (oldUrl in this.renderCache_)) { - this.renderCache_[newUrl] = this.renderCache_[oldUrl]; - delete this.renderCache_[oldUrl]; - } -};
diff --git a/ui/file_manager/gallery/js/ribbon_unittest.js b/ui/file_manager/gallery/js/ribbon_unittest.js deleted file mode 100644 index 708f93c..0000000 --- a/ui/file_manager/gallery/js/ribbon_unittest.js +++ /dev/null
@@ -1,16 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -function testEmptySpliceEvent() { - var dataModel = new cr.ui.ArrayDataModel([]); - var selectionModel = new cr.ui.ListSelectionModel(); - var thumbnailModel = /** @type{!ThumbnailModel} */ ({}); - var ribbon = - new Ribbon(document, window, dataModel, selectionModel, thumbnailModel); - ribbon.enable(); - var event = new Event('splice'); - event.added = []; - event.removed = []; - dataModel.dispatchEvent(event); -}
diff --git a/ui/file_manager/gallery/js/slide_mode.js b/ui/file_manager/gallery/js/slide_mode.js deleted file mode 100644 index f5ec2c9..0000000 --- a/ui/file_manager/gallery/js/slide_mode.js +++ /dev/null
@@ -1,2236 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/* eslint-disable no-var */ - -/** - * Slide mode displays a single image and has a set of controls to navigate - * between the images and to edit an image. - */ -class SlideMode extends cr.EventTarget { - /** - * @param {!HTMLElement} container Main container element. - * @param {!HTMLElement} content Content container element. - * @param {!HTMLElement} topToolbar Top toolbar element. - * @param {!HTMLElement} bottomToolbar Toolbar element. - * @param {!ImageEditorPrompt} prompt Prompt. - * @param {!ErrorBanner} errorBanner Error banner. - * @param {!GalleryDataModel} dataModel Data model. - * @param {!cr.ui.ListSelectionModel} selectionModel Selection model. - * @param {!MetadataModel} metadataModel - * @param {!ThumbnailModel} thumbnailModel - * @param {!{appWindow: chrome.app.window.AppWindow, readonlyDirName: string, - * displayStringFunction: function(), loadTimeData: Object}} context - * Context. - * @param {!VolumeManager} volumeManager Volume manager. - * @param {function(function())} toggleMode Function to toggle the Gallery - * mode. - * @param {function(string):string} displayStringFunction String formatting - * function. - * @param {!DimmableUIController} dimmableUIController Dimmable UI controller. - */ - constructor( - container, content, topToolbar, bottomToolbar, prompt, errorBanner, - dataModel, selectionModel, metadataModel, thumbnailModel, context, - volumeManager, toggleMode, displayStringFunction, dimmableUIController) { - super(); - - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.container_ = container; - - /** - * @type {!Document} - * @private - * @const - */ - this.document_ = assert(container.ownerDocument); - - /** - * @type {!HTMLElement} - * @const - */ - this.content = content; - - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.topToolbar_ = topToolbar; - - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.bottomToolbar_ = bottomToolbar; - - /** - * @type {!ImageEditorPrompt} - * @private - * @const - */ - this.prompt_ = prompt; - - /** - * @type {!ErrorBanner} - * @private - * @const - */ - this.errorBanner_ = errorBanner; - - /** - * @type {!GalleryDataModel} - * @private - * @const - */ - this.dataModel_ = dataModel; - - /** - * @type {!cr.ui.ListSelectionModel} - * @private - * @const - */ - this.selectionModel_ = selectionModel; - - /** - * @type {{appWindow: chrome.app.window.AppWindow, readonlyDirName: string, - * displayStringFunction: function(), loadTimeData: Object}} - * @private - * @const - */ - this.context_ = context; - - /** - * @type {!VolumeManager} - * @private - * @const - */ - this.volumeManager_ = volumeManager; - - /** - * @type {function(function())} - * @private - * @const - */ - this.toggleMode_ = toggleMode; - - /** - * @type {function(string):string} - * @private - * @const - */ - this.displayStringFunction_ = displayStringFunction; - - /** - * @private {!DimmableUIController} - * @const - */ - this.dimmableUIController_ = dimmableUIController; - - /** - * @type {function(this:SlideMode)} - * @private - * @const - */ - this.onSelectionBound_ = this.onSelection_.bind(this); - - /** - * @type {function(this:SlideMode,!Event)} - * @private - * @const - */ - this.onSpliceBound_ = this.onSplice_.bind(this); - - /** - * Unique numeric key, incremented per each load attempt used to discard - * old attempts. This can happen especially when changing selection fast or - * Internet connection is slow. - * - * @type {number} - * @private - */ - this.currentUniqueKey_ = 0; - - /** - * @type {number} - * @private - */ - this.sequenceDirection_ = 0; - - /** - * @type {number} - * @private - */ - this.sequenceLength_ = 0; - - /** - * @type {Array<number>} - * @private - */ - this.savedSelection_ = null; - - /** - * @type {GalleryItem} - * @private - */ - this.displayedItem_ = null; - - /** - * @type {?number} - * @private - */ - this.slideHint_ = null; - - /** - * @type {boolean} - * @private - */ - this.active_ = false; - - /** - * @private {GallerySubMode} - */ - this.subMode_ = GallerySubMode.BROWSE; - - /** - * @type {boolean} - * @private - */ - this.leaveAfterSlideshow_ = false; - - /** - * @type {boolean} - * @private - */ - this.fullscreenBeforeSlideshow_ = false; - - /** - * @type {?number} - * @private - */ - this.slideShowTimeout_ = null; - - /** - * @private {string|undefined} - */ - this.loadingItemUrl_ = undefined; - - /** - * @private {number} - */ - this.progressBarTimer_ = 0; - - /** - * @type {?number} - * @private - */ - this.spinnerTimer_ = null; - - window.addEventListener('resize', this.onResize_.bind(this)); - - // ---------------------------------------------------------------- - // Initializes the UI. - - /** - * Container for displayed image. - * @type {!HTMLElement} - * @private - * @const - */ - this.imageContainer_ = util.createChild( - queryRequiredElement('.content', this.document_), 'image-container'); - - this.document_.addEventListener('click', this.onDocumentClick_.bind(this)); - - /** - * Overwrite options and info bubble. - * @type {!HTMLElement} - * @private - * @const - */ - this.options_ = queryRequiredElement('.options', this.bottomToolbar_); - - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.savedLabel_ = queryRequiredElement('.saved', this.options_); - - /** - * @private {!Element} - * @const - */ - this.overwriteOriginalCheckbox_ = /** @type {!Element} */ - (queryRequiredElement('.overwrite-original', this.options_)); - this.overwriteOriginalCheckbox_.addEventListener( - 'change', this.onOverwriteOriginalCheckboxChanged_.bind(this)); - - /** - * @private {!HTMLElement} - * @const - */ - this.bubble_ = queryRequiredElement('.bubble', this.bottomToolbar_); - - var bubbleContent = queryRequiredElement('.content', this.bubble_); - // GALLERY_OVERWRITE_BUBBLE contains <br> tag inside message. - bubbleContent.innerHTML = strf('GALLERY_OVERWRITE_BUBBLE'); - - var bubbleClose = queryRequiredElement('.close-x', this.bubble_); - bubbleClose.addEventListener('click', this.onCloseBubble_.bind(this)); - - /** - * Ribbon and related controls. - * @type {!HTMLElement} - * @private - * @const - */ - this.arrowBox_ = util.createChild(this.container_, 'arrow-box'); - - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.arrowLeft_ = - util.createChild(this.arrowBox_, 'arrow left tool dimmable'); - this.arrowLeft_.addEventListener( - 'click', this.advanceManually.bind(this, -1)); - util.createChild(this.arrowLeft_); - - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.arrowRight_ = - util.createChild(this.arrowBox_, 'arrow right tool dimmable'); - this.arrowRight_.addEventListener( - 'click', this.advanceManually.bind(this, 1)); - util.createChild(this.arrowRight_); - - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.ribbonSpacer_ = - queryRequiredElement('.ribbon-spacer', this.bottomToolbar_); - - /** - * @type {!Ribbon} - * @private - * @const - */ - this.ribbon_ = new Ribbon( - this.document_, window, this.dataModel_, this.selectionModel_, - thumbnailModel); - this.ribbonSpacer_.appendChild(this.ribbon_); - - util.createChild(this.container_, 'spinner'); - - /** - * @type {!HTMLElement} - * @const - */ - var slideShowButton = - queryRequiredElement('button.slideshow', this.topToolbar_); - slideShowButton.addEventListener( - 'click', - this.startSlideshow.bind(this, SlideMode.SLIDESHOW_INTERVAL_FIRST)); - - /** - * @private {!PaperProgressElement} - * @const - */ - this.progressBar_ = /** @type {!PaperProgressElement} */ - (queryRequiredElement('#progress-bar', document)); - chrome.fileManagerPrivate.onFileTransfersUpdated.addListener( - this.updateProgressBar_.bind(this)); - - /** - * @type {!HTMLElement} - * @const - */ - var slideShowToolbar = - util.createChild(this.container_, 'tool slideshow-toolbar'); - - // Play & Pause Button. - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.slideshowPlay_ = util.createChild(slideShowToolbar, 'slideshow-play'); - this.slideshowPlay_.addEventListener( - 'click', this.toggleSlideshowPause_.bind(this)); - util.createChild(slideShowToolbar, 'slideshow-end') - .addEventListener('click', this.stopSlideshow_.bind(this)); - - // Editor. - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.editButton_ = queryRequiredElement('button.edit', this.topToolbar_); - GalleryUtil.decorateMouseFocusHandling(this.editButton_); - this.editButton_.addEventListener('click', this.toggleEditor.bind(this)); - - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.printButton_ = queryRequiredElement('button.print', this.topToolbar_); - GalleryUtil.decorateMouseFocusHandling(this.printButton_); - this.printButton_.addEventListener('click', this.print_.bind(this)); - - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.editBarSpacer_ = - queryRequiredElement('.edit-bar-spacer', this.bottomToolbar_); - - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.editBarMain_ = util.createChild(this.editBarSpacer_, 'edit-main'); - - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.editBarMode_ = - /** @type {!HTMLElement} */ (document.createElement('div')); - this.editBarMode_.className = 'edit-modal'; - // Edit modal bar should be inserted before the bottom toolbar to make the - // tab order and visual position consistent. - this.container_.insertBefore( - this.editBarMode_, document.querySelector('#bottom-toolbar')); - - /** - * @type {!HTMLElement} - * @private - * @const - */ - this.editBarModeWrapper_ = - util.createChild(this.editBarMode_, 'edit-modal-wrapper dimmable'); - this.editBarModeWrapper_.hidden = true; - - /** - * Objects supporting image display and editing. - * @type {!Viewport} - * @private - * @const - */ - this.viewport_ = new Viewport(window); - this.viewport_.addEventListener( - 'resize', this.onViewportResize_.bind(this)); - - /** - * @type {!ImageView} - * @private - * @const - */ - this.imageView_ = - new ImageView(this.imageContainer_, this.viewport_, metadataModel); - - /** - * @type {!ImageEditor} - * @private - * @const - */ - this.editor_ = new ImageEditor( - this.viewport_, this.imageView_, this.prompt_, { - root: this.container_, - image: this.imageContainer_, - toolbar: this.editBarMain_, - mode: this.editBarModeWrapper_ - }, - SlideMode.EDITOR_MODES, this.displayStringFunction_); - this.editor_.addEventListener( - 'exit-clicked', this.onExitClicked_.bind(this)); - - /** - * @type {!TouchHandler} - * @private - * @const - */ - this.touchHandlers_ = new TouchHandler(this.imageContainer_, this); - } - - /** - * Returns editor warning message if it should be shown. - * @param {!GalleryItem} item - * @param {string} readonlyDirName Name of read only volume. Pass empty string - * if volume is writable. - * @param {!DirectoryEntry} fallbackSaveDirectory - * @return {!Promise<?string>} Warning message. null if no warning message - * should be shown. - */ - static getEditorWarningMessage(item, readonlyDirName, fallbackSaveDirectory) { - var isReadOnlyVolume = !!readonlyDirName; - var isWritableFormat = item.isWritableFormat(); - - if (isReadOnlyVolume && !isWritableFormat) { - return item.getCopyName(fallbackSaveDirectory).then(function(copyName) { - return strf( - 'GALLERY_READONLY_AND_NON_WRITABLE_FORMAT_WARNING', readonlyDirName, - copyName); - }); - } else if (isReadOnlyVolume) { - return Promise.resolve(/** @type {?string} */ - (strf( - 'GALLERY_READONLY_WARNING', readonlyDirName))); - } else if (!isWritableFormat) { - var entry = item.getEntry(); - return new Promise(entry.getParent.bind(entry)) - .then(function(parentDir) { - return item.getCopyName(parentDir); - }) - .then(function(copyName) { - return strf('GALLERY_NON_WRITABLE_FORMAT_WARNING', copyName); - }); - } else { - return Promise.resolve(/** @type {?string} */ (null)); - } - } - - /** - * Handles exit-clicked event. - * @private - */ - onExitClicked_() { - if (this.isEditing()) { - this.toggleEditor(); - } - } - - /** - * @return {string} Mode name. - */ - getName() { - return 'slide'; - } - - /** - * @return {string} Mode title. - */ - getTitle() { - return 'GALLERY_SLIDE'; - } - - /** - * @return {!Viewport} Viewport. - */ - getViewport() { - return this.viewport_; - } - - /** - * Load items, display the selected item. - * @param {ImageRect} zoomFromRect Rectangle for zoom effect. - * @param {function()} displayCallback Called when the image is displayed. - * @param {function()} loadCallback Called when the image is displayed. - */ - enter(zoomFromRect, displayCallback, loadCallback) { - this.sequenceDirection_ = 0; - this.sequenceLength_ = 0; - - // The latest |leave| call might have left the image animating. Remove it. - this.unloadImage_(); - this.errorBanner_.clear(); - - new Promise(function(fulfill) { - // If the items are empty, just show the error message. - if (this.getItemCount_() === 0) { - this.displayedItem_ = null; - this.errorBanner_.show('GALLERY_NO_IMAGES'); - fulfill(); - return; - } - - // Remember the selection if it is empty or multiple. It will be restored - // in |leave| if the user did not changing the selection manually. - var currentSelection = this.selectionModel_.selectedIndexes; - if (currentSelection.length === 1) { - this.savedSelection_ = null; - } else { - this.savedSelection_ = currentSelection; - } - - // Ensure valid single selection. - // Note that the SlideMode object is not listening to selection change - // yet. - this.select(Math.max(0, this.getSelectedIndex())); - - // Show the selected item ASAP, then complete the initialization - // (loading the ribbon thumbnails can take some time). - var selectedItem = this.getSelectedItem(); - this.displayedItem_ = selectedItem; - - // Load the image of the item. - this.loadItem_( - assert(selectedItem), - zoomFromRect ? this.imageView_.createZoomEffect(zoomFromRect) : - new ImageView.Effect.None(), - displayCallback, function(loadType, delay) { - fulfill(delay); - }); - }.bind(this)) - .then(function(delay) { - // Turn the mode active. - this.active_ = true; - ImageUtil.setAttribute( - this.arrowBox_, 'active', this.getItemCount_() > 1); - this.ribbon_.enable(); - - // Register handlers. - this.selectionModel_.addEventListener( - 'change', this.onSelectionBound_); - this.dataModel_.addEventListener('splice', this.onSpliceBound_); - this.touchHandlers_.enabled = true; - - // Wait 1000ms after the animation is done, then prefetch the next - // image. - this.requestPrefetch(1, delay + 1000); - - // Call load callback. - if (loadCallback) { - loadCallback(); - } - }.bind(this)) - .catch(function(error) { - console.error(error.stack, error); - }); - } - - /** - * Leave the mode. - * @param {ImageRect} zoomToRect Rectangle for zoom effect. - * @param {function()} callback Called when the image is committed and - * the zoom-out animation has started. - */ - leave(zoomToRect, callback) { - var commitDone = function() { - this.stopEditing_(); - this.stopSlideshow_(); - ImageUtil.setAttribute(this.arrowBox_, 'active', false); - this.selectionModel_.removeEventListener( - 'change', this.onSelectionBound_); - this.dataModel_.removeEventListener('splice', this.onSpliceBound_); - this.ribbon_.disable(); - this.active_ = false; - if (this.savedSelection_) { - this.selectionModel_.selectedIndexes = this.savedSelection_; - } - this.unloadImage_(zoomToRect); - callback(); - }.bind(this); - - this.viewport_.resetView(); - if (this.getItemCount_() === 0) { - this.errorBanner_.clear(); - commitDone(); - } else { - this.commitItem_(commitDone); - } - - // Disable the slide-mode only buttons when leaving. - this.editButton_.disabled = true; - this.printButton_.disabled = true; - - // Disable touch operation. - this.touchHandlers_.enabled = false; - } - - /** - * Activate content, in a way that makes sense for the content. Currently this - * causes video to start playing. - */ - activateContent() { - var content = this.imageContainer_.firstElementChild; - if (content instanceof HTMLVideoElement) { - content.autoplay = true; - // Disable controls for half a second. This avoids the video starting with - // a big "pause" button in the center (even if the mouse is not moving). - // If the user moves their mouse after the timeout, the controls will - // appear. - content.controls = false; - setTimeout(function() { - content.controls = true; - }, 500); - } - } - - /** - * Execute an action when the editor is not busy. - * - * @param {function()} action Function to execute. - */ - executeWhenReady(action) { - this.editor_.executeWhenReady(action); - } - - /** - * @return {boolean} True if the mode has active tools (that should not fade). - */ - hasActiveTool() { - return this.isEditing(); - } - - /** - * @return {number} Item count. - * @private - */ - getItemCount_() { - return this.dataModel_.length; - } - - /** - * @param {number} index Index. - * @return {GalleryItem} Item. - */ - getItem(index) { - var item = - /** @type {(GalleryItem|undefined)} */ (this.dataModel_.item(index)); - return item === undefined ? null : item; - } - - /** - * @return {number} Selected index. - */ - getSelectedIndex() { - return this.selectionModel_.selectedIndex; - } - - /** - * @return {ImageRect} Screen rectangle of the selected image. - */ - getSelectedImageRect() { - if (this.getSelectedIndex() < 0) { - return null; - } else { - return this.viewport_.getImageBoundsOnScreen(); - } - } - - /** - * @return {GalleryItem} Selected item. - */ - getSelectedItem() { - return this.getItem(this.getSelectedIndex()); - } - - /** - * Toggles the full screen mode. - * @private - */ - toggleFullScreen_() { - util.toggleFullScreen( - this.context_.appWindow, !util.isFullScreen(this.context_.appWindow)); - } - - /** - * Selection change handler. - * - * Commits the current image and displays the newly selected image. - * @private - */ - onSelection_() { - if (this.selectionModel_.selectedIndexes.length === 0) { - return; // Ignore temporary empty selection. - } - - // Forget the saved selection if the user changed the selection manually. - if (!this.isSlideshowOn_()) { - this.savedSelection_ = null; - } - - if (this.getSelectedItem() === this.displayedItem_) { - return; // Do not reselect. - } - - this.commitItem_(this.loadSelectedItem_.bind(this)); - } - - /** - * Change the selection. - * - * @param {number} index New selected index. - * @param {number=} opt_slideHint Slide animation direction (-1|1). - */ - select(index, opt_slideHint) { - this.slideHint_ = opt_slideHint || null; - this.selectionModel_.selectedIndex = index; - this.selectionModel_.leadIndex = index; - } - - /** - * Load the selected item. - * - * @private - */ - loadSelectedItem_() { - var slideHint = this.slideHint_; - this.slideHint_ = null; - - if (this.getSelectedItem() === this.displayedItem_) { - return; - } // Do not reselect. - - var index = this.getSelectedIndex(); - if (index < 0) { - return; - } - - var displayedIndex = this.dataModel_.indexOf(this.displayedItem_); - var step = slideHint || (displayedIndex > 0 ? index - displayedIndex : 1); - - if (Math.abs(step) != 1) { - // Long leap, the sequence is broken, we have no good prefetch candidate. - this.sequenceDirection_ = 0; - this.sequenceLength_ = 0; - } else if (this.sequenceDirection_ === step) { - // Keeping going in sequence. - this.sequenceLength_++; - } else { - // Reversed the direction. Reset the counter. - this.sequenceDirection_ = step; - this.sequenceLength_ = 1; - } - - this.displayedItem_ = this.getSelectedItem(); - var selectedItem = assertInstanceof(this.getSelectedItem(), GalleryItem); - - function shouldPrefetch(loadType, step, sequenceLength) { - // Never prefetch when selecting out of sequence. - if (Math.abs(step) != 1) { - return false; - } - - // Always prefetch if the previous load was from cache. - if (loadType === ImageView.LoadType.CACHED_FULL) { - return true; - } - - // Prefetch if we have been going in the same direction for long enough. - return sequenceLength >= 3; - } - - this.currentUniqueKey_++; - var selectedUniqueKey = this.currentUniqueKey_; - - // Discard, since another load has been invoked after this one. - if (selectedUniqueKey != this.currentUniqueKey_) { - return; - } - - this.loadItem_( - selectedItem, - new ImageView.Effect.Slide(step, this.isSlideshowPlaying_()), - function() {} /* no displayCallback */, - function(loadType, delay) { - // Discard, since another load has been invoked after this one. - if (selectedUniqueKey != this.currentUniqueKey_) { - return; - } - if (shouldPrefetch(loadType, step, this.sequenceLength_)) { - this.requestPrefetch(step, delay); - } - if (this.isSlideshowPlaying_()) { - this.scheduleNextSlide_(); - } - }.bind(this)); - } - - /** - * Unload the current image. - * - * @param {ImageRect=} opt_zoomToRect Rectangle for zoom effect. - * @private - */ - unloadImage_(opt_zoomToRect) { - this.imageView_.unload(opt_zoomToRect); - } - - /** - * Data model 'splice' event handler. - * @param {!Event} event Event. - * @this {SlideMode} - * @private - */ - onSplice_(event) { - ImageUtil.setAttribute(this.arrowBox_, 'active', this.getItemCount_() > 1); - - // Splice invalidates saved indices, drop the saved selection. - this.savedSelection_ = null; - - if (event.removed.length != 1) { - return; - } - - // Delay the selection to let the ribbon splice handler work first. - setTimeout(function() { - if (this.dataModel_.length === 0) { - // No items left. Unload the image, disable edit and print button, and - // show the banner. - this.commitItem_(function() { - this.unloadImage_(); - this.printButton_.disabled = true; - this.editButton_.disabled = true; - this.errorBanner_.show('GALLERY_NO_IMAGES'); - if (this.isEditing()) { - this.toggleEditor(); - } - }.bind(this)); - return; - } - - var displayedItemNotRemvoed = event.removed.every(function(item) { - return item !== this.displayedItem_; - }.bind(this)); - if (!displayedItemNotRemvoed) { - // There is the next item, select it. Otherwise, select the last item. - var nextIndex = Math.min(event.index, this.dataModel_.length - 1); - // To force to dispatch a selection change event, unselect all before. - this.selectionModel_.unselectAll(); - this.select(nextIndex); - // If the removed image was edit, leave the editing mode. - if (this.isEditing()) { - this.toggleEditor(); - } - } - }.bind(this), 0); - } - - /** - * @param {number} direction -1 for left, 1 for right. - * @return {number} Next index in the given direction, with wrapping. - * @private - */ - getNextSelectedIndex_(direction) { - function advance(index, limit) { - index += (direction > 0 ? 1 : -1); - if (index < 0) { - return limit - 1; - } - if (index === limit) { - return 0; - } - return index; - } - - // If the saved selection is multiple the Slideshow should cycle through - // the saved selection. - if (this.isSlideshowOn_() && this.savedSelection_ && - this.savedSelection_.length > 1) { - var pos = advance( - this.savedSelection_.indexOf(this.getSelectedIndex()), - this.savedSelection_.length); - return this.savedSelection_[pos]; - } else { - return advance(this.getSelectedIndex(), this.getItemCount_()); - } - } - - /** - * Advance the selection based on the pressed key ID. - * @param {string} keyID Key of the KeyboardEvent. - */ - advanceWithKeyboard(keyID) { - if (this.getItemCount_() === 0) { - return; - } - - var prev = - (keyID === 'ArrowUp' || keyID === 'ArrowLeft' || - keyID === 'MediaTrackPrevious'); - this.advanceManually(prev ? -1 : 1); - } - - /** - * Advance the selection as a result of a user action (as opposed to an - * automatic change in the slideshow mode). - * @param {number} direction -1 for left, 1 for right. - */ - advanceManually(direction) { - if (this.isSlideshowPlaying_()) { - this.pauseSlideshow_(); - } - cr.dispatchSimpleEvent(this, 'useraction'); - this.selectNext(direction); - } - - /** - * Select the next item. - * @param {number} direction -1 for left, 1 for right. - */ - selectNext(direction) { - this.select(this.getNextSelectedIndex_(direction), direction); - } - - /** - * Select the first item. - */ - selectFirst() { - this.select(0); - } - - /** - * Select the last item. - */ - selectLast() { - this.select(this.getItemCount_() - 1); - } - - // Loading/unloading - - /** - * Load and display an item. - * - * @param {!GalleryItem} item Item. - * @param {!ImageView.Effect} effect Transition effect object. - * @param {function()} displayCallback Called when the image is displayed - * (which can happen before the image load due to caching). - * @param {function(number, number)} loadCallback Called when the image is - * fully loaded. - * @private - */ - loadItem_(item, effect, displayCallback, loadCallback) { - this.dimmableUIController_.setLoading(true); - this.showProgressBar_(item); - - var loadDone = this.itemLoaded_.bind(this, item, loadCallback); - - var displayDone = function() { - cr.dispatchSimpleEvent(this, 'image-displayed'); - displayCallback(); - }.bind(this); - - if (item.isEditable()) { - this.editor_.openSession( - item, effect, this.saveCurrentImage_.bind(this, item), displayDone, - loadDone); - } else { - this.imageView_.load(item, effect, displayDone, loadDone); - } - } - - /** - * A callback function when the editor opens a editing session for an image. - * @param {!GalleryItem} item Gallery item. - * @param {function(number, number)} loadCallback Called when the image is - * fully loaded. - * @param {number} loadType Load type. - * @param {number} delay Delay. - * @param {*=} opt_error Error. - * @private - */ - itemLoaded_(item, loadCallback, loadType, delay, opt_error) { - var entry = item.getEntry(); - - this.hideProgressBar_(); - this.dimmableUIController_.setLoading(false); - - if (loadType === ImageView.LoadType.ERROR) { - // if we have a specific error, then display it - if (opt_error) { - this.errorBanner_.show(/** @type {string} */ (opt_error)); - } else { - // otherwise try to infer general error - this.errorBanner_.show('GALLERY_IMAGE_ERROR'); - } - } else if (loadType === ImageView.LoadType.OFFLINE) { - this.errorBanner_.show('GALLERY_IMAGE_OFFLINE'); - } - - metrics.recordUserAction(ImageUtil.getMetricName('View')); - - var toMillions = function(number) { - return Math.round(number / (1000 * 1000)); - }; - - var metadata = item.getMetadataItem(); - if (metadata) { - metrics.recordSmallCount( - ImageUtil.getMetricName('Size.MB'), toMillions(metadata.size)); - } - - var image = this.imageView_.getMedia(); - metrics.recordSmallCount( - ImageUtil.getMetricName('Size.MPix'), - toMillions(image.width * image.height)); - - var extIndex = entry.name.lastIndexOf('.'); - var ext = extIndex < 0 ? '' : entry.name.substr(extIndex + 1).toLowerCase(); - if (ext === 'jpeg') { - ext = 'jpg'; - } - metrics.recordEnum( - ImageUtil.getMetricName('FileType'), ext, ImageUtil.FILE_TYPES); - - // Enable or disable buttons for editing and printing. - const canEditAndPrint = !opt_error && item.isEditable(); - this.editButton_.disabled = !canEditAndPrint; - this.printButton_.disabled = !canEditAndPrint; - - // Saved label is hidden by default. - this.savedLabel_.hidden = true; - - // Disable overwrite original checkbox until settings is loaded. - this.overwriteOriginalCheckbox_.disabled = true; - this.overwriteOriginalCheckbox_.checked = false; - - var keys = {}; - keys[SlideMode.OVERWRITE_ORIGINAL_KEY] = true; - chrome.storage.local.get(keys, function(values) { - // Users can overwrite original file only if loaded image is original - // and writable. - if (item.isOriginal() && item.isWritableFile(this.volumeManager_)) { - this.overwriteOriginalCheckbox_.disabled = false; - this.overwriteOriginalCheckbox_.checked = - values[SlideMode.OVERWRITE_ORIGINAL_KEY]; - } - }.bind(this)); - - loadCallback(loadType, delay); - } - - /** - * Commit changes to the current item and reset all messages/indicators. - * - * @param {function()} callback Callback. - * @private - */ - commitItem_(callback) { - this.showSpinner_(false); - this.errorBanner_.clear(); - this.editor_.getPrompt().hide(); - this.editor_.closeSession(callback); - } - - /** - * Request a prefetch for the next image. - * - * @param {number} direction -1 or 1. - * @param {number} delay Delay in ms. Used to prevent the CPU-heavy image - * loading from disrupting the animation that might be still in progress. - */ - requestPrefetch(direction, delay) { - if (this.getItemCount_() <= 1) { - return; - } - - var index = this.getNextSelectedIndex_(direction); - this.imageView_.prefetch(assert(this.getItem(index)), delay); - } - - // Event handlers. - - /** - * Click handler for the entire document. - * @param {!Event} event Mouse click event. - * @private - */ - onDocumentClick_(event) { - // Events created in fakeMouseClick in test util don't pass this test. - if (!window.IN_TEST) { - event = assertInstanceof(event, MouseEvent); - } - - var targetElement = assertInstanceof(event.target, HTMLElement); - // Close the bubble if clicked outside of it and if it is visible. - if (!this.bubble_.contains(targetElement) && - !this.editButton_.contains(targetElement) && - !this.arrowLeft_.contains(targetElement) && - !this.arrowRight_.contains(targetElement) && !this.bubble_.hidden) { - this.bubble_.hidden = true; - } - } - - /** - * Keydown handler. - * - * @param {!Event} event Event. - * @return {boolean} True if handled. - */ - onKeyDown(event) { - var keyID = util.getKeyModifiers(event) + event.key; - - if (this.isSlideshowOn_()) { - switch (keyID) { - case 'Escape': - case 'MediaStop': - this.stopSlideshow_(event); - break; - - case ' ': // Space pauses/resumes the slideshow. - case 'MediaPlayPause': - this.toggleSlideshowPause_(); - break; - - case 'ArrowUp': - case 'ArrowDown': - case 'ArrowLeft': - case 'ArrowRight': - case 'MediaTrackNex': - case 'MediaTrackPrevious': - this.advanceWithKeyboard(keyID); - break; - } - return true; // Consume all keystrokes in the slideshow mode. - } - - // Handles shortcut keys common for both modes (editing and not-editing). - switch (keyID) { - case 'Ctrl-p': // Ctrl+'p' prints the current image. - if (!this.printButton_.disabled) { - this.print_(); - } - return true; - - case 'e': // 'e' toggles the editor. - if (!this.editButton_.disabled) { - this.toggleEditor(event); - } - return true; - } - - // Handles shortcurt keys for editing mode. - if (this.isEditing()) { - if (this.editor_.onKeyDown(event)) { - return true; - } - - if (keyID === 'Escape') { // Escape - this.toggleEditor(event); - return true; - } - - return false; - } - - // Handles shortcut keys for not-editing mode. - switch (keyID) { - case 'Escape': - if (this.viewport_.isZoomed()) { - this.viewport_.resetView(); - this.touchHandlers_.stopOperation(); - this.imageView_.applyViewportChange(); - return true; - } - break; - - case 'Home': - this.selectFirst(); - return true; - - case 'End': - this.selectLast(); - return true; - - case 'ArrowUp': - case 'ArrowDown': - case 'ArrowLeft': - case 'ArrowRight': - if (this.viewport_.isZoomed()) { - var delta = SlideMode.KEY_OFFSET_MAP[keyID]; - this.viewport_.setOffset( - ~~(this.viewport_.getOffsetX() + - delta[0] * this.viewport_.getZoom()), - ~~(this.viewport_.getOffsetY() + - delta[1] * this.viewport_.getZoom())); - this.touchHandlers_.stopOperation(); - this.imageView_.applyViewportChange(); - } else { - this.advanceWithKeyboard(keyID); - } - return true; - - case 'MediaTrackNext': - case 'MediaTrackPrevious': - this.advanceWithKeyboard(keyID); - return true; - - case 'Ctrl-=': // Ctrl+'=' zoom in. - this.viewport_.zoomIn(); - this.touchHandlers_.stopOperation(); - this.imageView_.applyViewportChange(); - return true; - - case 'Ctrl--': // Ctrl+'-' zoom out. - this.viewport_.zoomOut(); - this.touchHandlers_.stopOperation(); - this.imageView_.applyViewportChange(); - return true; - - case 'Ctrl-0': // Ctrl+'0' zoom reset. - this.viewport_.setZoom(1.0); - this.touchHandlers_.stopOperation(); - this.imageView_.applyViewportChange(); - return true; - } - - return false; - } - - /** - * Resize handler. - * @private - */ - onResize_() { - this.touchHandlers_.stopOperation(); - } - - /** - * Handles resize event of viewport. - * @private - */ - onViewportResize_() { - // This method must be called after the resize of viewport. - this.editor_.getBuffer().draw(); - } - - /** - * Update thumbnails. - */ - updateThumbnails() { - this.ribbon_.reset(); - if (this.active_) { - this.ribbon_.redraw(); - } - } - - // Saving - - /** - * Save the current image to a file. - * - * @param {!GalleryItem} item Item to save the image. - * @param {function()} callback Callback. - * @private - */ - saveCurrentImage_(item, callback) { - this.showSpinner_(true); - - var savedPromise = this.dataModel_.saveItem( - this.volumeManager_, item, - ImageUtil.ensureCanvas(this.imageView_.getEditableImage()), - this.overwriteOriginalCheckbox_.checked); - - savedPromise - .then(function() { - this.showSpinner_(false); - this.flashSavedLabel_(); - - // Record UMA for the first edit. - if (this.imageView_.getContentRevision() === 1) { - metrics.recordUserAction(ImageUtil.getMetricName('Edit')); - } - - // Users can change overwrite original setting only if there is no - // undo stack and item is original and writable. - var ableToChangeOverwriteOriginalSetting = !this.editor_.canUndo() && - item.isOriginal() && item.isWritableFile(this.volumeManager_); - this.overwriteOriginalCheckbox_.disabled = - !ableToChangeOverwriteOriginalSetting; - - callback(); - }.bind(this)) - .catch(function(error) { - console.error(error.stack || error); - - this.showSpinner_(false); - this.errorBanner_.show('GALLERY_SAVE_FAILED'); - - callback(); - }.bind(this)); - } - - /** - * Flash 'Saved' label briefly to indicate that the image has been saved. - * @private - */ - flashSavedLabel_() { - this.savedLabel_.hidden = false; - var setLabelHighlighted = - ImageUtil.setAttribute.bind(null, this.savedLabel_, 'highlighted'); - setTimeout(setLabelHighlighted.bind(null, true), 0); - setTimeout(setLabelHighlighted.bind(null, false), 300); - } - - /** - * Handles change event of overwrite original checkbox. - * @private - */ - onOverwriteOriginalCheckboxChanged_() { - var items = {}; - items[SlideMode.OVERWRITE_ORIGINAL_KEY] = - this.overwriteOriginalCheckbox_.checked; - chrome.storage.local.set(items); - } - - /** - * Overwrite info bubble close handler. - * @private - */ - onCloseBubble_() { - this.bubble_.hidden = true; - this.setOverwriteBubbleCount_(SlideMode.OVERWRITE_BUBBLE_MAX_TIMES); - } - - /** - * @return {boolean} True if the slideshow is on. - * @private - */ - isSlideshowOn_() { - return this.container_.hasAttribute('slideshow'); - } - - /** - * Starts the slideshow. - * @param {number=} opt_interval First interval in ms. - * @param {Event=} opt_event Event. - */ - startSlideshow(opt_interval, opt_event) { - // Reset zoom. - this.viewport_.resetView(); - this.imageView_.applyViewportChange(); - - // Disable touch operation. - this.touchHandlers_.enabled = false; - - // Set the attribute early to prevent the toolbar from flashing when - // the slideshow is being started from the mosaic view. - this.container_.setAttribute('slideshow', 'playing'); - - // Hide the slideshow Play / Pause Button in the toolbar if - // there is less than two items and show it if there is more than 1 image. - this.slideshowPlay_.hidden = (this.getItemCount_() === 1); - - if (this.active_) { - this.stopEditing_(); - } else { - // We are in the Mosaic mode. Toggle the mode but remember to return. - this.leaveAfterSlideshow_ = true; - - // Wait until the zoom animation from the mosaic mode is done. - var startSlideshowAfterTransition = function() { - setTimeout(function() { - this.startSlideshow.call( - this, SlideMode.SLIDESHOW_INTERVAL, opt_event); - }.bind(this), ImageView.MODE_TRANSITION_DURATION); - }.bind(this); - this.toggleMode_(startSlideshowAfterTransition); - return; - } - - if (opt_event) { // Caused by user action, notify the Gallery. - cr.dispatchSimpleEvent(this, 'useraction'); - } - - this.fullscreenBeforeSlideshow_ = - util.isFullScreen(this.context_.appWindow); - if (!this.fullscreenBeforeSlideshow_) { - this.toggleFullScreen_(); - opt_interval = (opt_interval || SlideMode.SLIDESHOW_INTERVAL) + - SlideMode.FULLSCREEN_TOGGLE_DELAY; - } - - // These are workarounds. Mouseout event is not dispatched when window - // becomes fullscreen and cursor gets out of the element - // TODO(yawano): Find better implementation. - this.dimmableUIController_.setCursorOutOfTools(); - - this.resumeSlideshow_(opt_interval); - - this.setSubMode_(GallerySubMode.SLIDESHOW); - } - - /** - * Stops the slideshow. - * @param {Event=} opt_event Event. - * @private - */ - stopSlideshow_(opt_event) { - if (!this.isSlideshowOn_()) { - return; - } - - if (opt_event) { // Caused by user action, notify the Gallery. - cr.dispatchSimpleEvent(this, 'useraction'); - } - - this.pauseSlideshow_(); - this.container_.removeAttribute('slideshow'); - - // Do not restore fullscreen if we exited fullscreen while in slideshow. - var fullscreen = util.isFullScreen(this.context_.appWindow); - var toggleModeDelay = 0; - if (!this.fullscreenBeforeSlideshow_ && fullscreen) { - this.toggleFullScreen_(); - toggleModeDelay = SlideMode.FULLSCREEN_TOGGLE_DELAY; - } - if (this.leaveAfterSlideshow_) { - this.leaveAfterSlideshow_ = false; - setTimeout(this.toggleMode_.bind(this), toggleModeDelay); - } - - // Re-enable touch operation. - this.touchHandlers_.enabled = true; - - this.setSubMode_(GallerySubMode.BROWSE); - } - - /** - * @return {boolean} True if the slideshow is playing (not paused). - * @private - */ - isSlideshowPlaying_() { - return this.container_.getAttribute('slideshow') === 'playing'; - } - - /** - * Pauses/resumes the slideshow. - * @private - */ - toggleSlideshowPause_() { - cr.dispatchSimpleEvent(this, 'useraction'); // Show the tools. - if (this.isSlideshowPlaying_()) { - this.pauseSlideshow_(); - } else { - this.resumeSlideshow_(SlideMode.SLIDESHOW_INTERVAL_FIRST); - } - } - - /** - * @param {number=} opt_interval Slideshow interval in ms. - * @private - */ - scheduleNextSlide_(opt_interval) { - console.assert(this.isSlideshowPlaying_(), 'Inconsistent slideshow state'); - - if (this.slideShowTimeout_) { - clearTimeout(this.slideShowTimeout_); - } - - this.slideShowTimeout_ = setTimeout(function() { - this.slideShowTimeout_ = null; - this.selectNext(1); - }.bind(this), opt_interval || SlideMode.SLIDESHOW_INTERVAL); - } - - /** - * Resumes the slideshow. - * @param {number=} opt_interval Slideshow interval in ms. - * @private - */ - resumeSlideshow_(opt_interval) { - this.container_.setAttribute('slideshow', 'playing'); - this.scheduleNextSlide_(opt_interval); - } - - /** - * Pauses the slideshow. - * @private - */ - pauseSlideshow_() { - this.container_.setAttribute('slideshow', 'paused'); - if (this.slideShowTimeout_) { - clearTimeout(this.slideShowTimeout_); - this.slideShowTimeout_ = null; - } - } - - /** - * @return {boolean} True if the editor is active. - */ - isEditing() { - return this.container_.hasAttribute('editing'); - } - - /** - * Stops editing. - * @private - */ - stopEditing_() { - if (this.isEditing()) { - this.toggleEditor(); - } - } - - /** - * Sets current sub mode. - * @param {GallerySubMode} subMode - * @private - */ - setSubMode_(subMode) { - if (this.subMode_ === subMode) { - return; - } - - this.subMode_ = subMode; - - var event = new Event('sub-mode-change'); - event.subMode = this.subMode_; - this.dispatchEvent(event); - } - - /** - * Returns current sub mode. - * @return {GallerySubMode} - */ - getSubMode() { - return this.subMode_; - } - - /** - * Activate/deactivate editor. - * @param {Event=} opt_event Event. - */ - toggleEditor(opt_event) { - if (opt_event) { // Caused by user action, notify the Gallery. - cr.dispatchSimpleEvent(this, 'useraction'); - } - - if (!this.active_) { - this.toggleMode_(this.toggleEditor.bind(this)); - return; - } - - this.stopSlideshow_(); - - // Disable entering edit mode for videos. - let startEditing = false; - let item = this.getItem(this.getSelectedIndex()); - if (item != null) { - startEditing = !this.isEditing() && item.isEditable(); - } - - ImageUtil.setAttribute(this.container_, 'editing', startEditing); - - if (this.isEditing()) { // isEditing has just been flipped to a new value. - // The item should not be null. - item = assert(item); - - // Reset zoom. - this.viewport_.resetView(); - - // Scale the screen so that it doesn't overlap the toolbars. - this.viewport_.setScreenTop(ImageEditorToolbar.HEIGHT); - this.viewport_.setScreenBottom(ImageEditorToolbar.HEIGHT); - - this.imageView_.applyViewportChange(); - - this.touchHandlers_.enabled = false; - - // Show editor warning message. - SlideMode - .getEditorWarningMessage( - item, this.context_.readonlyDirName, - assert(this.dataModel_.fallbackSaveDirectory)) - .then(function(warningMessage) { - if (!warningMessage) { - return; - } - - }.bind(this)); - - // Show overwrite original bubble if it hasn't been shown for max times. - this.getOverwriteBubbleCount_().then(function(count) { - if (count >= SlideMode.OVERWRITE_BUBBLE_MAX_TIMES) { - return; - } - - this.setOverwriteBubbleCount_(count + 1); - this.bubble_.hidden = false; - }.bind(this)); - - this.setSubMode_(GallerySubMode.EDIT); - this.editor_.onStartEditing(); - } else { - this.editor_.getPrompt().hide(); - this.editor_.leaveMode(false /* not to switch mode */); - - this.viewport_.setScreenTop(0); - this.viewport_.setScreenBottom(0); - this.imageView_.applyViewportChange(); - - this.bubble_.hidden = true; - - this.touchHandlers_.enabled = true; - - this.setSubMode_(GallerySubMode.BROWSE); - } - } - - /** - * Gets count of overwrite bubble. - * @return {!Promise<number>} - * @private - */ - getOverwriteBubbleCount_() { - return new Promise(function(resolve, reject) { - var requests = {}; - requests[SlideMode.OVERWRITE_BUBBLE_KEY] = 0; - - chrome.storage.local.get(requests, function(results) { - if (chrome.runtime.lastError) { - reject(chrome.runtime.lastError); - return; - } - - resolve(results[SlideMode.OVERWRITE_BUBBLE_KEY]); - }); - }); - } - - /** - * Sets count of overwrite bubble. - * @param {number} value - * @private - */ - setOverwriteBubbleCount_(value) { - var requests = {}; - requests[SlideMode.OVERWRITE_BUBBLE_KEY] = value; - chrome.storage.local.set(requests); - } - - /** - * Prints the current item. - * @private - */ - print_() { - this.stopEditing_(); - cr.dispatchSimpleEvent(this, 'useraction'); - window.print(); - } - - /** - * Shows progress bar. - * @param {!GalleryItem} item - * @private - */ - showProgressBar_(item) { - this.loadingItemUrl_ = item.getEntry().toURL(); - - if (this.progressBarTimer_ !== 0) { - clearTimeout(this.progressBarTimer_); - this.progressBarTimer_ = 0; - } - - this.progressBar_.setAttribute('indeterminate', true); - - this.progressBarTimer_ = setTimeout(function() { - this.progressBar_.hidden = false; - }.bind(this), 1000); - } - - /** - * Hides progress bar. - * @private - */ - hideProgressBar_() { - if (this.progressBarTimer_ !== 0) { - clearTimeout(this.progressBarTimer_); - this.progressBarTimer_ = 0; - } - - this.loadingItemUrl_ = undefined; - - this.progressBar_.hidden = true; - } - - /** - * Updates progress bar. - * @param {!chrome.fileManagerPrivate.FileTransferStatus} status - * @private - */ - updateProgressBar_(status) { - if (status.fileUrl !== this.loadingItemUrl_ || status.numTotalJobs !== 1) { - // If user starts to download another image (or file), we cannot show - // determinate progress bar anymore since total and processed are for all - // current downloads. - this.progressBar_.setAttribute('indeterminate', true); - return; - } - - // Progress begins from 5%. - var progress = 5 + (95 * status.processed / status.total); - - this.progressBar_.removeAttribute('indeterminate'); - this.progressBar_.value = progress; - } - - /** - * Shows/hides the busy spinner. - * - * @param {boolean} on True if show, false if hide. - * @private - */ - showSpinner_(on) { - if (this.spinnerTimer_) { - clearTimeout(this.spinnerTimer_); - this.spinnerTimer_ = null; - } - - if (on) { - this.spinnerTimer_ = setTimeout(function() { - this.spinnerTimer_ = null; - ImageUtil.setAttribute(this.container_, 'spinner', true); - }.bind(this), 1000); - } else { - ImageUtil.setAttribute(this.container_, 'spinner', false); - } - } - - /** - * Apply the change of viewport. - */ - applyViewportChange() { - this.imageView_.applyViewportChange(); - } -} - -/** - * List of available editor modes. - * @type {!Array<ImageEditorMode>} - * @const - */ -SlideMode.EDITOR_MODES = [ - new ImageEditorMode.InstantAutofix(), new ImageEditorMode.Crop(), - new ImageEditorMode.Resize(), new ImageEditorMode.Exposure(), - new ImageEditorMode.OneClick( - 'rotate_left', 'GALLERY_ROTATE_LEFT', new Command.Rotate(-1)), - new ImageEditorMode.OneClick( - 'rotate_right', 'GALLERY_ROTATE_RIGHT', new Command.Rotate(1)) -]; - -/** - * Map of the key identifier and offset delta. - * @enum {!Array<number>}) - * @const - */ -SlideMode.KEY_OFFSET_MAP = { - 'Up': [0, 20], - 'Down': [0, -20], - 'Left': [20, 0], - 'Right': [-20, 0] -}; - -/** - * Local storage key for the number of times that - * the overwrite info bubble has been displayed. - * @const {string} - */ -SlideMode.OVERWRITE_BUBBLE_KEY = 'gallery-overwrite-bubble'; - -/** - * Local storage key for overwrite original checkbox value. - * @const {string} - */ -SlideMode.OVERWRITE_ORIGINAL_KEY = 'gallery-overwrite-original'; - -/** - * Max number that the overwrite info bubble is shown. - * @const {number} - */ -SlideMode.OVERWRITE_BUBBLE_MAX_TIMES = 5; - -// Slideshow - -/** - * Slideshow interval in ms. - */ -SlideMode.SLIDESHOW_INTERVAL = 5000; - -/** - * First slideshow interval in ms. It should be shorter so that the user - * is not guessing whether the button worked. - */ -SlideMode.SLIDESHOW_INTERVAL_FIRST = 1000; - -/** - * Empirically determined duration of the fullscreen toggle animation. - */ -SlideMode.FULLSCREEN_TOGGLE_DELAY = 500; - -/** - * Touch handlers of the slide mode. - */ -class TouchHandler { - /** - * @param {!Element} targetElement Event source. - * @param {!SlideMode} slideMode Slide mode to be operated by the handler. - */ - constructor(targetElement, slideMode) { - /** - * Event source. - * @type {!Element} - * @private - * @const - */ - this.targetElement_ = targetElement; - - /** - * Target of touch operations. - * @type {!SlideMode} - * @private - * @const - */ - this.slideMode_ = slideMode; - - /** - * Flag to enable/disable touch operation. - * @type {boolean} - * @private - */ - this.enabled_ = true; - - /** - * Whether it is in a touch operation that is started from targetElement or - * not. - * @type {boolean} - * @private - */ - this.touchStarted_ = false; - - /** - * Whether the element is being clicked now or not. - * @type {boolean} - * @private - */ - this.clickStarted_ = false; - - /** - * The swipe action that should happen only once in an operation is already - * done or not. - * @type {boolean} - * @private - */ - this.done_ = false; - - /** - * Event on beginning of the current gesture. - * The variable is updated when the number of touch finger changed. - * @type {TouchEvent} - * @private - */ - this.gestureStartEvent_ = null; - - /** - * Rotation value on beginning of the current gesture. - * @type {number} - * @private - */ - this.gestureStartRotation_ = 0; - - /** - * Last touch event. - * @type {TouchEvent} - * @private - */ - this.lastEvent_ = null; - - /** - * Zoom value just after last touch event. - * @type {number} - * @private - */ - this.lastZoom_ = 1.0; - - /** - * @type {number} - * @private - */ - this.mouseWheelZoomOperationId_ = 0; - - targetElement.addEventListener('touchstart', this.onTouchStart_.bind(this)); - var onTouchEventBound = this.onTouchEvent_.bind(this); - targetElement.ownerDocument.addEventListener( - 'touchmove', onTouchEventBound); - targetElement.ownerDocument.addEventListener('touchend', onTouchEventBound); - - targetElement.addEventListener('mousedown', this.onMouseDown_.bind(this)); - targetElement.ownerDocument.addEventListener( - 'mousemove', this.onMouseMove_.bind(this)); - targetElement.ownerDocument.addEventListener( - 'mouseup', this.onMouseUp_.bind(this)); - targetElement.addEventListener('mousewheel', this.onMouseWheel_.bind(this)); - } - - /** - * Obtains distance between fingers. - * @param {!TouchEvent} event Touch event. It should include more than two - * touches. - * @return {number} Distance between touch[0] and touch[1]. - */ - static getDistance(event) { - var touch1 = event.touches[0]; - var touch2 = event.touches[1]; - var dx = touch1.clientX - touch2.clientX; - var dy = touch1.clientY - touch2.clientY; - return Math.sqrt(dx * dx + dy * dy); - } - - /** - * Obtains the degrees of the pinch twist angle. - * @param {!TouchEvent} event1 Start touch event. It should include more than - * two touches. - * @param {!TouchEvent} event2 Current touch event. It should include more - * than two touches. - * @return {number} Degrees of the pinch twist angle. - */ - static getTwistAngle(event1, event2) { - var dx1 = event1.touches[1].clientX - event1.touches[0].clientX; - var dy1 = event1.touches[1].clientY - event1.touches[0].clientY; - var dx2 = event2.touches[1].clientX - event2.touches[0].clientX; - var dy2 = event2.touches[1].clientY - event2.touches[0].clientY; - var innerProduct = dx1 * dx2 + dy1 * dy2; // |v1| * |v2| * cos(t) = x / r - var outerProduct = dx1 * dy2 - dy1 * dx2; // |v1| * |v2| * sin(t) = y / r - return Math.atan2(outerProduct, innerProduct) * 180 / - Math.PI; // atan(y / x) - } - - /** - * @param {boolean} flag New value. - */ - set enabled(flag) { - this.enabled_ = flag; - if (!this.enabled_) { - this.stopOperation(); - } - } - - /** - * Stops the current touch operation. - */ - stopOperation() { - this.touchStarted_ = false; - this.done_ = false; - this.gestureStartEvent_ = null; - this.lastEvent_ = null; - this.lastZoom_ = 1.0; - } - - /** - * Handles touch start events. - * @param {!Event} event Touch event. - * @private - */ - onTouchStart_(event) { - event = assertInstanceof(event, TouchEvent); - if (this.enabled_ && event.touches.length === 1) { - this.touchStarted_ = true; - } - } - - /** - * Handles touch move and touch end events. - * @param {!Event} event Touch event. - * @private - */ - onTouchEvent_(event) { - event = assertInstanceof(event, TouchEvent); - // Check if the current touch operation started from the target element or - // not. - if (!this.touchStarted_) { - return; - } - - // Check if the current touch operation ends with the event. - if (event.touches.length === 0) { - this.stopOperation(); - return; - } - - // Check if a new gesture started or not. - var viewport = this.slideMode_.getViewport(); - if (!this.lastEvent_ || - this.lastEvent_.touches.length !== event.touches.length) { - if (event.touches.length === 2 || event.touches.length === 1) { - this.gestureStartEvent_ = event; - this.gestureStartRotation_ = viewport.getRotation(); - this.lastEvent_ = event; - this.lastZoom_ = viewport.getZoom(); - } else { - this.gestureStartEvent_ = null; - this.gestureStartRotation_ = 0; - this.lastEvent_ = null; - this.lastZoom_ = 1.0; - } - return; - } - - // Handle the gesture movement. - switch (event.touches.length) { - case 1: - if (viewport.isZoomed()) { - // Scrolling an image by swipe. - var dx = - event.touches[0].screenX - this.lastEvent_.touches[0].screenX; - var dy = - event.touches[0].screenY - this.lastEvent_.touches[0].screenY; - viewport.setOffset( - viewport.getOffsetX() + dx, viewport.getOffsetY() + dy); - this.slideMode_.applyViewportChange(); - } else { - // Traversing images by swipe. - if (this.done_) { - break; - } - var dx = event.touches[0].clientX - - this.gestureStartEvent_.touches[0].clientX; - if (dx > TouchHandler.SWIPE_THRESHOLD) { - this.slideMode_.advanceManually(-1); - this.done_ = true; - } else if (dx < -TouchHandler.SWIPE_THRESHOLD) { - this.slideMode_.advanceManually(1); - this.done_ = true; - } - } - break; - - case 2: - // Pinch zoom. - var distance1 = TouchHandler.getDistance(this.lastEvent_); - var distance2 = TouchHandler.getDistance(event); - if (distance1 === 0) { - break; - } - var zoom = distance2 / distance1 * this.lastZoom_; - viewport.setZoom(zoom); - - // Pinch rotation. - assert(this.gestureStartEvent_); - var angle = TouchHandler.getTwistAngle(this.gestureStartEvent_, event); - if (angle > TouchHandler.ROTATION_THRESHOLD) { - viewport.setRotation(this.gestureStartRotation_ + 1); - } else if (angle < -TouchHandler.ROTATION_THRESHOLD) { - viewport.setRotation(this.gestureStartRotation_ - 1); - } else { - viewport.setRotation(this.gestureStartRotation_); - } - this.slideMode_.applyViewportChange(); - break; - } - - // Update the last event. - this.lastEvent_ = event; - this.lastZoom_ = viewport.getZoom(); - } - - /** - * Handles mouse wheel events. - * @param {!Event} event Wheel event. - * @private - */ - onMouseWheel_(event) { - var event = assertInstanceof(event, MouseEvent); - if (!this.enabled_) { - return; - } - - this.stopOperation(); - - var viewport = this.slideMode_.getViewport(); - var zoom = viewport.getZoom(); - if (event.wheelDeltaY > 0) { - zoom *= TouchHandler.WHEEL_ZOOM_FACTOR; - } else { - zoom /= TouchHandler.WHEEL_ZOOM_FACTOR; - } - - // Request animation frame not to set zoom more than once in a frame. This - // is a fix for https://crbug.com/591033 - requestAnimationFrame(function(operationId) { - if (this.mouseWheelZoomOperationId_ !== operationId) { - return; - } - - viewport.setZoom(zoom); - this.slideMode_.applyViewportChange(); - }.bind(this, ++this.mouseWheelZoomOperationId_)); - } - - /** - * Handles mouse down events. - * @param {!Event} event Wheel event. - * @private - */ - onMouseDown_(event) { - var event = assertInstanceof(event, MouseEvent); - var viewport = this.slideMode_.getViewport(); - if (!this.enabled_ || event.button !== 0) { - return; - } - this.clickStarted_ = true; - } - - /** - * Handles mouse move events. - * @param {!Event} event Wheel event. - * @private - */ - onMouseMove_(event) { - var event = assertInstanceof(event, MouseEvent); - var viewport = this.slideMode_.getViewport(); - if (!this.enabled_ || !this.clickStarted_) { - return; - } - this.stopOperation(); - viewport.setOffset( - viewport.getOffsetX() + - (/** @type {{movementX: number}} */ (event)).movementX, - viewport.getOffsetY() + - (/** @type {{movementY: number}} */ (event)).movementY); - this.slideMode_.imageView_.applyViewportChange(); - } - - /** - * Handles mouse up events. - * @param {!Event} event Wheel event. - * @private - */ - onMouseUp_(event) { - if (event.button !== 0) { - return; - } - this.clickStarted_ = false; - } -} - -/** - * If the user touched the image and moved the finger more than SWIPE_THRESHOLD - * horizontally it's considered as a swipe gesture (change the current image). - * @type {number} - * @const - */ -TouchHandler.SWIPE_THRESHOLD = 100; - -/** - * Rotation threshold in degrees. - * @type {number} - * @const - */ -TouchHandler.ROTATION_THRESHOLD = 25; - -/** - * Zoom magnification of one scroll event. - * @private {number} - * @const - */ -TouchHandler.WHEEL_ZOOM_FACTOR = 1.05;
diff --git a/ui/file_manager/gallery/js/slide_mode_unittest.js b/ui/file_manager/gallery/js/slide_mode_unittest.js deleted file mode 100644 index 2dc1acd..0000000 --- a/ui/file_manager/gallery/js/slide_mode_unittest.js +++ /dev/null
@@ -1,99 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @suppress {checkTypes, constantProperty} - */ -chrome.fileManagerPrivate = { - FormatFileSystemType: { - VFAT: 'vfat', - EXFAT: 'exfat', - NTFS: 'ntfs', - }, -}; - -/** - * Mock implementation of strf function. - */ -function strf(id, var_args) { - return `${id}-${Array.from(arguments).slice(1).join("-")}`; -} - -const fallbackDir = /** @type{!DirectoryEntry} */ ({fullPath: '/fallback'}); - -/** - * Test case for writable format and writable volume. - */ -function testGetEditorWarningMessageWritableFormatAndVolumeCase(callback) { - const item = /** @type{!GalleryItem} */ ({isWritableFormat: () => true}); - - reportPromise(SlideMode.getEditorWarningMessage( - item, '', fallbackDir).then(function(message) { - assertEquals(message, null); - }), callback); -} - -/** - * Test case for writable format and read only volume. - */ -function testGetEditorWarningMessageWritableFormatReadOnlyCase(callback) { - const item = /** @type{!GalleryItem} */ ({isWritableFormat: () => true}); - - reportPromise(SlideMode.getEditorWarningMessage( - item, 'NON_WRITABLE_VOLUME', fallbackDir).then(function(message) { - assertEquals(message, 'GALLERY_READONLY_WARNING-NON_WRITABLE_VOLUME'); - }), callback); -} - -/** - * Test case for non-writable format and writable volume. - */ -function testGetEditorWarningMessageNonWritableFormatAndWritableVolumeCase( - callback) { - const item = /** @type{!GalleryItem} */ ({ - isWritableFormat: function() { - return false; - }, - getEntry: function() { - return { - fullPath: '/parent/test.png', - getParent: function(callback) { - callback({ fullPath: '/parent' }); - } - }; - }, - getCopyName: function(dirEntry) { - assertEquals(dirEntry.fullPath, '/parent'); - return Promise.resolve('test - Edited.png'); - } - }); - - reportPromise(SlideMode.getEditorWarningMessage( - item, '', fallbackDir).then(function(message) { - assertEquals(message, - 'GALLERY_NON_WRITABLE_FORMAT_WARNING-test - Edited.png'); - }), callback); -} - -/** - * Test case for non-writable format and read only volume. - */ -function testGetEditorWarningMessageNonWritableFormatAndReadOnlyCase(callback) { - const item = /** @type{!GalleryItem} */ ({ - isWritableFormat: function() { - return false; - }, - getCopyName: function(dirEntry) { - assertEquals(dirEntry.fullPath, '/fallback'); - return Promise.resolve('test - Edited.png'); - } - }); - - reportPromise(SlideMode.getEditorWarningMessage( - item, 'NON_WRITABLE_VOLUME', fallbackDir).then(function(message) { - assertEquals(message, - "GALLERY_READONLY_AND_NON_WRITABLE_FORMAT_WARNING-" + - "NON_WRITABLE_VOLUME-test - Edited.png"); - }), callback); -}
diff --git a/ui/file_manager/gallery/js/test_util.js b/ui/file_manager/gallery/js/test_util.js deleted file mode 100644 index 3ae96ab..0000000 --- a/ui/file_manager/gallery/js/test_util.js +++ /dev/null
@@ -1,77 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * This variable is checked in SelectFileDialogExtensionBrowserTest. - * @type {number} - */ -window.JSErrorCount = 0; - -/** - * Counts uncaught exceptions. - */ -window.onerror = function() { - window.JSErrorCount++; -}; - -/** - * Opens the gallery window and waits until it is ready. - * - * @param {Array<string>} urls URLs to be opened. - * @param {function(string)} callback Completion callback with the new window's - * App ID. - */ -test.util.async.openGallery = function(urls, callback) { - openGalleryWindow(urls, false).then(callback); -}; - -/** - * Gets the metadata of the entry. - * - * @param {string} url URL to be get the metadata. - * @param {function(string)} callback Completion callback with the metadata. - */ -test.util.async.getMetadata = function(url, callback) { - util.URLsToEntries([url]).then(function(result) { - if (result.entries.length != 1) { - callback(null); - } else { - result.entries[0].getMetadata(function(metadata) { - callback({ - modificationTime: metadata.modificationTime, - size: metadata.size - }); - }); - } - }); -}; - -/** - * Changes the value of the input element. - * - * @param {Window} contentWindow Window to be tested. - * @param {string} query Query for the input element. - * @param {string} newValue Value to be assigned. - */ -test.util.sync.changeValue = function(contentWindow, query, newValue) { - var element = contentWindow.document.querySelector(query); - element.value = newValue; - chrome.test.assertTrue(element.dispatchEvent(new Event('change'))); -}; - -/** - * Changes the file name. - * - * @param {Window} contentWindow Window to be tested. - * @param {string} newName Name to be newly assigned. - */ -test.util.sync.changeName = function(contentWindow, newName) { - var nameBox = contentWindow.document.querySelector('#rename-input'); - nameBox.focus(); - nameBox.value = newName; - nameBox.blur(); -}; - -// Register the test utils. -test.util.registerRemoteTestUtils();
diff --git a/ui/file_manager/gallery/js/thumbnail_mode.js b/ui/file_manager/gallery/js/thumbnail_mode.js deleted file mode 100644 index 00e1a61..0000000 --- a/ui/file_manager/gallery/js/thumbnail_mode.js +++ /dev/null
@@ -1,1067 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * Thumbnail Mode. - */ -class ThumbnailMode extends cr.EventTarget { - /** - * @param {!HTMLElement} container A container. - * @param {!ErrorBanner} errorBanner Error banner. - * @param {!GalleryDataModel} dataModel Gallery data model. - * @param {!cr.ui.ListSelectionModel} selectionModel List selection model. - * @param {function()} changeToSlideModeCallback A callback to be called to - * change to slide mode due to activating a thumbnail. - */ - constructor( - container, errorBanner, dataModel, selectionModel, - changeToSlideModeCallback) { - super(); - - /** - * @private {!ErrorBanner} - * @const - */ - this.errorBanner_ = errorBanner; - - /** - * @private {!GalleryDataModel} - * @const - */ - this.dataModel_ = dataModel; - - /** - * @private {function()} - * @const - */ - this.changeToSlideModeCallback_ = changeToSlideModeCallback; - - this.dataModel_.addEventListener('splice', this.onSplice_.bind(this)); - - this.thumbnailView_ = - new ThumbnailView(container, dataModel, selectionModel); - this.thumbnailView_.addEventListener( - 'thumbnail-double-click', this.onThumbnailDoubleClick_.bind(this)); - } - - /** - * Returns name of this mode. - * @return {string} Mode name. - */ - getName() { - return 'thumbnail'; - } - - /** - * Returns title of this mode. - * @return {string} Mode title. - */ - getTitle() { - return 'GALLERY_THUMBNAIL'; - } - - /** - * Returns current sub mode. - * @return {GallerySubMode} - */ - getSubMode() { - return GallerySubMode.BROWSE; - } - - /** - * Executes an action. An action is executed immediately since this mode does - * not have busy state. - */ - executeWhenReady(action) { - action(); - } - - /** - * @return {boolean} Always true. Toolbar is always visible. - */ - hasActiveTool() { - return true; - } - - /** - * Handles key down event. - * @param {!Event} event An event. - * @return {boolean} True when an event is handled. - */ - onKeyDown(event) { - switch (event.key) { - case 'Enter': - if (event.target.matches('li.thumbnail')) { - this.changeToSlideModeCallback_(); - return true; - } - break; - } - - return false; - } - - /** - * Handles splice event of data model. - */ - onSplice_() { - if (this.dataModel_.length === 0) { - this.errorBanner_.show('GALLERY_NO_IMAGES'); - } else { - this.errorBanner_.clear(); - } - } - - /** - * Handles thumbnail double click event of Thumbnail View. - * @param {!Event} event An event. - * @private - */ - onThumbnailDoubleClick_(event) { - this.changeToSlideModeCallback_(); - } - - /** - * Shows thumbnail view. - */ - show() { - this.thumbnailView_.show(); - } - - /** - * Hides thumbnail view. - */ - hide() { - this.thumbnailView_.hide(); - } - - /** - * Performs thumbnail mode enter animation. - * @param {number} index Selected thumbnail index. - * @param {!ImageRect} rect A rect from which the transformation starts. - */ - performEnterAnimation(index, rect) { - this.thumbnailView_.performEnterAnimation(index, rect); - } - - /** - * Focus to thumbnail mode. - */ - focus() { - this.thumbnailView_.focus(); - } - - /** - * Returns thumbnail rect of the index. - * @param {number} index An index of thumbnail. - * @return {!ClientRect} A rect of thumbnail. - */ - getThumbnailRect(index) { - return this.thumbnailView_.getThumbnailRect(index); - } -} - -/** - * Thumbnail view. - * TODO(yawano): Optimization. Remove DOMs outside of viewport, reuse them. - * TODO(yawano): Extract ThumbnailView as a polymer element. - */ -class ThumbnailView extends cr.EventTarget { - /** - * @param {!HTMLElement} container A container. - * @param {!GalleryDataModel} dataModel Gallery data model. - * @param {!cr.ui.ListSelectionModel} selectionModel List selection model. - */ - constructor(container, dataModel, selectionModel) { - super(); - - /** - * @private {!HTMLElement} - */ - this.container_ = container; - - /** - * @private {!GalleryDataModel} - */ - this.dataModel_ = dataModel; - - /** - * @private {!cr.ui.ListSelectionModel} - */ - this.selectionModel_ = selectionModel; - - /** - * @private {!Object} - */ - this.thumbnails_ = {}; - - /** - * @private {boolean} - */ - this.scrolling_ = false; - - /** - * @private {number} - */ - this.initialScreenY_ = 0; - - /** - * @private {number} - */ - this.initialScrollTop_ = 0; - - /** - * @private {number} - */ - this.scrollbarTimeoutId_ = 0; - - /** - * @private {!HTMLElement} - */ - this.list_ = assertInstanceof(document.createElement('ul'), HTMLElement); - this.container_.appendChild(this.list_); - - /** - * @private {!HTMLElement} - */ - this.scrollbar_ = - assertInstanceof(document.createElement('div'), HTMLElement); - this.scrollbar_.classList.add('scrollbar'); - - /** - * @private {!HTMLElement} - */ - this.scrollbarThumb_ = - assertInstanceof(document.createElement('div'), HTMLElement); - this.scrollbarThumb_.classList.add('thumb'); - this.scrollbar_.appendChild(this.scrollbarThumb_); - this.container_.appendChild(this.scrollbar_); - - /** - * @private {!HTMLElement} - */ - this.animationThumbnail_ = - assertInstanceof(document.createElement('div'), HTMLElement); - this.animationThumbnail_.classList.add('animation-thumbnail'); - this.container_.appendChild(this.animationThumbnail_); - - this.container_.addEventListener('scroll', this.onScroll_.bind(this)); - this.container_.addEventListener('click', this.onClick_.bind(this)); - this.container_.addEventListener('dblclick', this.onDblClick_.bind(this)); - - // Set tabIndex to -1 as the container can capture keydown events. - this.container_.tabIndex = -1; - this.container_.addEventListener('keydown', this.onKeydown_.bind(this)); - - this.scrollbarThumb_.addEventListener( - 'mousedown', this.onScrollbarThumbMouseDown_.bind(this)); - window.addEventListener('mousemove', this.onWindowMouseMove_.bind(this)); - window.addEventListener('mouseup', this.onWindowMouseUp_.bind(this)); - - this.dataModel_.addEventListener('splice', this.onSplice_.bind(this)); - this.dataModel_.addEventListener('content', this.onContent_.bind(this)); - this.selectionModel_.addEventListener( - 'change', this.onSelectionChange_.bind(this)); - } - - /** - * Shows thumbnail view. - */ - show() { - this.container_.hidden = false; - } - - /** - * Hides thumbnail view. - */ - hide() { - this.container_.hidden = true; - } - - /** - * Handles scroll bar thumb mouse down event. - * @param {!Event} event An event. - * @private - */ - onScrollbarThumbMouseDown_(event) { - this.scrolling_ = true; - this.initialScreenY_ = event.screenY; - this.initialScrollTop_ = this.container_.scrollTop; - } - - /** - * Handles mouse move event of window. - * @param {!Event} event An event. - * @private - */ - onWindowMouseMove_(event) { - if (this.scrolling_) { - var diff = event.screenY - this.initialScreenY_; - var scrollTop = this.initialScrollTop_ + - (diff * this.container_.scrollHeight / this.scrollbar_.clientHeight); - this.container_.scrollTop = scrollTop; - } - - this.resetTimerOfScrollbar_(); - } - - /** - * Handles mouse up event of window. - * @param {!Event} event An event. - * @private - */ - onWindowMouseUp_(event) { - this.scrolling_ = false; - this.resetTimerOfScrollbar_(); - } - - /** - * Handles scroll of viewport. - * @param {!Event} event An event. - * @private - */ - onScroll_(event) { - this.updateScrollBar_(); - } - - /** - * Moves selection to specified direction. - * @param {string} direction Direction. Should be 'ArrowLeft', 'ArrowRight', - * 'ArrowUp', or 'ArrowDown'. - * @param {boolean} selectRange True to perform range selection. - * @private - */ - moveSelection_(direction, selectRange) { - var step; - if ((direction === 'ArrowLeft' && !isRTL()) || - (direction === 'ArrowRight' && isRTL()) || (direction === 'ArrowUp')) { - step = -1; - } else if ( - (direction === 'ArrowRight' && !isRTL()) || - (direction === 'ArrowLeft' && isRTL()) || (direction === 'ArrowDown')) { - step = 1; - } else { - assertNotReached(); - } - - var vertical = direction === 'ArrowUp' || direction === 'ArrowDown'; - var baseIndex = this.selectionModel_.leadIndex !== -1 ? - this.selectionModel_.leadIndex : - this.selectionModel_.selectedIndex; - var baseRect = this.getThumbnailRect(baseIndex); - var baseCenter = baseRect.left + baseRect.width / 2; - var minHorizontalGap = Number.MAX_VALUE; - var index = null; - - for (var i = baseIndex + step; 0 <= i && i < this.dataModel_.length; - i += step) { - // Skip error thumbnail. - var thumbnail = this.getThumbnailAt_(i); - if (thumbnail.isError()) { - continue; - } - - // Look for the horizontally nearest item if it is vertical move. - // Otherwise it just use the current i. - if (vertical) { - var rect = this.getThumbnailRect(i); - var verticalGap = Math.abs(baseRect.top - rect.top); - if (verticalGap === 0) { - continue; - } else if (verticalGap >= ThumbnailView.ROW_HEIGHT * 2) { - break; - } - // If centerGap - rect.width / 2 < 0, the image is located just - // above the center point of base image since baseCenter is in the range - // (rect.left, rect.right). In this case we use 0 as distance. Otherwise - // centerGap - rect.width / 2 equals to the distance between baseCenter - // and either of rect.left or rect.right that is closer to centerGap. - var centerGap = Math.abs(baseCenter - (rect.left + rect.width / 2)); - var horizontalGap = Math.max(centerGap - rect.width / 2, 0); - if (horizontalGap < minHorizontalGap) { - minHorizontalGap = horizontalGap; - index = i; - } - } else { - index = i; - break; - } - } - - if (index !== null) { - // Move selection. - if (selectRange && this.selectionModel_.anchorIndex !== -1) { - // Since anchorIndex will be set to 0 by unselectAll, copy the value. - var anchorIndex = this.selectionModel_.anchorIndex; - this.selectionModel_.unselectAll(); - this.selectionModel_.selectRange(anchorIndex, index); - this.selectionModel_.anchorIndex = anchorIndex; - } else { - this.selectionModel_.selectedIndex = index; - this.selectionModel_.anchorIndex = index; - } - - this.selectionModel_.leadIndex = index; - this.scrollTo_(index); - } - } - - /** - * Scrolls viewport to show the thumbnail of the index. - * @param {number} index Index of a thumbnail which becomes to appear in the - * viewport. - * @private - * - * TODO(yawano): Add scroll animation. - */ - scrollTo_(index) { - var thumbnailRect = this.getThumbnailRect(index); - - if (thumbnailRect.top - ThumbnailView.MARGIN < ImageEditorToolbar.HEIGHT) { - this.container_.scrollTop -= - ImageEditorToolbar.HEIGHT - thumbnailRect.top + ThumbnailView.MARGIN; - } else if ( - thumbnailRect.bottom + ThumbnailView.MARGIN > - this.container_.clientHeight) { - this.container_.scrollTop += thumbnailRect.bottom + ThumbnailView.MARGIN - - this.container_.clientHeight; - } - } - - /** - * Updates scroll bar. - * @private - */ - updateScrollBar_() { - var scrollTop = this.container_.scrollTop; - var scrollHeight = this.container_.scrollHeight; - var clientHeight = this.container_.clientHeight; - - // If viewport is not long enough to scroll, do not show scrollbar. - if (scrollHeight <= clientHeight) { - this.scrollbar_.hidden = true; - return; - } - - this.scrollbar_.hidden = false; - - var thumbHeight = - ~~(this.scrollbar_.clientHeight * clientHeight / scrollHeight); - var thumbTop = ~~(scrollTop * this.scrollbar_.clientHeight / scrollHeight); - - this.scrollbarThumb_.style.height = thumbHeight + 'px'; - this.scrollbarThumb_.style.marginTop = thumbTop + 'px'; - - this.resetTimerOfScrollbar_(); - } - - /** - * Resets timer to fade out scrollbar. If scrollbar is already faded-out, this - * method makes it visible and set timeout. If user is scrolling, this method - * just clears existing timer. - * @private - */ - resetTimerOfScrollbar_() { - this.scrollbar_.classList.toggle('transparent', false); - - if (this.scrollbarTimeoutId_) { - clearTimeout(this.scrollbarTimeoutId_); - this.scrollbarTimeoutId_ = 0; - } - - // If user is scrolling, do not set timeout. - if (this.scrolling_) { - return; - } - - this.scrollbarTimeoutId_ = setTimeout(function() { - this.scrollbarTimeoutId_ = 0; - this.scrollbar_.classList.toggle('transparent', true); - }.bind(this), ThumbnailView.SCROLLBAR_TIMEOUT); - } - - /** - * Handles splice event of data model. - * @param {!Event} event An event. - * @private - */ - onSplice_(event) { - if (event.removed) { - for (var i = 0; i < event.removed.length; i++) { - this.remove_(event.removed[i]); - } - } - - if (event.added && event.added.length > 0) { - // Get a thumbnail before which new thumbnail is inserted. - var insertBefore = null; - var galleryItem = this.dataModel_.item(event.index + event.added.length); - if (galleryItem) { - insertBefore = this.thumbnails_[galleryItem.getEntry().toURL()]; - } - - for (var i = 0; i < event.added.length; i++) { - this.insert_(event.added[i], insertBefore); - } - } - } - - /** - * Handles content event of data model. - * @param {!Event} event An event. - * @private - */ - onContent_(event) { - var galleryItem = event.item; - var oldEntry = event.oldEntry; - var thumbnail = this.thumbnails_[oldEntry.toURL()]; - if (thumbnail) { - // Update map. - delete this.thumbnails_[oldEntry.toURL()]; - this.thumbnails_[galleryItem.getEntry().toURL()] = thumbnail; - - thumbnail.update(); - } - } - - /** - * Handles selection change event. - * @param {!Event} event An event. - * @private - */ - onSelectionChange_(event) { - var changes = event.changes; - var lastSelectedThumbnail = null; - - for (var i = 0; i < changes.length; i++) { - var change = changes[i]; - - var galleryItem = this.dataModel_.item(change.index); - if (!galleryItem) { - continue; - } - - var thumbnail = this.thumbnails_[galleryItem.getEntry().toURL()]; - if (!thumbnail) { - continue; - } - - thumbnail.setSelected(change.selected); - - // We should not focus to error thumbnail. - if (change.selected && !thumbnail.isError()) { - lastSelectedThumbnail = thumbnail; - } - } - - // If new item is selected, focus to it. If multiple thumbnails are - // selected, focus to the last one. - if (lastSelectedThumbnail) { - lastSelectedThumbnail.getContainer().focus(); - } - } - - /** - * Handles click event. - * @param {!Event} event An event. - * @private - */ - onClick_(event) { - var target = event.target; - if (target.matches('.selection.frame')) { - var selectionMode = ThumbnailView.SelectionMode.SINGLE; - if (event.ctrlKey) { - selectionMode = ThumbnailView.SelectionMode.MULTIPLE; - } - if (event.shiftKey) { - selectionMode = ThumbnailView.SelectionMode.RANGE; - } - - this.selectByThumbnail_(target.parentNode.getThumbnail(), selectionMode); - return; - } - - // If empty space is clicked, unselect current selection. - this.selectionModel_.unselectAll(); - } - - /** - * Handles double click event. - * @param {!Event} event An event. - * @private - */ - onDblClick_(event) { - var target = event.target; - if (target.matches('.selection.frame')) { - this.selectByThumbnail_( - target.parentNode.getThumbnail(), ThumbnailView.SelectionMode.SINGLE); - var thumbnailDoubleClickEvent = new Event('thumbnail-double-click'); - this.dispatchEvent(thumbnailDoubleClickEvent); - } - } - - /** - * Handles keydown event. - * @param {!Event} event - * @private - */ - onKeydown_(event) { - var keyString = util.getKeyModifiers(event) + event.key; - - switch (keyString) { - case 'ArrowRight': - case 'ArrowLeft': - case 'ArrowUp': - case 'ArrowDown': - case 'Shift-ArrowRight': - case 'Shift-ArrowLeft': - case 'Shift-ArrowUp': - case 'Shift-ArrowDown': - this.moveSelection_(event.key, event.shiftKey); - event.stopPropagation(); - break; - case 'Ctrl-a': // Crtl+A - this.selectionModel_.selectAll(); - event.stopPropagation(); - break; - } - } - - /** - * Selects a thumbnail. - * @param {!ThumbnailView.Thumbnail} thumbnail Thumbnail to be selected. - * @param {ThumbnailView.SelectionMode} selectionMode - * @private - */ - selectByThumbnail_(thumbnail, selectionMode) { - var index = this.dataModel_.indexOf(thumbnail.getGalleryItem()); - - if (selectionMode === ThumbnailView.SelectionMode.SINGLE) { - this.selectionModel_.unselectAll(); - this.selectionModel_.setIndexSelected(index, true); - this.selectionModel_.anchorIndex = index; - } else if (selectionMode === ThumbnailView.SelectionMode.MULTIPLE) { - this.selectionModel_.setIndexSelected( - index, this.selectionModel_.selectedIndexes.indexOf(index) === -1); - } else if (selectionMode === ThumbnailView.SelectionMode.RANGE) { - var leadIndex = this.selectionModel_.leadIndex; - if (leadIndex < 0) { - leadIndex = 0; - } - this.selectionModel_.unselectAll(); - this.selectionModel_.selectRange(leadIndex, index); - } else { - assertNotReached(); - } - - this.selectionModel_.leadIndex = index; - } - - /** - * Inserts an item. - * @param {!GalleryItem} galleryItem A gallery item. - * @param {!ThumbnailView.Thumbnail} insertBefore A thumbnail before which new - * thumbnail is inserted. Set null for adding at the end of the list. - * @private - */ - insert_(galleryItem, insertBefore) { - var thumbnail = new ThumbnailView.Thumbnail(galleryItem); - this.thumbnails_[galleryItem.getEntry().toURL()] = thumbnail; - if (insertBefore) { - this.list_.insertBefore( - thumbnail.getContainer(), insertBefore.getContainer()); - } else { - this.list_.appendChild(thumbnail.getContainer()); - } - - // Set selection state. - var index = this.dataModel_.indexOf(galleryItem); - thumbnail.setSelected(this.selectionModel_.getIndexSelected(index)); - - this.updateScrollBar_(); - } - - /** - * Removes an item. - * @param {!GalleryItem} galleryItem A gallery item. - * @private - */ - remove_(galleryItem) { - var thumbnail = this.thumbnails_[galleryItem.getEntry().toURL()]; - this.list_.removeChild(thumbnail.getContainer()); - delete this.thumbnails_[galleryItem.getEntry().toURL()]; - } - - /** - * Returns thumbnail instance at specified index. - * @param {number} index Index of the thumbnail. - * @return {!ThumbnailView.Thumbnail} Thumbnail at the index. - * @private - */ - getThumbnailAt_(index) { - var galleryItem = this.dataModel_.item(index); - return this.thumbnails_[galleryItem.getEntry().toURL()]; - } - - /** - * Returns a rect of the specified thumbnail. - * @param {number} index An index of the thumbnail. - * @return {!ClientRect} Rect of the thumbnail. - */ - getThumbnailRect(index) { - var thumbnail = this.getThumbnailAt_(index); - return thumbnail.getContainer().getBoundingClientRect(); - } - - /** - * Performs enter animation. - * @param {number} index Index of the thumbnail which is animated. - * @param {!ImageRect} rect A rect from which the transformation starts. - * - * TODO(yawano): Consider to move this logic to thumbnail mode. - */ - performEnterAnimation(index, rect) { - this.scrollTo_(index); - this.updateScrollBar_(); - - var thumbnailRect = this.getThumbnailRect(index); - var thumbnail = this.getThumbnailAt_(index); - - // If thumbnail is not loaded yet or failed to load, do not perform - // animation. - if (!thumbnail.getBackgroundImage() || thumbnail.isError()) { - return; - } - - // Hide animating thumbnail. - thumbnail.setTransparent(true); - - this.animationThumbnail_.style.backgroundImage = - thumbnail.getBackgroundImage(); - this.animationThumbnail_.classList.add('animating'); - this.animationThumbnail_.width = thumbnail.getWidth(); - this.animationThumbnail_.height = ThumbnailView.ROW_HEIGHT; - - var animationPlayer = this.animationThumbnail_.animate( - [ - { - height: rect.height + 'px', - left: rect.left + 'px', - top: rect.top + 'px', - width: rect.width + 'px', - offset: 0, - easing: 'linear' - }, - { - height: thumbnailRect.height + 'px', - left: thumbnailRect.left + 'px', - top: thumbnailRect.top + 'px', - width: thumbnailRect.width + 'px', - offset: 1 - } - ], - 250); - - animationPlayer.addEventListener('finish', function() { - this.animationThumbnail_.classList.remove('animating'); - thumbnail.setTransparent(false); - }.bind(this)); - } - - /** - * Focus to thumbnail view. If an item is selected, focus to it. - */ - focus() { - if (this.selectionModel_.selectedIndexes.length === 0) { - this.container_.focus(); - return; - } - - var index = this.selectionModel_.leadIndex !== -1 ? - this.selectionModel_.leadIndex : - this.selectionModel_.selectedIndex; - var thumbnail = this.getThumbnailAt_(index); - thumbnail.getContainer().focus(); - } -} - -/** - * Row height. - * @const {number} - * - * TODO(yawano): Change so that Gallery adjust row height depending on image - * collection and window size to cover viewport as much as possible. - */ -ThumbnailView.ROW_HEIGHT = 160; // px - -/** - * Margins between thumbnails. This should be synced with CSS. - * @const {number} - */ -ThumbnailView.MARGIN = 4; // px - -/** - * Timeout to fade out scrollbar. - * @const {number} - */ -ThumbnailView.SCROLLBAR_TIMEOUT = 1500; // ms - -/** - * Selection mode. - * @enum {string} - */ -ThumbnailView.SelectionMode = { - SINGLE: 'single', - MULTIPLE: 'multiple', - RANGE: 'range' -}; - -/** - * Thumbnail. - */ -ThumbnailView.Thumbnail = class { - /** - * @param {!GalleryItem} galleryItem A gallery item. - */ - constructor(galleryItem) { - /** - * @private {!GalleryItem} - */ - this.galleryItem_ = galleryItem; - - /** - * @private {boolean} - */ - this.selected_ = false; - - /** - * @private {ThumbnailLoader} - */ - this.thumbnailLoader_ = null; - - /** - * @private {number} - */ - this.thumbnailLoadRequestId_ = 0; - - /** - * @private {number} - */ - this.width_ = 0; - - /** - * @private {*} - */ - this.error_ = null; - - /** - * @private {!HTMLElement} - */ - this.container_ = - assertInstanceof(document.createElement('li'), HTMLElement); - this.container_.tabIndex = 1; - this.container_.classList.add('thumbnail'); - - /** - * @private {!HTMLElement} - */ - this.imageFrame_ = - assertInstanceof(document.createElement('div'), HTMLElement); - this.imageFrame_.classList.add('image', 'frame'); - - if (FileType.isVideo(galleryItem.getEntry())) { - this.imageFrame_.classList.add('video'); - } - - this.container_.appendChild(this.imageFrame_); - - /** - * @private {!HTMLElement} - */ - this.selectionFrame_ = - assertInstanceof(document.createElement('div'), HTMLElement); - this.selectionFrame_.classList.add('selection', 'frame'); - this.container_.appendChild(this.selectionFrame_); - - this.container_.style.height = ThumbnailView.ROW_HEIGHT + 'px'; - this.container_.getThumbnail = function(thumbnail) { - return thumbnail; - }.bind(null, this); - - this.update(); - } - - /** - * Returns a gallery item. - * @return {!GalleryItem} A gallery item. - */ - getGalleryItem() { - return this.galleryItem_; - } - - /** - * Change selection state of this thumbnail. - * @param {boolean} selected True to make this thumbnail selected. - */ - setSelected(selected) { - this.selected_ = selected; - this.container_.classList.toggle('selected', selected); - } - - /** - * Returns a container. - * @return {!HTMLElement} A container. - */ - getContainer() { - return this.container_; - } - - /** - * Sets this thumbnail as transparent. - * @param {boolean} transparent True to make this thumbnail transparent. - */ - setTransparent(transparent) { - this.container_.classList.toggle('transparent', transparent); - } - - /** - * Returns width of this thumbnail. - * @return {number} Width of this thumbnail. - */ - getWidth() { - return this.width_; - } - - /** - * Returns whether this has failed to load thumbnail or not. - * @return {boolean} True if thumbnail load has failed. - */ - isError() { - return !!this.error_; - } - - /** - * Sets error. - * @param {*} error Error object. Set null to clear error. - * @private - */ - setError_(error) { - this.error_ = error; - this.container_.classList.toggle('error', !!this.error_); - } - - /** - * Sets width of this thumbnail. - * @param {number} width Width. - * @private - */ - setWidth_(width) { - if (this.width_ === width) { - return; - } - - this.width_ = width; - this.container_.style.width = this.width_ + 'px'; - } - - /** - * Returns background image style of this thumbnail. - * @return {string} Background image. - */ - getBackgroundImage() { - return this.imageFrame_.style.backgroundImage; - } - - /** - * Updates thumbnail. - */ - update() { - // Update title. - this.container_.setAttribute('title', this.galleryItem_.getFileName()); - - // Calculate and set width. - var metadata = this.galleryItem_.getMetadataItem(); - if (!metadata) { - this.setWidth_(ThumbnailView.ROW_HEIGHT); - return; - } - - var rotated = metadata.imageRotation % 2 === 1; - var imageWidth = rotated ? metadata.imageHeight : metadata.imageWidth; - var imageHeight = rotated ? metadata.imageWidth : metadata.imageHeight; - this.setWidth_(~~(imageWidth * ThumbnailView.ROW_HEIGHT / imageHeight)); - - // Set thumbnail. - var thumbnailMetadata = this.galleryItem_.getThumbnailMetadataItem(); - if (!thumbnailMetadata) { - return; - } - - this.loadAndSetThumbnail_( - thumbnailMetadata, false /* do not force to generate thumbnail */) - .then(function(result) { - if (!result || result.height >= ThumbnailView.ROW_HEIGHT || - result.loadTarget === ThumbnailLoader.LoadTarget.FILE_ENTRY || - metadata.imageHeight <= ThumbnailView.ROW_HEIGHT || - (thumbnailMetadata.external && - !thumbnailMetadata.external.present)) { - return; - } - - // If thumbnail height is lower than ThumbnailView.ROW_HEIGHT, - // generate thumbnail from image content. - this.loadAndSetThumbnail_( - thumbnailMetadata, true /* force to generate thumbnail */); - }.bind(this)); - } - - /** - * Loads thumbnail and sets it. - * @param {!ThumbnailMetadataItem} thumbnailMetadata - * @param {boolean} forceToGenerate True to force generating thumbnail from - * image content. - * @return {!Promise<?{height:number, - * loadTarget:?ThumbnailLoader.LoadTarget}>} null is returned for error - * case. - * @private - */ - loadAndSetThumbnail_(thumbnailMetadata, forceToGenerate) { - this.thumbnailLoadRequestId_++; - - var loadTargets = forceToGenerate ? - [ThumbnailLoader.LoadTarget.FILE_ENTRY] : - undefined /* default value */; - - this.thumbnailLoader_ = new ThumbnailLoader( - this.galleryItem_.getEntry(), undefined /* opt_loaderType */, - thumbnailMetadata, undefined /* opt_mediaType */, loadTargets); - return this.thumbnailLoader_.loadAsDataUrl(ThumbnailLoader.FillMode.FIT) - .then(function(requestId, result) { - // Discard the result of old request. - if (requestId !== this.thumbnailLoadRequestId_) { - return null; - } - - // Update width by using the width of actual data. - this.setWidth_( - ~~(result.width * ThumbnailView.ROW_HEIGHT / result.height)); - - this.imageFrame_.style.backgroundImage = 'url(' + result.data + ')'; - this.setError_(null); - - return { - height: result.height, - loadTarget: this.thumbnailLoader_.getLoadTarget() - }; - }.bind(this, this.thumbnailLoadRequestId_)) - .catch(function(requestId, error) { - if (requestId !== this.thumbnailLoadRequestId_) { - return null; - } - - this.setError_(error); - return null; - }.bind(this, this.thumbnailLoadRequestId_)); - } -};
diff --git a/ui/file_manager/gallery/manifest.json b/ui/file_manager/gallery/manifest.json deleted file mode 100644 index f9f4d62..0000000 --- a/ui/file_manager/gallery/manifest.json +++ /dev/null
@@ -1,90 +0,0 @@ -{ - // chrome-extension://nlkncpkkdoccmpiclbokaimcnedabhhm/ - "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1hFTC+sl9e4ufs6ccGhspbgnMQb3GMikA/QwghFVp00WDwFu7no8xIOWJwY9lFQP+NrbSsze3JL9Wg6FmHC6xKIncwZJKwyDDd2g+9/gEZLn5Ar1piPyf+ELtuX+m0Pjp0l2+rVMz2UiP5OUvFqvmCZqJJQnTVjRut3IMjDP6npb5HyDTgqlPgNHWmsLAQZZKTyYfqswBFkvmwiSHTNJuxkh+i1hxo2m8RcBQsXWL8Mt9+WPl0uABIZc7UvLoZwNz1pAKWb5sv0y4oBugpw4ZVIvCT/pxplLXF35GGBNWAkgimkpYu+SldoZQV8SZW1kUSIcrpYW80mA7KxfK5H8vwIDAQAB", - "manifest_version": 2, - "name": "Gallery", - "version": "2.2", - "description": "Picture browser app", - "display_in_launcher": false, - "incognito" : "split", - "icons": { - "16": "images/icon16.png", - "32": "images/icon32.png", - "48": "images/icon48.png", - "64": "images/icon64.png", - "96": "images/icon96.png", - "128": "images/icon128.png", - "192": "images/icon192.png", - "256": "images/icon256.png" - }, - "permissions": [ - "accessibilityFeatures.read", - "app.window.fullscreen.overrideEsc", - "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj", - "chrome://extension-icon/", - "chrome://resources/", - "chrome://theme/", - "commandLinePrivate", - "fileManagerPrivate", - { - "fileSystem": ["requestFileSystem", "write"] - }, - "fullscreen", - "metricsPrivate", - "storage", - "webview" - ], - "file_handlers": { - "open": { - "extensions": [ - // Image - "bmp", - "gif", - "ico", - "jpg", - "jpeg", - "png", - "webp", - // Raw: Sync with the raw type list in file_type.js. - "arw", - "cr2", - "dng", - "nef", - "nrw", - "orf", - "raf", - "rw2", - // Video. - "3gp", - "3gpp", - "avi", - "m4v", - "mkv", - "mov", - "mp4", - "mpeg", - "mpeg4", - "mpg", - "mpg4", - "ogm", - "ogv", - "ogx", - "webm" - ] - } - }, - "app": { - "background": { - "scripts": [ - "chrome://resources/js/cr.js", - "chrome://resources/js/cr/event_target.js", - "chrome://resources/js/assert.js", - "chrome://resources/js/cr/ui/array_data_model.js", - "chrome://resources/js/load_time_data.js", - "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/background/js/background_common_scripts.js", - "js/background_scripts.js" - ] - }, - "content_security_policy": "default-src 'none'; script-src 'self' blob: filesystem: chrome://resources chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj; style-src 'self' blob: filesystem: chrome://resources 'unsafe-inline'; img-src 'self' blob: filesystem: chrome://resources chrome://theme data: https://*.googleusercontent.com chrome://extension-icon; media-src 'self' blob: filesystem:; connect-src chrome://resources ; object-src 'self' blob: filesystem:; font-src chrome://resources; " - } -}
diff --git a/ui/file_manager/image_loader/image_loader.js b/ui/file_manager/image_loader/image_loader.js index c356ffd..e6d937e 100644 --- a/ui/file_manager/image_loader/image_loader.js +++ b/ui/file_manager/image_loader/image_loader.js
@@ -100,7 +100,6 @@ */ ImageLoader.ALLOWED_CLIENT_ORIGINS = [ 'chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj', // File Manager - 'chrome-extension://nlkncpkkdoccmpiclbokaimcnedabhhm', // Gallery 'chrome-extension://jcgeabjmjgoblfofpppfkcoakmfobdko', // Video Player 'chrome://file-manager', // File Manager SWA ];
diff --git a/ui/file_manager/image_loader/manifest.json b/ui/file_manager/image_loader/manifest.json index 5e10dbc..81e102e 100644 --- a/ui/file_manager/image_loader/manifest.json +++ b/ui/file_manager/image_loader/manifest.json
@@ -23,7 +23,6 @@ "externally_connectable": { "ids": [ "hhaomjibdihmijegdhdafkllkbggdgoj", // File Manager - "nlkncpkkdoccmpiclbokaimcnedabhhm", // Gallery "jcgeabjmjgoblfofpppfkcoakmfobdko" // Video Player ], "matches": [
diff --git a/ui/file_manager/integration_tests/file_manager/background.js b/ui/file_manager/integration_tests/file_manager/background.js index 9e593600..bf906d13b 100644 --- a/ui/file_manager/integration_tests/file_manager/background.js +++ b/ui/file_manager/integration_tests/file_manager/background.js
@@ -14,15 +14,6 @@ const remoteCall = new RemoteCallFilesApp(FILE_MANAGER_EXTENSIONS_ID); /** - * Extension ID of Gallery. - * @type {string} - * @const - */ -const GALLERY_APP_ID = 'nlkncpkkdoccmpiclbokaimcnedabhhm'; - -const galleryApp = new RemoteCallGallery(GALLERY_APP_ID); - -/** * Extension ID of Audio Player. * @type {string} * @const
diff --git a/ui/file_manager/integration_tests/gallery/OWNERS b/ui/file_manager/integration_tests/gallery/OWNERS deleted file mode 100644 index 13c6f49f..0000000 --- a/ui/file_manager/integration_tests/gallery/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -tapted@chromium.org
diff --git a/ui/file_manager/integration_tests/gallery/background.js b/ui/file_manager/integration_tests/gallery/background.js deleted file mode 100644 index e49189a..0000000 --- a/ui/file_manager/integration_tests/gallery/background.js +++ /dev/null
@@ -1,110 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -'use strict'; - -/** - * Extension ID of gallery app. - * @type {string} - * @const - */ -var GALLERY_APP_ID = 'nlkncpkkdoccmpiclbokaimcnedabhhm'; - -var gallery = new RemoteCallGallery(GALLERY_APP_ID); - -/** - * Launches the gallery with the given entries. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @param {Array<TestEntryInfo>} entries Entries to be parepared and passed to - * the application. - * @param {Array<TestEntryInfo>=} opt_selected Entries to be selected. Should - * be a sub-set of the entries argument. - * @return {Promise} Promise to be fulfilled with the data of the main element - * in the allery. - */ -function launch(testVolumeName, volumeType, entries, opt_selected) { - var entriesPromise = addEntries([testVolumeName], entries).then(function() { - var selectedEntries = opt_selected || entries; - var selectedEntryNames = selectedEntries.map(function(entry) { - return entry.nameText; - }); - return gallery.callRemoteTestUtil( - 'getFilesUnderVolume', null, [volumeType, selectedEntryNames]); - }); - - var appId = null; - var urls = []; - return entriesPromise.then(function(result) { - urls = result; - return gallery.callRemoteTestUtil('openGallery', null, [urls]); - }).then(function(windowId) { - chrome.test.assertTrue(!!windowId); - appId = windowId; - return gallery.waitForElement(appId, 'div.gallery'); - }).then(function(args) { - return { - appId: appId, - mailElement: args[0], - urls: urls, - }; - }); -} - -/** - * Namespace for test cases. - */ -var testcase = {}; - -/** - * When the FileManagerBrowserTest harness loads this test extension, request - * configuration and other details from that harness, including the test case - * name to run. Use the configuration/details to setup the test ennvironment, - * then run the test case using chrome.test.RunTests. - */ -window.addEventListener('load', function() { - var steps = [ - // Request the guest mode state. - function() { - sendBrowserTestCommand({name: 'isInGuestMode'}, steps.shift()); - }, - // Request the root entry paths. - function(mode) { - if (JSON.parse(mode) != chrome.extension.inIncognitoContext) { - return; - } - sendBrowserTestCommand({name: 'getRootPaths'}, steps.shift()); - }, - // Request the test case name. - function(paths) { - var roots = JSON.parse(paths); - RootPath.DOWNLOADS = roots.downloads; - RootPath.DRIVE = roots.drive; - sendBrowserTestCommand({name: 'getTestName'}, steps.shift()); - }, - // Run the test case. - function(testCaseName) { - // Get the test function from testcase namespace testCaseName. - var test = testcase[testCaseName]; - // Verify test is an unnamed (aka 'anonymous') Function. - if (!(test instanceof Function) || test.name) { - chrome.test.fail('[' + testCaseName + '] not found.'); - return; - } - // Define the test case and its name for chrome.test logging. - test.generatedName = testCaseName; - var testCaseSymbol = Symbol(testCaseName); - var testCase = { - [testCaseSymbol] :() => { - return testPromiseAndApps(test(), [gallery]); - }, - }; - // Run the test. - chrome.test.runTests([testCase[testCaseSymbol]]); - } - ]; - steps.shift()(); -});
diff --git a/ui/file_manager/integration_tests/gallery/open_image_files.js b/ui/file_manager/integration_tests/gallery/open_image_files.js deleted file mode 100644 index 124bcce..0000000 --- a/ui/file_manager/integration_tests/gallery/open_image_files.js +++ /dev/null
@@ -1,191 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -'use strict'; - -/** - * Runs a test to open a single image. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled with on success. - */ -function openSingleImage(testVolumeName, volumeType) { - var launchedPromise = launch(testVolumeName, volumeType, [ENTRIES.desktop]); - return launchedPromise.then(function(args) { - var WIDTH = 880; - var HEIGHT = 602; /* Inner height 570px + native header 32px. */ - var appId = args.appId; - var resizedWindowPromise = gallery.callRemoteTestUtil( - 'resizeWindow', appId, [WIDTH, HEIGHT] - ).then(function() { - return repeatUntil(function() { - return gallery.callRemoteTestUtil('getWindows', null, [] - ).then(function(windows) { - var bounds = windows[appId]; - if (!bounds) { - return pending('Window is not ready yet.'); - } - - if (bounds.outerWidth !== WIDTH || bounds.outerHeight !== HEIGHT) { - return pending( - 'Window bounds is expected %d x %d, but is %d x %d', - WIDTH, HEIGHT, - bounds.outerWidth, - bounds.outerHeight); - } - return true; - }); - }); - }); - - return resizedWindowPromise.then(function() { - var rootElementPromise = - gallery.waitForElement(appId, '.gallery[mode="slide"]'); - var fullImagePromsie = gallery.waitForElementStyles( - appId, '.gallery .image-container > .image', ['any']); - return Promise.all([rootElementPromise, fullImagePromsie]). - then(function(args) { - chrome.test.assertEq(760, args[1].renderedWidth); - chrome.test.assertEq(570, args[1].renderedHeight); - chrome.test.assertEq(800, args[1].imageWidth); - chrome.test.assertEq(600, args[1].imageHeight); - }); - }); - }); -} - -/** - * Confirms that two images are loaded in thumbnail mode. This method doesn't - * care whether two images are loaded with error or not. - * - * @param {string} appId - * @return {Promise} Promise to be fulfilled with on success. - */ -function confirmTwoImagesAreLoadedInThumbnailMode(appId) { - // Wait until Gallery changes to thumbnail mode. - return gallery.waitForElement( - appId, '.gallery[mode="thumbnail"]').then(function() { - // Confirm that two tiles are shown. - return repeatUntil(function() { - return gallery - .callRemoteTestUtil( - 'queryAllElements', appId, ['.thumbnail-view .thumbnail']) - .then(function(tiles) { - if (tiles.length !== 2) { - return pending( - 'The number of tiles is expected 2, but is %d', tiles.length); - } - return tiles; - }); - }); - }); -} - -/** - * Runs a test to open multiple images. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled with on success. - */ -function openMultipleImages(testVolumeName, volumeType) { - var testEntries = [ENTRIES.desktop, ENTRIES.image3]; - var launchedPromise = launch(testVolumeName, volumeType, testEntries); - return launchedPromise.then(function(args) { - var appId = args.appId; - return confirmTwoImagesAreLoadedInThumbnailMode(appId); - }); -} - -/** - * Runs a test to open multiple images and change to slide mode with keyboard. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled with on success. - */ -function openMultipleImagesAndChangeToSlideMode(testVolumeName, volumeType) { - var testEntries = [ENTRIES.desktop, ENTRIES.image3]; - var launchedPromise = launch(testVolumeName, volumeType, testEntries); - return launchedPromise.then(function(args) { - var appId = args.appId; - return confirmTwoImagesAreLoadedInThumbnailMode(appId).then(function() { - // Press Enter key and mode should be changed to slide mode. - return gallery.callRemoteTestUtil( - 'fakeKeyDown', appId, - [null /* active element */, 'Enter', false, false, false]); - }).then(function() { - // Wait until it changes to slide mode. - return gallery.waitForElement(appId, '.gallery[mode="slide"]'); - }); - }); -} - -/** - * The openSingleImage test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.openSingleImageOnDownloads = function() { - return openSingleImage('local', 'downloads'); -}; - -/** - * The openSingleImage test for Google Drive. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.openSingleImageOnDrive = function() { - return openSingleImage('drive', 'drive'); -}; - -/** - * The openMultiImages test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.openMultipleImagesOnDownloads = function() { - return openMultipleImages('local', 'downloads'); -}; - -/** - * The openMultiImages test for Google Drive. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.openMultipleImagesOnDrive = function() { - return openMultipleImages('drive', 'drive'); -}; - -/** - * The openMultipleImagesAndChangeToSlideMode test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.openMultipleImagesAndChangeToSlideModeOnDownloads = function() { - return openMultipleImagesAndChangeToSlideMode('local', 'downloads'); -}; - -/** - * Runs a test to check whether the rename-input field is hidden after - * deleting the only selected image in the gallery. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.deleteSingleOpenPhotoOnDownloads = () => { - const launchedPromise = launch('local', 'downloads', [ENTRIES.desktop]); - let appId; - return launchedPromise.then(args => { - appId = args.appId; - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }).then(() => { - // Click the delete button. - return gallery.waitAndClickElement(appId, 'button.delete'); - }).then(result => { - chrome.test.assertTrue(!!result); - // Wait and click delete button of confirmation dialog. - return gallery.waitAndClickElement(appId, '.cr-dialog-ok'); - }).then(() => { - // Check: The edit name field should hide. - return gallery.waitForElement(appId, '#rename-input[hidden]'); - }); -}; \ No newline at end of file
diff --git a/ui/file_manager/integration_tests/gallery/photo_editor.js b/ui/file_manager/integration_tests/gallery/photo_editor.js deleted file mode 100644 index 2f1a8b5..0000000 --- a/ui/file_manager/integration_tests/gallery/photo_editor.js +++ /dev/null
@@ -1,498 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -'use strict'; - -/** - * Prepares the photo editor. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled with on success. - */ -function setupPhotoEditor(testVolumeName, volumeType) { - // Lauch the gallery. - var launchedPromise = launch( - testVolumeName, - volumeType, - [ENTRIES.desktop]); - return launchedPromise.then(function(args) { - var appId = args.appId; - - // Show the slide image. - var slideImagePromise = gallery.waitForSlideImage( - appId, - 800, - 600, - 'My Desktop Background'); - - // Lauch the photo editor. - var photoEditorPromise = slideImagePromise.then(function() { - return gallery.waitAndClickElement(appId, 'button.edit'); - }); - - return photoEditorPromise.then(function() { - return args; - }); - }); -} - -/** - * Tests to rotate an image. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled with on success. - */ -function rotateImage(testVolumeName, volumeType) { - var launchedPromise = setupPhotoEditor(testVolumeName, volumeType); - return launchedPromise.then(function(args) { - var appId = args.appId; - return gallery.waitAndClickElement( - appId, '.gallery:not([locked]) button.rotate_right').then(function() { - return gallery.waitForSlideImage( - appId, - 600, - 800, - 'My Desktop Background'); - }). - then(function() { - return gallery.waitAndClickElement( - appId, '.gallery:not([locked]) button.rotate_left'); - }). - then(function() { - return gallery.waitForSlideImage( - appId, - 800, - 600, - 'My Desktop Background'); - }); - }); -} - -/** - * Tests to crop an image and undoes it. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled with on success. - */ -function cropImage(testVolumeName, volumeType) { - var launchedPromise = setupPhotoEditor(testVolumeName, volumeType); - return launchedPromise.then(function(args) { - var appId = args.appId; - return gallery - .waitAndClickElement(appId, '.gallery:not([locked]) button.crop') - .then(function() { - return Promise.all([ - gallery.waitForElement(appId, '.crop-overlay') - ]); - }) - .then(function() { - return gallery.callRemoteTestUtil( - 'queryAllElements', appId, ['.crop-aspect-ratio:focus']); - }) - .then(function(result) { - // Tests that crop aspect ratio buttons hold no focus on launch - // crbug.com/655943 - chrome.test.assertEq(0, result.length); - }) - .then(function() { - return gallery.fakeKeyDown( - appId, 'body', 'Enter', false, false, false); - }) - .then(function(ret) { - chrome.test.assertTrue(ret); - return Promise.all([ - gallery.waitForElementLost(appId, '.crop-overlay') - ]); - }) - .then(function() { - return gallery.waitForSlideImage( - appId, - 534, - 400, - 'My Desktop Background'); - }) - .then(function() { - return gallery.waitAndClickElement( - appId, '.gallery:not([locked]) button.undo'); - }) - .then(function() { - return gallery.waitForSlideImage( - appId, 800, 600, 'My Desktop Background'); - }); - }); -} - -/** - * Tests to exposure an image. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled with on success. - */ -function exposureImage(testVolumeName, volumeType) { - var launchedPromise = setupPhotoEditor(testVolumeName, volumeType); - return launchedPromise.then(function(args) { - var appId = args.appId; - var url = args.urls[0]; - var buttonQuery = '.gallery:not([locked]) button.exposure'; - var origMetadata = null; - - // Click the exposure button. - return gallery.waitAndClickElement(appId, buttonQuery) - .then(function() { - // Wait until the edit controls appear. - return Promise.all([ - gallery.waitForElement(appId, '.brightness > cr-slider'), - gallery.waitForElement(appId, '.contrast > cr-slider'), - ]); - }) - .then(function() { - return gallery.callRemoteTestUtil( - 'changeValue', appId, ['.brightness > cr-slider', 20]); - }) - .then(function() { - return gallery.callRemoteTestUtil( - 'changeValue', appId, ['.contrast > cr-slider', -20]); - }) - .then(function() { - return gallery.callRemoteTestUtil('getMetadata', null, [url]); - }) - .then(function(metadata) { - origMetadata = metadata; - - // Push the Enter key. - return gallery.fakeKeyDown( - appId, 'body', 'Enter', false, false, false); - }) - .then(function() { - // Wait until the image is updated. - return repeatUntil(function() { - return gallery.callRemoteTestUtil('getMetadata', null, [url]) - .then(function(metadata) { - if (origMetadata.modificationTime != - metadata.modificationTime) { - return true; - } else { - return pending( - '%s is not updated. First ' + - 'last modified: %s, Second last modified: %s.', - url, origMetadata.modificationTime, - metadata.modificationTime); - } - }); - }); - }); - }); -} - -/** - * Tests to resize an image and undoes it. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local' - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled with on success. - */ -function resizeImage(testVolumeName, volumeType) { - var launchedPromise = setupPhotoEditor(testVolumeName, volumeType); - return launchedPromise.then(function(args) { - var appId = args.appId; - - return gallery - .waitAndClickElement(appId, '.gallery:not([locked]) button.resize') - .then(function() { - return Promise.all([ - gallery.waitForElement(appId, '.width > cr-input'), - gallery.waitForElement(appId, '.height > cr-input'), - gallery.waitForElement(appId, '.lockicon[locked]'), - ]); - }) - .then(function() { - return gallery.callRemoteTestUtil( - 'changeValue', appId, ['.height > cr-input', 500]); - }) - .then(function() { - return gallery.fakeKeyDown( - appId, 'body', 'Enter', false, false, false); - }) - .then(function() { - return gallery.waitForSlideImage(appId, 667, 500, - 'My Desktop Background'); - }) - .then(function() { - return gallery.waitAndClickElement( - appId, '.gallery:not([locked]) button.undo'); - }) - .then(function() { - return gallery.waitForSlideImage(appId, 800, 600, - 'My Desktop Background'); - }) - .then(function() { - return gallery.waitAndClickElement( - appId, '.gallery:not([locked]) button.resize'); - }) - .then(function() { - return Promise.all([ - gallery.waitForElement(appId, '.width > cr-input'), - gallery.waitForElement(appId, '.height > cr-input'), - gallery.waitForElement(appId, '.lockicon[locked]'), - ]); - }) - .then(function() { - return gallery.waitAndClickElement( - appId, '.gallery:not([locked]) .lockicon[locked]'); - }) - .then(function() { - return gallery.callRemoteTestUtil( - 'changeValue', appId, ['.width > cr-input', 500]); - }) - .then(function() { - return gallery.callRemoteTestUtil( - 'changeValue', appId, ['.height > cr-input', 300]); - }) - .then(function() { - return gallery.fakeKeyDown( - appId, 'body', 'Enter', false, false, false); - }) - .then(function() { - return gallery.waitForSlideImage(appId, 500, 300, - 'My Desktop Background'); - }) - .then(function() { - return gallery.waitAndClickElement( - appId, '.gallery:not([locked]) button.undo'); - }) - .then(function() { - return gallery.waitForSlideImage(appId, 800, 600, - 'My Desktop Background'); - }); - }); -} - -/** - * Tests deleting an image while editing to ensure the edit toolbar - * is hidden. - * - * For reference: crbug.com/912489 - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local' - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled with on success. - */ -function deleteImageWhileEditing(testVolumeName, volumeType) { - var launchedPromise = setupPhotoEditor(testVolumeName, volumeType); - return launchedPromise.then(function(args) { - var appId = args.appId; - - return gallery.waitAndClickElement(appId, 'button.delete') - .then(result => { - chrome.test.assertTrue(!!result); - // Wait and click delete button of confirmation dialog. - return gallery.waitAndClickElement(appId, '.cr-dialog-ok'); - }) - .then(() => { - // Wait for the edit mode toolbar to hide. - return repeatUntil(function() { - return gallery - .waitForElementStyles( - appId, '.edit-mode-toolbar', ['visibility']) - .then(function(result) { - if (result.styles.visibility != 'hidden') { - return pending( - 'Expected edit-mode-toolbar to be hidden but was %s', - result.styles.visibility); - } - return result; - }); - }); - }); - }); -} - -/** - * Tests whether overwrite original checkbox is enabled or disabled properly. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled with on success. - */ -function enableDisableOverwriteOriginalCheckbox(testVolumeName, volumeType) { - var appId; - var launchedPromise = setupPhotoEditor(testVolumeName, volumeType); - return launchedPromise - .then(function(result) { - appId = result.appId; - - // Confirm overwrite original checkbox is enabled and checked. - return gallery.waitForElementStyles( - appId, '.overwrite-original[checked]:not([disabled])', ['cursor']); - }) - .then(function(result) { - // Ensure that checkbox cursor style takes pointer state when enabled. - // https://crbug.com/888464 - chrome.test.assertEq('pointer', result.styles.cursor); - // Uncheck overwrite original. - return gallery.waitAndClickElement(appId, '.overwrite-original'); - }) - .then(function() { - // Rotate image. - return gallery.waitAndClickElement(appId, '.rotate_right'); - }) - .then(function() { - // Confirm that edited image has been saved. - return gallery.waitForAFile( - volumeType, 'My Desktop Background - Edited.png'); - }) - .then(function() { - // Confirm overwrite original checkbox is disabled and not checked. - return gallery.waitForElementStyles( - appId, '.overwrite-original[disabled]:not([checked])', ['cursor']); - }) - .then(function(result) { - // Ensure that checkbox cursor style takes auto state when disabled. - // https://crbug.com/888464 - chrome.test.assertEq('auto', result.styles.cursor); - // Go back to the slide mode. - return gallery.waitAndClickElement(appId, 'button.edit'); - }) - .then(function() { - // Confirm current image is My Desktop Background - Edited.png. - return gallery.waitForSlideImage( - appId, 600, 800, 'My Desktop Background - Edited'); - }) - .then(function() { - // Move to My Desktop Background.png. Switching to other image is - // required to end edit session of the edited image. - return gallery.waitAndClickElement(appId, '.arrow.right'); - }) - .then(function() { - // Confirm current image has changed to another image. - return gallery.waitForSlideImage( - appId, 800, 600, 'My Desktop Background'); - }) - .then(function() { - // Back to the edited image. - return gallery.waitAndClickElement(appId, '.arrow.left'); - }) - .then(function() { - // Confirm current image is switched to My Desktop Background - - // Edited.png. - return gallery.waitForSlideImage( - appId, 600, 800, 'My Desktop Background - Edited'); - }) - .then(function() { - // Click edit button again. - return gallery.waitAndClickElement(appId, 'button.edit'); - }) - .then(function() { - // Confirm overwrite original checkbox is enabled and not checked. - return gallery.waitForElement( - appId, '.overwrite-original:not([checked]):not([disabled])'); - }); -} - -/** - * The rotateImage test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.rotateImageOnDownloads = function() { - return rotateImage('local', 'downloads'); -}; - -/** - * The rotateImage test for Google Drive. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.rotateImageOnDrive = function() { - return rotateImage('drive', 'drive'); -}; - -/** - * The cropImage test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.cropImageOnDownloads = function() { - return cropImage('local', 'downloads'); -}; - -/** - * The cropImage test for Google Drive. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.cropImageOnDrive = function() { - return cropImage('drive', 'drive'); -}; - -/** - * The exposureImage test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.exposureImageOnDownloads = function() { - return exposureImage('local', 'downloads'); -}; - -/** - * The exposureImage test for Google Drive. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.exposureImageOnDrive = function() { - return exposureImage('drive', 'drive'); -}; - -/** - * The resize test for Downloas. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.resizeImageOnDownloads = function() { - return resizeImage('local', 'downloads'); -}; - -/** - * The resize test for Google Drive - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.resizeImageOnDrive = function() { - return resizeImage('drive', 'drive'); -}; - -/** - * The enableDisableOverwriteOriginalCheckbox test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.enableDisableOverwriteOriginalCheckboxOnDownloads = function() { - return enableDisableOverwriteOriginalCheckbox('local', 'downloads'); -}; - -/** - * The enableDisableOverwriteOriginalCheckbox test for Drive. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.enableDisableOverwriteOriginalCheckboxOnDrive = function() { - return enableDisableOverwriteOriginalCheckbox('drive', 'drive'); -}; - -/** - * The deleteImageWhileEditing test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.deleteImageWhileEditingOnDownloads = function() { - return deleteImageWhileEditing('local', 'downloads'); -}; - -/** - * The deleteImageWhileEditing test for Drive. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.deleteImageWhileEditingOnDrive = function() { - return deleteImageWhileEditing('drive', 'drive'); -}; \ No newline at end of file
diff --git a/ui/file_manager/integration_tests/gallery/slide_mode.js b/ui/file_manager/integration_tests/gallery/slide_mode.js deleted file mode 100644 index 7417cfb..0000000 --- a/ui/file_manager/integration_tests/gallery/slide_mode.js +++ /dev/null
@@ -1,334 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -'use strict'; - -/** - * Runs a test to traverse images in the slide mode. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled with on success. - */ -function traverseSlideImages(testVolumeName, volumeType) { - var testEntries = [ENTRIES.desktop, ENTRIES.image3]; - var launchedPromise = launch( - testVolumeName, volumeType, testEntries, testEntries.slice(0, 1)); - var appId; - return launchedPromise.then(function(args) { - appId = args.appId; - return gallery.waitForElement(appId, '.gallery[mode="slide"]'); - }).then(function() { - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }).then(function() { - return gallery.waitAndClickElement(appId, '.arrow.right'); - }).then(function() { - return gallery.waitForSlideImage(appId, 640, 480, 'image3'); - }).then(function() { - return gallery.waitAndClickElement(appId, '.arrow.right'); - }).then(function() { - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }); -} - -/** - * Runs a test to traverse the thumbnails in the slide mode. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled on success. - */ -function traverseSlideThumbnails(testVolumeName, volumeType) { - var testEntries = [ENTRIES.desktop, ENTRIES.image3]; - var launchedPromise = launch( - testVolumeName, volumeType, testEntries, [ENTRIES.desktop]); - var appId; - return launchedPromise.then(function(args) { - appId = args.appId; - return gallery.waitForElement(appId, '.gallery[mode="slide"]'); - }).then(function() { - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }).then(function() { - return gallery.waitAndClickElement(appId, '#thumbnail-1'); - }).then(function() { - return gallery.waitForSlideImage(appId, 640, 480, 'image3'); - }).then(function() { - return gallery.waitAndClickElement(appId, '#thumbnail-0'); - }).then(function() { - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }); -} - -/** - * Runs a test to rename an image. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled with on success. - */ -function renameImage(testVolumeName, volumeType) { - var launchedPromise = launch( - testVolumeName, volumeType, [ENTRIES.desktop]); - var appId; - return launchedPromise.then(function(args) { - appId = args.appId; - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }).then(function() { - return gallery.changeNameAndWait(appId, 'New Image Name'); - }).then(function() { - return gallery.waitForAFile(volumeType, 'New Image Name.png'); - }); -} - -/** - * Runs a test to delete an image. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled with on success. - */ -function deleteImage(testVolumeName, volumeType) { - var launchedPromise = launch( - testVolumeName, volumeType, [ENTRIES.desktop]); - var appId; - return launchedPromise - .then(function(args) { - appId = args.appId; - return gallery.waitForSlideImage( - appId, 800, 600, 'My Desktop Background'); - }) - .then(function() { - return gallery.waitAndClickElement(appId, 'button.delete'); - }) - .then(function() { - return gallery.waitAndClickElement(appId, '.cr-dialog-ok'); - }) - .then(function() { - return repeatUntil(function() { - return gallery.getFilesUnderVolume(volumeType, ['New Image Name.png']) - .then(function(urls) { - if (urls.length == 0) { - return true; - } - return pending('"New Image Name.png" is still there.'); - }); - }); - }); -} - -/** - * Runs test to check availability of share button. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @param {boolean} available True if share button should be available in test. - * @return {Promise} Promise to be fulfilled with on success. - */ -function checkAvailabilityOfShareButton(testVolumeName, volumeType, available) { - var appId; - return launch( - testVolumeName, volumeType, [ENTRIES.desktop]).then(function(args) { - appId = args.appId; - // Wait until UI has been initialized. - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }).then(function() { - return gallery.waitForElement(appId, - 'button.share' + (available ? ':not([disabled])' : '[disabled]')); - }); -} - - -/** - * Ensures edit and print buttons are available for images, disabled for video. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.checkAvailabilityOfEditAndPrintButtons = function() { - var launchedPromise = launch( - 'local', 'downloads', [ENTRIES.image3, ENTRIES.world], [ENTRIES.image3]); - var appId; - return launchedPromise - .then(function(result) { - appId = result.appId; - // The buttons are disabled in the static gallery.html DOM, so wait for - // the image to be fully loaded before querying the button state. - return gallery.waitForSlideImage(appId, 640, 480, 'image3'); - }) - .then(function() { - return gallery.callRemoteTestUtil( - 'queryAllElements', appId, - ['#top-toolbar > div > button.edit,button.print']); - }) - .then(function(results) { - chrome.test.assertEq(2, results.length); - chrome.test.assertFalse('disabled' in results[0].attributes); - chrome.test.assertFalse('disabled' in results[0].attributes); - - // Switch to the video. - return gallery.waitAndClickElement(appId, '.arrow.right'); - }) - .then(function() { - return gallery.waitForElement(appId, 'video'); - }) - .then(function() { - return gallery.callRemoteTestUtil( - 'queryAllElements', appId, - ['#top-toolbar > div > button.edit,button.print']); - }) - .then(function(results) { - chrome.test.assertEq(2, results.length); - chrome.test.assertTrue('disabled' in results[0].attributes); - chrome.test.assertTrue('disabled' in results[0].attributes); - return true; - }); -}; - -/** - * The traverseSlideImages test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.traverseSlideImagesOnDownloads = function() { - return traverseSlideImages('local', 'downloads'); -}; - -/** - * The traverseSlideImages test for Google Drive. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.traverseSlideImagesOnDrive = function() { - return traverseSlideImages('drive', 'drive'); -}; - -/** -* The traverseSlideThumbnails test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.traverseSlideThumbnailsOnDownloads = function() { - return traverseSlideThumbnails('local', 'downloads'); -}; - -/** -* The traverseSlideThumbnails test for Drive. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.traverseSlideThumbnailsOnDrive = function() { - return traverseSlideThumbnails('drive', 'drive'); -}; - -/** - * The renameImage test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.renameImageOnDownloads = function() { - return renameImage('local', 'downloads'); -}; - -/** - * The renameImage test for Google Drive. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.renameImageOnDrive = function() { - return renameImage('drive', 'drive'); -}; - -/** - * The deleteImage test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.deleteImageOnDownloads = function() { - return deleteImage('local', 'downloads'); -}; - -/** - * The deleteImage test for Google Drive. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.deleteImageOnDrive = function() { - return deleteImage('drive', 'drive'); -}; - -/** - * The checkAvailabilityOfShareButton test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.checkAvailabilityOfShareButtonOnDownloads = function() { - return checkAvailabilityOfShareButton( - 'local', 'downloads', false /* not available */); -}; - -/** - * The checkAvailabilityOfShareButton test for Google Drive. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.checkAvailabilityOfShareButtonOnDrive = function() { - return checkAvailabilityOfShareButton('drive', 'drive', true /* available */); -}; - -/** - * Tests activating a video (in slide mode) when double clicked from thumbnail - * mode. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.activateVideoFromThumbnailMode = function() { - // Launch with an image and a video. Start with the image selected so there's - // less work before switching to thumbnail mode. - var launchedPromise = launch( - 'local', 'downloads', [ENTRIES.image3, ENTRIES.world], [ENTRIES.image3]); - var appId; - return launchedPromise - .then(function(result) { - appId = result.appId; - return gallery.waitForElement(appId, '.gallery[mode="slide"]'); - }) - .then(function() { - // Switch to thumbnail mode. - return gallery.waitAndClickElement(appId, 'button.mode'); - }) - .then(function() { - return gallery.waitForElement( - appId, '.thumbnail-view > ul > li > .video'); - }) - .then(function() { - return gallery.callRemoteTestUtil( - 'queryAllElements', appId, ['.thumbnail-view > ul > li']); - }) - .then(function(results) { - // Confirm the video (and the image) appear in the thumbnail view. - chrome.test.assertEq(2, results.length); - chrome.test.assertEq('image3.jpg', results[0].attributes.title); - chrome.test.assertEq('world.ogv', results[1].attributes.title); - return gallery.callRemoteTestUtil( - 'queryAllElements', appId, - ['.thumbnail-view > ul > li > div.image.frame']); - }) - .then(function(results) { - // Confirm that the video has the video class (and the image does not). - chrome.test.assertEq(2, results.length); - chrome.test.assertTrue( - results[0].attributes.class.split(' ').indexOf('video') == -1); - chrome.test.assertTrue( - results[1].attributes.class.split(' ').indexOf('video') >= 0); - - // Activate the video with a double-click. - return gallery.callRemoteTestUtil( - 'fakeMouseDoubleClick', appId, - ['.thumbnail-view > ul > li > div.video.frame + .selection']); - }) - .then(function() { - // A <video> should appear. - return gallery.waitForElement(appId, 'video'); - }) - .then(function() { - return gallery.callRemoteTestUtil('queryAllElements', appId, ['video']); - }) - .then(function(results) { - chrome.test.assertEq(1, results.length); - chrome.test.assertTrue('autoplay' in results[0].attributes); - return true; - }); -};
diff --git a/ui/file_manager/integration_tests/gallery/slideshow.js b/ui/file_manager/integration_tests/gallery/slideshow.js deleted file mode 100644 index d77a11ad..0000000 --- a/ui/file_manager/integration_tests/gallery/slideshow.js +++ /dev/null
@@ -1,157 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -'use strict'; - -/** - * Runs a test to ensure slideshow traverses images automatically - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled on success. - */ -function slideshowTraversal(testVolumeName, volumeType) { - // Setup - var testEntries = [ENTRIES.desktop, ENTRIES.image3]; - var launchedPromise = launch( - testVolumeName, volumeType, testEntries, testEntries.slice(0, 1)); - var appId; - return launchedPromise.then(function(args) { - appId = args.appId; - return gallery.waitForElement(appId, '.gallery[mode="slide"]'); - }).then(function() { - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }).then(function() { - // Check: There is a pause / play button - return gallery.waitForElement(appId, '.slideshow-play:not([hidden])'); - }).then(function() { - // Start slideshow. - return gallery.waitAndClickElement(appId, '.slideshow.icon-button'); - }).then(function() { - // Image will change automatically. - return gallery.waitForSlideImage(appId, 640, 480, 'image3'); - }).then(function() { - // Will rollover to the start again. - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }); -} - -/** - * Runs a test to ensure pausing slideshow stops images rolling - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled on success. - */ -function stopStartSlideshow(testVolumeName, volumeType) { - var testEntries = [ENTRIES.desktop, ENTRIES.image3]; - var launchedPromise = launch( - testVolumeName, volumeType, testEntries, testEntries.slice(0, 1)); - var appId; - return launchedPromise.then(function(args) { - appId = args.appId; - return gallery.waitForElement(appId, '.gallery[mode="slide"]'); - }).then(function() { - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }).then(function() { - // Start slideshow. - return gallery.waitAndClickElement(appId, '.slideshow.icon-button'); - }).then(function() { - // Image will change automatically. - return gallery.waitForSlideImage(appId, 640, 480, 'image3'); - }).then(function() { - // Pause. - return gallery.waitAndClickElement(appId, '.slideshow-play'); - }).then(function() { - // Wait some time. - return wait(3000); - }).then(function() { - // Check image is the same. - return gallery.waitForSlideImage(appId, 640, 480, 'image3'); - }).then(function() { - // Play. - return gallery.waitAndClickElement(appId, '.slideshow-play'); - }).then(function() { - // Check image changes. - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }); -} - -/** - * Runs a test to ensure there is not a play/pause button when one image - * is selected. - * - * @param {string} testVolumeName Test volume name passed to the addEntries - * function. Either 'drive' or 'local'. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {Promise} Promise to be fulfilled on success. - */ -function oneImageSlideshowNoPauseButton(testVolumeName, volumeType) { - const testEntries = [ENTRIES.desktop]; - const launchedPromise = launch( - testVolumeName, volumeType, testEntries); - let appId; - return launchedPromise.then(args => { - appId = args.appId; - return gallery.waitForElement(appId, '.gallery[mode="slide"]'); - }).then(() => { - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }).then(() => { - // Start slideshow. - return gallery.waitAndClickElement(appId, '.slideshow.icon-button'); - }).then(() => { - // Check: There is no pause / play button - return gallery.waitForElement(appId, '.slideshow-play[hidden]'); - }); -} - -/** - * The slideshowTraversal test for Downloads. - * @return {Promise} Promise to be fulfilled on success. - */ -testcase.slideshowTraversalOnDownloads = function() { - return slideshowTraversal('local', 'downloads'); -}; - -/** - * The slideshowTraversal test for Drive. - * @return {Promise} Promise to be fulfilled on success. - */ -testcase.slideshowTraversalOnDrive = function() { - return slideshowTraversal('drive', 'drive'); -}; - -/** - * The stopStartSlideshow test for Downloads. - * @return {Promise} Promise to be fulfilled on success. - */ -testcase.stopStartSlideshowOnDownloads = function() { - return stopStartSlideshow('local', 'downloads'); -}; - -/** - * The stopStartSlideshow test for Drive. - * @return {Promise} Promise to be fulfilled on success. - */ -testcase.stopStartSlideshowOnDrive = function() { - return stopStartSlideshow('drive', 'drive'); -}; - -/** - * The oneImageSlideshowNoPauseButton test for Downloads. - * @return {Promise} Promise to be fulfilled on success. - */ -testcase.oneImageSlideshowNoPauseButtonOnDownloads = () => { - return oneImageSlideshowNoPauseButton('local', 'downloads'); -}; - -/** - * The oneImageSlideshowNoPauseButton test for Drive. - * @return {Promise} Promise to be fulfilled on success. - */ -testcase.oneImageSlideshowNoPauseButtonOnDrive = () => { - return oneImageSlideshowNoPauseButton('drive', 'drive'); -}; \ No newline at end of file
diff --git a/ui/file_manager/integration_tests/gallery/thumbnail_mode.js b/ui/file_manager/integration_tests/gallery/thumbnail_mode.js deleted file mode 100644 index 597f4db..0000000 --- a/ui/file_manager/integration_tests/gallery/thumbnail_mode.js +++ /dev/null
@@ -1,434 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * Additional test image entry. - */ -ENTRIES.image4 = new TestEntryInfo({ - type: EntryType.FILE, - sourceFileName: 'image3.jpg', - targetPath: 'image4.jpg', - mimeType: 'image/jpeg', - lastModifiedTime: 'Jan 18, 2038, 1:02 AM', - nameText: 'image3.jpg', - sizeText: '3 KB', - typeText: 'JPEG image' -}); - -/** - * Renames an image in thumbnail mode and confirms that thumbnail of renamed - * image is successfully updated. - * @param {string} testVolumeName Test volume name. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {!Promise} Promise to be fulfilled with on success. - */ -function renameImageInThumbnailMode(testVolumeName, volumeType) { - var launchedPromise = launch(testVolumeName, volumeType, - [ENTRIES.desktop, ENTRIES.image3], [ENTRIES.desktop]); - var appId; - return launchedPromise.then(function(result) { - // Confirm initial state after the launch. - appId = result.appId; - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }).then(function() { - // Goes to thumbnail mode. - return gallery.waitAndClickElement(appId, 'button.mode'); - }).then(function() { - return gallery.selectImageInThumbnailMode(appId, 'image3.jpg'); - }).then(function(result) { - chrome.test.assertTrue(result); - return gallery.callRemoteTestUtil('changeName', appId, ['New Image Name']); - }).then(function() { - // Assert that rename had done successfully. - return gallery.waitForAFile(volumeType, 'New Image Name.jpg'); - }).then(function() { - return gallery.selectImageInThumbnailMode( - appId, 'My Desktop Background.png'); - }).then(function(result) { - chrome.test.assertTrue(result); - return gallery.callRemoteTestUtil('queryAllElements', appId, - ['.thumbnail-view > ul > li.selected']); - }).then(function(result) { - // Only My Desktop Background.png is selected. - chrome.test.assertEq(1, result.length); - - chrome.test.assertEq('My Desktop Background.png', - result[0].attributes['title']); - return gallery.callRemoteTestUtil('queryAllElements', appId, - ['.thumbnail-view > ul > li:not(.selected)']); - }).then(function(result) { - // Confirm that thumbnail of renamed image has updated. - chrome.test.assertEq(1, result.length); - chrome.test.assertEq('New Image Name.jpg', - result[0].attributes['title']); - }); -} - -/** - * Delete all images in thumbnail mode and confirm that no-images error banner - * is shown. - * @param {string} testVolumeName Test volume name. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @param {string} operation How the test do delete operation. - * @return {!Promise} Promise to be fulfilled with on success. - */ -function deleteAllImagesInThumbnailMode(testVolumeName, volumeType, operation) { - var launchedPromise = launch(testVolumeName, volumeType, - [ENTRIES.desktop, ENTRIES.image3]); - var appId; - return launchedPromise.then(function(result) { - appId = result.appId; - // Wait until current mode is set to thumbnail mode. - return gallery.waitForElement(appId, '.gallery[mode="thumbnail"]'); - }).then(function() { - switch (operation) { - case 'mouse': - // Click delete button. - return gallery.waitAndClickElement(appId, 'button.delete'); - break; - case 'enter-key': - // Press enter key on delete button. - return gallery.waitForElement( - appId, 'button.delete').then(function() { - return gallery.callRemoteTestUtil( - 'focus', appId, ['button.delete']); - }).then(function() { - return gallery.callRemoteTestUtil( - 'fakeKeyDown', appId, - ['button.delete', 'Enter', false, false, false]); - }).then(function() { - // When user has pressed enter key on button, click event is - // dispatched after keydown event. - return gallery.callRemoteTestUtil( - 'fakeEvent', appId, ['button.delete', 'click']); - }); - break; - case 'delete-key': - // Press delete key. - return gallery.callRemoteTestUtil( - 'fakeKeyDown', appId, ['body', 'Delete', false, false, false]); - break; - } - }).then(function(result) { - chrome.test.assertTrue(!!result); - // Wait and click delete button of confirmation dialog. - return gallery.waitAndClickElement(appId, '.cr-dialog-ok'); - }).then(function(result) { - chrome.test.assertTrue(!!result); - // Wait until error banner is shown. - return gallery.waitForElement(appId, '.gallery[error] .error-banner'); - }).then(function() { - // Check: The edit name field should hide. - return gallery.waitForElement(appId, '#rename-input[hidden]'); - }); -} - -/** - * Clicks an empty space in thumbnail view and confirms that current selection - * is unselected. - * @param {string} testVolumeName Test volume name. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {!Promise} Promise to be fulfilled with on success. - */ -function emptySpaceClickUnselectsInThumbnailMode(testVolumeName, volumeType) { - var launchedPromise = launch(testVolumeName, volumeType, - [ENTRIES.desktop, ENTRIES.image3], [ENTRIES.desktop]); - var appId; - return launchedPromise.then(function(result) { - // Confirm initial state after the launch. - appId = result.appId; - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }).then(function(result) { - // Switch to thumbnail mode. - return gallery.waitAndClickElement(appId, 'button.mode'); - }).then(function(result) { - // Confirm My Desktop Background.png is selected in thumbnail view. - return gallery.callRemoteTestUtil('queryAllElements', appId, - ['.thumbnail-view > ul > li.selected']); - }).then(function(results) { - chrome.test.assertEq(1, results.length); - chrome.test.assertEq('My Desktop Background.png', - results[0].attributes['title']); - // Click empty space of thumbnail view. - return gallery.waitAndClickElement(appId, '.thumbnail-view > ul'); - }).then(function(result) { - // Confirm no image is selected. - return gallery.callRemoteTestUtil('queryAllElements', appId, - ['.thumbnail-view > ul > li.selected']); - }).then(function(results) { - chrome.test.assertEq(0, results.length); - // Confirm delete button is disabled. - return gallery.waitForElement(appId, 'button.delete[disabled]'); - }).then(function(result) { - // Confirm slideshow button is disabled. - return gallery.waitForElement(appId, 'button.slideshow[disabled]'); - }).then(function() { - // Check: The edit name field should hide. - return gallery.waitForElement(appId, '#rename-input[hidden]'); - }).then(function() { - // Switch back to slide mode by clicking mode button. - return gallery.waitAndClickElement(appId, 'button.mode:not([disabled])'); - }).then(function(result) { - // First image in the image set (image3) should be shown. - return gallery.waitForSlideImage(appId, 640, 480, 'image3'); - }); -} - -/** - * Selects multiple images with shift key. - * @param {string} testVolumeName Test volume name. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {!Promise} Promise to be fulfilled with on success. - */ -function selectMultipleImagesWithShiftKey(testVolumeName, volumeType) { - var launchedPromise = launch(testVolumeName, volumeType, - [ENTRIES.image3, ENTRIES.image4, ENTRIES.desktop], [ENTRIES.image3]); - var appId; - return launchedPromise.then(function(result) { - // Confirm initial state after the launch. - appId = result.appId; - return gallery.waitForSlideImage(appId, 640, 480, 'image3'); - }).then(function() { - // Swith to thumbnail mode. - return gallery.waitAndClickElement(appId, 'button.mode'); - }).then(function() { - // Confirm that image3 is selected first: [1] 2 3 - return gallery.callRemoteTestUtil('queryAllElements', appId, - ['.thumbnail-view > ul > li.selected']); - }).then(function(results) { - chrome.test.assertEq(1, results.length); - chrome.test.assertEq('image3.jpg', - results[0].attributes['title']); - - // Press Right key with shift. - return gallery.fakeKeyDown( - appId, '.thumbnail-view', 'ArrowRight', false, true /* Shift */, false); - }).then(function() { - // Confirm 2 images are selected: [1][2] 3 - return gallery.callRemoteTestUtil('queryAllElements', appId, - ['.thumbnail-view > ul > li.selected']); - }).then(function(results) { - chrome.test.assertEq(2, results.length); - chrome.test.assertEq('image3.jpg', results[0].attributes['title']); - chrome.test.assertEq('image4.jpg', results[1].attributes['title']); - - // Press Right key with shift. - return gallery.fakeKeyDown( - appId, '.thumbnail-view', 'ArrowRight', false, true /* Shift */, false); - }).then(function() { - // Confirm 3 images are selected: [1][2][3] - return gallery.callRemoteTestUtil('queryAllElements', appId, - ['.thumbnail-view > ul > li.selected']); - }).then(function(results) { - chrome.test.assertEq(3, results.length); - chrome.test.assertEq('image3.jpg', results[0].attributes['title']); - chrome.test.assertEq('image4.jpg', results[1].attributes['title']); - chrome.test.assertEq('My Desktop Background.png', - results[2].attributes['title']); - - // Press Left key with shift. - return gallery.fakeKeyDown( - appId, '.thumbnail-view', 'ArrowLeft', false, true /* Shift */, false); - }).then(function() { - // Confirm 2 images are selected: [1][2] 3 - return gallery.callRemoteTestUtil('queryAllElements', appId, - ['.thumbnail-view > ul > li.selected']); - }).then(function(results) { - chrome.test.assertEq(2, results.length); - chrome.test.assertEq('image3.jpg', results[0].attributes['title']); - chrome.test.assertEq('image4.jpg', results[1].attributes['title']); - - // Press Right key without shift. - return gallery.fakeKeyDown( - appId, '.thumbnail-view', 'ArrowRight', false, false, false); - }).then(function() { - // Confirm only the last image is selected: 1 2 [3] - return gallery.callRemoteTestUtil('queryAllElements', appId, - ['.thumbnail-view > ul > li.selected']); - }).then(function(results) { - chrome.test.assertEq(1, results.length); - chrome.test.assertEq('My Desktop Background.png', - results[0].attributes['title']); - }); -} - -/** - * Selects all images in thumbnail mode after deleted an image in slide mode. - * @param {string} testVolumeName Test volume name. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {!Promise} Promise to be fulfilled with on success. - */ -function selectAllImagesAfterImageDeletionOnDownloads( - testVolumeName, volumeType) { - var launchedPromise = launch(testVolumeName, volumeType, - [ENTRIES.image3, ENTRIES.image4, ENTRIES.desktop], [ENTRIES.image3]); - var appId; - return launchedPromise.then(function(result) { - appId = result.appId; - // Confirm initial state after launch. - return gallery.waitForSlideImage(appId, 640, 480, 'image3'); - }).then(function() { - // Delete an image. - return gallery.waitAndClickElement(appId, 'button.delete'); - }).then(function() { - // Press OK button in confirmation dialog. - return gallery.waitAndClickElement(appId, '.cr-dialog-ok'); - }).then(function() { - // Confirm the state after the image is deleted. - return gallery.waitForSlideImage(appId, 640, 480, 'image4'); - }).then(function() { - // Press thumbnail mode button. - return gallery.waitAndClickElement(appId, 'button.mode'); - }).then(function() { - // Confirm mode has been changed to thumbnail mode. - return gallery.waitForElement(appId, '.gallery[mode="thumbnail"]'); - }).then(function() { - // Press Ctrl+A to select all images. - return gallery.fakeKeyDown(appId, '.thumbnail-view', - 'a', true /* Ctrl*/, false /* Shift */, false /* Alt */); - }).then(function() { - // Confirm that 2 images are selected. - return gallery.callRemoteTestUtil('queryAllElements', appId, - ['.thumbnail-view > ul > li.selected']); - }).then(function(results) { - chrome.test.assertEq(2, results.length); - }); -} - -/** - * Selects all images in thumbnail mode with shift key when nothing - * is selected. (crbug.com/900619) - * @param {string} testVolumeName Test volume name. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {!Promise} Promise to be fulfilled with on success. - */ -function shiftSelectFromNothingSelected( - testVolumeName, volumeType) { - const launchedPromise = launch(testVolumeName, volumeType, - [ENTRIES.desktop, ENTRIES.image3], [ENTRIES.desktop]); - let appId; - return launchedPromise.then((result) => { - // Confirm initial state after the launch. - appId = result.appId; - return gallery.waitForSlideImage(appId, 800, 600, 'My Desktop Background'); - }).then(() => { - // Switch to thumbnail mode. - return gallery.waitAndClickElement(appId, 'button.mode'); - }).then(() => { - // Confirm something is selected. - return gallery.waitForElement(appId, '.thumbnail-view > ul > li.selected'); - }).then(() => { - // Click empty space of thumbnail view. - return gallery.waitAndClickElement(appId, '.thumbnail-view > ul'); - }).then(() => { - // Confirm no image is selected. - return gallery.waitForElementLost(appId, '.thumbnail-view > ul > li.selected'); - }).then(() => { - // Select shift all elements - return gallery.callRemoteTestUtil( - 'fakeMouseClick', appId, - ['.thumbnail-view > ul > li:last-child > .selection.frame', - {alt:false, shift:true, ctrl:false}]); - }).then(() => { - // Check: The edit name field should show. - return gallery.waitForElement(appId, '#rename-input:not([hidden])'); - }).then((result) => { - // Validate that the proper number of items are selected and displayed - chrome.test.assertEq(result.value, '2 items selected'); - }); -} - -/** - * Rename test in thumbnail mode for Downloads. - * @return {!Promise} Promise to be fulfilled with on success. - */ -testcase.renameImageInThumbnailModeOnDownloads = function() { - return renameImageInThumbnailMode('local', 'downloads'); -}; - -/** - * Rename test in thumbnail mode for Drive. - * @return {!Promise} Promise to be fulfilled with on success. - */ -testcase.renameImageInThumbnailModeOnDrive = function() { - return renameImageInThumbnailMode('drive', 'drive'); -}; - -/** - * Delete all images test in thumbnail mode for Downloads. - * @return {!Promise} Promise to be fulfilled with on success. - */ -testcase.deleteAllImagesInThumbnailModeOnDownloads = function() { - return deleteAllImagesInThumbnailMode('local', 'downloads', 'mouse'); -}; - -/** - * Delete all images test in thumbnail mode for Drive. - * @return {!Promise} Promise to be fulfilled with on success. - */ -testcase.deleteAllImagesInThumbnailModeOnDrive = function() { - return deleteAllImagesInThumbnailMode('drive', 'drive', 'mouse'); -}; - -/** - * Delete all images test in thumbnail mode by pressing Enter key on Delete - * button. - * @return {!Promise} Promise to be fulfilled with on success. - */ -testcase.deleteAllImagesInThumbnailModeWithEnterKey = function() { - return deleteAllImagesInThumbnailMode('local', 'downloads', 'enter-key'); -}; - -/** - * Delete all images test in thumbnail mode with Delete key. - * @return {!Promise} Promise to be fulfilled with on success. - */ -testcase.deleteAllImagesInThumbnailModeWithDeleteKey = function() { - return deleteAllImagesInThumbnailMode('local', 'downloads', 'delete-key'); -}; - -/** - * Empty space click unselects current selection in thumbnail mode for - * Downloads. - * @return {!Promise} Promise to be fulfilled with on success. - */ -testcase.emptySpaceClickUnselectsInThumbnailModeOnDownloads = function() { - return emptySpaceClickUnselectsInThumbnailMode('local', 'downloads'); -}; - -/** - * Empty space click unselects current selection in thumbnail mode for Drive. - * @return {!Promise} Promise to be fulfilled with on success. - */ -testcase.emptySpaceClickUnselectsInThumbnailModeOnDrive = function() { - return emptySpaceClickUnselectsInThumbnailMode('drive', 'drive'); -}; - -/** - * Selects multiple images with shift key in Downloads. - * @return {!Promise} Promise to be fulfilled with on success. - */ -testcase.selectMultipleImagesWithShiftKeyOnDownloads = function() { - return selectMultipleImagesWithShiftKey('local', 'downloads'); -}; - -/** - * Selects all images in thumbnail mode after deleted an image in slide mode. - * @return {!Promise} Promise to be fulfilled with on success. - */ -testcase.selectAllImagesAfterImageDeletionOnDownloads = function() { - return selectAllImagesAfterImageDeletionOnDownloads('local', 'downloads'); -}; - -/** - * Selects all images from nothing selected and the edit title is updated. - * @returns {!Promise} Promise to be fulfilled with on success. -*/ -testcase.shiftSelectFromNothingSelectedOnDownloads = () => { - return shiftSelectFromNothingSelected('local', 'downloads'); -}; - -testcase.shiftSelectFromNothingSelectedOnDrive = () => { - return shiftSelectFromNothingSelected('drive', 'drive'); -};
diff --git a/ui/file_manager/integration_tests/gallery_test_manifest.json b/ui/file_manager/integration_tests/gallery_test_manifest.json deleted file mode 100644 index 51bdb70..0000000 --- a/ui/file_manager/integration_tests/gallery_test_manifest.json +++ /dev/null
@@ -1,22 +0,0 @@ -{ - // chrome-extension://ejhcmmdhhpdhhgmifplfmjobgegbibkn/ - "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2fEDh/TfZD2T2rabtuF922S/6n6Yby1EbjYFEaMExJrir3KJOxOMbUMF+6xChlweGnHBy7CQPtzMfYfjmftHpN7SDWVRRjcAb8vu8pyEcaEkdFQPkij5k8L9v6d7uoygbEsak/IOTYNWTZDrqPc47ypuEM4AG7/bf8NNVDq7EYlj1Lq4AfboEujMb7iif5VNRSfCTUcfddbLxk/J6QyomYlSpssKBCw1LRDnM+aRMFt70ElTozf3Oho7/22P7BXEYPr6YHdZotzb2HyBZ6pV6W9LAhuhsbmNByj0s9C+UmiqeyLoL0ALlqTydW7zDSYGHDRRDZLVMIgrb9D1ZQcUnwIDAQAB", - "name": "Gallery test extension", - "version": "0.1", - "manifest_version": 2, - "description": "Gallery test extension", - "web_accessible_resources": ["*"], - "background": { - "scripts": [ - "test_util.js", - "remote_call.js", - "gallery/background.js", - "gallery/open_image_files.js", - "gallery/slide_mode.js", - "gallery/slideshow.js", - "gallery/thumbnail_mode.js", - "gallery/photo_editor.js" - ]}, - "incognito" : "split", - "permissions": ["commandLinePrivate"] -}
diff --git a/ui/file_manager/integration_tests/remote_call.js b/ui/file_manager/integration_tests/remote_call.js index c8e7162..d598d051 100644 --- a/ui/file_manager/integration_tests/remote_call.js +++ b/ui/file_manager/integration_tests/remote_call.js
@@ -668,86 +668,3 @@ appId, `/${rootLabel}${path}`); } } - -/** - * Class to manipulate the window in the remote extension. - */ -class RemoteCallGallery extends RemoteCall { - /** - * Waits until the expected image is shown. - * - * @param {string} appId App window Id. - * @param {number} width Expected width of the image. - * @param {number} height Expected height of the image. - * @param {string|null} name Expected name of the image. - * @return {Promise} Promsie to be fulfilled when the check is passed. - */ - waitForSlideImage(appId, width, height, name) { - const expected = {}; - if (width) { - expected.width = width; - } - if (height) { - expected.height = height; - } - if (name) { - expected.name = name; - } - - const caller = getCaller(); - return repeatUntil(async () => { - const query = '.gallery[mode="slide"] .image-container > .image'; - const [nameBox, image] = await Promise.all([ - this.waitForElement(appId, '#rename-input'), - this.waitForElementStyles(appId, query, ['any']) - ]); - const actual = {}; - if (width && image) { - actual.width = image.imageWidth; - } - if (height && image) { - actual.height = image.imageHeight; - } - if (name && nameBox) { - actual.name = nameBox.value; - } - - if (!chrome.test.checkDeepEq(expected, actual)) { - return pending( - caller, 'Slide mode state, expected is %j, actual is %j.', expected, - actual); - } - return actual; - }); - } - - async changeNameAndWait(appId, newName) { - await this.callRemoteTestUtil('changeName', appId, [newName]); - return this.waitForSlideImage(appId, 0, 0, newName); - } - - /** - * Waits for the "Press Enter" message. - * - * @param {string} appId App window Id. - * @return {Promise} Promise to be fulfilled when the element appears. - */ - async waitForPressEnterMessage(appId) { - const element = await this.waitForElement(appId, '.prompt-wrapper .prompt'); - chrome.test.assertEq('Press Enter when done', element.text.trim()); - } - - /** - * Shorthand for selecting an image in thumbnail mode. - * @param {string} appId App window Id. - * @param {string} name File name to be selected. - * @return {!Promise<boolean>} A promise which will be resolved with true if - * the thumbnail has clicked. This method does not guarantee whether the - * thumbnail has actually selected or not. - */ - selectImageInThumbnailMode(appId, name) { - return this.callRemoteTestUtil( - 'fakeMouseClick', appId, - ['.thumbnail-view > ul > li[title="' + name + '"] > .selection.frame']); - } -}
diff --git a/ui/gl/child_window_win.cc b/ui/gl/child_window_win.cc index 573a2686..c52a937 100644 --- a/ui/gl/child_window_win.cc +++ b/ui/gl/child_window_win.cc
@@ -131,7 +131,7 @@ thread_ = std::make_unique<base::Thread>("Window owner thread"); base::Thread::Options options(base::MessagePumpType::UI, 0); - thread_->StartWithOptions(options); + thread_->StartWithOptions(std::move(options)); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::NOT_SIGNALED);
diff --git a/ui/ozone/platform/wayland/common/wayland_util.cc b/ui/ozone/platform/wayland/common/wayland_util.cc index e368b4a..12b0e1170 100644 --- a/ui/ozone/platform/wayland/common/wayland_util.cc +++ b/ui/ozone/platform/wayland/common/wayland_util.cc
@@ -267,12 +267,12 @@ ui::WaylandWindow* parent_window) { DCHECK(window); DCHECK(parent_window); - DCHECK_EQ(window->buffer_scale(), parent_window->buffer_scale()); + DCHECK_EQ(window->window_scale(), parent_window->window_scale()); DCHECK_EQ(window->ui_scale(), parent_window->ui_scale()); return gfx::ScaleToRoundedRect( wl::TranslateBoundsToParentCoordinates(window->GetBounds(), parent_window->GetBounds()), - 1.0 / window->buffer_scale()); + 1.0 / window->window_scale()); } std::vector<gfx::Rect> CreateRectsFromSkPath(const SkPath& path) {
diff --git a/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc b/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc index 3f253b2d..e2d5505 100644 --- a/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc +++ b/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc
@@ -26,7 +26,7 @@ return; CreateSubsurface(); - UpdateBufferScale(false); + UpdateWindowScale(false); WaylandWindow::Show(inactive); } @@ -76,8 +76,8 @@ DCHECK(parent_window()); - // We need to make sure that buffer scale matches the parent window. - UpdateBufferScale(true); + // We need to make sure that window scale matches the parent window. + UpdateWindowScale(true); subsurface_ = root_surface()->CreateSubsurface(parent_window()->root_surface());
diff --git a/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc b/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc index d8f6da5..8459b506 100644 --- a/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc +++ b/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc
@@ -127,7 +127,8 @@ if (icon_bitmap_) { icon_surface_ = std::make_unique<WaylandSurface>(connection_, nullptr); if (icon_surface_->Initialize()) { - icon_surface_->SetBufferScale(origin_window_->buffer_scale()); + // Corresponds to actual scale factor of the origin surface. + icon_surface_->SetSurfaceBufferScale(origin_window_->window_scale()); } else { LOG(ERROR) << "Failed to create wl_surface"; icon_surface_.reset();
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context.cc b/ui/ozone/platform/wayland/host/wayland_input_method_context.cc index cca68fd..4b15883 100644 --- a/ui/ozone/platform/wayland/host/wayland_input_method_context.cc +++ b/ui/ozone/platform/wayland/host/wayland_input_method_context.cc
@@ -6,10 +6,12 @@ #include "base/bind.h" #include "base/command_line.h" +#include "base/i18n/char_iterator.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/notreached.h" #include "base/strings/string_util.h" +#include "base/strings/utf_offset_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "build/chromeos_buildflags.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -200,8 +202,95 @@ void WaylandInputMethodContext::SetSurroundingText( const std::u16string& text, const gfx::Range& selection_range) { - if (text_input_) - text_input_->SetSurroundingText(text, selection_range); + if (!text_input_) + return; + + // The text length for set_surrounding_text can not be longer than the maximum + // length of wayland messages. The maximum length of the text is explicitly + // specified as 4000 in the protocol spec of text-input-unstable-v3. + static constexpr size_t kWaylandMessageDataMaxLength = 4000; + + // Convert |text| and |selection_range| into UTF8 form. + std::vector<size_t> offsets_for_adjustment = {selection_range.start(), + selection_range.end()}; + const std::string text_utf8 = + base::UTF16ToUTF8AndAdjustOffsets(text, &offsets_for_adjustment); + if (offsets_for_adjustment[0] == std::u16string::npos || + offsets_for_adjustment[1] == std::u16string::npos) { + LOG(DFATAL) << "The selection range is invalid."; + return; + } + gfx::Range selection_range_utf8 = {offsets_for_adjustment[0], + offsets_for_adjustment[1]}; + + // If the selection range in UTF8 form is longer than the maximum length of + // wayland messages, skip sending set_surrounding_text requests. + if (selection_range_utf8.length() > kWaylandMessageDataMaxLength) + return; + + surrounding_text_ = text_utf8; + + if (text_utf8.size() <= kWaylandMessageDataMaxLength) { + // We separate this case to run the function simpler and faster since this + // condition is satisfied in most cases. + surrounding_text_offset_ = 0; + text_input_->SetSurroundingText(text_utf8, selection_range_utf8); + return; + } + + // If the text in UTF8 form is longer than the maximum length of wayland + // messages while the selection range in UTF8 form is not, truncate the text + // into the limitation and adjust indices of |selection_range|. + + // Decide where to start. The truncated text should be around the selection + // range. We choose a text whose center point is same to the center of the + // selection range unless this chosen text is shorter than the maximum + // length of wayland messages because of the original text position. + uint32_t selection_range_utf8_center = + selection_range_utf8.start() + selection_range_utf8.length() / 2; + // The substring starting with |start_index| might be invalid as UTF8. + size_t start_index; + if (selection_range_utf8_center <= kWaylandMessageDataMaxLength / 2) { + // The selection range is near enough to the start point of original text. + start_index = 0; + } else if (text_utf8.size() - selection_range_utf8_center < + kWaylandMessageDataMaxLength / 2) { + // The selection range is near enough to the end point of original text. + start_index = text_utf8.size() - kWaylandMessageDataMaxLength; + } else { + // Choose a text whose center point is same to the center of the selection + // range. + start_index = + selection_range_utf8_center - kWaylandMessageDataMaxLength / 2; + } + + // Truncate the text to fit into the wayland message size and adjust indices + // of |selection_range|. Since the text is in UTF8 form, we need to adjust + // the text and selection range positions where all characters are valid. + // + // TODO(crbug.com/1214957): We should use base::i18n::BreakIterator + // to get the offsets and convert it into UTF8 form instead of using + // UTF8CharIterator. + base::i18n::UTF8CharIterator iter(text_utf8); + while (iter.array_pos() < start_index) + iter.Advance(); + size_t truncated_text_start = iter.array_pos(); + size_t truncated_text_end; + while (iter.array_pos() <= start_index + kWaylandMessageDataMaxLength) { + truncated_text_end = iter.array_pos(); + if (!iter.Advance()) + break; + } + + std::string truncated_text = text_utf8.substr( + truncated_text_start, truncated_text_end - truncated_text_start); + gfx::Range relocated_selection_range( + selection_range_utf8.start() - truncated_text_start, + selection_range_utf8.end() - truncated_text_start); + DCHECK(relocated_selection_range.IsBoundedBy( + gfx::Range(0, kWaylandMessageDataMaxLength))); + surrounding_text_offset_ = truncated_text_start; + text_input_->SetSurroundingText(truncated_text, relocated_selection_range); } void WaylandInputMethodContext::OnPreeditString( @@ -270,7 +359,31 @@ void WaylandInputMethodContext::OnDeleteSurroundingText(int32_t index, uint32_t length) { - ime_delegate_->OnDeleteSurroundingText(index, length); + // |index| and |length| are expected to be in UTF8 form, so we convert these + // into UTF16 form. + // Here, we use the surrounding text stored in SetSurroundingText which should + // be called before OnDeleteSurroundingText. + if (surrounding_text_.empty()) { + LOG(DFATAL) + << "SetSurroundingText should run before OnDeleteSurroundingText."; + return; + } + + std::vector<size_t> offsets_for_adjustment = { + surrounding_text_offset_ + index, + surrounding_text_offset_ + index + length}; + base::UTF8ToUTF16AndAdjustOffsets(surrounding_text_, &offsets_for_adjustment); + if (offsets_for_adjustment[0] == std::u16string::npos || + offsets_for_adjustment[1] == std::u16string::npos) { + LOG(DFATAL) << "The selection range for surrounding text is invalid."; + return; + } + + // Move by offset calculated in SetSurroundingText to adjust to the original + // text place. + ime_delegate_->OnDeleteSurroundingText( + offsets_for_adjustment[0], + offsets_for_adjustment[1] - offsets_for_adjustment[0]); } void WaylandInputMethodContext::OnKeysym(uint32_t keysym,
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context.h b/ui/ozone/platform/wayland/host/wayland_input_method_context.h index 912c348..395fe8d 100644 --- a/ui/ozone/platform/wayland/host/wayland_input_method_context.h +++ b/ui/ozone/platform/wayland/host/wayland_input_method_context.h
@@ -68,6 +68,12 @@ // including dead key etc. CharacterComposer character_composer_; + // Stores the parameters required for OnDeleteSurroundingText. + // The index moved by SetSurroundingText. This is byte-offset in UTF8 form. + size_t surrounding_text_offset_ = 0; + // The string in SetSurroundingText. + std::string surrounding_text_; + DISALLOW_COPY_AND_ASSIGN(WaylandInputMethodContext); };
diff --git a/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc b/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc index d73061c..4ffd6ba 100644 --- a/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_input_method_context_unittest.cc
@@ -6,10 +6,12 @@ #include <wayland-server.h> #include <memory> +#include "base/strings/utf_string_conversions.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/ime/linux/linux_input_method_context.h" #include "ui/events/event.h" +#include "ui/gfx/range/range.h" #include "ui/ozone/platform/wayland/host/wayland_input_method_context.h" #include "ui/ozone/platform/wayland/host/wayland_input_method_context_factory.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" @@ -19,6 +21,8 @@ #include "ui/ozone/platform/wayland/test/wayland_test.h" using ::testing::_; +using ::testing::DoAll; +using ::testing::Mock; using ::testing::SaveArg; using ::testing::Values; @@ -37,7 +41,9 @@ } void OnPreeditEnd() override {} void OnPreeditStart() override {} - void OnDeleteSurroundingText(int32_t index, uint32_t length) override {} + void OnDeleteSurroundingText(int32_t index, uint32_t length) override { + on_delete_surrounding_text_range_ = gfx::Range(index, index + length); + } bool was_on_commit_called() { return was_on_commit_called_; } @@ -45,9 +51,14 @@ return was_on_preedit_changed_called_; } + absl::optional<gfx::Range> on_delete_surrounding_text_range() { + return on_delete_surrounding_text_range_; + } + private: bool was_on_commit_called_ = false; bool was_on_preedit_changed_called_ = false; + absl::optional<gfx::Range> on_delete_surrounding_text_range_; DISALLOW_COPY_AND_ASSIGN(TestInputMethodContextDelegate); }; @@ -121,6 +132,133 @@ Sync(); } +TEST_P(WaylandInputMethodContextTest, SetSurroundingTextForShortText) { + std::u16string text(50, u'あ'); + gfx::Range range(20, 30); + + std::string sent_text; + gfx::Range sent_range; + EXPECT_CALL(*zwp_text_input_, SetSurroundingText(_, _)) + .WillOnce(DoAll(SaveArg<0>(&sent_text), SaveArg<1>(&sent_range))); + input_method_context_->SetSurroundingText(text, range); + connection_->ScheduleFlush(); + Sync(); + Mock::VerifyAndClearExpectations(zwp_text_input_); + // The text and range sent as wayland protocol must be same to the original + // text and range where the original text is shorter than 4000 byte. + EXPECT_EQ(sent_text, base::UTF16ToUTF8(text)); + EXPECT_EQ(sent_range, gfx::Range(60, 90)); + + // Test OnDeleteSurroundingText with this input. + zwp_text_input_v1_send_delete_surrounding_text( + zwp_text_input_->resource(), sent_range.start(), sent_range.length()); + Sync(); + absl::optional<gfx::Range> index = + input_method_context_delegate_->on_delete_surrounding_text_range(); + EXPECT_TRUE(index.has_value()); + EXPECT_EQ(index, range); +} + +TEST_P(WaylandInputMethodContextTest, SetSurroundingTextForLongText) { + std::u16string text(5000, u'あ'); + gfx::Range range(2800, 3200); + + std::string sent_text; + gfx::Range sent_range; + EXPECT_CALL(*zwp_text_input_, SetSurroundingText(_, _)) + .WillOnce(DoAll(SaveArg<0>(&sent_text), SaveArg<1>(&sent_range))); + input_method_context_->SetSurroundingText(text, range); + connection_->ScheduleFlush(); + Sync(); + Mock::VerifyAndClearExpectations(zwp_text_input_); + // The text sent as wayland protocol must be at most 4000 byte and long + // enough in the limitation. + EXPECT_EQ(sent_text.size(), 3996UL); + EXPECT_EQ(sent_text, base::UTF16ToUTF8(std::u16string(1332, u'あ'))); + // The selection range must be relocated accordingly to the sent text. + EXPECT_EQ(sent_range, gfx::Range(1398, 2598)); + + // Test OnDeleteSurroundingText with this input. + zwp_text_input_v1_send_delete_surrounding_text( + zwp_text_input_->resource(), sent_range.start(), sent_range.length()); + Sync(); + absl::optional<gfx::Range> index = + input_method_context_delegate_->on_delete_surrounding_text_range(); + EXPECT_TRUE(index.has_value()); + EXPECT_EQ(index, range); +} + +TEST_P(WaylandInputMethodContextTest, SetSurroundingTextForLongTextInLeftEdge) { + std::u16string text(5000, u'あ'); + gfx::Range range(0, 500); + + std::string sent_text; + gfx::Range sent_range; + EXPECT_CALL(*zwp_text_input_, SetSurroundingText(_, _)) + .WillOnce(DoAll(SaveArg<0>(&sent_text), SaveArg<1>(&sent_range))); + input_method_context_->SetSurroundingText(text, range); + connection_->ScheduleFlush(); + Sync(); + Mock::VerifyAndClearExpectations(zwp_text_input_); + // The text sent as wayland protocol must be at most 4000 byte and large + // enough in the limitation. + EXPECT_EQ(sent_text.size(), 3999UL); + EXPECT_EQ(sent_text, base::UTF16ToUTF8(std::u16string(1333, u'あ'))); + // The selection range must be relocated accordingly to the sent text. + EXPECT_EQ(sent_range, gfx::Range(0, 1500)); + + // Test OnDeleteSurroundingText with this input. + zwp_text_input_v1_send_delete_surrounding_text( + zwp_text_input_->resource(), sent_range.start(), sent_range.length()); + Sync(); + absl::optional<gfx::Range> index = + input_method_context_delegate_->on_delete_surrounding_text_range(); + EXPECT_TRUE(index.has_value()); + EXPECT_EQ(index, range); +} + +TEST_P(WaylandInputMethodContextTest, + SetSurroundingTextForLongTextInRightEdge) { + std::u16string text(5000, u'あ'); + gfx::Range range(4500, 5000); + + std::string sent_text; + gfx::Range sent_range; + EXPECT_CALL(*zwp_text_input_, SetSurroundingText(_, _)) + .WillOnce(DoAll(SaveArg<0>(&sent_text), SaveArg<1>(&sent_range))); + input_method_context_->SetSurroundingText(text, range); + connection_->ScheduleFlush(); + Sync(); + Mock::VerifyAndClearExpectations(zwp_text_input_); + // The text sent as wayland protocol must be at most 4000 byte and large + // enough in the limitation. + EXPECT_EQ(sent_text.size(), 3999UL); + EXPECT_EQ(sent_text, base::UTF16ToUTF8(std::u16string(1333, u'あ'))); + // The selection range must be relocated accordingly to the sent text. + EXPECT_EQ(sent_range, gfx::Range(2499, 3999)); + + // Test OnDeleteSurroundingText with this input. + zwp_text_input_v1_send_delete_surrounding_text( + zwp_text_input_->resource(), sent_range.start(), sent_range.length()); + Sync(); + absl::optional<gfx::Range> index = + input_method_context_delegate_->on_delete_surrounding_text_range(); + EXPECT_TRUE(index.has_value()); + EXPECT_EQ(index, range); +} + +TEST_P(WaylandInputMethodContextTest, SetSurroundingTextForLongRange) { + std::u16string text(5000, u'あ'); + gfx::Range range(1000, 4000); + + // set_surrounding_text request should be skipped when the selection range in + // UTF8 form is longer than 4000 byte. + EXPECT_CALL(*zwp_text_input_, SetSurroundingText(_, _)).Times(0); + input_method_context_->SetSurroundingText(text, range); + connection_->ScheduleFlush(); + Sync(); +} + TEST_P(WaylandInputMethodContextTest, OnPreeditChanged) { zwp_text_input_v1_send_preedit_string(zwp_text_input_->resource(), 0, "PreeditString", "");
diff --git a/ui/ozone/platform/wayland/host/wayland_output_manager.cc b/ui/ozone/platform/wayland/host/wayland_output_manager.cc index 731f5f1..c55ed97 100644 --- a/ui/ozone/platform/wayland/host/wayland_output_manager.cc +++ b/ui/ozone/platform/wayland/host/wayland_output_manager.cc
@@ -102,7 +102,7 @@ } auto* wayland_window_manager = connection_->wayland_window_manager(); for (auto* window : wayland_window_manager->GetWindowsOnOutput(output_id)) - window->UpdateBufferScale(true); + window->UpdateWindowScale(true); } WaylandOutputManager::OutputList::const_iterator
diff --git a/ui/ozone/platform/wayland/host/wayland_popup.cc b/ui/ozone/platform/wayland/host/wayland_popup.cc index 1ae4426..24016a89 100644 --- a/ui/ozone/platform/wayland/host/wayland_popup.cc +++ b/ui/ozone/platform/wayland/host/wayland_popup.cc
@@ -35,10 +35,10 @@ if (!pending_initial_bounds_px_.IsEmpty()) { SetBounds(pending_initial_bounds_px_); pending_initial_bounds_px_ = gfx::Rect(); - } else if (buffer_scale() != parent_window()->buffer_scale()) { + } else if (window_scale() != parent_window()->window_scale()) { // If scale changed while this was hidden (when WaylandPopup hides, parent // window's child is reset), update buffer scale accordingly. - UpdateBufferScale(true); + UpdateWindowScale(true); } const auto bounds_dip = @@ -138,7 +138,7 @@ // parent top level window instead. if (new_bounds_dip.y() < 0) { // Move parent bounds along y-axis. - parent_bounds.set_y(-(new_bounds_dip.y() * buffer_scale())); + parent_bounds.set_y(-(new_bounds_dip.y() * window_scale())); new_bounds_dip.set_y(0); } else { // If the menu window is located at correct origin from the browser point @@ -153,9 +153,9 @@ // a display. new_bounds_dip = gfx::ScaleToRoundedRect( wl::TranslateBoundsToTopLevelCoordinates( - gfx::ScaleToRoundedRect(new_bounds_dip, buffer_scale()), + gfx::ScaleToRoundedRect(new_bounds_dip, window_scale()), parent_window()->GetBounds()), - 1.0 / buffer_scale()); + 1.0 / window_scale()); DCHECK(new_bounds_dip.y() >= 0); } @@ -175,7 +175,7 @@ bool WaylandPopup::OnInitialize(PlatformWindowInitProperties properties) { DCHECK(parent_window()); - root_surface()->SetBufferScale(parent_window()->buffer_scale()); + SetWindowScale(parent_window()->window_scale()); set_ui_scale(parent_window()->ui_scale()); shadow_type_ = properties.shadow_type; @@ -196,14 +196,14 @@ // The bounds are initially given in the scale of the primary display, so we // have to upscale or downscale the rect to the scale of the target display, // if that scale is different. - if (primary_display_scale_factor < buffer_scale()) { - scale = static_cast<float>(buffer_scale()) / + if (primary_display_scale_factor < window_scale()) { + scale = static_cast<float>(window_scale()) / static_cast<float>(primary_display_scale_factor); transform.Scale(scale, scale); transform.TransformRect(&float_rect); - } else if (primary_display_scale_factor > buffer_scale()) { + } else if (primary_display_scale_factor > window_scale()) { scale = static_cast<float>(primary_display_scale_factor) / - static_cast<float>(buffer_scale()); + static_cast<float>(window_scale()); transform.Scale(scale, scale); transform.TransformRectReverse(&float_rect); }
diff --git a/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc b/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc index 3ff6033..7d79615 100644 --- a/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
@@ -648,7 +648,7 @@ // Checks that the surface that backs the window receives new scale of the // output that it is in. -TEST_P(WaylandScreenTest, SetBufferScale) { +TEST_P(WaylandScreenTest, SetWindowScale) { // Place the window onto the output. wl_surface_send_enter(surface_->resource(), output_->resource()); @@ -656,13 +656,12 @@ // the new scale and update scale of their buffers. The default UI scale // equals the output scale. const int32_t kTripleScale = 3; - EXPECT_CALL(*surface_, SetBufferScale(kTripleScale)); output_->SetScale(kTripleScale); output_->Flush(); Sync(); - EXPECT_EQ(window_->buffer_scale(), kTripleScale); + EXPECT_EQ(window_->window_scale(), kTripleScale); EXPECT_EQ(window_->ui_scale_, kTripleScale); // Now simulate the --force-device-scale-factor=1.5 @@ -678,13 +677,12 @@ const int32_t kDoubleScale = 2; // Question ourselves before questioning others! EXPECT_NE(kForcedUIScale, kDoubleScale); - EXPECT_CALL(*surface_, SetBufferScale(kDoubleScale)); output_->SetScale(kDoubleScale); output_->Flush(); Sync(); - EXPECT_EQ(window_->buffer_scale(), kDoubleScale); + EXPECT_EQ(window_->window_scale(), kDoubleScale); EXPECT_EQ(window_->ui_scale_, kForcedUIScale); display::Display::ResetForceDeviceScaleFactorForTesting();
diff --git a/ui/ozone/platform/wayland/host/wayland_subsurface.cc b/ui/ozone/platform/wayland/host/wayland_subsurface.cc index f15d6ac2..4b701d2 100644 --- a/ui/ozone/platform/wayland/host/wayland_subsurface.cc +++ b/ui/ozone/platform/wayland/host/wayland_subsurface.cc
@@ -82,7 +82,7 @@ // Translate location from screen to surface coordinates. auto bounds_px = AdjustSubsurfaceBounds(bounds_px_, parent_->GetBounds(), - parent_->ui_scale(), parent_->buffer_scale()); + parent_->ui_scale(), parent_->window_scale()); wl_subsurface_set_position(subsurface_.get(), bounds_px.x(), bounds_px.y()); } } @@ -99,7 +99,7 @@ // relative to parent window. auto bounds_px = AdjustSubsurfaceBounds(bounds_px_, parent_->GetBounds(), - parent_->ui_scale(), parent_->buffer_scale()); + parent_->ui_scale(), parent_->window_scale()); DCHECK(subsurface_); wl_subsurface_set_position(subsurface_.get(), bounds_px.x(), bounds_px.y()); @@ -120,11 +120,12 @@ gfx::OverlayTransform transform, const gfx::RectF& src_rect, const gfx::Rect& bounds_rect, + int32_t buffer_scale, bool enable_blend, const WaylandSurface* reference_below, const WaylandSurface* reference_above) { wayland_surface()->SetBufferTransform(transform); - wayland_surface()->SetBufferScale(parent_->buffer_scale()); + wayland_surface()->SetSurfaceBufferScale(buffer_scale); auto old_bounds = bounds_px_; SetBounds(bounds_rect);
diff --git a/ui/ozone/platform/wayland/host/wayland_subsurface.h b/ui/ozone/platform/wayland/host/wayland_subsurface.h index 26a4c58..f45e606 100644 --- a/ui/ozone/platform/wayland/host/wayland_subsurface.h +++ b/ui/ozone/platform/wayland/host/wayland_subsurface.h
@@ -25,7 +25,6 @@ ~WaylandSubsurface(); wl_surface* surface() const { return wayland_surface_.surface(); } - int32_t buffer_scale() const { return wayland_surface_.buffer_scale(); } WaylandSurface* wayland_surface() { return &wayland_surface_; } gfx::Rect bounds_px() { return bounds_px_; } bool IsOpaque() const { return !enable_blend_; } @@ -43,6 +42,7 @@ // |reference_below| & |reference_above|: this subsurface is taken from the // subsurface stack and inserted back to be immediately below/above the // reference subsurface. + // |buffer_scale|: the scale factor of the next attached buffer. // // The coordinate transformations from buffer pixel coordinates up to the // surface-local coordinates happen in the following order: @@ -52,6 +52,7 @@ void ConfigureAndShowSurface(gfx::OverlayTransform transform, const gfx::RectF& src_rect, const gfx::Rect& bounds_rect, + int32_t buffer_scale, bool enable_blend, const WaylandSurface* reference_below, const WaylandSurface* reference_above);
diff --git a/ui/ozone/platform/wayland/host/wayland_surface.cc b/ui/ozone/platform/wayland/host/wayland_surface.cc index 3a0a9192..d3451a0 100644 --- a/ui/ozone/platform/wayland/host/wayland_surface.cc +++ b/ui/ozone/platform/wayland/host/wayland_surface.cc
@@ -116,8 +116,18 @@ // Apply buffer_transform (wl_surface.set_buffer_transform). gfx::Size bounds = wl::ApplyWaylandTransform( buffer_size, wl::ToWaylandTransform(buffer_transform_)); - // Apply buffer_scale (wl_surface.set_buffer_scale). - bounds = gfx::ScaleToCeiledSize(bounds, 1.f / buffer_scale_); + + // When crop_rect is set, wp_viewport will crop and scale the surface + // accordingly. Thus, there is no need to downscale bounds as Wayland + // compositor understands that. + // TODO(msisov): it'd be better to decide to set source, destination or buffer + // scale at commit time and avoid these kind of conditions. + if (crop_rect_.IsEmpty()) { + bounds = gfx::ScaleToCeiledSize(bounds, 1.f / buffer_scale_); + } else { + // Unset buffer scale if wp_viewport is set. + SetSurfaceBufferScale(1); + } // Apply crop (wp_viewport.set_source). gfx::Rect viewport_src = gfx::Rect(bounds); if (!crop_rect_.IsEmpty()) { @@ -206,24 +216,9 @@ wl_surface_set_buffer_transform(surface_.get(), wl_transform); } -void WaylandSurface::SetBufferScale(int32_t new_scale) { - DCHECK_GT(new_scale, 0); - - if (new_scale == buffer_scale_) - return; - - buffer_scale_ = new_scale; - wl_surface_set_buffer_scale(surface_.get(), buffer_scale_); - - if (!display_size_px_.IsEmpty()) { - gfx::Size viewport_dst = - gfx::ScaleToCeiledSize(display_size_px_, 1.f / buffer_scale_); - if (viewport()) { - wp_viewport_set_destination(viewport(), viewport_dst.width(), - viewport_dst.height()); - } - } - +void WaylandSurface::SetSurfaceBufferScale(int32_t scale) { + wl_surface_set_buffer_scale(surface_.get(), scale); + buffer_scale_ = scale; connection_->ScheduleFlush(); } @@ -275,8 +270,8 @@ wl_region_add(region.get(), rect.x(), rect.y(), rect.width(), rect.height()); } else { - gfx::Rect region_dip = - gfx::ScaleToEnclosingRect(region_px, 1.f / buffer_scale_); + gfx::Rect region_dip = gfx::ScaleToEnclosingRect( + region_px, 1.f / root_window_->window_scale()); wl_region_add(region.get(), region_dip.x(), region_dip.y(), region_dip.width(), region_dip.height()); } @@ -305,6 +300,7 @@ void WaylandSurface::SetViewportDestination(const gfx::Size& dest_size_px) { if (dest_size_px == display_size_px_) return; + if (dest_size_px.IsEmpty()) { display_size_px_ = gfx::Size(); if (viewport()) {
diff --git a/ui/ozone/platform/wayland/host/wayland_surface.h b/ui/ozone/platform/wayland/host/wayland_surface.h index 63e179d..0afc7189 100644 --- a/ui/ozone/platform/wayland/host/wayland_surface.h +++ b/ui/ozone/platform/wayland/host/wayland_surface.h
@@ -48,8 +48,6 @@ return entered_outputs_; } - int32_t buffer_scale() const { return buffer_scale_; } - void set_buffer_scale(int32_t scale) { buffer_scale_ = scale; } void set_explicit_release_callback(ExplicitReleaseCallback callback) { explicit_release_callback_ = callback; } @@ -88,8 +86,11 @@ // the contents of the buffer attached to this surface. void SetBufferTransform(gfx::OverlayTransform transform); - // Sets the buffer scale for this surface. - void SetBufferScale(int32_t scale); + // Sets the |buffer_scale| (with respect to the scale factor used by the GPU + // process) for the next submitted buffer. This helps Wayland compositor to + // determine buffer size in dip (GPU operates in pixels. So, when buffers are + // created, their requested size is in pixels). + void SetSurfaceBufferScale(int32_t scale); // Sets the region that is opaque on this surface in physical pixels. This is // expected to be called whenever the region that the surface span changes or @@ -167,8 +168,7 @@ // buffer. gfx::OverlayTransform buffer_transform_ = gfx::OVERLAY_TRANSFORM_NONE; - // Wayland's scale factor for the output that this surface currently belongs - // to. + // Current scale factor of a next attached buffer used by the GPU process. int32_t buffer_scale_ = 1; // Following fields are used to help determine the damage_region in
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc index 9df1113..098faa4 100644 --- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc +++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -97,7 +97,7 @@ return; } - UpdateBufferScale(false); + UpdateWindowScale(false); if (auto* drag_controller = connection()->window_drag_controller()) drag_controller->OnToplevelWindowCreated(this); @@ -250,10 +250,10 @@ return window_shape_in_dips_; } -void WaylandToplevelWindow::UpdateBufferScale(bool update_bounds) { - auto old_scale = buffer_scale(); - WaylandWindow::UpdateBufferScale(update_bounds); - if (old_scale == buffer_scale()) +void WaylandToplevelWindow::UpdateWindowScale(bool update_bounds) { + auto old_scale = window_scale(); + WaylandWindow::UpdateWindowScale(update_bounds); + if (old_scale == window_scale()) return; // Update min/max size in DIP if buffer scale is updated. @@ -311,7 +311,7 @@ gfx::ScaleToRoundedSize(GetRestoredBoundsInPixels().IsEmpty() ? GetBounds().size() : GetRestoredBoundsInPixels().size(), - 1.0 / buffer_scale())); + 1.0 / window_scale())); } // Store the restored bounds of current state differs from the normal state. @@ -330,7 +330,7 @@ void WaylandToplevelWindow::HandleSurfaceConfigure(uint32_t serial) { if (pending_bounds_dip_ == - gfx::ScaleToRoundedRect(GetBounds(), 1.f / buffer_scale()) && + gfx::ScaleToRoundedRect(GetBounds(), 1.f / window_scale()) && pending_configures_.empty()) { // If |pending_bounds_dip_| matches GetBounds(), and |pending_configures_| // is empty, implying that the window is already rendering at @@ -371,7 +371,7 @@ if (!shell_toplevel_) return; - auto size_dip = gfx::ScaleToRoundedSize(size_px, 1.f / buffer_scale()); + auto size_dip = gfx::ScaleToRoundedSize(size_px, 1.f / window_scale()); auto result = std::find_if(pending_configures_.begin(), pending_configures_.end(), [&size_dip](auto& configure) { @@ -553,13 +553,13 @@ if (min_size_.has_value()) { auto min_size_dip = - gfx::ScaleToRoundedSize(min_size_.value(), 1.0f / buffer_scale()); + gfx::ScaleToRoundedSize(min_size_.value(), 1.0f / window_scale()); shell_toplevel_->SetMinSize(min_size_dip.width(), min_size_dip.height()); } if (max_size_.has_value()) { auto max_size_dip = - gfx::ScaleToRoundedSize(max_size_.value(), 1.0f / buffer_scale()); + gfx::ScaleToRoundedSize(max_size_.value(), 1.0f / window_scale()); shell_toplevel_->SetMaxSize(max_size_dip.width(), max_size_dip.height()); } @@ -645,7 +645,7 @@ return; } SkPath window_mask_in_dips = - wl::ConvertPathToDIP(window_mask_in_pixels, buffer_scale()); + wl::ConvertPathToDIP(window_mask_in_pixels, window_scale()); window_shape_in_dips_ = wl::CreateRectsFromSkPath(window_mask_in_dips); }
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h index a6df59f..1282e99 100644 --- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h +++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
@@ -64,7 +64,7 @@ private: // WaylandWindow overrides: - void UpdateBufferScale(bool update_bounds) override; + void UpdateWindowScale(bool update_bounds) override; void HandleToplevelConfigure(int32_t width, int32_t height, bool is_maximized,
diff --git a/ui/ozone/platform/wayland/host/wayland_window.cc b/ui/ozone/platform/wayland/host/wayland_window.cc index 58d6c256..18145f87 100644 --- a/ui/ozone/platform/wayland/host/wayland_window.cc +++ b/ui/ozone/platform/wayland/host/wayland_window.cc
@@ -94,7 +94,7 @@ delegate_->OnLostCapture(); } -void WaylandWindow::UpdateBufferScale(bool update_bounds) { +void WaylandWindow::UpdateWindowScale(bool update_bounds) { DCHECK(connection_->wayland_output_manager()); auto preferred_outputs_id = GetPreferredEnteredOutputId(); @@ -120,12 +120,8 @@ int32_t new_scale = output->scale_factor(); ui_scale_ = output->GetUIScaleFactor(); - int32_t old_scale = buffer_scale(); - root_surface_->SetBufferScale(new_scale); - if (primary_subsurface_) - primary_subsurface_->wayland_surface()->SetBufferScale(new_scale); - for (auto& subsurface : wayland_subsurfaces_) - subsurface->wayland_surface()->SetBufferScale(new_scale); + int32_t old_scale = window_scale(); + window_scale_ = new_scale; // We need to keep DIP size of the window the same whenever the scale changes. if (update_bounds) @@ -133,13 +129,18 @@ // Propagate update to the child windows if (child_window_) - child_window_->UpdateBufferScale(update_bounds); + child_window_->UpdateWindowScale(update_bounds); } gfx::AcceleratedWidget WaylandWindow::GetWidget() const { return accelerated_widget_; } +void WaylandWindow::SetWindowScale(int32_t new_scale) { + DCHECK_GE(new_scale, 0); + window_scale_ = new_scale; +} + uint32_t WaylandWindow::GetPreferredEnteredOutputId() { // Child windows don't store entered outputs. Instead, take the window's // root parent window and use its preferred output. @@ -265,7 +266,7 @@ } gfx::Rect WaylandWindow::GetBoundsInDIP() const { - return gfx::ScaleToRoundedRect(bounds_px_, 1.0 / buffer_scale()); + return gfx::ScaleToRoundedRect(bounds_px_, 1.0 / window_scale()); } void WaylandWindow::SetTitle(const std::u16string& title) {} @@ -392,7 +393,7 @@ // physical pixels. UpdateCursorPositionFromEvent(Event::Clone(*event)); event->AsLocatedEvent()->set_location_f(gfx::ScalePoint( - event->AsLocatedEvent()->location_f(), buffer_scale(), buffer_scale())); + event->AsLocatedEvent()->location_f(), window_scale(), window_scale())); // We must reroute the events to the event grabber iff these windows belong // to the same root parent window. For example, there are 2 top level @@ -468,7 +469,7 @@ return; auto location_px = gfx::ScalePoint(TranslateLocationToRootWindow(point), - buffer_scale(), buffer_scale()); + window_scale(), window_scale()); // Wayland sends locations in DIP so they need to be translated to // physical pixels. @@ -483,7 +484,7 @@ return 0; auto location_px = gfx::ScalePoint(TranslateLocationToRootWindow(point), - buffer_scale(), buffer_scale()); + window_scale(), window_scale()); // Wayland sends locations in DIP so they need to be translated to // physical pixels. @@ -516,7 +517,7 @@ } void WaylandWindow::SetBoundsDip(const gfx::Rect& bounds_dip) { - SetBounds(gfx::ScaleToRoundedRect(bounds_dip, buffer_scale())); + SetBounds(gfx::ScaleToRoundedRect(bounds_dip, window_scale())); } bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) { @@ -572,7 +573,7 @@ if (AsWaylandPopup()) return; - UpdateBufferScale(true); + UpdateWindowScale(true); } void WaylandWindow::OnEnteredOutputIdRemoved() { @@ -582,7 +583,7 @@ if (AsWaylandPopup()) return; - UpdateBufferScale(true); + UpdateWindowScale(true); } void WaylandWindow::UpdateCursorPositionFromEvent( @@ -755,8 +756,8 @@ } (*iter)->ConfigureAndShowSurface( (*overlay_iter)->transform, (*overlay_iter)->crop_rect, - (*overlay_iter)->bounds_rect, (*overlay_iter)->enable_blend, - nullptr, reference_above); + (*overlay_iter)->bounds_rect, (*overlay_iter)->surface_scale_factor, + (*overlay_iter)->enable_blend, nullptr, reference_above); connection_->buffer_manager_host()->CommitBufferInternal( (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(), /*wait_for_frame_callback=*/true, @@ -786,8 +787,8 @@ } (*iter)->ConfigureAndShowSurface( (*overlay_iter)->transform, (*overlay_iter)->crop_rect, - (*overlay_iter)->bounds_rect, (*overlay_iter)->enable_blend, - reference_below, nullptr); + (*overlay_iter)->bounds_rect, (*overlay_iter)->surface_scale_factor, + (*overlay_iter)->enable_blend, reference_below, nullptr); connection_->buffer_manager_host()->CommitBufferInternal( (*iter)->wayland_surface(), (*overlay_iter)->buffer_id, gfx::Rect(), /*wait_for_frame_callback=*/true, @@ -803,6 +804,8 @@ if (split == overlays.end() && overlays.front()->z_order == INT32_MIN) split = overlays.begin(); + + root_surface_->SetSurfaceBufferScale((*split)->surface_scale_factor); UpdateVisualSize((*split)->bounds_rect.size()); if (!wayland_overlay_delegation_enabled_) { @@ -831,7 +834,8 @@ (*split)->transform, (*split)->crop_rect, (*split)->crop_rect == gfx::RectF(1.f, 1.f) ? gfx::Rect() : (*split)->bounds_rect, - (*split)->enable_blend, nullptr, nullptr); + (*split)->surface_scale_factor, (*split)->enable_blend, nullptr, + nullptr); connection_->buffer_manager_host()->CommitBufferInternal( primary_subsurface_->wayland_surface(), (*split)->buffer_id, (*split)->damage_region,
diff --git a/ui/ozone/platform/wayland/host/wayland_window.h b/ui/ozone/platform/wayland/host/wayland_window.h index 21dfcca5..e2e1db0 100644 --- a/ui/ozone/platform/wayland/host/wayland_window.h +++ b/ui/ozone/platform/wayland/host/wayland_window.h
@@ -53,12 +53,12 @@ void OnWindowLostCapture(); - // Updates the surface buffer scale of the window. Top level windows take + // Updates the surface scale of the window. Top level windows take // scale from the output attached to either their current display or the // primary one if their widget is not yet created, children inherit scale from // their parent. The method recalculates window bounds appropriately if asked // to do so (this is not needed upon window initialization). - virtual void UpdateBufferScale(bool update_bounds); + virtual void UpdateWindowScale(bool update_bounds); WaylandSurface* root_surface() const { return root_surface_.get(); } WaylandSubsurface* primary_subsurface() const { @@ -103,7 +103,11 @@ void set_child_window(WaylandWindow* window) { child_window_ = window; } WaylandWindow* child_window() const { return child_window_; } - int32_t buffer_scale() const { return root_surface_->buffer_scale(); } + // Sets the window_scale for this window with respect to a display this window + // is located at. This determines how events can be translated and how size of + // the surface is treated (px to DIP conversion and vice versa.) + void SetWindowScale(int32_t new_scale); + int32_t window_scale() const { return window_scale_; } float ui_scale() const { return ui_scale_; } // A preferred output is the one with the largest scale. This is needed to @@ -244,7 +248,7 @@ virtual void UpdateWindowMask(); private: - FRIEND_TEST_ALL_PREFIXES(WaylandScreenTest, SetBufferScale); + FRIEND_TEST_ALL_PREFIXES(WaylandScreenTest, SetWindowScale); // Initializes the WaylandWindow with supplied properties. bool Initialize(PlatformWindowInitProperties properties); @@ -322,6 +326,8 @@ // replaces the default value that is equal to the natural device scale. // We need it to place and size the menus properly. float ui_scale_ = 1.0f; + // Current scale factor of the output where the window is located at. + int32_t window_scale_ = 1; // Stores current opacity of the window. Set on ::Initialize call. ui::PlatformWindowOpacity opacity_;
diff --git a/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc index 5dbe486..25de1de1 100644 --- a/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc
@@ -754,7 +754,7 @@ ASSERT_TRUE(surface); wl_surface_send_enter(surface->resource(), output->resource()); self->Sync(); - EXPECT_EQ(output->GetScale(), window->buffer_scale()); + EXPECT_EQ(output->GetScale(), window->window_scale()); self->SendDndMotion({20, 20}); self->Sync();
diff --git a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc index 8f96876..cff48fe 100644 --- a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -1615,11 +1615,11 @@ } } -TEST_P(WaylandWindowTest, ToplevelWindowUpdateBufferScale) { +TEST_P(WaylandWindowTest, ToplevelWindowUpdateWindowScale) { VerifyAndClearExpectations(); - // Buffer scale must be 1 when no output has been entered by the window. - EXPECT_EQ(1, window_->buffer_scale()); + // Surface scale must be 1 when no output has been entered by the window. + EXPECT_EQ(1, window_->window_scale()); // Creating an output with scale 1. wl::TestOutput* output1 = server_.CreateAndInitializeOutput(); @@ -1641,7 +1641,7 @@ Sync(); // The window's scale and bounds must remain unchanged. - EXPECT_EQ(1, window_->buffer_scale()); + EXPECT_EQ(1, window_->window_scale()); EXPECT_EQ(gfx::Rect(0, 0, 800, 600), window_->GetBounds()); // Simulating drag process from |output1| to |output2|. @@ -1650,11 +1650,11 @@ Sync(); // The window must change its scale and bounds to keep DIP bounds the same. - EXPECT_EQ(2, window_->buffer_scale()); + EXPECT_EQ(2, window_->window_scale()); EXPECT_EQ(gfx::Rect(0, 0, 1600, 1200), window_->GetBounds()); } -TEST_P(WaylandWindowTest, WaylandPopupBufferScale) { +TEST_P(WaylandWindowTest, WaylandPopupSurfaceScale) { VerifyAndClearExpectations(); // Creating an output with scale 1. @@ -1689,8 +1689,8 @@ // the wayland_popup window should inherit its buffer scale from the focused // window. - EXPECT_EQ(1, window_->buffer_scale()); - EXPECT_EQ(window_->buffer_scale(), wayland_popup->buffer_scale()); + EXPECT_EQ(1, window_->window_scale()); + EXPECT_EQ(window_->window_scale(), wayland_popup->window_scale()); EXPECT_EQ(wayland_popup_bounds, wayland_popup->GetBounds()); wayland_popup->Hide(); @@ -1699,16 +1699,16 @@ wl_surface_send_leave(surface->resource(), output1->resource()); Sync(); - EXPECT_EQ(2, window_->buffer_scale()); + EXPECT_EQ(2, window_->window_scale()); wayland_popup->Show(false); Sync(); // |wayland_popup|'s scale and bounds must change whenever its parents // scale is changed. - EXPECT_EQ(window_->buffer_scale(), wayland_popup->buffer_scale()); + EXPECT_EQ(window_->window_scale(), wayland_popup->window_scale()); EXPECT_EQ(gfx::ScaleToRoundedRect(wayland_popup_bounds, - wayland_popup->buffer_scale()), + wayland_popup->window_scale()), wayland_popup->GetBounds()); wayland_popup->Hide(); @@ -1801,7 +1801,7 @@ VerifyAndClearExpectations(); // Buffer scale must be 1 when no output has been entered by the window. - EXPECT_EQ(1, window_->buffer_scale()); + EXPECT_EQ(1, window_->window_scale()); // Creating an output with scale 1. wl::TestOutput* output1 = server_.CreateAndInitializeOutput(); @@ -1904,7 +1904,7 @@ VerifyAndClearExpectations(); // Buffer scale must be 1 when no output has been entered by the window. - EXPECT_EQ(1, window_->buffer_scale()); + EXPECT_EQ(1, window_->window_scale()); MockPlatformWindowDelegate menu_window_delegate; std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams( @@ -2882,9 +2882,9 @@ auto* mock_surface_subsurface = server_.GetObject<wl::MockSurface>( wayland_subsurface->wayland_surface()->GetSurfaceId()); EXPECT_TRUE(mock_surface_subsurface); - wayland_subsurface->ConfigureAndShowSurface(gfx::OVERLAY_TRANSFORM_NONE, - gfx::RectF(), subsurface_bounds, - true, nullptr, nullptr); + wayland_subsurface->ConfigureAndShowSurface( + gfx::OVERLAY_TRANSFORM_NONE, gfx::RectF(), subsurface_bounds, + 1 /*buffer_scale*/, true, nullptr, nullptr); connection_->ScheduleFlush(); Sync();
diff --git a/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc b/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc index caae9a4..855144c3 100644 --- a/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc +++ b/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc
@@ -201,7 +201,7 @@ gfx::Rect anchor_rect = GetAnchorRect( params.menu_type, params.bounds, gfx::ScaleToRoundedRect(parent_window->GetBounds(), - 1.0 / parent_window->buffer_scale())); + 1.0 / parent_window->window_scale())); xdg_positioner_set_anchor_rect(positioner, anchor_rect.x(), anchor_rect.y(), anchor_rect.width(), anchor_rect.height());
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h index db2eeca..bda502ba 100644 --- a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h +++ b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper.h
@@ -48,7 +48,8 @@ virtual void OnCommitString(base::StringPiece text) = 0; // Called when client needs to delete all or part of the text surrounding - // the cursor + // the cursor. |index| and |length| are expected to be a byte offset of |text| + // passed via ZWPTextInputWrapper::SetSurroundingText. virtual void OnDeleteSurroundingText(int32_t index, uint32_t length) = 0; // Notify when a key event was sent. Key events should not be used @@ -77,7 +78,7 @@ virtual void HideInputPanel() = 0; virtual void SetCursorRect(const gfx::Rect& rect) = 0; - virtual void SetSurroundingText(const std::u16string& text, + virtual void SetSurroundingText(const std::string& text, const gfx::Range& selection_range) = 0; };
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc index 1110df40..3c41382 100644 --- a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc +++ b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.cc
@@ -7,10 +7,7 @@ #include <string> #include <utility> -#include "base/logging.h" #include "base/memory/ptr_util.h" -#include "base/strings/utf_offset_string_conversions.h" -#include "base/strings/utf_string_conversions.h" #include "ui/gfx/range/range.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" @@ -81,30 +78,10 @@ } void ZWPTextInputWrapperV1::SetSurroundingText( - const std::u16string& text, + const std::string& text, const gfx::Range& selection_range) { - static constexpr size_t kWaylandMessageDataMaxLength = 4000; - std::vector<size_t> offsets_for_adjustment = {selection_range.start(), - selection_range.end()}; - const std::string text_utf8 = - base::UTF16ToUTF8AndAdjustOffsets(text, &offsets_for_adjustment); - // The text length for set_surrounding_text can not be longer than the maximum - // length of wayland messages. The maximum length of the text is explicitly - // specified as 4000 in the protocol spec of text-input-unstable-v3. - // If the client is unware of the text around the cursor, we can skip sending - // set_surrounding_text requests. We fall back to this case when the text is - // too long. - // TODO(fukino): If the length of |text| doesn't fit into the 4000 bytes - // limitation, we should truncate the text and adjust indices of - // |selection_range| to make use of set_surrounding_text as much as possible. - // crbug.com/1173465. - if (text_utf8.size() > kWaylandMessageDataMaxLength) - return; - - text_for_surrounding_text_ = text_utf8; - zwp_text_input_v1_set_surrounding_text(obj_.get(), text_utf8.c_str(), - offsets_for_adjustment[0], - offsets_for_adjustment[1]); + zwp_text_input_v1_set_surrounding_text( + obj_.get(), text.c_str(), selection_range.start(), selection_range.end()); } void ZWPTextInputWrapperV1::ResetInputEventState() { @@ -191,22 +168,7 @@ int32_t index, uint32_t length) { ZWPTextInputWrapperV1* wti = static_cast<ZWPTextInputWrapperV1*>(data); - std::vector<size_t> offsets_for_adjustment = {index, index + length}; - if (wti->text_for_surrounding_text_.empty()) { - LOG(DFATAL) - << "SetSurroundingText should run before OnDeleteSurrondingText."; - return; - } - base::UTF8ToUTF16AndAdjustOffsets(wti->text_for_surrounding_text_, - &offsets_for_adjustment); - if (offsets_for_adjustment[0] == std::u16string::npos || - offsets_for_adjustment[1] == std::u16string::npos) { - LOG(DFATAL) << "The selection range for surrounding text is invalid."; - return; - } - wti->client_->OnDeleteSurroundingText( - offsets_for_adjustment[0], - offsets_for_adjustment[1] - offsets_for_adjustment[0]); + wti->client_->OnDeleteSurroundingText(index, length); } void ZWPTextInputWrapperV1::OnKeysym(void* data,
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.h b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.h index f68f7e9..55595118 100644 --- a/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.h +++ b/ui/ozone/platform/wayland/host/zwp_text_input_wrapper_v1.h
@@ -39,7 +39,7 @@ void HideInputPanel() override; void SetCursorRect(const gfx::Rect& rect) override; - void SetSurroundingText(const std::u16string& text, + void SetSurroundingText(const std::string& text, const gfx::Range& selection_range) override; private:
diff --git a/ui/ozone/platform/wayland/host/zxdg_popup_v6_wrapper_impl.cc b/ui/ozone/platform/wayland/host/zxdg_popup_v6_wrapper_impl.cc index b21774f..9242fe3 100644 --- a/ui/ozone/platform/wayland/host/zxdg_popup_v6_wrapper_impl.cc +++ b/ui/ozone/platform/wayland/host/zxdg_popup_v6_wrapper_impl.cc
@@ -205,7 +205,7 @@ gfx::Rect anchor_rect = GetAnchorRect( params.menu_type, params.bounds, gfx::ScaleToRoundedRect(parent_window->GetBounds(), - 1.0 / parent_window->buffer_scale())); + 1.0 / parent_window->window_scale())); zxdg_positioner_v6_set_anchor_rect(positioner, anchor_rect.x(), anchor_rect.y(), anchor_rect.width(),
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_text_input.cc b/ui/ozone/platform/wayland/test/mock_zwp_text_input.cc index f8a8cca..9a74d362 100644 --- a/ui/ozone/platform/wayland/test/mock_zwp_text_input.cc +++ b/ui/ozone/platform/wayland/test/mock_zwp_text_input.cc
@@ -33,6 +33,15 @@ GetUserDataAs<MockZwpTextInput>(resource)->Reset(); } +void TextInputV1SetSurroundingText(wl_client* client, + wl_resource* resource, + const char* text, + uint32_t cursor, + uint32_t anchor) { + GetUserDataAs<MockZwpTextInput>(resource)->SetSurroundingText( + text, gfx::Range(cursor, anchor)); +} + void TextInputV1SetCursorRectangle(wl_client* client, wl_resource* resource, int32_t x, @@ -50,7 +59,7 @@ &TextInputV1ShowInputPanel, // show_input_panel &TextInputV1HideInputPanel, // hide_input_panel &TextInputV1Reset, // reset - nullptr, // set_surrounding_text + &TextInputV1SetSurroundingText, // set_surrounding_text nullptr, // set_content_type &TextInputV1SetCursorRectangle, // set_cursor_rectangle nullptr, // set_preferred_language
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_text_input.h b/ui/ozone/platform/wayland/test/mock_zwp_text_input.h index aba9761..edde776 100644 --- a/ui/ozone/platform/wayland/test/mock_zwp_text_input.h +++ b/ui/ozone/platform/wayland/test/mock_zwp_text_input.h
@@ -9,6 +9,7 @@ #include "base/macros.h" #include "testing/gmock/include/gmock/gmock.h" +#include "ui/gfx/range/range.h" #include "ui/ozone/platform/wayland/test/server_object.h" struct wl_resource; @@ -30,6 +31,8 @@ MOCK_METHOD0(HideInputPanel, void()); MOCK_METHOD4(SetCursorRect, void(int32_t x, int32_t y, int32_t width, int32_t height)); + MOCK_METHOD2(SetSurroundingText, + void(std::string text, gfx::Range selection_range)); private: DISALLOW_COPY_AND_ASSIGN(MockZwpTextInput);
diff --git a/ui/strings/translations/ui_strings_af.xtb b/ui/strings/translations/ui_strings_af.xtb index 8db9843..13d8a198 100644 --- a/ui/strings/translations/ui_strings_af.xtb +++ b/ui/strings/translations/ui_strings_af.xtb
@@ -165,6 +165,7 @@ <translation id="6012623610530968780">Bladsy <ph name="SELECTED_PAGE" /> van <ph name="TOTAL_PAGE_NUM" /></translation> <translation id="6022924867608035986">Vee soekkassieteks uit</translation> <translation id="6040143037577758943">Maak toe</translation> +<translation id="6117103120090651229">Middelknoppie</translation> <translation id="6119846243427417423">aktiveer</translation> <translation id="6129953537138746214">Spasie</translation> <translation id="6134259848159370930">Deursoek jou toestel, programme en die web.</translation>
diff --git a/ui/strings/translations/ui_strings_fil.xtb b/ui/strings/translations/ui_strings_fil.xtb index de4b074..c00d522c 100644 --- a/ui/strings/translations/ui_strings_fil.xtb +++ b/ui/strings/translations/ui_strings_fil.xtb
@@ -165,6 +165,7 @@ <translation id="6012623610530968780">Page <ph name="SELECTED_PAGE" /> ng <ph name="TOTAL_PAGE_NUM" /></translation> <translation id="6022924867608035986">I-clear ang text sa searchbox</translation> <translation id="6040143037577758943">Isara</translation> +<translation id="6117103120090651229">Gitnang button</translation> <translation id="6119846243427417423">isaaktibo</translation> <translation id="6129953537138746214">Puwang</translation> <translation id="6134259848159370930">Maghanap sa iyong device, mga app, mga setting, at web.</translation>
diff --git a/ui/strings/translations/ui_strings_iw.xtb b/ui/strings/translations/ui_strings_iw.xtb index 738a811..3dd7ea8 100644 --- a/ui/strings/translations/ui_strings_iw.xtb +++ b/ui/strings/translations/ui_strings_iw.xtb
@@ -165,6 +165,7 @@ <translation id="6012623610530968780">דף <ph name="SELECTED_PAGE" /> מתוך <ph name="TOTAL_PAGE_NUM" /></translation> <translation id="6022924867608035986">ניקוי הטקסט בתיבת החיפוש</translation> <translation id="6040143037577758943">סגירה</translation> +<translation id="6117103120090651229">הלחצן המרכזי</translation> <translation id="6119846243427417423">הפעלה</translation> <translation id="6129953537138746214">רווח</translation> <translation id="6134259848159370930">אפשר לחפש במכשיר, באפליקציות ובאינטרנט.</translation>
diff --git a/ui/strings/translations/ui_strings_lo.xtb b/ui/strings/translations/ui_strings_lo.xtb index 9a3ff276..f2990b1 100644 --- a/ui/strings/translations/ui_strings_lo.xtb +++ b/ui/strings/translations/ui_strings_lo.xtb
@@ -165,6 +165,7 @@ <translation id="6012623610530968780">ໜ້າທີ <ph name="SELECTED_PAGE" /> ຈາກທັງໝົດ <ph name="TOTAL_PAGE_NUM" /></translation> <translation id="6022924867608035986">ລຶບລ້າງຂໍ້ຄວາມໃນກ່ອງຊອກຫາ</translation> <translation id="6040143037577758943">ປິດ</translation> +<translation id="6117103120090651229">ປຸ່ມທາງກາງ</translation> <translation id="6119846243427417423">ເປີດໃຊ້ງານ</translation> <translation id="6129953537138746214">ຍະຫວ່າງ</translation> <translation id="6134259848159370930">ຊອກຫາໃນອຸປະກອນ, ແອັບ, ການຕັ້ງຄ່າ ແລະ ເວັບ.</translation>
diff --git a/ui/strings/translations/ui_strings_mn.xtb b/ui/strings/translations/ui_strings_mn.xtb index ff036411..88dae443 100644 --- a/ui/strings/translations/ui_strings_mn.xtb +++ b/ui/strings/translations/ui_strings_mn.xtb
@@ -165,6 +165,7 @@ <translation id="6012623610530968780">Хуудас <ph name="TOTAL_PAGE_NUM" />-н <ph name="SELECTED_PAGE" /></translation> <translation id="6022924867608035986">Хайлтын талбарын текстийг утсгах</translation> <translation id="6040143037577758943">Хаах</translation> +<translation id="6117103120090651229">Голын товчлуур</translation> <translation id="6119846243427417423">идэвхжүүлэх</translation> <translation id="6129953537138746214">Space</translation> <translation id="6134259848159370930">Төхөөрөмж, апп, тохиргоо, вебээс хайна уу.</translation>
diff --git a/ui/strings/translations/ui_strings_si.xtb b/ui/strings/translations/ui_strings_si.xtb index d93c497..4af6d25 100644 --- a/ui/strings/translations/ui_strings_si.xtb +++ b/ui/strings/translations/ui_strings_si.xtb
@@ -165,6 +165,7 @@ <translation id="6012623610530968780"><ph name="TOTAL_PAGE_NUM" /> න් <ph name="SELECTED_PAGE" /> වැනි පිටුව</translation> <translation id="6022924867608035986">සෙවුම් කොටු පෙළ හිස් කරන්න</translation> <translation id="6040143037577758943">වසන්න</translation> +<translation id="6117103120090651229">මැද බොත්තම</translation> <translation id="6119846243427417423">සක්රීය කරන්න</translation> <translation id="6129953537138746214">හිඩස</translation> <translation id="6134259848159370930">ඔබේ උපාංගය, යෙදුම්, සැකසීම් සහ වෙබය තුළ සොයන්න.</translation>
diff --git a/ui/strings/translations/ui_strings_sw.xtb b/ui/strings/translations/ui_strings_sw.xtb index 1f375467..77cf032 100644 --- a/ui/strings/translations/ui_strings_sw.xtb +++ b/ui/strings/translations/ui_strings_sw.xtb
@@ -165,6 +165,7 @@ <translation id="6012623610530968780">Ukurasa wa <ph name="SELECTED_PAGE" /> kati ya <ph name="TOTAL_PAGE_NUM" /></translation> <translation id="6022924867608035986">Futa maandishi ya kisanduku cha utafutaji</translation> <translation id="6040143037577758943">Funga</translation> +<translation id="6117103120090651229">Kitufe cha katikati</translation> <translation id="6119846243427417423">wezesha</translation> <translation id="6129953537138746214">Nafasi</translation> <translation id="6134259848159370930">Tafuta kwenye wavuti, programu, mipangilio na kifaa chako.</translation>
diff --git a/weblayer/browser/permissions/permission_manager_factory.cc b/weblayer/browser/permissions/permission_manager_factory.cc index 2188a8a4..bcc43a8 100644 --- a/weblayer/browser/permissions/permission_manager_factory.cc +++ b/weblayer/browser/permissions/permission_manager_factory.cc
@@ -14,6 +14,7 @@ #include "components/permissions/contexts/midi_sysex_permission_context.h" #include "components/permissions/contexts/payment_handler_permission_context.h" #include "components/permissions/contexts/sensor_permission_context.h" +#include "components/permissions/contexts/wake_lock_permission_context.h" #include "components/permissions/permission_context_base.h" #include "components/permissions/permission_manager.h" #include "content/public/browser/permission_type.h" @@ -123,6 +124,12 @@ std::make_unique<BackgroundSyncPermissionContext>(browser_context); permission_contexts[ContentSettingsType::SENSORS] = std::make_unique<permissions::SensorPermissionContext>(browser_context); + permission_contexts[ContentSettingsType::WAKE_LOCK_SCREEN] = + std::make_unique<permissions::WakeLockPermissionContext>( + browser_context, ContentSettingsType::WAKE_LOCK_SCREEN); + permission_contexts[ContentSettingsType::WAKE_LOCK_SYSTEM] = + std::make_unique<permissions::WakeLockPermissionContext>( + browser_context, ContentSettingsType::WAKE_LOCK_SYSTEM); // For now, all requests are denied. As features are added, their permission // contexts can be added here instead of DeniedPermissionContext.