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