1/*
2 * Copyright (C) 2011, 2012 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 are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32
33#if ENABLE(INSPECTOR) && ENABLE(FILE_SYSTEM)
34
35#include "InspectorFileSystemAgent.h"
36
37#include "DOMFileSystem.h"
38#include "DOMImplementation.h"
39#include "DirectoryEntry.h"
40#include "DirectoryReader.h"
41#include "Document.h"
42#include "EntriesCallback.h"
43#include "EntryArray.h"
44#include "EntryCallback.h"
45#include "ErrorCallback.h"
46#include "Event.h"
47#include "File.h"
48#include "FileCallback.h"
49#include "FileEntry.h"
50#include "FileError.h"
51#include "FileReader.h"
52#include "FileSystemCallback.h"
53#include "FileSystemCallbacks.h"
54#include "Frame.h"
55#include "InspectorPageAgent.h"
56#include "InspectorState.h"
57#include "InstrumentingAgents.h"
58#include "KURL.h"
59#include "LocalFileSystem.h"
60#include "MIMETypeRegistry.h"
61#include "Metadata.h"
62#include "MetadataCallback.h"
63#include "ScriptExecutionContext.h"
64#include "SecurityOrigin.h"
65#include "TextEncoding.h"
66#include "TextResourceDecoder.h"
67#include "VoidCallback.h"
68#include <wtf/text/Base64.h>
69
70using WebCore::TypeBuilder::Array;
71
72typedef WebCore::InspectorBackendDispatcher::FileSystemCommandHandler::RequestFileSystemRootCallback RequestFileSystemRootCallback;
73typedef WebCore::InspectorBackendDispatcher::FileSystemCommandHandler::RequestDirectoryContentCallback RequestDirectoryContentCallback;
74typedef WebCore::InspectorBackendDispatcher::FileSystemCommandHandler::RequestMetadataCallback RequestMetadataCallback;
75typedef WebCore::InspectorBackendDispatcher::FileSystemCommandHandler::RequestFileContentCallback RequestFileContentCallback;
76typedef WebCore::InspectorBackendDispatcher::FileSystemCommandHandler::DeleteEntryCallback DeleteEntryCallback;
77
78namespace WebCore {
79
80namespace FileSystemAgentState {
81static const char fileSystemAgentEnabled[] = "fileSystemAgentEnabled";
82}
83
84namespace {
85
86template<typename BaseCallback, typename Handler, typename Argument>
87class CallbackDispatcher : public BaseCallback {
88public:
89    typedef bool (Handler::*HandlingMethod)(Argument*);
90
91    static PassRefPtr<CallbackDispatcher> create(PassRefPtr<Handler> handler, HandlingMethod handlingMethod)
92    {
93        return adoptRef(new CallbackDispatcher(handler, handlingMethod));
94    }
95
96    virtual bool handleEvent(Argument* argument) OVERRIDE
97    {
98        return (m_handler.get()->*m_handlingMethod)(argument);
99    }
100
101private:
102    CallbackDispatcher(PassRefPtr<Handler> handler, HandlingMethod handlingMethod)
103        : m_handler(handler)
104        , m_handlingMethod(handlingMethod) { }
105
106    RefPtr<Handler> m_handler;
107    HandlingMethod m_handlingMethod;
108};
109
110template<typename BaseCallback>
111class CallbackDispatcherFactory {
112public:
113    template<typename Handler, typename Argument>
114    static PassRefPtr<CallbackDispatcher<BaseCallback, Handler, Argument> > create(Handler* handler, bool (Handler::*handlingMethod)(Argument*))
115    {
116        return CallbackDispatcher<BaseCallback, Handler, Argument>::create(PassRefPtr<Handler>(handler), handlingMethod);
117    }
118};
119
120class FileSystemRootRequest : public RefCounted<FileSystemRootRequest> {
121    WTF_MAKE_NONCOPYABLE(FileSystemRootRequest);
122public:
123    static PassRefPtr<FileSystemRootRequest> create(PassRefPtr<RequestFileSystemRootCallback> requestCallback, const String& type)
124    {
125        return adoptRef(new FileSystemRootRequest(requestCallback, type));
126    }
127
128    void start(ScriptExecutionContext*);
129
130private:
131    bool didHitError(FileError* error)
132    {
133        reportResult(error->code());
134        return true;
135    }
136
137    bool didGetEntry(Entry*);
138
139    void reportResult(FileError::ErrorCode errorCode, PassRefPtr<TypeBuilder::FileSystem::Entry> entry = 0)
140    {
141        m_requestCallback->sendSuccess(static_cast<int>(errorCode), entry);
142    }
143
144    FileSystemRootRequest(PassRefPtr<RequestFileSystemRootCallback> requestCallback, const String& type)
145        : m_requestCallback(requestCallback)
146        , m_type(type) { }
147
148    RefPtr<RequestFileSystemRootCallback> m_requestCallback;
149    String m_type;
150};
151
152void FileSystemRootRequest::start(ScriptExecutionContext* scriptExecutionContext)
153{
154    ASSERT(scriptExecutionContext);
155
156    RefPtr<ErrorCallback> errorCallback = CallbackDispatcherFactory<ErrorCallback>::create(this, &FileSystemRootRequest::didHitError);
157    FileSystemType type;
158    if (m_type == DOMFileSystemBase::persistentPathPrefix)
159        type = FileSystemTypePersistent;
160    else if (m_type == DOMFileSystemBase::temporaryPathPrefix)
161        type = FileSystemTypeTemporary;
162    else {
163        errorCallback->handleEvent(FileError::create(FileError::SYNTAX_ERR).get());
164        return;
165    }
166
167    RefPtr<EntryCallback> successCallback = CallbackDispatcherFactory<EntryCallback>::create(this, &FileSystemRootRequest::didGetEntry);
168    OwnPtr<ResolveURICallbacks> fileSystemCallbacks = ResolveURICallbacks::create(successCallback, errorCallback, scriptExecutionContext, type, "/");
169
170    LocalFileSystem::localFileSystem().readFileSystem(scriptExecutionContext, type, fileSystemCallbacks.release());
171}
172
173bool FileSystemRootRequest::didGetEntry(Entry* entry)
174{
175    RefPtr<TypeBuilder::FileSystem::Entry> result = TypeBuilder::FileSystem::Entry::create()
176        .setUrl(entry->toURL())
177        .setName("/")
178        .setIsDirectory(true);
179    reportResult(static_cast<FileError::ErrorCode>(0), result);
180    return true;
181}
182
183class DirectoryContentRequest : public RefCounted<DirectoryContentRequest> {
184    WTF_MAKE_NONCOPYABLE(DirectoryContentRequest);
185public:
186    static PassRefPtr<DirectoryContentRequest> create(PassRefPtr<RequestDirectoryContentCallback> requestCallback, const String& url)
187    {
188        return adoptRef(new DirectoryContentRequest(requestCallback, url));
189    }
190
191    virtual ~DirectoryContentRequest()
192    {
193        reportResult(FileError::ABORT_ERR);
194    }
195
196    void start(ScriptExecutionContext*);
197
198private:
199    bool didHitError(FileError* error)
200    {
201        reportResult(error->code());
202        return true;
203    }
204
205    bool didGetEntry(Entry*);
206    bool didReadDirectoryEntries(EntryArray*);
207
208    void reportResult(FileError::ErrorCode errorCode, PassRefPtr<Array<TypeBuilder::FileSystem::Entry> > entries = 0)
209    {
210        m_requestCallback->sendSuccess(static_cast<int>(errorCode), entries);
211    }
212
213    DirectoryContentRequest(PassRefPtr<RequestDirectoryContentCallback> requestCallback, const String& url)
214        : m_requestCallback(requestCallback)
215        , m_url(ParsedURLString, url) { }
216
217    void readDirectoryEntries();
218
219    RefPtr<RequestDirectoryContentCallback> m_requestCallback;
220    KURL m_url;
221    RefPtr<Array<TypeBuilder::FileSystem::Entry> > m_entries;
222    RefPtr<DirectoryReader> m_directoryReader;
223};
224
225void DirectoryContentRequest::start(ScriptExecutionContext* scriptExecutionContext)
226{
227    ASSERT(scriptExecutionContext);
228
229    RefPtr<ErrorCallback> errorCallback = CallbackDispatcherFactory<ErrorCallback>::create(this, &DirectoryContentRequest::didHitError);
230    FileSystemType type;
231    String path;
232    if (!DOMFileSystemBase::crackFileSystemURL(m_url, type, path)) {
233        errorCallback->handleEvent(FileError::create(FileError::SYNTAX_ERR).get());
234        return;
235    }
236
237    RefPtr<EntryCallback> successCallback = CallbackDispatcherFactory<EntryCallback>::create(this, &DirectoryContentRequest::didGetEntry);
238    OwnPtr<ResolveURICallbacks> fileSystemCallbacks = ResolveURICallbacks::create(successCallback, errorCallback, scriptExecutionContext, type, path);
239
240    LocalFileSystem::localFileSystem().readFileSystem(scriptExecutionContext, type, fileSystemCallbacks.release());
241}
242
243bool DirectoryContentRequest::didGetEntry(Entry* entry)
244{
245    if (!entry->isDirectory()) {
246        reportResult(FileError::TYPE_MISMATCH_ERR);
247        return true;
248    }
249
250    m_directoryReader = static_cast<DirectoryEntry*>(entry)->createReader();
251    m_entries = Array<TypeBuilder::FileSystem::Entry>::create();
252    readDirectoryEntries();
253    return true;
254}
255
256void DirectoryContentRequest::readDirectoryEntries()
257{
258    if (!m_directoryReader->filesystem()->scriptExecutionContext()) {
259        reportResult(FileError::ABORT_ERR);
260        return;
261    }
262
263    RefPtr<EntriesCallback> successCallback = CallbackDispatcherFactory<EntriesCallback>::create(this, &DirectoryContentRequest::didReadDirectoryEntries);
264    RefPtr<ErrorCallback> errorCallback = CallbackDispatcherFactory<ErrorCallback>::create(this, &DirectoryContentRequest::didHitError);
265    m_directoryReader->readEntries(successCallback, errorCallback);
266}
267
268bool DirectoryContentRequest::didReadDirectoryEntries(EntryArray* entries)
269{
270    if (!entries->length()) {
271        reportResult(static_cast<FileError::ErrorCode>(0), m_entries);
272        return true;
273    }
274
275    for (unsigned i = 0; i < entries->length(); ++i) {
276        Entry* entry = entries->item(i);
277        RefPtr<TypeBuilder::FileSystem::Entry> entryForFrontend = TypeBuilder::FileSystem::Entry::create()
278            .setUrl(entry->toURL())
279            .setName(entry->name())
280            .setIsDirectory(entry->isDirectory());
281
282        using TypeBuilder::Page::ResourceType;
283        if (!entry->isDirectory()) {
284            String mimeType = MIMETypeRegistry::getMIMETypeForPath(entry->name());
285            ResourceType::Enum resourceType;
286            if (MIMETypeRegistry::isSupportedImageMIMEType(mimeType)) {
287                resourceType = ResourceType::Image;
288                entryForFrontend->setIsTextFile(false);
289            } else if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType)) {
290                resourceType = ResourceType::Script;
291                entryForFrontend->setIsTextFile(true);
292            } else if (MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType)) {
293                resourceType = ResourceType::Document;
294                entryForFrontend->setIsTextFile(true);
295            } else {
296                resourceType = ResourceType::Other;
297                entryForFrontend->setIsTextFile(DOMImplementation::isXMLMIMEType(mimeType) || DOMImplementation::isTextMIMEType(mimeType));
298            }
299
300            entryForFrontend->setMimeType(mimeType);
301            entryForFrontend->setResourceType(resourceType);
302        }
303
304        m_entries->addItem(entryForFrontend);
305    }
306    readDirectoryEntries();
307    return true;
308}
309
310class MetadataRequest : public RefCounted<MetadataRequest> {
311    WTF_MAKE_NONCOPYABLE(MetadataRequest);
312public:
313    static PassRefPtr<MetadataRequest> create(PassRefPtr<RequestMetadataCallback> requestCallback, const String& url)
314    {
315        return adoptRef(new MetadataRequest(requestCallback, url));
316    }
317
318    virtual ~MetadataRequest()
319    {
320        reportResult(FileError::ABORT_ERR);
321    }
322
323    void start(ScriptExecutionContext*);
324
325private:
326    bool didHitError(FileError* error)
327    {
328        reportResult(error->code());
329        return true;
330    }
331
332    bool didGetEntry(Entry*);
333    bool didGetMetadata(Metadata*);
334
335    void reportResult(FileError::ErrorCode errorCode, PassRefPtr<TypeBuilder::FileSystem::Metadata> metadata = 0)
336    {
337        m_requestCallback->sendSuccess(static_cast<int>(errorCode), metadata);
338    }
339
340    MetadataRequest(PassRefPtr<RequestMetadataCallback> requestCallback, const String& url)
341        : m_requestCallback(requestCallback)
342        , m_url(ParsedURLString, url) { }
343
344    RefPtr<RequestMetadataCallback> m_requestCallback;
345    KURL m_url;
346    String m_path;
347    bool m_isDirectory;
348};
349
350void MetadataRequest::start(ScriptExecutionContext* scriptExecutionContext)
351{
352    ASSERT(scriptExecutionContext);
353
354    RefPtr<ErrorCallback> errorCallback = CallbackDispatcherFactory<ErrorCallback>::create(this, &MetadataRequest::didHitError);
355
356    FileSystemType type;
357    if (!DOMFileSystemBase::crackFileSystemURL(m_url, type, m_path)) {
358        errorCallback->handleEvent(FileError::create(FileError::SYNTAX_ERR).get());
359        return;
360    }
361
362    RefPtr<EntryCallback> successCallback = CallbackDispatcherFactory<EntryCallback>::create(this, &MetadataRequest::didGetEntry);
363    OwnPtr<ResolveURICallbacks> fileSystemCallbacks = ResolveURICallbacks::create(successCallback, errorCallback, scriptExecutionContext, type, m_path);
364    LocalFileSystem::localFileSystem().readFileSystem(scriptExecutionContext, type, fileSystemCallbacks.release());
365}
366
367bool MetadataRequest::didGetEntry(Entry* entry)
368{
369    if (!entry->filesystem()->scriptExecutionContext()) {
370        reportResult(FileError::ABORT_ERR);
371        return true;
372    }
373
374    RefPtr<MetadataCallback> successCallback = CallbackDispatcherFactory<MetadataCallback>::create(this, &MetadataRequest::didGetMetadata);
375    RefPtr<ErrorCallback> errorCallback = CallbackDispatcherFactory<ErrorCallback>::create(this, &MetadataRequest::didHitError);
376    entry->getMetadata(successCallback, errorCallback);
377    m_isDirectory = entry->isDirectory();
378    return true;
379}
380
381bool MetadataRequest::didGetMetadata(Metadata* metadata)
382{
383    using TypeBuilder::FileSystem::Metadata;
384    RefPtr<Metadata> result = Metadata::create()
385        .setModificationTime(metadata->modificationTime())
386        .setSize(metadata->size());
387    reportResult(static_cast<FileError::ErrorCode>(0), result);
388    return true;
389}
390
391class FileContentRequest : public EventListener {
392    WTF_MAKE_NONCOPYABLE(FileContentRequest);
393public:
394    static PassRefPtr<FileContentRequest> create(PassRefPtr<RequestFileContentCallback> requestCallback, const String& url, bool readAsText, long long start, long long end, const String& charset)
395    {
396        return adoptRef(new FileContentRequest(requestCallback, url, readAsText, start, end, charset));
397    }
398
399    virtual ~FileContentRequest()
400    {
401        reportResult(FileError::ABORT_ERR);
402    }
403
404    void start(ScriptExecutionContext*);
405
406    virtual bool operator==(const EventListener& other) OVERRIDE
407    {
408        return this == &other;
409    }
410
411    virtual void handleEvent(ScriptExecutionContext*, Event* event) OVERRIDE
412    {
413        if (event->type() == eventNames().loadEvent)
414            didRead();
415        else if (event->type() == eventNames().errorEvent)
416            didHitError(m_reader->error().get());
417    }
418
419private:
420    bool didHitError(FileError* error)
421    {
422        reportResult(error->code());
423        return true;
424    }
425
426    bool didGetEntry(Entry*);
427    bool didGetFile(File*);
428    void didRead();
429
430    void reportResult(FileError::ErrorCode errorCode, const String* result = 0, const String* charset = 0)
431    {
432        m_requestCallback->sendSuccess(static_cast<int>(errorCode), result, charset);
433    }
434
435    FileContentRequest(PassRefPtr<RequestFileContentCallback> requestCallback, const String& url, bool readAsText, long long start, long long end, const String& charset)
436        : EventListener(EventListener::CPPEventListenerType)
437        , m_requestCallback(requestCallback)
438        , m_url(ParsedURLString, url)
439        , m_readAsText(readAsText)
440        , m_start(start)
441        , m_end(end)
442        , m_charset(charset) { }
443
444    RefPtr<RequestFileContentCallback> m_requestCallback;
445    KURL m_url;
446    bool m_readAsText;
447    int m_start;
448    long long m_end;
449    String m_mimeType;
450    String m_charset;
451
452    RefPtr<FileReader> m_reader;
453};
454
455void FileContentRequest::start(ScriptExecutionContext* scriptExecutionContext)
456{
457    ASSERT(scriptExecutionContext);
458
459    RefPtr<ErrorCallback> errorCallback = CallbackDispatcherFactory<ErrorCallback>::create(this, &FileContentRequest::didHitError);
460
461    FileSystemType type;
462    String path;
463    if (!DOMFileSystemBase::crackFileSystemURL(m_url, type, path)) {
464        errorCallback->handleEvent(FileError::create(FileError::SYNTAX_ERR).get());
465        return;
466    }
467
468    RefPtr<EntryCallback> successCallback = CallbackDispatcherFactory<EntryCallback>::create(this, &FileContentRequest::didGetEntry);
469    OwnPtr<ResolveURICallbacks> fileSystemCallbacks = ResolveURICallbacks::create(successCallback, errorCallback, scriptExecutionContext, type, path);
470
471    LocalFileSystem::localFileSystem().readFileSystem(scriptExecutionContext, type, fileSystemCallbacks.release());
472}
473
474bool FileContentRequest::didGetEntry(Entry* entry)
475{
476    if (entry->isDirectory()) {
477        reportResult(FileError::TYPE_MISMATCH_ERR);
478        return true;
479    }
480
481    if (!entry->filesystem()->scriptExecutionContext()) {
482        reportResult(FileError::ABORT_ERR);
483        return true;
484    }
485
486    RefPtr<FileCallback> successCallback = CallbackDispatcherFactory<FileCallback>::create(this, &FileContentRequest::didGetFile);
487    RefPtr<ErrorCallback> errorCallback = CallbackDispatcherFactory<ErrorCallback>::create(this, &FileContentRequest::didHitError);
488    static_cast<FileEntry*>(entry)->file(successCallback, errorCallback);
489
490    m_reader = FileReader::create(entry->filesystem()->scriptExecutionContext());
491    m_mimeType = MIMETypeRegistry::getMIMETypeForPath(entry->name());
492
493    return true;
494}
495
496bool FileContentRequest::didGetFile(File* file)
497{
498    RefPtr<Blob> blob = file->slice(m_start, m_end);
499    m_reader->setOnload(this);
500    m_reader->setOnerror(this);
501
502    m_reader->readAsArrayBuffer(blob.get(), IGNORE_EXCEPTION);
503    return true;
504}
505
506void FileContentRequest::didRead()
507{
508    RefPtr<ArrayBuffer> buffer = m_reader->arrayBufferResult();
509
510    if (!m_readAsText) {
511        String result = base64Encode(static_cast<char*>(buffer->data()), buffer->byteLength());
512        reportResult(static_cast<FileError::ErrorCode>(0), &result, 0);
513        return;
514    }
515
516    RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create(m_mimeType, m_charset, true);
517    String result = decoder->decode(static_cast<char*>(buffer->data()), buffer->byteLength());
518    result.append(decoder->flush());
519    m_charset = decoder->encoding().domName();
520    reportResult(static_cast<FileError::ErrorCode>(0), &result, &m_charset);
521}
522
523class DeleteEntryRequest : public VoidCallback {
524public:
525    static PassRefPtr<DeleteEntryRequest> create(PassRefPtr<DeleteEntryCallback> requestCallback, const KURL& url)
526    {
527        return adoptRef(new DeleteEntryRequest(requestCallback, url));
528    }
529
530    virtual ~DeleteEntryRequest()
531    {
532        reportResult(FileError::ABORT_ERR);
533    }
534
535    virtual bool handleEvent() OVERRIDE
536    {
537        return didDeleteEntry();
538    }
539
540    void start(ScriptExecutionContext*);
541
542private:
543    bool didHitError(FileError* error)
544    {
545        reportResult(error->code());
546        return true;
547    }
548
549    bool didGetEntry(Entry*);
550    bool didDeleteEntry();
551
552    void reportResult(FileError::ErrorCode errorCode)
553    {
554        m_requestCallback->sendSuccess(static_cast<int>(errorCode));
555    }
556
557    DeleteEntryRequest(PassRefPtr<DeleteEntryCallback> requestCallback, const KURL& url)
558        : m_requestCallback(requestCallback)
559        , m_url(url) { }
560
561    RefPtr<DeleteEntryCallback> m_requestCallback;
562    KURL m_url;
563};
564
565void DeleteEntryRequest::start(ScriptExecutionContext* scriptExecutionContext)
566{
567    ASSERT(scriptExecutionContext);
568
569    RefPtr<ErrorCallback> errorCallback = CallbackDispatcherFactory<ErrorCallback>::create(this, &DeleteEntryRequest::didHitError);
570
571    FileSystemType type;
572    String path;
573    if (!DOMFileSystemBase::crackFileSystemURL(m_url, type, path)) {
574        errorCallback->handleEvent(FileError::create(FileError::SYNTAX_ERR).get());
575        return;
576    }
577
578    if (path == "/") {
579        OwnPtr<AsyncFileSystemCallbacks> fileSystemCallbacks = VoidCallbacks::create(this, errorCallback);
580        LocalFileSystem::localFileSystem().deleteFileSystem(scriptExecutionContext, type, fileSystemCallbacks.release());
581    } else {
582        RefPtr<EntryCallback> successCallback = CallbackDispatcherFactory<EntryCallback>::create(this, &DeleteEntryRequest::didGetEntry);
583        OwnPtr<ResolveURICallbacks> fileSystemCallbacks = ResolveURICallbacks::create(successCallback, errorCallback, scriptExecutionContext, type, path);
584        LocalFileSystem::localFileSystem().readFileSystem(scriptExecutionContext, type, fileSystemCallbacks.release());
585    }
586}
587
588bool DeleteEntryRequest::didGetEntry(Entry* entry)
589{
590    RefPtr<ErrorCallback> errorCallback = CallbackDispatcherFactory<ErrorCallback>::create(this, &DeleteEntryRequest::didHitError);
591    if (entry->isDirectory()) {
592        DirectoryEntry* directoryEntry = static_cast<DirectoryEntry*>(entry);
593        directoryEntry->removeRecursively(this, errorCallback);
594    } else
595        entry->remove(this, errorCallback);
596    return true;
597}
598
599bool DeleteEntryRequest::didDeleteEntry()
600{
601    reportResult(static_cast<FileError::ErrorCode>(0));
602    return true;
603}
604
605} // anonymous namespace
606
607// static
608PassOwnPtr<InspectorFileSystemAgent> InspectorFileSystemAgent::create(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorCompositeState* state)
609{
610    return adoptPtr(new InspectorFileSystemAgent(instrumentingAgents, pageAgent, state));
611}
612
613InspectorFileSystemAgent::~InspectorFileSystemAgent()
614{
615    m_instrumentingAgents->setInspectorFileSystemAgent(0);
616}
617
618void InspectorFileSystemAgent::enable(ErrorString*)
619{
620    if (m_enabled)
621        return;
622    m_enabled = true;
623    m_state->setBoolean(FileSystemAgentState::fileSystemAgentEnabled, m_enabled);
624}
625
626void InspectorFileSystemAgent::disable(ErrorString*)
627{
628    if (!m_enabled)
629        return;
630    m_enabled = false;
631    m_state->setBoolean(FileSystemAgentState::fileSystemAgentEnabled, m_enabled);
632}
633
634void InspectorFileSystemAgent::requestFileSystemRoot(ErrorString* error, const String& origin, const String& type, PassRefPtr<RequestFileSystemRootCallback> requestCallback)
635{
636    if (!assertEnabled(error))
637        return;
638
639    ScriptExecutionContext* scriptExecutionContext = assertScriptExecutionContextForOrigin(error, SecurityOrigin::createFromString(origin).get());
640    if (!scriptExecutionContext)
641        return;
642
643    FileSystemRootRequest::create(requestCallback, type)->start(scriptExecutionContext);
644}
645
646void InspectorFileSystemAgent::requestDirectoryContent(ErrorString* error, const String& url, PassRefPtr<RequestDirectoryContentCallback> requestCallback)
647{
648    if (!assertEnabled(error))
649        return;
650
651    ScriptExecutionContext* scriptExecutionContext = assertScriptExecutionContextForOrigin(error, SecurityOrigin::createFromString(url).get());
652    if (!scriptExecutionContext)
653        return;
654
655    DirectoryContentRequest::create(requestCallback, url)->start(scriptExecutionContext);
656}
657
658void InspectorFileSystemAgent::requestMetadata(ErrorString* error, const String& url, PassRefPtr<RequestMetadataCallback> requestCallback)
659{
660    if (!assertEnabled(error))
661        return;
662
663    ScriptExecutionContext* scriptExecutionContext = assertScriptExecutionContextForOrigin(error, SecurityOrigin::createFromString(url).get());
664    if (!scriptExecutionContext)
665        return;
666
667    MetadataRequest::create(requestCallback, url)->start(scriptExecutionContext);
668}
669
670void InspectorFileSystemAgent::requestFileContent(ErrorString* error, const String& url, bool readAsText, const int* start, const int* end, const String* charset, PassRefPtr<RequestFileContentCallback> requestCallback)
671{
672    if (!assertEnabled(error))
673        return;
674
675    ScriptExecutionContext* scriptExecutionContext = assertScriptExecutionContextForOrigin(error, SecurityOrigin::createFromString(url).get());
676    if (!scriptExecutionContext)
677        return;
678
679    long long startPosition = start ? *start : 0;
680    long long endPosition = end ? *end : std::numeric_limits<long long>::max();
681    FileContentRequest::create(requestCallback, url, readAsText, startPosition, endPosition, charset ? *charset : "")->start(scriptExecutionContext);
682}
683
684void InspectorFileSystemAgent::deleteEntry(ErrorString* error, const String& urlString, PassRefPtr<DeleteEntryCallback> requestCallback)
685{
686    if (!assertEnabled(error))
687        return;
688
689    KURL url(ParsedURLString, urlString);
690
691    ScriptExecutionContext* scriptExecutionContext = assertScriptExecutionContextForOrigin(error, SecurityOrigin::create(url).get());
692    if (!scriptExecutionContext)
693        return;
694
695    DeleteEntryRequest::create(requestCallback, url)->start(scriptExecutionContext);
696}
697
698void InspectorFileSystemAgent::clearFrontend()
699{
700    m_enabled = false;
701    m_state->setBoolean(FileSystemAgentState::fileSystemAgentEnabled, m_enabled);
702}
703
704void InspectorFileSystemAgent::restore()
705{
706    m_enabled = m_state->getBoolean(FileSystemAgentState::fileSystemAgentEnabled);
707}
708
709InspectorFileSystemAgent::InspectorFileSystemAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorCompositeState* state)
710    : InspectorBaseAgent<InspectorFileSystemAgent>("FileSystem", instrumentingAgents, state)
711    , m_pageAgent(pageAgent)
712    , m_enabled(false)
713{
714    ASSERT(instrumentingAgents);
715    ASSERT(state);
716    ASSERT(m_pageAgent);
717    m_instrumentingAgents->setInspectorFileSystemAgent(this);
718}
719
720bool InspectorFileSystemAgent::assertEnabled(ErrorString* error)
721{
722    if (!m_enabled) {
723        *error = "FileSystem agent is not enabled.";
724        return false;
725    }
726    return true;
727}
728
729ScriptExecutionContext* InspectorFileSystemAgent::assertScriptExecutionContextForOrigin(ErrorString* error, SecurityOrigin* origin)
730{
731    for (Frame* frame = m_pageAgent->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
732        if (frame->document() && frame->document()->securityOrigin()->isSameSchemeHostPort(origin))
733            return frame->document();
734    }
735
736    *error = "No frame is available for the request";
737    return 0;
738}
739
740} // namespace WebCore
741
742#endif // ENABLE(INSPECTOR) && ENABLE(FILE_SYSTEM)
743