• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  * Copyright (C) 2009, 2011 Google Inc. All Rights Reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #include "config.h"
29 
30 #if ENABLE(WORKERS)
31 
32 #include "WorkerContext.h"
33 
34 #include "AbstractDatabase.h"
35 #include "ActiveDOMObject.h"
36 #include "Database.h"
37 #include "DatabaseCallback.h"
38 #include "DatabaseSync.h"
39 #include "DatabaseTracker.h"
40 #include "DOMTimer.h"
41 #include "DOMURL.h"
42 #include "DOMWindow.h"
43 #include "ErrorEvent.h"
44 #include "Event.h"
45 #include "EventException.h"
46 #include "InspectorInstrumentation.h"
47 #include "KURL.h"
48 #include "MessagePort.h"
49 #include "NotImplemented.h"
50 #include "ScriptCallStack.h"
51 #include "ScriptSourceCode.h"
52 #include "ScriptValue.h"
53 #include "SecurityOrigin.h"
54 #include "WorkerInspectorController.h"
55 #include "WorkerLocation.h"
56 #include "WorkerNavigator.h"
57 #include "WorkerObjectProxy.h"
58 #include "WorkerScriptLoader.h"
59 #include "WorkerThread.h"
60 #include "WorkerThreadableLoader.h"
61 #include "XMLHttpRequestException.h"
62 #include <wtf/RefPtr.h>
63 #include <wtf/UnusedParam.h>
64 
65 #if ENABLE(NOTIFICATIONS)
66 #include "NotificationCenter.h"
67 #endif
68 
69 #if ENABLE(FILE_SYSTEM)
70 #include "AsyncFileSystem.h"
71 #include "DirectoryEntrySync.h"
72 #include "DOMFileSystem.h"
73 #include "DOMFileSystemBase.h"
74 #include "DOMFileSystemSync.h"
75 #include "ErrorCallback.h"
76 #include "FileEntrySync.h"
77 #include "FileError.h"
78 #include "FileException.h"
79 #include "FileSystemCallback.h"
80 #include "FileSystemCallbacks.h"
81 #include "LocalFileSystem.h"
82 #include "SyncCallbackHelper.h"
83 #endif
84 
85 namespace WebCore {
86 
87 class CloseWorkerContextTask : public ScriptExecutionContext::Task {
88 public:
create()89     static PassOwnPtr<CloseWorkerContextTask> create()
90     {
91         return new CloseWorkerContextTask;
92     }
93 
performTask(ScriptExecutionContext * context)94     virtual void performTask(ScriptExecutionContext *context)
95     {
96         ASSERT(context->isWorkerContext());
97         WorkerContext* workerContext = static_cast<WorkerContext*>(context);
98         // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop().
99         workerContext->thread()->workerReportingProxy().workerContextClosed();
100     }
101 
isCleanupTask() const102     virtual bool isCleanupTask() const { return true; }
103 };
104 
WorkerContext(const KURL & url,const String & userAgent,WorkerThread * thread)105 WorkerContext::WorkerContext(const KURL& url, const String& userAgent, WorkerThread* thread)
106     : m_url(url)
107     , m_userAgent(userAgent)
108     , m_script(new WorkerScriptController(this))
109     , m_thread(thread)
110 #if ENABLE(INSPECTOR)
111     , m_workerInspectorController(new WorkerInspectorController(this))
112 #endif
113     , m_closing(false)
114 {
115     setSecurityOrigin(SecurityOrigin::create(url));
116 }
117 
~WorkerContext()118 WorkerContext::~WorkerContext()
119 {
120     ASSERT(currentThread() == thread()->threadID());
121 #if ENABLE(NOTIFICATIONS)
122     m_notifications.clear();
123 #endif
124 
125     // Make sure we have no observers.
126     notifyObserversOfStop();
127 
128     // Notify proxy that we are going away. This can free the WorkerThread object, so do not access it after this.
129     thread()->workerReportingProxy().workerContextDestroyed();
130 }
131 
scriptExecutionContext() const132 ScriptExecutionContext* WorkerContext::scriptExecutionContext() const
133 {
134     return const_cast<WorkerContext*>(this);
135 }
136 
virtualURL() const137 const KURL& WorkerContext::virtualURL() const
138 {
139     return m_url;
140 }
141 
virtualCompleteURL(const String & url) const142 KURL WorkerContext::virtualCompleteURL(const String& url) const
143 {
144     return completeURL(url);
145 }
146 
completeURL(const String & url) const147 KURL WorkerContext::completeURL(const String& url) const
148 {
149     // Always return a null URL when passed a null string.
150     // FIXME: Should we change the KURL constructor to have this behavior?
151     if (url.isNull())
152         return KURL();
153     // Always use UTF-8 in Workers.
154     return KURL(m_url, url);
155 }
156 
userAgent(const KURL &) const157 String WorkerContext::userAgent(const KURL&) const
158 {
159     return m_userAgent;
160 }
161 
location() const162 WorkerLocation* WorkerContext::location() const
163 {
164     if (!m_location)
165         m_location = WorkerLocation::create(m_url);
166     return m_location.get();
167 }
168 
close()169 void WorkerContext::close()
170 {
171     if (m_closing)
172         return;
173 
174     // Let current script run to completion but prevent future script evaluations.
175     // After m_closing is set, all the tasks in the queue continue to be fetched but only
176     // tasks with isCleanupTask()==true will be executed.
177     m_closing = true;
178     postTask(CloseWorkerContextTask::create());
179 }
180 
navigator() const181 WorkerNavigator* WorkerContext::navigator() const
182 {
183     if (!m_navigator)
184         m_navigator = WorkerNavigator::create(m_userAgent);
185     return m_navigator.get();
186 }
187 
hasPendingActivity() const188 bool WorkerContext::hasPendingActivity() const
189 {
190     ActiveDOMObjectsMap& activeObjects = activeDOMObjects();
191     ActiveDOMObjectsMap::const_iterator activeObjectsEnd = activeObjects.end();
192     for (ActiveDOMObjectsMap::const_iterator iter = activeObjects.begin(); iter != activeObjectsEnd; ++iter) {
193         if (iter->first->hasPendingActivity())
194             return true;
195     }
196 
197     // Keep the worker active as long as there is a MessagePort with pending activity or that is remotely entangled.
198     HashSet<MessagePort*>::const_iterator messagePortsEnd = messagePorts().end();
199     for (HashSet<MessagePort*>::const_iterator iter = messagePorts().begin(); iter != messagePortsEnd; ++iter) {
200         if ((*iter)->hasPendingActivity() || ((*iter)->isEntangled() && !(*iter)->locallyEntangledPort()))
201             return true;
202     }
203 
204     return false;
205 }
206 
postTask(PassOwnPtr<Task> task)207 void WorkerContext::postTask(PassOwnPtr<Task> task)
208 {
209     thread()->runLoop().postTask(task);
210 }
211 
setTimeout(PassOwnPtr<ScheduledAction> action,int timeout)212 int WorkerContext::setTimeout(PassOwnPtr<ScheduledAction> action, int timeout)
213 {
214     return DOMTimer::install(scriptExecutionContext(), action, timeout, true);
215 }
216 
clearTimeout(int timeoutId)217 void WorkerContext::clearTimeout(int timeoutId)
218 {
219     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
220 }
221 
setInterval(PassOwnPtr<ScheduledAction> action,int timeout)222 int WorkerContext::setInterval(PassOwnPtr<ScheduledAction> action, int timeout)
223 {
224     return DOMTimer::install(scriptExecutionContext(), action, timeout, false);
225 }
226 
clearInterval(int timeoutId)227 void WorkerContext::clearInterval(int timeoutId)
228 {
229     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
230 }
231 
importScripts(const Vector<String> & urls,ExceptionCode & ec)232 void WorkerContext::importScripts(const Vector<String>& urls, ExceptionCode& ec)
233 {
234     ec = 0;
235     Vector<String>::const_iterator urlsEnd = urls.end();
236     Vector<KURL> completedURLs;
237     for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) {
238         const KURL& url = scriptExecutionContext()->completeURL(*it);
239         if (!url.isValid()) {
240             ec = SYNTAX_ERR;
241             return;
242         }
243         completedURLs.append(url);
244     }
245     Vector<KURL>::const_iterator end = completedURLs.end();
246 
247     for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) {
248         WorkerScriptLoader scriptLoader(ResourceRequestBase::TargetIsScript);
249         scriptLoader.loadSynchronously(scriptExecutionContext(), *it, AllowCrossOriginRequests);
250 
251         // If the fetching attempt failed, throw a NETWORK_ERR exception and abort all these steps.
252         if (scriptLoader.failed()) {
253             ec = XMLHttpRequestException::NETWORK_ERR;
254             return;
255         }
256 
257        InspectorInstrumentation::scriptImported(scriptExecutionContext(), scriptLoader.identifier(), scriptLoader.script());
258 
259         ScriptValue exception;
260         m_script->evaluate(ScriptSourceCode(scriptLoader.script(), scriptLoader.responseURL()), &exception);
261         if (!exception.hasNoValue()) {
262             m_script->setException(exception);
263             return;
264         }
265     }
266 }
267 
errorEventTarget()268 EventTarget* WorkerContext::errorEventTarget()
269 {
270     return this;
271 }
272 
logExceptionToConsole(const String & errorMessage,int lineNumber,const String & sourceURL,PassRefPtr<ScriptCallStack>)273 void WorkerContext::logExceptionToConsole(const String& errorMessage, int lineNumber, const String& sourceURL, PassRefPtr<ScriptCallStack>)
274 {
275     thread()->workerReportingProxy().postExceptionToWorkerObject(errorMessage, lineNumber, sourceURL);
276 }
277 
addMessage(MessageSource source,MessageType type,MessageLevel level,const String & message,unsigned lineNumber,const String & sourceURL,PassRefPtr<ScriptCallStack>)278 void WorkerContext::addMessage(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL, PassRefPtr<ScriptCallStack>)
279 {
280     thread()->workerReportingProxy().postConsoleMessageToWorkerObject(source, type, level, message, lineNumber, sourceURL);
281 }
282 
283 #if ENABLE(NOTIFICATIONS)
webkitNotifications() const284 NotificationCenter* WorkerContext::webkitNotifications() const
285 {
286     if (!m_notifications)
287         m_notifications = NotificationCenter::create(scriptExecutionContext(), m_thread->getNotificationPresenter());
288     return m_notifications.get();
289 }
290 #endif
291 
292 #if ENABLE(DATABASE)
openDatabase(const String & name,const String & version,const String & displayName,unsigned long estimatedSize,PassRefPtr<DatabaseCallback> creationCallback,ExceptionCode & ec)293 PassRefPtr<Database> WorkerContext::openDatabase(const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec)
294 {
295     if (!securityOrigin()->canAccessDatabase() || !AbstractDatabase::isAvailable()) {
296         ec = SECURITY_ERR;
297         return 0;
298     }
299 
300     return Database::openDatabase(this, name, version, displayName, estimatedSize, creationCallback, ec);
301 }
302 
databaseExceededQuota(const String &)303 void WorkerContext::databaseExceededQuota(const String&)
304 {
305 #if !PLATFORM(CHROMIUM)
306     // FIXME: This needs a real implementation; this is a temporary solution for testing.
307     const unsigned long long defaultQuota = 5 * 1024 * 1024;
308     DatabaseTracker::tracker().setQuota(securityOrigin(), defaultQuota);
309 #endif
310 }
311 
openDatabaseSync(const String & name,const String & version,const String & displayName,unsigned long estimatedSize,PassRefPtr<DatabaseCallback> creationCallback,ExceptionCode & ec)312 PassRefPtr<DatabaseSync> WorkerContext::openDatabaseSync(const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec)
313 {
314     if (!securityOrigin()->canAccessDatabase() || !AbstractDatabase::isAvailable()) {
315         ec = SECURITY_ERR;
316         return 0;
317     }
318 
319     return DatabaseSync::openDatabaseSync(this, name, version, displayName, estimatedSize, creationCallback, ec);
320 }
321 #endif
322 
isContextThread() const323 bool WorkerContext::isContextThread() const
324 {
325     return currentThread() == thread()->threadID();
326 }
327 
isJSExecutionForbidden() const328 bool WorkerContext::isJSExecutionForbidden() const
329 {
330     return m_script->isExecutionForbidden();
331 }
332 
eventTargetData()333 EventTargetData* WorkerContext::eventTargetData()
334 {
335     return &m_eventTargetData;
336 }
337 
ensureEventTargetData()338 EventTargetData* WorkerContext::ensureEventTargetData()
339 {
340     return &m_eventTargetData;
341 }
342 
343 #if ENABLE(BLOB)
webkitURL() const344 DOMURL* WorkerContext::webkitURL() const
345 {
346     if (!m_domURL)
347         m_domURL = DOMURL::create(this->scriptExecutionContext());
348     return m_domURL.get();
349 }
350 #endif
351 
352 #if ENABLE(FILE_SYSTEM)
webkitRequestFileSystem(int type,long long size,PassRefPtr<FileSystemCallback> successCallback,PassRefPtr<ErrorCallback> errorCallback)353 void WorkerContext::webkitRequestFileSystem(int type, long long size, PassRefPtr<FileSystemCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback)
354 {
355     if (!AsyncFileSystem::isAvailable() || !securityOrigin()->canAccessFileSystem()) {
356         DOMFileSystem::scheduleCallback(this, errorCallback, FileError::create(FileError::SECURITY_ERR));
357         return;
358     }
359 
360     AsyncFileSystem::Type fileSystemType = static_cast<AsyncFileSystem::Type>(type);
361     if (fileSystemType != AsyncFileSystem::Temporary && fileSystemType != AsyncFileSystem::Persistent && fileSystemType != AsyncFileSystem::External) {
362         DOMFileSystem::scheduleCallback(this, errorCallback, FileError::create(FileError::INVALID_MODIFICATION_ERR));
363         return;
364     }
365 
366     LocalFileSystem::localFileSystem().requestFileSystem(this, fileSystemType, size, FileSystemCallbacks::create(successCallback, errorCallback, this), false);
367 }
368 
webkitRequestFileSystemSync(int type,long long size,ExceptionCode & ec)369 PassRefPtr<DOMFileSystemSync> WorkerContext::webkitRequestFileSystemSync(int type, long long size, ExceptionCode& ec)
370 {
371     ec = 0;
372     if (!AsyncFileSystem::isAvailable() || !securityOrigin()->canAccessFileSystem()) {
373         ec = FileException::SECURITY_ERR;
374         return 0;
375     }
376 
377     AsyncFileSystem::Type fileSystemType = static_cast<AsyncFileSystem::Type>(type);
378     if (fileSystemType != AsyncFileSystem::Temporary && fileSystemType != AsyncFileSystem::Persistent && fileSystemType != AsyncFileSystem::External) {
379         ec = FileException::INVALID_MODIFICATION_ERR;
380         return 0;
381     }
382 
383     FileSystemSyncCallbackHelper helper;
384     LocalFileSystem::localFileSystem().requestFileSystem(this, fileSystemType, size, FileSystemCallbacks::create(helper.successCallback(), helper.errorCallback(), this), true);
385     return helper.getResult(ec);
386 }
387 
webkitResolveLocalFileSystemURL(const String & url,PassRefPtr<EntryCallback> successCallback,PassRefPtr<ErrorCallback> errorCallback)388 void WorkerContext::webkitResolveLocalFileSystemURL(const String& url, PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback)
389 {
390     KURL completedURL = completeURL(url);
391     if (!AsyncFileSystem::isAvailable() || !securityOrigin()->canAccessFileSystem() || !securityOrigin()->canRequest(completedURL)) {
392         DOMFileSystem::scheduleCallback(this, errorCallback, FileError::create(FileError::SECURITY_ERR));
393         return;
394     }
395 
396     AsyncFileSystem::Type type;
397     String filePath;
398     if (!completedURL.isValid() || !DOMFileSystemBase::crackFileSystemURL(completedURL, type, filePath)) {
399         DOMFileSystem::scheduleCallback(this, errorCallback, FileError::create(FileError::ENCODING_ERR));
400         return;
401     }
402 
403     LocalFileSystem::localFileSystem().readFileSystem(this, type, ResolveURICallbacks::create(successCallback, errorCallback, this, filePath));
404 }
405 
webkitResolveLocalFileSystemSyncURL(const String & url,ExceptionCode & ec)406 PassRefPtr<EntrySync> WorkerContext::webkitResolveLocalFileSystemSyncURL(const String& url, ExceptionCode& ec)
407 {
408     ec = 0;
409     KURL completedURL = completeURL(url);
410     if (!AsyncFileSystem::isAvailable() || !securityOrigin()->canAccessFileSystem() || !securityOrigin()->canRequest(completedURL)) {
411         ec = FileException::SECURITY_ERR;
412         return 0;
413     }
414 
415     AsyncFileSystem::Type type;
416     String filePath;
417     if (!completedURL.isValid() || !DOMFileSystemBase::crackFileSystemURL(completedURL, type, filePath)) {
418         ec = FileException::ENCODING_ERR;
419         return 0;
420     }
421 
422     FileSystemSyncCallbackHelper readFileSystemHelper;
423     LocalFileSystem::localFileSystem().readFileSystem(this, type, FileSystemCallbacks::create(readFileSystemHelper.successCallback(), readFileSystemHelper.errorCallback(), this), true);
424     RefPtr<DOMFileSystemSync> fileSystem = readFileSystemHelper.getResult(ec);
425     if (!fileSystem)
426         return 0;
427 
428     RefPtr<EntrySync> entry = fileSystem->root()->getDirectory(filePath, 0, ec);
429     if (ec == FileException::TYPE_MISMATCH_ERR)
430         return fileSystem->root()->getFile(filePath, 0, ec);
431 
432     return entry.release();
433 }
434 
435 COMPILE_ASSERT(static_cast<int>(WorkerContext::TEMPORARY) == static_cast<int>(AsyncFileSystem::Temporary), enum_mismatch);
436 COMPILE_ASSERT(static_cast<int>(WorkerContext::PERSISTENT) == static_cast<int>(AsyncFileSystem::Persistent), enum_mismatch);
437 COMPILE_ASSERT(static_cast<int>(WorkerContext::EXTERNAL) == static_cast<int>(AsyncFileSystem::External), enum_mismatch);
438 #endif
439 
Observer(WorkerContext * context)440 WorkerContext::Observer::Observer(WorkerContext* context)
441     : m_context(context)
442 {
443     ASSERT(m_context && m_context->isContextThread());
444     m_context->registerObserver(this);
445 }
446 
~Observer()447 WorkerContext::Observer::~Observer()
448 {
449     if (!m_context)
450         return;
451     ASSERT(m_context->isContextThread());
452     m_context->unregisterObserver(this);
453 }
454 
stopObserving()455 void WorkerContext::Observer::stopObserving()
456 {
457     if (!m_context)
458         return;
459     ASSERT(m_context->isContextThread());
460     m_context->unregisterObserver(this);
461     m_context = 0;
462 }
463 
registerObserver(Observer * observer)464 void WorkerContext::registerObserver(Observer* observer)
465 {
466     ASSERT(observer);
467     m_workerObservers.add(observer);
468 }
469 
unregisterObserver(Observer * observer)470 void WorkerContext::unregisterObserver(Observer* observer)
471 {
472     ASSERT(observer);
473     m_workerObservers.remove(observer);
474 }
475 
notifyObserversOfStop()476 void WorkerContext::notifyObserversOfStop()
477 {
478     HashSet<Observer*>::iterator iter = m_workerObservers.begin();
479     while (iter != m_workerObservers.end()) {
480         WorkerContext::Observer* observer = *iter;
481         observer->stopObserving();
482         observer->notifyStop();
483         iter = m_workerObservers.begin();
484     }
485 }
486 
487 } // namespace WebCore
488 
489 #endif // ENABLE(WORKERS)
490