Adds media license nodes to cookie tree model and cookies view.

Adds a new node "Media license" when appropriate to the cookie
tree model and displayis it in the "Cookies and site data" dialog.
When selected, it displays details of media license (origin,
size on disk, last modified time) in the details frame.

BUG=649523
TEST=new tests pass
As well, nodes show up in the cookies dialog after visiting a site
that uses media licenses.
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:closure_compilation

Review-Url: https://codereview.chromium.org/2359393002
Cr-Commit-Position: refs/heads/master@{#425166}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8edef1e..2bee1e8 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -153,6 +153,8 @@
     "browsing_data/browsing_data_indexed_db_helper.h",
     "browsing_data/browsing_data_local_storage_helper.cc",
     "browsing_data/browsing_data_local_storage_helper.h",
+    "browsing_data/browsing_data_media_license_helper.cc",
+    "browsing_data/browsing_data_media_license_helper.h",
     "browsing_data/browsing_data_quota_helper.cc",
     "browsing_data/browsing_data_quota_helper.h",
     "browsing_data/browsing_data_quota_helper_impl.cc",
@@ -4031,6 +4033,8 @@
     "browsing_data/mock_browsing_data_indexed_db_helper.h",
     "browsing_data/mock_browsing_data_local_storage_helper.cc",
     "browsing_data/mock_browsing_data_local_storage_helper.h",
+    "browsing_data/mock_browsing_data_media_license_helper.cc",
+    "browsing_data/mock_browsing_data_media_license_helper.h",
     "browsing_data/mock_browsing_data_quota_helper.cc",
     "browsing_data/mock_browsing_data_quota_helper.h",
     "browsing_data/mock_browsing_data_service_worker_helper.cc",
diff --git a/chrome/browser/android/preferences/website_preference_bridge.cc b/chrome/browser/android/preferences/website_preference_bridge.cc
index 509ab44e1..4ab1bf2 100644
--- a/chrome/browser/android/preferences/website_preference_bridge.cc
+++ b/chrome/browser/android/preferences/website_preference_bridge.cc
@@ -531,7 +531,7 @@
         new BrowsingDataCookieHelper(profile_->GetRequestContext()),
         new BrowsingDataDatabaseHelper(profile_),
         new BrowsingDataLocalStorageHelper(profile_),
-        NULL,
+        nullptr,
         new BrowsingDataAppCacheHelper(profile_),
         new BrowsingDataIndexedDBHelper(indexed_db_context),
         BrowsingDataFileSystemHelper::Create(file_system_context),
@@ -539,7 +539,8 @@
         BrowsingDataChannelIDHelper::Create(profile_->GetRequestContext()),
         new BrowsingDataServiceWorkerHelper(service_worker_context),
         new BrowsingDataCacheStorageHelper(cache_storage_context),
-        NULL);
+        nullptr,
+        nullptr);
 
     cookies_tree_model_.reset(new CookiesTreeModel(
         container, profile_->GetExtensionSpecialStoragePolicy()));
