blob: 7750c37f5f7028bf2565cb3a7fbd7c98b3fdccba [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2021 The Chromium Authors
Sreeja Kamishetty9e1d0e732021-05-27 18:20:092// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/renderer_host/page_impl.h"
6
Matt Falkenhagenf78c2192021-07-24 02:01:437#include "base/barrier_closure.h"
Sky Malice3774b002022-10-21 19:18:408#include "base/feature_list.h"
Dominic Farolino5c606c12021-12-18 09:40:149#include "base/i18n/character_encoding.h"
Lingqi Chidcf722442021-09-02 01:47:1910#include "base/trace_event/optional_trace_event.h"
Sky Malice3774b002022-10-21 19:18:4011#include "cc/base/features.h"
Jeremy Roman4bd173d2021-06-17 00:05:4412#include "content/browser/manifest/manifest_manager_host.h"
Dave Tapuska9c9afe82021-06-22 19:07:4513#include "content/browser/renderer_host/frame_tree_node.h"
Jeremy Roman2d8dfe132021-07-06 20:51:2614#include "content/browser/renderer_host/page_delegate.h"
Julie Jeongeun Kim9e204512021-06-24 07:28:5415#include "content/browser/renderer_host/render_frame_host_delegate.h"
Sreeja Kamishetty9e1d0e732021-05-27 18:20:0916#include "content/browser/renderer_host/render_frame_host_impl.h"
Lingqi Chidcf722442021-09-02 01:47:1917#include "content/browser/renderer_host/render_frame_proxy_host.h"
Jeremy Roman2d8dfe132021-07-06 20:51:2618#include "content/browser/renderer_host/render_view_host_delegate.h"
Matt Falkenhagenf78c2192021-07-24 02:01:4319#include "content/browser/renderer_host/render_view_host_impl.h"
Jeremy Roman2d8dfe132021-07-06 20:51:2620#include "content/public/browser/render_view_host.h"
Camillia Smith Barnesddaf5b12023-01-24 00:06:3221#include "third_party/blink/public/common/features.h"
Sreeja Kamishetty0be3b1b2021-08-12 17:04:1522#include "third_party/blink/public/common/loader/loader_constants.h"
Julie Jeongeun Kim33ef6a22022-03-22 09:46:1123#include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
Jeremy Roman2d8dfe132021-07-06 20:51:2624#include "third_party/perfetto/include/perfetto/tracing/traced_value.h"
Sreeja Kamishetty9e1d0e732021-05-27 18:20:0925
26namespace content {
Jeremy Roman2d8dfe132021-07-06 20:51:2627
28PageImpl::PageImpl(RenderFrameHostImpl& rfh, PageDelegate& delegate)
Lingqi Chidcf722442021-09-02 01:47:1929 : main_document_(rfh),
30 delegate_(delegate),
Camillia Smith Barnesddaf5b12023-01-24 00:06:3231 text_autosizer_page_info_({0, 0, 1.f}) {
32 if (base::FeatureList::IsEnabled(
Camillia Smith Barnes477a3112023-02-28 19:06:3033 blink::features::kSharedStorageSelectURLLimit)) {
34 select_url_overall_budget_ = static_cast<double>(
35 blink::features::kSharedStorageSelectURLBitBudgetPerPageLoad.Get());
Camillia Smith Barnes74552232023-10-02 18:50:0736 select_url_max_bits_per_site_ = static_cast<double>(
37 blink::features::kSharedStorageSelectURLBitBudgetPerSitePerPageLoad
Camillia Smith Barnes477a3112023-02-28 19:06:3038 .Get());
39 }
Camillia Smith Barnesddaf5b12023-01-24 00:06:3240}
Sreeja Kamishetty9e1d0e732021-05-27 18:20:0941
Sreeja Kamishetty1b5c1432021-06-25 11:32:5942PageImpl::~PageImpl() {
43 // As SupportsUserData is a base class of PageImpl, Page members will be
44 // destroyed before running ~SupportsUserData, which would delete the
45 // associated PageUserData objects. Avoid this by calling ClearAllUserData
46 // explicitly here to ensure that the PageUserData destructors can access
47 // associated Page object.
48 ClearAllUserData();
49}
Sreeja Kamishetty9e1d0e732021-05-27 18:20:0950
Julie Jeongeun Kim9e204512021-06-24 07:28:5451const absl::optional<GURL>& PageImpl::GetManifestUrl() const {
Sreeja Kamishetty7c91ab22021-06-03 13:29:5252 return manifest_url_;
53}
54
Jeremy Roman4bd173d2021-06-17 00:05:4455void PageImpl::GetManifest(GetManifestCallback callback) {
56 ManifestManagerHost* manifest_manager_host =
Julie Jeongeun Kim33ef6a22022-03-22 09:46:1157 ManifestManagerHost::GetOrCreateForPage(*this);
Jeremy Roman4bd173d2021-06-17 00:05:4458 manifest_manager_host->GetManifest(std::move(callback));
59}
60
Julie Jeongeun Kimda529922023-01-13 02:59:5961bool PageImpl::IsPrimary() const {
Dominic Farolino4bc10ee2021-08-31 00:37:3662 // TODO(1244137): Check for portals as well, once they are migrated to MPArch.
Ali Hijazid87307d2022-11-07 20:15:0363 if (main_document_->IsFencedFrameRoot())
Dominic Farolino4bc10ee2021-08-31 00:37:3664 return false;
65
Ali Hijazid87307d2022-11-07 20:15:0366 return main_document_->lifecycle_state() ==
Khushalc5eaf222021-06-30 20:15:4867 RenderFrameHostImpl::LifecycleStateImpl::kActive;
Dave Tapuska9c9afe82021-06-22 19:07:4568}
69
Julie Jeongeun Kim9e204512021-06-24 07:28:5470void PageImpl::UpdateManifestUrl(const GURL& manifest_url) {
71 manifest_url_ = manifest_url;
72
73 // If |main_document_| is not active, the notification is sent on the page
74 // activation.
Ali Hijazid87307d2022-11-07 20:15:0375 if (!main_document_->IsActive())
Julie Jeongeun Kim9e204512021-06-24 07:28:5476 return;
77
Ali Hijazid87307d2022-11-07 20:15:0378 main_document_->delegate()->OnManifestUrlChanged(*this);
Julie Jeongeun Kim9e204512021-06-24 07:28:5479}
80
Jeremy Roman2d8dfe132021-07-06 20:51:2681void PageImpl::WriteIntoTrace(perfetto::TracedValue context) {
82 auto dict = std::move(context).WriteDictionary();
Ali Hijazid87307d2022-11-07 20:15:0383 dict.Add("main_document", *main_document_);
Jeremy Roman2d8dfe132021-07-06 20:51:2684}
85
Miyoung Shinfa182e472021-09-03 12:39:3286base::WeakPtr<Page> PageImpl::GetWeakPtr() {
87 return weak_factory_.GetWeakPtr();
88}
89
Yao Xiaoc7224362022-02-16 08:21:4090base::WeakPtr<PageImpl> PageImpl::GetWeakPtrImpl() {
91 return weak_factory_.GetWeakPtr();
92}
93
Kevin McNee3183a7792021-11-09 21:03:3694bool PageImpl::IsPageScaleFactorOne() {
Kevin McNeec4325ba2022-04-08 23:18:2395 return GetPageScaleFactor() == 1.f;
Kevin McNee3183a7792021-11-09 21:03:3696}
97
Takashi Toyoshima6c58bbd2023-05-19 09:41:3598const std::string& PageImpl::GetContentsMimeType() const {
99 return contents_mime_type_;
100}
101
Jeremy Roman2d8dfe132021-07-06 20:51:26102void PageImpl::OnFirstVisuallyNonEmptyPaint() {
103 did_first_visually_non_empty_paint_ = true;
Ali Hijazid87307d2022-11-07 20:15:03104 delegate_->OnFirstVisuallyNonEmptyPaint(*this);
Jeremy Roman2d8dfe132021-07-06 20:51:26105}
106
107void PageImpl::OnThemeColorChanged(const absl::optional<SkColor>& theme_color) {
108 main_document_theme_color_ = theme_color;
Ali Hijazid87307d2022-11-07 20:15:03109 delegate_->OnThemeColorChanged(*this);
Jeremy Roman2d8dfe132021-07-06 20:51:26110}
111
Aaron Krajeski628c58c2023-04-04 16:24:12112void PageImpl::DidChangeBackgroundColor(SkColor4f background_color,
Jeremy Roman2d8dfe132021-07-06 20:51:26113 bool color_adjust) {
Aaron Krajeski628c58c2023-04-04 16:24:12114 // TODO(aaronhk): This should remain an SkColor4f
115 main_document_background_color_ = background_color.toSkColor();
Ali Hijazid87307d2022-11-07 20:15:03116 delegate_->OnBackgroundColorChanged(*this);
Jeremy Roman2d8dfe132021-07-06 20:51:26117 if (color_adjust) {
118 // <meta name="color-scheme" content="dark"> may pass the dark canvas
119 // background before the first paint in order to avoid flashing the white
120 // background in between loading documents. If we perform a navigation
121 // within the same renderer process, we keep the content background from the
122 // previous page while rendering is blocked in the new page, but for cross
123 // process navigations we would paint the default background (typically
124 // white) while the rendering is blocked.
Ali Hijazid87307d2022-11-07 20:15:03125 main_document_->GetRenderWidgetHost()->GetView()->SetContentBackgroundColor(
Aaron Krajeski628c58c2023-04-04 16:24:12126 background_color.toSkColor());
Jeremy Roman2d8dfe132021-07-06 20:51:26127 }
128}
129
Michael Bai19f17a302021-12-08 04:08:33130void PageImpl::DidInferColorScheme(
131 blink::mojom::PreferredColorScheme color_scheme) {
132 main_document_inferred_color_scheme_ = color_scheme;
Ali Hijazid87307d2022-11-07 20:15:03133 delegate_->DidInferColorScheme(*this);
Michael Bai19f17a302021-12-08 04:08:33134}
135
Julie Jeongeun Kimd4597df12022-11-11 02:44:51136void PageImpl::NotifyPageBecameCurrent() {
137 if (!IsPrimary())
138 return;
139 delegate_->NotifyPageBecamePrimary(*this);
140}
141
Jeremy Roman2d8dfe132021-07-06 20:51:26142void PageImpl::SetContentsMimeType(std::string mime_type) {
143 contents_mime_type_ = std::move(mime_type);
144}
145
Lingqi Chidcf722442021-09-02 01:47:19146void PageImpl::OnTextAutosizerPageInfoChanged(
147 blink::mojom::TextAutosizerPageInfoPtr page_info) {
148 OPTIONAL_TRACE_EVENT0("content", "PageImpl::OnTextAutosizerPageInfoChanged");
149
Dave Tapuska2cf1f532022-08-10 15:30:49150 // Keep a copy of `page_info` in case we create a new `blink::WebView` before
151 // the next update, so that the PageImpl can tell the newly created
152 // `blink::WebView` about the autosizer info.
Lingqi Chidcf722442021-09-02 01:47:19153 text_autosizer_page_info_.main_frame_width = page_info->main_frame_width;
154 text_autosizer_page_info_.main_frame_layout_width =
155 page_info->main_frame_layout_width;
156 text_autosizer_page_info_.device_scale_adjustment =
157 page_info->device_scale_adjustment;
158
159 auto remote_frames_broadcast_callback = base::BindRepeating(
160 [](const blink::mojom::TextAutosizerPageInfo& page_info,
161 RenderFrameProxyHost* proxy_host) {
162 DCHECK(proxy_host);
163 proxy_host->GetAssociatedRemoteMainFrame()->UpdateTextAutosizerPageInfo(
164 page_info.Clone());
165 },
166 text_autosizer_page_info_);
167
Ali Hijazid87307d2022-11-07 20:15:03168 main_document_->frame_tree()
Lingqi Chidcf722442021-09-02 01:47:19169 ->root()
170 ->render_manager()
171 ->ExecuteRemoteFramesBroadcastMethod(
172 std::move(remote_frames_broadcast_callback),
Sharon Yang6b5313432023-03-24 05:07:57173 main_document_->GetSiteInstance()->group());
Lingqi Chidcf722442021-09-02 01:47:19174}
175
Matt Falkenhagenf78c2192021-07-24 02:01:43176void PageImpl::SetActivationStartTime(base::TimeTicks activation_start) {
177 DCHECK(!activation_start_time_for_prerendering_);
178 activation_start_time_for_prerendering_ = activation_start;
179}
180
181void PageImpl::ActivateForPrerendering(
Vladimir Levin48d51002023-02-27 17:23:27182 StoredPage::RenderViewHostImplSafeRefSet& render_view_hosts,
183 absl::optional<blink::ViewTransitionState> view_transition_state) {
Lingqi Chief8657c2023-06-08 08:40:31184 TRACE_EVENT0("navigation", "PageImpl::ActivateForPrerendering");
185
Matt Falkenhagenf78c2192021-07-24 02:01:43186 base::OnceClosure did_activate_render_views =
187 base::BindOnce(&PageImpl::DidActivateAllRenderViewsForPrerendering,
188 weak_factory_.GetWeakPtr());
189
190 base::RepeatingClosure barrier = base::BarrierClosure(
191 render_view_hosts.size(), std::move(did_activate_render_views));
Dave Tapuskac3e58352022-09-28 19:05:27192 for (const auto& rvh : render_view_hosts) {
Vladimir Levin48d51002023-02-27 17:23:27193 auto params = blink::mojom::PrerenderPageActivationParams::New();
194
Matt Falkenhagenf78c2192021-07-24 02:01:43195 // Only send navigation_start to the RenderViewHost for the main frame to
196 // avoid sending the info cross-origin. Only this RenderViewHost needs the
197 // info, as we expect the other RenderViewHosts are made for cross-origin
198 // iframes which have not yet loaded their document. To the renderer, it
199 // just looks like an ongoing navigation is happening in the frame and has
200 // not yet committed. These RenderViews still need to know about activation
201 // so their documents are created in the non-prerendered state once their
202 // navigation is committed.
Vladimir Levin48d51002023-02-27 17:23:27203 if (main_document_->GetRenderViewHost() == &*rvh) {
204 params->activation_start = *activation_start_time_for_prerendering_;
205 params->view_transition_state = std::move(view_transition_state);
206 }
Matt Falkenhagenf78c2192021-07-24 02:01:43207
Hiroki Nakagawaab53cd22022-04-13 19:18:02208 params->was_user_activated =
Ali Hijazid87307d2022-11-07 20:15:03209 main_document_->frame_tree_node()
210 ->has_received_user_gesture_before_nav()
Hiroki Nakagawaab53cd22022-04-13 19:18:02211 ? blink::mojom::WasActivatedOption::kYes
212 : blink::mojom::WasActivatedOption::kNo;
Hiroki Nakagawaab53cd22022-04-13 19:18:02213 rvh->ActivatePrerenderedPage(std::move(params), barrier);
Matt Falkenhagenf78c2192021-07-24 02:01:43214 }
215
216 // Prepare each RenderFrameHostImpl in this Page for activation.
217 // TODO(https://crbug.com/1232528): Currently we check GetPage() below because
218 // RenderFrameHostImpls may be in a different Page, if, e.g., they are in an
219 // inner WebContents. These are in a different FrameTree which might not know
220 // it is being prerendered. We should teach these FrameTrees that they are
221 // being prerendered, or ban inner FrameTrees in a prerendering page.
Ali Hijazid87307d2022-11-07 20:15:03222 main_document_->ForEachRenderFrameHostIncludingSpeculative(
Daniel Cheng982f2b22022-08-25 23:46:16223 [this](RenderFrameHostImpl* rfh) {
224 if (&rfh->GetPage() != this)
Matt Falkenhagenf78c2192021-07-24 02:01:43225 return;
226 rfh->RendererWillActivateForPrerendering();
Daniel Cheng982f2b22022-08-25 23:46:16227 });
Matt Falkenhagenf78c2192021-07-24 02:01:43228}
229
Sreeja Kamishetty81fbeefb2021-08-12 07:21:41230void PageImpl::MaybeDispatchLoadEventsOnPrerenderActivation() {
231 DCHECK(IsPrimary());
232
Sreeja Kamishetty0be3b1b2021-08-12 17:04:15233 // Dispatch LoadProgressChanged notification on activation with the
234 // prerender last load progress value if the value is not equal to
235 // blink::kFinalLoadProgress, whose notification is dispatched during call
236 // to DidStopLoading.
237 if (load_progress() != blink::kFinalLoadProgress)
Ali Hijazid87307d2022-11-07 20:15:03238 main_document_->DidChangeLoadProgress(load_progress());
Sreeja Kamishetty0be3b1b2021-08-12 17:04:15239
Sreeja Kamishetty49783302022-01-28 17:52:25240 // Dispatch PrimaryMainDocumentElementAvailable before dispatching following
241 // load complete events.
242 if (is_main_document_element_available())
Ali Hijazid87307d2022-11-07 20:15:03243 main_document_->MainDocumentElementAvailable(uses_temporary_zoom_level());
Sreeja Kamishettycd5560912021-11-22 11:54:53244
Ali Hijazid87307d2022-11-07 20:15:03245 main_document_->ForEachRenderFrameHost(
Daniel Cheng982f2b22022-08-25 23:46:16246 &RenderFrameHostImpl::MaybeDispatchDOMContentLoadedOnPrerenderActivation);
Sreeja Kamishetty81fbeefb2021-08-12 07:21:41247
248 if (is_on_load_completed_in_main_document())
Ali Hijazid87307d2022-11-07 20:15:03249 main_document_->DocumentOnLoadCompleted();
Sreeja Kamishetty81fbeefb2021-08-12 07:21:41250
Ali Hijazid87307d2022-11-07 20:15:03251 main_document_->ForEachRenderFrameHost(
Daniel Cheng982f2b22022-08-25 23:46:16252 &RenderFrameHostImpl::MaybeDispatchDidFinishLoadOnPrerenderActivation);
Sreeja Kamishetty81fbeefb2021-08-12 07:21:41253}
254
Matt Falkenhagenf78c2192021-07-24 02:01:43255void PageImpl::DidActivateAllRenderViewsForPrerendering() {
Lingqi Chief8657c2023-06-08 08:40:31256 TRACE_EVENT0("navigation",
257 "PageImpl::DidActivateAllRenderViewsForPrerendering");
258
Matt Falkenhagenf78c2192021-07-24 02:01:43259 // Tell each RenderFrameHostImpl in this Page that activation finished.
Hiroki Nakagawaf98d009a2023-03-16 02:26:10260 main_document_->ForEachRenderFrameHostIncludingSpeculative(
261 [this](RenderFrameHostImpl* rfh) {
262 if (&rfh->GetPage() != this) {
263 return;
264 }
265 rfh->RendererDidActivateForPrerendering();
266 });
Matt Falkenhagenf78c2192021-07-24 02:01:43267}
268
Sreeja Kamishetty1b5c1432021-06-25 11:32:59269RenderFrameHost& PageImpl::GetMainDocumentHelper() {
Ali Hijazid87307d2022-11-07 20:15:03270 return *main_document_;
Sreeja Kamishetty1b5c1432021-06-25 11:32:59271}
272
273RenderFrameHostImpl& PageImpl::GetMainDocument() const {
Ali Hijazid87307d2022-11-07 20:15:03274 return *main_document_;
Sreeja Kamishetty1b5c1432021-06-25 11:32:59275}
276
Yoshisato Yanagisawad016d62d32021-10-15 04:38:55277void PageImpl::UpdateBrowserControlsState(cc::BrowserControlsState constraints,
278 cc::BrowserControlsState current,
279 bool animate) {
280 // TODO(https://crbug.com/1154852): Asking for the LocalMainFrame interface
281 // before the RenderFrame is created is racy.
Nasko Oskov8b04f402022-05-26 23:29:56282 if (!GetMainDocument().IsRenderFrameLive())
Yoshisato Yanagisawad016d62d32021-10-15 04:38:55283 return;
284
Sky Malice3774b002022-10-21 19:18:40285 if (base::FeatureList::IsEnabled(
286 features::kUpdateBrowserControlsWithoutProxy)) {
287 GetMainDocument().GetRenderWidgetHost()->UpdateBrowserControlsState(
288 constraints, current, animate);
289 } else {
290 GetMainDocument().GetAssociatedLocalMainFrame()->UpdateBrowserControlsState(
291 constraints, current, animate);
292 }
Yoshisato Yanagisawad016d62d32021-10-15 04:38:55293}
294
Kevin McNeec4325ba2022-04-08 23:18:23295float PageImpl::GetPageScaleFactor() const {
296 return GetMainDocument().GetPageScaleFactor();
297}
298
Dominic Farolino5c606c12021-12-18 09:40:14299void PageImpl::UpdateEncoding(const std::string& encoding_name) {
300 if (encoding_name == last_reported_encoding_)
301 return;
302 last_reported_encoding_ = encoding_name;
303
304 canonical_encoding_ =
305 base::GetCanonicalEncodingNameByAliasName(encoding_name);
306}
307
Yoshisato Yanagisawa8ae0d112022-04-20 01:49:30308void PageImpl::NotifyVirtualKeyboardOverlayRect(
309 const gfx::Rect& keyboard_rect) {
310 // TODO(https://crbug.com/1317002): send notification to outer frames if
311 // needed.
David Bokand6e44055b2022-09-21 03:58:08312 DCHECK_EQ(virtual_keyboard_mode(),
313 ui::mojom::VirtualKeyboardMode::kOverlaysContent);
Yoshisato Yanagisawa8ae0d112022-04-20 01:49:30314 GetMainDocument().GetAssociatedLocalFrame()->NotifyVirtualKeyboardOverlayRect(
315 keyboard_rect);
316}
317
David Bokand6e44055b2022-09-21 03:58:08318void PageImpl::SetVirtualKeyboardMode(ui::mojom::VirtualKeyboardMode mode) {
319 if (virtual_keyboard_mode_ == mode)
320 return;
321
322 virtual_keyboard_mode_ = mode;
323
Ali Hijazid87307d2022-11-07 20:15:03324 delegate_->OnVirtualKeyboardModeChanged(*this);
David Bokand6e44055b2022-09-21 03:58:08325}
326
Yoshisato Yanagisawa668f8442022-04-20 04:45:58327base::flat_map<std::string, std::string> PageImpl::GetKeyboardLayoutMap() {
328 return GetMainDocument().GetRenderWidgetHost()->GetKeyboardLayoutMap();
329}
330
Camillia Smith Barnes74552232023-10-02 18:50:07331bool PageImpl::CheckAndMaybeDebitSelectURLBudgets(
332 const net::SchemefulSite& site,
333 double bits_to_charge) {
Camillia Smith Barnes477a3112023-02-28 19:06:30334 if (!select_url_overall_budget_) {
335 // The limits are not enabled.
Camillia Smith Barnes9d70e5ae82023-01-18 19:25:24336 return true;
337 }
338
Camillia Smith Barnes477a3112023-02-28 19:06:30339 // Return false if there is insufficient overall budget.
340 if (bits_to_charge > select_url_overall_budget_.value()) {
Camillia Smith Barnes574d4d42023-01-10 18:57:47341 return false;
342 }
343
Camillia Smith Barnes74552232023-10-02 18:50:07344 DCHECK(select_url_max_bits_per_site_);
Camillia Smith Barnes477a3112023-02-28 19:06:30345
Camillia Smith Barnes74552232023-10-02 18:50:07346 // Return false if the max bits per site is set to a value smaller than the
Camillia Smith Barnes477a3112023-02-28 19:06:30347 // current bits to charge.
Camillia Smith Barnes74552232023-10-02 18:50:07348 if (bits_to_charge > select_url_max_bits_per_site_.value()) {
Camillia Smith Barnes477a3112023-02-28 19:06:30349 return false;
350 }
351
Camillia Smith Barnes74552232023-10-02 18:50:07352 // Charge the per-site budget or return false if there is not enough.
353 auto it = select_url_per_site_budget_.find(site);
354 if (it == select_url_per_site_budget_.end()) {
355 select_url_per_site_budget_[site] =
356 select_url_max_bits_per_site_.value() - bits_to_charge;
Camillia Smith Barnes477a3112023-02-28 19:06:30357 } else if (bits_to_charge > it->second) {
Camillia Smith Barnes74552232023-10-02 18:50:07358 // There is insufficient per-site budget remaining.
Camillia Smith Barnes477a3112023-02-28 19:06:30359 return false;
360 } else {
361 it->second -= bits_to_charge;
362 }
363
364 // Charge the overall budget.
365 select_url_overall_budget_.value() -= bits_to_charge;
Camillia Smith Barnes574d4d42023-01-10 18:57:47366 return true;
367}
368
Sreeja Kamishetty9e1d0e732021-05-27 18:20:09369} // namespace content