1/* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27 28#if ENABLE(VIDEO_TRACK) 29 30#include "TextTrackLoader.h" 31 32#include "CachedResourceLoader.h" 33#include "CachedResourceRequest.h" 34#include "CachedTextTrack.h" 35#include "CrossOriginAccessControl.h" 36#include "Document.h" 37#include "Logging.h" 38#include "ResourceBuffer.h" 39#include "ScriptCallStack.h" 40#include "SecurityOrigin.h" 41#include "WebVTTParser.h" 42 43namespace WebCore { 44 45TextTrackLoader::TextTrackLoader(TextTrackLoaderClient* client, ScriptExecutionContext* context) 46 : m_client(client) 47 , m_scriptExecutionContext(context) 48 , m_cueLoadTimer(this, &TextTrackLoader::cueLoadTimerFired) 49 , m_state(Idle) 50 , m_parseOffset(0) 51 , m_newCuesAvailable(false) 52{ 53} 54 55TextTrackLoader::~TextTrackLoader() 56{ 57 if (m_cachedCueData) 58 m_cachedCueData->removeClient(this); 59} 60 61void TextTrackLoader::cueLoadTimerFired(Timer<TextTrackLoader>* timer) 62{ 63 ASSERT_UNUSED(timer, timer == &m_cueLoadTimer); 64 65 if (m_newCuesAvailable) { 66 m_newCuesAvailable = false; 67 m_client->newCuesAvailable(this); 68 } 69 70 if (m_state >= Finished) 71 m_client->cueLoadingCompleted(this, m_state == Failed); 72} 73 74void TextTrackLoader::cancelLoad() 75{ 76 if (m_cachedCueData) { 77 m_cachedCueData->removeClient(this); 78 m_cachedCueData = 0; 79 } 80} 81 82void TextTrackLoader::processNewCueData(CachedResource* resource) 83{ 84 ASSERT(m_cachedCueData == resource); 85 86 if (m_state == Failed || !resource->resourceBuffer()) 87 return; 88 89 ResourceBuffer* buffer = resource->resourceBuffer(); 90 if (m_parseOffset == buffer->size()) 91 return; 92 93 if (!m_cueParser) 94 m_cueParser = WebVTTParser::create(this, m_scriptExecutionContext); 95 96 const char* data; 97 unsigned length; 98 99 while ((length = buffer->getSomeData(data, m_parseOffset))) { 100 m_cueParser->parseBytes(data, length); 101 m_parseOffset += length; 102 } 103} 104 105// FIXME: This is a very unusual pattern, no other CachedResourceClient does this. Refactor to use notifyFinished() instead. 106void TextTrackLoader::deprecatedDidReceiveCachedResource(CachedResource* resource) 107{ 108 ASSERT(m_cachedCueData == resource); 109 110 if (!resource->resourceBuffer()) 111 return; 112 113 processNewCueData(resource); 114} 115 116void TextTrackLoader::corsPolicyPreventedLoad() 117{ 118 DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Cross-origin text track load denied by Cross-Origin Resource Sharing policy."))); 119 Document* document = toDocument(m_scriptExecutionContext); 120 document->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, consoleMessage); 121 m_state = Failed; 122} 123 124void TextTrackLoader::notifyFinished(CachedResource* resource) 125{ 126 ASSERT(m_cachedCueData == resource); 127 128 Document* document = toDocument(m_scriptExecutionContext); 129 if (!m_crossOriginMode.isNull() 130 && !document->securityOrigin()->canRequest(resource->response().url()) 131 && !resource->passesAccessControlCheck(document->securityOrigin())) { 132 133 corsPolicyPreventedLoad(); 134 } 135 136 if (m_state != Failed) { 137 processNewCueData(resource); 138 if (m_state != Failed) 139 m_state = resource->errorOccurred() ? Failed : Finished; 140 } 141 142 if (!m_cueLoadTimer.isActive()) 143 m_cueLoadTimer.startOneShot(0); 144 145 cancelLoad(); 146} 147 148bool TextTrackLoader::load(const KURL& url, const String& crossOriginMode) 149{ 150 cancelLoad(); 151 152 if (!m_client->shouldLoadCues(this)) 153 return false; 154 155 ASSERT(m_scriptExecutionContext->isDocument()); 156 Document* document = toDocument(m_scriptExecutionContext); 157 CachedResourceRequest cueRequest(ResourceRequest(document->completeURL(url))); 158 159 if (!crossOriginMode.isNull()) { 160 m_crossOriginMode = crossOriginMode; 161 StoredCredentials allowCredentials = equalIgnoringCase(crossOriginMode, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials; 162 updateRequestForAccessControl(cueRequest.mutableResourceRequest(), document->securityOrigin(), allowCredentials); 163 } else { 164 // Cross-origin resources that are not suitably CORS-enabled may not load. 165 if (!document->securityOrigin()->canRequest(url)) { 166 corsPolicyPreventedLoad(); 167 return false; 168 } 169 } 170 171 CachedResourceLoader* cachedResourceLoader = document->cachedResourceLoader(); 172 m_cachedCueData = cachedResourceLoader->requestTextTrack(cueRequest); 173 if (m_cachedCueData) 174 m_cachedCueData->addClient(this); 175 176 m_client->cueLoadingStarted(this); 177 178 return true; 179} 180 181void TextTrackLoader::newCuesParsed() 182{ 183 if (m_cueLoadTimer.isActive()) 184 return; 185 186 m_newCuesAvailable = true; 187 m_cueLoadTimer.startOneShot(0); 188} 189 190#if ENABLE(WEBVTT_REGIONS) 191void TextTrackLoader::newRegionsParsed() 192{ 193 m_client->newRegionsAvailable(this); 194} 195#endif 196 197void TextTrackLoader::fileFailedToParse() 198{ 199 LOG(Media, "TextTrackLoader::fileFailedToParse"); 200 201 m_state = Failed; 202 203 if (!m_cueLoadTimer.isActive()) 204 m_cueLoadTimer.startOneShot(0); 205 206 cancelLoad(); 207} 208 209void TextTrackLoader::getNewCues(Vector<RefPtr<TextTrackCue> >& outputCues) 210{ 211 ASSERT(m_cueParser); 212 if (m_cueParser) 213 m_cueParser->getNewCues(outputCues); 214} 215 216#if ENABLE(WEBVTT_REGIONS) 217void TextTrackLoader::getNewRegions(Vector<RefPtr<TextTrackRegion> >& outputRegions) 218{ 219 ASSERT(m_cueParser); 220 if (m_cueParser) 221 m_cueParser->getNewRegions(outputRegions); 222} 223#endif 224} 225 226#endif 227