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