1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/modules/app_banner/before_install_prompt_event.h"
6 
7 #include "third_party/blink/renderer/bindings/modules/v8/v8_before_install_prompt_event_init.h"
8 #include "third_party/blink/renderer/core/dom/document.h"
9 #include "third_party/blink/renderer/core/dom/dom_exception.h"
10 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
11 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
12 #include "third_party/blink/renderer/core/frame/web_feature.h"
13 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
14 #include "third_party/blink/renderer/platform/heap/heap.h"
15 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
16 
17 namespace blink {
18 
BeforeInstallPromptEvent(const AtomicString & name,ExecutionContext & context,mojo::PendingRemote<mojom::blink::AppBannerService> service_remote,mojo::PendingReceiver<mojom::blink::AppBannerEvent> event_receiver,const Vector<String> & platforms)19 BeforeInstallPromptEvent::BeforeInstallPromptEvent(
20     const AtomicString& name,
21     ExecutionContext& context,
22     mojo::PendingRemote<mojom::blink::AppBannerService> service_remote,
23     mojo::PendingReceiver<mojom::blink::AppBannerEvent> event_receiver,
24     const Vector<String>& platforms)
25     : Event(name, Bubbles::kNo, Cancelable::kYes),
26       ExecutionContextClient(&context),
27       banner_service_remote_(&context),
28       receiver_(this, &context),
29       platforms_(platforms),
30       user_choice_(MakeGarbageCollected<UserChoiceProperty>(&context)) {
31   banner_service_remote_.Bind(
32       std::move(service_remote),
33       context.GetTaskRunner(TaskType::kApplicationLifeCycle));
34   receiver_.Bind(std::move(event_receiver),
35                  context.GetTaskRunner(TaskType::kApplicationLifeCycle));
36   DCHECK(banner_service_remote_.is_bound());
37   DCHECK(receiver_.is_bound());
38   UseCounter::Count(context, WebFeature::kBeforeInstallPromptEvent);
39 }
40 
BeforeInstallPromptEvent(ExecutionContext * execution_context,const AtomicString & name,const BeforeInstallPromptEventInit * init)41 BeforeInstallPromptEvent::BeforeInstallPromptEvent(
42     ExecutionContext* execution_context,
43     const AtomicString& name,
44     const BeforeInstallPromptEventInit* init)
45     : Event(name, init),
46       ExecutionContextClient(execution_context),
47       banner_service_remote_(execution_context),
48       receiver_(this, execution_context) {
49   if (init->hasPlatforms())
50     platforms_ = init->platforms();
51 }
52 
53 BeforeInstallPromptEvent::~BeforeInstallPromptEvent() = default;
54 
platforms() const55 Vector<String> BeforeInstallPromptEvent::platforms() const {
56   return platforms_;
57 }
58 
userChoice(ScriptState * script_state,ExceptionState & exception_state)59 ScriptPromise BeforeInstallPromptEvent::userChoice(
60     ScriptState* script_state,
61     ExceptionState& exception_state) {
62   UseCounter::Count(ExecutionContext::From(script_state),
63                     WebFeature::kBeforeInstallPromptEventUserChoice);
64   // |m_binding| must be bound to allow the AppBannerService to resolve the
65   // userChoice promise.
66   if (user_choice_ && receiver_.is_bound())
67     return user_choice_->Promise(script_state->World());
68   exception_state.ThrowDOMException(
69       DOMExceptionCode::kInvalidStateError,
70       "userChoice cannot be accessed on this event.");
71   return ScriptPromise();
72 }
73 
prompt(ScriptState * script_state,ExceptionState & exception_state)74 ScriptPromise BeforeInstallPromptEvent::prompt(
75     ScriptState* script_state,
76     ExceptionState& exception_state) {
77   // |m_bannerService| must be bound to allow us to inform the AppBannerService
78   // to display the banner now.
79   if (!banner_service_remote_.is_bound()) {
80     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
81                                       "The prompt() method cannot be called.");
82     return ScriptPromise();
83   }
84 
85   LocalDOMWindow* window = LocalDOMWindow::From(script_state);
86   if (!LocalFrame::ConsumeTransientUserActivation(window ? window->GetFrame()
87                                                          : nullptr)) {
88     exception_state.ThrowDOMException(
89         DOMExceptionCode::kNotAllowedError,
90         "The prompt() method must be called with a user gesture");
91     return ScriptPromise();
92   }
93 
94   UseCounter::Count(window, WebFeature::kBeforeInstallPromptEventPrompt);
95   banner_service_remote_->DisplayAppBanner();
96   return user_choice_->Promise(script_state->World());
97 }
98 
InterfaceName() const99 const AtomicString& BeforeInstallPromptEvent::InterfaceName() const {
100   return event_interface_names::kBeforeInstallPromptEvent;
101 }
102 
preventDefault()103 void BeforeInstallPromptEvent::preventDefault() {
104   Event::preventDefault();
105   if (target()) {
106     UseCounter::Count(target()->GetExecutionContext(),
107                       WebFeature::kBeforeInstallPromptEventPreventDefault);
108   }
109 }
110 
HasPendingActivity() const111 bool BeforeInstallPromptEvent::HasPendingActivity() const {
112   return user_choice_ &&
113          user_choice_->GetState() == UserChoiceProperty::kPending;
114 }
115 
BannerAccepted(const String & platform)116 void BeforeInstallPromptEvent::BannerAccepted(const String& platform) {
117   AppBannerPromptResult* result = AppBannerPromptResult::Create();
118   result->setPlatform(platform);
119   result->setOutcome("accepted");
120   user_choice_->Resolve(result);
121 }
122 
BannerDismissed()123 void BeforeInstallPromptEvent::BannerDismissed() {
124   AppBannerPromptResult* result = AppBannerPromptResult::Create();
125   result->setPlatform(g_empty_atom);
126   result->setOutcome("dismissed");
127   user_choice_->Resolve(result);
128 }
129 
Trace(Visitor * visitor) const130 void BeforeInstallPromptEvent::Trace(Visitor* visitor) const {
131   visitor->Trace(banner_service_remote_);
132   visitor->Trace(receiver_);
133   visitor->Trace(user_choice_);
134   Event::Trace(visitor);
135   ExecutionContextClient::Trace(visitor);
136 }
137 
138 }  // namespace blink
139