Commit 8f8a21d0 authored by Richard Knoll's avatar Richard Knoll Committed by Commit Bot

[Nearby] Add empty chrome://nearby WebUI

This will host the Nearby Share UI for Chrome browser and ChromeOS. It
renders an example Polymer component that will be embedded in various
placed depending on the OS. CrOS will show it in a WebView in the native
Share Sheet and other platforms will have a standalone dialog. The page
is guarded by the kNearbySharing feature flag.

Bug: 1079271
Change-Id: Ia72ef02876518bd6dd594fa2b0fa88fbb8f4d3e2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2187614
Commit-Queue: Richard Knoll <knollr@chromium.org>
Reviewed-by: default avatardpapad <dpapad@chromium.org>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Reviewed-by: default avatarJames Vecore <vecore@google.com>
Cr-Commit-Position: refs/heads/master@{#781752}
parent be469741
...@@ -1314,6 +1314,7 @@ group("extra_resources") { ...@@ -1314,6 +1314,7 @@ group("extra_resources") {
"//chrome/browser/resources:history_resources", "//chrome/browser/resources:history_resources",
"//chrome/browser/resources:local_ntp_resources", "//chrome/browser/resources:local_ntp_resources",
"//chrome/browser/resources:nearby_internals_resources", "//chrome/browser/resources:nearby_internals_resources",
"//chrome/browser/resources:nearby_share_dialog_resources",
"//chrome/browser/resources:new_tab_page_resources", "//chrome/browser/resources:new_tab_page_resources",
"//chrome/browser/resources:settings_resources", "//chrome/browser/resources:settings_resources",
] ]
......
...@@ -36,6 +36,7 @@ if (enable_js_type_check) { ...@@ -36,6 +36,7 @@ if (enable_js_type_check) {
"management:closure_compile", "management:closure_compile",
"media_router:closure_compile", "media_router:closure_compile",
"nearby_internals:closure_compile", "nearby_internals:closure_compile",
"nearby_share:closure_compile",
"new_tab_page:closure_compile", "new_tab_page:closure_compile",
"ntp4:closure_compile", "ntp4:closure_compile",
"omnibox:closure_compile", "omnibox:closure_compile",
...@@ -215,6 +216,26 @@ if (!is_android) { ...@@ -215,6 +216,26 @@ if (!is_android) {
output_dir = "$root_gen_dir/chrome" output_dir = "$root_gen_dir/chrome"
} }
grit("nearby_share_dialog_resources") {
source = "nearby_share/nearby_share_dialog_resources.grd"
grit_flags = [
"-E",
"root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
]
deps = [ "//chrome/browser/resources/nearby_share:web_components" ]
defines = chrome_grit_defines
outputs = [
"grit/nearby_share_dialog_resources.h",
"grit/nearby_share_dialog_resources_map.cc",
"grit/nearby_share_dialog_resources_map.h",
"nearby_share_dialog_resources.pak",
]
output_dir = "$root_gen_dir/chrome"
}
grit("new_tab_page_resources") { grit("new_tab_page_resources") {
if (optimize_webui) { if (optimize_webui) {
source = "new_tab_page/new_tab_page_resources_vulcanized.grd" source = "new_tab_page/new_tab_page_resources_vulcanized.grd"
......
# Copyright 2020 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.
import("//third_party/closure_compiler/compile_js.gni")
import("//tools/polymer/html_to_js.gni")
js_type_check("closure_compile") {
is_polymer3 = true
deps = [
":app",
":nearby_discovery_page",
]
}
js_library("app") {
deps = [
":nearby_discovery_page",
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
"//ui/webui/resources/cr_elements/cr_view_manager:cr_view_manager.m",
]
}
js_library("nearby_discovery_page") {
deps = [
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
}
html_to_js("web_components") {
js_files = [
"app.js",
"nearby_discovery_page.js",
]
}
file://chrome/browser/nearby_sharing/OWNERS
# COMPONENT: UI>Browser>Sharing>Nearby
<cr-view-manager id="viewManager">
<nearby-discovery-page id="[[Page.DISCOVERY]]" slot="view" class="active">
</nearby-discovery-page>
</cr-view-manager>
// Copyright 2020 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.
/**
* @fileoverview The 'nearby-share' component is the entry point for the Nearby
* Share flow. It is used as a standalone dialog via chrome://nearby and as part
* of the ChromeOS share sheet.
*/
import 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.m.js';
import './nearby_discovery_page.js';
import './strings.m.js';
import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
/** @enum {string} */
const Page = {
DISCOVERY: 'discovery',
};
Polymer({
is: 'nearby-share-app',
_template: html`{__html_template__}`,
properties: {
/** Mirroring the enum so that it can be used from HTML bindings. */
Page: {
type: Object,
value: Page,
},
},
});
// Copyright 2020 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.
/**
* @fileoverview The 'nearby-discovery-page' component shows the discovery UI of
* the Nearby Share flow. It shows a list of devices to select from.
*/
import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
Polymer({
is: 'nearby-discovery-page',
_template: html`{__html_template__}`,
});
<!doctype html>
<html dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
<meta charset="utf-8">
<base href="chrome://nearby">
<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
<style>
body {
margin: 0;
}
</style>
</head>
<body>
<nearby-share-app></nearby-share-app>
<script type="module" src="app.js"></script>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
<outputs>
<output filename="grit/nearby_share_dialog_resources.h" type="rc_header">
<emit emit_type='prepend'></emit>
</output>
<output filename="grit/nearby_share_dialog_resources_map.cc"
type="resource_file_map_source" />
<output filename="grit/nearby_share_dialog_resources_map.h"
type="resource_map_header" />
<output filename="nearby_share_dialog_resources.pak" type="data_package" />
</outputs>
<release seq="1">
<includes>
<include name="IDR_NEARBY_SHARE_NEARBY_SHARE_DIALOG_HTML"
file="nearby_share_dialog.html"
type="BINDATA"/>
<!-- Generated Polymer 3 elements -->
<include name="IDR_NEARBY_SHARE_APP_JS"
file="${root_gen_dir}/chrome/browser/resources/nearby_share/app.js"
use_base_dir="false" type="BINDATA"/>
<include name="IDR_NEARBY_SHARE_NEARBY_DISCOVERY_PAGE_JS"
file="${root_gen_dir}/chrome/browser/resources/nearby_share/nearby_discovery_page.js"
use_base_dir="false" type="BINDATA"/>
</includes>
</release>
</grit>
...@@ -1313,6 +1313,8 @@ static_library("ui") { ...@@ -1313,6 +1313,8 @@ static_library("ui") {
"webui/media_router/web_contents_display_observer.h", "webui/media_router/web_contents_display_observer.h",
"webui/nearby_internals/nearby_internals_ui.cc", "webui/nearby_internals/nearby_internals_ui.cc",
"webui/nearby_internals/nearby_internals_ui.h", "webui/nearby_internals/nearby_internals_ui.h",
"webui/nearby_share/nearby_share_dialog_ui.cc",
"webui/nearby_share/nearby_share_dialog_ui.h",
"webui/new_tab_page/new_tab_page_handler.cc", "webui/new_tab_page/new_tab_page_handler.cc",
"webui/new_tab_page/new_tab_page_handler.h", "webui/new_tab_page/new_tab_page_handler.h",
"webui/new_tab_page/new_tab_page_ui.cc", "webui/new_tab_page/new_tab_page_ui.cc",
......
...@@ -144,6 +144,7 @@ ...@@ -144,6 +144,7 @@
#include "chrome/browser/ui/webui/inspect_ui.h" #include "chrome/browser/ui/webui/inspect_ui.h"
#include "chrome/browser/ui/webui/management_ui.h" #include "chrome/browser/ui/webui/management_ui.h"
#include "chrome/browser/ui/webui/media_router/media_router_internals_ui.h" #include "chrome/browser/ui/webui/media_router/media_router_internals_ui.h"
#include "chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h"
#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h" #include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h"
#include "chrome/browser/ui/webui/ntp/new_tab_ui.h" #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
#include "chrome/browser/ui/webui/page_not_available_for_guest/page_not_available_for_guest_ui.h" #include "chrome/browser/ui/webui/page_not_available_for_guest/page_not_available_for_guest_ui.h"
...@@ -523,9 +524,13 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui, ...@@ -523,9 +524,13 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
// Identity API is not available on Android. // Identity API is not available on Android.
if (url.host_piece() == chrome::kChromeUIIdentityInternalsHost) if (url.host_piece() == chrome::kChromeUIIdentityInternalsHost)
return &NewWebUI<IdentityInternalsUI>; return &NewWebUI<IdentityInternalsUI>;
if (url.host_piece() == chrome::kChromeUINearbyInternalsHost && if (base::FeatureList::IsEnabled(features::kNearbySharing)) {
base::FeatureList::IsEnabled(features::kNearbySharing)) { if (url.host_piece() == chrome::kChromeUINearbyInternalsHost)
return &NewWebUI<NearbyInternalsUI>; return &NewWebUI<NearbyInternalsUI>;
if (url.host_piece() == chrome::kChromeUINearbyShareHost &&
profile->IsRegularProfile()) {
return &NewWebUI<nearby_share::NearbyShareDialogUI>;
}
} }
if (url.host_piece() == chrome::kChromeUINewTabHost) if (url.host_piece() == chrome::kChromeUINewTabHost)
return &NewWebUI<NewTabUI>; return &NewWebUI<NewTabUI>;
......
file://chrome/browser/nearby_sharing/OWNERS
# COMPONENT: UI>Browser>Sharing>Nearby
// Copyright 2020 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/ui/webui/nearby_share/nearby_share_dialog_ui.h"
#include <string>
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/nearby_share_dialog_resources.h"
#include "chrome/grit/nearby_share_dialog_resources_map.h"
#include "chrome/grit/theme_resources.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/webui/web_ui_util.h"
namespace {
constexpr char kNearbyShareGeneratedPath[] =
"@out_folder@/gen/chrome/browser/resources/nearby_share/";
} // namespace
namespace nearby_share {
NearbyShareDialogUI::NearbyShareDialogUI(content::WebUI* web_ui)
: ui::MojoWebUIController(web_ui) {
Profile* profile = Profile::FromWebUI(web_ui);
// Nearby Share is not available to incognito or guest profiles.
DCHECK(profile->IsRegularProfile());
content::WebUIDataSource* html_source =
content::WebUIDataSource::Create(chrome::kChromeUINearbyShareHost);
webui::SetupWebUIDataSource(html_source,
base::make_span(kNearbyShareDialogResources,
kNearbyShareDialogResourcesSize),
kNearbyShareGeneratedPath,
IDR_NEARBY_SHARE_NEARBY_SHARE_DIALOG_HTML);
content::WebUIDataSource::Add(profile, html_source);
}
NearbyShareDialogUI::~NearbyShareDialogUI() = default;
WEB_UI_CONTROLLER_TYPE_IMPL(NearbyShareDialogUI)
} // namespace nearby_share
// Copyright 2020 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_UI_WEBUI_NEARBY_SHARE_NEARBY_SHARE_DIALOG_UI_H_
#define CHROME_BROWSER_UI_WEBUI_NEARBY_SHARE_NEARBY_SHARE_DIALOG_UI_H_
#include "ui/webui/mojo_web_ui_controller.h"
namespace nearby_share {
// The WebUI controller for chrome://nearby.
class NearbyShareDialogUI : public ui::MojoWebUIController {
public:
explicit NearbyShareDialogUI(content::WebUI* web_ui);
NearbyShareDialogUI(const NearbyShareDialogUI&) = delete;
NearbyShareDialogUI& operator=(const NearbyShareDialogUI&) = delete;
~NearbyShareDialogUI() override;
private:
WEB_UI_CONTROLLER_TYPE_DECL();
};
} // namespace nearby_share
#endif // CHROME_BROWSER_UI_WEBUI_NEARBY_SHARE_NEARBY_SHARE_DIALOG_UI_H_
// Copyright 2020 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 "base/test/scoped_feature_list.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "url/gurl.h"
namespace {
class NearbyShareDialogUITest : public InProcessBrowserTest {
public:
NearbyShareDialogUITest() {
scoped_feature_list_.InitWithFeatures({features::kNearbySharing}, {});
}
~NearbyShareDialogUITest() override = default;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
} // namespace
IN_PROC_BROWSER_TEST_F(NearbyShareDialogUITest, RendersComponent) {
// First, check that navigation succeeds.
GURL kUrl(content::GetWebUIURL(chrome::kChromeUINearbyShareHost));
ui_test_utils::NavigateToURL(browser(), kUrl);
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
EXPECT_EQ(kUrl, web_contents->GetLastCommittedURL());
EXPECT_FALSE(web_contents->IsCrashed());
// Assert that we render the nearby-share-app component.
int num_nearby_share_app = -1;
ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
web_contents,
"domAutomationController.send("
"document.getElementsByTagName('nearby-share-app').length)",
&num_nearby_share_app));
EXPECT_EQ(1, num_nearby_share_app);
}
...@@ -136,6 +136,7 @@ template("chrome_extra_paks") { ...@@ -136,6 +136,7 @@ template("chrome_extra_paks") {
"$root_gen_dir/chrome/history_resources.pak", "$root_gen_dir/chrome/history_resources.pak",
"$root_gen_dir/chrome/local_ntp_resources.pak", "$root_gen_dir/chrome/local_ntp_resources.pak",
"$root_gen_dir/chrome/nearby_internals_resources.pak", "$root_gen_dir/chrome/nearby_internals_resources.pak",
"$root_gen_dir/chrome/nearby_share_dialog_resources.pak",
"$root_gen_dir/chrome/new_tab_page_resources.pak", "$root_gen_dir/chrome/new_tab_page_resources.pak",
"$root_gen_dir/chrome/settings_resources.pak", "$root_gen_dir/chrome/settings_resources.pak",
"$root_gen_dir/content/browser/devtools/devtools_resources.pak", "$root_gen_dir/content/browser/devtools/devtools_resources.pak",
...@@ -150,6 +151,7 @@ template("chrome_extra_paks") { ...@@ -150,6 +151,7 @@ template("chrome_extra_paks") {
"//chrome/browser/resources:history_resources", "//chrome/browser/resources:history_resources",
"//chrome/browser/resources:local_ntp_resources", "//chrome/browser/resources:local_ntp_resources",
"//chrome/browser/resources:nearby_internals_resources", "//chrome/browser/resources:nearby_internals_resources",
"//chrome/browser/resources:nearby_share_dialog_resources",
"//chrome/browser/resources:new_tab_page_resources", "//chrome/browser/resources:new_tab_page_resources",
"//chrome/browser/resources:settings_resources", "//chrome/browser/resources:settings_resources",
"//content/browser/devtools:devtools_resources", "//content/browser/devtools:devtools_resources",
......
...@@ -325,6 +325,11 @@ const char kChromeUIHatsURL[] = "chrome://hats/"; ...@@ -325,6 +325,11 @@ const char kChromeUIHatsURL[] = "chrome://hats/";
const char kChromeUIProfilePickerHost[] = "profile-picker"; const char kChromeUIProfilePickerHost[] = "profile-picker";
#endif #endif
#if !defined(OS_ANDROID)
const char kChromeUINearbyShareHost[] = "nearby";
const char kChromeUINearbyShareURL[] = "chrome://nearby/";
#endif // !defined(OS_ANDROID)
#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
const char kChromeUILinuxProxyConfigHost[] = "linux-proxy-config"; const char kChromeUILinuxProxyConfigHost[] = "linux-proxy-config";
#endif #endif
......
...@@ -284,6 +284,11 @@ extern const char kChromeUIHatsURL[]; ...@@ -284,6 +284,11 @@ extern const char kChromeUIHatsURL[];
extern const char kChromeUIProfilePickerHost[]; extern const char kChromeUIProfilePickerHost[];
#endif #endif
#if !defined(OS_ANDROID)
extern const char kChromeUINearbyShareHost[];
extern const char kChromeUINearbyShareURL[];
#endif // !defined(OS_CHROMEOS)
#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
extern const char kChromeUILinuxProxyConfigHost[]; extern const char kChromeUILinuxProxyConfigHost[];
#endif #endif
......
...@@ -1351,6 +1351,7 @@ if (!is_android) { ...@@ -1351,6 +1351,7 @@ if (!is_android) {
"../browser/ui/webui/management_a11y_browsertest.cc", "../browser/ui/webui/management_a11y_browsertest.cc",
"../browser/ui/webui/management_a11y_browsertest.h", "../browser/ui/webui/management_a11y_browsertest.h",
"../browser/ui/webui/management_ui_browsertest.cc", "../browser/ui/webui/management_ui_browsertest.cc",
"../browser/ui/webui/nearby_share/nearby_share_dialog_ui_browsertest.cc",
"../browser/ui/webui/net_internals/net_internals_ui_browsertest.cc", "../browser/ui/webui/net_internals/net_internals_ui_browsertest.cc",
"../browser/ui/webui/net_internals/net_internals_ui_browsertest.h", "../browser/ui/webui/net_internals/net_internals_ui_browsertest.h",
"../browser/ui/webui/new_tab_page/webui_ntp_browsertest.cc", "../browser/ui/webui/new_tab_page/webui_ntp_browsertest.cc",
......
...@@ -146,59 +146,62 @@ ...@@ -146,59 +146,62 @@
"chrome/browser/resources/local_ntp/local_ntp_resources.grd": { "chrome/browser/resources/local_ntp/local_ntp_resources.grd": {
"includes": [1620], "includes": [1620],
}, },
"chrome/browser/resources/nearby_internals/nearby_internals_resources.grd": { "chrome/browser/resources/nearby_internals/nearby_internals_resources.grd": {
"includes": [1630], "includes": [1630],
}, },
"chrome/browser/resources/nearby_share/nearby_share_dialog_resources.grd": {
"includes": [1650],
},
"chrome/browser/resources/new_tab_page/new_tab_page_resources_vulcanized.grd": { "chrome/browser/resources/new_tab_page/new_tab_page_resources_vulcanized.grd": {
"includes": [1640], "includes": [1660],
}, },
"chrome/browser/resources/new_tab_page/new_tab_page_resources.grd": { "chrome/browser/resources/new_tab_page/new_tab_page_resources.grd": {
"includes": [1660], "includes": [1680],
}, },
"chrome/browser/resources/print_preview/print_preview_resources_vulcanized.grd": { "chrome/browser/resources/print_preview/print_preview_resources_vulcanized.grd": {
"includes": [1680], "includes": [1700],
}, },
"chrome/browser/resources/print_preview/print_preview_resources.grd": { "chrome/browser/resources/print_preview/print_preview_resources.grd": {
"includes": [1700], "includes": [1720],
"structures": [1720], "structures": [1740],
}, },
"chrome/browser/resources/print_preview/print_preview_pdf_resources.grd": { "chrome/browser/resources/print_preview/print_preview_pdf_resources.grd": {
"includes": [1730], "includes": [1750],
}, },
"chrome/browser/resources/settings/os_settings_resources_vulcanized.grd": { "chrome/browser/resources/settings/os_settings_resources_vulcanized.grd": {
"includes": [1740], "includes": [1760],
}, },
"chrome/browser/resources/settings/os_settings_resources.grd": { "chrome/browser/resources/settings/os_settings_resources.grd": {
"includes": [1760], "includes": [1780],
"structures": [1780], "structures": [1800],
}, },
"chrome/browser/resources/settings/settings_resources_vulcanized.grd": { "chrome/browser/resources/settings/settings_resources_vulcanized.grd": {
"includes": [1800], "includes": [1820],
}, },
"chrome/browser/resources/settings/settings_resources.grd": { "chrome/browser/resources/settings/settings_resources.grd": {
"includes": [1810], "includes": [1830],
"structures": [1820], "structures": [1840],
}, },
"chrome/browser/resources/signin/profile_picker/profile_picker_resources.grd": { "chrome/browser/resources/signin/profile_picker/profile_picker_resources.grd": {
"includes": [1840], "includes": [1860],
"structures": [1850], "structures": [1870],
}, },
"chrome/browser/resources/tab_strip/tab_strip_resources.grd": { "chrome/browser/resources/tab_strip/tab_strip_resources.grd": {
"structures": [1860], "structures": [1880],
"includes": [1880], "includes": [1900],
}, },
"chrome/browser/resources/welcome/welcome_resources.grd": { "chrome/browser/resources/welcome/welcome_resources.grd": {
"includes": [1900], "includes": [1920],
"structures": [1920], "structures": [1940],
}, },
"chrome/browser/supervised_user/supervised_user_unscaled_resources.grd": { "chrome/browser/supervised_user/supervised_user_unscaled_resources.grd": {
"includes": [1930], "includes": [1950],
}, },
"chrome/browser/test_dummy/internal/android/resources/resources.grd": { "chrome/browser/test_dummy/internal/android/resources/resources.grd": {
"includes": [1940], "includes": [1960],
}, },
"chrome/browser/vr/testapp/vr_testapp_resources.grd": { "chrome/browser/vr/testapp/vr_testapp_resources.grd": {
"includes": [1950], "includes": [1970],
}, },
# END chrome/browser section. # END chrome/browser section.
......
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