Files
cef/tests/ceftests/pdf_viewer_unittest.cc
Marshall Greenblatt dd81904a2f Add initial support for API versioning (see #3836)
- Generated files are now created when running cef_create_projects or
  the new version_manager.py tool. These files are still created in the
  cef/ source tree (same location as before) but Git ignores them due to
  the generated .gitignore file.
- API hashes are committed to Git as a new cef_api_versions.json file.
  This file is used for both code generation and CEF version calculation
  (replacing the previous usage of cef_api_hash.h for this purpose).
  It will be updated by the CEF admin before merging breaking API
  changes upstream.
- As an added benefit to the above, contributor PRs will no longer
  contain generated code that is susceptible to frequent merge conflicts.
- From a code generation perspective, the main difference is that we now
  use versioned structs (e.g. cef_browser_0_t instead of cef_browser_t)
  on the libcef (dll/framework) side. Most of the make_*.py tool changes
  are related to supporting this.
- From the client perspective, you can now define CEF_API_VERSION in the
  project configuration (or get CEF_EXPERIMENTAL by default). This
  define will change the API exposed in CEF’s include/ and include/capi
  header files. All client-side targets including libcef_dll_wrapper
  will need be recompiled when changing this define.
- Examples of the new API-related define usage are provided in
  cef_api_version_test.h, api_version_test_impl.cc and
  api_version_unittest.cc.

To test:
- Run `ceftests --gtest_filter=ApiVersionTest.*`
- Add `cef_api_version=13300` to GN_DEFINES. Re-run configure, build and
  ceftests steps.
- Repeat with 13301, 13302, 13303 (all supported test versions).
2025-01-08 17:19:43 -05:00

226 lines
7.1 KiB
C++

// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "include/base/cef_callback.h"
#include "include/cef_request_context_handler.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_stream_resource_handler.h"
#include "tests/ceftests/test_handler.h"
#include "tests/ceftests/test_util.h"
#include "tests/gtest/include/gtest/gtest.h"
#include "tests/shared/browser/client_app_browser.h"
#include "tests/shared/browser/resource_util.h"
namespace {
const char kPdfHtmlUrl[] = "https://tests/pdf.html";
const char kPdfDirectUrl[] = "https://tests/pdf.pdf";
// Delay waiting for iframe tests to load the PDF file.
#if defined(OS_LINUX)
const int64_t kPdfLoadDelayMs = 7000;
#else
const int64_t kPdfLoadDelayMs = 5000;
#endif
// Browser-side test handler.
class PdfViewerTestHandler : public TestHandler, public CefContextMenuHandler {
public:
enum Mode {
// No specified context or handler (implicitly uses the global context).
GLOBAL_DEFAULT,
// Global context with no handler.
GLOBAL_NO_HANDLER,
// Custom context with no handler.
CUSTOM_NO_HANDLER,
};
PdfViewerTestHandler(Mode mode, const std::string& url)
: mode_(mode), url_(url) {}
// Loading the PDF directly in the main frame instead of a sub-frame.
bool HasDirectPdfLoad() const { return url_ == kPdfDirectUrl; }
CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() override {
return this;
}
void RunTest() override {
CefRefPtr<CefRequestContext> request_context;
if (mode_ == GLOBAL_NO_HANDLER) {
// Use the global request context.
request_context = CefRequestContext::CreateContext(
CefRequestContext::GetGlobalContext(), nullptr);
} else if (mode_ == CUSTOM_NO_HANDLER) {
// Create the request context that will use an in-memory cache.
CefRequestContextSettings settings;
request_context = CefRequestContext::CreateContext(settings, nullptr);
}
// Create the browser.
CreateBrowser(url_, request_context);
// Time out the test after a reasonable period of time.
SetTestTimeout(5000 + kPdfLoadDelayMs);
}
CefRefPtr<CefResourceHandler> GetResourceHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) override {
const std::string& url = request->GetURL();
if (url == kPdfHtmlUrl) {
CefRefPtr<CefStreamReader> stream =
client::GetBinaryResourceReader("pdf.html");
return new CefStreamResourceHandler("text/html", stream);
} else if (url == kPdfDirectUrl) {
CefRefPtr<CefStreamReader> stream =
client::GetBinaryResourceReader("pdf.pdf");
return new CefStreamResourceHandler("application/pdf", stream);
}
return nullptr;
}
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) override {
bool is_pdf1 = false;
const std::string& url = frame->GetURL();
if (url == "about:blank" || url.find("chrome-extension://") == 0) {
return;
}
if (url == kPdfHtmlUrl) {
if (!got_on_load_end_html_) {
got_on_load_end_html_.yes();
} else {
NOTREACHED();
}
} else if (url == kPdfDirectUrl) {
if (!got_on_load_end_pdf1_) {
got_on_load_end_pdf1_.yes();
is_pdf1 = true;
} else if (!got_on_load_end_pdf2_) {
got_on_load_end_pdf2_.yes();
} else {
NOTREACHED();
}
} else {
NOTREACHED() << "url=" << url;
}
if (is_pdf1) {
// The first PDF document has loaded.
if (got_context_menu_dismissed_) {
// After context menu display. Destroy the test.
CefPostDelayedTask(
TID_UI, base::BindOnce(&PdfViewerTestHandler::DestroyTest, this),
kPdfLoadDelayMs);
} else {
// Trigger the context menu.
CefPostDelayedTask(
TID_UI,
base::BindOnce(&PdfViewerTestHandler::TriggerContextMenu, this,
frame->GetBrowser()),
kPdfLoadDelayMs);
}
}
}
void TriggerContextMenu(CefRefPtr<CefBrowser> browser) {
CefMouseEvent mouse_event;
if (HasDirectPdfLoad()) {
// Somewhere in the main PDF viewing area (avoid left preview bar).
mouse_event.x = 400;
mouse_event.y = 200;
} else {
// Somewhere in the first PDF viewing area.
mouse_event.x = 100;
mouse_event.y = 100;
}
// Send right-click mouse down and mouse up to tigger context menu.
SendMouseClickEvent(browser, mouse_event, MBT_RIGHT);
}
bool RunContextMenu(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefContextMenuParams> params,
CefRefPtr<CefMenuModel> model,
CefRefPtr<CefRunContextMenuCallback> callback) override {
EXPECT_FALSE(got_run_context_menu_);
got_run_context_menu_.yes();
// Do nothing with the context menu.
callback->Cancel();
return true;
}
void OnContextMenuDismissed(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame) override {
EXPECT_FALSE(got_context_menu_dismissed_);
got_context_menu_dismissed_.yes();
CefPostTask(TID_UI,
base::BindOnce(&PdfViewerTestHandler::DestroyTest, this));
}
void DestroyTest() override {
EXPECT_TRUE(got_run_context_menu_);
EXPECT_TRUE(got_context_menu_dismissed_);
if (url_ == kPdfHtmlUrl) {
// The HTML file will load the PDF twice in iframes.
EXPECT_TRUE(got_on_load_end_html_);
EXPECT_TRUE(got_on_load_end_pdf1_);
EXPECT_TRUE(got_on_load_end_pdf2_);
} else if (url_ == kPdfDirectUrl) {
// Load the PDF file directly.
EXPECT_FALSE(got_on_load_end_html_);
EXPECT_TRUE(got_on_load_end_pdf1_);
EXPECT_FALSE(got_on_load_end_pdf2_);
} else {
NOTREACHED();
}
TestHandler::DestroyTest();
}
const Mode mode_;
const std::string url_;
TrackCallback got_on_load_end_html_;
TrackCallback got_on_load_end_pdf1_;
TrackCallback got_on_load_end_pdf2_;
TrackCallback got_run_context_menu_;
TrackCallback got_context_menu_dismissed_;
IMPLEMENT_REFCOUNTING(PdfViewerTestHandler);
};
} // namespace
#define RUN_TEST(name, type, url) \
TEST(PdfViewerTest, name) { \
CefRefPtr<PdfViewerTestHandler> handler = \
new PdfViewerTestHandler(PdfViewerTestHandler::type, url); \
handler->ExecuteTest(); \
ReleaseAndWaitForDestructor(handler); \
}
RUN_TEST(GlobalDefaultPdfDirect, GLOBAL_DEFAULT, kPdfDirectUrl)
RUN_TEST(GlobalDefaultPdfHtml, GLOBAL_DEFAULT, kPdfHtmlUrl)
RUN_TEST(GlobalNoHandlerPdfDirect, GLOBAL_NO_HANDLER, kPdfDirectUrl)
RUN_TEST(GlobalNoHandlerPdfHtml, GLOBAL_NO_HANDLER, kPdfHtmlUrl)
RUN_TEST(CustomNoHandlerPdfDirect, CUSTOM_NO_HANDLER, kPdfDirectUrl)
RUN_TEST(CustomNoHandlerPdfHtml, CUSTOM_NO_HANDLER, kPdfHtmlUrl)