diff --git a/chrome/browser/browsing_data/browsing_data_media_license_helper.cc b/chrome/browser/browsing_data/browsing_data_media_license_helper.cc
new file mode 100644
index 0000000..653af7ff
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_media_license_helper.cc
@@ -0,0 +1,148 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/browsing_data/browsing_data_media_license_helper.h"
+
+#include <memory>
+#include <set>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/sequenced_task_runner.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "content/public/browser/browser_thread.h"
+#include "storage/browser/fileapi/file_system_context.h"
+#include "storage/browser/fileapi/file_system_quota_util.h"
+#include "storage/browser/fileapi/plugin_private_file_system_backend.h"
+#include "storage/common/fileapi/file_system_types.h"
+
+using content::BrowserThread;
+
+namespace {
+
+// An implementation of the BrowsingDataMediaLicenseHelper interface that
+// determine data on media licenses in a given |filesystem_context| and
+// returns a list of MediaLicenseInfo items to a client.
+class BrowsingDataMediaLicenseHelperImpl
+    : public BrowsingDataMediaLicenseHelper {
+ public:
+  // BrowsingDataMediaLicenseHelper implementation
+  explicit BrowsingDataMediaLicenseHelperImpl(
+      storage::FileSystemContext* filesystem_context);
+  void StartFetching(const FetchCallback& callback) final;
+  void DeleteMediaLicenseOrigin(const GURL& origin) final;
+
+ private:
+  ~BrowsingDataMediaLicenseHelperImpl() final;
+
+  // Enumerates all filesystem files, storing the resulting list into
+  // file_system_file_ for later use. This must be called on the file
+  // task runner.
+  void FetchMediaLicenseInfoOnFileTaskRunner(const FetchCallback& callback);
+
+  // Deletes all file systems associated with |origin|. This must be called on
+  // the file task runner.
+  void DeleteMediaLicenseOriginOnFileTaskRunner(const GURL& origin);
+
+  // Returns the file task runner for the |filesystem_context_|.
+  base::SequencedTaskRunner* file_task_runner() {
+    return filesystem_context_->default_file_task_runner();
+  }
+
+  // Keep a reference to the FileSystemContext object for the current profile
+  // for use on the file task runner.
+  scoped_refptr<storage::FileSystemContext> filesystem_context_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowsingDataMediaLicenseHelperImpl);
+};
+
+BrowsingDataMediaLicenseHelperImpl::BrowsingDataMediaLicenseHelperImpl(
+    storage::FileSystemContext* filesystem_context)
+    : filesystem_context_(filesystem_context) {
+  DCHECK(filesystem_context_.get());
+}
+
+BrowsingDataMediaLicenseHelperImpl::~BrowsingDataMediaLicenseHelperImpl() {}
+
+void BrowsingDataMediaLicenseHelperImpl::StartFetching(
+    const FetchCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(!callback.is_null());
+  file_task_runner()->PostTask(
+      FROM_HERE, base::Bind(&BrowsingDataMediaLicenseHelperImpl::
+                                FetchMediaLicenseInfoOnFileTaskRunner,
+                            this, callback));
+}
+
+void BrowsingDataMediaLicenseHelperImpl::DeleteMediaLicenseOrigin(
+    const GURL& origin) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  file_task_runner()->PostTask(
+      FROM_HERE, base::Bind(&BrowsingDataMediaLicenseHelperImpl::
+                                DeleteMediaLicenseOriginOnFileTaskRunner,
+                            this, origin));
+}
+
+void BrowsingDataMediaLicenseHelperImpl::FetchMediaLicenseInfoOnFileTaskRunner(
+    const FetchCallback& callback) {
+  DCHECK(file_task_runner()->RunsTasksOnCurrentThread());
+  DCHECK(!callback.is_null());
+
+  const storage::FileSystemType kType = storage::kFileSystemTypePluginPrivate;
+
+  storage::PluginPrivateFileSystemBackend* backend =
+      static_cast<storage::PluginPrivateFileSystemBackend*>(
+          filesystem_context_->GetFileSystemBackend(kType));
+
+  // Determine the set of origins used.
+  std::set<GURL> origins;
+  std::list<MediaLicenseInfo> result;
+  backend->GetOriginsForTypeOnFileTaskRunner(kType, &origins);
+  for (const GURL& origin : origins) {
+    if (!BrowsingDataHelper::HasWebScheme(origin))
+      continue;  // Non-websafe state is not considered browsing data.
+
+    int64_t size;
+    base::Time last_modified_time;
+    backend->GetOriginDetailsOnFileTaskRunner(filesystem_context_.get(), origin,
+                                              &size, &last_modified_time);
+    result.push_back(MediaLicenseInfo(origin, size, last_modified_time));
+  }
+
+  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+                          base::Bind(callback, result));
+}
+
+void BrowsingDataMediaLicenseHelperImpl::
+    DeleteMediaLicenseOriginOnFileTaskRunner(const GURL& origin) {
+  DCHECK(file_task_runner()->RunsTasksOnCurrentThread());
+
+  const storage::FileSystemType kType = storage::kFileSystemTypePluginPrivate;
+  storage::FileSystemBackend* backend =
+      filesystem_context_->GetFileSystemBackend(kType);
+  storage::FileSystemQuotaUtil* quota_util = backend->GetQuotaUtil();
+  quota_util->DeleteOriginDataOnFileTaskRunner(
+      filesystem_context_.get(), filesystem_context_->quota_manager_proxy(),
+      origin, kType);
+}
+
+}  // namespace
+
+BrowsingDataMediaLicenseHelper::MediaLicenseInfo::MediaLicenseInfo(
+    const GURL& origin,
+    int64_t size,
+    base::Time last_modified_time)
+    : origin(origin), size(size), last_modified_time(last_modified_time) {}
+
+BrowsingDataMediaLicenseHelper::MediaLicenseInfo::MediaLicenseInfo(
+    const MediaLicenseInfo& other) = default;
+
+BrowsingDataMediaLicenseHelper::MediaLicenseInfo::~MediaLicenseInfo() {}
+
+// static
+BrowsingDataMediaLicenseHelper* BrowsingDataMediaLicenseHelper::Create(
+    storage::FileSystemContext* filesystem_context) {
+  return new BrowsingDataMediaLicenseHelperImpl(filesystem_context);
+}
diff --git a/chrome/browser/browsing_data/browsing_data_media_license_helper.h b/chrome/browser/browsing_data/browsing_data_media_license_helper.h
new file mode 100644
index 0000000..d1cecfd
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_media_license_helper.h
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_MEDIA_LICENSE_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_MEDIA_LICENSE_HELPER_H_
+
+#include <stdint.h>
+#include <list>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "url/gurl.h"
+
+namespace storage {
+class FileSystemContext;
+}
+
+// Defines an interface for classes that deal with aggregating and deleting
+// media licenses.
+// BrowsingDataMediaLicenseHelper instances for a specific profile should
+// be created via the static Create method. Each instance will lazily fetch
+// data when a client calls StartFetching from the UI thread, and will
+// notify the client via a supplied callback when the data is available.
+//
+// The client's callback is passed a list of MediaLicenseInfo objects
+// containing usage information for each origin's media licenses.
+class BrowsingDataMediaLicenseHelper
+    : public base::RefCountedThreadSafe<BrowsingDataMediaLicenseHelper> {
+ public:
+  // Detailed information about a media license, including it's origin GURL
+  // and the amount of data (in bytes).
+  struct MediaLicenseInfo {
+    MediaLicenseInfo(const GURL& origin,
+                     int64_t size,
+                     base::Time last_modified_time);
+    MediaLicenseInfo(const MediaLicenseInfo& other);
+    ~MediaLicenseInfo();
+
+    // The origin for which the information is relevant.
+    GURL origin;
+    // Size (in bytes).
+    int64_t size;
+    // Last modified time.
+    base::Time last_modified_time;
+  };
+
+  using FetchCallback =
+      base::Callback<void(const std::list<MediaLicenseInfo>&)>;
+
+  // Creates a BrowsingDataMediaLicenseHelper instance for the media
+  // licenses stored in |profile|'s user data directory. The
+  // BrowsingDataMediaLicenseHelper object will hold a reference to the
+  // Profile that's passed in, but is not responsible for destroying it.
+  //
+  // The BrowsingDataMediaLicenseHelper will not change the profile itself,
+  // but can modify data it contains (by removing media licenses).
+  static BrowsingDataMediaLicenseHelper* Create(
+      storage::FileSystemContext* file_system_context);
+
+  // Starts the process of fetching media license data, which will call
+  // |callback| upon completion, passing it a constant list of
+  // MediaLicenseInfo objects. StartFetching must be called only in the UI
+  // thread; the provided Callback will likewise be executed asynchronously
+  // on the UI thread. Obtaining the data will occur asynchronously on the
+  // FILE thread.
+  virtual void StartFetching(const FetchCallback& callback) = 0;
+
+  // Deletes any media licenses associated with |origin| from the disk.
+  // Deletion will occur asynchronously on the FILE thread, but this function
+  // must be called only on the UI thread.
+  virtual void DeleteMediaLicenseOrigin(const GURL& origin) = 0;
+
+ protected:
+  friend class base::RefCountedThreadSafe<BrowsingDataMediaLicenseHelper>;
+
+  BrowsingDataMediaLicenseHelper() {}
+  virtual ~BrowsingDataMediaLicenseHelper() {}
+};
+
+#endif  // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_MEDIA_LICENSE_HELPER_H_
diff --git a/chrome/browser/browsing_data/browsing_data_media_license_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_media_license_helper_unittest.cc
new file mode 100644
index 0000000..8f4782e
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_media_license_helper_unittest.cc
@@ -0,0 +1,348 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browsing_data/browsing_data_media_license_helper.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/test/test_browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "ppapi/shared_impl/ppapi_constants.h"
+#include "storage/browser/fileapi/async_file_util.h"
+#include "storage/browser/fileapi/file_system_context.h"
+#include "storage/browser/fileapi/file_system_operation_context.h"
+#include "storage/browser/fileapi/file_system_url.h"
+#include "storage/browser/fileapi/isolated_context.h"
+#include "storage/browser/quota/quota_manager.h"
+#include "storage/common/fileapi/file_system_types.h"
+#include "storage/common/fileapi/file_system_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+
+namespace {
+
+// We'll use these three distinct origins for testing, both as strings and as
+// GURLs in appropriate contexts.
+const char kTestOrigin1[] = "http://host1:1/";
+const char kTestOrigin2[] = "http://host2:2/";
+const char kTestOrigin3[] = "http://host3:1/";
+
+const GURL kOrigin1(kTestOrigin1);
+const GURL kOrigin2(kTestOrigin2);
+const GURL kOrigin3(kTestOrigin3);
+
+const char kWidevineCdmPluginId[] = "application_x-ppapi-widevine-cdm";
+const char kClearKeyCdmPluginId[] = "application_x-ppapi-clearkey-cdm";
+
+class AwaitCompletionHelper {
+ public:
+  AwaitCompletionHelper() : start_(false), already_quit_(false) {}
+  virtual ~AwaitCompletionHelper() {}
+
+  void BlockUntilNotified() {
+    if (!already_quit_) {
+      DCHECK(!start_);
+      start_ = true;
+      base::RunLoop().Run();
+    } else {
+      DCHECK(!start_);
+      already_quit_ = false;
+    }
+  }
+
+  base::Closure NotifyClosure() {
+    return base::Bind(&AwaitCompletionHelper::Notify, base::Unretained(this));
+  }
+
+ private:
+  void Notify() {
+    if (start_) {
+      DCHECK(!already_quit_);
+      base::MessageLoop::current()->QuitWhenIdle();
+      start_ = false;
+    } else {
+      DCHECK(!already_quit_);
+      already_quit_ = true;
+    }
+  }
+
+  // Helps prevent from running message_loop, if the callback invoked
+  // immediately.
+  bool start_;
+  bool already_quit_;
+
+  DISALLOW_COPY_AND_ASSIGN(AwaitCompletionHelper);
+};
+
+// The FileSystem APIs are all asynchronous; this testing class wraps up the
+// boilerplate code necessary to deal with waiting for responses. In a nutshell,
+// any async call whose response we want to test ought to be followed by a call
+// to BlockUntilNotified(), which will block until Notify() is called.
+class BrowsingDataMediaLicenseHelperTest : public testing::Test {
+ public:
+  BrowsingDataMediaLicenseHelperTest() {
+    now_ = base::Time::Now();
+    profile_.reset(new TestingProfile());
+    filesystem_context_ =
+        BrowserContext::GetDefaultStoragePartition(profile_.get())
+            ->GetFileSystemContext();
+    helper_ = BrowsingDataMediaLicenseHelper::Create(filesystem_context_);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  ~BrowsingDataMediaLicenseHelperTest() override {
+    // Avoid memory leaks.
+    profile_.reset();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Calls StartFetching() on the test's BrowsingDataMediaLicenseHelper
+  // object, then blocks until the callback is executed.
+  void FetchMediaLicenses() {
+    AwaitCompletionHelper await_completion;
+    helper_->StartFetching(
+        base::Bind(&BrowsingDataMediaLicenseHelperTest::OnFetchMediaLicenses,
+                   base::Unretained(this), await_completion.NotifyClosure()));
+    await_completion.BlockUntilNotified();
+  }
+
+  // Callback that should be executed in response to StartFetching(), and stores
+  // found file systems locally so that they are available via GetFileSystems().
+  void OnFetchMediaLicenses(
+      const base::Closure& done_cb,
+      const std::list<BrowsingDataMediaLicenseHelper::MediaLicenseInfo>&
+          media_license_info_list) {
+    media_license_info_list_.reset(
+        new std::list<BrowsingDataMediaLicenseHelper::MediaLicenseInfo>(
+            media_license_info_list));
+    done_cb.Run();
+  }
+
+  // Add some files to the PluginPrivateFileSystem. They are created as follows:
+  //   kOrigin1 - ClearKey - 1 file - timestamp 10 days ago
+  //   kOrigin2 - Widevine - 2 files - timestamps now and 60 days ago
+  //   kOrigin3 - Widevine - 2 files - timestamps 20 and 30 days ago
+  virtual void PopulateTestMediaLicenseData() {
+    const base::Time ten_days_ago = now_ - base::TimeDelta::FromDays(10);
+    const base::Time twenty_days_ago = now_ - base::TimeDelta::FromDays(20);
+    const base::Time thirty_days_ago = now_ - base::TimeDelta::FromDays(30);
+    const base::Time sixty_days_ago = now_ - base::TimeDelta::FromDays(60);
+
+    std::string clearkey_fsid =
+        CreateFileSystem(kClearKeyCdmPluginId, kOrigin1);
+    storage::FileSystemURL clearkey_file =
+        CreateFile(kOrigin1, clearkey_fsid, "foo");
+    SetFileTimestamp(clearkey_file, ten_days_ago);
+
+    std::string widevine_fsid =
+        CreateFileSystem(kWidevineCdmPluginId, kOrigin2);
+    storage::FileSystemURL widevine_file1 =
+        CreateFile(kOrigin2, widevine_fsid, "bar1");
+    storage::FileSystemURL widevine_file2 =
+        CreateFile(kOrigin2, widevine_fsid, "bar2");
+    SetFileTimestamp(widevine_file1, now_);
+    SetFileTimestamp(widevine_file2, sixty_days_ago);
+
+    std::string widevine_fsid2 =
+        CreateFileSystem(kWidevineCdmPluginId, kOrigin3);
+    storage::FileSystemURL widevine_file3 =
+        CreateFile(kOrigin3, widevine_fsid2, "test1");
+    storage::FileSystemURL widevine_file4 =
+        CreateFile(kOrigin3, widevine_fsid2, "test2");
+    SetFileTimestamp(widevine_file3, twenty_days_ago);
+    SetFileTimestamp(widevine_file4, thirty_days_ago);
+  }
+
+  const base::Time Now() { return now_; }
+
+  void DeleteMediaLicenseOrigin(const GURL& origin) {
+    helper_->DeleteMediaLicenseOrigin(origin);
+  }
+
+  std::list<BrowsingDataMediaLicenseHelper::MediaLicenseInfo>*
+  ReturnedMediaLicenseInfo() const {
+    return media_license_info_list_.get();
+  }
+
+ private:
+  // Creates a PluginPrivateFileSystem for the |plugin_name| and |origin|
+  // provided. Returns the file system ID for the created
+  // PluginPrivateFileSystem.
+  std::string CreateFileSystem(const std::string& plugin_name,
+                               const GURL& origin) {
+    AwaitCompletionHelper await_completion;
+    std::string fsid = storage::IsolatedContext::GetInstance()
+                           ->RegisterFileSystemForVirtualPath(
+                               storage::kFileSystemTypePluginPrivate,
+                               ppapi::kPluginPrivateRootName, base::FilePath());
+    EXPECT_TRUE(storage::ValidateIsolatedFileSystemId(fsid));
+    filesystem_context_->OpenPluginPrivateFileSystem(
+        origin, storage::kFileSystemTypePluginPrivate, fsid, plugin_name,
+        storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
+        base::Bind(&BrowsingDataMediaLicenseHelperTest::OnFileSystemOpened,
+                   base::Unretained(this), await_completion.NotifyClosure()));
+    await_completion.BlockUntilNotified();
+    return fsid;
+  }
+
+  void OnFileSystemOpened(const base::Closure& done_cb,
+                          base::File::Error result) {
+    EXPECT_EQ(base::File::FILE_OK, result) << base::File::ErrorToString(result);
+    done_cb.Run();
+  }
+
+  // Creates a file named |file_name| in the PluginPrivateFileSystem identified
+  // by |origin| and |fsid|. The file is empty (so size = 0). Returns the URL
+  // for the created file. The file must not already exist or the test will
+  // fail.
+  storage::FileSystemURL CreateFile(const GURL& origin,
+                                    const std::string& fsid,
+                                    const std::string& file_name) {
+    AwaitCompletionHelper await_completion;
+    std::string root = storage::GetIsolatedFileSystemRootURIString(
+        origin, fsid, ppapi::kPluginPrivateRootName);
+    storage::FileSystemURL file_url =
+        filesystem_context_->CrackURL(GURL(root + file_name));
+    storage::AsyncFileUtil* file_util = filesystem_context_->GetAsyncFileUtil(
+        storage::kFileSystemTypePluginPrivate);
+    std::unique_ptr<storage::FileSystemOperationContext> operation_context =
+        base::WrapUnique(
+            new storage::FileSystemOperationContext(filesystem_context_));
+    operation_context->set_allowed_bytes_growth(
+        storage::QuotaManager::kNoLimit);
+    file_util->EnsureFileExists(
+        std::move(operation_context), file_url,
+        base::Bind(&BrowsingDataMediaLicenseHelperTest::OnFileCreated,
+                   base::Unretained(this), await_completion.NotifyClosure()));
+    await_completion.BlockUntilNotified();
+    return file_url;
+  }
+
+  void OnFileCreated(const base::Closure& done_cb,
+                     base::File::Error result,
+                     bool created) {
+    EXPECT_EQ(base::File::FILE_OK, result) << base::File::ErrorToString(result);
+    EXPECT_TRUE(created);
+    done_cb.Run();
+  }
+
+  // Sets the last_access_time and last_modified_time to |time_stamp| on the
+  // file specified by |file_url|. The file must already exist.
+  void SetFileTimestamp(const storage::FileSystemURL& file_url,
+                        const base::Time& time_stamp) {
+    AwaitCompletionHelper await_completion;
+    storage::AsyncFileUtil* file_util = filesystem_context_->GetAsyncFileUtil(
+        storage::kFileSystemTypePluginPrivate);
+    std::unique_ptr<storage::FileSystemOperationContext> operation_context =
+        base::WrapUnique(
+            new storage::FileSystemOperationContext(filesystem_context_));
+    file_util->Touch(
+        std::move(operation_context), file_url, time_stamp, time_stamp,
+        base::Bind(&BrowsingDataMediaLicenseHelperTest::OnFileTouched,
+                   base::Unretained(this), await_completion.NotifyClosure()));
+    await_completion.BlockUntilNotified();
+  }
+
+  void OnFileTouched(const base::Closure& done_cb, base::File::Error result) {
+    EXPECT_EQ(base::File::FILE_OK, result) << base::File::ErrorToString(result);
+    done_cb.Run();
+  }
+
+  content::TestBrowserThreadBundle thread_bundle_;
+  std::unique_ptr<TestingProfile> profile_;
+  scoped_refptr<BrowsingDataMediaLicenseHelper> helper_;
+
+  // Keep a fixed "now" so that we can compare timestamps.
+  base::Time now_;
+
+  // We don't own this pointer.
+  storage::FileSystemContext* filesystem_context_;
+
+  // Storage to pass information back from callbacks.
+  std::unique_ptr<std::list<BrowsingDataMediaLicenseHelper::MediaLicenseInfo>>
+      media_license_info_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowsingDataMediaLicenseHelperTest);
+};
+
+// Verifies that the BrowsingDataMediaLicenseHelper correctly handles an empty
+// filesystem.
+TEST_F(BrowsingDataMediaLicenseHelperTest, Empty) {
+  FetchMediaLicenses();
+  EXPECT_EQ(0u, ReturnedMediaLicenseInfo()->size());
+}
+
+// Verifies that the BrowsingDataMediaLicenseHelper correctly finds the test
+// data, and that each media license returned contains the expected data.
+TEST_F(BrowsingDataMediaLicenseHelperTest, FetchData) {
+  PopulateTestMediaLicenseData();
+
+  FetchMediaLicenses();
+  EXPECT_EQ(3u, ReturnedMediaLicenseInfo()->size());
+
+  // Order is arbitrary, verify both origins.
+  bool test_hosts_found[] = {false, false, false};
+  for (const auto& info : *ReturnedMediaLicenseInfo()) {
+    if (info.origin == kOrigin1) {
+      EXPECT_FALSE(test_hosts_found[0]);
+      test_hosts_found[0] = true;
+      EXPECT_EQ(0u, info.size);
+      // Single file for origin1 should be 10 days ago.
+      EXPECT_EQ(10, (Now() - info.last_modified_time).InDays());
+    } else if (info.origin == kOrigin2) {
+      EXPECT_FALSE(test_hosts_found[1]);
+      test_hosts_found[1] = true;
+      EXPECT_EQ(0u, info.size);
+      // Files for origin2 are now and 60 days ago, so it should report now.
+      EXPECT_EQ(0, (Now() - info.last_modified_time).InDays());
+    } else if (info.origin == kOrigin3) {
+      EXPECT_FALSE(test_hosts_found[2]);
+      test_hosts_found[2] = true;
+      EXPECT_EQ(0u, info.size);
+      // Files for origin3 are 20 and 30 days ago, so it should report 20.
+      EXPECT_EQ(20, (Now() - info.last_modified_time).InDays());
+    } else {
+      ADD_FAILURE() << info.origin.spec() << " isn't an origin we added.";
+    }
+  }
+  for (size_t i = 0; i < arraysize(test_hosts_found); i++) {
+    EXPECT_TRUE(test_hosts_found[i]);
+  }
+}
+
+// Verifies that the BrowsingDataMediaLicenseHelper correctly deletes media
+// licenses via DeleteMediaLicenseOrigin().
+TEST_F(BrowsingDataMediaLicenseHelperTest, DeleteData) {
+  PopulateTestMediaLicenseData();
+
+  DeleteMediaLicenseOrigin(kOrigin1);
+  DeleteMediaLicenseOrigin(kOrigin2);
+
+  FetchMediaLicenses();
+  EXPECT_EQ(1u, ReturnedMediaLicenseInfo()->size());
+
+  BrowsingDataMediaLicenseHelper::MediaLicenseInfo info =
+      *(ReturnedMediaLicenseInfo()->begin());
+  EXPECT_EQ(kOrigin3, info.origin);
+  EXPECT_EQ(0u, info.size);
+  EXPECT_EQ(20, (Now() - info.last_modified_time).InDays());
+}
+
+}  // namespace
diff --git a/chrome/browser/browsing_data/cookies_tree_model.cc b/chrome/browser/browsing_data/cookies_tree_model.cc
index 2d713e28..bfcf2fb 100644
--- a/chrome/browser/browsing_data/cookies_tree_model.cc
+++ b/chrome/browser/browsing_data/cookies_tree_model.cc
@@ -144,6 +144,7 @@
     case CookieTreeNode::DetailedInfo::TYPE_QUOTA:
     case CookieTreeNode::DetailedInfo::TYPE_CHANNEL_ID:
     case CookieTreeNode::DetailedInfo::TYPE_FLASH_LSO:
+    case CookieTreeNode::DetailedInfo::TYPE_MEDIA_LICENSE:
       return false;
     default:
       break;
@@ -279,6 +280,15 @@
   return *this;
 }
 
