Commit ca5dfb21 authored by estade@chromium.org's avatar estade@chromium.org

retry r147895:

switch mediaGalleries to .idl api definition

BUG=none
TEST=manual
TBR=asargent

Review URL: https://chromiumcodereview.appspot.com/10800083

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@148011 0039d316-1c4b-4281-b951-d872f2087c98
parent 7286e695
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// Implements the Chrome Extensions Media Galleries API. // Implements the Chrome Extensions Media Galleries API.
#include "chrome/browser/extensions/api/media_gallery/media_gallery_api.h" #include "chrome/browser/extensions/api/media_galleries/media_galleries_api.h"
#include <string> #include <string>
#include <vector> #include <vector>
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/platform_file.h" #include "base/platform_file.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/media_gallery/media_file_system_registry.h" #include "chrome/browser/media_gallery/media_file_system_registry.h"
#include "chrome/common/extensions/api/experimental_media_galleries.h"
#include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h" #include "content/public/browser/render_view_host.h"
...@@ -22,12 +23,38 @@ ...@@ -22,12 +23,38 @@
namespace extensions { namespace extensions {
namespace {
const char kInvalidInteractive[] = "Unknown value for interactive.";
} // namespace
using chrome::MediaFileSystemRegistry; using chrome::MediaFileSystemRegistry;
using content::ChildProcessSecurityPolicy; using content::ChildProcessSecurityPolicy;
GetMediaFileSystemsFunction::~GetMediaFileSystemsFunction() {} namespace MediaGalleries = extensions::api::experimental_media_galleries;
namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems;
MediaGalleriesGetMediaFileSystemsFunction::
~MediaGalleriesGetMediaFileSystemsFunction() {}
bool MediaGalleriesGetMediaFileSystemsFunction::RunImpl() {
scoped_ptr<GetMediaFileSystems::Params> params(
GetMediaFileSystems::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
MediaGalleries::GetMediaFileSystemsInteractivity interactive = "no";
if (params->details.get() && params->details->interactive.get())
interactive = *params->details->interactive;
if (interactive == "yes") {
// TODO(estade): implement.
} else if (interactive == "if_needed") {
// TODO(estade): implement.
} else if (interactive != "no") {
error_ = kInvalidInteractive;
return false;
}
bool GetMediaFileSystemsFunction::RunImpl() {
const content::RenderProcessHost* rph = render_view_host()->GetProcess(); const content::RenderProcessHost* rph = render_view_host()->GetProcess();
chrome::MediaFileSystemRegistry* media_fs_registry = chrome::MediaFileSystemRegistry* media_fs_registry =
MediaFileSystemRegistry::GetInstance(); MediaFileSystemRegistry::GetInstance();
...@@ -63,17 +90,10 @@ bool GetMediaFileSystemsFunction::RunImpl() { ...@@ -63,17 +90,10 @@ bool GetMediaFileSystemsFunction::RunImpl() {
return true; return true;
} }
OpenMediaGalleryManagerFunction::~OpenMediaGalleryManagerFunction() {} MediaGalleriesAssembleMediaFileFunction::
~MediaGalleriesAssembleMediaFileFunction() {}
bool OpenMediaGalleryManagerFunction::RunImpl() {
// TODO(vandebo) Open the Media Gallery Manager UI.
SetResult(Value::CreateNullValue());
return true;
}
AssembleMediaFileFunction::~AssembleMediaFileFunction() {}
bool AssembleMediaFileFunction::RunImpl() { bool MediaGalleriesAssembleMediaFileFunction::RunImpl() {
// TODO(vandebo) Update the metadata and return the new file. // TODO(vandebo) Update the metadata and return the new file.
SetResult(Value::CreateNullValue()); SetResult(Value::CreateNullValue());
return true; return true;
......
...@@ -5,43 +5,33 @@ ...@@ -5,43 +5,33 @@
// Defines the Chrome Extensions Media Galleries API functions for accessing // Defines the Chrome Extensions Media Galleries API functions for accessing
// user's media files, as specified in the extension API JSON. // user's media files, as specified in the extension API JSON.
#ifndef CHROME_BROWSER_EXTENSIONS_API_MEDIA_GALLERY_MEDIA_GALLERY_API_H_ #ifndef CHROME_BROWSER_EXTENSIONS_API_MEDIA_GALLERIES_MEDIA_GALLERIES_API_H_
#define CHROME_BROWSER_EXTENSIONS_API_MEDIA_GALLERY_MEDIA_GALLERY_API_H_ #define CHROME_BROWSER_EXTENSIONS_API_MEDIA_GALLERIES_MEDIA_GALLERIES_API_H_
#include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/extensions/extension_function.h"
namespace extensions { namespace extensions {
class GetMediaFileSystemsFunction : public SyncExtensionFunction { class MediaGalleriesGetMediaFileSystemsFunction : public SyncExtensionFunction {
public: public:
DECLARE_EXTENSION_FUNCTION_NAME( DECLARE_EXTENSION_FUNCTION_NAME(
"experimental.mediaGalleries.getMediaFileSystems") "experimental.mediaGalleries.getMediaFileSystems")
protected: protected:
virtual ~GetMediaFileSystemsFunction(); virtual ~MediaGalleriesGetMediaFileSystemsFunction();
virtual bool RunImpl() OVERRIDE; virtual bool RunImpl() OVERRIDE;
}; };
class OpenMediaGalleryManagerFunction : public SyncExtensionFunction { class MediaGalleriesAssembleMediaFileFunction : public SyncExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION_NAME(
"experimental.mediaGalleries.openMediaGalleryManager")
protected:
virtual ~OpenMediaGalleryManagerFunction();
virtual bool RunImpl() OVERRIDE;
};
class AssembleMediaFileFunction : public SyncExtensionFunction {
public: public:
DECLARE_EXTENSION_FUNCTION_NAME( DECLARE_EXTENSION_FUNCTION_NAME(
"experimental.mediaGalleries.assembleMediaFile") "experimental.mediaGalleries.assembleMediaFile")
protected: protected:
virtual ~AssembleMediaFileFunction(); virtual ~MediaGalleriesAssembleMediaFileFunction();
virtual bool RunImpl() OVERRIDE; virtual bool RunImpl() OVERRIDE;
}; };
} // namespace extensions } // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_MEDIA_GALLERY_MEDIA_GALLERY_API_H_ #endif // CHROME_BROWSER_EXTENSIONS_API_MEDIA_GALLERIES_MEDIA_GALLERIES_API_H_
...@@ -18,6 +18,6 @@ class ExperimentalExtensionApiTest : public ExtensionApiTest { ...@@ -18,6 +18,6 @@ class ExperimentalExtensionApiTest : public ExtensionApiTest {
} // namespace } // namespace
IN_PROC_BROWSER_TEST_F(ExperimentalExtensionApiTest, MediaGallery) { IN_PROC_BROWSER_TEST_F(ExperimentalExtensionApiTest, MediaGalleries) {
ASSERT_TRUE(RunExtensionTest("media_gallery")) << message_; ASSERT_TRUE(RunExtensionTest("media_galleries")) << message_;
} }
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include "chrome/browser/extensions/api/extension_action/extension_page_actions_api.h" #include "chrome/browser/extensions/api/extension_action/extension_page_actions_api.h"
#include "chrome/browser/extensions/api/extension_action/extension_script_badge_api.h" #include "chrome/browser/extensions/api/extension_action/extension_script_badge_api.h"
#include "chrome/browser/extensions/api/managed_mode/managed_mode_api.h" #include "chrome/browser/extensions/api/managed_mode/managed_mode_api.h"
#include "chrome/browser/extensions/api/media_gallery/media_gallery_api.h"
#include "chrome/browser/extensions/api/metrics/metrics.h" #include "chrome/browser/extensions/api/metrics/metrics.h"
#include "chrome/browser/extensions/api/offscreen_tabs/offscreen_tabs_api.h" #include "chrome/browser/extensions/api/offscreen_tabs/offscreen_tabs_api.h"
#include "chrome/browser/extensions/api/omnibox/omnibox_api.h" #include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
...@@ -491,11 +490,6 @@ void ExtensionFunctionRegistry::ResetFunctions() { ...@@ -491,11 +490,6 @@ void ExtensionFunctionRegistry::ResetFunctions() {
// Runtime // Runtime
RegisterFunction<extensions::RuntimeGetBackgroundPageFunction>(); RegisterFunction<extensions::RuntimeGetBackgroundPageFunction>();
// Media Gallery
RegisterFunction<extensions::GetMediaFileSystemsFunction>();
RegisterFunction<extensions::OpenMediaGalleryManagerFunction>();
RegisterFunction<extensions::AssembleMediaFileFunction>();
// Generated APIs // Generated APIs
extensions::api::GeneratedFunctionRegistry::RegisterAll(this); extensions::api::GeneratedFunctionRegistry::RegisterAll(this);
#endif // defined(ENABLE_EXTENSIONS) #endif // defined(ENABLE_EXTENSIONS)
......
...@@ -162,8 +162,8 @@ ...@@ -162,8 +162,8 @@
'browser/extensions/api/identity/identity_api.h', 'browser/extensions/api/identity/identity_api.h',
'browser/extensions/api/identity/web_auth_flow.cc', 'browser/extensions/api/identity/web_auth_flow.cc',
'browser/extensions/api/identity/web_auth_flow.h', 'browser/extensions/api/identity/web_auth_flow.h',
'browser/extensions/api/media_gallery/media_gallery_api.cc', 'browser/extensions/api/media_galleries/media_galleries_api.cc',
'browser/extensions/api/media_gallery/media_gallery_api.h', 'browser/extensions/api/media_galleries/media_galleries_api.h',
'browser/extensions/api/offscreen_tabs/offscreen_tabs_api.cc', 'browser/extensions/api/offscreen_tabs/offscreen_tabs_api.cc',
'browser/extensions/api/offscreen_tabs/offscreen_tabs_api.h', 'browser/extensions/api/offscreen_tabs/offscreen_tabs_api.h',
'browser/extensions/api/offscreen_tabs/offscreen_tabs_constants.cc', 'browser/extensions/api/offscreen_tabs/offscreen_tabs_constants.cc',
......
...@@ -2749,7 +2749,7 @@ ...@@ -2749,7 +2749,7 @@
'browser/extensions/api/identity/identity_apitest.cc', 'browser/extensions/api/identity/identity_apitest.cc',
'browser/extensions/api/idltest/idltest_apitest.cc', 'browser/extensions/api/idltest/idltest_apitest.cc',
'browser/extensions/api/managed_mode/managed_mode_apitest.cc', 'browser/extensions/api/managed_mode/managed_mode_apitest.cc',
'browser/extensions/api/media_gallery/media_gallery_apitest.cc', 'browser/extensions/api/media_galleries/media_galleries_apitest.cc',
'browser/extensions/api/metrics/metrics_apitest.cc', 'browser/extensions/api/metrics/metrics_apitest.cc',
'browser/extensions/api/offscreen_tabs/offscreen_tabs_apitest.cc', 'browser/extensions/api/offscreen_tabs/offscreen_tabs_apitest.cc',
'browser/extensions/api/omnibox/omnibox_apitest.cc', 'browser/extensions/api/omnibox/omnibox_apitest.cc',
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
'experimental_dns.idl', 'experimental_dns.idl',
'experimental_identity.idl', 'experimental_identity.idl',
'experimental_idltest.idl', 'experimental_idltest.idl',
'experimental_media_galleries.idl',
'experimental_serial.idl', 'experimental_serial.idl',
'experimental_socket.idl', 'experimental_socket.idl',
'experimental_usb.idl', 'experimental_usb.idl',
......
// Copyright (c) 2012 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.
// File-level comment to appease parser. Eventually this will not be necessary.
namespace experimental.mediaGalleries {
[inline_doc] enum GetMediaFileSystemsInteractivity {
// Do not act interactively.
no,
// Ask the user to manage permitted media galleries.
yes,
// Ask the user to manage permitted galleries only if the return set would
// otherwise be empty.
if_needed
};
[inline_doc] dictionary MediaFileSystemsDetails {
// Whether to prompt the user for additional media galleries before
// returning the permitted set. Default is silent.
GetMediaFileSystemsInteractivity? interactive;
};
callback MediaFileSystemsCallback =
void ([instanceOf=LocalFileSystem] optional object[] mediaFileSystems);
callback AssembleMediaFileCallback =
void ([instanceOf=Blob] optional object mediaFile);
interface Functions {
// Get the media galleries configured in this user agent. If none are
// configured or available, the callback will receive an empty array.
static void getMediaFileSystems(optional MediaFileSystemsDetails details,
MediaFileSystemsCallback callback);
// Create a new MediaFile setting the metadata in the Blob to the supplied
// values, overriding any existing metadata in the media file. If user agent
// does not recognize the Blob as a supported file format, it will fail.
// |mediaFileContents| : the media bytes.
// |metadata| : the metadata. TODO(estade): this should be
// [instanceOf=Metafile].
static void assembleMediaFile(
[instanceOf=Blob] object mediaFileContents,
object metadata,
AssembleMediaFileCallback callback);
// Get any thumbnails contained in the passed media file. The resulting
// directory reader refers to a virtual directory that can not be navigated
// to. If there are no thumbnails in the passed file entry, the virtual
// directory will have no entries.
// TODO(estade): The return type should be Directory. The argument type
// should be [instanceOf=FileEntry].
[nocompile] static object extractEmbeddedThumbnails(object mediaFile);
};
};
// Copyright (c) 2012 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.
[
{
"namespace": "experimental.mediaGalleries",
"functions": [
{
"name": "getMediaFileSystems",
"type": "function",
"description": "Get the media galleries configured in this user agent. If none are configured or available, the callback will receive an empty array.",
"parameters": [
{
"type": "function",
"name": "callback",
"parameters": [
{
"name": "mediaFileSystems",
"type": "array",
// TODO(vandebo) Specify the item type with isInstanceOf when
// http://crbug.com/119372 is resolved.
"items": { "type": "object"}
}
]
}
]
},
{
"name": "openMediaGalleryManager",
"type": "function",
"description": "The user can configure which directories on their computer are exposed as media galleries. Calling this function will cause the UI which configures media galleris to be visible in the current tab.",
"parameters": []
},
{
"name": "extractEmbeddedThumbnails",
"type": "function",
"description": "Get any thumbnails contained in the passed media file. The resulting directory reader refers to a virtual directory that can not be navigated to. If there are no thumbnails in the passed file entry, the virtual directory will have no entries.",
"parameters": [
{
// TODO(vandebo) Make this a non-optional FileEntry when
// http://crbug.com/119372 is resolved.
"type": "object",
"optional": true,
"name": "mediaFile"
}
],
"returns": {
"type": "object",
"optional": true
}
},
{
"name": "assembleMediaFile",
"type": "function",
"description": "Create a new MediaFile setting the metadata in the Blob to the supplied values, overriding any existing metadata in the media file. If user agent does not recognize the Blob as a supported file format, it will return null in the callback.",
// TODO(vandebo) Remove allowAmbiguousOptionalArguments when
// http://crbug.com/119372 is resolved.
"allowAmbiguousOptionalArguments": true,
"parameters": [
{
// TODO(vandebo) Make this a non-optional Blob when
// http://crbug.com/119372 is resolved.
"type": "object",
"optional": true,
"name": "mediaFileContents"
},
{
// TODO(vandebo) Make this a non-optional Metafile when
// http://crbug.com/119372 is resolved.
"type": "object",
"optional": true,
"name": "metadata"
},
{
"type": "function",
"name": "callback",
"parameters": [
{
"name": "mediaFile",
// TODO(vandebo) Make this an optional Blob when
// http://crbug.com/119372 is resolved.
"type": "object",
"optional": true
}
]
}
]
}
]
}
]
...@@ -379,8 +379,6 @@ void ExtensionAPI::InitDefaultConfiguration() { ...@@ -379,8 +379,6 @@ void ExtensionAPI::InitDefaultConfiguration() {
IDR_EXTENSION_API_JSON_EXPERIMENTAL_INPUT_VIRTUALKEYBOARD)); IDR_EXTENSION_API_JSON_EXPERIMENTAL_INPUT_VIRTUALKEYBOARD));
RegisterSchema("experimental.keybinding", ReadFromResource( RegisterSchema("experimental.keybinding", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_KEYBINDING)); IDR_EXTENSION_API_JSON_EXPERIMENTAL_KEYBINDING));
RegisterSchema("experimental.mediaGalleries", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_MEDIAGALLERIES));
RegisterSchema("experimental.offscreenTabs", ReadFromResource( RegisterSchema("experimental.offscreenTabs", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_OFFSCREENTABS)); IDR_EXTENSION_API_JSON_EXPERIMENTAL_OFFSCREENTABS));
RegisterSchema("experimental.processes", ReadFromResource( RegisterSchema("experimental.processes", ReadFromResource(
......
...@@ -45,7 +45,7 @@ var MODULE_SCHEMAS = [ ...@@ -45,7 +45,7 @@ var MODULE_SCHEMAS = [
'../../api/experimental_infobars.json', '../../api/experimental_infobars.json',
'../../api/experimental_input_virtual_keyboard.json', '../../api/experimental_input_virtual_keyboard.json',
'../../api/experimental_keybinding.json', '../../api/experimental_keybinding.json',
'../../api/experimental_media_galleries.json', '../../api/experimental_media_galleries.json', // autogenerated
'../../api/experimental_offscreen_tabs.json', '../../api/experimental_offscreen_tabs.json',
'../../api/experimental_processes.json', '../../api/experimental_processes.json',
'../../api/experimental_rlz.json', '../../api/experimental_rlz.json',
......
...@@ -156,7 +156,6 @@ ...@@ -156,7 +156,6 @@
"chrome.experimental.mediaGalleries.assembleMediaFile": "experimental.mediaGalleries.html#method-assembleMediaFile", "chrome.experimental.mediaGalleries.assembleMediaFile": "experimental.mediaGalleries.html#method-assembleMediaFile",
"chrome.experimental.mediaGalleries.extractEmbeddedThumbnails": "experimental.mediaGalleries.html#method-extractEmbeddedThumbnails", "chrome.experimental.mediaGalleries.extractEmbeddedThumbnails": "experimental.mediaGalleries.html#method-extractEmbeddedThumbnails",
"chrome.experimental.mediaGalleries.getMediaFileSystems": "experimental.mediaGalleries.html#method-getMediaFileSystems", "chrome.experimental.mediaGalleries.getMediaFileSystems": "experimental.mediaGalleries.html#method-getMediaFileSystems",
"chrome.experimental.mediaGalleries.openMediaGalleryManager": "experimental.mediaGalleries.html#method-openMediaGalleryManager",
"chrome.experimental.offscreenTabs.create": "experimental.offscreenTabs.html#method-create", "chrome.experimental.offscreenTabs.create": "experimental.offscreenTabs.html#method-create",
"chrome.experimental.offscreenTabs.get": "experimental.offscreenTabs.html#method-get", "chrome.experimental.offscreenTabs.get": "experimental.offscreenTabs.html#method-get",
"chrome.experimental.offscreenTabs.getAll": "experimental.offscreenTabs.html#method-getAll", "chrome.experimental.offscreenTabs.getAll": "experimental.offscreenTabs.html#method-getAll",
......
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
<include name="IDR_EXTENSION_API_JSON_EXPERIMENTAL_INFOBARS" file="extensions\api\experimental_infobars.json" type="BINDATA" /> <include name="IDR_EXTENSION_API_JSON_EXPERIMENTAL_INFOBARS" file="extensions\api\experimental_infobars.json" type="BINDATA" />
<include name="IDR_EXTENSION_API_JSON_EXPERIMENTAL_INPUT_VIRTUALKEYBOARD" file="extensions\api\experimental_input_virtual_keyboard.json" type="BINDATA" /> <include name="IDR_EXTENSION_API_JSON_EXPERIMENTAL_INPUT_VIRTUALKEYBOARD" file="extensions\api\experimental_input_virtual_keyboard.json" type="BINDATA" />
<include name="IDR_EXTENSION_API_JSON_EXPERIMENTAL_KEYBINDING" file="extensions\api\experimental_keybinding.json" type="BINDATA" /> <include name="IDR_EXTENSION_API_JSON_EXPERIMENTAL_KEYBINDING" file="extensions\api\experimental_keybinding.json" type="BINDATA" />
<include name="IDR_EXTENSION_API_JSON_EXPERIMENTAL_MEDIAGALLERIES" file="extensions\api\experimental_media_galleries.json" type="BINDATA" />
<include name="IDR_EXTENSION_API_JSON_EXPERIMENTAL_OFFSCREENTABS" file="extensions\api\experimental_offscreen_tabs.json" type="BINDATA" /> <include name="IDR_EXTENSION_API_JSON_EXPERIMENTAL_OFFSCREENTABS" file="extensions\api\experimental_offscreen_tabs.json" type="BINDATA" />
<include name="IDR_EXTENSION_API_JSON_EXPERIMENTAL_PROCESSES" file="extensions\api\experimental_processes.json" type="BINDATA" /> <include name="IDR_EXTENSION_API_JSON_EXPERIMENTAL_PROCESSES" file="extensions\api\experimental_processes.json" type="BINDATA" />
<include name="IDR_EXTENSION_API_JSON_EXPERIMENTAL_RECORD" file="extensions\api\experimental_record.json" type="BINDATA" /> <include name="IDR_EXTENSION_API_JSON_EXPERIMENTAL_RECORD" file="extensions\api\experimental_record.json" type="BINDATA" />
......
...@@ -31,7 +31,7 @@ chromeHidden.registerCustomHook('experimental.mediaGalleries', ...@@ -31,7 +31,7 @@ chromeHidden.registerCustomHook('experimental.mediaGalleries',
// synchronously return a result. The result object's state is computable // synchronously return a result. The result object's state is computable
// from the function's input. // from the function's input.
apiFunctions.setHandleRequest('extractEmbeddedThumbnails', apiFunctions.setHandleRequest('extractEmbeddedThumbnails',
function(fileEntry) { function(fileEntry) {
return mediaGalleriesNatives.ExtractEmbeddedThumbnails(fileEntry); return mediaGalleriesNatives.ExtractEmbeddedThumbnails(fileEntry);
}); });
}); });
...@@ -29,20 +29,15 @@ chrome.test.runTests([ ...@@ -29,20 +29,15 @@ chrome.test.runTests([
chrome.test.callbackPass(mediaFileSystemsListCallback)); chrome.test.callbackPass(mediaFileSystemsListCallback));
}, },
function openMediaGalleryManager() {
// Just confirm that the function exists.
chrome.test.assertTrue(mediaGalleries.openMediaGalleryManager !== null);
chrome.test.succeed()
},
function extractEmbeddedThumbnails() { function extractEmbeddedThumbnails() {
var result = mediaGalleries.extractEmbeddedThumbnails({}); var result = mediaGalleries.extractEmbeddedThumbnails({});
chrome.test.assertEq(null, result); chrome.test.assertEq(null, result);
chrome.test.succeed() chrome.test.succeed();
}, },
function assembleMediaFile() { function assembleMediaFile() {
mediaGalleries.assembleMediaFile( mediaGalleries.assembleMediaFile(
{}, {}, chrome.test.callbackPass(nullCallback)); new Blob, {},
chrome.test.callbackPass(nullCallback));
}, },
]); ]);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment