blob: 30f35211886fdd59b6cd44b080eccc62da0bb031 [file] [log] [blame]
// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_MATCHING_METRICS_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_MATCHING_METRICS_H_
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "third_party/blink/public/common/privacy_budget/identifiable_token.h"
#include "third_party/blink/public/common/privacy_budget/identifiable_token_builder.h"
#include "third_party/blink/renderer/platform/fonts/font_description.h"
#include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/timer.h"
#include "third_party/blink/renderer/platform/wtf/hash_functions.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
namespace ukm {
class UkmRecorder;
} // namespace ukm
namespace blink {
// A (generic) wrapper around IdentifiableToken to enable its use as a HashMap
// key. The |token| represents the parameters by which a font was looked up.
// However, if |is_deleted_value| or |is_empty_value|, this key represents an
// object for HashMap's internal use only. In that case, |token| is left as a
// default value.
struct IdentifiableTokenKey {
IdentifiableToken token;
bool is_deleted_value = false;
bool is_empty_value = false;
IdentifiableTokenKey() : is_empty_value(true) {}
explicit IdentifiableTokenKey(const IdentifiableToken& token)
: token(token) {}
explicit IdentifiableTokenKey(WTF::HashTableDeletedValueType)
: is_deleted_value(true) {}
bool IsHashTableDeletedValue() const { return is_deleted_value; }
bool operator==(const IdentifiableTokenKey& other) const {
return token == other.token && is_deleted_value == other.is_deleted_value &&
is_empty_value == other.is_empty_value;
}
bool operator!=(const IdentifiableTokenKey& other) const {
return !(*this == other);
}
};
// A helper that defines the hash and equality functions that HashMap should use
// internally for comparing IdentifiableTokenKeys.
struct IdentifiableTokenKeyHash {
STATIC_ONLY(IdentifiableTokenKeyHash);
static unsigned GetHash(const IdentifiableTokenKey& key) {
IntHash<int64_t> hasher;
return hasher.GetHash(key.token.ToUkmMetricValue()) ^
hasher.GetHash((key.is_deleted_value << 1) + key.is_empty_value);
}
static bool Equal(const IdentifiableTokenKey& a,
const IdentifiableTokenKey& b) {
return a == b;
}
static const bool safe_to_compare_to_empty_or_deleted = true;
};
// A helper that defines the invalid 'empty value' that HashMap should use
// internally.
struct IdentifiableTokenKeyHashTraits
: WTF::SimpleClassHashTraits<IdentifiableTokenKey> {
STATIC_ONLY(IdentifiableTokenKeyHashTraits);
static const bool kEmptyValueIsZero = false;
static IdentifiableTokenKey EmptyValue() { return IdentifiableTokenKey(); }
};
// Tracks and reports UKM metrics of attempted font family match attempts (both
// successful and not successful) by the current frame.
//
// The number of successful / not successful font family match attempts are
// reported to UKM. The class de-dupes attempts to match the same font family
// name such that they are counted as one attempt.
//
// Each local font lookup is also reported as is each mapping of generic font
// family name to its corresponding actual font family names. Local font lookups
// are deduped according to the family name looked up in the FontCache and the
// FontSelectionRequest parameters (i.e. weight, width and slope). Generic font
// family lookups are de-duped according to the generic name, the
// GenericFamilyType and the script. Both types of lookup events are reported
// regularly.
class PLATFORM_EXPORT FontMatchingMetrics {
public:
enum FontLoadContext { kTopLevelFrame = 0, kSubframe, kWorker };
// Create a FontMatchingMetrics objects for a frame, with |top_level|
// indicating whether it is a mainframe.
FontMatchingMetrics(bool top_level,
ukm::UkmRecorder* ukm_recorder,
ukm::SourceId source_id,
scoped_refptr<base::SingleThreadTaskRunner> task_runner);
// Create a FontMatchingMetrics objects for a worker.
FontMatchingMetrics(ukm::UkmRecorder* ukm_recorder,
ukm::SourceId source_id,
scoped_refptr<base::SingleThreadTaskRunner> task_runner);
// Called when a page attempts to match a font family, and the font family is
// available.
void ReportSuccessfulFontFamilyMatch(const AtomicString& font_family_name);
// Called when a page attempts to match a font family, and the font family is
// not available.
void ReportFailedFontFamilyMatch(const AtomicString& font_family_name);
// Called when a page attempts to match a system font family.
void ReportSystemFontFamily(const AtomicString& font_family_name);
// Called when a page attempts to match a web font family.
void ReportWebFontFamily(const AtomicString& font_family_name);
// Reports a font listed in a @font-face src:local rule that successfully
// matched.
void ReportSuccessfulLocalFontMatch(const AtomicString& font_name);
// Reports a font listed in a @font-face src:local rule that didn't
// successfully match.
void ReportFailedLocalFontMatch(const AtomicString& font_name);
// Reports a local font was looked up by a name and font description. This
// only includes lookups where the name is allowed to match family names,
// PostScript names and full font names.
void ReportFontLookupByUniqueOrFamilyName(
const AtomicString& name,
const FontDescription& font_description,
SimpleFontData* resulting_font_data);
// Reports a local font was looked up by a name and font description. This
// only includes lookups where the name is allowed to match PostScript names
// and full font names, but not family names.
void ReportFontLookupByUniqueNameOnly(const AtomicString& name,
const FontDescription& font_description,
SimpleFontData* resulting_font_data,
bool is_loading_fallback = false);
// Reports a font was looked up by a fallback character, fallback priority,
// and a font description.
void ReportFontLookupByFallbackCharacter(
UChar32 fallback_character,
FontFallbackPriority fallback_priority,
const FontDescription& font_description,
SimpleFontData* resulting_font_data);
// Reports a last-resort fallback font was looked up by a font description.
void ReportLastResortFallbackFontLookup(
const FontDescription& font_description,
SimpleFontData* resulting_font_data);
// Reports a generic font family name was matched according to the script and
// the user's preferences to a font family name.
void ReportFontFamilyLookupByGenericFamily(
const AtomicString& generic_font_family_name,
UScriptCode script,
FontDescription::GenericFamilyType generic_family_type,
const AtomicString& resulting_font_name);
// Called on page unload and forces metrics to be flushed.
void PublishAllMetrics();
// Called whenever a font lookup event that will be saved in |font_tracker| or
// |user_font_preference_mapping| occurs.
void OnFontLookup();
// Publishes the font lookup events. Recorded on document shutdown/worker
// destruction and every minute, as long as additional lookups are occurring.
void PublishIdentifiabilityMetrics();
// Publishes the number of font family matches attempted (both successful
// and otherwise) to UKM. Recorded on page unload.
void PublishUkmMetrics();
private:
void IdentifiabilityMetricsTimerFired(TimerBase*);
// This HashMap generically stores details of font lookups, i.e. what was used
// to search for the font, and what the resulting font was. The key is an
// IdentifiableTokenKey representing a wrapper around a digest of the lookup
// parameters. The value is an IdentifiableToken representing either a digest
// of the returned typeface or 0, if no valid typeface was found.
using TokenToTokenHashMap = HashMap<IdentifiableTokenKey,
IdentifiableToken,
IdentifiableTokenKeyHash,
IdentifiableTokenKeyHashTraits>;
// Adds a digest of the |font_data|'s typeface to |hash_map| using the key
// |input_key|, unless that key is already present. If |font_data| is not
// nullptr, then the typeface digest will also be saved with its PostScript
// name in |font_load_postscript_name_|.
void InsertFontHashIntoMap(IdentifiableTokenKey input_key,
SimpleFontData* font_data,
TokenToTokenHashMap& hash_map);
// Reports a local font's existence was looked up by a name, but its actual
// font data may or may not have been loaded. This only includes lookups where
// the name is allowed to match PostScript names and full font names, but not
// family names.
void ReportLocalFontExistenceByUniqueNameOnly(const AtomicString& font_name,
bool font_exists);
// Constructs a builder with a hash of the FontSelectionRequest already added.
IdentifiableTokenBuilder GetTokenBuilderWithFontSelectionRequest(
const FontDescription& font_description);
// Get a hash that uniquely represents the font data. Returns 0 if |font_data|
// is nullptr.
int64_t GetHashForFontData(SimpleFontData* font_data);
void Initialize();
// Get a token that uniquely represents the typeface's PostScript name. May
// represent the empty string if no PostScript name was found.
IdentifiableToken GetPostScriptNameTokenForFontData(
SimpleFontData* font_data);
// Font family names successfully matched.
HashSet<AtomicString> successful_font_families_;
// Font family names that weren't successfully matched.
HashSet<AtomicString> failed_font_families_;
// System font families the page attempted to match.
HashSet<AtomicString> system_font_families_;
// Web font families the page attempted to match.
HashSet<AtomicString> web_font_families_;
// @font-face src:local fonts that successfully matched.
HashSet<AtomicString> local_fonts_succeeded_;
// @font-face src:local fonts that didn't successfully match.
HashSet<AtomicString> local_fonts_failed_;
// Indicates whether this FontMatchingMetrics instance is for a top-level
// frame, a subframe or a worker.
const FontLoadContext load_context_;
TokenToTokenHashMap font_lookups_by_unique_or_family_name_;
TokenToTokenHashMap font_lookups_by_unique_name_only_;
TokenToTokenHashMap font_lookups_by_fallback_character_;
TokenToTokenHashMap font_lookups_as_last_resort_;
TokenToTokenHashMap generic_font_lookups_;
TokenToTokenHashMap font_load_postscript_name_;
TokenToTokenHashMap local_font_existence_by_unique_name_only_;
ukm::UkmRecorder* const ukm_recorder_;
const ukm::SourceId source_id_;
TaskRunnerTimer<FontMatchingMetrics> identifiability_metrics_timer_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_MATCHING_METRICS_H_