+CookieTreeNode::DetailedInfo& CookieTreeNode::DetailedInfo::InitMediaLicense(
+    const BrowsingDataMediaLicenseHelper::MediaLicenseInfo*
+        media_license_info) {
+  Init(TYPE_MEDIA_LICENSE);
+  this->media_license_info = media_license_info;
+  this->origin = media_license_info->origin;
+  return *this;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // CookieTreeNode, public:
 
@@ -583,6 +593,32 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// CookieTreeMediaLicenseNode, public:
+
+CookieTreeMediaLicenseNode::CookieTreeMediaLicenseNode(
+    const std::list<BrowsingDataMediaLicenseHelper::MediaLicenseInfo>::iterator
+        media_license_info)
+    : CookieTreeNode(base::UTF8ToUTF16(media_license_info->origin.spec())),
+      media_license_info_(media_license_info) {}
+
+CookieTreeMediaLicenseNode::~CookieTreeMediaLicenseNode() {}
+
+void CookieTreeMediaLicenseNode::DeleteStoredObjects() {
+  LocalDataContainer* container = GetLocalDataContainerForNode(this);
+
+  if (container) {
+    container->media_license_helper_->DeleteMediaLicenseOrigin(
+        media_license_info_->origin);
+    container->media_license_info_list_.erase(media_license_info_);
+  }
+}
+
+CookieTreeNode::DetailedInfo CookieTreeMediaLicenseNode::GetDetailedInfo()
+    const {
+  return DetailedInfo().InitMediaLicense(&*media_license_info_);
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // CookieTreeRootNode, public:
 
 CookieTreeRootNode::CookieTreeRootNode(CookiesTreeModel* model)
@@ -749,6 +785,15 @@
   return flash_lso_child_;
 }
 
+CookieTreeMediaLicensesNode*
+CookieTreeHostNode::GetOrCreateMediaLicensesNode() {
+  if (media_licenses_child_)
+    return media_licenses_child_;
+  media_licenses_child_ = new CookieTreeMediaLicensesNode();
+  AddChildSortedByTitle(base::WrapUnique(media_licenses_child_));
+  return media_licenses_child_;
+}
+
 void CookieTreeHostNode::CreateContentException(
     content_settings::CookieSettings* cookie_settings,
     ContentSetting setting) const {
@@ -935,6 +980,18 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// CookieTreeMediaLicensesNode
+CookieTreeMediaLicensesNode::CookieTreeMediaLicensesNode()
+    : CookieTreeNode(l10n_util::GetStringUTF16(IDS_COOKIES_MEDIA_LICENSES)) {}
+
+CookieTreeMediaLicensesNode::~CookieTreeMediaLicensesNode() {}
+
+CookieTreeNode::DetailedInfo CookieTreeMediaLicensesNode::GetDetailedInfo()
+    const {
+  return DetailedInfo().Init(DetailedInfo::TYPE_MEDIA_LICENSES);
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // ScopedBatchUpdateNotifier
 CookiesTreeModel::ScopedBatchUpdateNotifier::ScopedBatchUpdateNotifier(
     CookiesTreeModel* model,
@@ -1028,6 +1085,7 @@
     case CookieTreeNode::DetailedInfo::TYPE_FILE_SYSTEM:
     case CookieTreeNode::DetailedInfo::TYPE_SERVICE_WORKER:
     case CookieTreeNode::DetailedInfo::TYPE_CACHE_STORAGE:
+    case CookieTreeNode::DetailedInfo::TYPE_MEDIA_LICENSE:
       return DATABASE;
     case CookieTreeNode::DetailedInfo::TYPE_QUOTA:
       return -1;
@@ -1171,6 +1229,11 @@
   PopulateFlashLSOInfoWithFilter(container, &notifier, base::string16());
 }
 
+void CookiesTreeModel::PopulateMediaLicenseInfo(LocalDataContainer* container) {
+  ScopedBatchUpdateNotifier notifier(this, GetRoot());
+  PopulateMediaLicenseInfoWithFilter(container, &notifier, base::string16());
+}
+
 void CookiesTreeModel::PopulateAppCacheInfoWithFilter(
     LocalDataContainer* container,
     ScopedBatchUpdateNotifier* notifier,
@@ -1493,6 +1556,33 @@
   }
 }
 
+void CookiesTreeModel::PopulateMediaLicenseInfoWithFilter(
+    LocalDataContainer* container,
+    ScopedBatchUpdateNotifier* notifier,
+    const base::string16& filter) {
+  CookieTreeRootNode* root = static_cast<CookieTreeRootNode*>(GetRoot());
+
+  if (container->media_license_info_list_.empty())
+    return;
+
+  notifier->StartBatchUpdate();
+  for (MediaLicenseInfoList::iterator media_license_info =
+           container->media_license_info_list_.begin();
+       media_license_info != container->media_license_info_list_.end();
+       ++media_license_info) {
+    GURL origin(media_license_info->origin);
+
+    if (filter.empty() || (CookieTreeHostNode::TitleForUrl(origin).find(
+                               filter) != base::string16::npos)) {
+      CookieTreeHostNode* host_node = root->GetOrCreateHostNode(origin);
+      CookieTreeMediaLicensesNode* media_licenses_node =
+          host_node->GetOrCreateMediaLicensesNode();
+      media_licenses_node->AddMediaLicenseNode(
+          base::MakeUnique<CookieTreeMediaLicenseNode>(media_license_info));
+    }
+  }
+}
+
 void CookiesTreeModel::SetBatchExpectation(int batches_expected, bool reset) {
   batches_expected_ = batches_expected;
   if (reset) {
diff --git a/chrome/browser/browsing_data/cookies_tree_model.h b/chrome/browser/browsing_data/cookies_tree_model.h
index 2d81901..96205bb 100644
--- a/chrome/browser/browsing_data/cookies_tree_model.h
+++ b/chrome/browser/browsing_data/cookies_tree_model.h
@@ -53,6 +53,8 @@
 class CookieTreeIndexedDBsNode;
 class CookieTreeLocalStorageNode;
 class CookieTreeLocalStoragesNode;
+class CookieTreeMediaLicenseNode;
+class CookieTreeMediaLicensesNode;
 class CookieTreeQuotaNode;
 class CookieTreeServiceWorkerNode;
 class CookieTreeServiceWorkersNode;
@@ -109,6 +111,8 @@
       TYPE_CACHE_STORAGES,    // This is used for CookieTreeCacheStoragesNode.
       TYPE_CACHE_STORAGE,     // This is used for CookieTreeCacheStorageNode.
       TYPE_FLASH_LSO,         // This is used for CookieTreeFlashLSONode.
+      TYPE_MEDIA_LICENSES,    // This is used for CookieTreeMediaLicensesNode.
+      TYPE_MEDIA_LICENSE,     // This is used for CookieTreeMediaLicenseNode.
     };
 
     DetailedInfo();
@@ -141,6 +145,9 @@
     DetailedInfo& InitCacheStorage(
         const content::CacheStorageUsageInfo* cache_storage_info);
     DetailedInfo& InitFlashLSO(const std::string& flash_lso_domain);
+    DetailedInfo& InitMediaLicense(
+        const BrowsingDataMediaLicenseHelper::MediaLicenseInfo*
+            media_license_info);
 
     NodeType node_type;
     GURL origin;
@@ -159,6 +166,8 @@
     const content::ServiceWorkerUsageInfo* service_worker_info = nullptr;
     const content::CacheStorageUsageInfo* cache_storage_info = nullptr;
     std::string flash_lso_domain;
+    const BrowsingDataMediaLicenseHelper::MediaLicenseInfo* media_license_info =
+        nullptr;
   };
 
   CookieTreeNode() {}
@@ -229,6 +238,7 @@
   CookieTreeQuotaNode* UpdateOrCreateQuotaNode(
       std::list<BrowsingDataQuotaHelper::QuotaInfo>::iterator quota_info);
   CookieTreeFlashLSONode* GetOrCreateFlashLSONode(const std::string& domain);
+  CookieTreeMediaLicensesNode* GetOrCreateMediaLicensesNode();
 
   std::string canonicalized_host() const { return canonicalized_host_; }
 
@@ -260,6 +270,7 @@
   CookieTreeServiceWorkersNode* service_workers_child_ = nullptr;
   CookieTreeCacheStoragesNode* cache_storages_child_ = nullptr;
   CookieTreeFlashLSONode* flash_lso_child_ = nullptr;
+  CookieTreeMediaLicensesNode* media_licenses_child_ = nullptr;
 
   // The URL for which this node was initially created.
   GURL url_;
@@ -684,6 +695,45 @@
   DISALLOW_COPY_AND_ASSIGN(CookieTreeFlashLSONode);
 };
 
+// CookieTreeMediaLicenseNode -----------------------------------------------
+class CookieTreeMediaLicenseNode : public CookieTreeNode {
+ public:
+  friend class CookieTreeMediaLicensesNode;
+
+  // |media_license_info| is expected to remain valid as long as the
+  // CookieTreeMediaLicenseNode is valid.
+  explicit CookieTreeMediaLicenseNode(
+      const std::list<BrowsingDataMediaLicenseHelper::MediaLicenseInfo>::
+          iterator media_license_info);
+  ~CookieTreeMediaLicenseNode() override;
+
+  void DeleteStoredObjects() override;
+  DetailedInfo GetDetailedInfo() const override;
+
+ private:
+  // |media_license_info_| is expected to remain valid as long as the
+  // CookieTreeMediaLicenseNode is valid.
+  std::list<BrowsingDataMediaLicenseHelper::MediaLicenseInfo>::iterator
+      media_license_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(CookieTreeMediaLicenseNode);
+};
+
+class CookieTreeMediaLicensesNode : public CookieTreeNode {
+ public:
+  CookieTreeMediaLicensesNode();
+  ~CookieTreeMediaLicensesNode() override;
+
+  DetailedInfo GetDetailedInfo() const override;
+
+  void AddMediaLicenseNode(std::unique_ptr<CookieTreeMediaLicenseNode> child) {
+    AddChildSortedByTitle(std::move(child));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CookieTreeMediaLicensesNode);
+};
+
 // CookiesTreeModel -----------------------------------------------------------
 class CookiesTreeModel : public ui::TreeNodeModel<CookieTreeNode> {
  public:
@@ -773,6 +823,7 @@
   void PopulateServiceWorkerUsageInfo(LocalDataContainer* container);
   void PopulateCacheStorageUsageInfo(LocalDataContainer* container);
   void PopulateFlashLSOInfo(LocalDataContainer* container);
+  void PopulateMediaLicenseInfo(LocalDataContainer* container);
 
   BrowsingDataCookieHelper* GetCookieHelper(const std::string& app_id);
   LocalDataContainer* data_container() {
@@ -849,6 +900,9 @@
   void PopulateFlashLSOInfoWithFilter(LocalDataContainer* container,
                                       ScopedBatchUpdateNotifier* notifier,
                                       const base::string16& filter);
+  void PopulateMediaLicenseInfoWithFilter(LocalDataContainer* container,
+                                          ScopedBatchUpdateNotifier* notifier,
+                                          const base::string16& filter);
 
 #if defined(ENABLE_EXTENSIONS)
   // The extension special storage policy; see ExtensionsProtectingNode() above.
diff --git a/chrome/browser/browsing_data/cookies_tree_model_unittest.cc b/chrome/browser/browsing_data/cookies_tree_model_unittest.cc
index ee95f9e..35074ff 100644
--- a/chrome/browser/browsing_data/cookies_tree_model_unittest.cc
+++ b/chrome/browser/browsing_data/cookies_tree_model_unittest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/browsing_data/mock_browsing_data_flash_lso_helper.h"
 #include "chrome/browser/browsing_data/mock_browsing_data_indexed_db_helper.h"
 #include "chrome/browser/browsing_data/mock_browsing_data_local_storage_helper.h"
+#include "chrome/browser/browsing_data/mock_browsing_data_media_license_helper.h"
 #include "chrome/browser/browsing_data/mock_browsing_data_quota_helper.h"
 #include "chrome/browser/browsing_data/mock_browsing_data_service_worker_helper.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
@@ -81,6 +82,8 @@
         new MockBrowsingDataCacheStorageHelper(profile_.get());
     mock_browsing_data_flash_lso_helper_ =
         new MockBrowsingDataFlashLSOHelper(profile_.get());
+    mock_browsing_data_media_license_helper_ =
+        new MockBrowsingDataMediaLicenseHelper(profile_.get());
 
 #if defined(ENABLE_EXTENSIONS)
     special_storage_policy_ =
@@ -100,6 +103,7 @@
     mock_browsing_data_local_storage_helper_ = nullptr;
     mock_browsing_data_database_helper_ = nullptr;
     mock_browsing_data_flash_lso_helper_ = nullptr;
+    mock_browsing_data_media_license_helper_ = nullptr;
     base::RunLoop().RunUntilIdle();
   }
   std::unique_ptr<CookiesTreeModel> CreateCookiesTreeModelWithInitialSample() {
@@ -115,7 +119,8 @@
                                mock_browsing_data_channel_id_helper_,
                                mock_browsing_data_service_worker_helper_,
                                mock_browsing_data_cache_storage_helper_,
-                               mock_browsing_data_flash_lso_helper_);
+                               mock_browsing_data_flash_lso_helper_,
+                               mock_browsing_data_media_license_helper_);
 
     CookiesTreeModel* cookies_model =
         new CookiesTreeModel(container, special_storage_policy());
@@ -149,21 +154,23 @@
     mock_browsing_data_cache_storage_helper_->Notify();
     mock_browsing_data_flash_lso_helper_->AddFlashLSODomain("xyz.com");
     mock_browsing_data_flash_lso_helper_->Notify();
+    mock_browsing_data_media_license_helper_->AddMediaLicenseSamples();
+    mock_browsing_data_media_license_helper_->Notify();
 
     {
       SCOPED_TRACE(
           "Initial State 3 cookies, 2 databases, 2 local storages, "
           "2 session storages, 2 indexed DBs, 3 filesystems, "
           "2 quotas, 2 server bound certs, 2 service workers, "
-          "2 cache storages, 1 Flash LSO");
-      // 65 because there's the root, then
+          "2 cache storages, 1 Flash LSO, 2 media licenses");
+      // 71 because there's the root, then
       // cshost1 -> cache storage -> https://cshost1:1/
       // cshost2 -> cache storage -> https://cshost2:2/
       // foo1 -> cookies -> a,
       // foo2 -> cookies -> b,
       // foo3 -> cookies -> c,
-      // dbhost1 -> database -> db1,
-      // dbhost2 -> database -> db2,
+      // gdbhost1 -> database -> db1,
+      // gdbhost2 -> database -> db2,
       // host1 -> localstorage -> http://host1:1/,
       //       -> sessionstorage -> http://host1:1/,
       // host2 -> localstorage -> http://host2:2/.
@@ -173,6 +180,8 @@
       // fshost1 -> filesystem -> http://fshost1:1/,
       // fshost2 -> filesystem -> http://fshost2:1/,
       // fshost3 -> filesystem -> http://fshost3:1/,
+      // media1 -> media_licenses -> media_license,
+      // media2 -> media_licenses -> media_license,
       // quotahost1 -> quotahost1,
       // quotahost2 -> quotahost2,
       // sbc1 -> sbcerts -> sbc1,
@@ -180,7 +189,7 @@
       // swhost1 -> service worker -> https://swhost1:1
       // swhost2 -> service worker -> https://swhost1:2
       // xyz.com -> flash_lsos
-      EXPECT_EQ(65, cookies_model->GetRoot()->GetTotalNodeCount());
+      EXPECT_EQ(71, cookies_model->GetRoot()->GetTotalNodeCount());
       EXPECT_EQ("A,B,C", GetDisplayedCookies(cookies_model));
       EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model));
       EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -201,6 +210,8 @@
                 GetDisplayedCacheStorages(cookies_model));
       EXPECT_EQ("xyz.com",
                 GetDisplayedFlashLSOs(cookies_model));
+      EXPECT_EQ("https://media1/,https://media2/",
+                GetDisplayedMediaLicenses(cookies_model));
     }
     return base::WrapUnique(cookies_model);
   }
@@ -283,6 +294,8 @@
         return node->GetDetailedInfo().cache_storage_info->origin.spec() + ",";
       case CookieTreeNode::DetailedInfo::TYPE_FLASH_LSO:
         return node->GetDetailedInfo().flash_lso_domain + ",";
+      case CookieTreeNode::DetailedInfo::TYPE_MEDIA_LICENSE:
+        return node->GetDetailedInfo().media_license_info->origin.spec() + ",";
       default:
         return std::string();
     }
@@ -337,6 +350,11 @@
         node, CookieTreeNode::DetailedInfo::TYPE_FLASH_LSO);
   }
 
+  std::string GetMediaLicensesOfChildren(const CookieTreeNode* node) {
+    return GetNodesOfChildren(node,
+                              CookieTreeNode::DetailedInfo::TYPE_MEDIA_LICENSE);
+  }
+
   // Get the nodes names displayed in the view (if we had one) in the order
   // they are displayed, as a comma seperated string.
   // Ex: EXPECT_STREQ("X,Y", GetDisplayedNodes(cookies_view, type).c_str());
@@ -410,6 +428,11 @@
         cookies_model, CookieTreeNode::DetailedInfo::TYPE_FLASH_LSO);
   }
 
