Commit 6ed110fb authored by Daniel Hosseinian's avatar Daniel Hosseinian Committed by Commit Bot

Implement a mechanism to request thumbnails from PDFiumEngine

PDFiumEngine is sent a request for a thumbnail from the client. If the
page is available for rendering, the request is immediately scheduled.
Otherwise, the request is scheduled when the page becomes available.

Bug: 652400
Change-Id: I9377619a5f71dce5daa727630a9003f2dc9aac5d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2415588
Commit-Queue: Daniel Hosseinian <dhoss@chromium.org>
Reviewed-by: default avatarKahmy Moon <kmoon@google.com>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Reviewed-by: default avatarK. Moon <kmoon@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810798}
parent 27d3750c
......@@ -59,10 +59,13 @@ class VarDictionary;
namespace chrome_pdf {
class InputEvent;
class Thumbnail;
class UrlLoader;
struct DocumentAttachmentInfo;
struct DocumentMetadata;
using SendThumbnailCallback = base::OnceCallback<void(Thumbnail)>;
// Do one time initialization of the SDK.
// If |enable_v8| is false, then the PDFEngine will not be able to run
// JavaScript.
......@@ -491,6 +494,12 @@ class PDFEngine {
virtual uint32_t GetLoadedByteSize() = 0;
virtual bool ReadLoadedBytes(uint32_t length, void* buffer) = 0;
// Requests for a thumbnail to be sent using a callback when the page is ready
// to be rendered. |send_callback| is run with the thumbnail data when ready.
virtual void RequestThumbnail(int page_index,
float device_pixel_ratio,
SendThumbnailCallback send_callback) = 0;
};
// Interface for exports that wrap the PDF engine.
......
......@@ -4139,6 +4139,14 @@ void PDFiumEngine::SetLinkUnderCursorForAnnotation(FPDF_ANNOTATION annot,
UpdateLinkUnderCursor(target.url);
}
void PDFiumEngine::RequestThumbnail(int page_index,
float device_pixel_ratio,
SendThumbnailCallback send_callback) {
DCHECK(PageIndexInBounds(page_index));
pages_[page_index]->RequestThumbnail(device_pixel_ratio,
std::move(send_callback));
}
PDFiumEngine::ProgressivePaint::ProgressivePaint(int index,
const gfx::Rect& rect)
: page_index_(index), rect_(rect) {}
......
......@@ -174,6 +174,9 @@ class PDFiumEngine : public PDFEngine,
PP_PrivateAccessibilityFocusInfo GetFocusInfo() override;
uint32_t GetLoadedByteSize() override;
bool ReadLoadedBytes(uint32_t length, void* buffer) override;
void RequestThumbnail(int page_index,
float device_pixel_ratio,
SendThumbnailCallback send_callback) override;
// DocumentLoader::Client:
pp::Instance* GetPluginInstance() override;
......
......@@ -8,6 +8,8 @@
#include "base/hash/md5.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/gtest_util.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "pdf/document_attachment_info.h"
......@@ -19,6 +21,7 @@
#include "pdf/ppapi_migration/input_event_conversions.h"
#include "pdf/test/test_client.h"
#include "pdf/test/test_document_loader.h"
#include "pdf/thumbnail.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/point.h"
......@@ -111,7 +114,6 @@ class PDFiumEngineTest : public PDFiumTestBase {
return loaded_incrementally;
}
private:
// Counts the number of available pages. Returns `int` instead of `size_t` for
// consistency with `PDFiumEngine::GetNumberOfPages()`.
int CountAvailablePages(const PDFiumEngine& engine) {
......@@ -395,6 +397,88 @@ TEST_F(PDFiumEngineTest, IncrementalLoadingFeatureDisabled) {
EXPECT_FALSE(TryLoadIncrementally());
}
TEST_F(PDFiumEngineTest, RequestThumbnail) {
TestClient client;
std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
&client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
ASSERT_TRUE(engine);
const int num_pages = engine->GetNumberOfPages();
ASSERT_EQ(5, num_pages);
ASSERT_EQ(num_pages, CountAvailablePages(*engine));
// Each page should immediately return a thumbnail.
for (int i = 0; i < num_pages; ++i) {
base::MockCallback<SendThumbnailCallback> send_callback;
EXPECT_CALL(send_callback, Run);
engine->RequestThumbnail(/*page_index=*/i, /*device_pixel_ratio=*/1,
send_callback.Get());
}
}
TEST_F(PDFiumEngineTest, RequestThumbnailLinearized) {
NiceMock<MockTestClient> client;
InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
&client, FILE_PATH_LITERAL("linearized.pdf"));
ASSERT_TRUE(initialize_result.engine);
PDFiumEngine& engine = *initialize_result.engine;
// Load only some pages.
initialize_result.document_loader->SimulateLoadData(8192);
// Note: Plugin size chosen so all pages of the document are visible. The
// engine only updates availability incrementally for visible pages.
engine.PluginSizeUpdated({1024, 4096});
const int num_pages = engine.GetNumberOfPages();
ASSERT_EQ(3, num_pages);
const int available_pages = CountAvailablePages(engine);
ASSERT_LT(0, available_pages);
ASSERT_GT(num_pages, available_pages);
// Initialize callbacks for first and last pages.
base::MockCallback<SendThumbnailCallback> first_loaded;
base::MockCallback<SendThumbnailCallback> last_loaded;
// When the document is partially loaded, `SendThumbnailCallback` is only run
// for the loaded page even though `RequestThumbnail()` gets called for both
// pages.
EXPECT_CALL(first_loaded, Run);
engine.RequestThumbnail(/*page_index=*/0, /*device_pixel_ratio=*/1,
first_loaded.Get());
engine.RequestThumbnail(/*page_index=*/num_pages - 1,
/*device_pixel_ratio=*/1, last_loaded.Get());
// Finish loading the document. `SendThumbnailCallback` should be run for the
// last page.
EXPECT_CALL(last_loaded, Run);
while (initialize_result.document_loader->SimulateLoadData(UINT32_MAX))
continue;
}
using PDFiumEngineDeathTest = PDFiumEngineTest;
TEST_F(PDFiumEngineDeathTest, RequestThumbnailRedundant) {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
NiceMock<MockTestClient> client;
InitializeEngineResult initialize_result = InitializeEngineWithoutLoading(
&client, FILE_PATH_LITERAL("linearized.pdf"));
ASSERT_TRUE(initialize_result.engine);
PDFiumEngine& engine = *initialize_result.engine;
// Load only some pages.
initialize_result.document_loader->SimulateLoadData(8192);
// Twice request a thumbnail for the second page, which is not loaded. The
// second call should crash.
base::MockCallback<SendThumbnailCallback> mock_callback;
engine.RequestThumbnail(/*page_index=*/1, /*device_pixel_ratio=*/1,
mock_callback.Get());
EXPECT_DCHECK_DEATH(engine.RequestThumbnail(
/*page_index=*/1, /*device_pixel_ratio=*/1, mock_callback.Get()));
}
class TabbingTestClient : public TestClient {
public:
TabbingTestClient() = default;
......
......@@ -11,6 +11,8 @@
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/check_op.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
......@@ -1463,6 +1465,22 @@ gfx::Rect PDFiumPage::PageToScreen(const gfx::Point& page_point,
new_size_y.ValueOrDie());
}
void PDFiumPage::RequestThumbnail(float device_pixel_ratio,
SendThumbnailCallback send_callback) {
DCHECK(!thumbnail_callback_);
if (available()) {
GenerateAndSendThumbnail(device_pixel_ratio, std::move(send_callback));
return;
}
// It is safe to use base::Unretained(this) because the callback is only used
// by |this|.
thumbnail_callback_ = base::BindOnce(
&PDFiumPage::GenerateAndSendThumbnail, base::Unretained(this),
device_pixel_ratio, std::move(send_callback));
}
Thumbnail PDFiumPage::GenerateThumbnail(float device_pixel_ratio) {
DCHECK(available());
......@@ -1491,6 +1509,19 @@ Thumbnail PDFiumPage::GenerateThumbnail(float device_pixel_ratio) {
return thumbnail;
}
void PDFiumPage::GenerateAndSendThumbnail(float device_pixel_ratio,
SendThumbnailCallback send_callback) {
std::move(send_callback).Run(GenerateThumbnail(device_pixel_ratio));
}
void PDFiumPage::MarkAvailable() {
available_ = true;
// Fulfill pending thumbnail request.
if (thumbnail_callback_)
std::move(thumbnail_callback_).Run();
}
PDFiumPage::ScopedUnloadPreventer::ScopedUnloadPreventer(PDFiumPage* page)
: page_(page) {
page_->preventing_unload_count_++;
......
......@@ -10,6 +10,8 @@
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/optional.h"
#include "base/strings/string16.h"
......@@ -159,7 +161,11 @@ class PDFiumPage {
double bottom,
PageOrientation orientation) const;
// Generate a page thumbnail accommodating a specific |device_pixel_ratio|.
// Sets the callbacks for sending the thumbnail.
void RequestThumbnail(float device_pixel_ratio,
SendThumbnailCallback send_callback);
// Generates a page thumbnail accommodating a specific |device_pixel_ratio|.
Thumbnail GenerateThumbnail(float device_pixel_ratio);
int index() const { return index_; }
......@@ -170,7 +176,7 @@ class PDFiumPage {
// Availability is a one-way transition: A page can become available, but it
// cannot become unavailable (unless deleted entirely).
bool available() const { return available_; }
void MarkAvailable() { available_ = true; }
void MarkAvailable();
void set_calculated_links(bool calculated_links) {
calculated_links_ = calculated_links;
......@@ -376,6 +382,9 @@ class PDFiumPage {
const std::vector<Highlight>& highlights);
bool PopulateFormFieldProperties(FPDF_ANNOTATION annot,
FormField* form_field);
// Generates and sends the thumbnail using |send_callback|.
void GenerateAndSendThumbnail(float device_pixel_ratio,
SendThumbnailCallback send_callback);
PDFiumEngine* engine_;
ScopedFPDFPage page_;
......@@ -397,6 +406,7 @@ class PDFiumPage {
// The set of character indices on which text runs need to be broken for page
// objects.
std::set<int> page_object_text_run_breaks_;
base::OnceClosure thumbnail_callback_;
bool available_;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment