blob: 779a4bc041daf5c97957d33256ca55d9a6d4820c [file] [log] [blame]
/*
* Copyright (C) 2012 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#if ENABLE(VIDEO_TRACK)
#import "CaptionUserPreferencesMac.h"
#import "ColorMac.h"
#import "DOMWrapperWorld.h"
#import "FloatConversion.h"
#import "KURL.h"
#import "PageGroup.h"
#import "TextTrackCue.h"
#import "UserStyleSheetTypes.h"
#import "WebCoreSystemInterface.h"
#import <wtf/RetainPtr.h>
#import <wtf/text/StringBuilder.h>
using namespace std;
namespace WebCore {
static void userCaptionPreferencesChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef, const void *, CFDictionaryRef)
{
static_cast<CaptionUserPreferencesMac*>(observer)->captionPreferencesChanged();
}
CaptionUserPreferencesMac::CaptionUserPreferencesMac(PageGroup* group)
: CaptionUserPreferences(group)
, m_listeningForPreferenceChanges(false)
{
}
CaptionUserPreferencesMac::~CaptionUserPreferencesMac()
{
if (wkCaptionAppearanceGetSettingsChangedNotification())
CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), this, wkCaptionAppearanceGetSettingsChangedNotification(), NULL);
}
bool CaptionUserPreferencesMac::userHasCaptionPreferences() const
{
return wkCaptionAppearanceHasUserPreferences();
}
bool CaptionUserPreferencesMac::userPrefersCaptions() const
{
return wkCaptionAppearanceShowCaptionsWhenAvailable();
}
void CaptionUserPreferencesMac::registerForCaptionPreferencesChangedCallbacks(CaptionPreferencesChangedListener* listener)
{
ASSERT(!m_captionPreferenceChangeListeners.contains(listener));
if (!wkCaptionAppearanceGetSettingsChangedNotification())
return;
if (!m_listeningForPreferenceChanges) {
m_listeningForPreferenceChanges = true;
CFNotificationCenterAddObserver (CFNotificationCenterGetLocalCenter(), this, userCaptionPreferencesChangedNotificationCallback, wkCaptionAppearanceGetSettingsChangedNotification(), NULL, CFNotificationSuspensionBehaviorCoalesce);
updateCaptionStyleSheetOveride();
}
m_captionPreferenceChangeListeners.add(listener);
}
void CaptionUserPreferencesMac::unregisterForCaptionPreferencesChangedCallbacks(CaptionPreferencesChangedListener* listener)
{
if (wkCaptionAppearanceGetSettingsChangedNotification())
m_captionPreferenceChangeListeners.remove(listener);
}
Color CaptionUserPreferencesMac::captionsWindowColor() const
{
RetainPtr<CGColorRef> color(AdoptCF, wkCaptionAppearanceCopyWindowColor());
Color windowColor(color.get());
if (!windowColor.isValid())
windowColor = Color::transparent;
CGFloat opacity;
if (wkCaptionAppearanceGetWindowOpacity(&opacity))
return Color(windowColor.red(), windowColor.green(), windowColor.blue(), static_cast<int>(opacity * 255));
if (!color)
return Color();
return windowColor;
}
Color CaptionUserPreferencesMac::captionsBackgroundColor() const
{
// This default value must be the same as the one specified in mediaControls.css for -webkit-media-text-track-past-nodes
// and webkit-media-text-track-future-nodes.
DEFINE_STATIC_LOCAL(Color, defaultBackgroundColor, (Color(0, 0, 0, 0.8 * 255)));
RetainPtr<CGColorRef> color(AdoptCF, wkCaptionAppearanceCopyBackgroundColor());
Color backgroundColor(color.get());
if (!backgroundColor.isValid()) {
backgroundColor = defaultBackgroundColor;
}
CGFloat opacity;
if (wkCaptionAppearanceGetBackgroundOpacity(&opacity))
return Color(backgroundColor.red(), backgroundColor.green(), backgroundColor.blue(), static_cast<int>(opacity * 255));
if (!color)
return Color();
return backgroundColor;
}
Color CaptionUserPreferencesMac::captionsTextColor() const
{
RetainPtr<CGColorRef> color(AdoptCF, wkCaptionAppearanceCopyForegroundColor());
Color textColor(color.get());
if (!textColor.isValid()) {
// This default value must be the same as the one specified in mediaControls.css for -webkit-media-text-track-container.
textColor = Color::white;
}
CGFloat opacity;
if (wkCaptionAppearanceGetForegroundOpacity(&opacity))
return Color(textColor.red(), textColor.green(), textColor.blue(), static_cast<int>(opacity * 255));
if (!color)
return Color();
return textColor;
}
Color CaptionUserPreferencesMac::captionsEdgeColorForTextColor(const Color& textColor) const
{
int distanceFromWhite = differenceSquared(textColor, Color::white);
int distanceFromBlack = differenceSquared(textColor, Color::black);
if (distanceFromWhite < distanceFromBlack)
return textColor.dark();
return textColor.light();
}
String CaptionUserPreferencesMac::cssPropertyWithTextEdgeColor(CSSPropertyID id, const String& value, const Color& textColor) const
{
StringBuilder builder;
builder.append(getPropertyNameString(id));
builder.append(':');
builder.append(value);
builder.append(' ');
builder.append(captionsEdgeColorForTextColor(textColor).serialized());
builder.append(';');
return builder.toString();
}
String CaptionUserPreferencesMac::cssColorProperty(CSSPropertyID id, const Color& color) const
{
StringBuilder builder;
builder.append(getPropertyNameString(id));
builder.append(':');
builder.append(color.serialized());
builder.append(';');
return builder.toString();
}
String CaptionUserPreferencesMac::captionsTextEdgeStyle() const
{
DEFINE_STATIC_LOCAL(const String, edgeStyleRaised, (" -.05em -.05em 0 ", String::ConstructFromLiteral));
DEFINE_STATIC_LOCAL(const String, edgeStyleDepressed, (" .05em .05em 0 ", String::ConstructFromLiteral));
DEFINE_STATIC_LOCAL(const String, edgeStyleDropShadow, (" .075em .075em 0 ", String::ConstructFromLiteral));
DEFINE_STATIC_LOCAL(const String, edgeStyleUniform, (" .03em ", String::ConstructFromLiteral));
Color color = captionsTextColor();
if (!color.isValid())
color.setNamedColor("black");
color = captionsEdgeColorForTextColor(color);
wkCaptionTextEdgeStyle textEdgeStyle = static_cast<wkCaptionTextEdgeStyle>(wkCaptionAppearanceGetTextEdgeStyle());
switch (textEdgeStyle) {
case wkCaptionTextEdgeStyleUndefined:
case wkCaptionTextEdgeStyleNone:
return emptyString();
case wkCaptionTextEdgeStyleRaised:
return cssPropertyWithTextEdgeColor(CSSPropertyTextShadow, edgeStyleRaised, color);
case wkCaptionTextEdgeStyleDepressed:
return cssPropertyWithTextEdgeColor(CSSPropertyTextShadow, edgeStyleDepressed, color);
case wkCaptionTextEdgeStyleDropShadow:
return cssPropertyWithTextEdgeColor(CSSPropertyTextShadow, edgeStyleDropShadow, color);
case wkCaptionTextEdgeStyleUniform:
return cssPropertyWithTextEdgeColor(CSSPropertyWebkitTextStroke, edgeStyleUniform, color);
case wkCaptionTextEdgeStyleMax:
ASSERT_NOT_REACHED();
default:
ASSERT_NOT_REACHED();
break;
}
return emptyString();
}
String CaptionUserPreferencesMac::captionsDefaultFont() const
{
RetainPtr<CGFontRef> font(AdoptCF, wkCaptionAppearanceCopyFontForStyle(wkCaptionFontStyleDefault));
if (!font)
return emptyString();
RetainPtr<CFStringRef> name(AdoptCF, CGFontCopyPostScriptName(font.get()));
if (!name)
return emptyString();
StringBuilder builder;
builder.append(getPropertyNameString(CSSPropertyFontFamily));
builder.append(": \"");
builder.append(name.get());
builder.append("\";");
return builder.toString();
}
String CaptionUserPreferencesMac::captionsStyleSheetOverride() const
{
StringBuilder captionsOverrideStyleSheet;
Color color = captionsBackgroundColor();
if (color.isValid()) {
captionsOverrideStyleSheet.append(" video::");
captionsOverrideStyleSheet.append(TextTrackCue::pastNodesShadowPseudoId());
captionsOverrideStyleSheet.append('{');
captionsOverrideStyleSheet.append(cssColorProperty(CSSPropertyBackgroundColor, color));
captionsOverrideStyleSheet.append('}');
captionsOverrideStyleSheet.append(" video::");
captionsOverrideStyleSheet.append(TextTrackCue::futureNodesShadowPseudoId());
captionsOverrideStyleSheet.append('{');
captionsOverrideStyleSheet.append(cssColorProperty(CSSPropertyBackgroundColor, color));
captionsOverrideStyleSheet.append('}');
}
color = captionsWindowColor();
if (color.isValid()) {
captionsOverrideStyleSheet.append(" video::");
captionsOverrideStyleSheet.append(TextTrackCueBox::textTrackCueBoxShadowPseudoId());
captionsOverrideStyleSheet.append('{');
captionsOverrideStyleSheet.append(cssColorProperty(CSSPropertyBackgroundColor, color));
captionsOverrideStyleSheet.append('}');
}
color = captionsTextColor();
String edgeStyle = captionsTextEdgeStyle();
String fontName = captionsDefaultFont();
if (color.isValid() || !edgeStyle.isEmpty() || !fontName.isEmpty()) {
captionsOverrideStyleSheet.append(" video::");
captionsOverrideStyleSheet.append(TextTrackCueBox::textTrackCueBoxShadowPseudoId());
captionsOverrideStyleSheet.append('{');
if (color.isValid())
captionsOverrideStyleSheet.append(cssColorProperty(CSSPropertyColor, color));
if (!edgeStyle.isEmpty())
captionsOverrideStyleSheet.append(edgeStyle);
if (!fontName.isEmpty())
captionsOverrideStyleSheet.append(fontName);
captionsOverrideStyleSheet.append('}');
}
return captionsOverrideStyleSheet.toString();
}
float CaptionUserPreferencesMac::captionFontSizeScale() const
{
CGFloat characterScale = CaptionUserPreferences::captionFontSizeScale();
CGFloat scaleAdjustment;
if (!wkCaptionAppearanceGetRelativeCharacterSize(&scaleAdjustment))
return characterScale;
#if defined(__LP64__) && __LP64__
return narrowPrecisionToFloat(scaleAdjustment * characterScale);
#else
return scaleAdjustment * characterScale;
#endif
}
void CaptionUserPreferencesMac::captionPreferencesChanged()
{
if (m_captionPreferenceChangeListeners.isEmpty())
return;
updateCaptionStyleSheetOveride();
for (HashSet<CaptionPreferencesChangedListener*>::iterator i = m_captionPreferenceChangeListeners.begin(); i != m_captionPreferenceChangeListeners.end(); ++i)
(*i)->captionPreferencesChanged();
}
void CaptionUserPreferencesMac::updateCaptionStyleSheetOveride()
{
// 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);
if (!userHasCaptionPreferences())
return;
String captionsOverrideStyleSheet = captionsStyleSheetOverride();
if (captionsOverrideStyleSheet.isEmpty())
return;
pageGroup()->addUserStyleSheetToWorld(mainThreadNormalWorld(), captionsOverrideStyleSheet, captionsStyleSheetURL, Vector<String>(),
Vector<String>(), InjectInAllFrames, UserStyleAuthorLevel, InjectInExistingDocuments);
}
}
#endif // ENABLE(VIDEO_TRACK)