+  std::string GetDisplayedMediaLicenses(CookiesTreeModel* cookies_model) {
+    return GetDisplayedNodes(cookies_model,
+                             CookieTreeNode::DetailedInfo::TYPE_MEDIA_LICENSE);
+  }
+
   // Do not call on the root.
   void DeleteStoredObjects(CookieTreeNode* node) {
     node->DeleteStoredObjects();
@@ -453,6 +476,8 @@
       mock_browsing_data_cache_storage_helper_;
   scoped_refptr<MockBrowsingDataFlashLSOHelper>
       mock_browsing_data_flash_lso_helper_;
+  scoped_refptr<MockBrowsingDataMediaLicenseHelper>
+      mock_browsing_data_media_license_helper_;
 
 #if defined(ENABLE_EXTENSIONS)
   scoped_refptr<ExtensionSpecialStoragePolicy> special_storage_policy_;
@@ -488,6 +513,8 @@
               GetDisplayedServiceWorkers(cookies_model.get()));
     EXPECT_EQ("xyz.com",
               GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
   }
 
   mock_browsing_data_cookie_helper_->Reset();
@@ -521,6 +548,7 @@
     EXPECT_TRUE(mock_browsing_data_service_worker_helper_->AllDeleted());
     EXPECT_TRUE(mock_browsing_data_cache_storage_helper_->AllDeleted());
     EXPECT_TRUE(mock_browsing_data_flash_lso_helper_->AllDeleted());
+    EXPECT_TRUE(mock_browsing_data_media_license_helper_->AllDeleted());
   }
 }
 
@@ -544,18 +572,21 @@
   // 11. `host2`
   // 12. `idbhost1`
   // 13. `idbhost2`
-  // 14. `quotahost1`
-  // 15. `quotahost2`
-  // 16. `sbc1`
-  // 17. `sbc2`
-  // 18. `swhost1`
-  // 19. `swhost2`
-  // 20. `xyz.com`
+  // 14. `media1`
+  // 15. `media2`
+  // 16. `quotahost1`
+  // 17. `quotahost2`
+  // 18. `sbc1`
+  // 19. `sbc2`
+  // 20. `swhost1`
+  // 21. `swhost2`
+  // 22. `xyz.com`
   //
   // Here, we'll remove them one by one, starting from the end, and
