Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(517)

Side by Side Diff: chrome/browser/chromeos/gdata/gdata_file_system_proxy.cc

Issue 10600013: Wired support for file truncating with RemoteFileSystemOperation::OpenFile() method (case when base… (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/chromeos/gdata/gdata_file_system_proxy.h" 5 #include "chrome/browser/chromeos/gdata/gdata_file_system_proxy.h"
6 6
7 #include <string> 7 #include <string>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
(...skipping 16 matching lines...) Expand all
27 using fileapi::FileSystemURL; 27 using fileapi::FileSystemURL;
28 using fileapi::FileSystemOperationInterface; 28 using fileapi::FileSystemOperationInterface;
29 using webkit_blob::ShareableFileReference; 29 using webkit_blob::ShareableFileReference;
30 30
31 namespace { 31 namespace {
32 32
33 const char kGDataRootDirectory[] = "drive"; 33 const char kGDataRootDirectory[] = "drive";
34 const char kFeedField[] = "feed"; 34 const char kFeedField[] = "feed";
35 35
36 // Helper function that creates platform file on bocking IO thread pool. 36 // Helper function that creates platform file on bocking IO thread pool.
37 void CreatePlatformFileOnIOPool(const FilePath& local_path, 37 void OpenPlatformFileOnIOPool(const FilePath& local_path,
38 int file_flags, 38 int file_flags,
39 base::PlatformFile* platform_file, 39 base::PlatformFile* platform_file,
40 base::PlatformFileError* open_error) { 40 base::PlatformFileError* open_error) {
41 bool created; 41 bool created;
42 *platform_file = base::CreatePlatformFile(local_path, 42 *platform_file = base::CreatePlatformFile(local_path,
43 file_flags, 43 file_flags,
44 &created, 44 &created,
45 open_error); 45 open_error);
46 } 46 }
47 47
48 // Helper function to run reply on results of CreatePlatformFileOnIOPool() on 48 // Helper function to run reply on results of OpenPlatformFileOnIOPool() on
49 // IO thread. 49 // IO thread.
50 void OnPlatformFileCreated( 50 void OnPlatformFileOpened(
51 const FileSystemOperationInterface::OpenFileCallback& callback, 51 const FileSystemOperationInterface::OpenFileCallback& callback,
52 base::ProcessHandle peer_handle, 52 base::ProcessHandle peer_handle,
53 base::PlatformFile* platform_file, 53 base::PlatformFile* platform_file,
54 base::PlatformFileError* open_error) { 54 base::PlatformFileError* open_error) {
55 callback.Run(*open_error, *platform_file, peer_handle); 55 callback.Run(*open_error, *platform_file, peer_handle);
56 } 56 }
57 57
58 // Helper function to run OpenFileCallback from 58 // Helper function to run OpenFileCallback from
59 // GDataFileSystemProxy::OpenFile(). 59 // GDataFileSystemProxy::OpenFile().
60 void OnGetFileByPathForOpen( 60 void OnGetFileByPathForOpen(
61 const FileSystemOperationInterface::OpenFileCallback& callback, 61 const FileSystemOperationInterface::OpenFileCallback& callback,
62 int file_flags, 62 int file_flags,
63 base::ProcessHandle peer_handle, 63 base::ProcessHandle peer_handle,
64 gdata::GDataFileError gdata_error, 64 gdata::GDataFileError gdata_error,
65 const FilePath& local_path, 65 const FilePath& local_path,
66 const std::string& unused_mime_type, 66 const std::string& unused_mime_type,
67 gdata::GDataFileType file_type) { 67 gdata::GDataFileType file_type) {
68 base::PlatformFileError error = 68 base::PlatformFileError error =
69 gdata::util::GDataFileErrorToPlatformError(gdata_error); 69 gdata::util::GDataFileErrorToPlatformError(gdata_error);
70 if (error != base::PLATFORM_FILE_OK) { 70 if (error != base::PLATFORM_FILE_OK) {
71 callback.Run(error, base::kInvalidPlatformFileValue, peer_handle); 71 callback.Run(error, base::kInvalidPlatformFileValue, peer_handle);
72 return; 72 return;
73 } 73 }
74 74
75 base::PlatformFile* platform_file = new base::PlatformFile( 75 base::PlatformFile* platform_file = new base::PlatformFile(
76 base::kInvalidPlatformFileValue); 76 base::kInvalidPlatformFileValue);
77 base::PlatformFileError* open_error = 77 base::PlatformFileError* open_error =
78 new base::PlatformFileError(base::PLATFORM_FILE_ERROR_FAILED); 78 new base::PlatformFileError(base::PLATFORM_FILE_ERROR_FAILED);
79 BrowserThread::GetBlockingPool()->PostTaskAndReply(FROM_HERE, 79 BrowserThread::GetBlockingPool()->PostTaskAndReply(FROM_HERE,
80 base::Bind(&CreatePlatformFileOnIOPool, 80 base::Bind(&OpenPlatformFileOnIOPool,
81 local_path, 81 local_path,
82 file_flags, 82 file_flags,
83 platform_file, 83 platform_file,
84 open_error), 84 open_error),
85 base::Bind(&OnPlatformFileCreated, 85 base::Bind(&OnPlatformFileOpened,
86 callback, 86 callback,
87 peer_handle, 87 peer_handle,
88 base::Owned(platform_file), 88 base::Owned(platform_file),
89 base::Owned(open_error))); 89 base::Owned(open_error)));
90 90
91 } 91 }
92 92
93 // Helper function to run SnapshotFileCallback from 93 // Helper function to run SnapshotFileCallback from
94 // GDataFileSystemProxy::CreateSnapshotFile(). 94 // GDataFileSystemProxy::CreateSnapshotFile().
95 void CallSnapshotFileCallback( 95 void CallSnapshotFileCallback(
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
141 result); 141 result);
142 if (*result == base::PLATFORM_FILE_OK) { 142 if (*result == base::PLATFORM_FILE_OK) {
143 DCHECK_NE(base::kInvalidPlatformFileValue, file); 143 DCHECK_NE(base::kInvalidPlatformFileValue, file);
144 if (!base::TruncatePlatformFile(file, length)) 144 if (!base::TruncatePlatformFile(file, length))
145 *result = base::PLATFORM_FILE_ERROR_FAILED; 145 *result = base::PLATFORM_FILE_ERROR_FAILED;
146 base::ClosePlatformFile(file); 146 base::ClosePlatformFile(file);
147 } 147 }
148 } 148 }
149 149
150 void DidCloseFileForTruncate( 150 void DidCloseFileForTruncate(
151 const fileapi::FileSystemOperationInterface::StatusCallback& callback, 151 const FileSystemOperationInterface::StatusCallback& callback,
152 base::PlatformFileError truncate_result, 152 base::PlatformFileError truncate_result,
153 gdata::GDataFileError close_result) { 153 gdata::GDataFileError close_result) {
154 // Reports the first error. 154 // Reports the first error.
155 callback.Run(truncate_result == base::PLATFORM_FILE_OK ? 155 callback.Run(truncate_result == base::PLATFORM_FILE_OK ?
156 gdata::util::GDataFileErrorToPlatformError(close_result) : 156 gdata::util::GDataFileErrorToPlatformError(close_result) :
157 truncate_result); 157 truncate_result);
158 } 158 }
159 159
160 } // namespace 160 } // namespace
161 161
(...skipping 18 matching lines...) Expand all
180 GDataFileSystemInterface* file_system) 180 GDataFileSystemInterface* file_system)
181 : file_system_(file_system) { 181 : file_system_(file_system) {
182 // Should be created from the file browser extension API (AddMountFunction) 182 // Should be created from the file browser extension API (AddMountFunction)
183 // on UI thread. 183 // on UI thread.
184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
185 } 185 }
186 186
187 void GDataFileSystemProxy::GetFileInfo(const FileSystemURL& file_url, 187 void GDataFileSystemProxy::GetFileInfo(const FileSystemURL& file_url,
188 const FileSystemOperationInterface::GetMetadataCallback& callback) { 188 const FileSystemOperationInterface::GetMetadataCallback& callback) {
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
190
191 FilePath file_path; 190 FilePath file_path;
192 if (!ValidateUrl(file_url, &file_path)) { 191 if (!ValidateUrl(file_url, &file_path)) {
193 base::MessageLoopProxy::current()->PostTask( 192 MessageLoopProxy::current()->PostTask(FROM_HERE,
194 FROM_HERE, 193 base::Bind(callback,
195 base::Bind(callback, 194 base::PLATFORM_FILE_ERROR_NOT_FOUND,
196 base::PLATFORM_FILE_ERROR_NOT_FOUND, 195 base::PlatformFileInfo(),
197 base::PlatformFileInfo(), 196 FilePath()));
198 FilePath()));
199 return; 197 return;
200 } 198 }
201 199
202 file_system_->GetEntryInfoByPath( 200 file_system_->GetEntryInfoByPath(
203 file_path, 201 file_path,
204 base::Bind(&GDataFileSystemProxy::OnGetMetadata, 202 base::Bind(&GDataFileSystemProxy::OnGetMetadata,
205 this, 203 this,
206 file_path, 204 file_path,
207 callback)); 205 callback));
208 } 206 }
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
319 } 317 }
320 318
321 file_system_->CreateFile( 319 file_system_->CreateFile(
322 file_path, 320 file_path,
323 exclusive, 321 exclusive,
324 base::Bind(&GDataFileSystemProxy::OnStatusCallback, this, callback)); 322 base::Bind(&GDataFileSystemProxy::OnStatusCallback, this, callback));
325 } 323 }
326 324
327 void GDataFileSystemProxy::Truncate( 325 void GDataFileSystemProxy::Truncate(
328 const FileSystemURL& file_url, int64 length, 326 const FileSystemURL& file_url, int64 length,
329 const fileapi::FileSystemOperationInterface::StatusCallback& callback) { 327 const FileSystemOperationInterface::StatusCallback& callback) {
330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 328 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
331 329
332 if (length < 0) { 330 if (length < 0) {
333 MessageLoopProxy::current()->PostTask(FROM_HERE, 331 MessageLoopProxy::current()->PostTask(FROM_HERE,
334 base::Bind(callback, base::PLATFORM_FILE_ERROR_INVALID_OPERATION)); 332 base::Bind(callback, base::PLATFORM_FILE_ERROR_INVALID_OPERATION));
335 return; 333 return;
336 } 334 }
337 335
338 FilePath file_path; 336 FilePath file_path;
339 if (!ValidateUrl(file_url, &file_path)) { 337 if (!ValidateUrl(file_url, &file_path)) {
340 MessageLoopProxy::current()->PostTask(FROM_HERE, 338 MessageLoopProxy::current()->PostTask(FROM_HERE,
341 base::Bind(callback, base::PLATFORM_FILE_ERROR_NOT_FOUND)); 339 base::Bind(callback, base::PLATFORM_FILE_ERROR_NOT_FOUND));
342 return; 340 return;
343 } 341 }
344 342
345 // TODO(kinaba): http://crbug.com/132780. 343 // TODO(kinaba): http://crbug.com/132780.
346 // Optimize the cases for small |length|, at least for |length| == 0. 344 // Optimize the cases for small |length|, at least for |length| == 0.
347 // CreateWritableSnapshotFile downloads the whole content unnecessarily. 345 // CreateWritableSnapshotFile downloads the whole content unnecessarily.
348 file_system_->OpenFile( 346 file_system_->OpenFile(
349 file_path, 347 file_path,
350 base::Bind(&GDataFileSystemProxy::OnFileOpenedForTruncate, 348 base::Bind(&GDataFileSystemProxy::OnFileOpenedForTruncate,
351 this, 349 this,
352 file_path, 350 file_path,
353 length, 351 length,
354 callback)); 352 callback));
355 } 353 }
356 354
355 void GDataFileSystemProxy::OnOpenFileForWriting(
356 int file_flags,
357 base::ProcessHandle peer_handle,
358 const FileSystemOperationInterface::OpenFileCallback& callback,
359 GDataFileError gdata_error,
360 const FilePath& local_cache_path) {
361 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
362
363 base::PlatformFileError error =
364 gdata::util::GDataFileErrorToPlatformError(gdata_error);
365
366 if (error != base::PLATFORM_FILE_OK) {
367 callback.Run(error, base::kInvalidPlatformFileValue, peer_handle);
368 return;
369 }
370
371 // Cache file prepared for modification is available. Truncate it.
372 // File operation must be done on FILE thread, so relay the operation.
373 base::PlatformFileError* result =
374 new base::PlatformFileError(base::PLATFORM_FILE_ERROR_FAILED);
375 base::PlatformFile* platform_file = new base::PlatformFile(
376 base::kInvalidPlatformFileValue);
377 bool posted = BrowserThread::GetBlockingPool()->PostTaskAndReply(FROM_HERE,
378 base::Bind(&OpenPlatformFileOnIOPool,
379 local_cache_path,
380 file_flags,
381 platform_file,
382 result),
383 base::Bind(&OnPlatformFileOpened,
384 callback,
385 peer_handle,
386 base::Owned(platform_file),
387 base::Owned(result)));
388 DCHECK(posted);
389 }
390
391 void GDataFileSystemProxy::OnCreateFileForOpen(
392 const FilePath& file_path,
393 int file_flags,
394 base::ProcessHandle peer_handle,
395 const FileSystemOperationInterface::OpenFileCallback& callback,
396 GDataFileError gdata_error) {
397 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
398 base::PlatformFileError create_result =
399 gdata::util::GDataFileErrorToPlatformError(gdata_error);
400
401 if ((create_result == base::PLATFORM_FILE_OK) ||
402 ((create_result == base::PLATFORM_FILE_ERROR_EXISTS) &&
403 (file_flags & base::PLATFORM_FILE_CREATE_ALWAYS))) {
404 // If we are trying to always create an existing file, then
405 // if it really exists open it as truncated.
406 file_flags &= ~base::PLATFORM_FILE_CREATE;
407 file_flags &= ~base::PLATFORM_FILE_CREATE_ALWAYS;
408 file_flags |= base::PLATFORM_FILE_OPEN_TRUNCATED;
409 } else {
410 callback.Run(create_result, base::kInvalidPlatformFileValue, peer_handle);
411 return;
412 }
413
414 // Open created (or existing) file for writing.
415 file_system_->OpenFile(
416 file_path,
417 base::Bind(&GDataFileSystemProxy::OnOpenFileForWriting,
418 this,
419 file_flags,
420 peer_handle,
421 callback));
422 }
423
357 void GDataFileSystemProxy::OnFileOpenedForTruncate( 424 void GDataFileSystemProxy::OnFileOpenedForTruncate(
358 const FilePath& virtual_path, 425 const FilePath& virtual_path,
359 int64 length, 426 int64 length,
360 const fileapi::FileSystemOperationInterface::StatusCallback& callback, 427 const fileapi::FileSystemOperationInterface::StatusCallback& callback,
361 GDataFileError open_result, 428 GDataFileError open_result,
362 const FilePath& local_cache_path) { 429 const FilePath& local_cache_path) {
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 430 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
364 431
365 if (open_result != GDATA_FILE_OK) { 432 if (open_result != GDATA_FILE_OK) {
366 callback.Run(util::GDataFileErrorToPlatformError(open_result)); 433 callback.Run(util::GDataFileErrorToPlatformError(open_result));
(...skipping 14 matching lines...) Expand all
381 base::Bind(&GDataFileSystemProxy::DidTruncate, 448 base::Bind(&GDataFileSystemProxy::DidTruncate,
382 this, 449 this,
383 virtual_path, 450 virtual_path,
384 callback, 451 callback,
385 base::Owned(result))); 452 base::Owned(result)));
386 DCHECK(posted); 453 DCHECK(posted);
387 } 454 }
388 455
389 void GDataFileSystemProxy::DidTruncate( 456 void GDataFileSystemProxy::DidTruncate(
390 const FilePath& virtual_path, 457 const FilePath& virtual_path,
391 const fileapi::FileSystemOperationInterface::StatusCallback& callback, 458 const FileSystemOperationInterface::StatusCallback& callback,
392 base::PlatformFileError* truncate_result) { 459 base::PlatformFileError* truncate_result) {
393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 460 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
394 461
395 // Truncation finished. We must close the file no matter |truncate_result| 462 // Truncation finished. We must close the file no matter |truncate_result|
396 // indicates an error or not. 463 // indicates an error or not.
397 file_system_->CloseFile( 464 file_system_->CloseFile(
398 virtual_path, 465 virtual_path,
399 base::Bind(&DidCloseFileForTruncate, 466 base::Bind(&DidCloseFileForTruncate,
400 callback, 467 callback,
401 base::PlatformFileError(*truncate_result))); 468 base::PlatformFileError(*truncate_result)));
402 } 469 }
403 470
404 void GDataFileSystemProxy::OpenFile( 471 void GDataFileSystemProxy::OpenFile(
405 const FileSystemURL& file_url, 472 const FileSystemURL& file_url,
406 int file_flags, 473 int file_flags,
407 base::ProcessHandle peer_handle, 474 base::ProcessHandle peer_handle,
408 const FileSystemOperationInterface::OpenFileCallback& callback) { 475 const FileSystemOperationInterface::OpenFileCallback& callback) {
409 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 476 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
410 477
411 FilePath file_path; 478 FilePath file_path;
412 if (!ValidateUrl(file_url, &file_path)) { 479 if (!ValidateUrl(file_url, &file_path)) {
413 MessageLoopProxy::current()->PostTask(FROM_HERE, 480 MessageLoopProxy::current()->PostTask(FROM_HERE,
414 base::Bind(callback, 481 base::Bind(callback,
415 base::PLATFORM_FILE_ERROR_NOT_FOUND, 482 base::PLATFORM_FILE_ERROR_NOT_FOUND,
416 base::kInvalidPlatformFileValue, 483 base::kInvalidPlatformFileValue,
417 peer_handle)); 484 peer_handle));
418 return; 485 return;
419 } 486 }
420 487
421 file_system_->GetFileByPath(file_path, 488 // TODO(zelidrag): Wire all other file open operations.
422 base::Bind(&OnGetFileByPathForOpen, 489 if ((file_flags & base::PLATFORM_FILE_DELETE_ON_CLOSE)) {
423 callback, 490 NOTIMPLEMENTED() << "File create/write operations not yet supported "
424 file_flags, 491 << file_path.value();
425 peer_handle), 492 MessageLoopProxy::current()->PostTask(FROM_HERE,
426 GetDownloadDataCallback()); 493 base::Bind(callback,
494 base::PLATFORM_FILE_ERROR_FAILED,
495 base::kInvalidPlatformFileValue,
496 peer_handle));
497 return;
498 }
499
500 if ((file_flags & base::PLATFORM_FILE_OPEN) ||
501 (file_flags & base::PLATFORM_FILE_OPEN_ALWAYS) ||
502 (file_flags & base::PLATFORM_FILE_OPEN_TRUNCATED)) {
503 if ((file_flags & base::PLATFORM_FILE_OPEN_TRUNCATED) ||
504 (file_flags & base::PLATFORM_FILE_OPEN_ALWAYS) ||
505 (file_flags & base::PLATFORM_FILE_WRITE) ||
506 (file_flags & base::PLATFORM_FILE_EXCLUSIVE_WRITE)) {
507 // Open existing file for writing.
508 file_system_->OpenFile(
509 file_path,
510 base::Bind(&GDataFileSystemProxy::OnOpenFileForWriting,
511 this,
512 file_flags,
513 peer_handle,
514 callback));
515 } else {
516 // Read-only file open.
517 file_system_->GetFileByPath(file_path,
518 base::Bind(&OnGetFileByPathForOpen,
519 callback,
520 file_flags,
521 peer_handle),
522 GetDownloadDataCallback());
523 }
524 } else if ((file_flags & base::PLATFORM_FILE_CREATE) ||
525 (file_flags & base::PLATFORM_FILE_CREATE_ALWAYS)) {
526 // Open existing file for writing.
527 file_system_->CreateFile(
528 file_path,
529 file_flags & base::PLATFORM_FILE_EXCLUSIVE_WRITE,
530 base::Bind(&GDataFileSystemProxy::OnCreateFileForOpen,
531 this,
532 file_path,
533 file_flags,
534 peer_handle,
535 callback));
536 } else {
537 NOTREACHED() << "Unhandled file flags combination " << file_flags;
538 MessageLoopProxy::current()->PostTask(FROM_HERE,
539 base::Bind(callback,
540 base::PLATFORM_FILE_ERROR_FAILED,
541 base::kInvalidPlatformFileValue,
542 peer_handle));
543 }
427 } 544 }
428 545
429 void GDataFileSystemProxy::NotifyCloseFile(const FileSystemURL& url) { 546 void GDataFileSystemProxy::NotifyCloseFile(const FileSystemURL& url) {
430 // TODO(kinaba,zelidrag): crbug.com/132236. 547 FilePath file_path;
431 // Once OpenFile() for writing is implemented, we also need to implement the 548 if (!ValidateUrl(url, &file_path))
432 // corresponding NotifyCloseFile for committing dirty cache. 549 return;
550
551 file_system_->CloseFile(file_path, FileOperationCallback());
433 } 552 }
434 553
435 void GDataFileSystemProxy::CreateSnapshotFile( 554 void GDataFileSystemProxy::CreateSnapshotFile(
436 const FileSystemURL& file_url, 555 const FileSystemURL& file_url,
437 const FileSystemOperationInterface::SnapshotFileCallback& callback) { 556 const FileSystemOperationInterface::SnapshotFileCallback& callback) {
438 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 557 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
439 558
440 FilePath file_path; 559 FilePath file_path;
441 if (!ValidateUrl(file_url, &file_path)) { 560 if (!ValidateUrl(file_url, &file_path)) {
442 MessageLoopProxy::current()->PostTask(FROM_HERE, 561 MessageLoopProxy::current()->PostTask(FROM_HERE,
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after
611 730
612 void GDataFileSystemProxy::CloseWritableSnapshotFile( 731 void GDataFileSystemProxy::CloseWritableSnapshotFile(
613 const FilePath& virtual_path, 732 const FilePath& virtual_path,
614 const FilePath& local_path) { 733 const FilePath& local_path) {
615 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 734 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
616 735
617 file_system_->CloseFile(virtual_path, base::Bind(&OnClose, virtual_path)); 736 file_system_->CloseFile(virtual_path, base::Bind(&OnClose, virtual_path));
618 } 737 }
619 738
620 } // namespace gdata 739 } // namespace gdata
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698