summaryrefslogtreecommitdiff
path: root/chromium/components/paint_preview
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/components/paint_preview
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-85-based.tar.gz
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/paint_preview')
-rw-r--r--chromium/components/paint_preview/browser/BUILD.gn1
-rw-r--r--chromium/components/paint_preview/browser/compositor_utils.cc8
-rw-r--r--chromium/components/paint_preview/browser/directory_key.cc4
-rw-r--r--chromium/components/paint_preview/browser/directory_key.h2
-rw-r--r--chromium/components/paint_preview/browser/file_manager.cc72
-rw-r--r--chromium/components/paint_preview/browser/file_manager.h12
-rw-r--r--chromium/components/paint_preview/browser/file_manager_unittest.cc57
-rw-r--r--chromium/components/paint_preview/browser/paint_preview_base_service.cc4
-rw-r--r--chromium/components/paint_preview/browser/paint_preview_client.cc19
-rw-r--r--chromium/components/paint_preview/browser/paint_preview_client_unittest.cc6
-rw-r--r--chromium/components/paint_preview/browser/service_sandbox_type.h29
-rw-r--r--chromium/components/paint_preview/common/mojom/paint_preview_recorder.mojom3
-rw-r--r--chromium/components/paint_preview/common/paint_preview_tracker.cc12
-rw-r--r--chromium/components/paint_preview/common/paint_preview_tracker.h5
-rw-r--r--chromium/components/paint_preview/common/paint_preview_tracker_unittest.cc27
-rw-r--r--chromium/components/paint_preview/common/proto/paint_preview.proto6
-rw-r--r--chromium/components/paint_preview/features/features.h2
-rw-r--r--chromium/components/paint_preview/player/android/BUILD.gn6
-rw-r--r--chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/OverscrollHandler.java33
-rw-r--r--chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PaintPreviewFrame.java27
-rw-r--r--chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java27
-rw-r--r--chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java70
-rw-r--r--chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerSwipeRefreshHandler.java82
-rw-r--r--chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java20
-rw-r--r--chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java9
-rw-r--r--chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java317
-rw-r--r--chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameProperties.java6
-rw-r--r--chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java61
-rw-r--r--chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewBinder.java2
-rw-r--r--chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewDelegate.java25
-rw-r--r--chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java131
-rw-r--r--chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewTestRule.java30
-rw-r--r--chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/PlayerManagerTest.java24
-rw-r--r--chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PaintPreviewCustomFlingingShadowScroller.java4
-rw-r--r--chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java586
-rw-r--r--chromium/components/paint_preview/player/android/player_compositor_delegate_android.cc73
-rw-r--r--chromium/components/paint_preview/player/android/player_compositor_delegate_android.h7
-rw-r--r--chromium/components/paint_preview/player/android/player_compositor_delegate_android_unittest.cc20
-rw-r--r--chromium/components/paint_preview/player/player_compositor_delegate.cc23
-rw-r--r--chromium/components/paint_preview/player/player_compositor_delegate.h7
-rw-r--r--chromium/components/paint_preview/player/player_compositor_delegate_unittest.cc62
-rw-r--r--chromium/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc18
-rw-r--r--chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc27
-rw-r--r--chromium/components/paint_preview/renderer/paint_preview_recorder_utils.cc2
44 files changed, 1721 insertions, 247 deletions
diff --git a/chromium/components/paint_preview/browser/BUILD.gn b/chromium/components/paint_preview/browser/BUILD.gn
index 90ebaf83615..3712dfeccb2 100644
--- a/chromium/components/paint_preview/browser/BUILD.gn
+++ b/chromium/components/paint_preview/browser/BUILD.gn
@@ -25,6 +25,7 @@ source_set("browser") {
"paint_preview_compositor_service_impl.cc",
"paint_preview_compositor_service_impl.h",
"paint_preview_policy.h",
+ "service_sandbox_type.h",
]
deps = [
diff --git a/chromium/components/paint_preview/browser/compositor_utils.cc b/chromium/components/paint_preview/browser/compositor_utils.cc
index c8e342b96dc..120d2434685 100644
--- a/chromium/components/paint_preview/browser/compositor_utils.cc
+++ b/chromium/components/paint_preview/browser/compositor_utils.cc
@@ -7,9 +7,9 @@
#include <utility>
#include "base/bind.h"
-#include "base/task/post_task.h"
#include "build/build_config.h"
#include "components/discardable_memory/service/discardable_shared_memory_manager.h"
+#include "components/paint_preview/browser/service_sandbox_type.h"
#include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/browser_task_traits.h"
@@ -41,12 +41,10 @@ CreateCompositorCollection() {
void CreateCompositorCollectionPending(
mojo::PendingReceiver<mojom::PaintPreviewCompositorCollection> collection) {
- // TODO(crbug/1074323): Investigate using a different SandboxType.
content::ServiceProcessHost::Launch<mojom::PaintPreviewCompositorCollection>(
std::move(collection),
content::ServiceProcessHost::Options()
.WithDisplayName(IDS_PAINT_PREVIEW_COMPOSITOR_SERVICE_DISPLAY_NAME)
- .WithSandboxType(service_manager::SandboxType::kPrintCompositor)
.Pass());
}
@@ -56,8 +54,8 @@ void BindDiscardableSharedMemoryManager(
discardable_memory_manager;
// Set up the discardable memory manager.
- base::PostTask(
- FROM_HERE, {content::BrowserThread::IO},
+ content::GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(
&BindDiscardableSharedMemoryManagerOnIOThread,
discardable_memory_manager.InitWithNewPipeAndPassReceiver()));
diff --git a/chromium/components/paint_preview/browser/directory_key.cc b/chromium/components/paint_preview/browser/directory_key.cc
index 819e98c4bb9..45e07acdb7f 100644
--- a/chromium/components/paint_preview/browser/directory_key.cc
+++ b/chromium/components/paint_preview/browser/directory_key.cc
@@ -10,4 +10,8 @@ bool operator<(const DirectoryKey& a, const DirectoryKey& b) {
return a.AsciiDirname() < b.AsciiDirname();
}
+bool operator==(const DirectoryKey& a, const DirectoryKey& b) {
+ return a.AsciiDirname() == b.AsciiDirname();
+}
+
} // namespace paint_preview
diff --git a/chromium/components/paint_preview/browser/directory_key.h b/chromium/components/paint_preview/browser/directory_key.h
index a24b66afa29..bed4752f370 100644
--- a/chromium/components/paint_preview/browser/directory_key.h
+++ b/chromium/components/paint_preview/browser/directory_key.h
@@ -27,6 +27,8 @@ class DirectoryKey {
bool operator<(const DirectoryKey& a, const DirectoryKey& b);
+bool operator==(const DirectoryKey& a, const DirectoryKey& b);
+
} // namespace paint_preview
#endif // COMPONENTS_PAINT_PREVIEW_BROWSER_DIRECTORY_KEY_H_
diff --git a/chromium/components/paint_preview/browser/file_manager.cc b/chromium/components/paint_preview/browser/file_manager.cc
index 70fc611b3f2..8566302a2df 100644
--- a/chromium/components/paint_preview/browser/file_manager.cc
+++ b/chromium/components/paint_preview/browser/file_manager.cc
@@ -4,9 +4,14 @@
#include "components/paint_preview/browser/file_manager.h"
+#include <algorithm>
+#include <vector>
+
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/hash/hash.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
#include "base/strings/string_number_conversions.h"
#include "components/paint_preview/common/file_utils.h"
#include "third_party/zlib/google/zip.h"
@@ -18,6 +23,11 @@ namespace {
constexpr char kProtoName[] = "proto.pb";
constexpr char kZipExt[] = ".zip";
+bool CompareByLastModified(const base::FileEnumerator::FileInfo& a,
+ const base::FileEnumerator::FileInfo& b) {
+ return a.GetLastModifiedTime() < b.GetLastModifiedTime();
+}
+
} // namespace
FileManager::FileManager(
@@ -70,6 +80,10 @@ base::Optional<base::File::Info> FileManager::GetInfo(
return info;
}
+size_t FileManager::GetTotalDiskUsage() const {
+ return base::ComputeDirectorySize(root_directory_);
+}
+
bool FileManager::DirectoryExists(const DirectoryKey& key) const {
DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
base::FilePath path;
@@ -185,8 +199,23 @@ bool FileManager::SerializePaintPreviewProto(const DirectoryKey& key,
auto path = CreateOrGetDirectory(key, false);
if (!path.has_value())
return false;
- return WriteProtoToFile(path->AppendASCII(kProtoName), proto) &&
- (!compress || CompressDirectory(key));
+ bool result = WriteProtoToFile(path->AppendASCII(kProtoName), proto) &&
+ (!compress || CompressDirectory(key));
+
+ if (compress) {
+ auto info = GetInfo(key);
+ if (info.has_value()) {
+ base::UmaHistogramMemoryKB(
+ "Browser.PaintPreview.Capture.CompressedOnDiskSize",
+ info->size / 1000);
+ }
+ } else {
+ size_t size_bytes = base::ComputeDirectorySize(path.value());
+ base::UmaHistogramMemoryKB(
+ "Browser.PaintPreview.Capture.UncompressedOnDiskSize",
+ size_bytes / 1000);
+ }
+ return result;
}
std::unique_ptr<PaintPreviewProto> FileManager::DeserializePaintPreviewProto(
@@ -212,6 +241,45 @@ base::flat_set<DirectoryKey> FileManager::ListUsedKeys() const {
return base::flat_set<DirectoryKey>(std::move(keys));
}
+std::vector<DirectoryKey> FileManager::GetOldestArtifactsForCleanup(
+ size_t max_size) {
+ // The rest of this function is expensive so cleanup should exit early if not
+ // required.
+ size_t size = base::ComputeDirectorySize(root_directory_);
+ if (size <= max_size)
+ return std::vector<DirectoryKey>();
+
+ base::FileEnumerator file_enum(
+ root_directory_, false,
+ base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
+ std::vector<base::FileEnumerator::FileInfo> file_infos;
+ for (base::FilePath name = file_enum.Next(); !name.empty();
+ name = file_enum.Next()) {
+ file_infos.push_back(file_enum.GetInfo());
+ }
+
+ std::sort(file_infos.begin(), file_infos.end(), CompareByLastModified);
+
+ std::vector<DirectoryKey> keys_to_remove;
+ for (const auto& file_info : file_infos) {
+ base::FilePath full_path = root_directory_.Append(file_info.GetName());
+
+ size_t size_delta = file_info.GetSize();
+ // Computing a directory size is expensive. Most files should hopefully be
+ // compressed already.
+ if (file_info.IsDirectory())
+ size_delta = base::ComputeDirectorySize(full_path);
+
+ // Directory names should always be ASCII.
+ keys_to_remove.emplace_back(
+ file_info.GetName().RemoveExtension().MaybeAsASCII());
+ size -= size_delta;
+ if (size <= max_size)
+ break;
+ }
+ return keys_to_remove;
+}
+
FileManager::StorageType FileManager::GetPathForKey(
const DirectoryKey& key,
base::FilePath* path) const {
diff --git a/chromium/components/paint_preview/browser/file_manager.h b/chromium/components/paint_preview/browser/file_manager.h
index 3d9173e4ce4..126e418fd96 100644
--- a/chromium/components/paint_preview/browser/file_manager.h
+++ b/chromium/components/paint_preview/browser/file_manager.h
@@ -16,6 +16,7 @@
#include "url/gurl.h"
namespace paint_preview {
+class PaintPreviewTabService;
// FileManager manages paint preview files associated with a root directory.
// Typically the root directory is <profile_dir>/paint_previews/<feature>.
@@ -47,6 +48,9 @@ class FileManager : public base::RefCountedThreadSafe<FileManager> {
size_t GetSizeOfArtifacts(const DirectoryKey& key) const;
base::Optional<base::File::Info> GetInfo(const DirectoryKey& key) const;
+ // Returns the total disk usage of all paint previews.
+ size_t GetTotalDiskUsage() const;
+
// Returns true if the directory for |key| exists.
bool DirectoryExists(const DirectoryKey& key) const;
@@ -87,10 +91,18 @@ class FileManager : public base::RefCountedThreadSafe<FileManager> {
// Lists the current set of in-use DirectoryKeys.
base::flat_set<DirectoryKey> ListUsedKeys() const;
+ // Returns a list of the least recently modified artifact sets until which
+ // when deleted would result in a total capture size on disk that is less than
+ // |max_size|.
+ std::vector<DirectoryKey> GetOldestArtifactsForCleanup(size_t max_size);
+
private:
friend class base::RefCountedThreadSafe<FileManager>;
~FileManager();
+ friend class PaintPreviewTabService;
+ base::FilePath GetPath() const { return root_directory_; }
+
enum StorageType {
kNone = 0,
kDirectory = 1,
diff --git a/chromium/components/paint_preview/browser/file_manager_unittest.cc b/chromium/components/paint_preview/browser/file_manager_unittest.cc
index cacb0ce55e8..0ee9043e5ca 100644
--- a/chromium/components/paint_preview/browser/file_manager_unittest.cc
+++ b/chromium/components/paint_preview/browser/file_manager_unittest.cc
@@ -138,6 +138,7 @@ TEST_F(FileManagerTest, TestCompression) {
// Compress.
base::FilePath zip_path = directory.AddExtensionASCII(".zip");
EXPECT_TRUE(manager->CompressDirectory(key));
+ EXPECT_TRUE(manager->CompressDirectory(key)); // no-op
EXPECT_GT(manager->GetSizeOfArtifacts(key), 0U);
EXPECT_FALSE(base::PathExists(directory));
EXPECT_FALSE(base::PathExists(test_file));
@@ -275,10 +276,66 @@ TEST_F(FileManagerTest, HandleProtoCompressed) {
metadata->set_url(GURL("www.chromium.org").spec());
EXPECT_TRUE(manager->SerializePaintPreviewProto(key, original_proto, true));
+ EXPECT_TRUE(manager->CaptureExists(key));
+
EXPECT_TRUE(base::PathExists(path.AddExtensionASCII(".zip")));
auto out_proto = manager->DeserializePaintPreviewProto(key);
EXPECT_NE(out_proto, nullptr);
EXPECT_THAT(*out_proto, EqualsProto(original_proto));
+
+ EXPECT_TRUE(manager->CaptureExists(key));
+}
+
+TEST_F(FileManagerTest, OldestFilesForCleanup) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ auto manager =
+ base::MakeRefCounted<FileManager>(temp_dir.GetPath(), MainTaskRunner());
+
+ const std::string data = "Foobar";
+
+ auto key_0 = manager->CreateKey(0U);
+ base::FilePath path_0 =
+ manager->CreateOrGetDirectory(key_0, true).value_or(base::FilePath());
+ base::WriteFile(path_0.AppendASCII("0.txt"), data.data(), data.size());
+ {
+ auto to_delete = manager->GetOldestArtifactsForCleanup(0U);
+ ASSERT_EQ(to_delete.size(), 1U);
+ EXPECT_EQ(to_delete[0], key_0);
+ }
+ {
+ auto to_delete = manager->GetOldestArtifactsForCleanup(50U);
+ EXPECT_EQ(to_delete.size(), 0U);
+ }
+
+ auto key_1 = manager->CreateKey(1U);
+ base::FilePath path_1 =
+ manager->CreateOrGetDirectory(key_1, true).value_or(base::FilePath());
+ base::WriteFile(path_1.AppendASCII("1.txt"), data.data(), data.size());
+ manager->CompressDirectory(key_1);
+ base::Time modified_time = base::Time::Now();
+ auto path_1_zip = path_1.AddExtensionASCII(".zip");
+ base::TouchFile(path_0, modified_time - base::TimeDelta::FromSeconds(10),
+ modified_time - base::TimeDelta::FromSeconds(10));
+ base::TouchFile(path_1_zip, modified_time, modified_time);
+
+ {
+ auto to_delete = manager->GetOldestArtifactsForCleanup(0U);
+ ASSERT_EQ(to_delete.size(), 2U);
+ // Elements should be ordered in oldest to newest.
+ EXPECT_EQ(to_delete[0], key_0);
+ EXPECT_EQ(to_delete[1], key_1);
+ }
+ {
+ // Zip is ~116 bytes.
+ auto to_delete = manager->GetOldestArtifactsForCleanup(120U);
+ ASSERT_EQ(to_delete.size(), 1U);
+ EXPECT_EQ(to_delete[0], key_0);
+ }
+ {
+ auto to_delete = manager->GetOldestArtifactsForCleanup(150U);
+ EXPECT_EQ(to_delete.size(), 0U);
+ }
}
} // namespace paint_preview
diff --git a/chromium/components/paint_preview/browser/paint_preview_base_service.cc b/chromium/components/paint_preview/browser/paint_preview_base_service.cc
index 7b54b73c970..d99dce23ff4 100644
--- a/chromium/components/paint_preview/browser/paint_preview_base_service.cc
+++ b/chromium/components/paint_preview/browser/paint_preview_base_service.cc
@@ -148,7 +148,9 @@ void PaintPreviewBaseService::OnCaptured(
if (web_contents)
web_contents->DecrementCapturerCount(true);
- if (status != mojom::PaintPreviewStatus::kOk || !proto) {
+ if (!(status == mojom::PaintPreviewStatus::kOk ||
+ status == mojom::PaintPreviewStatus::kPartialSuccess) ||
+ !proto) {
DVLOG(1) << "ERROR: Paint Preview failed to capture for document "
<< guid.ToString() << " with error " << status;
std::move(callback).Run(kCaptureFailed, nullptr);
diff --git a/chromium/components/paint_preview/browser/paint_preview_client.cc b/chromium/components/paint_preview/browser/paint_preview_client.cc
index 35dec779664..d18772758fd 100644
--- a/chromium/components/paint_preview/browser/paint_preview_client.cc
+++ b/chromium/components/paint_preview/browser/paint_preview_client.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "base/files/file_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
@@ -57,6 +58,8 @@ PaintPreviewCaptureResponseToPaintPreviewFrameProto(
PaintPreviewFrameProto* proto) {
proto->set_embedding_token_high(frame_guid.GetHighForSerialization());
proto->set_embedding_token_low(frame_guid.GetLowForSerialization());
+ proto->set_scroll_offset_x(response->scroll_offsets.width());
+ proto->set_scroll_offset_y(response->scroll_offsets.height());
std::vector<base::UnguessableToken> frame_guids;
for (const auto& id_pair : response->content_id_to_embedding_token) {
@@ -332,9 +335,23 @@ void PaintPreviewClient::OnPaintPreviewCapturedCallback(
// |status|
MarkFrameAsProcessed(guid, frame_guid);
- if (status == mojom::PaintPreviewStatus::kOk)
+ if (status == mojom::PaintPreviewStatus::kOk) {
status = RecordFrame(guid, frame_guid, is_main_frame, filename,
render_frame_id, std::move(response));
+ } else {
+ // If the capture failed then cleanup the file.
+ base::ThreadPool::PostTask(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+ base::BindOnce(base::GetDeleteFileCallback(), filename));
+
+ // If this is the main frame we should just abort the capture.
+ if (is_main_frame) {
+ auto it = all_document_data_.find(guid);
+ if (it != all_document_data_.end())
+ OnFinished(guid, &it->second);
+ }
+ }
+
auto it = all_document_data_.find(guid);
if (it == all_document_data_.end())
return;
diff --git a/chromium/components/paint_preview/browser/paint_preview_client_unittest.cc b/chromium/components/paint_preview/browser/paint_preview_client_unittest.cc
index 91962004fd7..ad6126749bb 100644
--- a/chromium/components/paint_preview/browser/paint_preview_client_unittest.cc
+++ b/chromium/components/paint_preview/browser/paint_preview_client_unittest.cc
@@ -18,6 +18,7 @@
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/test_utils.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
@@ -138,11 +139,14 @@ TEST_F(PaintPreviewClientRenderViewHostTest, CaptureMainFrameMock) {
auto response = mojom::PaintPreviewCaptureResponse::New();
response->embedding_token = base::nullopt;
+ response->scroll_offsets = gfx::Size(5, 10);
PaintPreviewProto expected_proto;
expected_proto.mutable_metadata()->set_url(expected_url.spec());
PaintPreviewFrameProto* main_frame = expected_proto.mutable_root_frame();
main_frame->set_is_main_frame(true);
+ main_frame->set_scroll_offset_x(5);
+ main_frame->set_scroll_offset_y(10);
base::RunLoop loop;
auto callback = base::BindOnce(
@@ -223,6 +227,8 @@ TEST_F(PaintPreviewClientRenderViewHostTest, CaptureFailureMock) {
ASSERT_NE(client, nullptr);
client->CapturePaintPreview(params, main_rfh(), std::move(callback));
loop.Run();
+ content::RunAllTasksUntilIdle();
+ EXPECT_TRUE(base::IsDirectoryEmpty(temp_dir_.GetPath()));
}
TEST_F(PaintPreviewClientRenderViewHostTest, RenderFrameDeletedNotCapturing) {
diff --git a/chromium/components/paint_preview/browser/service_sandbox_type.h b/chromium/components/paint_preview/browser/service_sandbox_type.h
new file mode 100644
index 00000000000..f955efc4a33
--- /dev/null
+++ b/chromium/components/paint_preview/browser/service_sandbox_type.h
@@ -0,0 +1,29 @@
+// Copyright 2020 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_PAINT_PREVIEW_BROWSER_SERVICE_SANDBOX_TYPE_H_
+#define COMPONENTS_PAINT_PREVIEW_BROWSER_SERVICE_SANDBOX_TYPE_H_
+
+#include "content/public/browser/sandbox_type.h"
+#include "content/public/browser/service_process_host.h"
+
+// This file maps service classes to sandbox types. Services which
+// require a non-utility sandbox can be added here. See
+// ServiceProcessHost::Launch() for how these templates are consumed.
+
+// paint_preview::mojom::PaintPreviewCompositorCollection
+namespace paint_preview {
+namespace mojom {
+class PaintPreviewCompositorCollection;
+}
+} // namespace paint_preview
+
+template <>
+inline content::SandboxType content::GetServiceSandboxType<
+ paint_preview::mojom::PaintPreviewCompositorCollection>() {
+ // TODO(crbug/1074323): Investigate using a different SandboxType.
+ return content::SandboxType::kPrintCompositor;
+}
+
+#endif // COMPONENTS_PAINT_PREVIEW_BROWSER_SERVICE_SANDBOX_TYPE_H_
diff --git a/chromium/components/paint_preview/common/mojom/paint_preview_recorder.mojom b/chromium/components/paint_preview/common/mojom/paint_preview_recorder.mojom
index 0b5d1bd8492..8201d243563 100644
--- a/chromium/components/paint_preview/common/mojom/paint_preview_recorder.mojom
+++ b/chromium/components/paint_preview/common/mojom/paint_preview_recorder.mojom
@@ -79,6 +79,9 @@ struct PaintPreviewCaptureResponse {
// The size of the resulting serialized capture.
uint64 serialized_size;
+
+ // Scroll offsets of the frame at capture time.
+ gfx.mojom.Size scroll_offsets;
};
// Service for capturing a paint preview of a RenderFrame's contents. This
diff --git a/chromium/components/paint_preview/common/paint_preview_tracker.cc b/chromium/components/paint_preview/common/paint_preview_tracker.cc
index 77a76ca19ea..dede9c035f7 100644
--- a/chromium/components/paint_preview/common/paint_preview_tracker.cc
+++ b/chromium/components/paint_preview/common/paint_preview_tracker.cc
@@ -44,14 +44,16 @@ PaintPreviewTracker::PaintPreviewTracker(
bool is_main_frame)
: guid_(guid),
embedding_token_(embedding_token),
- is_main_frame_(is_main_frame) {}
+ is_main_frame_(is_main_frame),
+ scroll_(SkISize::Make(0, 0)) {}
PaintPreviewTracker::~PaintPreviewTracker() = default;
uint32_t PaintPreviewTracker::CreateContentForRemoteFrame(
const gfx::Rect& rect,
const base::UnguessableToken& embedding_token) {
sk_sp<SkPicture> pic = SkPicture::MakePlaceholder(
- SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()));
+ SkRect::MakeXYWH(rect.x() + scroll_.width(), rect.y() + scroll_.height(),
+ rect.width(), rect.height()));
const uint32_t content_id = pic->uniqueID();
DCHECK(!base::Contains(content_id_to_embedding_token_, content_id));
content_id_to_embedding_token_[content_id] = embedding_token;
@@ -59,6 +61,10 @@ uint32_t PaintPreviewTracker::CreateContentForRemoteFrame(
return content_id;
}
+void PaintPreviewTracker::SetScrollForFrame(const SkISize& scroll) {
+ scroll_ = scroll;
+}
+
void PaintPreviewTracker::AddGlyphs(const SkTextBlob* blob) {
if (!blob)
return;
@@ -105,7 +111,7 @@ void PaintPreviewTracker::CustomDataToSkPictureCallback(SkCanvas* canvas,
DCHECK(it != subframe_pics_.end());
SkRect rect = it->second->cullRect();
- SkMatrix matrix = SkMatrix::MakeTrans(rect.x(), rect.y());
+ SkMatrix matrix = SkMatrix::Translate(rect.x(), rect.y());
canvas->drawPicture(it->second, &matrix, nullptr);
}
diff --git a/chromium/components/paint_preview/common/paint_preview_tracker.h b/chromium/components/paint_preview/common/paint_preview_tracker.h
index f8993f48d06..0041603d721 100644
--- a/chromium/components/paint_preview/common/paint_preview_tracker.h
+++ b/chromium/components/paint_preview/common/paint_preview_tracker.h
@@ -50,6 +50,9 @@ class PaintPreviewTracker {
const gfx::Rect& rect,
const base::UnguessableToken& embedding_token);
+ // Sets the scroll position for the top layer frame.
+ void SetScrollForFrame(const SkISize& scroll);
+
// Adds the glyphs in |blob| to the glyph usage tracker for the |blob|'s
// associated typface.
void AddGlyphs(const SkTextBlob* blob);
@@ -83,6 +86,8 @@ class PaintPreviewTracker {
const base::Optional<base::UnguessableToken> embedding_token_;
const bool is_main_frame_;
+ SkISize scroll_;
+
std::vector<mojom::LinkDataPtr> links_;
PictureSerializationContext content_id_to_embedding_token_;
TypefaceUsageMap typeface_glyph_usage_;
diff --git a/chromium/components/paint_preview/common/paint_preview_tracker_unittest.cc b/chromium/components/paint_preview/common/paint_preview_tracker_unittest.cc
index ba050b22acb..8575ba71c47 100644
--- a/chromium/components/paint_preview/common/paint_preview_tracker_unittest.cc
+++ b/chromium/components/paint_preview/common/paint_preview_tracker_unittest.cc
@@ -61,6 +61,33 @@ TEST(PaintPreviewTrackerTest, TestRemoteFramePlaceholderPicture) {
// underlying private picture record.
}
+TEST(PaintPreviewTrackerTest, TestRemoteFramePlaceholderPictureWithScroll) {
+ const base::UnguessableToken kDocToken = base::UnguessableToken::Create();
+ const base::UnguessableToken kEmbeddingToken =
+ base::UnguessableToken::Create();
+ PaintPreviewTracker tracker(kDocToken, kEmbeddingToken, true);
+ tracker.SetScrollForFrame(SkISize::Make(10, 20));
+
+ const base::UnguessableToken kEmbeddingTokenChild =
+ base::UnguessableToken::Create();
+ gfx::Rect rect(50, 40, 30, 20);
+ uint32_t content_id =
+ tracker.CreateContentForRemoteFrame(rect, kEmbeddingTokenChild);
+ PictureSerializationContext* context =
+ tracker.GetPictureSerializationContext();
+ EXPECT_TRUE(context->count(content_id));
+ EXPECT_EQ((*context)[content_id], kEmbeddingTokenChild);
+
+ SkPictureRecorder recorder;
+ SkCanvas* canvas = recorder.beginRecording(100, 100);
+ tracker.CustomDataToSkPictureCallback(canvas, content_id);
+ sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
+
+ // TODO(crbug/1009552): find a good way to check that a filler picture was
+ // actually inserted into |pic|. This is difficult without using the
+ // underlying private picture record.
+}
+
TEST(PaintPreviewTrackerTest, TestGlyphRunList) {
const base::UnguessableToken kEmbeddingToken =
base::UnguessableToken::Create();
diff --git a/chromium/components/paint_preview/common/proto/paint_preview.proto b/chromium/components/paint_preview/common/proto/paint_preview.proto
index 155be9dd555..3216065037c 100644
--- a/chromium/components/paint_preview/common/proto/paint_preview.proto
+++ b/chromium/components/paint_preview/common/proto/paint_preview.proto
@@ -33,7 +33,7 @@ message ContentIdEmbeddingTokenPairProto {
}
// A paint preview of a single frame.
-// NEXT_TAG = 7
+// NEXT_TAG = 10
message PaintPreviewFrameProto {
// The embedding token for this frame to its parent. Every frame other than
// the main frame should have a non-zero value here.
@@ -51,6 +51,10 @@ message PaintPreviewFrameProto {
// A mapping between the content IDs of subframes and the |id| field.
repeated ContentIdEmbeddingTokenPairProto content_id_to_embedding_tokens = 6;
+
+ // Position information for this frame.
+ optional uint32 scroll_offset_x = 7;
+ optional uint32 scroll_offset_y = 8;
}
// Metadata for the capture.
diff --git a/chromium/components/paint_preview/features/features.h b/chromium/components/paint_preview/features/features.h
index ca6d840feb3..34953b0949f 100644
--- a/chromium/components/paint_preview/features/features.h
+++ b/chromium/components/paint_preview/features/features.h
@@ -26,7 +26,7 @@ extern const base::Feature kPaintPreviewDemo;
// Used to enable the paint preview capture and show on startup for Android. If
// enabled, paint previews for each tab are captured when a tab is hidden and
-// are deleted when a tab is cloased. When a tab with a captured paint perview
+// are deleted when a tab is closed. When a tab with a captured paint perview
// is shown at startup and there is no cached page we will show the paint
// preview.
extern const base::Feature kPaintPreviewShowOnStartup;
diff --git a/chromium/components/paint_preview/player/android/BUILD.gn b/chromium/components/paint_preview/player/android/BUILD.gn
index 471b734016e..094a188622a 100644
--- a/chromium/components/paint_preview/player/android/BUILD.gn
+++ b/chromium/components/paint_preview/player/android/BUILD.gn
@@ -48,10 +48,12 @@ android_library("java") {
sources = [
"java/src/org/chromium/components/paintpreview/player/LinkClickHandler.java",
+ "java/src/org/chromium/components/paintpreview/player/OverscrollHandler.java",
"java/src/org/chromium/components/paintpreview/player/PaintPreviewFrame.java",
"java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegate.java",
"java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java",
"java/src/org/chromium/components/paintpreview/player/PlayerManager.java",
+ "java/src/org/chromium/components/paintpreview/player/PlayerSwipeRefreshHandler.java",
"java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameBitmapPainter.java",
"java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java",
"java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java",
@@ -66,7 +68,9 @@ android_library("java") {
"//base:base_java",
"//base:jni_java",
"//components/paint_preview/browser/android:java",
+ "//third_party/android_swipe_refresh:android_swipe_refresh_java",
"//ui/android:ui_java",
+ "//ui/android:ui_java_resources",
"//url:gurl_java",
]
}
@@ -110,6 +114,7 @@ android_library("javatests") {
"//content/public/test/android:content_java_test_support",
"//third_party/android_support_test_runner:rules_java",
"//third_party/android_support_test_runner:runner_java",
+ "//third_party/hamcrest:hamcrest_java",
"//third_party/junit",
"//third_party/ub-uiautomator:ub_uiautomator_java",
"//ui/android:ui_java_test_support",
@@ -152,5 +157,6 @@ junit_binary("paint_preview_junit_tests") {
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
+ "//ui/android:ui_full_java",
]
}
diff --git a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/OverscrollHandler.java b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/OverscrollHandler.java
new file mode 100644
index 00000000000..d3c60dad447
--- /dev/null
+++ b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/OverscrollHandler.java
@@ -0,0 +1,33 @@
+// Copyright 2020 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.components.paintpreview.player;
+
+/**
+ * Interface for handling overscroll events in the player.
+ */
+public interface OverscrollHandler {
+ /**
+ * Used to start an overscroll event. Returns true if it is able to be created/consumed.
+ */
+ boolean start();
+
+ /**
+ * Updates the overscroll amount.
+ *
+ * @param yDelta The change in overscroll amount. Positive values indicate more overscrolling.
+ */
+ void pull(float yDelta);
+
+ /**
+ * Releases the overscroll event. This will trigger a refresh if a sufficient number and
+ * distance of {@link #pull} calls occurred.
+ */
+ void release();
+
+ /**
+ * Resets the overscroll event if it was aborted.
+ */
+ void reset();
+}
diff --git a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PaintPreviewFrame.java b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PaintPreviewFrame.java
index 383e5d8270b..84bd86665b0 100644
--- a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PaintPreviewFrame.java
+++ b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PaintPreviewFrame.java
@@ -27,18 +27,27 @@ class PaintPreviewFrame {
private PaintPreviewFrame[] mSubFrames;
// The coordinates of the sub-frames relative to this frame.
private Rect[] mSubFrameClips;
+ // The initial scroll position of this frame.
+ private int mInitialScrollX;
+ private int mInitialScrollY;
- PaintPreviewFrame(UnguessableToken guid, int contentWidth, int contentHeight) {
+ PaintPreviewFrame(UnguessableToken guid, int contentWidth, int contentHeight,
+ int initialScrollX, int initialScrollY) {
mGuid = guid;
mContentWidth = contentWidth;
mContentHeight = contentHeight;
+ mInitialScrollX = initialScrollX;
+ mInitialScrollY = initialScrollY;
}
private PaintPreviewFrame(UnguessableToken guid, int contentWidth, int contentHeight,
- PaintPreviewFrame[] subFrames, Rect[] subFrameClips) {
+ int initialScrollX, int initialScrollY, PaintPreviewFrame[] subFrames,
+ Rect[] subFrameClips) {
mGuid = guid;
mContentWidth = contentWidth;
mContentHeight = contentHeight;
+ mInitialScrollX = initialScrollX;
+ mInitialScrollY = initialScrollY;
mSubFrames = subFrames;
mSubFrameClips = subFrameClips;
}
@@ -63,6 +72,14 @@ class PaintPreviewFrame {
return mContentHeight;
}
+ int getInitialScrollX() {
+ return mInitialScrollX;
+ }
+
+ int getInitialScrollY() {
+ return mInitialScrollY;
+ }
+
PaintPreviewFrame[] getSubFrames() {
return mSubFrames;
}
@@ -107,7 +124,9 @@ class PaintPreviewFrame {
@VisibleForTesting
static PaintPreviewFrame createInstanceForTest(UnguessableToken guid, int contentWidth,
- int contentHeight, PaintPreviewFrame[] subFrames, Rect[] subFrameClips) {
- return new PaintPreviewFrame(guid, contentWidth, contentHeight, subFrames, subFrameClips);
+ int contentHeight, int initialScrollX, int initialScrollY,
+ PaintPreviewFrame[] subFrames, Rect[] subFrameClips) {
+ return new PaintPreviewFrame(guid, contentWidth, contentHeight, initialScrollX,
+ initialScrollY, subFrames, subFrameClips);
}
}
diff --git a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java
index 9e3cbe51b7a..32e0625367e 100644
--- a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java
+++ b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java
@@ -24,8 +24,8 @@ import javax.annotation.Nonnull;
@JNINamespace("paint_preview")
class PlayerCompositorDelegateImpl implements PlayerCompositorDelegate {
interface CompositorListener {
- void onCompositorReady(boolean safeToShow, UnguessableToken rootFrameGuid,
- UnguessableToken[] frameGuids, int[] frameContentSize, int[] subFramesCount,
+ void onCompositorReady(UnguessableToken rootFrameGuid, UnguessableToken[] frameGuids,
+ int[] frameContentSize, int[] scrollOffsets, int[] subFramesCount,
UnguessableToken[] subFrameGuids, int[] subFrameClipRects);
}
@@ -35,12 +35,13 @@ class PlayerCompositorDelegateImpl implements PlayerCompositorDelegate {
PlayerCompositorDelegateImpl(NativePaintPreviewServiceProvider service, GURL url,
String directoryKey, @Nonnull CompositorListener compositorListener,
- @Nonnull LinkClickHandler linkClickHandler) {
+ @Nonnull LinkClickHandler linkClickHandler, Runnable compositorErrorCallback) {
mCompositorListener = compositorListener;
mLinkClickHandler = linkClickHandler;
if (service != null && service.getNativeService() != 0) {
- mNativePlayerCompositorDelegate = PlayerCompositorDelegateImplJni.get().initialize(
- this, service.getNativeService(), url.getSpec(), directoryKey);
+ mNativePlayerCompositorDelegate = PlayerCompositorDelegateImplJni.get().initialize(this,
+ service.getNativeService(), url.getSpec(), directoryKey,
+ compositorErrorCallback);
}
// TODO(crbug.com/1021590): Handle initialization errors when
// mNativePlayerCompositorDelegate == 0.
@@ -49,14 +50,16 @@ class PlayerCompositorDelegateImpl implements PlayerCompositorDelegate {
/**
* Called by native when the Paint Preview compositor is ready.
*
- * @param safeToShow true if the native initialization of the Paint Preview player succeeded and
- * the captured result matched the expected URL.
* @param rootFrameGuid The GUID for the root frame.
* @param frameGuids Contains all frame GUIDs that are in this hierarchy.
* @param frameContentSize Contains the content size for each frame. In native, this is called
* scroll extent. The order corresponds to {@code frameGuids}. The content width and height for
* the ith frame in {@code frameGuids} are respectively in the {@code 2*i} and {@code 2*i+1}
* indices of {@code frameContentSize}.
+ * @param scrollOffsets Contains the initial scroll offsets for each frame. The order
+ * corresponds to {@code frameGuids}. The offset in x and y for the ith frame in
+ * {@code frameGuids} are respectively in the {@code 2*i} and {@code 2*i+1} indices of
+ * {@code scrollOffsets}.
* @param subFramesCount Contains the number of sub-frames for each frame. The order corresponds
* to {@code frameGuids}. The number of sub-frames for the {@code i}th frame in {@code
* frameGuids} is {@code subFramesCount[i]}.
@@ -73,11 +76,11 @@ class PlayerCompositorDelegateImpl implements PlayerCompositorDelegate {
* subFrameGuids[4*k+3]}, where {@code k} has the same value as above.
*/
@CalledByNative
- void onCompositorReady(boolean safeToShow, UnguessableToken rootFrameGuid,
- UnguessableToken[] frameGuids, int[] frameContentSize, int[] subFramesCount,
+ void onCompositorReady(UnguessableToken rootFrameGuid, UnguessableToken[] frameGuids,
+ int[] frameContentSize, int[] scrollOffsets, int[] subFramesCount,
UnguessableToken[] subFrameGuids, int[] subFrameClipRects) {
- mCompositorListener.onCompositorReady(safeToShow, rootFrameGuid, frameGuids,
- frameContentSize, subFramesCount, subFrameGuids, subFrameClipRects);
+ mCompositorListener.onCompositorReady(rootFrameGuid, frameGuids, frameContentSize,
+ scrollOffsets, subFramesCount, subFrameGuids, subFrameClipRects);
}
@Override
@@ -119,7 +122,7 @@ class PlayerCompositorDelegateImpl implements PlayerCompositorDelegate {
@NativeMethods
interface Natives {
long initialize(PlayerCompositorDelegateImpl caller, long nativePaintPreviewBaseService,
- String urlSpec, String directoryKey);
+ String urlSpec, String directoryKey, Runnable compositorErrorCallback);
void destroy(long nativePlayerCompositorDelegateAndroid);
void requestBitmap(long nativePlayerCompositorDelegateAndroid, UnguessableToken frameGuid,
Callback<Bitmap> bitmapCallback, Runnable errorCallback, float scaleFactor,
diff --git a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java
index 794b786c9b0..8ba8a396d08 100644
--- a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java
+++ b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java
@@ -13,7 +13,6 @@ import android.widget.FrameLayout;
import androidx.annotation.VisibleForTesting;
-import org.chromium.base.Callback;
import org.chromium.base.TraceEvent;
import org.chromium.base.UnguessableToken;
import org.chromium.components.paintpreview.browser.NativePaintPreviewServiceProvider;
@@ -24,6 +23,7 @@ import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
/**
* This is the only public class in this package and is hence the access point of this component for
@@ -34,22 +34,44 @@ public class PlayerManager {
private PlayerCompositorDelegateImpl mDelegate;
private PlayerFrameCoordinator mRootFrameCoordinator;
private FrameLayout mHostView;
- private Callback<Boolean> mViewReadyCallback;
+ private Runnable mViewReadyCallback;
private static final String sInitEvent = "paint_preview PlayerManager init";
+ private PlayerSwipeRefreshHandler mPlayerSwipeRefreshHandler;
+ private boolean mIgnoreInitialScrollOffset;
+ /**
+ * Creates a new {@link PlayerManager}.
+ * @param url The url for the stored content that should be shown.
+ * @param nativePaintPreviewServiceProvider The native paint preview service.
+ * @param directoryKey The key for the directory storing the data.
+ * @param linkClickHandler Called with a url to trigger a navigation.
+ * @param refreshCallback Called when the paint preview should be refreshed.
+ * @param viewReadyCallback Called when the view is ready. Will not be called if compositorError
+ * is called prior to the view being ready.
+ * @param backgroundColor The color used for the background.
+ * @param compositorErrorCallback Called when the compositor has had an error (either during
+ * initialization or due to a disconnect).
+ * @param ignoreInitialScrollOffset If true the initial scroll state that is recorded at capture
+ * time is ignored.
+ */
public PlayerManager(GURL url, Context context,
NativePaintPreviewServiceProvider nativePaintPreviewServiceProvider,
String directoryKey, @Nonnull LinkClickHandler linkClickHandler,
- Callback<Boolean> viewReadyCallback, int backgroundColor) {
+ @Nullable Runnable refreshCallback, Runnable viewReadyCallback, int backgroundColor,
+ Runnable compositorErrorCallback, boolean ignoreInitialScrollOffset) {
TraceEvent.startAsync(sInitEvent, hashCode());
mContext = context;
mDelegate = new PlayerCompositorDelegateImpl(nativePaintPreviewServiceProvider, url,
- directoryKey, this::onCompositorReady, linkClickHandler);
+ directoryKey, this::onCompositorReady, linkClickHandler, compositorErrorCallback);
mHostView = new FrameLayout(mContext);
+ if (refreshCallback != null) {
+ mPlayerSwipeRefreshHandler = new PlayerSwipeRefreshHandler(mContext, refreshCallback);
+ }
mHostView.setLayoutParams(
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mHostView.setBackgroundColor(backgroundColor);
mViewReadyCallback = viewReadyCallback;
+ mIgnoreInitialScrollOffset = ignoreInitialScrollOffset;
}
/**
@@ -57,26 +79,26 @@ public class PlayerManager {
* method initializes a sub-component for each frame and adds the view for the root frame to
* {@link #mHostView}.
*/
- private void onCompositorReady(boolean safeToShow, UnguessableToken rootFrameGuid,
- UnguessableToken[] frameGuids, int[] frameContentSize, int[] subFramesCount,
+ private void onCompositorReady(UnguessableToken rootFrameGuid, UnguessableToken[] frameGuids,
+ int[] frameContentSize, int[] scrollOffsets, int[] subFramesCount,
UnguessableToken[] subFrameGuids, int[] subFrameClipRects) {
- if (!safeToShow) {
- mViewReadyCallback.onResult(safeToShow);
- TraceEvent.finishAsync(sInitEvent, hashCode());
- return;
- }
-
PaintPreviewFrame rootFrame = buildFrameTreeHierarchy(rootFrameGuid, frameGuids,
- frameContentSize, subFramesCount, subFrameGuids, subFrameClipRects);
+ frameContentSize, scrollOffsets, subFramesCount, subFrameGuids, subFrameClipRects,
+ mIgnoreInitialScrollOffset);
mRootFrameCoordinator = new PlayerFrameCoordinator(mContext, mDelegate, rootFrame.getGuid(),
- rootFrame.getContentWidth(), rootFrame.getContentHeight(), true);
+ rootFrame.getContentWidth(), rootFrame.getContentHeight(),
+ rootFrame.getInitialScrollX(), rootFrame.getInitialScrollY(), true,
+ mPlayerSwipeRefreshHandler);
buildSubFrameCoordinators(mRootFrameCoordinator, rootFrame);
mHostView.addView(mRootFrameCoordinator.getView(),
new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ if (mPlayerSwipeRefreshHandler != null) {
+ mHostView.addView(mPlayerSwipeRefreshHandler.getView());
+ }
TraceEvent.finishAsync(sInitEvent, hashCode());
- mViewReadyCallback.onResult(safeToShow);
+ mViewReadyCallback.run();
}
/**
@@ -88,13 +110,16 @@ public class PlayerManager {
*/
@VisibleForTesting
static PaintPreviewFrame buildFrameTreeHierarchy(UnguessableToken rootFrameGuid,
- UnguessableToken[] frameGuids, int[] frameContentSize, int[] subFramesCount,
- UnguessableToken[] subFrameGuids, int[] subFrameClipRects) {
+ UnguessableToken[] frameGuids, int[] frameContentSize, int[] scrollOffsets,
+ int[] subFramesCount, UnguessableToken[] subFrameGuids, int[] subFrameClipRects,
+ boolean ignoreInitialScrollOffset) {
Map<UnguessableToken, PaintPreviewFrame> framesMap = new HashMap<>();
for (int i = 0; i < frameGuids.length; i++) {
+ int initalScrollX = ignoreInitialScrollOffset ? 0 : scrollOffsets[i * 2];
+ int initalScrollY = ignoreInitialScrollOffset ? 0 : scrollOffsets[(i * 2) + 1];
framesMap.put(frameGuids[i],
- new PaintPreviewFrame(
- frameGuids[i], frameContentSize[i * 2], frameContentSize[(i * 2) + 1]));
+ new PaintPreviewFrame(frameGuids[i], frameContentSize[i * 2],
+ frameContentSize[(i * 2) + 1], initalScrollX, initalScrollY));
}
int subFrameIdIndex = 0;
@@ -130,9 +155,10 @@ public class PlayerManager {
for (int i = 0; i < frame.getSubFrames().length; i++) {
PaintPreviewFrame childFrame = frame.getSubFrames()[i];
- PlayerFrameCoordinator childCoordinator =
- new PlayerFrameCoordinator(mContext, mDelegate, childFrame.getGuid(),
- childFrame.getContentWidth(), childFrame.getContentHeight(), false);
+ PlayerFrameCoordinator childCoordinator = new PlayerFrameCoordinator(mContext,
+ mDelegate, childFrame.getGuid(), childFrame.getContentWidth(),
+ childFrame.getContentHeight(), childFrame.getInitialScrollX(),
+ childFrame.getInitialScrollY(), false, null);
buildSubFrameCoordinators(childCoordinator, childFrame);
frameCoordinator.addSubFrame(childCoordinator, frame.getSubFrameClips()[i]);
}
diff --git a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerSwipeRefreshHandler.java b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerSwipeRefreshHandler.java
new file mode 100644
index 00000000000..3662b5f8560
--- /dev/null
+++ b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerSwipeRefreshHandler.java
@@ -0,0 +1,82 @@
+// Copyright 2020 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.components.paintpreview.player;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+import org.chromium.third_party.android.swiperefresh.SwipeRefreshLayout;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A class for handling overscroll to refresh behavior for the Paint Preview player. This is based
+ * on the modified version of the Android compat library's SwipeRefreshLayout due to the Player's
+ * FrameLayout not behaving like a normal scrolling view.
+ */
+public class PlayerSwipeRefreshHandler implements OverscrollHandler {
+ // The duration of the refresh animation after a refresh signal.
+ private static final int STOP_REFRESH_ANIMATION_DELAY_MS = 500;
+
+ // The modified AppCompat version of the refresh effect.
+ private SwipeRefreshLayout mSwipeRefreshLayout;
+
+ // A handler to delegate refreshes event to.
+ private Runnable mRefreshCallback;
+
+ /*
+ * Constructs a new instance of the handler.
+ *
+ * @param context The Context to create tha handler for.
+ * @param refreshCallback The handler that refresh events are delegated to.
+ */
+ public PlayerSwipeRefreshHandler(Context context, @Nonnull Runnable refreshCallback) {
+ mRefreshCallback = refreshCallback;
+ mSwipeRefreshLayout = new SwipeRefreshLayout(context);
+ mSwipeRefreshLayout.setLayoutParams(
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ // Use the same colors as {@link org.chromium.chrome.browser.SwipeRefreshHandler}.
+ mSwipeRefreshLayout.setProgressBackgroundColorSchemeResource(
+ org.chromium.ui.R.color.default_bg_color_elev_2);
+ mSwipeRefreshLayout.setColorSchemeResources(
+ org.chromium.ui.R.color.default_control_color_active);
+ mSwipeRefreshLayout.setEnabled(true);
+
+ mSwipeRefreshLayout.setOnRefreshListener(() -> {
+ mSwipeRefreshLayout.postDelayed(() -> {
+ mSwipeRefreshLayout.setRefreshing(false);
+ }, STOP_REFRESH_ANIMATION_DELAY_MS);
+ mRefreshCallback.run();
+ });
+ }
+
+ /*
+ * Gets the view that contains the swipe to refresh animations.
+ */
+ public View getView() {
+ return mSwipeRefreshLayout;
+ }
+
+ @Override
+ public boolean start() {
+ return mSwipeRefreshLayout.start();
+ }
+
+ @Override
+ public void pull(float yDelta) {
+ mSwipeRefreshLayout.pull(yDelta);
+ }
+
+ @Override
+ public void release() {
+ mSwipeRefreshLayout.release(true);
+ }
+
+ @Override
+ public void reset() {
+ mSwipeRefreshLayout.reset();
+ }
+}
diff --git a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java
index 275c31ab192..85d7d1dfc74 100644
--- a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java
+++ b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java
@@ -7,13 +7,17 @@ package org.chromium.components.paintpreview.player.frame;
import android.content.Context;
import android.graphics.Rect;
import android.view.View;
-import android.widget.Scroller;
+import android.view.ViewConfiguration;
+import android.widget.OverScroller;
import org.chromium.base.UnguessableToken;
+import org.chromium.components.paintpreview.player.OverscrollHandler;
import org.chromium.components.paintpreview.player.PlayerCompositorDelegate;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+import javax.annotation.Nullable;
+
/**
* Sets up the view and the logic behind it for a Paint Preview frame.
*/
@@ -26,12 +30,18 @@ public class PlayerFrameCoordinator {
* binds them together.
*/
public PlayerFrameCoordinator(Context context, PlayerCompositorDelegate compositorDelegate,
- UnguessableToken frameGuid, int contentWidth, int contentHeight,
- boolean canDetectZoom) {
+ UnguessableToken frameGuid, int contentWidth, int contentHeight, int initialScrollX,
+ int initialScrollY, boolean canDetectZoom,
+ @Nullable OverscrollHandler overscrollHandler) {
PropertyModel model = new PropertyModel.Builder(PlayerFrameProperties.ALL_KEYS).build();
- mMediator = new PlayerFrameMediator(model, compositorDelegate, new Scroller(context),
- frameGuid, contentWidth, contentHeight);
+ OverScroller scroller = new OverScroller(context);
+ scroller.setFriction(ViewConfiguration.getScrollFriction() / 2);
+ mMediator = new PlayerFrameMediator(model, compositorDelegate, scroller, frameGuid,
+ contentWidth, contentHeight, initialScrollX, initialScrollY);
mView = new PlayerFrameView(context, canDetectZoom, mMediator);
+ if (overscrollHandler != null) {
+ mMediator.setOverscrollHandler(overscrollHandler);
+ }
PropertyModelChangeProcessor.create(model, mView, PlayerFrameViewBinder::bind);
}
diff --git a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java
index 756a79d6d67..daf4b27ed32 100644
--- a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java
+++ b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java
@@ -64,6 +64,9 @@ class PlayerFrameGestureDetector
mScaleGestureDetector.onTouchEvent(event);
}
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ mPlayerFrameViewDelegate.onRelease();
+ }
return mGestureDetector.onTouchEvent(event);
}
@@ -133,5 +136,9 @@ class PlayerFrameGestureDetector
}
@Override
- public void onScaleEnd(ScaleGestureDetector detector) {}
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ assert mCanDetectZoom;
+ mPlayerFrameViewDelegate.scaleFinished(
+ detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY());
+ }
}
diff --git a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java
index 500369a6d28..21fe4fc8794 100644
--- a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java
+++ b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java
@@ -5,15 +5,17 @@
package org.chromium.components.paintpreview.player.frame;
import android.graphics.Bitmap;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Handler;
import android.view.View;
-import android.widget.Scroller;
+import android.widget.OverScroller;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.Callback;
import org.chromium.base.UnguessableToken;
+import org.chromium.components.paintpreview.player.OverscrollHandler;
import org.chromium.components.paintpreview.player.PlayerCompositorDelegate;
import org.chromium.ui.modelutil.PropertyModel;
@@ -37,6 +39,8 @@ import java.util.Map;
* </ul>
*/
class PlayerFrameMediator implements PlayerFrameViewDelegate {
+ private static final float MAX_SCALE_FACTOR = 5f;
+
/** The GUID associated with the frame that this class is representing. */
private final UnguessableToken mGuid;
/** The content width inside this frame, at a scale factor of 1. */
@@ -65,7 +69,7 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
private final List<Rect> mVisibleSubFrameScaledRects = new ArrayList<>();
private final PropertyModel mModel;
private final PlayerCompositorDelegate mCompositorDelegate;
- private final Scroller mScroller;
+ private final OverScroller mScroller;
private final Handler mScrollerHandler;
/** The user-visible area for this frame. */
private final Rect mViewportRect = new Rect();
@@ -88,12 +92,24 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
final Map<Float, boolean[][]> mRequiredBitmaps = new HashMap<>();
/** The current scale factor. */
private float mScaleFactor;
+ private float mInitialScaleFactor;
+ private float mUncommittedScaleFactor = 0f;
+ @VisibleForTesting
+ final Matrix mViewportScaleMatrix = new Matrix();
+ private final Matrix mBitmapScaleMatrix = new Matrix();
+
+ /** For swipe-to-refresh logic */
+ private OverscrollHandler mOverscrollHandler;
+ private boolean mIsOverscrolling = false;
+ private float mOverscrollAmount = 0f;
PlayerFrameMediator(PropertyModel model, PlayerCompositorDelegate compositorDelegate,
- Scroller scroller, UnguessableToken frameGuid, int contentWidth, int contentHeight) {
+ OverScroller scroller, UnguessableToken frameGuid, int contentWidth, int contentHeight,
+ int initialScrollX, int initialScrollY) {
mModel = model;
mModel.set(PlayerFrameProperties.SUBFRAME_VIEWS, mVisibleSubFrameViews);
mModel.set(PlayerFrameProperties.SUBFRAME_RECTS, mVisibleSubFrameScaledRects);
+ mModel.set(PlayerFrameProperties.SCALE_MATRIX, mBitmapScaleMatrix);
mCompositorDelegate = compositorDelegate;
mScroller = scroller;
@@ -101,6 +117,8 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
mContentWidth = contentWidth;
mContentHeight = contentHeight;
mScrollerHandler = new Handler();
+ mViewportRect.offset(initialScrollX, initialScrollY);
+ mViewportScaleMatrix.postTranslate(-initialScrollX, -initialScrollY);
}
/**
@@ -116,16 +134,62 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
@Override
public void setLayoutDimensions(int width, int height) {
+ // Don't trigger a re-draw if we are actively scaling.
+ if (!mBitmapScaleMatrix.isIdentity()) {
+ mViewportRect.set(mViewportRect.left, mViewportRect.top, mViewportRect.left + width,
+ mViewportRect.top + height);
+ // Set scale factor to 0 so subframes get the correct scale factor on scale completion.
+ mScaleFactor = 0;
+ return;
+ }
+
// Set initial scale so that content width fits within the layout dimensions.
- float initialScaleFactor = ((float) width) / ((float) mContentWidth);
- updateViewportSize(width, height, mScaleFactor == 0f ? initialScaleFactor : mScaleFactor);
+ mInitialScaleFactor = ((float) width) / ((float) mContentWidth);
+ updateViewportSize(
+ width, height, (mScaleFactor == 0f) ? mInitialScaleFactor : mScaleFactor);
+ }
+
+ @Override
+ public void setBitmapScaleMatrix(Matrix matrix, float scaleFactor) {
+ // Don't update the subframes if the matrix is identity as it will be forcibly recalculated.
+ if (!matrix.isIdentity()) {
+ updateSubFrames(mViewportRect, scaleFactor);
+ }
+ setBitmapScaleMatrixInternal(matrix, scaleFactor);
+ }
+
+ private void setBitmapScaleMatrixInternal(Matrix matrix, float scaleFactor) {
+ float[] matrixValues = new float[9];
+ matrix.getValues(matrixValues);
+ mBitmapScaleMatrix.setValues(matrixValues);
+ Matrix childBitmapScaleMatrix = new Matrix();
+ childBitmapScaleMatrix.setScale(
+ matrixValues[Matrix.MSCALE_X], matrixValues[Matrix.MSCALE_Y]);
+ for (View subFrameView : mVisibleSubFrameViews) {
+ ((PlayerFrameView) subFrameView)
+ .updateDelegateScaleMatrix(childBitmapScaleMatrix, scaleFactor);
+ }
+ mModel.set(PlayerFrameProperties.SCALE_MATRIX, mBitmapScaleMatrix);
+ }
+
+ @Override
+ public void forceRedraw() {
+ mInitialScaleFactor = ((float) mViewportRect.width()) / ((float) mContentWidth);
+ moveViewport(0, 0, (mScaleFactor == 0f) ? mInitialScaleFactor : mScaleFactor);
+ for (View subFrameView : mVisibleSubFrameViews) {
+ ((PlayerFrameView) subFrameView).forceRedraw();
+ }
}
void updateViewportSize(int width, int height, float scaleFactor) {
if (width <= 0 || height <= 0) return;
- mViewportRect.set(mViewportRect.left, mViewportRect.top, mViewportRect.left + width,
- mViewportRect.top + height);
+ // Ensure the viewport is within the bounds of the content.
+ final int left = Math.max(
+ 0, Math.min(mViewportRect.left, Math.round(mContentWidth * scaleFactor) - width));
+ final int top = Math.max(
+ 0, Math.min(mViewportRect.top, Math.round(mContentHeight * scaleFactor) - height));
+ mViewportRect.set(left, top, left + width, top + height);
moveViewport(0, 0, scaleFactor);
}
@@ -157,16 +221,13 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
mRequiredBitmaps.put(scaleFactor, requiredBitmaps);
}
- // If the scale factor is changed, the view should get the correct bitmap matrix.
- if (scaleFactor != mScaleFactor) {
- mModel.set(PlayerFrameProperties.BITMAP_MATRIX, mBitmapMatrix.get(scaleFactor));
- mScaleFactor = scaleFactor;
- }
-
// Update mViewportRect and let the view know. PropertyModelChangeProcessor is smart about
// this and will only update the view if mViewportRect is actually changed.
mViewportRect.offset(distanceX, distanceY);
- updateSubFrames();
+ // Keep translations up to date for the viewport matrix so that future scale operations are
+ // correct.
+ mViewportScaleMatrix.postTranslate(-distanceX, -distanceY);
+ updateSubFrames(mViewportRect, scaleFactor);
mModel.set(PlayerFrameProperties.TILE_DIMENSIONS, tileDimensions);
mModel.set(PlayerFrameProperties.VIEWPORT, mViewportRect);
@@ -180,10 +241,12 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
// Request bitmaps for tiles inside the view port that don't already have a bitmap.
final int tileWidth = tileDimensions[0];
final int tileHeight = tileDimensions[1];
- final int colStart = mViewportRect.left / tileWidth;
- final int colEnd = (int) Math.ceil((double) mViewportRect.right / tileWidth);
- final int rowStart = mViewportRect.top / tileHeight;
- final int rowEnd = (int) Math.ceil((double) mViewportRect.bottom / tileHeight);
+ final int colStart = Math.max(0, (int) Math.floor((double) mViewportRect.left / tileWidth));
+ final int colEnd = Math.min(requiredBitmaps[0].length,
+ (int) Math.ceil((double) mViewportRect.right / tileWidth));
+ final int rowStart = Math.max(0, (int) Math.floor((double) mViewportRect.top / tileHeight));
+ final int rowEnd = Math.min(requiredBitmaps.length,
+ (int) Math.ceil((double) mViewportRect.bottom / tileHeight));
for (int col = colStart; col < colEnd; col++) {
for (int row = rowStart; row < rowEnd; row++) {
int tileLeft = col * tileWidth;
@@ -201,17 +264,27 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
requestBitmapForAdjacentTiles(tileWidth, tileHeight, row, col, scaleFactor);
}
}
+
+ // If the scale factor is changed, the view should get the correct bitmap matrix.
+ // TODO(crbug/1090804): "Double buffer" this such that there is no period where there is a
+ // blank screen between scale finishing and new bitmaps being fetched.
+ if (scaleFactor != mScaleFactor) {
+ mModel.set(PlayerFrameProperties.BITMAP_MATRIX, mBitmapMatrix.get(scaleFactor));
+ mBitmapMatrix.remove(mScaleFactor); // Eagerly free to avoid OOM.
+ mRequiredBitmaps.remove(mScaleFactor);
+ mScaleFactor = scaleFactor;
+ }
}
- private void updateSubFrames() {
+ private void updateSubFrames(Rect viewport, float scaleFactor) {
mVisibleSubFrameViews.clear();
mVisibleSubFrameScaledRects.clear();
for (int i = 0; i < mSubFrameRects.size(); i++) {
Rect subFrameScaledRect = mSubFrameScaledRects.get(i);
- scaleRect(mSubFrameRects.get(i), subFrameScaledRect, mScaleFactor);
- if (Rect.intersects(subFrameScaledRect, mViewportRect)) {
- int transformedLeft = subFrameScaledRect.left - mViewportRect.left;
- int transformedTop = subFrameScaledRect.top - mViewportRect.top;
+ scaleRect(mSubFrameRects.get(i), subFrameScaledRect, scaleFactor);
+ if (Rect.intersects(subFrameScaledRect, viewport)) {
+ int transformedLeft = subFrameScaledRect.left - viewport.left;
+ int transformedTop = subFrameScaledRect.top - viewport.top;
subFrameScaledRect.set(transformedLeft, transformedTop,
transformedLeft + subFrameScaledRect.width(),
transformedTop + subFrameScaledRect.height());
@@ -278,6 +351,8 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
private void deleteUnrequiredBitmaps(float scaleFactor) {
Bitmap[][] bitmapMatrix = mBitmapMatrix.get(scaleFactor);
boolean[][] requiredBitmaps = mRequiredBitmaps.get(scaleFactor);
+ if (bitmapMatrix == null || requiredBitmaps == null) return;
+
for (int row = 0; row < bitmapMatrix.length; row++) {
for (int col = 0; col < bitmapMatrix[row].length; col++) {
Bitmap bitmap = bitmapMatrix[row][col];
@@ -299,10 +374,46 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
@Override
public boolean scrollBy(float distanceX, float distanceY) {
mScroller.forceFinished(true);
+
return scrollByInternal(distanceX, distanceY);
}
+ @Override
+ public void onRelease() {
+ if (mOverscrollHandler == null || !mIsOverscrolling) return;
+
+ mOverscrollHandler.release();
+ mIsOverscrolling = false;
+ mOverscrollAmount = 0.0f;
+ }
+
+ private boolean maybeHandleOverscroll(float distanceY) {
+ if (mOverscrollHandler == null || mViewportRect.top != 0) return false;
+
+ // Ignore if there is no active overscroll and the direction is down.
+ if (!mIsOverscrolling && distanceY <= 0) return false;
+
+ mOverscrollAmount += distanceY;
+
+ // If the overscroll is completely eased off the cancel the event.
+ if (mOverscrollAmount <= 0) {
+ mIsOverscrolling = false;
+ mOverscrollHandler.reset();
+ return false;
+ }
+
+ // Start the overscroll event if the scroll direction is correct and one isn't active.
+ if (!mIsOverscrolling && distanceY > 0) {
+ mOverscrollAmount = distanceY;
+ mIsOverscrolling = mOverscrollHandler.start();
+ }
+ mOverscrollHandler.pull(distanceY);
+ return mIsOverscrolling;
+ }
+
private boolean scrollByInternal(float distanceX, float distanceY) {
+ if (maybeHandleOverscroll(-distanceY)) return true;
+
int validDistanceX = 0;
int validDistanceY = 0;
float scaledContentWidth = mContentWidth * mScaleFactor;
@@ -319,16 +430,147 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
validDistanceY = (int) Math.min(distanceY, scaledContentHeight - mViewportRect.bottom);
}
- if (validDistanceX == 0 && validDistanceY == 0) return false;
+ if (validDistanceX == 0 && validDistanceY == 0) {
+ return false;
+ }
moveViewport(validDistanceX, validDistanceY, mScaleFactor);
return true;
}
+ /**
+ * How scale for the paint preview player works.
+ *
+ * There are two reference frames:
+ * - The currently loaded bitmaps, which changes as scaling happens.
+ * - The viewport, which is static until scaling is finished.
+ *
+ * During {@link #scaleBy()} the gesture is still ongoing.
+ *
+ * On each scale gesture the |scaleFactor| is applied to |mUncommittedScaleFactor| which
+ * accumulates the scale starting from the currently committed |mScaleFactor|. This is
+ * committed when {@link #scaleFinished()} event occurs. This is for the viewport reference
+ * frame. |mViewportScaleMatrix| also accumulates the transforms to track the translation
+ * behavior.
+ *
+ * |mBitmapScaleMatrix| tracks scaling from the perspective of the bitmaps. This is used to
+ * transform the canvas the bitmaps are painted on such that scaled images can be shown
+ * mid-gesture.
+ *
+ * Each subframe is updated with a new rect based on the interim scale factor and when the
+ * matrix is set in {@link PlayerFrameView#updateMatrix} the subframe matricies are recursively
+ * updated.
+ *
+ * On {@link #scaleFinished()} the gesture is now considered finished.
+ *
+ * The values of |mViewportScaleMatrix| are extracted to get the final translation applied to
+ * the viewport. The transform for the bitmaps (that is |mBitmapScaleMatrix|) is cancelled.
+ *
+ * During {@link #moveViewport()} new bitmaps are requested for the main frame and subframes
+ * to improve quality.
+ *
+ * NOTE: |mViewportScaleMatrix| also need to consume scroll events in order to keep track of
+ * the correct translation.
+ */
@Override
public boolean scaleBy(float scaleFactor, float focalPointX, float focalPointY) {
- // TODO(crbug.com/1020702): Add support for zooming.
- return false;
+ // This is filtered to only apply to the top level view upstream.
+ if (mUncommittedScaleFactor == 0f) {
+ mUncommittedScaleFactor = mScaleFactor;
+ }
+ mUncommittedScaleFactor *= scaleFactor;
+
+ // Don't scale outside of the acceptable range. The value is still accumulated such that the
+ // continuous gesture feels smooth.
+ if (mUncommittedScaleFactor < mInitialScaleFactor) return true;
+ if (mUncommittedScaleFactor > MAX_SCALE_FACTOR) return true;
+
+ // TODO(crbug/1090804): trigger a fetch of new bitmaps periodically when zooming out.
+
+ mViewportScaleMatrix.postScale(scaleFactor, scaleFactor, focalPointX, focalPointY);
+ mBitmapScaleMatrix.postScale(scaleFactor, scaleFactor, focalPointX, focalPointY);
+
+ float[] viewportScaleMatrixValues = new float[9];
+ mViewportScaleMatrix.getValues(viewportScaleMatrixValues);
+
+ float[] bitmapScaleMatrixValues = new float[9];
+ mBitmapScaleMatrix.getValues(bitmapScaleMatrixValues);
+
+ // It is possible the scale pushed the viewport outside the content bounds. These new values
+ // are forced to be within bounds.
+ final float newX = -1f
+ * Math.max(0f,
+ Math.min(-viewportScaleMatrixValues[Matrix.MTRANS_X],
+ mContentWidth * mUncommittedScaleFactor - mViewportRect.width()));
+ final float newY = -1f
+ * Math.max(0f,
+ Math.min(-viewportScaleMatrixValues[Matrix.MTRANS_Y],
+ mContentHeight * mUncommittedScaleFactor - mViewportRect.height()));
+ final int newXRounded = Math.abs(Math.round(newX));
+ final int newYRounded = Math.abs(Math.round(newY));
+ updateSubFrames(new Rect(newXRounded, newYRounded, newXRounded + mViewportRect.width(),
+ newYRounded + mViewportRect.height()),
+ mUncommittedScaleFactor);
+
+ if (newX != viewportScaleMatrixValues[Matrix.MTRANS_X]
+ || newY != viewportScaleMatrixValues[Matrix.MTRANS_Y]) {
+ // This is the delta required to force the viewport to be inside the bounds of the
+ // content.
+ final float deltaX = newX - viewportScaleMatrixValues[Matrix.MTRANS_X];
+ final float deltaY = newY - viewportScaleMatrixValues[Matrix.MTRANS_Y];
+
+ // Directly used the forced bounds of the viewport reference frame for the viewport
+ // scale matrix.
+ viewportScaleMatrixValues[Matrix.MTRANS_X] = newX;
+ viewportScaleMatrixValues[Matrix.MTRANS_Y] = newY;
+ mViewportScaleMatrix.setValues(viewportScaleMatrixValues);
+
+ // For the bitmap matrix we only want the delta as its position will be different as the
+ // coordinates are bitmap relative.
+ bitmapScaleMatrixValues[Matrix.MTRANS_X] += deltaX;
+ bitmapScaleMatrixValues[Matrix.MTRANS_Y] += deltaY;
+ mBitmapScaleMatrix.setValues(bitmapScaleMatrixValues);
+ }
+ setBitmapScaleMatrixInternal(mBitmapScaleMatrix, mUncommittedScaleFactor);
+
+ return true;
+ }
+
+ @Override
+ public boolean scaleFinished(float scaleFactor, float focalPointX, float focalPointY) {
+ // Remove the bitmap scaling to avoid issues when new bitmaps are requested.
+ // TODO(crbug/1090804): Defer clearing this so that double buffering can occur.
+ mBitmapScaleMatrix.reset();
+ setBitmapScaleMatrixInternal(mBitmapScaleMatrix, 1f);
+
+ final float finalScaleFactor =
+ Math.max(mInitialScaleFactor, Math.min(mUncommittedScaleFactor, MAX_SCALE_FACTOR));
+ mUncommittedScaleFactor = 0f;
+
+ float[] matrixValues = new float[9];
+ mViewportScaleMatrix.getValues(matrixValues);
+ final float newX = Math.max(0f,
+ Math.min(-matrixValues[Matrix.MTRANS_X],
+ mContentWidth * finalScaleFactor - mViewportRect.width()));
+ final float newY = Math.max(0f,
+ Math.min(-matrixValues[Matrix.MTRANS_Y],
+ mContentHeight * finalScaleFactor - mViewportRect.height()));
+ matrixValues[Matrix.MTRANS_X] = -newX;
+ matrixValues[Matrix.MTRANS_Y] = -newY;
+ matrixValues[Matrix.MSCALE_X] = finalScaleFactor;
+ matrixValues[Matrix.MSCALE_Y] = finalScaleFactor;
+ mViewportScaleMatrix.setValues(matrixValues);
+ final int newXRounded = Math.round(newX);
+ final int newYRounded = Math.round(newY);
+ mViewportRect.set(newXRounded, newYRounded, newXRounded + mViewportRect.width(),
+ newYRounded + mViewportRect.height());
+
+ moveViewport(0, 0, finalScaleFactor);
+ for (View subFrameView : mVisibleSubFrameViews) {
+ ((PlayerFrameView) subFrameView).forceRedraw();
+ }
+
+ return true;
}
@Override
@@ -353,6 +595,10 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
return true;
}
+ public void setOverscrollHandler(OverscrollHandler overscrollHandler) {
+ mOverscrollHandler = overscrollHandler;
+ }
+
/**
* Handles a fling update by computing the next scroll offset and programmatically scrolling.
*/
@@ -389,16 +635,25 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
*/
@Override
public void onResult(Bitmap result) {
- assert mBitmapMatrix.get(mRequestScaleFactor) != null;
+ if (result == null) {
+ run();
+ return;
+ }
+ Bitmap[][] bitmapMatrix = mBitmapMatrix.get(mRequestScaleFactor);
+ boolean[][] requiredBitmaps = mRequiredBitmaps.get(mRequestScaleFactor);
+ if (bitmapMatrix == null || requiredBitmaps == null) {
+ result.recycle();
+ return;
+ }
+
assert mBitmapMatrix.get(mRequestScaleFactor)[mRequestRow][mRequestCol] == null;
assert mPendingBitmapRequests.get(mRequestScaleFactor)[mRequestRow][mRequestCol];
mPendingBitmapRequests.get(mScaleFactor)[mRequestRow][mRequestCol] = false;
- if (mRequiredBitmaps.get(mRequestScaleFactor)[mRequestRow][mRequestCol]) {
- mBitmapMatrix.get(mScaleFactor)[mRequestRow][mRequestCol] = result;
+ if (requiredBitmaps[mRequestRow][mRequestCol]) {
+ bitmapMatrix[mRequestRow][mRequestCol] = result;
if (PlayerFrameMediator.this.mScaleFactor == mRequestScaleFactor) {
- mModel.set(
- PlayerFrameProperties.BITMAP_MATRIX, mBitmapMatrix.get(mScaleFactor));
+ mModel.set(PlayerFrameProperties.BITMAP_MATRIX, bitmapMatrix);
}
} else {
result.recycle();
diff --git a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameProperties.java b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameProperties.java
index 6404d7f7459..e8f825d4f28 100644
--- a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameProperties.java
+++ b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameProperties.java
@@ -5,6 +5,7 @@
package org.chromium.components.paintpreview.player.frame;
import android.graphics.Bitmap;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.View;
@@ -39,6 +40,9 @@ class PlayerFrameProperties {
*/
static final PropertyModel.WritableObjectPropertyKey<List<Rect>> SUBFRAME_RECTS =
new PropertyModel.WritableObjectPropertyKey<>();
+ /** The matrix to apply to the view before a zoom is committed. */
+ static final PropertyModel.WritableObjectPropertyKey<Matrix> SCALE_MATRIX =
+ new PropertyModel.WritableObjectPropertyKey<>(true);
static final PropertyKey[] ALL_KEYS = {
- BITMAP_MATRIX, TILE_DIMENSIONS, VIEWPORT, SUBFRAME_VIEWS, SUBFRAME_RECTS};
+ BITMAP_MATRIX, TILE_DIMENSIONS, VIEWPORT, SUBFRAME_VIEWS, SUBFRAME_RECTS, SCALE_MATRIX};
}
diff --git a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java
index bfd47130bae..1d47081ef66 100644
--- a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java
+++ b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java
@@ -7,6 +7,7 @@ package org.chromium.components.paintpreview.player.frame;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
@@ -28,6 +29,7 @@ class PlayerFrameView extends FrameLayout {
private PlayerFrameViewDelegate mDelegate;
private List<View> mSubFrameViews;
private List<Rect> mSubFrameRects;
+ private Matrix mScaleMatrix;
/**
* @param context Used for initialization.
@@ -72,6 +74,47 @@ class PlayerFrameView extends FrameLayout {
void updateViewPort(int left, int top, int right, int bottom) {
mBitmapPainter.updateViewPort(left, top, right, bottom);
+ layoutSubframes();
+ }
+
+ void updateBitmapMatrix(Bitmap[][] bitmapMatrix) {
+ mBitmapPainter.updateBitmapMatrix(bitmapMatrix);
+ }
+
+ void updateTileDimensions(int[] tileDimensions) {
+ mBitmapPainter.updateTileDimensions(tileDimensions);
+ }
+
+ void updateScaleMatrix(Matrix matrix) {
+ mScaleMatrix = matrix;
+ if (mScaleMatrix.isIdentity()) return;
+
+ postInvalidate();
+ layoutSubframes();
+ }
+
+ void updateDelegateScaleMatrix(Matrix matrix, float scaleFactor) {
+ mDelegate.setBitmapScaleMatrix(matrix, scaleFactor);
+ }
+
+ void forceRedraw() {
+ mDelegate.forceRedraw();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.save();
+ canvas.setMatrix(mScaleMatrix);
+ mBitmapPainter.onDraw(canvas);
+ canvas.restore();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
+ }
+
+ private void layoutSubframes() {
// Remove all views if there are no sub-frames.
if (mSubFrameViews == null || mSubFrameRects == null) {
removeAllViews();
@@ -97,22 +140,4 @@ class PlayerFrameView extends FrameLayout {
}
}
}
-
- void updateBitmapMatrix(Bitmap[][] bitmapMatrix) {
- mBitmapPainter.updateBitmapMatrix(bitmapMatrix);
- }
-
- void updateTileDimensions(int[] tileDimensions) {
- mBitmapPainter.updateTileDimensions(tileDimensions);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- mBitmapPainter.onDraw(canvas);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
- }
}
diff --git a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewBinder.java b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewBinder.java
index d18f49d0f10..2ae8bc0c451 100644
--- a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewBinder.java
+++ b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewBinder.java
@@ -25,6 +25,8 @@ class PlayerFrameViewBinder {
view.updateSubFrameViews(model.get(PlayerFrameProperties.SUBFRAME_VIEWS));
} else if (key.equals(PlayerFrameProperties.SUBFRAME_RECTS)) {
view.updateSubFrameRects(model.get(PlayerFrameProperties.SUBFRAME_RECTS));
+ } else if (key.equals(PlayerFrameProperties.SCALE_MATRIX)) {
+ view.updateScaleMatrix(model.get(PlayerFrameProperties.SCALE_MATRIX));
}
}
}
diff --git a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewDelegate.java b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewDelegate.java
index f671c1ca108..3b43a9c6dd5 100644
--- a/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewDelegate.java
+++ b/chromium/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewDelegate.java
@@ -4,6 +4,8 @@
package org.chromium.components.paintpreview.player.frame;
+import android.graphics.Matrix;
+
/**
* Used by {@link PlayerFrameView} to delegate view events to {@link PlayerFrameMediator}.
*/
@@ -12,6 +14,17 @@ interface PlayerFrameViewDelegate {
* Called on layout with the attributed width and height.
*/
void setLayoutDimensions(int width, int height);
+
+ /**
+ * Called to set the bitmap scale matrix for this frame.
+ */
+ void setBitmapScaleMatrix(Matrix matrix, float scaleFactor);
+
+ /**
+ * Triggers a redraw if one is needed.
+ */
+ void forceRedraw();
+
/**
* Called when a scroll gesture is performed.
* @param distanceX Horizontal scroll values in pixels.
@@ -19,6 +32,7 @@ interface PlayerFrameViewDelegate {
* @return Whether this scroll event was consumed.
*/
boolean scrollBy(float distanceX, float distanceY);
+
/**
* Called when a scale gesture is performed.
* @return Whether this scale event was consumed.
@@ -26,6 +40,12 @@ interface PlayerFrameViewDelegate {
boolean scaleBy(float scaleFactor, float focalPointX, float focalPointY);
/**
+ * Called when a scale gesture is finished.
+ * @return Whether this scale event was consumed.
+ */
+ boolean scaleFinished(float scaleFactor, float focalPointX, float focalPointY);
+
+ /**
* Called when a single tap gesture is performed.
* @param x X coordinate of the point clicked.
* @param y Y coordinate of the point clicked.
@@ -39,4 +59,9 @@ interface PlayerFrameViewDelegate {
* @return Whether this fling was consumed.
*/
boolean onFling(float velocityX, float velocityY);
+
+ /**
+ * Called when a gesture is released.
+ */
+ void onRelease();
}
diff --git a/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java b/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
index 8e52536bc7b..18066e021a1 100644
--- a/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
+++ b/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
@@ -6,11 +6,13 @@ package org.chromium.components.paintpreview.player;
import android.graphics.Rect;
import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
import android.support.test.uiautomator.UiDevice;
import android.view.View;
import android.view.ViewGroup;
+import androidx.test.filters.MediumTest;
+
+import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
@@ -22,6 +24,7 @@ import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.ScalableTimeout;
import org.chromium.base.test.util.UrlUtils;
import org.chromium.content_public.browser.UiThreadTaskTraits;
+import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.ui.test.util.DummyUiActivityTestCase;
import org.chromium.url.GURL;
@@ -51,6 +54,7 @@ public class PaintPreviewPlayerTest extends DummyUiActivityTestCase {
private PlayerManager mPlayerManager;
private TestLinkClickHandler mLinkClickHandler;
+ private CallbackHelper mRefreshedCallback;
/**
* LinkClickHandler implementation for caching the last URL that was clicked.
@@ -86,17 +90,14 @@ public class PaintPreviewPlayerTest extends DummyUiActivityTestCase {
final View activityContentView = getActivity().findViewById(android.R.id.content);
// Assert that the player view has the same dimensions as the content view.
- CriteriaHelper.pollUiThread(
- () -> {
- boolean contentSizeNonZero = activityContentView.getWidth() > 0
- && activityContentView.getHeight() > 0;
- boolean viewSizeMatchContent =
- activityContentView.getWidth() == playerHostView.getWidth()
- && activityContentView.getHeight() == playerHostView.getHeight();
- return contentSizeNonZero && viewSizeMatchContent;
- },
- "Player size doesn't match R.id.content", TIMEOUT_MS,
- CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+ CriteriaHelper.pollUiThread(() -> {
+ Criteria.checkThat(activityContentView.getWidth(), Matchers.greaterThan(0));
+ Criteria.checkThat(activityContentView.getHeight(), Matchers.greaterThan(0));
+ Criteria.checkThat(
+ activityContentView.getWidth(), Matchers.is(playerHostView.getWidth()));
+ Criteria.checkThat(
+ activityContentView.getHeight(), Matchers.is(playerHostView.getHeight()));
+ }, TIMEOUT_MS, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
}
/**
@@ -126,23 +127,70 @@ public class PaintPreviewPlayerTest extends DummyUiActivityTestCase {
assertLinkUrl(playerHostView, 422, 4965, TEST_OUT_OF_VIEWPORT_LINK_URL);
}
- /**
- * Scrolls to the bottom fo the paint preview.
- */
- private void scrollToBottom() {
+ @Test
+ @MediumTest
+ public void overscrollRefreshTest() throws Exception {
+ initPlayerManager();
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
int deviceHeight = uiDevice.getDisplayHeight();
+ int statusBarHeight = statusBarHeight();
+ int navigationBarHeight = navigationBarHeight();
+ int padding = 20;
+ int toY = deviceHeight - navigationBarHeight - padding;
+ int fromY = statusBarHeight + padding;
+ uiDevice.swipe(50, fromY, 50, toY, 5);
+ mRefreshedCallback.waitForFirst();
+ }
+
+ /**
+ * Tests that an initialization failure is reported properly.
+ */
+ @Test
+ @MediumTest
+ public void initializationCallbackErrorReported() throws Exception {
+ CallbackHelper compositorErrorCallback = new CallbackHelper();
+ mLinkClickHandler = new TestLinkClickHandler();
+ PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
+ PaintPreviewTestService service =
+ new PaintPreviewTestService(UrlUtils.getIsolatedTestFilePath(TEST_DATA_DIR));
+ // Use the wrong URL to simulate a failure.
+ mPlayerManager = new PlayerManager(new GURL("about:blank"), getActivity(), service,
+ TEST_DIRECTORY_KEY, mLinkClickHandler,
+ () -> { Assert.fail("Unexpected overscroll refresh attempted."); },
+ () -> {
+ Assert.fail("View Ready callback occurred, but expected a failure.");
+ },
+ 0xffffffff, () -> { compositorErrorCallback.notifyCalled(); }, false);
+ });
+ compositorErrorCallback.waitForFirst();
+ }
+
+ private int statusBarHeight() {
Rect visibleContentRect = new Rect();
getActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(visibleContentRect);
- int statusBarHeight = visibleContentRect.top;
+ return visibleContentRect.top;
+ }
+ private int navigationBarHeight() {
int navigationBarHeight = 100;
int resourceId = getActivity().getResources().getIdentifier(
"navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
navigationBarHeight = getActivity().getResources().getDimensionPixelSize(resourceId);
}
+ return navigationBarHeight;
+ }
+
+ /**
+ * Scrolls to the bottom fo the paint preview.
+ */
+ private void scrollToBottom() {
+ UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ int deviceHeight = uiDevice.getDisplayHeight();
+
+ int statusBarHeight = statusBarHeight();
+ int navigationBarHeight = navigationBarHeight();
int padding = 20;
int swipeSteps = 5;
@@ -160,24 +208,38 @@ public class PaintPreviewPlayerTest extends DummyUiActivityTestCase {
private void initPlayerManager() {
mLinkClickHandler = new TestLinkClickHandler();
+ mRefreshedCallback = new CallbackHelper();
+ CallbackHelper viewReady = new CallbackHelper();
PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
PaintPreviewTestService service =
new PaintPreviewTestService(UrlUtils.getIsolatedTestFilePath(TEST_DATA_DIR));
mPlayerManager = new PlayerManager(new GURL(TEST_URL), getActivity(), service,
- TEST_DIRECTORY_KEY, mLinkClickHandler, Assert::assertTrue, 0xffffffff);
+ TEST_DIRECTORY_KEY, mLinkClickHandler,
+ () -> { mRefreshedCallback.notifyCalled(); },
+ () -> { viewReady.notifyCalled(); },
+ 0xffffffff, () -> { Assert.fail("Compositor initialization failed."); },
+ false);
getActivity().setContentView(mPlayerManager.getView());
});
// Wait until PlayerManager is initialized.
- CriteriaHelper.pollUiThread(() -> mPlayerManager != null,
- "PlayerManager was not initialized.", TIMEOUT_MS,
- CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+ CriteriaHelper.pollUiThread(() -> {
+ Criteria.checkThat(
+ "PlayerManager was not initialized.", mPlayerManager, Matchers.notNullValue());
+ }, TIMEOUT_MS, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+
+ try {
+ viewReady.waitForFirst();
+ } catch (Exception e) {
+ Assert.fail("View ready was not called.");
+ }
// Assert that the player view is added to the player host view.
- CriteriaHelper.pollUiThread(
- () -> ((ViewGroup) mPlayerManager.getView()).getChildCount() > 0,
- "Player view is not added to the host view.", TIMEOUT_MS,
- CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+ CriteriaHelper.pollUiThread(() -> {
+ Criteria.checkThat("Player view is not added to the host view.",
+ ((ViewGroup) mPlayerManager.getView()).getChildCount(),
+ Matchers.greaterThan(0));
+ }, TIMEOUT_MS, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
}
/*
@@ -209,18 +271,11 @@ public class PaintPreviewPlayerTest extends DummyUiActivityTestCase {
UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
device.click(scaledX + locationXY[0], scaledY + locationXY[1]);
- CriteriaHelper.pollUiThread(
- ()
- -> {
- GURL url = mLinkClickHandler.mUrl;
- if (url == null) return false;
-
- return url.getSpec().equals(expectedUrl);
- },
- "Link press on abs (" + x + ", " + y + ") failed. Expected: " + expectedUrl
- + ", found: "
- + (mLinkClickHandler.mUrl == null ? null
- : mLinkClickHandler.mUrl.getSpec()),
- TIMEOUT_MS, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+ CriteriaHelper.pollUiThread(() -> {
+ GURL url = mLinkClickHandler.mUrl;
+ String msg = "Link press on abs (" + x + ", " + y + ") failed.";
+ Criteria.checkThat(msg, url, Matchers.notNullValue());
+ Criteria.checkThat(msg, url.getSpec(), Matchers.is(expectedUrl));
+ }, TIMEOUT_MS, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
}
}
diff --git a/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewTestRule.java b/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewTestRule.java
index 422d38d76e1..fb583dbae53 100644
--- a/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewTestRule.java
+++ b/chromium/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewTestRule.java
@@ -4,43 +4,39 @@
package org.chromium.components.paintpreview.player;
+import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
-import org.chromium.components.signin.AccountManagerFacadeImpl;
import org.chromium.components.signin.AccountManagerFacadeProvider;
-import org.chromium.components.signin.test.util.FakeAccountManagerDelegate;
-import org.chromium.content_public.browser.test.NativeLibraryTestRule;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.components.signin.test.util.FakeAccountManagerFacade;
+import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
/**
* Loads native and initializes the browser process for Paint Preview instrumentation tests.
*/
-public class PaintPreviewTestRule extends NativeLibraryTestRule {
- private FakeAccountManagerDelegate mAccountManager;
-
+public class PaintPreviewTestRule implements TestRule {
/**
* {@link AccountManagerFacadeProvider#getInstance()} is called in the browser initialization
* path. If we don't mock {@link AccountManagerFacade}, we'll run into a failed assertion.
*/
private void setUp() {
- mAccountManager = new FakeAccountManagerDelegate(
- FakeAccountManagerDelegate.DISABLE_PROFILE_DATA_SOURCE);
- TestThreadUtils.runOnUiThreadBlocking(() -> {
- AccountManagerFacadeProvider.setInstanceForTests(
- new AccountManagerFacadeImpl(mAccountManager));
- });
- loadNativeLibraryAndInitBrowserProcess();
+ AccountManagerFacadeProvider.setInstanceForTests(new FakeAccountManagerFacade(null));
+ NativeLibraryTestUtils.loadNativeLibraryAndInitBrowserProcess();
}
@Override
public Statement apply(final Statement base, Description description) {
- return super.apply(new Statement() {
+ return new Statement() {
@Override
public void evaluate() throws Throwable {
setUp();
- base.evaluate();
+ try {
+ base.evaluate();
+ } finally {
+ AccountManagerFacadeProvider.resetInstanceForTests();
+ }
}
- }, description);
+ };
}
}
diff --git a/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/PlayerManagerTest.java b/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/PlayerManagerTest.java
index ba583b83118..b7a2c0205da 100644
--- a/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/PlayerManagerTest.java
+++ b/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/PlayerManagerTest.java
@@ -36,9 +36,10 @@ public class PlayerManagerTest {
public void testFrameHierarchyBuilderNoSubFrames() {
UnguessableToken token = makeToken(3728193L, 3283974L);
PaintPreviewFrame generatedFrame1 = PlayerManager.buildFrameTreeHierarchy(token,
- new UnguessableToken[] {token}, new int[] {500, 600}, new int[] {0}, null, null);
+ new UnguessableToken[] {token}, new int[] {500, 600}, new int[] {100, 200},
+ new int[] {0}, null, null, false);
PaintPreviewFrame expectedFrame1 = PaintPreviewFrame.createInstanceForTest(
- token, 500, 600, new PaintPreviewFrame[] {}, new Rect[] {});
+ token, 500, 600, 100, 200, new PaintPreviewFrame[] {}, new Rect[] {});
Assert.assertEquals(expectedFrame1, generatedFrame1);
}
@@ -52,12 +53,12 @@ public class PlayerManagerTest {
UnguessableToken subframeToken = makeToken(123982L, 637846L);
PaintPreviewFrame generatedFrame2 = PlayerManager.buildFrameTreeHierarchy(mainToken,
new UnguessableToken[] {mainToken, subframeToken}, new int[] {500, 600, 100, 200},
- new int[] {1, 0}, new UnguessableToken[] {subframeToken},
- new int[] {10, 10, 50, 50});
+ new int[] {50, 60, 0, 5}, new int[] {1, 0}, new UnguessableToken[] {subframeToken},
+ new int[] {10, 10, 50, 50}, false);
PaintPreviewFrame expectedFrame2Subframe = PaintPreviewFrame.createInstanceForTest(
- subframeToken, 100, 200, new PaintPreviewFrame[] {}, new Rect[] {});
+ subframeToken, 100, 200, 0, 5, new PaintPreviewFrame[] {}, new Rect[] {});
PaintPreviewFrame expectedFrame2 = PaintPreviewFrame.createInstanceForTest(mainToken, 500,
- 600, new PaintPreviewFrame[] {expectedFrame2Subframe},
+ 600, 50, 60, new PaintPreviewFrame[] {expectedFrame2Subframe},
new Rect[] {new Rect(10, 10, 60, 60)});
Assert.assertEquals(expectedFrame2, generatedFrame2);
}
@@ -75,17 +76,18 @@ public class PlayerManagerTest {
UnguessableToken subframe2Token = makeToken(989272L, 3789489L);
PaintPreviewFrame generatedFrame3 = PlayerManager.buildFrameTreeHierarchy(mainToken,
new UnguessableToken[] {mainToken, subframe1Token, subframe2Token},
- new int[] {500, 600, 200, 100, 10, 20}, new int[] {1, 2, 0},
+ new int[] {500, 600, 200, 100, 10, 20}, new int[] {50, 60, 20, 10, 1, 2},
+ new int[] {1, 2, 0},
new UnguessableToken[] {subframe1Token, subframe2Token, subframe2Token},
- new int[] {50, 60, 100, 150, 10, 15, 20, 25, 30, 35, 5, 15});
+ new int[] {50, 60, 100, 150, 10, 15, 20, 25, 30, 35, 5, 15}, false);
PaintPreviewFrame expectedFrame3Subframe2 = PaintPreviewFrame.createInstanceForTest(
- subframe2Token, 10, 20, new PaintPreviewFrame[] {}, new Rect[] {});
+ subframe2Token, 10, 20, 1, 2, new PaintPreviewFrame[] {}, new Rect[] {});
PaintPreviewFrame expectedFrame3Subframe1 =
- PaintPreviewFrame.createInstanceForTest(subframe1Token, 200, 100,
+ PaintPreviewFrame.createInstanceForTest(subframe1Token, 200, 100, 20, 10,
new PaintPreviewFrame[] {expectedFrame3Subframe2, expectedFrame3Subframe2},
new Rect[] {new Rect(10, 15, 30, 40), new Rect(30, 35, 35, 50)});
PaintPreviewFrame expectedFrame3 = PaintPreviewFrame.createInstanceForTest(mainToken, 500,
- 600, new PaintPreviewFrame[] {expectedFrame3Subframe1},
+ 600, 50, 60, new PaintPreviewFrame[] {expectedFrame3Subframe1},
new Rect[] {new Rect(50, 60, 150, 210)});
Assert.assertEquals(expectedFrame3, generatedFrame3);
}
diff --git a/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PaintPreviewCustomFlingingShadowScroller.java b/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PaintPreviewCustomFlingingShadowScroller.java
index 26a2eecaae1..2a2525d25c3 100644
--- a/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PaintPreviewCustomFlingingShadowScroller.java
+++ b/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PaintPreviewCustomFlingingShadowScroller.java
@@ -5,7 +5,7 @@
package org.chromium.components.paintpreview.player.frame;
import android.content.Context;
-import android.widget.Scroller;
+import android.widget.OverScroller;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@@ -13,7 +13,7 @@ import org.robolectric.annotation.Implements;
/**
* A custom shadow of {@link Scroller} that supports fake flinging.
*/
-@Implements(Scroller.class)
+@Implements(OverScroller.class)
public class PaintPreviewCustomFlingingShadowScroller {
private int mFinalX;
private int mFinalY;
diff --git a/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java b/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
index f9badc92c0f..ec49bee8b2e 100644
--- a/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
+++ b/chromium/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
@@ -4,12 +4,17 @@
package org.chromium.components.paintpreview.player.frame;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
import android.graphics.Bitmap;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Parcel;
import android.util.Pair;
import android.view.View;
-import android.widget.Scroller;
+import android.widget.OverScroller;
import androidx.annotation.NonNull;
@@ -17,6 +22,7 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;
@@ -44,7 +50,7 @@ public class PlayerFrameMediatorTest {
private UnguessableToken mFrameGuid;
private PropertyModel mModel;
private TestPlayerCompositorDelegate mCompositorDelegate;
- private Scroller mScroller;
+ private OverScroller mScroller;
private PlayerFrameMediator mMediator;
/**
@@ -160,14 +166,27 @@ public class PlayerFrameMediatorTest {
}
}
+ private class MatrixMatcher implements ArgumentMatcher<Matrix> {
+ private Matrix mLeft;
+
+ MatrixMatcher(Matrix left) {
+ mLeft = left;
+ }
+
+ @Override
+ public boolean matches(Matrix right) {
+ return mLeft.equals(right);
+ }
+ }
+
@Before
public void setUp() {
mFrameGuid = frameGuid();
mModel = new PropertyModel.Builder(PlayerFrameProperties.ALL_KEYS).build();
mCompositorDelegate = new TestPlayerCompositorDelegate();
- mScroller = new Scroller(ContextUtils.getApplicationContext());
- mMediator = new PlayerFrameMediator(
- mModel, mCompositorDelegate, mScroller, mFrameGuid, CONTENT_WIDTH, CONTENT_HEIGHT);
+ mScroller = new OverScroller(ContextUtils.getApplicationContext());
+ mMediator = new PlayerFrameMediator(mModel, mCompositorDelegate, mScroller, mFrameGuid,
+ CONTENT_WIDTH, CONTENT_HEIGHT, 0, 0);
}
private static Rect getRectForTile(int tileWidth, int tileHeight, int row, int col) {
@@ -706,8 +725,561 @@ public class PlayerFrameMediatorTest {
}
/**
- * TODO(crbug.com/1020702): Implement after zooming support is added.
+ * Tests that {@link PlayerFrameMediator} correctly consumes scale events and scales between
+ * the allowed range.
+ */
+ @Test
+ public void testViewPortOnScaleBy() {
+ // Initial view port setup.
+ mMediator.setLayoutDimensions(100, 200);
+ mMediator.updateViewportSize(100, 200, 1f);
+
+ // The current view port fully matches the top left bitmap tile.
+ // Below is a schematic of the entire bitmap matrix. Tiles marked with x are required for
+ // the current view port.
+ // -------------------------
+ // | x | x | | | | |
+ // -------------------------
+ // | x | | | | | |
+ // -------------------------
+ // | | | | | | |
+ // -------------------------
+ // | | | | | | |
+ // -------------------------
+ // | | | | | | |
+ // -------------------------
+ // | | | | | | |
+ boolean[][] expectedRequiredBitmaps = new boolean[6][6];
+ expectedRequiredBitmaps[0][0] = true;
+ expectedRequiredBitmaps[0][1] = true;
+ expectedRequiredBitmaps[1][0] = true;
+ Assert.assertTrue(
+ Arrays.deepEquals(expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(1f)));
+
+ // Now a scale factor of 2 will be applied. This will happen at a focal point of 0, 0.
+ // The same bitmaps will be required but the grid will be double the size.
+ Assert.assertTrue(mMediator.scaleBy(2f, 0, 0));
+ Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+
+ expectedRequiredBitmaps = new boolean[12][12];
+ expectedRequiredBitmaps[0][0] = true;
+ expectedRequiredBitmaps[0][1] = true;
+ expectedRequiredBitmaps[1][0] = true;
+ Assert.assertTrue(
+ Arrays.deepEquals(expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(2f)));
+ Assert.assertNull(mMediator.mRequiredBitmaps.get(1f));
+
+ // Reduce the scale factor by 0.5 returning to a scale of 1.
+ Assert.assertTrue(mMediator.scaleBy(0.5f, 0, 0));
+ Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+
+ expectedRequiredBitmaps = new boolean[6][6];
+ expectedRequiredBitmaps[0][0] = true;
+ expectedRequiredBitmaps[0][1] = true;
+ expectedRequiredBitmaps[1][0] = true;
+ Assert.assertTrue(
+ Arrays.deepEquals(expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(1f)));
+ Assert.assertNull(mMediator.mRequiredBitmaps.get(2f));
+
+ // Increase the scale factor to 6 which is above the maximum limit returning to a scale
+ // of 5. Note that the grid is smaller than 30x30 as the viewport is not a multiple of the
+ // content width and height so there is difference.
+ Assert.assertTrue(mMediator.scaleBy(6f, 0, 0));
+ Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+
+ expectedRequiredBitmaps = new boolean[29][28];
+ expectedRequiredBitmaps[0][0] = true;
+ expectedRequiredBitmaps[0][1] = true;
+ expectedRequiredBitmaps[1][0] = true;
+ Assert.assertTrue(
+ Arrays.deepEquals(expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(5f)));
+ Assert.assertNull(mMediator.mRequiredBitmaps.get(1f));
+
+ // Reduce the scale factor back to 1.
+ Assert.assertTrue(mMediator.scaleBy(0.2f, 0, 0));
+ Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+
+ expectedRequiredBitmaps = new boolean[6][6];
+ expectedRequiredBitmaps[0][0] = true;
+ expectedRequiredBitmaps[0][1] = true;
+ expectedRequiredBitmaps[1][0] = true;
+ Assert.assertTrue(
+ Arrays.deepEquals(expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(1f)));
+ Assert.assertNull(mMediator.mRequiredBitmaps.get(5f));
+
+ // We now reduce the scale factor to less than mInitialScaleFactor; however, the maximum
+ // scale out is limited to mInitialScaleFactor.
+ float initialScaleFactor = 100f / 560f;
+ Assert.assertTrue(mMediator.scaleBy(initialScaleFactor, 0, 0));
+ Assert.assertTrue(mMediator.scaleBy(0.5f, 0, 0));
+ Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+
+ expectedRequiredBitmaps = new boolean[2][1];
+ expectedRequiredBitmaps[0][0] = true;
+ expectedRequiredBitmaps[1][0] = true;
+ Assert.assertTrue(Arrays.deepEquals(
+ expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(initialScaleFactor)));
+ Assert.assertNull(mMediator.mRequiredBitmaps.get(1f));
+ }
+
+ /**
+ * Tests that {@link PlayerFrameMediator} works correctly when scrolling.
+ */
+ @Test
+ public void testViewPortOnScaleByWithScroll() {
+ // Initial view port setup.
+ mMediator.updateViewportSize(100, 200, 1f);
+
+ boolean[][] expectedRequiredBitmaps = new boolean[6][6];
+
+ // STEP 1: Original request.
+ // The current view port fully matches the top left bitmap tile.
+ // Below is a schematic of the entire bitmap matrix. Tiles marked with x are required for
+ // the current view port.
+ // -------------------------
+ // | x | x | | | | |
+ // -------------------------
+ // | x | | | | | |
+ // -------------------------
+ // | | | | | | |
+ // -------------------------
+ // | | | | | | |
+ // -------------------------
+ // | | | | | | |
+ // -------------------------
+ // | | | | | | |
+ expectedRequiredBitmaps[0][0] = true;
+ expectedRequiredBitmaps[0][1] = true;
+ expectedRequiredBitmaps[1][0] = true;
+ List<RequestedBitmap> expectedRequestedBitmaps = new ArrayList<>();
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 0), 1f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 0), 1f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 1), 1f));
+
+ // Both matricies should be identity to start.
+ Assert.assertTrue(mMediator.mViewportScaleMatrix.isIdentity());
+ Assert.assertTrue(mModel.get(PlayerFrameProperties.SCALE_MATRIX).isIdentity());
+ // Ensure the correct bitmaps are required and requested.
+ Assert.assertTrue(
+ Arrays.deepEquals(expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(1f)));
+ Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap);
+
+ // STEP 2: Scroll slightly.
+ mMediator.scrollBy(10, 15);
+ // The current viewport covers portions of the 4 top left bitmap tiles.
+ // -------------------------
+ // | x | x | x | | | |
+ // -------------------------
+ // | x | x | x | | | |
+ // -------------------------
+ // | x | x | | | | |
+ // -------------------------
+ // | | | | | | |
+ // -------------------------
+ // | | | | | | |
+ // -------------------------
+ // | | | | | | |
+ expectedRequiredBitmaps[0][2] = true;
+ expectedRequiredBitmaps[1][1] = true;
+ expectedRequiredBitmaps[1][2] = true;
+ expectedRequiredBitmaps[2][0] = true;
+ expectedRequiredBitmaps[2][1] = true;
+ Assert.assertTrue(
+ Arrays.deepEquals(expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(1f)));
+
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 1), 1f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 0), 1f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 2), 1f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 1), 1f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 2), 1f));
+ Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap);
+
+ // The viewport matrix should track scroll and zoom.
+ Matrix expectedViewportMatrix = new Matrix();
+ float[] expectedViewportMatrixValues = new float[9];
+ expectedViewportMatrix.getValues(expectedViewportMatrixValues);
+ expectedViewportMatrixValues[Matrix.MTRANS_X] = -10;
+ expectedViewportMatrixValues[Matrix.MTRANS_Y] = -15;
+ expectedViewportMatrix.setValues(expectedViewportMatrixValues);
+
+ Assert.assertEquals(expectedViewportMatrix, mMediator.mViewportScaleMatrix);
+ Assert.assertTrue(mModel.get(PlayerFrameProperties.SCALE_MATRIX).isIdentity());
+
+ // STEP 3: Now a scale factor of 2 will be applied. This will happen at a focal point of 50,
+ // 100.
+ Assert.assertTrue(mMediator.scaleBy(2f, 50f, 100f));
+
+ // Before the scaling commits both matricies should update.
+ expectedViewportMatrix.postScale(2f, 2f, 50f, 100f);
+ Matrix expectedBitmapMatrix = new Matrix();
+ expectedBitmapMatrix.postScale(2f, 2f, 50f, 100f);
+ Assert.assertEquals(expectedViewportMatrix, mMediator.mViewportScaleMatrix);
+ Assert.assertEquals(expectedBitmapMatrix, mModel.get(PlayerFrameProperties.SCALE_MATRIX));
+
+ // Bitmaps should be the same as before scaling until scaling is finished.
+ Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap);
+ mCompositorDelegate.mRequestedBitmap.clear();
+ expectedRequestedBitmaps.clear();
+
+ Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+
+ expectedRequiredBitmaps = new boolean[12][12];
+ expectedRequiredBitmaps[0][0] = true;
+ expectedRequiredBitmaps[0][1] = true;
+ expectedRequiredBitmaps[1][0] = true;
+ expectedRequiredBitmaps[0][2] = true;
+ expectedRequiredBitmaps[1][1] = true;
+ expectedRequiredBitmaps[1][2] = true;
+ expectedRequiredBitmaps[2][0] = true;
+ expectedRequiredBitmaps[2][1] = true;
+ Assert.assertTrue(
+ Arrays.deepEquals(expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(2f)));
+ Assert.assertNull(mMediator.mRequiredBitmaps.get(1f));
+
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 0), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 0), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 1), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 1), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 0), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 2), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 1), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 2), 2f));
+ Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap);
+
+ // The bitmap matrix should be cleared.
+ expectedBitmapMatrix.reset();
+ Assert.assertTrue(mModel.get(PlayerFrameProperties.SCALE_MATRIX).isIdentity());
+
+ // STEP4: Now a scale factor of 0.5 will be applied. This will happen at a focal point of
+ // 50, 100.
+ Assert.assertTrue(mMediator.scaleBy(0.5f, 50f, 100f));
+
+ // Ensure the matricies are correct mid-scale.
+ expectedViewportMatrix.postScale(0.5f, 0.5f, 50f, 100f);
+ expectedBitmapMatrix.postScale(0.5f, 0.5f, 50f, 100f);
+ Assert.assertEquals(expectedViewportMatrix, mMediator.mViewportScaleMatrix);
+ Assert.assertEquals(expectedBitmapMatrix, mModel.get(PlayerFrameProperties.SCALE_MATRIX));
+
+ // Bitmaps should be the same as before scaling until scaling is finished.
+ Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap);
+ mCompositorDelegate.mRequestedBitmap.clear();
+ expectedRequestedBitmaps.clear();
+
+ Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+
+ expectedRequiredBitmaps = new boolean[6][6];
+ expectedRequiredBitmaps[0][0] = true;
+ expectedRequiredBitmaps[0][1] = true;
+ expectedRequiredBitmaps[1][0] = true;
+ expectedRequiredBitmaps[0][2] = true;
+ expectedRequiredBitmaps[1][1] = true;
+ expectedRequiredBitmaps[1][2] = true;
+ expectedRequiredBitmaps[2][0] = true;
+ expectedRequiredBitmaps[2][1] = true;
+ Assert.assertTrue(
+ Arrays.deepEquals(expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(1f)));
+ Assert.assertNull(mMediator.mRequiredBitmaps.get(2f));
+
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 0), 1f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 0), 1f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 1), 1f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 1), 1f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 0), 1f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 2), 1f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 1), 1f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 2), 1f));
+ Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap);
+
+ expectedBitmapMatrix.reset();
+ Assert.assertTrue(mModel.get(PlayerFrameProperties.SCALE_MATRIX).isIdentity());
+
+ // Now a scale factor of 2 will be applied. This will happen at a focal point of 100, 200.
+ // Due to the position of the focal point the required bitmaps will move.
+ // -------------------------
+ // | | x | x | | | |
+ // -------------------------
+ // | x | x | x | x | | |
+ // -------------------------
+ // | x | x | x | x | | |
+ // -------------------------
+ // | | x | x | | | |
+ // -------------------------
+ // | | | | | | |
+ // -------------------------
+ // | | | | | | |
+ Assert.assertTrue(mMediator.scaleBy(2f, 100f, 200f));
+
+ expectedViewportMatrix.postScale(2f, 2f, 100f, 200f);
+ expectedBitmapMatrix.postScale(2f, 2f, 100f, 200f);
+ Assert.assertEquals(expectedViewportMatrix, mMediator.mViewportScaleMatrix);
+ Assert.assertEquals(expectedBitmapMatrix, mModel.get(PlayerFrameProperties.SCALE_MATRIX));
+
+ Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap);
+ mCompositorDelegate.mRequestedBitmap.clear();
+ expectedRequestedBitmaps.clear();
+
+ Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+
+ expectedRequiredBitmaps = new boolean[12][12];
+ expectedRequiredBitmaps[0][1] = true;
+ expectedRequiredBitmaps[0][2] = true;
+ expectedRequiredBitmaps[1][0] = true;
+ expectedRequiredBitmaps[1][1] = true;
+ expectedRequiredBitmaps[1][2] = true;
+ expectedRequiredBitmaps[1][3] = true;
+ expectedRequiredBitmaps[2][0] = true;
+ expectedRequiredBitmaps[2][1] = true;
+ expectedRequiredBitmaps[2][2] = true;
+ expectedRequiredBitmaps[2][3] = true;
+ expectedRequiredBitmaps[3][1] = true;
+ expectedRequiredBitmaps[3][2] = true;
+ Assert.assertTrue(
+ Arrays.deepEquals(expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(2f)));
+ Assert.assertNull(mMediator.mRequiredBitmaps.get(1f));
+
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 1), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 1), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 2), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 2), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 1), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 0), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 3, 1), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 0), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 2), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 1, 3), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 3, 2), 2f));
+ expectedRequestedBitmaps.add(
+ new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 2, 3), 2f));
+ Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap);
+
+ expectedBitmapMatrix.reset();
+ Assert.assertTrue(mModel.get(PlayerFrameProperties.SCALE_MATRIX).isIdentity());
+ }
+
+ /**
+ * Tests that {@link PlayerFrameMediator} works correctly when subframes are present.
*/
@Test
- public void testViewPortOnScaleBy() {}
+ public void testViewPortOnScaleByWithSubFrames() {
+ PlayerFrameView subFrame1View = Mockito.mock(PlayerFrameView.class);
+ Pair<View, Rect> subFrame1 = new Pair<>(subFrame1View, new Rect(10, 20, 60, 40));
+ PlayerFrameView subFrame2View = Mockito.mock(PlayerFrameView.class);
+ Pair<View, Rect> subFrame2 = new Pair<>(subFrame2View, new Rect(30, 50, 70, 160));
+
+ mMediator.addSubFrame(subFrame1.first, subFrame1.second);
+ mMediator.addSubFrame(subFrame2.first, subFrame2.second);
+
+ // Both subframes should be visible.
+ mMediator.updateViewportSize(50, 100, 1f);
+ List<View> expectedVisibleViews = new ArrayList<>();
+ List<Rect> expectedVisibleRects = new ArrayList<>();
+ expectedVisibleViews.add(subFrame1.first);
+ expectedVisibleViews.add(subFrame2.first);
+ expectedVisibleRects.add(subFrame1.second);
+ expectedVisibleRects.add(subFrame2.second);
+ Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+ Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS));
+
+ expectedVisibleViews.clear();
+ expectedVisibleRects.clear();
+ expectedVisibleViews.add(subFrame1.first);
+ expectedVisibleRects.add(new Rect(20, 40, 120, 80));
+
+ // During scaling the second subframe should disappear from the viewport.
+ Assert.assertTrue(mMediator.scaleBy(2f, 0f, 0f));
+ Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+ Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS));
+ Matrix expectedMatrix = new Matrix();
+ expectedMatrix.setScale(2f, 2f);
+ verify(subFrame1View)
+ .updateDelegateScaleMatrix(argThat(new MatrixMatcher(expectedMatrix)), eq(2f));
+
+ Assert.assertTrue(mMediator.scaleFinished(1f, 0f, 0f));
+ Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+ Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS));
+ expectedMatrix.reset();
+ verify(subFrame1View)
+ .updateDelegateScaleMatrix(argThat(new MatrixMatcher(expectedMatrix)), eq(1f));
+ verify(subFrame1View).forceRedraw();
+
+ // Scroll so the second subframe is back in the viewport..
+ mMediator.scrollBy(20, 40);
+ expectedVisibleViews.add(subFrame2.first);
+ expectedVisibleRects.clear();
+ expectedVisibleRects.add(new Rect(0, 0, 100, 40));
+ expectedVisibleRects.add(new Rect(40, 60, 120, 280));
+ Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+ Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS));
+
+ // Scale out keeping the subframes in the viewport..
+ Assert.assertTrue(mMediator.scaleBy(0.75f, 25f, 50f));
+ expectedVisibleRects.clear();
+ expectedVisibleRects.add(new Rect(6, 13, 81, 43));
+ expectedVisibleRects.add(new Rect(36, 58, 96, 223));
+ Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+ Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS));
+ expectedMatrix.setScale(0.75f, 0.75f);
+ verify(subFrame1View)
+ .updateDelegateScaleMatrix(argThat(new MatrixMatcher(expectedMatrix)), eq(1.5f));
+ verify(subFrame2View)
+ .updateDelegateScaleMatrix(argThat(new MatrixMatcher(expectedMatrix)), eq(1.5f));
+ }
+
+ /**
+ * Tests that {@link PlayerFrameMediator} correctly scales and keeps the content in bounds.
+ */
+ @Test
+ public void testViewPortOnScaleByWithinBounds() {
+ // Initial view port setup.
+ mMediator.updateViewportSize(100, 200, 1f);
+
+ boolean[][] expectedRequiredBitmaps = new boolean[6][6];
+
+ // The current view port fully matches the top left bitmap tile.
+ // Below is a schematic of the entire bitmap matrix. Tiles marked with x are required for
+ // the current view port.
+ // -------------------------
+ // | x | x | | | | |
+ // -------------------------
+ // | x | | | | | |
+ // -------------------------
+ // | | | | | | |
+ // -------------------------
+ // | | | | | | |
+ // -------------------------
+ // | | | | | | |
+ // -------------------------
+ // | | | | | | |
+ expectedRequiredBitmaps[0][0] = true;
+ expectedRequiredBitmaps[0][1] = true;
+ expectedRequiredBitmaps[1][0] = true;
+ Assert.assertTrue(
+ Arrays.deepEquals(expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(1f)));
+
+ // Now a scale factor of 2 will be applied. This will happen at a focal point of 0, 0.
+ // The same bitmaps will be required but the grid will be double the size.
+ Assert.assertTrue(mMediator.scaleBy(2f, 0, 0));
+ Matrix expectedViewportMatrix = new Matrix();
+ Matrix expectedBitmapMatrix = new Matrix();
+ expectedViewportMatrix.postScale(2f, 2f, 0f, 0f);
+ expectedBitmapMatrix.postScale(2f, 2f, 0f, 0f);
+ Assert.assertEquals(expectedViewportMatrix, mMediator.mViewportScaleMatrix);
+ Assert.assertEquals(expectedBitmapMatrix, mModel.get(PlayerFrameProperties.SCALE_MATRIX));
+
+ Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+
+ expectedRequiredBitmaps = new boolean[12][12];
+ expectedRequiredBitmaps[0][0] = true;
+ expectedRequiredBitmaps[0][1] = true;
+ expectedRequiredBitmaps[1][0] = true;
+ Assert.assertTrue(
+ Arrays.deepEquals(expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(2f)));
+ Assert.assertNull(mMediator.mRequiredBitmaps.get(1f));
+
+ // Reduce the scale factor by 0.5 returning to a scale of 1 but try to do so with a focal
+ // point that causes translation outside the bounds. The focal point should be ignored.
+ Assert.assertTrue(mMediator.scaleBy(0.5f, 50f, 50f));
+ expectedViewportMatrix.postScale(0.5f, 0.5f, 0f, 0f);
+ expectedBitmapMatrix.reset();
+ expectedBitmapMatrix.postScale(0.5f, 0.5f, 0f, 0f);
+ Assert.assertEquals(expectedViewportMatrix, mMediator.mViewportScaleMatrix);
+ Assert.assertEquals(expectedBitmapMatrix, mModel.get(PlayerFrameProperties.SCALE_MATRIX));
+
+ Assert.assertTrue(mMediator.scaleFinished(1f, -50f, -50f));
+
+ expectedRequiredBitmaps = new boolean[6][6];
+ expectedRequiredBitmaps[0][0] = true;
+ expectedRequiredBitmaps[0][1] = true;
+ expectedRequiredBitmaps[1][0] = true;
+ Assert.assertTrue(
+ Arrays.deepEquals(expectedRequiredBitmaps, mMediator.mRequiredBitmaps.get(1f)));
+ Assert.assertNull(mMediator.mRequiredBitmaps.get(2f));
+ }
+
+ /**
+ * Tests that {@link PlayerFrameMediator} works correctly with nested subframes. This test
+ * pretends that mMediator is for a subframe. The calls made to this mediator are verified
+ * to occur in {@link testViewPortOnScaleByWithSubFrames}.
+ */
+ @Test
+ public void testViewPortOnScaleByWithNestedSubFrames() {
+ PlayerFrameView subFrameView = Mockito.mock(PlayerFrameView.class);
+ Pair<View, Rect> subFrame = new Pair<>(subFrameView, new Rect(10, 20, 60, 40));
+ mMediator.addSubFrame(subFrame.first, subFrame.second);
+
+ // The subframe should be visible.
+ mMediator.updateViewportSize(50, 100, 1f);
+ List<View> expectedVisibleViews = new ArrayList<>();
+ List<Rect> expectedVisibleRects = new ArrayList<>();
+ expectedVisibleViews.add(subFrame.first);
+ expectedVisibleRects.add(subFrame.second);
+ Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+ Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS));
+
+ expectedVisibleViews.clear();
+ expectedVisibleRects.clear();
+ expectedVisibleViews.add(subFrame.first);
+ expectedVisibleRects.add(new Rect(20, 40, 120, 80));
+
+ // Scale by a factor of two via the parent.
+ Matrix scaleMatrix = new Matrix();
+ scaleMatrix.setScale(2f, 2f);
+ mMediator.setBitmapScaleMatrix(scaleMatrix, 2f);
+ Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+ Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS));
+ verify(subFrameView)
+ .updateDelegateScaleMatrix(argThat(new MatrixMatcher(scaleMatrix)), eq(2f));
+
+ expectedVisibleViews.clear();
+ expectedVisibleRects.clear();
+ expectedVisibleViews.add(subFrame.first);
+ expectedVisibleRects.add(new Rect(15, 30, 90, 60));
+
+ // Zoom out by a factor of 0.75, note the scale factor argument is compounded.
+ scaleMatrix.setScale(0.75f, 0.75f);
+ mMediator.setBitmapScaleMatrix(scaleMatrix, 1.5f);
+ Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+ Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS));
+ verify(subFrameView)
+ .updateDelegateScaleMatrix(argThat(new MatrixMatcher(scaleMatrix)), eq(1.5f));
+
+ // Force a redraw and ensure it is recursive.
+ mMediator.forceRedraw();
+ verify(subFrameView).forceRedraw();
+ }
}
diff --git a/chromium/components/paint_preview/player/android/player_compositor_delegate_android.cc b/chromium/components/paint_preview/player/android/player_compositor_delegate_android.cc
index ee2c7928dfc..ead1788baef 100644
--- a/chromium/components/paint_preview/player/android/player_compositor_delegate_android.cc
+++ b/chromium/components/paint_preview/player/android/player_compositor_delegate_android.cc
@@ -11,6 +11,7 @@
#include "base/android/jni_string.h"
#include "base/android/unguessable_token_android.h"
#include "base/bind.h"
+#include "base/metrics/histogram_functions.h"
#include "base/task/post_task.h"
#include "base/trace_event/common/trace_event_common.h"
#include "base/unguessable_token.h"
@@ -49,7 +50,8 @@ ScopedJavaLocalRef<jobjectArray> ToJavaUnguessableTokenArray(
}
ScopedJavaGlobalRef<jobject> ConvertToJavaBitmap(const SkBitmap& sk_bitmap) {
- return ScopedJavaGlobalRef<jobject>(gfx::ConvertToJavaBitmap(&sk_bitmap));
+ return ScopedJavaGlobalRef<jobject>(
+ gfx::ConvertToJavaBitmap(&sk_bitmap, gfx::OomBehavior::kReturnNullOnOom));
}
} // namespace
@@ -59,12 +61,13 @@ jlong JNI_PlayerCompositorDelegateImpl_Initialize(
const JavaParamRef<jobject>& j_object,
jlong paint_preview_service,
const JavaParamRef<jstring>& j_url_spec,
- const JavaParamRef<jstring>& j_directory_key) {
+ const JavaParamRef<jstring>& j_directory_key,
+ const JavaParamRef<jobject>& j_compositor_error_callback) {
PlayerCompositorDelegateAndroid* delegate =
new PlayerCompositorDelegateAndroid(
env, j_object,
reinterpret_cast<PaintPreviewBaseService*>(paint_preview_service),
- j_url_spec, j_directory_key);
+ j_url_spec, j_directory_key, j_compositor_error_callback);
return reinterpret_cast<intptr_t>(delegate);
}
@@ -73,32 +76,50 @@ PlayerCompositorDelegateAndroid::PlayerCompositorDelegateAndroid(
const JavaParamRef<jobject>& j_object,
PaintPreviewBaseService* paint_preview_service,
const JavaParamRef<jstring>& j_url_spec,
- const JavaParamRef<jstring>& j_directory_key)
+ const JavaParamRef<jstring>& j_directory_key,
+ const JavaParamRef<jobject>& j_compositor_error_callback)
: PlayerCompositorDelegate(
paint_preview_service,
GURL(base::android::ConvertJavaStringToUTF8(env, j_url_spec)),
DirectoryKey{
- base::android::ConvertJavaStringToUTF8(env, j_directory_key)}),
- request_id_(0) {
+ base::android::ConvertJavaStringToUTF8(env, j_directory_key)},
+ base::BindOnce(
+ &base::android::RunRunnableAndroid,
+ ScopedJavaGlobalRef<jobject>(j_compositor_error_callback))),
+ request_id_(0),
+ startup_timestamp_(base::TimeTicks::Now()) {
java_ref_.Reset(env, j_object);
}
void PlayerCompositorDelegateAndroid::OnCompositorReady(
mojom::PaintPreviewCompositor::Status status,
mojom::PaintPreviewBeginCompositeResponsePtr composite_response) {
+ bool compositor_started =
+ status == mojom::PaintPreviewCompositor::Status::kSuccess;
+ base::UmaHistogramBoolean(
+ "Browser.PaintPreview.Player.CompositorProcessStartedCorrectly",
+ compositor_started);
+ if (!compositor_started && compositor_error_) {
+ std::move(compositor_error_).Run();
+ return;
+ }
+ base::UmaHistogramTimes(
+ "Browser.PaintPreview.Player.CompositorProcessStartupTime",
+ base::TimeTicks::Now() - startup_timestamp_);
JNIEnv* env = base::android::AttachCurrentThread();
std::vector<base::UnguessableToken> all_guids;
std::vector<int> scroll_extents;
+ std::vector<int> scroll_offsets;
std::vector<int> subframe_count;
std::vector<base::UnguessableToken> subframe_ids;
std::vector<int> subframe_rects;
base::UnguessableToken root_frame_guid;
if (composite_response) {
- CompositeResponseFramesToVectors(composite_response->frames, &all_guids,
- &scroll_extents, &subframe_count,
- &subframe_ids, &subframe_rects);
+ CompositeResponseFramesToVectors(
+ composite_response->frames, &all_guids, &scroll_extents,
+ &scroll_offsets, &subframe_count, &subframe_ids, &subframe_rects);
root_frame_guid = composite_response->root_frame_guid;
} else {
// If there is no composite response due to a failure we don't have a root
@@ -111,6 +132,8 @@ void PlayerCompositorDelegateAndroid::OnCompositorReady(
ToJavaUnguessableTokenArray(env, all_guids);
ScopedJavaLocalRef<jintArray> j_scroll_extents =
base::android::ToJavaIntArray(env, scroll_extents);
+ ScopedJavaLocalRef<jintArray> j_scroll_offsets =
+ base::android::ToJavaIntArray(env, scroll_offsets);
ScopedJavaLocalRef<jintArray> j_subframe_count =
base::android::ToJavaIntArray(env, subframe_count);
ScopedJavaLocalRef<jobjectArray> j_subframe_ids =
@@ -121,11 +144,8 @@ void PlayerCompositorDelegateAndroid::OnCompositorReady(
base::android::UnguessableTokenAndroid::Create(env, root_frame_guid);
Java_PlayerCompositorDelegateImpl_onCompositorReady(
- env, java_ref_,
- static_cast<jboolean>(status ==
- mojom::PaintPreviewCompositor::Status::kSuccess),
- j_root_frame_guid, j_all_guids, j_scroll_extents, j_subframe_count,
- j_subframe_ids, j_subframe_rects);
+ env, java_ref_, j_root_frame_guid, j_all_guids, j_scroll_extents,
+ j_scroll_offsets, j_subframe_count, j_subframe_ids, j_subframe_rects);
}
// static
@@ -133,6 +153,7 @@ void PlayerCompositorDelegateAndroid::CompositeResponseFramesToVectors(
const base::flat_map<base::UnguessableToken, mojom::FrameDataPtr>& frames,
std::vector<base::UnguessableToken>* all_guids,
std::vector<int>* scroll_extents,
+ std::vector<int>* scroll_offsets,
std::vector<int>* subframe_count,
std::vector<base::UnguessableToken>* subframe_ids,
std::vector<int>* subframe_rects) {
@@ -144,6 +165,8 @@ void PlayerCompositorDelegateAndroid::CompositeResponseFramesToVectors(
all_guids->push_back(pair.first);
scroll_extents->push_back(pair.second->scroll_extents.width());
scroll_extents->push_back(pair.second->scroll_extents.height());
+ scroll_offsets->push_back(pair.second->scroll_offsets.width());
+ scroll_offsets->push_back(pair.second->scroll_offsets.height());
subframe_count->push_back(pair.second->subframes.size());
all_subframes_count += pair.second->subframes.size();
}
@@ -200,15 +223,30 @@ void PlayerCompositorDelegateAndroid::OnBitmapCallback(
TRACE_ID_LOCAL(request_id), "status", static_cast<int>(status), "bytes",
sk_bitmap.computeByteSize());
- if (status == mojom::PaintPreviewCompositor::Status::kSuccess) {
+ if (status == mojom::PaintPreviewCompositor::Status::kSuccess &&
+ !sk_bitmap.isNull()) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::TaskPriority::USER_VISIBLE},
base::BindOnce(&ConvertToJavaBitmap, sk_bitmap),
- base::BindOnce(&base::android::RunObjectCallbackAndroid,
- j_bitmap_callback));
+ base::BindOnce(base::BindOnce(
+ [](const ScopedJavaGlobalRef<jobject>& j_bitmap_callback,
+ const ScopedJavaGlobalRef<jobject>& j_error_callback,
+ const ScopedJavaGlobalRef<jobject>& j_bitmap) {
+ if (!j_bitmap) {
+ base::android::RunRunnableAndroid(j_error_callback);
+ return;
+ }
+ base::android::RunObjectCallbackAndroid(j_bitmap_callback,
+ j_bitmap);
+ },
+ j_bitmap_callback, j_error_callback)));
} else {
base::android::RunRunnableAndroid(j_error_callback);
}
+ if (request_id == 0) {
+ base::UmaHistogramTimes("Browser.PaintPreview.Player.TimeToFirstBitmap",
+ base::TimeTicks::Now() - startup_timestamp_);
+ }
}
void PlayerCompositorDelegateAndroid::OnClick(
@@ -222,6 +260,7 @@ void PlayerCompositorDelegateAndroid::OnClick(
gfx::Rect(static_cast<int>(j_x), static_cast<int>(j_y), 1U, 1U));
if (res.empty())
return;
+ base::UmaHistogramBoolean("Browser.PaintPreview.Player.LinkClicked", true);
// TODO(crbug/1061435): Resolve cases where there are multiple links.
// For now just return the first in the list.
Java_PlayerCompositorDelegateImpl_onLinkClicked(
diff --git a/chromium/components/paint_preview/player/android/player_compositor_delegate_android.h b/chromium/components/paint_preview/player/android/player_compositor_delegate_android.h
index 8c9556ef5ca..aec2b24f199 100644
--- a/chromium/components/paint_preview/player/android/player_compositor_delegate_android.h
+++ b/chromium/components/paint_preview/player/android/player_compositor_delegate_android.h
@@ -19,10 +19,11 @@ class PlayerCompositorDelegateAndroid : public PlayerCompositorDelegate {
public:
PlayerCompositorDelegateAndroid(
JNIEnv* env,
- const base::android::JavaParamRef<jobject>& jobject,
+ const base::android::JavaParamRef<jobject>& j_object,
PaintPreviewBaseService* paint_preview_service,
const base::android::JavaParamRef<jstring>& j_url_spec,
- const base::android::JavaParamRef<jstring>& j_directory_key);
+ const base::android::JavaParamRef<jstring>& j_directory_key,
+ const base::android::JavaParamRef<jobject>& j_compositor_error_callback);
void OnCompositorReady(
mojom::PaintPreviewCompositor::Status status,
@@ -54,6 +55,7 @@ class PlayerCompositorDelegateAndroid : public PlayerCompositorDelegate {
const base::flat_map<base::UnguessableToken, mojom::FrameDataPtr>& frames,
std::vector<base::UnguessableToken>* all_guids,
std::vector<int>* scroll_extents,
+ std::vector<int>* scroll_offsets,
std::vector<int>* subframe_count,
std::vector<base::UnguessableToken>* subframe_ids,
std::vector<int>* subframe_rects);
@@ -72,6 +74,7 @@ class PlayerCompositorDelegateAndroid : public PlayerCompositorDelegate {
base::android::ScopedJavaGlobalRef<jobject> java_ref_;
int request_id_;
+ base::TimeTicks startup_timestamp_;
base::WeakPtrFactory<PlayerCompositorDelegateAndroid> weak_factory_{this};
diff --git a/chromium/components/paint_preview/player/android/player_compositor_delegate_android_unittest.cc b/chromium/components/paint_preview/player/android/player_compositor_delegate_android_unittest.cc
index 07328828993..193baae2b8b 100644
--- a/chromium/components/paint_preview/player/android/player_compositor_delegate_android_unittest.cc
+++ b/chromium/components/paint_preview/player/android/player_compositor_delegate_android_unittest.cc
@@ -32,6 +32,10 @@ TEST(PlayerCompositorDelegateAndroidTest,
frame_data_subframe_1->scroll_extents = gfx::Size(50, 60);
frame_data_subframe_2->scroll_extents = gfx::Size(10, 20);
+ frame_data_main->scroll_offsets = gfx::Size(150, 250);
+ frame_data_subframe_1->scroll_offsets = gfx::Size(55, 65);
+ frame_data_subframe_2->scroll_offsets = gfx::Size(15, 25);
+
mojom::SubframeClipRect clip_rect1(subframe_1_guid, gfx::Rect(5, 10, 50, 60));
mojom::SubframeClipRect clip_rect2(subframe_2_guid,
gfx::Rect(15, 25, 30, 40));
@@ -44,16 +48,18 @@ TEST(PlayerCompositorDelegateAndroidTest,
std::vector<base::UnguessableToken> all_guids;
std::vector<int> scroll_extents;
+ std::vector<int> scroll_offsets;
std::vector<int> subframe_count;
std::vector<base::UnguessableToken> subframe_ids;
std::vector<int> subframe_rects;
PlayerCompositorDelegateAndroid::CompositeResponseFramesToVectors(
- frames, &all_guids, &scroll_extents, &subframe_count, &subframe_ids,
- &subframe_rects);
+ frames, &all_guids, &scroll_extents, &scroll_offsets, &subframe_count,
+ &subframe_ids, &subframe_rects);
EXPECT_EQ(all_guids.size(), frames.size());
EXPECT_EQ(scroll_extents.size(), 2 * frames.size());
+ EXPECT_EQ(scroll_offsets.size(), 2 * frames.size());
EXPECT_EQ(subframe_count.size(), frames.size());
EXPECT_EQ(subframe_ids.size(), 2U); // 2 subframes.
EXPECT_EQ(subframe_rects.size(), 2U * 4U); // 2 * subframes * 4 per rect.
@@ -62,11 +68,13 @@ TEST(PlayerCompositorDelegateAndroidTest,
auto it = frames.find(all_guids[i]);
ASSERT_NE(it, frames.end());
- const size_t scroll_offset = i * 2;
- EXPECT_EQ(scroll_extents[scroll_offset],
- it->second->scroll_extents.width());
- EXPECT_EQ(scroll_extents[scroll_offset + 1],
+ const size_t scroll_index = i * 2;
+ EXPECT_EQ(scroll_extents[scroll_index], it->second->scroll_extents.width());
+ EXPECT_EQ(scroll_extents[scroll_index + 1],
it->second->scroll_extents.height());
+ EXPECT_EQ(scroll_offsets[scroll_index], it->second->scroll_offsets.width());
+ EXPECT_EQ(scroll_offsets[scroll_index + 1],
+ it->second->scroll_offsets.height());
EXPECT_EQ(subframe_count[i],
static_cast<int>(it->second->subframes.size()));
for (size_t j = 0; j < it->second->subframes.size(); ++j) {
diff --git a/chromium/components/paint_preview/player/player_compositor_delegate.cc b/chromium/components/paint_preview/player/player_compositor_delegate.cc
index a49839c1a05..1e4e2f79391 100644
--- a/chromium/components/paint_preview/player/player_compositor_delegate.cc
+++ b/chromium/components/paint_preview/player/player_compositor_delegate.cc
@@ -8,7 +8,6 @@
#include <utility>
#include <vector>
-#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/memory/read_only_shared_memory_region.h"
@@ -126,7 +125,7 @@ PrepareCompositeRequest(const paint_preview::PaintPreviewProto& proto) {
auto read_only_proto = ToReadOnlySharedMemory(proto);
if (!read_only_proto) {
- // TODO(crbug.com/1021590): Handle initialization errors.
+ DVLOG(1) << "Failed to read proto to read-only shared memory.";
return nullptr;
}
begin_composite_request->proto = std::move(read_only_proto.value());
@@ -139,8 +138,11 @@ PlayerCompositorDelegate::PlayerCompositorDelegate(
PaintPreviewBaseService* paint_preview_service,
const GURL& expected_url,
const DirectoryKey& key,
+ base::OnceClosure compositor_error,
bool skip_service_launch)
- : paint_preview_service_(paint_preview_service) {
+ : compositor_error_(std::move(compositor_error)),
+ paint_preview_service_(paint_preview_service),
+ key_(key) {
if (skip_service_launch) {
paint_preview_service_->GetCapturedPaintPreviewProto(
key, base::BindOnce(&PlayerCompositorDelegate::OnProtoAvailable,
@@ -164,10 +166,17 @@ PlayerCompositorDelegate::PlayerCompositorDelegate(
weak_factory_.GetWeakPtr()));
}
-PlayerCompositorDelegate::~PlayerCompositorDelegate() = default;
+PlayerCompositorDelegate::~PlayerCompositorDelegate() {
+ paint_preview_service_->GetTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(base::IgnoreResult(&FileManager::CompressDirectory),
+ paint_preview_service_->GetFileManager(), key_));
+}
void PlayerCompositorDelegate::OnCompositorServiceDisconnected() {
- // TODO(crbug.com/1039699): Handle compositor service disconnect event.
+ DVLOG(1) << "Compositor service disconnected.";
+ if (compositor_error_)
+ std::move(compositor_error_).Run();
}
void PlayerCompositorDelegate::OnCompositorClientCreated(
@@ -231,7 +240,9 @@ void PlayerCompositorDelegate::SendCompositeRequest(
}
void PlayerCompositorDelegate::OnCompositorClientDisconnected() {
- // TODO(crbug.com/1039699): Handle compositor client disconnect event.
+ DVLOG(1) << "Compositor client disconnected.";
+ if (compositor_error_)
+ std::move(compositor_error_).Run();
}
void PlayerCompositorDelegate::RequestBitmap(
diff --git a/chromium/components/paint_preview/player/player_compositor_delegate.h b/chromium/components/paint_preview/player/player_compositor_delegate.h
index 9759331fe44..372d568ee38 100644
--- a/chromium/components/paint_preview/player/player_compositor_delegate.h
+++ b/chromium/components/paint_preview/player/player_compositor_delegate.h
@@ -5,7 +5,7 @@
#ifndef COMPONENTS_PAINT_PREVIEW_PLAYER_PLAYER_COMPOSITOR_DELEGATE_H_
#define COMPONENTS_PAINT_PREVIEW_PLAYER_PLAYER_COMPOSITOR_DELEGATE_H_
-#include "base/callback_forward.h"
+#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
@@ -32,6 +32,7 @@ class PlayerCompositorDelegate {
PlayerCompositorDelegate(PaintPreviewBaseService* paint_preview_service,
const GURL& url,
const DirectoryKey& key,
+ base::OnceClosure compositor_error,
bool skip_service_launch = false);
virtual ~PlayerCompositorDelegate();
@@ -55,6 +56,9 @@ class PlayerCompositorDelegate {
std::vector<const GURL*> OnClick(const base::UnguessableToken& frame_guid,
const gfx::Rect& rect);
+ protected:
+ base::OnceClosure compositor_error_;
+
private:
void OnCompositorServiceDisconnected();
@@ -68,6 +72,7 @@ class PlayerCompositorDelegate {
mojom::PaintPreviewBeginCompositeRequestPtr begin_composite_request);
PaintPreviewBaseService* paint_preview_service_;
+ DirectoryKey key_;
std::unique_ptr<PaintPreviewCompositorService>
paint_preview_compositor_service_;
std::unique_ptr<PaintPreviewCompositorClient>
diff --git a/chromium/components/paint_preview/player/player_compositor_delegate_unittest.cc b/chromium/components/paint_preview/player/player_compositor_delegate_unittest.cc
index d37bd8c5473..66ff43daaa2 100644
--- a/chromium/components/paint_preview/player/player_compositor_delegate_unittest.cc
+++ b/chromium/components/paint_preview/player/player_compositor_delegate_unittest.cc
@@ -8,6 +8,7 @@
#include "base/callback.h"
#include "base/files/file_path.h"
+#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
@@ -79,21 +80,58 @@ TEST(PlayerCompositorDelegate, OnClick) {
loop.QuitClosure(), file_manager, proto, key));
loop.Run();
- PlayerCompositorDelegate player_compositor_delegate(&service, url, key, true);
+ {
+ PlayerCompositorDelegate player_compositor_delegate(
+ &service, url, key, base::DoNothing(), true);
+ env.RunUntilIdle();
+
+ auto res = player_compositor_delegate.OnClick(root_frame_id,
+ gfx::Rect(10, 20, 1, 1));
+ ASSERT_EQ(res.size(), 1U);
+ EXPECT_EQ(*(res[0]), root_frame_link);
+
+ res = player_compositor_delegate.OnClick(root_frame_id,
+ gfx::Rect(0, 0, 1, 1));
+ EXPECT_TRUE(res.empty());
+
+ res =
+ player_compositor_delegate.OnClick(subframe_id, gfx::Rect(1, 2, 1, 1));
+ ASSERT_EQ(res.size(), 1U);
+ EXPECT_EQ(*(res[0]), subframe_link);
+ }
env.RunUntilIdle();
+}
- auto res = player_compositor_delegate.OnClick(root_frame_id,
- gfx::Rect(10, 20, 1, 1));
- ASSERT_EQ(res.size(), 1U);
- EXPECT_EQ(*(res[0]), root_frame_link);
-
- res =
- player_compositor_delegate.OnClick(root_frame_id, gfx::Rect(0, 0, 1, 1));
- EXPECT_TRUE(res.empty());
+TEST(PlayerCompositorDelegate, CompressOnClose) {
+ base::test::TaskEnvironment env;
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- res = player_compositor_delegate.OnClick(subframe_id, gfx::Rect(1, 2, 1, 1));
- ASSERT_EQ(res.size(), 1U);
- EXPECT_EQ(*(res[0]), subframe_link);
+ PaintPreviewBaseService service(temp_dir.GetPath(), "test", nullptr, false);
+ auto file_manager = service.GetFileManager();
+ auto key = file_manager->CreateKey(1U);
+ base::FilePath dir;
+ file_manager->GetTaskRunner()->PostTaskAndReplyWithResult(
+ FROM_HERE,
+ base::BindOnce(&FileManager::CreateOrGetDirectory, file_manager, key,
+ false),
+ base::BindOnce(
+ [](base::FilePath* out,
+ const base::Optional<base::FilePath>& file_path) {
+ *out = file_path.value();
+ },
+ base::Unretained(&dir)));
+ env.RunUntilIdle();
+ std::string data = "foo";
+ EXPECT_TRUE(
+ base::WriteFile(dir.AppendASCII("test_file"), data.data(), data.size()));
+ {
+ PlayerCompositorDelegate player_compositor_delegate(
+ &service, GURL(), key, base::DoNothing(), true);
+ env.RunUntilIdle();
+ }
+ env.RunUntilIdle();
+ EXPECT_TRUE(base::PathExists(dir.AddExtensionASCII(".zip")));
}
} // namespace paint_preview
diff --git a/chromium/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc b/chromium/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc
index bcbf397ad37..fdfe4c8032d 100644
--- a/chromium/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc
+++ b/chromium/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc
@@ -101,7 +101,9 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameAndClipping) {
base::Unretained(&out_response)));
content::RunAllTasksUntilIdle();
- EXPECT_FALSE(out_response->embedding_token.has_value());
+ EXPECT_TRUE(out_response->embedding_token.has_value());
+ EXPECT_EQ(frame->GetWebFrame()->GetEmbeddingToken(),
+ out_response->embedding_token.value());
EXPECT_EQ(out_response->content_id_to_embedding_token.size(), 0U);
EXPECT_EQ(out_response->links.size(), 1U);
@@ -177,7 +179,9 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameWithScroll) {
base::Unretained(&out_response)));
content::RunAllTasksUntilIdle();
- EXPECT_FALSE(out_response->embedding_token.has_value());
+ EXPECT_TRUE(out_response->embedding_token.has_value());
+ EXPECT_EQ(frame->GetWebFrame()->GetEmbeddingToken(),
+ out_response->embedding_token.value());
EXPECT_EQ(out_response->content_id_to_embedding_token.size(), 0U);
// Relaxed checks on dimensions and no checks on positions. This is not
@@ -233,7 +237,9 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureFragment) {
&out_response));
content::RunAllTasksUntilIdle();
- EXPECT_FALSE(out_response->embedding_token.has_value());
+ EXPECT_TRUE(out_response->embedding_token.has_value());
+ EXPECT_EQ(frame->GetWebFrame()->GetEmbeddingToken(),
+ out_response->embedding_token.value());
EXPECT_EQ(out_response->content_id_to_embedding_token.size(), 0U);
EXPECT_EQ(out_response->links.size(), 1U);
@@ -293,7 +299,9 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameAndLocalFrame) {
base::Unretained(&out_response)));
content::RunAllTasksUntilIdle();
- EXPECT_FALSE(out_response->embedding_token.has_value());
+ EXPECT_TRUE(out_response->embedding_token.has_value());
+ EXPECT_EQ(frame->GetWebFrame()->GetEmbeddingToken(),
+ out_response->embedding_token.value());
EXPECT_EQ(out_response->content_id_to_embedding_token.size(), 0U);
}
@@ -327,7 +335,7 @@ TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureLocalFrame) {
base::Unretained(&out_response)));
content::RunAllTasksUntilIdle();
- EXPECT_FALSE(out_response->embedding_token.has_value());
+ EXPECT_TRUE(out_response->embedding_token.has_value());
EXPECT_EQ(out_response->content_id_to_embedding_token.size(), 0U);
}
diff --git a/chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc b/chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc
index c0cd610d353..95081f0f945 100644
--- a/chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc
+++ b/chromium/components/paint_preview/renderer/paint_preview_recorder_impl.cc
@@ -155,9 +155,13 @@ void PaintPreviewRecorderImpl::CapturePaintPreviewInternal(
bounds = gfx::Rect(params->clip_rect.size());
}
- cc::PaintRecorder recorder;
auto tracker = std::make_unique<PaintPreviewTracker>(
params->guid, frame->GetEmbeddingToken(), is_main_frame_);
+ auto size = frame->GetScrollOffset();
+ tracker->SetScrollForFrame(SkISize::Make(size.width, size.height));
+ response->scroll_offsets = gfx::Size(size.width, size.height);
+
+ cc::PaintRecorder recorder;
cc::PaintCanvas* canvas =
recorder.beginRecording(bounds.width(), bounds.height());
canvas->SetPaintPreviewTracker(tracker.get());
@@ -169,7 +173,8 @@ void PaintPreviewRecorderImpl::CapturePaintPreviewInternal(
// slow).
base::TimeTicks start_time = base::TimeTicks::Now();
TRACE_EVENT_BEGIN0("paint_preview", "WebLocalFrame::CapturePaintPreview");
- bool success = frame->CapturePaintPreview(bounds, canvas);
+ bool success = frame->CapturePaintPreview(
+ bounds, canvas, /*include_linked_destinations=*/true);
TRACE_EVENT_END0("paint_preview", "WebLocalFrame::CapturePaintPreview");
base::TimeDelta capture_time = base::TimeTicks::Now() - start_time;
response->blink_recording_time = capture_time;
@@ -202,18 +207,12 @@ void PaintPreviewRecorderImpl::CapturePaintPreviewInternal(
return;
}
- base::ThreadPool::PostTaskAndReplyWithResult(
- FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
- base::BindOnce(&FinishRecording, recorder.finishRecordingAsPicture(),
- bounds, std::move(tracker), std::move(params->file),
- params->max_capture_size, std::move(response)),
- base::BindOnce(
- [](CapturePaintPreviewCallback callback,
- FinishedRecording recording) {
- std::move(callback).Run(recording.status,
- std::move(recording.response));
- },
- std::move(callback)));
+ // This cannot be done async if the recording contains a GPU accelerated
+ // image.
+ FinishedRecording recording = FinishRecording(
+ recorder.finishRecordingAsPicture(), bounds, std::move(tracker),
+ std::move(params->file), params->max_capture_size, std::move(response));
+ std::move(callback).Run(recording.status, std::move(recording.response));
}
} // namespace paint_preview
diff --git a/chromium/components/paint_preview/renderer/paint_preview_recorder_utils.cc b/chromium/components/paint_preview/renderer/paint_preview_recorder_utils.cc
index 2386c202a6c..caa501fc937 100644
--- a/chromium/components/paint_preview/renderer/paint_preview_recorder_utils.cc
+++ b/chromium/components/paint_preview/renderer/paint_preview_recorder_utils.cc
@@ -48,7 +48,7 @@ bool SerializeAsSkPicture(sk_sp<const cc::PaintRecord> record,
auto skp = ToSkPicture(
record, SkRect::MakeWH(dimensions.width(), dimensions.height()), nullptr,
custom_callback);
- if (!skp)
+ if (!skp || skp->cullRect().width() == 0 || skp->cullRect().height() == 0)
return false;
TypefaceSerializationContext typeface_context(tracker->GetTypefaceUsageMap());