-  // check that the state makes sense.
+  // check that the state makes sense. Initially there are 71 total nodes.
 
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(20));
+  // xyz.com -> flash_lsos (2 nodes)
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(22));
   {
     SCOPED_TRACE("`xyz.com` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
@@ -577,9 +608,13 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
-    EXPECT_EQ(63, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
+    EXPECT_EQ(69, cookies_model->GetRoot()->GetTotalNodeCount());
   }
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(19));
+
+  // swhost2 -> service worker -> https://swhost1:2 (3 objects)
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(21));
   {
     SCOPED_TRACE("`swhost2` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
@@ -599,9 +634,13 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
-    EXPECT_EQ(60, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
+    EXPECT_EQ(66, cookies_model->GetRoot()->GetTotalNodeCount());
   }
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(18));
+
+  // swhost1 -> service worker -> https://swhost1:1 (3 nodes)
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(20));
   {
     SCOPED_TRACE("`swhost1` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
@@ -620,9 +659,13 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
-    EXPECT_EQ(57, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
+    EXPECT_EQ(63, cookies_model->GetRoot()->GetTotalNodeCount());
   }
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(17));
+
+  // sbc2 -> sbcerts -> sbc2 (3 objects)
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(19));
   {
     SCOPED_TRACE("`sbc2` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
@@ -643,9 +686,13 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
-    EXPECT_EQ(54, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
+    EXPECT_EQ(60, cookies_model->GetRoot()->GetTotalNodeCount());
   }
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(16));
+
+  // sbc1 -> sbcerts -> sbc1 (3 objects)
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(18));
   {
     SCOPED_TRACE("`sbc1` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
@@ -665,9 +712,13 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
-    EXPECT_EQ(51, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
+    EXPECT_EQ(57, cookies_model->GetRoot()->GetTotalNodeCount());
   }
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(15));
+
+  // quotahost2 -> quotahost2 (2 objects)
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(17));
   {
     SCOPED_TRACE("`quotahost2` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
@@ -686,9 +737,13 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
-    EXPECT_EQ(49, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
+    EXPECT_EQ(55, cookies_model->GetRoot()->GetTotalNodeCount());
   }
-  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(14));
+
+  // quotahost1 -> quotahost1 (2 objects)
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(16));
   {
     SCOPED_TRACE("`quotahost1` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
@@ -705,8 +760,57 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
+    EXPECT_EQ(53, cookies_model->GetRoot()->GetTotalNodeCount());
+  }
+
+  // media2 -> media_licenses -> media_license (3 objects)
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(15));
+  {
+    SCOPED_TRACE("`media2` removed.");
+    EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
+    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://host1:1/,http://host2:2/",
+              GetDisplayedLocalStorages(cookies_model.get()));
+    EXPECT_EQ("http://host1:1/,http://host2:2/",
+              GetDisplayedSessionStorages(cookies_model.get()));
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
+              GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedServiceWorkers(cookies_model.get()));
+    EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
+              GetDisplayedCacheStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("https://media1/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
+    EXPECT_EQ(50, cookies_model->GetRoot()->GetTotalNodeCount());
+  }
+
+  // media1 -> media_licenses -> media_license (3 objects)
+  DeleteStoredObjects(cookies_model->GetRoot()->GetChild(14));
+  {
+    SCOPED_TRACE("`media1` removed.");
+    EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
+    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://host1:1/,http://host2:2/",
+              GetDisplayedLocalStorages(cookies_model.get()));
+    EXPECT_EQ("http://host1:1/,http://host2:2/",
+              GetDisplayedSessionStorages(cookies_model.get()));
+    EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
+              GetDisplayedFileSystems(cookies_model.get()));
+    EXPECT_EQ("http://idbhost1:1/,http://idbhost2:2/",
+              GetDisplayedIndexedDBs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedServiceWorkers(cookies_model.get()));
+    EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
+              GetDisplayedCacheStorages(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(47, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+
+  // idbhost2 -> indexeddb -> http://idbhost2:2/ (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(13));
   {
     SCOPED_TRACE("`idbhost2` removed.");
@@ -724,8 +828,11 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(44, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+
+  // idbhost1 -> indexeddb -> http://idbhost1:1/ (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(12));
   {
     SCOPED_TRACE("`idbhost1` removed.");
@@ -742,8 +849,12 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(41, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+
+  // host2 -> localstorage -> http://host2:2/,
+  //       -> sessionstorage -> http://host2:2/ (5 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(11));
   {
     SCOPED_TRACE("`host2` removed.");
@@ -760,8 +871,12 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(36, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+
+  // host1 -> localstorage -> http://host1:1/,
+  //       -> sessionstorage -> http://host1:1/ (5 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(10));
   {
     SCOPED_TRACE("`host1` removed.");
@@ -776,8 +891,11 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(31, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+
+  // gdbhost2 -> database -> db2 (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(9));
   {
     SCOPED_TRACE("`gdbhost2` removed.");
@@ -792,8 +910,10 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(28, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+  // gdbhost1 -> database -> db1 (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(8));
   {
     SCOPED_TRACE("`gdbhost1` removed.");
@@ -808,8 +928,11 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(25, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+
+  // fshost3 -> filesystem -> http://fshost3:1/ (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(7));
   {
     SCOPED_TRACE("`fshost3` removed.");
@@ -824,8 +947,11 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(22, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+
+  // fshost2 -> filesystem -> http://fshost2:1/ (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(6));
   {
     SCOPED_TRACE("`fshost2` removed.");
@@ -840,8 +966,11 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(19, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+
+  // fshost1 -> filesystem -> http://fshost1:1/ (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(5));
   {
     SCOPED_TRACE("`fshost1` removed.");
@@ -855,8 +984,11 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(16, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+
+  // foo3 -> cookies -> c (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(4));
   {
     SCOPED_TRACE("`foo3` removed.");
@@ -870,8 +1002,11 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(13, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+
+  // foo2 -> cookies -> b (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(3));
   {
     SCOPED_TRACE("`foo2` removed.");
@@ -885,8 +1020,11 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(10, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+
+  // foo1 -> cookies -> a (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(2));
   {
     SCOPED_TRACE("`foo1` removed.");
@@ -900,8 +1038,11 @@
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(7, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+
+  // cshost2 -> cache storage -> https://cshost2:2/ (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(1));
   {
     SCOPED_TRACE("`cshost2` removed.");
@@ -915,8 +1056,11 @@
     EXPECT_EQ("https://cshost1:1/",
               GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(4, cookies_model->GetRoot()->GetTotalNodeCount());
   }
+
+  // cshost1 -> cache storage -> https://cshost1:1/ (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(0));
   {
     SCOPED_TRACE("`cshost1` removed.");
@@ -929,6 +1073,7 @@
     EXPECT_EQ("", GetDisplayedServiceWorkers(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedCacheStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(1, cookies_model->GetRoot()->GetTotalNodeCount());
   }
 }
@@ -941,9 +1086,9 @@
   {
     SCOPED_TRACE("First cookies origin removed");
     EXPECT_STREQ("B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    // 63 because in this case, the origin remains, although the COOKIES
+    // 69 because in this case, the origin remains, although the COOKIES
     // node beneath it has been deleted.
-    EXPECT_EQ(63, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ(69, cookies_model->GetRoot()->GetTotalNodeCount());
     EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
@@ -959,6 +1104,9 @@
               GetDisplayedServiceWorkers(cookies_model.get()));
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
+    EXPECT_EQ("xyz.com", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
   }
 
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(8)->GetChild(0));
@@ -980,7 +1128,10 @@
               GetDisplayedServiceWorkers(cookies_model.get()));
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
-    EXPECT_EQ(61, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("xyz.com", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
+    EXPECT_EQ(67, cookies_model->GetRoot()->GetTotalNodeCount());
   }
 
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(10)->GetChild(0));
@@ -1002,7 +1153,10 @@
               GetDisplayedServiceWorkers(cookies_model.get()));
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
-    EXPECT_EQ(59, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("xyz.com", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
+    EXPECT_EQ(65, cookies_model->GetRoot()->GetTotalNodeCount());
   }
 }
 
@@ -1029,9 +1183,12 @@
               GetDisplayedServiceWorkers(cookies_model.get()));
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
-    // 63 because in this case, the origin remains, although the COOKIES
+    EXPECT_EQ("xyz.com", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
+    // 69 because in this case, the origin remains, although the COOKIES
     // node beneath it has been deleted.
-    EXPECT_EQ(63, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ(69, cookies_model->GetRoot()->GetTotalNodeCount());
   }
 
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(8)->GetChild(0));
@@ -1053,7 +1210,10 @@
               GetDisplayedServiceWorkers(cookies_model.get()));
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
-    EXPECT_EQ(61, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("xyz.com", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
+    EXPECT_EQ(67, cookies_model->GetRoot()->GetTotalNodeCount());
   }
 
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(10)->GetChild(0));
@@ -1075,7 +1235,10 @@
               GetDisplayedServiceWorkers(cookies_model.get()));
     EXPECT_EQ("https://cshost1:1/,https://cshost2:2/",
               GetDisplayedCacheStorages(cookies_model.get()));
-    EXPECT_EQ(59, cookies_model->GetRoot()->GetTotalNodeCount());
+    EXPECT_EQ("xyz.com", GetDisplayedFlashLSOs(cookies_model.get()));
+    EXPECT_EQ("https://media1/,https://media2/",
+              GetDisplayedMediaLicenses(cookies_model.get()));
+    EXPECT_EQ(65, cookies_model->GetRoot()->GetTotalNodeCount());
   }
 }
 
@@ -1092,7 +1255,8 @@
                              mock_browsing_data_channel_id_helper_,
                              mock_browsing_data_service_worker_helper_,
                              mock_browsing_data_cache_storage_helper_,
-                             mock_browsing_data_flash_lso_helper_);
+                             mock_browsing_data_flash_lso_helper_,
+                             mock_browsing_data_media_license_helper_);
   CookiesTreeModel cookies_model(container, special_storage_policy());
 
   mock_browsing_data_cookie_helper_->
@@ -1199,7 +1363,8 @@
                              mock_browsing_data_channel_id_helper_,
                              mock_browsing_data_service_worker_helper_,
                              mock_browsing_data_cache_storage_helper_,
-                             mock_browsing_data_flash_lso_helper_);
+                             mock_browsing_data_flash_lso_helper_,
+                             mock_browsing_data_media_license_helper_);
   CookiesTreeModel cookies_model(container, special_storage_policy());
 
   mock_browsing_data_cookie_helper_->
@@ -1309,7 +1474,8 @@
                              mock_browsing_data_channel_id_helper_,
                              mock_browsing_data_service_worker_helper_,
                              mock_browsing_data_cache_storage_helper_,
-                             mock_browsing_data_flash_lso_helper_);
+                             mock_browsing_data_flash_lso_helper_,
+                             mock_browsing_data_media_license_helper_);
   CookiesTreeModel cookies_model(container, special_storage_policy());
 
   mock_browsing_data_cookie_helper_->
@@ -1353,7 +1519,8 @@
                              mock_browsing_data_channel_id_helper_,
                              mock_browsing_data_service_worker_helper_,
                              mock_browsing_data_cache_storage_helper_,
-                             mock_browsing_data_flash_lso_helper_);
+                             mock_browsing_data_flash_lso_helper_,
+                             mock_browsing_data_media_license_helper_);
   CookiesTreeModel cookies_model(container, special_storage_policy());
 
   mock_browsing_data_cookie_helper_->
@@ -1402,7 +1569,8 @@
                              mock_browsing_data_channel_id_helper_,
                              mock_browsing_data_service_worker_helper_,
                              mock_browsing_data_cache_storage_helper_,
-                             mock_browsing_data_flash_lso_helper_);
+                             mock_browsing_data_flash_lso_helper_,
+                             mock_browsing_data_media_license_helper_);
   CookiesTreeModel cookies_model(container, special_storage_policy());
 
   mock_browsing_data_cookie_helper_->AddCookieSamples(host, "A=1");
@@ -1507,7 +1675,8 @@
                              mock_browsing_data_channel_id_helper_,
                              mock_browsing_data_service_worker_helper_,
                              mock_browsing_data_cache_storage_helper_,
-                             mock_browsing_data_flash_lso_helper_);
+                             mock_browsing_data_flash_lso_helper_,
+                             mock_browsing_data_media_license_helper_);
   CookiesTreeModel cookies_model(container, special_storage_policy());
 
   mock_browsing_data_cookie_helper_->
@@ -1549,7 +1718,8 @@
                              mock_browsing_data_channel_id_helper_,
                              mock_browsing_data_service_worker_helper_,
                              mock_browsing_data_cache_storage_helper_,
-                             mock_browsing_data_flash_lso_helper_);
+                             mock_browsing_data_flash_lso_helper_,
+                             mock_browsing_data_media_license_helper_);
   CookiesTreeModel cookies_model(container, special_storage_policy());
 
   mock_browsing_data_cookie_helper_->AddCookieSamples(
@@ -1637,7 +1807,8 @@
                              mock_browsing_data_channel_id_helper_,
                              mock_browsing_data_service_worker_helper_,
                              mock_browsing_data_cache_storage_helper_,
-                             mock_browsing_data_flash_lso_helper_);
+                             mock_browsing_data_flash_lso_helper_,
+                             mock_browsing_data_media_license_helper_);
   CookiesTreeModel cookies_model(container, special_storage_policy());
 
   mock_browsing_data_cookie_helper_->
@@ -1648,18 +1819,19 @@
 
 TEST_F(CookiesTreeModelTest, Suborigins) {
   LocalDataContainer* container =
-      new LocalDataContainer(mock_browsing_data_cookie_helper_.get(),
-                             mock_browsing_data_database_helper_.get(),
-                             mock_browsing_data_local_storage_helper_.get(),
-                             mock_browsing_data_session_storage_helper_.get(),
-                             mock_browsing_data_appcache_helper_.get(),
-                             mock_browsing_data_indexed_db_helper_.get(),
-                             mock_browsing_data_file_system_helper_.get(),
-                             mock_browsing_data_quota_helper_.get(),
-                             mock_browsing_data_channel_id_helper_.get(),
-                             mock_browsing_data_service_worker_helper_.get(),
-                             mock_browsing_data_cache_storage_helper_.get(),
-                             mock_browsing_data_flash_lso_helper_.get());
+      new LocalDataContainer(mock_browsing_data_cookie_helper_,
+                             mock_browsing_data_database_helper_,
+                             mock_browsing_data_local_storage_helper_,
+                             mock_browsing_data_session_storage_helper_,
+                             mock_browsing_data_appcache_helper_,
+                             mock_browsing_data_indexed_db_helper_,
+                             mock_browsing_data_file_system_helper_,
+                             mock_browsing_data_quota_helper_,
+                             mock_browsing_data_channel_id_helper_,
+                             mock_browsing_data_service_worker_helper_,
+                             mock_browsing_data_cache_storage_helper_,
+                             mock_browsing_data_flash_lso_helper_,
+                             mock_browsing_data_media_license_helper_);
   CookiesTreeModel cookies_model(container, special_storage_policy());
 
   mock_browsing_data_local_storage_helper_
diff --git a/chrome/browser/browsing_data/local_data_container.cc b/chrome/browser/browsing_data/local_data_container.cc
index 395c329..00a4fdc 100644
--- a/chrome/browser/browsing_data/local_data_container.cc
+++ b/chrome/browser/browsing_data/local_data_container.cc
@@ -26,7 +26,8 @@
     scoped_refptr<BrowsingDataChannelIDHelper> channel_id_helper,
     scoped_refptr<BrowsingDataServiceWorkerHelper> service_worker_helper,
     scoped_refptr<BrowsingDataCacheStorageHelper> cache_storage_helper,
-    scoped_refptr<BrowsingDataFlashLSOHelper> flash_lso_helper)
+    scoped_refptr<BrowsingDataFlashLSOHelper> flash_lso_helper,
+    scoped_refptr<BrowsingDataMediaLicenseHelper> media_license_helper)
     : appcache_helper_(std::move(appcache_helper)),
       cookie_helper_(std::move(cookie_helper)),
       database_helper_(std::move(database_helper)),
@@ -39,6 +40,7 @@
       service_worker_helper_(std::move(service_worker_helper)),
       cache_storage_helper_(std::move(cache_storage_helper)),
       flash_lso_helper_(std::move(flash_lso_helper)),
+      media_license_helper_(std::move(media_license_helper)),
       weak_ptr_factory_(this) {}
 
 LocalDataContainer::~LocalDataContainer() {}
@@ -132,6 +134,13 @@
                    weak_ptr_factory_.GetWeakPtr()));
   }
 
+  if (media_license_helper_.get()) {
+    batches_started_++;
+    media_license_helper_->StartFetching(
+        base::Bind(&LocalDataContainer::OnMediaLicenseInfoLoaded,
+                   weak_ptr_factory_.GetWeakPtr()));
+  }
+
   model_->SetBatchExpectation(batches_started_, true);
 }
 
@@ -234,3 +243,10 @@
   DCHECK(model_);
   model_->PopulateFlashLSOInfo(this);
 }
+
+void LocalDataContainer::OnMediaLicenseInfoLoaded(
+    const MediaLicenseInfoList& media_license_info) {
+  media_license_info_list_ = media_license_info;
+  DCHECK(model_);
+  model_->PopulateMediaLicenseInfo(this);
+}
diff --git a/chrome/browser/browsing_data/local_data_container.h b/chrome/browser/browsing_data/local_data_container.h
index de2b923..5cbf450e6 100644
--- a/chrome/browser/browsing_data/local_data_container.h
+++ b/chrome/browser/browsing_data/local_data_container.h
@@ -22,6 +22,7 @@
 #include "chrome/browser/browsing_data/browsing_data_file_system_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_media_license_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_quota_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_service_worker_helper.h"
 #include "net/ssl/channel_id_store.h"
@@ -53,6 +54,8 @@
 typedef std::list<content::CacheStorageUsageInfo> CacheStorageUsageInfoList;
 typedef std::map<GURL, std::list<content::AppCacheInfo> > AppCacheInfoMap;
 typedef std::vector<std::string> FlashLSODomainList;
+typedef std::list<BrowsingDataMediaLicenseHelper::MediaLicenseInfo>
+    MediaLicenseInfoList;
 
 }  // namespace
 
