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