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