Web Inspector: Add requestMetadata command and metadataReceived event to FileSystem
authorkinuko@chromium.org <kinuko@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 26 Jun 2012 10:26:26 +0000 (10:26 +0000)
committerkinuko@chromium.org <kinuko@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 26 Jun 2012 10:26:26 +0000 (10:26 +0000)
https://bugs.webkit.org/show_bug.cgi?id=87856

Patch by Taiju Tsuiki <tzik@chromium.org> on 2012-06-26
Reviewed by Yury Semikhatsky.

Source/WebCore:

Test: http/tests/inspector/filesystem/get-metadata.html

* inspector/Inspector.json:
* inspector/InspectorFileSystemAgent.cpp:
(WebCore):
(WebCore::InspectorFileSystemAgent::requestFileSystemRoot):
(WebCore::InspectorFileSystemAgent::requestDirectoryContent):
(WebCore::InspectorFileSystemAgent::requestMetadata):
* inspector/InspectorFileSystemAgent.h:
(InspectorFileSystemAgent):
* inspector/front-end/FileSystemModel.js:
(WebInspector.FileSystemModel.prototype._directoryContentReceived):
(WebInspector.FileSystemModel.prototype.requestMetadata):
(WebInspector.FileSystemModel.Entry.prototype.get isDirectory):
(WebInspector.FileSystemModel.Entry.prototype.requestMetadata):
(WebInspector.FileSystemRequestManager):
(WebInspector.FileSystemRequestManager.prototype._directoryContentReceived):
(WebInspector.FileSystemRequestManager.prototype.requestMetadata.requestAccepted):
(WebInspector.FileSystemRequestManager.prototype.requestMetadata):
(WebInspector.FileSystemRequestManager.prototype._metadataReceived):
(WebInspector.FileSystemDispatcher.prototype.directoryContentReceived):
(WebInspector.FileSystemDispatcher.prototype.metadataReceived):

LayoutTests:

* http/tests/inspector/filesystem/filesystem-test.js:
(initialize_FileSystemTest.InspectorTest.writeFile):
(initialize_FileSystemTest.InspectorTest.dumpMetadataRequestResult):
(initialize_FileSystemTest):
(writeFile.didGetFileSystem):
(writeFile):
(writeFile.didGetWriter.writer.onwrite):
(writeFile.didGetWriter):
* http/tests/inspector/filesystem/get-metadata-expected.txt: Added.
* http/tests/inspector/filesystem/get-metadata.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@121251 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/http/tests/inspector/filesystem/filesystem-test.js
LayoutTests/http/tests/inspector/filesystem/get-metadata-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/inspector/filesystem/get-metadata.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/inspector/Inspector.json
Source/WebCore/inspector/InspectorFileSystemAgent.cpp
Source/WebCore/inspector/InspectorFileSystemAgent.h
Source/WebCore/inspector/front-end/FileSystemModel.js

index a71b447..f12c25b 100644 (file)
@@ -1,3 +1,21 @@
+2012-06-26  Taiju Tsuiki  <tzik@chromium.org>
+
+        Web Inspector: Add requestMetadata command and metadataReceived event to FileSystem
+        https://bugs.webkit.org/show_bug.cgi?id=87856
+
+        Reviewed by Yury Semikhatsky.
+
+        * http/tests/inspector/filesystem/filesystem-test.js:
+        (initialize_FileSystemTest.InspectorTest.writeFile):
+        (initialize_FileSystemTest.InspectorTest.dumpMetadataRequestResult):
+        (initialize_FileSystemTest):
+        (writeFile.didGetFileSystem):
+        (writeFile):
+        (writeFile.didGetWriter.writer.onwrite):
+        (writeFile.didGetWriter):
+        * http/tests/inspector/filesystem/get-metadata-expected.txt: Added.
+        * http/tests/inspector/filesystem/get-metadata.html: Added.
+
 2012-06-26  Philip Rogers  <pdr@google.com>
 
         Fix setCurrentTime for paused animations
