0

[unseasoned-pdf] Add support for "has-edits" attribute

Implements support for the "has-edits" attribute set when exiting from
annotation mode.

Also improves overall consistency between OutOfProcessInstance::Init()
and ParseWebPluginParams(), and adds default implementations of all copy
and move operations to ParsedParams.

Simplifies ParsedParamsTest to test individual attributes independently.

Fixed: 1257666
Bug: 1232152
Change-Id: I365cefb0069c4e2ebeb89abd278b1a471da260cd
Cq-Do-Not-Cancel-Tryjobs: true
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3228752
Commit-Queue: K. Moon <kmoon@chromium.org>
Reviewed-by: Daniel Hosseinian <dhoss@chromium.org>
Cr-Commit-Position: refs/heads/main@{#932687}
This commit is contained in:
K. Moon
2021-10-18 21:17:28 +00:00
committed by Chromium LUCI CQ
parent 5be415d687
commit 8b2a708e9d
7 changed files with 165 additions and 81 deletions

@ -496,9 +496,9 @@ export class PluginController {
} else {
url = URL.createObjectURL(new Blob([data]));
this.plugin_.setAttribute('src', url);
this.plugin_.setAttribute('has-edits', '');
}
this.plugin_.setAttribute('has-edits', '');
this.plugin_.style.display = 'block';
try {
await this.getLoadedCallback_();

@ -15,6 +15,7 @@ channel.port1.onmessage = e => {
URL.revokeObjectURL(plugin.src);
}
plugin.src = URL.createObjectURL(new Blob([e.data.dataToLoad]));
plugin.setAttribute('has-edits', '');
} else {
plugin.postMessage(e.data);
}

@ -450,6 +450,7 @@ bool OutOfProcessInstance::Init(uint32_t argc,
text_input_ = std::make_unique<pp::TextInput_Dev>(this);
// Parse attributes. Keep in sync with `ParseWebPluginParams()`.
const char* src_url = nullptr;
const char* original_url = nullptr;
const char* top_level_url = nullptr;

@ -9,6 +9,7 @@
#include "base/strings/string_number_conversions.h"
#include "pdf/pdfium/pdfium_form_filler.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_vector.h"
#include "third_party/blink/public/web/web_plugin_params.h"
namespace chrome_pdf {
@ -16,38 +17,45 @@ namespace chrome_pdf {
ParsedParams::ParsedParams() = default;
ParsedParams::ParsedParams(const ParsedParams& other) = default;
ParsedParams& ParsedParams::operator=(const ParsedParams& other) = default;
ParsedParams::ParsedParams(ParsedParams&& other) = default;
ParsedParams& ParsedParams::operator=(ParsedParams&& other) = default;
ParsedParams::~ParsedParams() = default;
absl::optional<ParsedParams> ParseWebPluginParams(
const blink::WebPluginParams& params) {
// Keep in sync with `OutOfProcessInstance::Init()`.
// TODO(crbug.com/1232152): Don't have two implementations.
ParsedParams result;
for (size_t i = 0; i < params.attribute_names.size(); ++i) {
if (params.attribute_names[i] == "original-url") {
result.original_url = params.attribute_values[i].Utf8();
} else if (params.attribute_names[i] == "src") {
if (params.attribute_names[i] == "src") {
result.src_url = params.attribute_values[i].Utf8();
} else if (params.attribute_names[i] == "original-url") {
result.original_url = params.attribute_values[i].Utf8();
} else if (params.attribute_names[i] == "top-level-url") {
result.top_level_url = params.attribute_values[i].Utf8();
} else if (params.attribute_names[i] == "full-frame") {
result.full_frame = true;
} else if (params.attribute_names[i] == "background-color") {
SkColor background_color;
if (!base::StringToUint(params.attribute_values[i].Utf8(),
&background_color)) {
&result.background_color)) {
return absl::nullopt;
}
result.background_color = background_color;
} else if (params.attribute_names[i] == "javascript") {
if (params.attribute_values[i] != "allow")
result.script_option = PDFiumFormFiller::ScriptOption::kNoJavaScript;
} else if (params.attribute_names[i] == "has-edits") {
result.has_edits = true;
}
}
if (result.src_url.empty())
return absl::nullopt;
if (result.original_url.empty()) {
if (result.original_url.empty())
result.original_url = result.src_url;
}
return result;
}

@ -13,30 +13,39 @@
namespace blink {
struct WebPluginParams;
}
} // namespace blink
namespace chrome_pdf {
struct ParsedParams {
ParsedParams();
ParsedParams(const ParsedParams& other);
ParsedParams& operator=(const ParsedParams& other);
ParsedParams(ParsedParams&& other);
ParsedParams& operator=(ParsedParams&& other);
~ParsedParams();
// The document original URL. Must not be empty.
std::string original_url;
// The plugin source URL. Must not be empty.
std::string src_url;
// The background color for the PDF viewer.
absl::optional<SkColor> background_color;
// The document original URL. Must not be empty.
std::string original_url;
// The top-level URL.
std::string top_level_url;
// Whether the plugin should occupy the entire frame.
bool full_frame = false;
// The background color for the PDF viewer.
SkColor background_color = SK_ColorTRANSPARENT;
// Whether to execute JavaScript and maybe XFA.
PDFiumFormFiller::ScriptOption script_option =
PDFiumFormFiller::DefaultScriptOption();
// Whether the PDF was edited previously in annotation mode.
bool has_edits = false;
};
// Creates an `ParsedParams` by parsing a `blink::WebPluginParams`. If

@ -6,8 +6,10 @@
#include <string>
#include "base/strings/string_number_conversions.h"
#include "pdf/pdfium/pdfium_form_filler.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_vector.h"
#include "third_party/blink/public/web/web_plugin_params.h"
@ -17,103 +19,166 @@ namespace chrome_pdf {
namespace {
constexpr char kDummyOriginalUrl[] = "https://test.com/dummy.pdf";
constexpr char kDummySrcUrl[] = "chrome-extension://dummy-source-url";
using ::testing::AnyOf;
constexpr SkColor kNewBackgroundColor = SkColorSetARGB(0xFF, 0x52, 0x56, 0x59);
constexpr char kFakeSrcUrl[] = "chrome-extension://fake-source-url";
// `kNewBackgroundColor` as a decimal number in string format.
constexpr char kNewBackgroundColorStr[] = "4283586137";
// Creates a `blink::WebPluginParams` without any URL attributes, namely "src"
// and "original-url". The return value only contains valid "background-color"
// and "full-frame" attributes.
blink::WebPluginParams CreateWebPluginParamsWithoutUrl() {
// Creates a `blink::WebPluginParams` with only required attributes.
blink::WebPluginParams CreateMinimalWebPluginParams() {
blink::WebPluginParams params;
params.attribute_names.push_back(blink::WebString("background-color"));
params.attribute_values.push_back(blink::WebString(kNewBackgroundColorStr));
params.attribute_names.push_back(blink::WebString("full-frame"));
params.attribute_values.push_back(blink::WebString(""));
return params;
}
// Creates a `blink::WebPluginParams` with only the URL attributes: "src" and
// "original-url".
blink::WebPluginParams CreateWebPluginParamsWithUrls() {
blink::WebPluginParams params;
params.attribute_names.push_back(blink::WebString("original-url"));
params.attribute_values.push_back(blink::WebString(kDummyOriginalUrl));
params.attribute_names.push_back(blink::WebString("src"));
params.attribute_values.push_back(blink::WebString(kDummySrcUrl));
params.attribute_values.push_back(blink::WebString(kFakeSrcUrl));
return params;
}
} // namespace
TEST(ParsedParamsTest, ParseValidWebPluginParams) {
blink::WebPluginParams params = CreateWebPluginParamsWithoutUrl();
params.attribute_names.push_back(blink::WebString("original-url"));
params.attribute_values.push_back(blink::WebString(kDummyOriginalUrl));
params.attribute_names.push_back(blink::WebString("src"));
params.attribute_values.push_back(blink::WebString(kDummySrcUrl));
TEST(ParsedParamsTest, ParseWebPluginParamsMinimal) {
blink::WebPluginParams params = CreateMinimalWebPluginParams();
absl::optional<ParsedParams> result = ParseWebPluginParams(params);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(kDummyOriginalUrl, result->original_url);
EXPECT_EQ(kDummySrcUrl, result->src_url);
ASSERT_TRUE(result->background_color.has_value());
EXPECT_EQ(kNewBackgroundColor, result->background_color.value());
EXPECT_TRUE(result->full_frame);
EXPECT_EQ(kFakeSrcUrl, result->src_url);
EXPECT_EQ(kFakeSrcUrl, result->original_url);
EXPECT_EQ("", result->top_level_url);
EXPECT_FALSE(result->full_frame);
EXPECT_EQ(SK_ColorTRANSPARENT, result->background_color);
EXPECT_EQ(PDFiumFormFiller::DefaultScriptOption(), result->script_option);
EXPECT_FALSE(result->has_edits);
}
TEST(ParsedParamsTest, ParseWebPluginParamsWithoutSourceUrl) {
blink::WebPluginParams params = CreateWebPluginParamsWithoutUrl();
params.attribute_names.push_back(blink::WebString("original-url"));
params.attribute_values.push_back(blink::WebString(kDummyOriginalUrl));
blink::WebPluginParams params;
// Expect the `ParsedParams` to be invalid due to missing the source URL.
absl::optional<ParsedParams> result = ParseWebPluginParams(params);
EXPECT_FALSE(result.has_value());
}
TEST(ParseParsedParamsTest, ParseWebPluginParamsWithoutOriginalUrl) {
blink::WebPluginParams params = CreateWebPluginParamsWithoutUrl();
params.attribute_names.push_back(blink::WebString("src"));
params.attribute_values.push_back(blink::WebString(kDummySrcUrl));
TEST(ParsedParamsTest, ParseWebPluginParamsWithOriginalUrl) {
blink::WebPluginParams params = CreateMinimalWebPluginParams();
params.attribute_names.push_back(blink::WebString("original-url"));
params.attribute_values.push_back(
blink::WebString("https://example.com/original.pdf"));
// Expect the `ParsedParams` to be valid and `original_url` to be the same as
// `src_url`.
absl::optional<ParsedParams> result = ParseWebPluginParams(params);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(kDummySrcUrl, result->original_url);
EXPECT_EQ(kDummySrcUrl, result->src_url);
ASSERT_TRUE(result->background_color.has_value());
EXPECT_EQ(kNewBackgroundColor, result->background_color.value());
EXPECT_EQ(kFakeSrcUrl, result->src_url);
EXPECT_EQ("https://example.com/original.pdf", result->original_url);
}
TEST(ParsedParamsTest, ParseWebPluginParamsWithTopLevelUrl) {
blink::WebPluginParams params = CreateMinimalWebPluginParams();
params.attribute_names.push_back(blink::WebString("top-level-url"));
params.attribute_values.push_back(
blink::WebString("https://example.net/top.html"));
absl::optional<ParsedParams> result = ParseWebPluginParams(params);
ASSERT_TRUE(result.has_value());
EXPECT_EQ("https://example.net/top.html", result->top_level_url);
}
TEST(ParsedParamsTest, ParseWebPluginParamsWithFullFrame) {
blink::WebPluginParams params = CreateMinimalWebPluginParams();
params.attribute_names.push_back(blink::WebString("full-frame"));
params.attribute_values.push_back(blink::WebString(""));
absl::optional<ParsedParams> result = ParseWebPluginParams(params);
ASSERT_TRUE(result.has_value());
EXPECT_TRUE(result->full_frame);
}
TEST(ParsedParamsTest, ParseWebPluginParamsWithoutBackgroundColor) {
blink::WebPluginParams params = CreateWebPluginParamsWithUrls();
TEST(ParsedParamsTest, ParseWebPluginParamsWithFullFrameNonEmpty) {
blink::WebPluginParams params = CreateMinimalWebPluginParams();
params.attribute_names.push_back(blink::WebString("full-frame"));
params.attribute_values.push_back(blink::WebString("false"));
// The `ParsedParams` can still be valid without the background color
// attribute.
absl::optional<ParsedParams> result = ParseWebPluginParams(params);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(kDummyOriginalUrl, result->original_url);
EXPECT_EQ(kDummySrcUrl, result->src_url);
EXPECT_FALSE(result->background_color.has_value());
EXPECT_FALSE(result->full_frame);
EXPECT_TRUE(result->full_frame);
}
TEST(ParsedParamsTest, ParseWebPluginParamsWithBackgroundColor) {
blink::WebPluginParams params = CreateMinimalWebPluginParams();
params.attribute_names.push_back(blink::WebString("background-color"));
params.attribute_values.push_back(blink::WebString("4283586137"));
absl::optional<ParsedParams> result = ParseWebPluginParams(params);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(4283586137, result->background_color);
}
TEST(ParsedParamsTest, ParseWebPluginParamsWithInvalidBackgroundColor) {
blink::WebPluginParams params = CreateWebPluginParamsWithUrls();
blink::WebPluginParams params = CreateMinimalWebPluginParams();
params.attribute_names.push_back(blink::WebString("background-color"));
params.attribute_values.push_back(blink::WebString("red"));
// Expect the `ParsedParams` to be invalid because the background color's
// attribute value is in the wrong format.
absl::optional<ParsedParams> result = ParseWebPluginParams(params);
EXPECT_FALSE(result.has_value());
}
TEST(ParsedParamsTest, ParseWebPluginParamsWithJavascriptAllow) {
blink::WebPluginParams params = CreateMinimalWebPluginParams();
params.attribute_names.push_back(blink::WebString("javascript"));
params.attribute_values.push_back(blink::WebString("allow"));
absl::optional<ParsedParams> result = ParseWebPluginParams(params);
ASSERT_TRUE(result.has_value());
EXPECT_THAT(result->script_option,
AnyOf(PDFiumFormFiller::ScriptOption::kJavaScript,
PDFiumFormFiller::ScriptOption::kJavaScriptAndXFA));
}
TEST(ParsedParamsTest, ParseWebPluginParamsWithJavascriptEmpty) {
blink::WebPluginParams params = CreateMinimalWebPluginParams();
params.attribute_names.push_back(blink::WebString("javascript"));
params.attribute_values.push_back(blink::WebString(""));
absl::optional<ParsedParams> result = ParseWebPluginParams(params);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(PDFiumFormFiller::ScriptOption::kNoJavaScript,
result->script_option);
}
TEST(ParsedParamsTest, ParseWebPluginParamsWithJavascriptNonEmpty) {
blink::WebPluginParams params = CreateMinimalWebPluginParams();
params.attribute_names.push_back(blink::WebString("javascript"));
params.attribute_values.push_back(blink::WebString("true"));
absl::optional<ParsedParams> result = ParseWebPluginParams(params);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(PDFiumFormFiller::ScriptOption::kNoJavaScript,
result->script_option);
}
TEST(ParsedParamsTest, ParseWebPluginParamsWithHasEdits) {
blink::WebPluginParams params = CreateMinimalWebPluginParams();
params.attribute_names.push_back(blink::WebString("has-edits"));
params.attribute_values.push_back(blink::WebString(""));
absl::optional<ParsedParams> result = ParseWebPluginParams(params);
ASSERT_TRUE(result.has_value());
EXPECT_TRUE(result->has_edits);
}
TEST(ParsedParamsTest, ParseWebPluginParamsWithHasEditsNonEmpty) {
blink::WebPluginParams params = CreateMinimalWebPluginParams();
params.attribute_names.push_back(blink::WebString("has-edits"));
params.attribute_values.push_back(blink::WebString("false"));
absl::optional<ParsedParams> result = ParseWebPluginParams(params);
ASSERT_TRUE(result.has_value());
EXPECT_TRUE(result->has_edits);
}
} // namespace chrome_pdf

@ -308,9 +308,10 @@ bool PdfViewWebPlugin::InitializeCommon(
if (!params.has_value())
return false;
PerProcessInitializer::GetInstance().Acquire();
// TODO(crbug.com/1232152): Set crash keys like
// `ppapi::proxy::PDFResource::SetCrashData()`.
// TODO(crbug.com/1257666): Implement "has-edits" support.
PerProcessInitializer::GetInstance().Acquire();
InitializeBase(
engine ? std::move(engine)
: std::make_unique<PDFiumEngine>(this, params->script_option),
@ -318,9 +319,8 @@ bool PdfViewWebPlugin::InitializeCommon(
/*src_url=*/params->src_url,
/*original_url=*/params->original_url,
/*full_frame=*/params->full_frame,
/*background_color=*/
params->background_color.value_or(SK_ColorTRANSPARENT),
/*has_edits=*/false);
/*background_color=*/params->background_color,
/*has_edits=*/params->has_edits);
return true;
}