OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 25 matching lines...) Expand all Loading... |
36 #include "bindings/core/v8/ScriptState.h" | 36 #include "bindings/core/v8/ScriptState.h" |
37 #include "bindings/core/v8/ScriptValue.h" | 37 #include "bindings/core/v8/ScriptValue.h" |
38 #include "bindings/core/v8/ScriptWrappable.h" | 38 #include "bindings/core/v8/ScriptWrappable.h" |
39 #include "bindings/core/v8/SerializedScriptValueFactory.h" | 39 #include "bindings/core/v8/SerializedScriptValueFactory.h" |
40 #include "core/dom/Document.h" | 40 #include "core/dom/Document.h" |
41 #include "core/dom/ExecutionContext.h" | 41 #include "core/dom/ExecutionContext.h" |
42 #include "core/dom/ExecutionContextTask.h" | 42 #include "core/dom/ExecutionContextTask.h" |
43 #include "core/dom/ScopedWindowFocusAllowedIndicator.h" | 43 #include "core/dom/ScopedWindowFocusAllowedIndicator.h" |
44 #include "core/events/Event.h" | 44 #include "core/events/Event.h" |
45 #include "core/frame/UseCounter.h" | 45 #include "core/frame/UseCounter.h" |
| 46 #include "modules/notifications/NotificationAction.h" |
| 47 #include "modules/notifications/NotificationData.h" |
46 #include "modules/notifications/NotificationOptions.h" | 48 #include "modules/notifications/NotificationOptions.h" |
47 #include "modules/notifications/NotificationPermissionClient.h" | 49 #include "modules/notifications/NotificationPermissionClient.h" |
48 #include "platform/RuntimeEnabledFeatures.h" | 50 #include "platform/RuntimeEnabledFeatures.h" |
49 #include "platform/UserGestureIndicator.h" | 51 #include "platform/UserGestureIndicator.h" |
50 #include "public/platform/Platform.h" | 52 #include "public/platform/Platform.h" |
51 #include "public/platform/WebSecurityOrigin.h" | 53 #include "public/platform/WebSecurityOrigin.h" |
52 #include "public/platform/WebString.h" | 54 #include "public/platform/WebString.h" |
53 #include "public/platform/modules/notifications/WebNotificationData.h" | 55 #include "public/platform/modules/notifications/WebNotificationAction.h" |
54 #include "public/platform/modules/notifications/WebNotificationManager.h" | 56 #include "public/platform/modules/notifications/WebNotificationManager.h" |
55 | 57 |
56 namespace blink { | 58 namespace blink { |
57 namespace { | 59 namespace { |
58 | 60 |
59 const int64_t kInvalidPersistentId = -1; | 61 const int64_t kInvalidPersistentId = -1; |
60 | 62 |
61 WebNotificationManager* notificationManager() | 63 WebNotificationManager* notificationManager() |
62 { | 64 { |
63 return Platform::current()->notificationManager(); | 65 return Platform::current()->notificationManager(); |
64 } | 66 } |
65 | 67 |
66 } // namespace | 68 } // namespace |
67 | 69 |
68 Notification* Notification::create(ExecutionContext* context, const String& titl
e, const NotificationOptions& options, ExceptionState& exceptionState) | 70 Notification* Notification::create(ExecutionContext* context, const String& titl
e, const NotificationOptions& options, ExceptionState& exceptionState) |
69 { | 71 { |
70 // The Web Notification constructor may be disabled through a runtime featur
e. The | 72 // The Web Notification constructor may be disabled through a runtime featur
e. The |
71 // behavior of the constructor is changing, but not completely agreed upon y
et. | 73 // behavior of the constructor is changing, but not completely agreed upon y
et. |
72 if (!RuntimeEnabledFeatures::notificationConstructorEnabled()) { | 74 if (!RuntimeEnabledFeatures::notificationConstructorEnabled()) { |
73 exceptionState.throwTypeError("Illegal constructor. Use ServiceWorkerReg
istration.showNotification() instead."); | 75 exceptionState.throwTypeError("Illegal constructor. Use ServiceWorkerReg
istration.showNotification() instead."); |
74 return nullptr; | 76 return nullptr; |
75 } | 77 } |
76 | 78 |
77 // The Web Notification constructor may not be used in Service Worker contex
ts. | 79 // The Web Notification constructor may not be used in Service Worker contex
ts. |
78 if (context->isServiceWorkerGlobalScope()) { | 80 if (context->isServiceWorkerGlobalScope()) { |
79 exceptionState.throwTypeError("Illegal constructor."); | 81 exceptionState.throwTypeError("Illegal constructor."); |
80 return nullptr; | 82 return nullptr; |
81 } | 83 } |
82 | 84 |
83 // If options's silent is true, and options's vibrate is present, throw a Ty
peError exception. | |
84 if (options.hasVibrate() && options.silent()) { | |
85 exceptionState.throwTypeError("Silent notifications must not specify vib
ration patterns."); | |
86 return nullptr; | |
87 } | |
88 | |
89 RefPtr<SerializedScriptValue> data; | |
90 if (options.hasData()) { | |
91 data = SerializedScriptValueFactory::instance().create(options.data().is
olate(), options.data(), nullptr, exceptionState); | |
92 if (exceptionState.hadException()) | |
93 return nullptr; | |
94 } | |
95 | |
96 for (const NotificationAction& action : options.actions()) { | |
97 if (action.action().isEmpty()) { | |
98 exceptionState.throwTypeError("NotificationAction action must not be
empty."); | |
99 return nullptr; | |
100 } | |
101 if (action.title().isEmpty()) { | |
102 exceptionState.throwTypeError("NotificationAction title must not be
empty."); | |
103 return nullptr; | |
104 } | |
105 } | |
106 | |
107 Notification* notification = new Notification(title, context); | |
108 | |
109 notification->setBody(options.body()); | |
110 notification->setTag(options.tag()); | |
111 notification->setLang(options.lang()); | |
112 notification->setDir(options.dir()); | |
113 notification->setVibrate(NavigatorVibration::sanitizeVibrationPattern(option
s.vibrate())); | |
114 notification->setSilent(options.silent()); | |
115 notification->setSerializedData(data.release()); | |
116 if (options.hasIcon()) { | |
117 KURL iconUrl = options.icon().isEmpty() ? KURL() : context->completeURL(
options.icon()); | |
118 if (!iconUrl.isEmpty() && iconUrl.isValid()) | |
119 notification->setIconUrl(iconUrl); | |
120 } | |
121 HeapVector<NotificationAction> actions; | |
122 actions.appendRange(options.actions().begin(), options.actions().begin() + s
td::min(maxActions(), options.actions().size())); | |
123 notification->setActions(actions); | |
124 | |
125 String insecureOriginMessage; | 85 String insecureOriginMessage; |
126 UseCounter::Feature feature = context->isPrivilegedContext(insecureOriginMes
sage) | 86 UseCounter::Feature feature = context->isPrivilegedContext(insecureOriginMes
sage) |
127 ? UseCounter::NotificationSecureOrigin | 87 ? UseCounter::NotificationSecureOrigin |
128 : UseCounter::NotificationInsecureOrigin; | 88 : UseCounter::NotificationInsecureOrigin; |
| 89 |
129 UseCounter::count(context, feature); | 90 UseCounter::count(context, feature); |
130 | 91 |
| 92 WebNotificationData data = createWebNotificationData(context, title, options
, exceptionState); |
| 93 if (exceptionState.hadException()) |
| 94 return nullptr; |
| 95 |
| 96 Notification* notification = new Notification(context, data); |
131 notification->scheduleShow(); | 97 notification->scheduleShow(); |
132 notification->suspendIfNeeded(); | 98 notification->suspendIfNeeded(); |
| 99 |
133 return notification; | 100 return notification; |
134 } | 101 } |
135 | 102 |
136 Notification* Notification::create(ExecutionContext* context, int64_t persistent
Id, const WebNotificationData& data) | 103 Notification* Notification::create(ExecutionContext* context, int64_t persistent
Id, const WebNotificationData& data) |
137 { | 104 { |
138 Notification* notification = new Notification(data.title, context); | 105 Notification* notification = new Notification(context, data); |
139 | |
140 notification->setPersistentId(persistentId); | 106 notification->setPersistentId(persistentId); |
141 notification->setDir(data.direction == WebNotificationData::DirectionLeftToR
ight ? "ltr" : "rtl"); | |
142 notification->setLang(data.lang); | |
143 notification->setBody(data.body); | |
144 notification->setTag(data.tag); | |
145 notification->setSilent(data.silent); | |
146 | |
147 if (!data.icon.isEmpty()) | |
148 notification->setIconUrl(data.icon); | |
149 | |
150 if (!data.vibrate.isEmpty()) { | |
151 NavigatorVibration::VibrationPattern pattern; | |
152 pattern.appendRange(data.vibrate.begin(), data.vibrate.end()); | |
153 notification->setVibrate(pattern); | |
154 } | |
155 | |
156 const WebVector<char>& dataBytes = data.data; | |
157 if (!dataBytes.isEmpty()) { | |
158 notification->setSerializedData(SerializedScriptValueFactory::instance()
.createFromWireBytes(dataBytes.data(), dataBytes.size())); | |
159 notification->serializedData()->registerMemoryAllocatedWithCurrentScript
Context(); | |
160 } | |
161 | |
162 HeapVector<NotificationAction> actions; | |
163 webActionsToActions(data.actions, &actions); | |
164 notification->setActions(actions); | |
165 | |
166 notification->setState(NotificationStateShowing); | 107 notification->setState(NotificationStateShowing); |
167 notification->suspendIfNeeded(); | 108 notification->suspendIfNeeded(); |
| 109 |
168 return notification; | 110 return notification; |
169 } | 111 } |
170 | 112 |
171 Notification::Notification(const String& title, ExecutionContext* context) | 113 Notification::Notification(ExecutionContext* context, const WebNotificationData&
data) |
172 : ActiveDOMObject(context) | 114 : ActiveDOMObject(context) |
173 , m_title(title) | 115 , m_data(data) |
174 , m_dir("auto") | |
175 , m_silent(false) | |
176 , m_persistentId(kInvalidPersistentId) | 116 , m_persistentId(kInvalidPersistentId) |
177 , m_state(NotificationStateIdle) | 117 , m_state(NotificationStateIdle) |
178 , m_asyncRunner(this, &Notification::show) | 118 , m_asyncRunner(this, &Notification::show) |
179 { | 119 { |
180 ASSERT(notificationManager()); | 120 ASSERT(notificationManager()); |
181 } | 121 } |
182 | 122 |
183 Notification::~Notification() | 123 Notification::~Notification() |
184 { | 124 { |
185 } | 125 } |
(...skipping 10 matching lines...) Expand all Loading... |
196 { | 136 { |
197 ASSERT(m_state == NotificationStateIdle); | 137 ASSERT(m_state == NotificationStateIdle); |
198 if (Notification::checkPermission(executionContext()) != WebNotificationPerm
issionAllowed) { | 138 if (Notification::checkPermission(executionContext()) != WebNotificationPerm
issionAllowed) { |
199 dispatchErrorEvent(); | 139 dispatchErrorEvent(); |
200 return; | 140 return; |
201 } | 141 } |
202 | 142 |
203 SecurityOrigin* origin = executionContext()->securityOrigin(); | 143 SecurityOrigin* origin = executionContext()->securityOrigin(); |
204 ASSERT(origin); | 144 ASSERT(origin); |
205 | 145 |
206 // FIXME: Do CSP checks on the associated notification icon. | 146 notificationManager()->show(WebSecurityOrigin(origin), m_data, this); |
207 WebNotificationData::Direction dir = m_dir == "rtl" ? WebNotificationData::D
irectionRightToLeft : WebNotificationData::DirectionLeftToRight; | |
208 | |
209 // The lifetime and availability of non-persistent notifications is tied to
the page | |
210 // they were created by, and thus the data doesn't have to be known to the e
mbedder. | |
211 Vector<char> emptyDataWireBytes; | |
212 | |
213 WebVector<WebNotificationAction> webActions; | |
214 actionsToWebActions(m_actions, &webActions); | |
215 | |
216 WebNotificationData notificationData(m_title, dir, m_lang, m_body, m_tag, m_
iconUrl, m_vibrate, m_silent, emptyDataWireBytes, webActions); | |
217 notificationManager()->show(WebSecurityOrigin(origin), notificationData, thi
s); | |
218 | 147 |
219 m_state = NotificationStateShowing; | 148 m_state = NotificationStateShowing; |
220 } | 149 } |
221 | 150 |
222 void Notification::close() | 151 void Notification::close() |
223 { | 152 { |
224 if (m_state != NotificationStateShowing) | 153 if (m_state != NotificationStateShowing) |
225 return; | 154 return; |
226 | 155 |
227 if (m_persistentId == kInvalidPersistentId) { | 156 if (m_persistentId == kInvalidPersistentId) { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 { | 190 { |
262 // The notification will be showing when the user initiated the close, or it
will be | 191 // The notification will be showing when the user initiated the close, or it
will be |
263 // closing if the developer initiated the close. | 192 // closing if the developer initiated the close. |
264 if (m_state != NotificationStateShowing && m_state != NotificationStateClosi
ng) | 193 if (m_state != NotificationStateShowing && m_state != NotificationStateClosi
ng) |
265 return; | 194 return; |
266 | 195 |
267 m_state = NotificationStateClosed; | 196 m_state = NotificationStateClosed; |
268 dispatchEvent(Event::create(EventTypeNames::close)); | 197 dispatchEvent(Event::create(EventTypeNames::close)); |
269 } | 198 } |
270 | 199 |
271 const NavigatorVibration::VibrationPattern& Notification::vibrate(bool& isNull)
const | 200 String Notification::title() const |
272 { | 201 { |
273 isNull = m_vibrate.isEmpty(); | 202 return m_data.title; |
274 return m_vibrate; | |
275 } | 203 } |
276 | 204 |
277 TextDirection Notification::direction() const | 205 String Notification::dir() const |
278 { | 206 { |
279 // FIXME: Resolve dir()=="auto" against the document. | 207 switch (m_data.direction) { |
280 return dir() == "rtl" ? RTL : LTR; | 208 case WebNotificationData::DirectionLeftToRight: |
| 209 return "ltr"; |
| 210 case WebNotificationData::DirectionRightToLeft: |
| 211 return "rtl"; |
| 212 case WebNotificationData::DirectionAuto: |
| 213 return "auto"; |
| 214 } |
| 215 |
| 216 ASSERT_NOT_REACHED(); |
| 217 return String(); |
| 218 } |
| 219 |
| 220 String Notification::lang() const |
| 221 { |
| 222 return m_data.lang; |
| 223 } |
| 224 |
| 225 String Notification::body() const |
| 226 { |
| 227 return m_data.body; |
| 228 } |
| 229 |
| 230 String Notification::tag() const |
| 231 { |
| 232 return m_data.tag; |
| 233 } |
| 234 |
| 235 String Notification::icon() const |
| 236 { |
| 237 return m_data.icon.string(); |
| 238 } |
| 239 |
| 240 NavigatorVibration::VibrationPattern Notification::vibrate(bool& isNull) const |
| 241 { |
| 242 NavigatorVibration::VibrationPattern pattern; |
| 243 pattern.appendRange(m_data.vibrate.begin(), m_data.vibrate.end()); |
| 244 |
| 245 if (!pattern.size()) |
| 246 isNull = true; |
| 247 |
| 248 return pattern; |
| 249 } |
| 250 |
| 251 bool Notification::silent() const |
| 252 { |
| 253 return m_data.silent; |
| 254 } |
| 255 |
| 256 ScriptValue Notification::data(ScriptState* scriptState) |
| 257 { |
| 258 // TODO(peter): The specification requires this to be [SameObject] - match t
his. |
| 259 |
| 260 if (!m_serializedData) { |
| 261 const WebVector<char> serializedData = m_data.data; |
| 262 if (serializedData.size()) |
| 263 m_serializedData = SerializedScriptValueFactory::instance().createFr
omWireBytes(serializedData.data(), serializedData.size()); |
| 264 else |
| 265 m_serializedData = SerializedScriptValueFactory::instance().create()
; |
| 266 } |
| 267 |
| 268 return ScriptValue(scriptState, m_serializedData->deserialize(scriptState->i
solate())); |
| 269 } |
| 270 |
| 271 HeapVector<NotificationAction> Notification::actions() const |
| 272 { |
| 273 HeapVector<NotificationAction> actions; |
| 274 actions.grow(m_data.actions.size()); |
| 275 |
| 276 for (size_t i = 0; i < m_data.actions.size(); ++i) { |
| 277 actions[i].setAction(m_data.actions[i].action); |
| 278 actions[i].setTitle(m_data.actions[i].title); |
| 279 } |
| 280 |
| 281 return actions; |
281 } | 282 } |
282 | 283 |
283 String Notification::permissionString(WebNotificationPermission permission) | 284 String Notification::permissionString(WebNotificationPermission permission) |
284 { | 285 { |
285 switch (permission) { | 286 switch (permission) { |
286 case WebNotificationPermissionAllowed: | 287 case WebNotificationPermissionAllowed: |
287 return "granted"; | 288 return "granted"; |
288 case WebNotificationPermissionDenied: | 289 case WebNotificationPermissionDenied: |
289 return "denied"; | 290 return "denied"; |
290 case WebNotificationPermissionDefault: | 291 case WebNotificationPermissionDefault: |
(...skipping 25 matching lines...) Expand all Loading... |
316 // TODO(peter): Assert that this code-path will only be reached for Docu
ment environments when Blink | 317 // TODO(peter): Assert that this code-path will only be reached for Docu
ment environments when Blink |
317 // supports [Exposed] annotations on class members in IDL definitions. S
ee https://crbug.com/442139. | 318 // supports [Exposed] annotations on class members in IDL definitions. S
ee https://crbug.com/442139. |
318 return ScriptPromise::cast(scriptState, v8String(scriptState->isolate(),
permission(context))); | 319 return ScriptPromise::cast(scriptState, v8String(scriptState->isolate(),
permission(context))); |
319 } | 320 } |
320 | 321 |
321 return permissionClient->requestPermission(scriptState, deprecatedCallback); | 322 return permissionClient->requestPermission(scriptState, deprecatedCallback); |
322 } | 323 } |
323 | 324 |
324 size_t Notification::maxActions() | 325 size_t Notification::maxActions() |
325 { | 326 { |
| 327 // Returns a fixed number for unit tests, which run without the availability
of the Platform object. |
| 328 if (!notificationManager()) |
| 329 return 2; |
| 330 |
326 return notificationManager()->maxActions(); | 331 return notificationManager()->maxActions(); |
327 } | 332 } |
328 | 333 |
329 void Notification::actionsToWebActions(const HeapVector<NotificationAction>& act
ions, WebVector<WebNotificationAction>* webActions) | |
330 { | |
331 size_t count = std::min(maxActions(), actions.size()); | |
332 WebVector<WebNotificationAction> clearedAndResized(count); | |
333 webActions->swap(clearedAndResized); | |
334 for (size_t i = 0; i < count; ++i) { | |
335 (*webActions)[i].action = actions[i].action(); | |
336 (*webActions)[i].title = actions[i].title(); | |
337 } | |
338 } | |
339 | |
340 void Notification::webActionsToActions(const WebVector<WebNotificationAction>& w
ebActions, HeapVector<NotificationAction>* actions) | |
341 { | |
342 actions->clear(); | |
343 actions->grow(webActions.size()); | |
344 for (size_t i = 0; i < webActions.size(); ++i) { | |
345 (*actions)[i].setAction(webActions[i].action); | |
346 (*actions)[i].setTitle(webActions[i].title); | |
347 } | |
348 } | |
349 | |
350 bool Notification::dispatchEventInternal(PassRefPtrWillBeRawPtr<Event> event) | 334 bool Notification::dispatchEventInternal(PassRefPtrWillBeRawPtr<Event> event) |
351 { | 335 { |
352 ASSERT(executionContext()->isContextThread()); | 336 ASSERT(executionContext()->isContextThread()); |
353 return EventTarget::dispatchEventInternal(event); | 337 return EventTarget::dispatchEventInternal(event); |
354 } | 338 } |
355 | 339 |
356 const AtomicString& Notification::interfaceName() const | 340 const AtomicString& Notification::interfaceName() const |
357 { | 341 { |
358 return EventTargetNames::Notification; | 342 return EventTargetNames::Notification; |
359 } | 343 } |
360 | 344 |
361 void Notification::stop() | 345 void Notification::stop() |
362 { | 346 { |
363 notificationManager()->notifyDelegateDestroyed(this); | 347 notificationManager()->notifyDelegateDestroyed(this); |
364 | 348 |
365 m_state = NotificationStateClosed; | 349 m_state = NotificationStateClosed; |
366 | 350 |
367 m_asyncRunner.stop(); | 351 m_asyncRunner.stop(); |
368 } | 352 } |
369 | 353 |
370 bool Notification::hasPendingActivity() const | 354 bool Notification::hasPendingActivity() const |
371 { | 355 { |
372 return m_state == NotificationStateShowing || m_asyncRunner.isActive(); | 356 return m_state == NotificationStateShowing || m_asyncRunner.isActive(); |
373 } | 357 } |
374 | 358 |
375 ScriptValue Notification::data(ScriptState* scriptState) const | |
376 { | |
377 if (!m_serializedData) | |
378 return ScriptValue::createNull(scriptState); | |
379 | |
380 return ScriptValue(scriptState, m_serializedData->deserialize(scriptState->i
solate())); | |
381 } | |
382 | |
383 DEFINE_TRACE(Notification) | 359 DEFINE_TRACE(Notification) |
384 { | 360 { |
385 visitor->trace(m_actions); | |
386 RefCountedGarbageCollectedEventTargetWithInlineData<Notification>::trace(vis
itor); | 361 RefCountedGarbageCollectedEventTargetWithInlineData<Notification>::trace(vis
itor); |
387 ActiveDOMObject::trace(visitor); | 362 ActiveDOMObject::trace(visitor); |
388 } | 363 } |
389 | 364 |
390 } // namespace blink | 365 } // namespace blink |
OLD | NEW |