index e8a3e2e..6db2ded 100644 (file)
@@ -29,6 +29,11 @@ var initialize_FileSystemTest = function()
         InspectorTest.evaluateInPage("createFile(unescape(\"" + escape(path) + "\"), " + InspectorTest.registerCallback(callback) + ")");
     };
 
+    InspectorTest.writeFile = function(path, content, callback)
+    {
+        InspectorTest.evaluateInPage("writeFile(unescape(\"" + escape(path) + "\"), unescape(\"" + escape(content) + "\"), " + InspectorTest.registerCallback(callback) + ")");
+    };
+
     InspectorTest.clearFileSystem = function(callback)
     {
         InspectorTest.evaluateInPage("clearFileSystem(" + InspectorTest.registerCallback(callback) + ")");
@@ -50,6 +55,18 @@ var initialize_FileSystemTest = function()
             InspectorTest.addResult("    " + j + ": " + entries[i][j]);
         }
     };
+
+    InspectorTest.dumpMetadataRequestResult = function(errorCode, metadata)
+    {
+        InspectorTest.addResult("errorCode: " + errorCode);
+        if (metadata) {
+            InspectorTest.addResult("metadata:");
+            InspectorTest.addResult("  modificationTime: " + ("modificationTime" in metadata ? "(exists)" : "(null)"));
+            InspectorTest.addResult("  size: " + ("size" in metadata ? metadata.size : "(null)"));
+        } else {
+            InspectorTest.addResult("metadata: (null)");
+        }
+    };
 }
 
 function dispatchCallback()
