blob: 533da7179c078840d9b4ce0d21f17d8a1764da2a [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/media/capture/crop_id_web_contents_helper.h"
#include <string>
#include <vector>
#include "base/containers/contains.h"
#include "base/functional/callback.h"
#include "base/token.h"
#include "base/uuid.h"
#include "build/build_config.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#if BUILDFLAG(IS_ANDROID)
#error Region Capture not supported on Android.
#endif
namespace content {
// TODO(crbug.com/1264849): Remove this protected static function.
// See header for more details.
base::Token CropIdWebContentsHelper::GUIDToToken(const base::Uuid& guid) {
std::string lowercase = guid.AsLowercaseString();
// |lowercase| is either empty, or follows the expected pattern.
// TODO(crbug.com/1260380): Resolve open question of correct treatment
// of an invalid GUID.
if (lowercase.empty()) {
return base::Token();
}
DCHECK_EQ(lowercase.length(), 32u + 4u); // 32 hex-chars; 4 hyphens.
base::RemoveChars(lowercase, "-", &lowercase);
DCHECK_EQ(lowercase.length(), 32u); // 32 hex-chars; 0 hyphens.
base::StringPiece string_piece(lowercase);
uint64_t high = 0;
bool success = base::HexStringToUInt64(string_piece.substr(0, 16), &high);
DCHECK(success);
uint64_t low = 0;
success = base::HexStringToUInt64(string_piece.substr(16, 16), &low);
DCHECK(success);
return base::Token(high, low);
}
CropIdWebContentsHelper::CropIdWebContentsHelper(WebContents* web_contents)
: WebContentsObserver(web_contents),
WebContentsUserData<CropIdWebContentsHelper>(*web_contents) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(web_contents);
}
CropIdWebContentsHelper::~CropIdWebContentsHelper() = default;
std::string CropIdWebContentsHelper::ProduceCropId() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Prevent Web-applications from producing an excessive number of crop-IDs.
if (crop_ids_.size() >= kMaxCropIdsPerWebContents) {
return std::string();
}
// Given the exceedingly low likelihood of collisions, the check for
// uniqueness could have theoretically been skipped. But it's cheap
// enough to perform, given that `kMaxCropIdsPerWebContents` is so small
// compared to the space of GUIDs, and it guarantees we never silently fail
// the application by cropping to the wrong, duplicate target.
base::Uuid guid;
base::Token crop_id;
do {
guid = base::Uuid::GenerateRandomV4();
crop_id = GUIDToToken(guid);
} while (IsAssociatedWithCropId(crop_id));
crop_ids_.push_back(crop_id);
return guid.AsLowercaseString();
}
bool CropIdWebContentsHelper::IsAssociatedWithCropId(
const base::Token& crop_id) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return base::Contains(crop_ids_, crop_id);
}
void CropIdWebContentsHelper::ReadyToCommitNavigation(
NavigationHandle* navigation_handle) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(navigation_handle);
// Cross-document navigation of the top-level frame invalidates all crop-IDs
// associated with the observed WebContents.
// Using IsInPrimaryMainFrame is valid here since the browser only caches this
// state for the active main frame.
if (!navigation_handle->IsSameDocument() &&
navigation_handle->IsInPrimaryMainFrame()) {
ClearCropIds();
}
}
void CropIdWebContentsHelper::ClearCropIds() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
crop_ids_.clear();
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(CropIdWebContentsHelper);
} // namespace content