@@ -76,7 +79,8 @@
       scoped_refptr<BrowsingDataChannelIDHelper> channel_id_helper,
       scoped_refptr<BrowsingDataServiceWorkerHelper> service_worker_helper,
       scoped_refptr<BrowsingDataCacheStorageHelper> cache_storage_helper,
-      scoped_refptr<BrowsingDataFlashLSOHelper> flash_data_helper);
+      scoped_refptr<BrowsingDataFlashLSOHelper> flash_data_helper,
+      scoped_refptr<BrowsingDataMediaLicenseHelper> media_license_helper);
   virtual ~LocalDataContainer();
 
   // This method must be called to start the process of fetching the resources.
@@ -86,6 +90,7 @@
  private:
   friend class CookiesTreeModel;
   friend class CookieTreeAppCacheNode;
+  friend class CookieTreeMediaLicenseNode;
   friend class CookieTreeCookieNode;
   friend class CookieTreeDatabaseNode;
   friend class CookieTreeLocalStorageNode;
@@ -118,6 +123,7 @@
   void OnCacheStorageModelInfoLoaded(
       const CacheStorageUsageInfoList& cache_storage_info);
   void OnFlashLSOInfoLoaded(const FlashLSODomainList& domains);
+  void OnMediaLicenseInfoLoaded(const MediaLicenseInfoList& media_license_info);
 
   // Pointers to the helper objects, needed to retreive all the types of locally
   // stored data.
@@ -133,6 +139,7 @@
   scoped_refptr<BrowsingDataServiceWorkerHelper> service_worker_helper_;
   scoped_refptr<BrowsingDataCacheStorageHelper> cache_storage_helper_;
   scoped_refptr<BrowsingDataFlashLSOHelper> flash_lso_helper_;
+  scoped_refptr<BrowsingDataMediaLicenseHelper> media_license_helper_;
 
   // Storage for all the data that was retrieved through the helper objects.
   // The collected data is used for (re)creating the CookiesTreeModel.
@@ -148,6 +155,7 @@
   ServiceWorkerUsageInfoList service_worker_info_list_;
   CacheStorageUsageInfoList cache_storage_info_list_;
   FlashLSODomainList flash_lso_domain_list_;
+  MediaLicenseInfoList media_license_info_list_;
 
   // A delegate, which must outlive this object. The update callbacks use the
   // delegate to deliver the updated data to the CookieTreeModel.
diff --git a/chrome/browser/browsing_data/mock_browsing_data_media_license_helper.cc b/chrome/browser/browsing_data/mock_browsing_data_media_license_helper.cc
new file mode 100644
index 0000000..48e887fb
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_media_license_helper.cc
@@ -0,0 +1,52 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/browsing_data/mock_browsing_data_media_license_helper.h"
+
+#include <algorithm>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+MockBrowsingDataMediaLicenseHelper::MockBrowsingDataMediaLicenseHelper(
+    Profile* profile) {}
+
+MockBrowsingDataMediaLicenseHelper::~MockBrowsingDataMediaLicenseHelper() {}
+
+void MockBrowsingDataMediaLicenseHelper::StartFetching(
+    const FetchCallback& callback) {
+  ASSERT_FALSE(callback.is_null());
+  ASSERT_TRUE(callback_.is_null());
+  callback_ = callback;
+}
+
+void MockBrowsingDataMediaLicenseHelper::DeleteMediaLicenseOrigin(
+    const GURL& origin) {
+  auto entry = std::find_if(media_licenses_.begin(), media_licenses_.end(),
+                            [origin](const MediaLicenseInfo& entry) {
+                              return entry.origin == origin;
+                            });
+  ASSERT_TRUE(entry != media_licenses_.end());
+  media_licenses_.erase(entry);
+}
+
+void MockBrowsingDataMediaLicenseHelper::AddMediaLicenseSamples() {
+  const GURL kOrigin1("https://media1/");
+  const GURL kOrigin2("https://media2/");
+  const base::Time ten_days_ago =
+      base::Time::Now() - base::TimeDelta::FromDays(10);
+  const base::Time twenty_days_ago =
+      base::Time::Now() - base::TimeDelta::FromDays(20);
+
+  media_licenses_.push_back(MediaLicenseInfo(kOrigin1, 1000, ten_days_ago));
+  media_licenses_.push_back(MediaLicenseInfo(kOrigin2, 50, twenty_days_ago));
+}
+
+void MockBrowsingDataMediaLicenseHelper::Notify() {
+  callback_.Run(media_licenses_);
+  callback_ = FetchCallback();
+}
+
+bool MockBrowsingDataMediaLicenseHelper::AllDeleted() {
+  return media_licenses_.empty();
+}
diff --git a/chrome/browser/browsing_data/mock_browsing_data_media_license_helper.h b/chrome/browser/browsing_data/mock_browsing_data_media_license_helper.h
new file mode 100644
index 0000000..206864c
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_media_license_helper.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_MEDIA_LICENSE_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_MEDIA_LICENSE_HELPER_H_
+
+#include <stdint.h>
+#include <list>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "chrome/browser/browsing_data/browsing_data_media_license_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "url/gurl.h"
+
+class MockBrowsingDataMediaLicenseHelper
+    : public BrowsingDataMediaLicenseHelper {
+ public:
+  explicit MockBrowsingDataMediaLicenseHelper(Profile* profile);
+
+  // BrowsingDataMediaLicenseHelper implementation:
+  void StartFetching(const FetchCallback& callback) override;
+  void DeleteMediaLicenseOrigin(const GURL& origin) override;
+
+  // Add some MediaLicenseInfo samples.
+  void AddMediaLicenseSamples();
+
+  // Notifies the callback.
+  void Notify();
+
+  // Returns true if the origin list is empty.
+  bool AllDeleted();
+
+ protected:
+  ~MockBrowsingDataMediaLicenseHelper() override;
+
+ private:
+  FetchCallback callback_;
+  std::list<MediaLicenseInfo> media_licenses_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockBrowsingDataMediaLicenseHelper);
+};
+
+#endif  // CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_MEDIA_LICENSE_HELPER_H_
diff --git a/chrome/browser/content_settings/local_shared_objects_container.cc b/chrome/browser/content_settings/local_shared_objects_container.cc
index 63dabf3..de5a4a2 100644
--- a/chrome/browser/content_settings/local_shared_objects_container.cc
+++ b/chrome/browser/content_settings/local_shared_objects_container.cc
@@ -220,7 +220,7 @@
   LocalDataContainer* container = new LocalDataContainer(
       cookies_, databases_, local_storages_, session_storages_, appcaches_,
       indexed_dbs_, file_systems_, nullptr, channel_ids_, service_workers_,
-      cache_storages_, nullptr);
+      cache_storages_, nullptr, nullptr);
 
   return base::MakeUnique<CookiesTreeModel>(container, nullptr);
 }