@@ -60,9 +77,9 @@ function dispatchCallback()
 
 function createDirectory(path, callback)
 {
-    webkitRequestFileSystem(TEMPORARY, 1, gotFileSystem);
+    webkitRequestFileSystem(TEMPORARY, 1, didGetFileSystem);
 
-    function gotFileSystem(fileSystem)
+    function didGetFileSystem(fileSystem)
     {
         fileSystem.root.getDirectory(path, {create:true}, function(entry) {
             callback();
@@ -72,9 +89,9 @@ function createDirectory(path, callback)
 
 function createFile(path, callback)
 {
-    webkitRequestFileSystem(TEMPORARY, 1, gotFileSystem);
+    webkitRequestFileSystem(TEMPORARY, 1, didGetFileSystem);
 
-    function gotFileSystem(fileSystem)
+    function didGetFileSystem(fileSystem)
     {
         fileSystem.root.getFile(path, {create:true}, function(entry) {
             callback();
@@ -82,11 +99,35 @@ function createFile(path, callback)
     }
 }
 
+function writeFile(path, content, callback)
+{
+    webkitRequestFileSystem(TEMPORARY, 1, didGetFileSystem);
+
+    function didGetFileSystem(fileSystem)
+    {
+        fileSystem.root.getFile(path, {create:true}, didGetFileEntry);
+    }
+
+    function didGetFileEntry(fileEntry)
+    {
+        fileEntry.createWriter(didGetWriter);
+    }
+
+    function didGetWriter(writer)
+    {
+        writer.write(new Blob([content]));
+        writer.onwrite = function() {
+            if (writer.readyState === writer.DONE)
+                callback();
+        };
+    }
+}
+
 function clearFileSystem(callback)
 {
-    webkitResolveLocalFileSystemURL("filesystem:" + location.origin + "/temporary/", gotRoot, onError);
+    webkitResolveLocalFileSystemURL("filesystem:" + location.origin + "/temporary/", didGetRoot, onError);
 
-    function gotRoot(root)
+    function didGetRoot(root)
     {
         var reader = root.createReader();
         reader.readEntries(didReadEntries);
diff --git a/LayoutTests/http/tests/inspector/filesystem/get-metadata-expected.txt b/LayoutTests/http/tests/inspector/filesystem/get-metadata-expected.txt
new file mode 100644 (file)
index 0000000..4818281
--- /dev/null
@@ -0,0 +1,11 @@
+Tests getMetadata.
+
+errorCode: 0
+metadata:
+  modificationTime: (exists)
+  size: 4
+errorCode: 0
+metadata:
+  modificationTime: (exists)
+  size: (null)
+
diff --git a/LayoutTests/http/tests/inspector/filesystem/get-metadata.html b/LayoutTests/http/tests/inspector/filesystem/get-metadata.html
new file mode 100644 (file)
index 0000000..62a66e9
--- /dev/null
@@ -0,0 +1,51 @@
+<!DOCTYPE html><meta charset="UTF-8">
+<script src="../inspector-test.js"></script>
+<script src="filesystem-test.js"></script>
+<script>
+document.addEventListener("DOMContentLoaded", runTest);
+function test()
+{
+    var fileSystemRequestManager = new WebInspector.FileSystemRequestManager();
+
+    InspectorTest.clearFileSystem(step1);
+
+    function step1()
+    {
+        InspectorTest.writeFile("/hoge", "fuga", step2);
+    }
+
+    function step2()
+    {
+        fileSystemRequestManager.requestMetadata("filesystem:http://127.0.0.1:8000/temporary/hoge", step3);
+    }
+
+    function step3(errorCode, metadata)
+    {
+        InspectorTest.dumpMetadataRequestResult(errorCode, metadata);
+        step4();
+    }
+
+    function step4()
+    {
+        InspectorTest.createDirectory("/piyo", step5);
+    }
+
+    function step5()
+    {
+        fileSystemRequestManager.requestMetadata("filesystem:http://127.0.0.1:8000/temporary/piyo", step6);
+    }
+
+    function step6(errorCode, metadata)
+    {
+        InspectorTest.dumpMetadataRequestResult(errorCode, metadata);
+        InspectorTest.clearFileSystem(step7);
+    }
+
+    function step7(errorCode, fileSystem)
+    {
+        InspectorTest.completeTest();
+    }
+}
+
+</script>
+<p>Tests getMetadata.</p>
index b8edc80..47a8d48 100755 (executable)
@@ -1,3 +1,33 @@
+2012-06-26  Taiju Tsuiki  <tzik@chromium.org>
+
+        Web Inspector: Add requestMetadata command and metadataReceived event to FileSystem
+        https://bugs.webkit.org/show_bug.cgi?id=87856
+
+        Reviewed by Yury Semikhatsky.
+
+        Test: http/tests/inspector/filesystem/get-metadata.html
+
+        * inspector/Inspector.json:
+        * inspector/InspectorFileSystemAgent.cpp:
+        (WebCore):
+        (WebCore::InspectorFileSystemAgent::requestFileSystemRoot):
+        (WebCore::InspectorFileSystemAgent::requestDirectoryContent):
+        (WebCore::InspectorFileSystemAgent::requestMetadata):
+        * inspector/InspectorFileSystemAgent.h:
+        (InspectorFileSystemAgent):
+        * inspector/front-end/FileSystemModel.js:
+        (WebInspector.FileSystemModel.prototype._directoryContentReceived):
+        (WebInspector.FileSystemModel.prototype.requestMetadata):
+        (WebInspector.FileSystemModel.Entry.prototype.get isDirectory):
+        (WebInspector.FileSystemModel.Entry.prototype.requestMetadata):
+        (WebInspector.FileSystemRequestManager):
+        (WebInspector.FileSystemRequestManager.prototype._directoryContentReceived):
+        (WebInspector.FileSystemRequestManager.prototype.requestMetadata.requestAccepted):
+        (WebInspector.FileSystemRequestManager.prototype.requestMetadata):
+        (WebInspector.FileSystemRequestManager.prototype._metadataReceived):
+        (WebInspector.FileSystemDispatcher.prototype.directoryContentReceived):
+        (WebInspector.FileSystemDispatcher.prototype.metadataReceived):
+
 2012-06-25  Jocelyn Turcotte  <turcotte.j@gmail.com>
 
         [Qt] GraphicsSurface: Fix tile update artifacts on Mac
index 880633e..0f64483 100644 (file)
                     { "name": "resourceType", "$ref": "Page.ResourceType", "optional": true, "description": "ResourceType of the entry, available for a file only." }
                 ],
                 "description": "Represents a browser side file or directory."
+            },
+            {
+                "id": "Metadata",
+                "type": "object",
+                "properties": [
+                    { "name": "modificationTime", "type": "number", "description": "Modification time." },
+                    { "name": "size", "type": "number", "optional": true, "description": "File size. This field is available only for a file." }
+                ],
+                "description": "Represents metadata of a file or entry."
             }
         ],
         "commands": [
                     { "name": "requestId", "$ref": "RequestId", "description": "Request identifier. Corresponding directoryContentReceived event should have same requestId with this." }
                 ],
                 "description": "Returns content of the directory as directoryContentReceived event."
+            },
+            {
+                "name": "requestMetadata",
+                "parameters": [
+                    { "name": "url", "type": "string", "description": "URL of the entry that the frontend is requesting to get metadata from." }
+                ],
+                "returns": [
+                    { "name": "requestId", "$ref": "RequestId", "description": "Request identifier. Corresponding metadataReceived event should have same requestId with this." }
+                ],
+                "description": "Returns metadata of the entry as metadataReceived event."
             }
         ],
         "events": [
                     { "name": "entries", "type": "array", "items": { "$ref": "FileSystem.Entry" }, "optional": true, "description": "Contains all entries on directory if the command completed successfully." }
                 ],
                 "description": "Completion event of requestDirectoryContent command."
+            },
+            {
+                "name": "metadataReceived",
+                "parameters": [
+                    { "name": "requestId", "type": "integer", "description": "Request Identifier that was returned in response to the corresponding getMetadata request." },
+                    { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise, errorCode is set to FileError::ErrorCode value." },
+                    { "name": "metadata", "$ref": "FileSystem.Metadata", "optional": true, "description": "Contains metadata of the entry if the command completed successfully." }
+                ],
+                "description": "Completion event of getMetadata command."
             }
         ]
     },
index 72a2788..06206de 100644 (file)
@@ -52,6 +52,8 @@
 #include "KURL.h"
 #include "LocalFileSystem.h"
 #include "MIMETypeRegistry.h"
+#include "Metadata.h"
+#include "MetadataCallback.h"
 #include "SecurityOrigin.h"
 
 using WebCore::TypeBuilder::Array;
@@ -142,7 +144,7 @@ public:
 
 private:
     bool didHitError(FileError*);
-    bool gotEntry(Entry*);
+    bool didGetEntry(Entry*);
 
     void reportResult(FileError::ErrorCode errorCode, PassRefPtr<TypeBuilder::FileSystem::Entry> entry)
     {
@@ -180,14 +182,14 @@ void GetFileSystemRootTask::start(ScriptExecutionContext* scriptExecutionContext
         return;
     }
 
-    RefPtr<EntryCallback> successCallback = CallbackDispatcherFactory<EntryCallback>::create(this, &GetFileSystemRootTask::gotEntry);
+    RefPtr<EntryCallback> successCallback = CallbackDispatcherFactory<EntryCallback>::create(this, &GetFileSystemRootTask::didGetEntry);
     RefPtr<ErrorCallback> errorCallback = CallbackDispatcherFactory<ErrorCallback>::create(this, &GetFileSystemRootTask::didHitError);
     OwnPtr<ResolveURICallbacks> fileSystemCallbacks = ResolveURICallbacks::create(successCallback, errorCallback, scriptExecutionContext, type, "/");
 
     LocalFileSystem::localFileSystem().readFileSystem(scriptExecutionContext, type, fileSystemCallbacks.release());
 }
 
-bool GetFileSystemRootTask::gotEntry(Entry* entry)
+bool GetFileSystemRootTask::didGetEntry(Entry* entry)
 {
     RefPtr<TypeBuilder::FileSystem::Entry> result(TypeBuilder::FileSystem::Entry::create().setUrl(entry->toURL()).setName("/").setIsDirectory(true));
     reportResult(static_cast<FileError::ErrorCode>(0), result);
@@ -216,7 +218,7 @@ private:
         return true;
     }
 
-    bool gotEntry(Entry*);
+    bool didGetEntry(Entry*);
     bool didReadDirectoryEntries(EntryArray*);
 
     void reportResult(FileError::ErrorCode errorCode, PassRefPtr<Array<TypeBuilder::FileSystem::Entry> > entries)
@@ -252,14 +254,14 @@ void ReadDirectoryTask::start(ScriptExecutionContext* scriptExecutionContext)
         return;
     }
 
-    RefPtr<EntryCallback> successCallback = CallbackDispatcherFactory<EntryCallback>::create(this, &ReadDirectoryTask::gotEntry);
+    RefPtr<EntryCallback> successCallback = CallbackDispatcherFactory<EntryCallback>::create(this, &ReadDirectoryTask::didGetEntry);
     RefPtr<ErrorCallback> errorCallback = CallbackDispatcherFactory<ErrorCallback>::create(this, &ReadDirectoryTask::didHitError);
     OwnPtr<ResolveURICallbacks> fileSystemCallbacks = ResolveURICallbacks::create(successCallback, errorCallback, scriptExecutionContext, type, path);
 
     LocalFileSystem::localFileSystem().readFileSystem(scriptExecutionContext, type, fileSystemCallbacks.release());
 }
 
-bool ReadDirectoryTask::gotEntry(Entry* entry)
+bool ReadDirectoryTask::didGetEntry(Entry* entry)
 {
     if (!entry->isDirectory()) {
         reportResult(FileError::TYPE_MISMATCH_ERR, 0);
@@ -318,6 +320,86 @@ bool ReadDirectoryTask::didReadDirectoryEntries(EntryArray* entries)
     return true;
 }
 
+class GetMetadataTask : public RefCounted<GetMetadataTask> {
+    WTF_MAKE_NONCOPYABLE(GetMetadataTask);
+public:
+    static PassRefPtr<GetMetadataTask> create(PassRefPtr<FrontendProvider> frontendProvider, int requestId, const String& url)
+    {
+        return adoptRef(new GetMetadataTask(frontendProvider, requestId, url));
+    }
+
+    virtual ~GetMetadataTask()
+    {
+        reportResult(FileError::ABORT_ERR, 0);
+    }
+
+    bool didHitError(FileError* error)
+    {
+        reportResult(error->code(), 0);
+        return true;
+    }
+
+    void start(ScriptExecutionContext*);
+    bool didGetEntry(Entry*);
+    bool didGetMetadata(Metadata*);
+
+    void reportResult(FileError::ErrorCode errorCode, PassRefPtr<TypeBuilder::FileSystem::Metadata> metadata)
+    {
+        if (!m_frontendProvider || !m_frontendProvider->frontend())
+            return;
+        m_frontendProvider->frontend()->metadataReceived(m_requestId, static_cast<int>(errorCode), metadata);
+        m_frontendProvider = 0;
+    }
+
+private:
+    GetMetadataTask(PassRefPtr<FrontendProvider> frontendProvider, int requestId, const String& url)
+        : m_frontendProvider(frontendProvider)
+        , m_requestId(requestId)
+        , m_url(ParsedURLString, url) { }
+
+    RefPtr<FrontendProvider> m_frontendProvider;
+    int m_requestId;
+    KURL m_url;
+    String m_path;
+    bool m_isDirectory;
+};
+
+void GetMetadataTask::start(ScriptExecutionContext* scriptExecutionContext)
+{
+    FileSystemType type;
+    DOMFileSystemBase::crackFileSystemURL(m_url, type, m_path);
+
+    RefPtr<EntryCallback> successCallback = CallbackDispatcherFactory<EntryCallback>::create(this, &GetMetadataTask::didGetEntry);
+    RefPtr<ErrorCallback> errorCallback = CallbackDispatcherFactory<ErrorCallback>::create(this, &GetMetadataTask::didHitError);
+
+    OwnPtr<ResolveURICallbacks> fileSystemCallbacks = ResolveURICallbacks::create(successCallback, errorCallback, scriptExecutionContext, type, m_path);
+    LocalFileSystem::localFileSystem().readFileSystem(scriptExecutionContext, type, fileSystemCallbacks.release());
+}
+
+bool GetMetadataTask::didGetEntry(Entry* entry)
+{
+    if (!entry->filesystem()->scriptExecutionContext()) {
+        reportResult(FileError::ABORT_ERR, 0);
+        return true;
+    }
+
+    RefPtr<MetadataCallback> successCallback = CallbackDispatcherFactory<MetadataCallback>::create(this, &GetMetadataTask::didGetMetadata);
+    RefPtr<ErrorCallback> errorCallback = CallbackDispatcherFactory<ErrorCallback>::create(this, &GetMetadataTask::didHitError);
+    entry->getMetadata(successCallback, errorCallback);
+    m_isDirectory = entry->isDirectory();
+    return true;
+}
+
+bool GetMetadataTask::didGetMetadata(Metadata* metadata)
+{
+    using TypeBuilder::FileSystem::Metadata;
+    RefPtr<Metadata> result = Metadata::create().setModificationTime(metadata->modificationTime());
+    if (!m_isDirectory)
+        result->setSize(metadata->size());
+    reportResult(static_cast<FileError::ErrorCode>(0), result);
+    return true;
+}
+
 }
 
 // static
