Use "= default" to denote default constructor or destructor
[WebKit-https.git] / Source / WebCore / page / CaptionUserPreferences.cpp
index 5b1590f..b1cc701 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  */
 
 #include "config.h"
+#include "CaptionUserPreferences.h"
 
 #if ENABLE(VIDEO_TRACK)
 
-#include "CaptionUserPreferences.h"
+#include "AudioTrackList.h"
 #include "DOMWrapperWorld.h"
+#include "LocalizedStrings.h"
+#include "MediaSelectionOption.h"
 #include "Page.h"
 #include "PageGroup.h"
 #include "Settings.h"
 #include "TextTrackList.h"
+#include "UserContentController.h"
+#include "UserContentTypes.h"
+#include "UserStyleSheet.h"
 #include "UserStyleSheetTypes.h"
-#include <wtf/NonCopyingSort.h>
+#include <heap/HeapInlines.h>
+#include <runtime/JSCellInlines.h>
+#include <runtime/StructureInlines.h>
+#include <wtf/Language.h>
 
 namespace WebCore {
 
-CaptionUserPreferences::CaptionUserPreferences(PageGroup* group)
+CaptionUserPreferences::CaptionUserPreferences(PageGroup& group)
     : m_pageGroup(group)
     , m_displayMode(ForcedOnly)
-    , m_timer(this, &CaptionUserPreferences::timerFired)
-    , m_testingMode(false)
-    , m_havePreferences(false)
+    , m_timer(*this, &CaptionUserPreferences::timerFired)
 {
 }
 
-CaptionUserPreferences::~CaptionUserPreferences()
+CaptionUserPreferences::~CaptionUserPreferences() = default;
+
+void CaptionUserPreferences::timerFired()
 {
+    captionPreferencesChanged();
 }
 
-void CaptionUserPreferences::timerFired(Timer<CaptionUserPreferences>*)
+void CaptionUserPreferences::beginBlockingNotifications()
 {
-    captionPreferencesChanged();
+    ++m_blockNotificationsCounter;
+}
+
+void CaptionUserPreferences::endBlockingNotifications()
+{
+    ASSERT(m_blockNotificationsCounter);
+    --m_blockNotificationsCounter;
 }
 
 void CaptionUserPreferences::notify()
 {
+    if (m_blockNotificationsCounter)
+        return;
+
     m_havePreferences = true;
     if (!m_timer.isActive())
-        m_timer.startOneShot(0);
+        m_timer.startOneShot(0_s);
 }
 
 CaptionUserPreferences::CaptionDisplayMode CaptionUserPreferences::captionDisplayMode() const
@@ -78,66 +97,74 @@ void CaptionUserPreferences::setCaptionDisplayMode(CaptionUserPreferences::Capti
     notify();
 }
 
+Page* CaptionUserPreferences::currentPage() const
+{
+    if (m_pageGroup.pages().isEmpty())
+        return nullptr;
+
+    return *(m_pageGroup.pages().begin());
+}
+
 bool CaptionUserPreferences::userPrefersCaptions() const
 {
-    Page* page = *(pageGroup()->pages().begin());
+    Page* page = currentPage();
     if (!page)
         return false;
 
-    return page->settings()->shouldDisplayCaptions();
+    return page->settings().shouldDisplayCaptions();
 }
 
 void CaptionUserPreferences::setUserPrefersCaptions(bool preference)
 {
-    Page* page = *(pageGroup()->pages().begin());
+    Page* page = currentPage();
     if (!page)
         return;
 
-    page->settings()->setShouldDisplayCaptions(preference);
+    page->settings().setShouldDisplayCaptions(preference);
     notify();
 }
 
 bool CaptionUserPreferences::userPrefersSubtitles() const
 {
-    Page* page = *(pageGroup()->pages().begin());
+    Page* page = currentPage();
     if (!page)
         return false;
 
-    return page->settings()->shouldDisplaySubtitles();
+    return page->settings().shouldDisplaySubtitles();
 }
 
 void CaptionUserPreferences::setUserPrefersSubtitles(bool preference)
 {
-    Page* page = *(pageGroup()->pages().begin());
+    Page* page = currentPage();
     if (!page)
         return;
 
-    page->settings()->setShouldDisplaySubtitles(preference);
+    page->settings().setShouldDisplaySubtitles(preference);
     notify();
 }
 
 bool CaptionUserPreferences::userPrefersTextDescriptions() const
 {
-    Page* page = *(pageGroup()->pages().begin());
+    Page* page = currentPage();
     if (!page)
         return false;
     
-    return page->settings()->shouldDisplayTextDescriptions();
+    return page->settings().shouldDisplayTextDescriptions();
 }
 
 void CaptionUserPreferences::setUserPrefersTextDescriptions(bool preference)
 {
-    Page* page = *(pageGroup()->pages().begin());
+    Page* page = currentPage();
     if (!page)
         return;
     
-    page->settings()->setShouldDisplayTextDescriptions(preference);
+    page->settings().setShouldDisplayTextDescriptions(preference);
     notify();
 }
 
 void CaptionUserPreferences::captionPreferencesChanged()
 {
-    m_pageGroup->captionPreferencesChanged();
+    m_pageGroup.captionPreferencesChanged();
 }
 
 Vector<String> CaptionUserPreferences::preferredLanguages() const
@@ -155,90 +182,147 @@ void CaptionUserPreferences::setPreferredLanguage(const String& language)
     notify();
 }
 
+void CaptionUserPreferences::setPreferredAudioCharacteristic(const String& characteristic)
+{
+    m_userPreferredAudioCharacteristic = characteristic;
+    notify();
+}
+
+Vector<String> CaptionUserPreferences::preferredAudioCharacteristics() const
+{
+    Vector<String> characteristics;
+    if (!m_userPreferredAudioCharacteristic.isEmpty())
+        characteristics.append(m_userPreferredAudioCharacteristic);
+    return characteristics;
+}
+
 static String trackDisplayName(TextTrack* track)
 {
-    if (track->label().isEmpty() && track->language().isEmpty())
+    if (track == TextTrack::captionMenuOffItem())
+        return textTrackOffMenuItemText();
+    if (track == TextTrack::captionMenuAutomaticItem())
+        return textTrackAutomaticMenuItemText();
+
+    if (track->label().isEmpty() && track->validBCP47Language().isEmpty())
         return textTrackNoLabelText();
     if (!track->label().isEmpty())
         return track->label();
-    return track->language();
+    return track->validBCP47Language();
 }
 
 String CaptionUserPreferences::displayNameForTrack(TextTrack* track) const
 {
     return trackDisplayName(track);
 }
+
+MediaSelectionOption CaptionUserPreferences::mediaSelectionOptionForTrack(TextTrack* track) const
+{
+    auto type = MediaSelectionOption::Type::Regular;
+    if (track == TextTrack::captionMenuOffItem())
+        type = MediaSelectionOption::Type::LegibleOff;
+    else if (track == TextTrack::captionMenuAutomaticItem())
+        type = MediaSelectionOption::Type::LegibleAuto;
+    return { displayNameForTrack(track), type };
+}
     
-static bool textTrackCompare(const RefPtr<TextTrack>& a, const RefPtr<TextTrack>& b)
+Vector<RefPtr<TextTrack>> CaptionUserPreferences::sortedTrackListForMenu(TextTrackList* trackList)
+{
+    ASSERT(trackList);
+
+    Vector<RefPtr<TextTrack>> tracksForMenu;
+
+    for (unsigned i = 0, length = trackList->length(); i < length; ++i) {
+        TextTrack* track = trackList->item(i);
+        auto kind = track->kind();
+        if (kind == TextTrack::Kind::Captions || kind == TextTrack::Kind::Descriptions || kind == TextTrack::Kind::Subtitles)
+            tracksForMenu.append(track);
+    }
+
+    std::sort(tracksForMenu.begin(), tracksForMenu.end(), [](auto& a, auto& b) {
+        return codePointCompare(trackDisplayName(a.get()), trackDisplayName(b.get())) < 0;
+    });
+
+    tracksForMenu.insert(0, TextTrack::captionMenuOffItem());
+    tracksForMenu.insert(1, TextTrack::captionMenuAutomaticItem());
+
+    return tracksForMenu;
+}
+
+static String trackDisplayName(AudioTrack* track)
 {
-    return codePointCompare(trackDisplayName(a.get()), trackDisplayName(b.get())) < 0;
+    if (track->label().isEmpty() && track->validBCP47Language().isEmpty())
+        return audioTrackNoLabelText();
+    if (!track->label().isEmpty())
+        return track->label();
+    return track->validBCP47Language();
 }
 
-Vector<RefPtr<TextTrack> > CaptionUserPreferences::sortedTrackListForMenu(TextTrackList* trackList)
+String CaptionUserPreferences::displayNameForTrack(AudioTrack* track) const
+{
+    return trackDisplayName(track);
+}
+
+MediaSelectionOption CaptionUserPreferences::mediaSelectionOptionForTrack(AudioTrack* track) const
+{
+    return { displayNameForTrack(track), MediaSelectionOption::Type::Regular };
+}
+
+Vector<RefPtr<AudioTrack>> CaptionUserPreferences::sortedTrackListForMenu(AudioTrackList* trackList)
 {
     ASSERT(trackList);
 
-    Vector<RefPtr<TextTrack> > tracksForMenu;
+    Vector<RefPtr<AudioTrack>> tracksForMenu;
 
-    for (unsigned i = 0, length = trackList->length(); i < length; ++i)
-        tracksForMenu.append(trackList->item(i));
+    for (unsigned i = 0, length = trackList->length(); i < length; ++i) {
+        AudioTrack* track = trackList->item(i);
+        tracksForMenu.append(track);
+    }
 
-    nonCopyingSort(tracksForMenu.begin(), tracksForMenu.end(), textTrackCompare);
+    std::sort(tracksForMenu.begin(), tracksForMenu.end(), [](auto& a, auto& b) {
+        return codePointCompare(trackDisplayName(a.get()), trackDisplayName(b.get())) < 0;
+    });
 
     return tracksForMenu;
 }
 
 int CaptionUserPreferences::textTrackSelectionScore(TextTrack* track, HTMLMediaElement*) const
 {
-    int trackScore = 0;
-
-    if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword())
-        return trackScore;
+    if (track->kind() != TextTrack::Kind::Captions && track->kind() != TextTrack::Kind::Subtitles)
+        return 0;
     
     if (!userPrefersSubtitles() && !userPrefersCaptions())
-        return trackScore;
-    
-    if (track->kind() == TextTrack::subtitlesKeyword() && userPrefersSubtitles())
-        trackScore = 1;
-    else if (track->kind() == TextTrack::captionsKeyword() && userPrefersCaptions())
-        trackScore = 1;
+        return 0;
     
-    return trackScore + textTrackLanguageSelectionScore(track, preferredLanguages());
+    return textTrackLanguageSelectionScore(track, preferredLanguages()) + 1;
 }
 
 int CaptionUserPreferences::textTrackLanguageSelectionScore(TextTrack* track, const Vector<String>& preferredLanguages) const
 {
-    if (track->language().isEmpty())
+    if (track->validBCP47Language().isEmpty())
         return 0;
 
-    size_t languageMatchIndex = indexOfBestMatchingLanguageInList(track->language(), preferredLanguages);
+    bool exactMatch;
+    size_t languageMatchIndex = indexOfBestMatchingLanguageInList(track->validBCP47Language(), preferredLanguages, exactMatch);
     if (languageMatchIndex >= preferredLanguages.size())
         return 0;
 
     // Matching a track language is more important than matching track type, so this multiplier must be
     // greater than the maximum value returned by textTrackSelectionScore.
-    return (preferredLanguages.size() - languageMatchIndex) * 10;
+    int bonus = exactMatch ? 1 : 0;
+    return (preferredLanguages.size() + bonus - languageMatchIndex) * 10;
 }
 
 void CaptionUserPreferences::setCaptionsStyleSheetOverride(const String& override)
 {
     m_captionsStyleSheetOverride = override;
-    updateCaptionStyleSheetOveride();
+    updateCaptionStyleSheetOverride();
 }
 
-void CaptionUserPreferences::updateCaptionStyleSheetOveride()
+void CaptionUserPreferences::updateCaptionStyleSheetOverride()
 {
-    // Identify our override style sheet with a unique URL - a new scheme and a UUID.
-    DEFINE_STATIC_LOCAL(KURL, captionsStyleSheetURL, (ParsedURLString, "user-captions-override:01F6AF12-C3B0-4F70-AF5E-A3E00234DC23"));
-
-    pageGroup()->removeUserStyleSheetFromWorld(mainThreadNormalWorld(), captionsStyleSheetURL);
-
     String captionsOverrideStyleSheet = captionsStyleSheetOverride();
-    if (captionsOverrideStyleSheet.isEmpty())
-        return;
-
-    pageGroup()->addUserStyleSheetToWorld(mainThreadNormalWorld(), captionsOverrideStyleSheet, captionsStyleSheetURL, Vector<String>(),
-        Vector<String>(), InjectInAllFrames, UserStyleAuthorLevel, InjectInExistingDocuments);
+    for (auto& page : m_pageGroup.pages())
+        page->setCaptionUserPreferencesStyleSheet(captionsOverrideStyleSheet);
 }
 
 String CaptionUserPreferences::primaryAudioTrackLanguageOverride() const