diff --git a/chrome/browser/resources/options/cookies_list.js b/chrome/browser/resources/options/cookies_list.js
index c3b97c2..21f4d00b 100644
--- a/chrome/browser/resources/options/cookies_list.js
+++ b/chrome/browser/resources/options/cookies_list.js
@@ -47,6 +47,9 @@
                        ['size', 'label_cache_storage_size'],
                        ['modified', 'label_cache_storage_last_modified']],
     'flash_lso': [['domain', 'label_cookie_domain']],
+    'media_license': [['origin', 'label_media_license_origin'],
+                      ['size', 'label_media_license_size'],
+                      ['modified', 'label_media_license_last_modified']],
   };
 
   /**
@@ -256,6 +259,7 @@
         channelIDs: 0,
         serviceWorker: false,
         cacheStorage: false,
+        mediaLicense: false,
       };
       if (this.origin)
         this.origin.collectSummaryInfo(info);
@@ -281,6 +285,8 @@
         list.push(loadTimeData.getString('cookie_cache_storage'));
       if (info.flashLSO)
         list.push(loadTimeData.getString('cookie_flash_lso'));
+      if (info.mediaLicense)
+        list.push(loadTimeData.getString('cookie_media_license'));
 
       var text = '';
       for (var i = 0; i < list.length; ++i) {
@@ -505,6 +511,8 @@
           info.cacheStorage = true;
         } else if (this.data.type == 'flash_lso') {
           info.flashLSO = true;
+        } else if (this.data.type == 'media_license') {
+          info.mediaLicense = true;
         }
 
         var apps = this.data.appsProtectingThis;
diff --git a/chrome/browser/resources/settings/site_settings/cookie_info.js b/chrome/browser/resources/settings/site_settings/cookie_info.js
index 3b912a3..f78e161f2 100644
--- a/chrome/browser/resources/settings/site_settings/cookie_info.js
+++ b/chrome/browser/resources/settings/site_settings/cookie_info.js
@@ -45,4 +45,7 @@
                     ['size', 'cacheStorageSize'],
                     ['modified', 'cacheStorageLastModified']],
   'flash_lso': [['domain', 'cookieDomain']],
+  'media_license': [['origin', 'mediaLicenseOrigin'],
+                    ['size', 'mediaLicenseSize'],
+                    ['modified', 'mediaLicenseLastModified']],
 };
diff --git a/chrome/browser/resources/settings/site_settings/cookie_tree_node.js b/chrome/browser/resources/settings/site_settings/cookie_tree_node.js
index 61dacf91..cfb0662 100644
--- a/chrome/browser/resources/settings/site_settings/cookie_tree_node.js
+++ b/chrome/browser/resources/settings/site_settings/cookie_tree_node.js
@@ -56,6 +56,7 @@
   'indexed_db': loadTimeData.getString('cookieDatabaseStorage'),
   'local_storage': loadTimeData.getString('cookieLocalStorage'),
   'service_worker': loadTimeData.getString('cookieServiceWorker'),
+  'media_license': loadTimeData.getString('cookieMediaLicense'),
 };
 
 /**
diff --git a/chrome/browser/ui/webui/cookies_tree_model_util.cc b/chrome/browser/ui/webui/cookies_tree_model_util.cc
index 82611ad..9e23782 100644
--- a/chrome/browser/ui/webui/cookies_tree_model_util.cc
+++ b/chrome/browser/ui/webui/cookies_tree_model_util.cc
@@ -299,6 +299,19 @@
       dict->SetString(kKeyDomain, node.GetDetailedInfo().flash_lso_domain);
       break;
     }
+    case CookieTreeNode::DetailedInfo::TYPE_MEDIA_LICENSE: {
+      dict->SetString(kKeyType, "media_license");
+      dict->SetString(kKeyIcon, "chrome://theme/IDR_COOKIE_STORAGE_ICON");
+
+      const BrowsingDataMediaLicenseHelper::MediaLicenseInfo&
+          media_license_info = *node.GetDetailedInfo().media_license_info;
+      dict->SetString(kKeyOrigin, media_license_info.origin.spec());
+      dict->SetString(kKeySize, ui::FormatBytes(media_license_info.size));
+      dict->SetString(kKeyModified,
+                      base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
+                          media_license_info.last_modified_time)));
+      break;
+    }
     default:
 #if defined(OS_MACOSX)
       dict->SetString(kKeyIcon, "chrome://theme/IDR_BOOKMARK_BAR_FOLDER");
diff --git a/chrome/browser/ui/webui/options/cookies_view_handler.cc b/chrome/browser/ui/webui/options/cookies_view_handler.cc
index deac6640..fed3637 100644
--- a/chrome/browser/ui/webui/options/cookies_view_handler.cc
+++ b/chrome/browser/ui/webui/options/cookies_view_handler.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_media_license_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_quota_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_service_worker_helper.h"
 #include "chrome/browser/profiles/profile.h"
@@ -110,6 +111,12 @@
       {"label_channel_id_expires", IDS_COOKIES_CHANNEL_ID_EXPIRES_LABEL},
       {"label_protected_by_apps",
        IDS_GEOLOCATION_SET_BY_HOVER},  // TODO(bauerb): Use a better string
+      {"cookie_media_license", IDS_COOKIES_MEDIA_LICENSE},
+      {"label_media_license_origin", IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+      {"label_media_license_size",
+       IDS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+      {"label_media_license_last_modified",
+       IDS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL},
   };
 
   RegisterStrings(localized_strings, resources, arraysize(resources));
@@ -216,7 +223,8 @@
         BrowsingDataChannelIDHelper::Create(profile->GetRequestContext()),
         new BrowsingDataServiceWorkerHelper(service_worker_context),
         new BrowsingDataCacheStorageHelper(cache_storage_context),
-        BrowsingDataFlashLSOHelper::Create(profile));
+        BrowsingDataFlashLSOHelper::Create(profile),
+        BrowsingDataMediaLicenseHelper::Create(file_system_context));
     cookies_tree_model_.reset(new CookiesTreeModel(
         container, profile->GetExtensionSpecialStoragePolicy()));
     cookies_tree_model_->AddCookiesTreeObserver(this);
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index ea73f2d..26a2f22 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1314,6 +1314,7 @@
       {"cookieFileSystem", IDS_COOKIES_FILE_SYSTEM},
       {"cookieFlashLso", IDS_COOKIES_FLASH_LSO},
       {"cookieLocalStorage", IDS_COOKIES_LOCAL_STORAGE},
+      {"cookieMediaLicense", IDS_COOKIES_MEDIA_LICENSE},
       {"cookiePlural", IDS_COOKIES_PLURAL_COOKIES},
       {"cookieServiceWorker", IDS_COOKIES_SERVICE_WORKER},
       {"cookieSingular", IDS_COOKIES_SINGLE_COOKIE},
@@ -1349,6 +1350,10 @@
        IDS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL},
       {"localStorageOrigin", IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
       {"localStorageSize", IDS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+      {"mediaLicenseOrigin", IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+      {"mediaLicenseSize", IDS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+      {"mediaLicenseLastModified",
+       IDS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL},
       {"serviceWorkerOrigin", IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
       {"serviceWorkerScopes", IDS_COOKIES_SERVICE_WORKER_SCOPES_LABEL},
       {"serviceWorkerSize", IDS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
diff --git a/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc b/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc
index 076e09d..841fc877 100644
--- a/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_media_license_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_quota_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_service_worker_helper.h"
 #include "chrome/browser/profiles/profile.h"
@@ -166,7 +167,8 @@
         BrowsingDataChannelIDHelper::Create(profile->GetRequestContext()),
         new BrowsingDataServiceWorkerHelper(service_worker_context),
         new BrowsingDataCacheStorageHelper(cache_storage_context),
-        BrowsingDataFlashLSOHelper::Create(profile));
+        BrowsingDataFlashLSOHelper::Create(profile),
+        BrowsingDataMediaLicenseHelper::Create(file_system_context));
     cookies_tree_model_.reset(
         new CookiesTreeModel(container,
                              profile->GetExtensionSpecialStoragePolicy()));