@@ -351,8 +433,10 @@ void InspectorFileSystemAgent::disable(ErrorString*)
 
 void InspectorFileSystemAgent::requestFileSystemRoot(ErrorString* error, const String& origin, const String& type, int* requestId)
 {
-    if (!m_enabled || !m_frontendProvider)
+    if (!m_enabled || !m_frontendProvider) {
+        *error = "FileSystem agent is not enabled";
         return;
+    }
     ASSERT(m_frontendProvider->frontend());
 
     *requestId = m_nextRequestId++;
@@ -362,10 +446,12 @@ void InspectorFileSystemAgent::requestFileSystemRoot(ErrorString* error, const S
         m_frontendProvider->frontend()->fileSystemRootReceived(*requestId, static_cast<int>(FileError::ABORT_ERR), 0);
 }
 
-void InspectorFileSystemAgent::requestDirectoryContent(ErrorString*, const String& url, int* requestId)
+void InspectorFileSystemAgent::requestDirectoryContent(ErrorString* error, const String& url, int* requestId)
 {
-    if (!m_enabled || !m_frontendProvider)
+    if (!m_enabled || !m_frontendProvider) {
+        *error = "FileSystem agent is not enabled";
         return;
+    }
     ASSERT(m_frontendProvider->frontend());
 
     *requestId = m_nextRequestId++;
@@ -376,6 +462,22 @@ void InspectorFileSystemAgent::requestDirectoryContent(ErrorString*, const Strin
         m_frontendProvider->frontend()->directoryContentReceived(*requestId, static_cast<int>(FileError::ABORT_ERR), 0);
 }
 
+void InspectorFileSystemAgent::requestMetadata(ErrorString* error, const String& url, int* requestId)
+{
+    if (!m_enabled || !m_frontendProvider) {
+        *error = "FileSystem agent is not enabled";
+        return;
+    }
+    ASSERT(m_frontendProvider->frontend());
+
+    *requestId = m_nextRequestId++;
+
+    if (ScriptExecutionContext* scriptExecutionContext = scriptExecutionContextForOrigin(SecurityOrigin::createFromString(url).get()))
+        GetMetadataTask::create(m_frontendProvider, *requestId, url)->start(scriptExecutionContext);
+    else
+        m_frontendProvider->frontend()->metadataReceived(*requestId, static_cast<int>(FileError::ABORT_ERR), 0);
+}
+
 void InspectorFileSystemAgent::setFrontend(InspectorFrontend* frontend)
 {
     ASSERT(frontend);
index 59a16e9..82f95c4 100644 (file)
@@ -61,6 +61,7 @@ public:
 
     virtual void requestFileSystemRoot(ErrorString*, const String& origin, const String& type, int* requestId) OVERRIDE;
     virtual void requestDirectoryContent(ErrorString*, const String& url, int* requestId) OVERRIDE;
+    virtual void requestMetadata(ErrorString*, const String& url, int* requestId) OVERRIDE;
 
     virtual void setFrontend(InspectorFrontend*) OVERRIDE;
     virtual void clearFrontend() OVERRIDE;
index 661e915..edb647f 100644 (file)
@@ -235,6 +235,11 @@ WebInspector.FileSystemModel.prototype = {
         }
 
         callback(errorCode, entries);
+    },
+
+    requestMetadata: function(entry, callback)
+    {
+        this._agentWrapper.requestMetadata(entry.url, callback);
     }
 }
 
@@ -325,6 +330,14 @@ WebInspector.FileSystemModel.Entry.prototype = {
     get isDirectory()
     {
         return this._isDirectory;
+    },
+
+    /**
+     * @param {function(number, FileSystemAgent.Metadata)} callback
+     */
+    requestMetadata: function(callback)
+    {
+        this.fileSystemModel.requestMetadata(this, callback);
     }
 }
 
@@ -394,6 +407,7 @@ WebInspector.FileSystemRequestManager = function()
 {
     this._pendingFileSystemRootRequests = {};
     this._pendingDirectoryContentRequests = {};
+    this._pendingMetadataRequests = {};
 
     InspectorBackend.registerFileSystemDispatcher(new WebInspector.FileSystemDispatcher(this));
     FileSystemAgent.enable();
@@ -459,6 +473,31 @@ WebInspector.FileSystemRequestManager.prototype = {
             return;
         delete this._pendingDirectoryContentRequests[requestId];
         callback(errorCode, backendEntries);
+    },
+
+    /**
+     * @param {string} url
+     * @param {function(number, FileSystemAgent.Metadata=)} callback
+     */
+    requestMetadata: function(url, callback)
+    {
+        var store = this._pendingMetadataRequests;
+        FileSystemAgent.requestMetadata(url, requestAccepted);
+
+        function requestAccepted(error, requestId)
+        {
+            if (!error)
+                store[requestId] = callback;
+        }
+    },
+
+    _metadataReceived: function(requestId, errorCode, metadata)
+    {
+        var callback = this._pendingMetadataRequests[requestId];
+        if (!callback)
+            return;
+        delete this._pendingMetadataRequests[requestId];
+        callback(errorCode, metadata);
     }
 }
 
@@ -491,5 +530,15 @@ WebInspector.FileSystemDispatcher.prototype = {
     directoryContentReceived: function(requestId, errorCode, backendEntries)
     {
         this._agentWrapper._directoryContentReceived(requestId, errorCode, backendEntries);
+    },
+
+    /**
+     * @param {number} requestId
+     * @param {number} errorCode
+     * @param {FileSystemAgent.Metadata=} metadata
+     */
+    metadataReceived: function(requestId, errorCode, metadata)
+    {
+        this._agentWrapper._metadataReceived(requestId, errorCode, metadata);
     }
 }