/* * 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 #import using namespace std; namespace WebCore { static void userCaptionPreferencesChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef, const void *, CFDictionaryRef) { static_cast(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 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(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 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(opacity * 255)); if (!color) return Color(); return backgroundColor; } Color CaptionUserPreferencesMac::captionsTextColor() const { RetainPtr 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(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(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 font(AdoptCF, wkCaptionAppearanceCopyFontForStyle(wkCaptionFontStyleDefault)); if (!font) return emptyString(); RetainPtr 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::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(), Vector(), InjectInAllFrames, UserStyleAuthorLevel, InjectInExistingDocuments); } } #endif // ENABLE(VIDEO_TRACK)