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.title().isEmpty()) { | |
98 exceptionState.throwTypeError("Notification action titles must not b e empty."); | |
99 return nullptr; | |
100 } | |
101 } | |
102 | |
103 Notification* notification = new Notification(title, context); | |
104 | |
105 notification->setBody(options.body()); | |
106 notification->setTag(options.tag()); | |
107 notification->setLang(options.lang()); | |
108 notification->setDir(options.dir()); | |
109 notification->setVibrate(NavigatorVibration::sanitizeVibrationPattern(option s.vibrate())); | |
110 notification->setSilent(options.silent()); | |
111 notification->setSerializedData(data.release()); | |
112 if (options.hasIcon()) { | |
113 KURL iconUrl = options.icon().isEmpty() ? KURL() : context->completeURL( options.icon()); | |
114 if (!iconUrl.isEmpty() && iconUrl.isValid()) | |
115 notification->setIconUrl(iconUrl); | |
116 } | |
117 HeapVector<NotificationAction> actions; | |
118 actions.appendRange(options.actions().begin(), options.actions().begin() + s td::min(maxActions(), options.actions().size())); | |
119 notification->setActions(actions); | |
120 | |
121 String insecureOriginMessage; | 85 String insecureOriginMessage; |
122 UseCounter::Feature feature = context->isPrivilegedContext(insecureOriginMes sage) | 86 UseCounter::Feature feature = context->isPrivilegedContext(insecureOriginMes sage) |
123 ? UseCounter::NotificationSecureOrigin | 87 ? UseCounter::NotificationSecureOrigin |
124 : UseCounter::NotificationInsecureOrigin; | 88 : UseCounter::NotificationInsecureOrigin; |
89 | |
125 UseCounter::count(context, feature); | 90 UseCounter::count(context, feature); |
126 | 91 |
92 WebNotificationData data = createWebNotificationData(context, title, options , exceptionState); | |
93 if (exceptionState.hadException()) | |
94 return nullptr; | |
95 | |
96 Notification* notification = new Notification(context, data); | |
127 notification->scheduleShow(); | 97 notification->scheduleShow(); |
128 notification->suspendIfNeeded(); | 98 notification->suspendIfNeeded(); |
99 | |
129 return notification; | 100 return notification; |
130 } | 101 } |
131 | 102 |
132 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) |
133 { | 104 { |
134 Notification* notification = new Notification(data.title, context); | 105 Notification* notification = new Notification(context, data); |
135 | |
136 notification->setPersistentId(persistentId); | 106 notification->setPersistentId(persistentId); |
137 notification->setDir(data.direction == WebNotificationData::DirectionLeftToR ight ? "ltr" : "rtl"); | |
138 notification->setLang(data.lang); | |
139 notification->setBody(data.body); | |
140 notification->setTag(data.tag); | |
141 notification->setSilent(data.silent); | |
142 | |
143 if (!data.icon.isEmpty()) | |
144 notification->setIconUrl(data.icon); | |
145 | |
146 if (!data.vibrate.isEmpty()) { | |
147 NavigatorVibration::VibrationPattern pattern; | |
148 pattern.appendRange(data.vibrate.begin(), data.vibrate.end()); | |
149 notification->setVibrate(pattern); | |
150 } | |
151 | |
152 const WebVector<char>& dataBytes = data.data; | |
153 if (!dataBytes.isEmpty()) { | |
154 notification->setSerializedData(SerializedScriptValueFactory::instance() .createFromWireBytes(dataBytes.data(), dataBytes.size())); | |
155 notification->serializedData()->registerMemoryAllocatedWithCurrentScript Context(); | |
156 } | |
157 | |
158 HeapVector<NotificationAction> actions; | |
159 webActionsToActions(data.actions, &actions); | |
160 notification->setActions(actions); | |
161 | |
162 notification->setState(NotificationStateShowing); | 107 notification->setState(NotificationStateShowing); |
163 notification->suspendIfNeeded(); | 108 notification->suspendIfNeeded(); |
109 | |
164 return notification; | 110 return notification; |
165 } | 111 } |
166 | 112 |
167 Notification::Notification(const String& title, ExecutionContext* context) | 113 Notification::Notification(ExecutionContext* context, const WebNotificationData& data) |
168 : ActiveDOMObject(context) | 114 : ActiveDOMObject(context) |
169 , m_title(title) | 115 , m_data(data) |
170 , m_dir("auto") | |
171 , m_silent(false) | |
172 , m_persistentId(kInvalidPersistentId) | 116 , m_persistentId(kInvalidPersistentId) |
173 , m_state(NotificationStateIdle) | 117 , m_state(NotificationStateIdle) |
174 , m_asyncRunner(this, &Notification::show) | 118 , m_asyncRunner(this, &Notification::show) |
175 { | 119 { |
176 ASSERT(notificationManager()); | 120 ASSERT(notificationManager()); |
177 } | 121 } |
178 | 122 |
179 Notification::~Notification() | 123 Notification::~Notification() |
180 { | 124 { |
181 } | 125 } |
(...skipping 10 matching lines...) Expand all Loading... | |
192 { | 136 { |
193 ASSERT(m_state == NotificationStateIdle); | 137 ASSERT(m_state == NotificationStateIdle); |
194 if (Notification::checkPermission(executionContext()) != WebNotificationPerm issionAllowed) { | 138 if (Notification::checkPermission(executionContext()) != WebNotificationPerm issionAllowed) { |
195 dispatchErrorEvent(); | 139 dispatchErrorEvent(); |
196 return; | 140 return; |
197 } | 141 } |
198 | 142 |
199 SecurityOrigin* origin = executionContext()->securityOrigin(); | 143 SecurityOrigin* origin = executionContext()->securityOrigin(); |
200 ASSERT(origin); | 144 ASSERT(origin); |
201 | 145 |
202 // FIXME: Do CSP checks on the associated notification icon. | 146 notificationManager()->show(WebSecurityOrigin(origin), m_data, this); |
203 WebNotificationData::Direction dir = m_dir == "rtl" ? WebNotificationData::D irectionRightToLeft : WebNotificationData::DirectionLeftToRight; | |
204 | |
205 // The lifetime and availability of non-persistent notifications is tied to the page | |
206 // they were created by, and thus the data doesn't have to be known to the e mbedder. | |
207 Vector<char> emptyDataWireBytes; | |
208 | |
209 WebVector<WebNotificationAction> webActions; | |
210 actionsToWebActions(m_actions, &webActions); | |
211 | |
212 WebNotificationData notificationData(m_title, dir, m_lang, m_body, m_tag, m_ iconUrl, m_vibrate, m_silent, emptyDataWireBytes, webActions); | |
213 notificationManager()->show(WebSecurityOrigin(origin), notificationData, thi s); | |
214 | 147 |
215 m_state = NotificationStateShowing; | 148 m_state = NotificationStateShowing; |
216 } | 149 } |
217 | 150 |
218 void Notification::close() | 151 void Notification::close() |
219 { | 152 { |
220 if (m_state != NotificationStateShowing) | 153 if (m_state != NotificationStateShowing) |
221 return; | 154 return; |
222 | 155 |
223 if (m_persistentId == kInvalidPersistentId) { | 156 if (m_persistentId == kInvalidPersistentId) { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
257 { | 190 { |
258 // 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 |
259 // closing if the developer initiated the close. | 192 // closing if the developer initiated the close. |
260 if (m_state != NotificationStateShowing && m_state != NotificationStateClosi ng) | 193 if (m_state != NotificationStateShowing && m_state != NotificationStateClosi ng) |
261 return; | 194 return; |
262 | 195 |
263 m_state = NotificationStateClosed; | 196 m_state = NotificationStateClosed; |
264 dispatchEvent(Event::create(EventTypeNames::close)); | 197 dispatchEvent(Event::create(EventTypeNames::close)); |
265 } | 198 } |
266 | 199 |
267 const NavigatorVibration::VibrationPattern& Notification::vibrate(bool& isNull) const | 200 String Notification::title() const |
268 { | 201 { |
269 isNull = m_vibrate.isEmpty(); | 202 return m_data.title; |
270 return m_vibrate; | |
271 } | 203 } |
272 | 204 |
273 TextDirection Notification::direction() const | 205 String Notification::dir() const |
274 { | 206 { |
275 // FIXME: Resolve dir()=="auto" against the document. | 207 switch (m_data.direction) { |
276 return dir() == "rtl" ? RTL : LTR; | 208 case WebNotificationData::DirectionLeftToRight: |
209 return "ltr"; | |
210 case WebNotificationData::DirectionRightToLeft: | |
211 return "rtl"; | |
212 } | |
213 | |
214 ASSERT_NOT_REACHED(); | |
215 return String(); | |
216 } | |
217 | |
218 String Notification::lang() const | |
219 { | |
220 return m_data.lang; | |
221 } | |
222 | |
223 String Notification::body() const | |
224 { | |
225 return m_data.body; | |
226 } | |
227 | |
228 String Notification::tag() const | |
229 { | |
230 return m_data.tag; | |
231 } | |
232 | |
233 String Notification::icon() const | |
234 { | |
235 return m_data.icon.string(); | |
236 } | |
237 | |
238 NavigatorVibration::VibrationPattern Notification::vibrate(bool& isNull) const | |
239 { | |
240 NavigatorVibration::VibrationPattern pattern; | |
241 pattern.appendRange(m_data.vibrate.begin(), m_data.vibrate.end()); | |
242 | |
243 if (!pattern.size()) | |
244 isNull = true; | |
245 | |
246 return pattern; | |
247 } | |
248 | |
249 bool Notification::silent() const | |
250 { | |
251 return m_data.silent; | |
252 } | |
253 | |
254 ScriptValue Notification::data(ScriptState* scriptState) | |
255 { | |
256 // TODO(peter): The specification requires this to be [SameObject] - match t his. | |
johnme
2015/08/03 15:41:36
Is this a change from before, or an old TODO that
Peter Beverloo
2015/08/03 17:29:30
TODO that still applies. I don't think we're doing
| |
257 | |
258 if (!m_serializedData) { | |
259 const WebVector<char> serializedData = m_data.data; | |
260 if (serializedData.size()) | |
261 m_serializedData = SerializedScriptValueFactory::instance().createFr omWireBytes(serializedData.data(), serializedData.size()); | |
262 else | |
263 m_serializedData = SerializedScriptValueFactory::instance().create() ; | |
264 } | |
265 | |
266 return ScriptValue(scriptState, m_serializedData->deserialize(scriptState->i solate())); | |
267 } | |
268 | |
269 HeapVector<NotificationAction> Notification::actions() const | |
270 { | |
271 HeapVector<NotificationAction> actions; | |
272 actions.grow(m_data.actions.size()); | |
273 | |
274 for (size_t i = 0; i < m_data.actions.size(); ++i) | |
275 actions[i].setTitle(m_data.actions[i].title); | |
276 | |
277 return actions; | |
277 } | 278 } |
278 | 279 |
279 String Notification::permissionString(WebNotificationPermission permission) | 280 String Notification::permissionString(WebNotificationPermission permission) |
280 { | 281 { |
281 switch (permission) { | 282 switch (permission) { |
282 case WebNotificationPermissionAllowed: | 283 case WebNotificationPermissionAllowed: |
283 return "granted"; | 284 return "granted"; |
284 case WebNotificationPermissionDenied: | 285 case WebNotificationPermissionDenied: |
285 return "denied"; | 286 return "denied"; |
286 case WebNotificationPermissionDefault: | 287 case WebNotificationPermissionDefault: |
(...skipping 25 matching lines...) Expand all Loading... | |
312 // TODO(peter): Assert that this code-path will only be reached for Docu ment environments when Blink | 313 // TODO(peter): Assert that this code-path will only be reached for Docu ment environments when Blink |
313 // supports [Exposed] annotations on class members in IDL definitions. S ee https://crbug.com/442139. | 314 // supports [Exposed] annotations on class members in IDL definitions. S ee https://crbug.com/442139. |
314 return ScriptPromise::cast(scriptState, v8String(scriptState->isolate(), permission(context))); | 315 return ScriptPromise::cast(scriptState, v8String(scriptState->isolate(), permission(context))); |
315 } | 316 } |
316 | 317 |
317 return permissionClient->requestPermission(scriptState, deprecatedCallback); | 318 return permissionClient->requestPermission(scriptState, deprecatedCallback); |
318 } | 319 } |
319 | 320 |
320 size_t Notification::maxActions() | 321 size_t Notification::maxActions() |
321 { | 322 { |
323 // Returns a fixed number for unit tests, which run without the availability of the Platform object. | |
324 if (!notificationManager()) | |
325 return 2; | |
326 | |
322 return notificationManager()->maxActions(); | 327 return notificationManager()->maxActions(); |
323 } | 328 } |
324 | 329 |
325 void Notification::actionsToWebActions(const HeapVector<NotificationAction>& act ions, WebVector<WebNotificationAction>* webActions) | |
326 { | |
327 size_t count = std::min(maxActions(), actions.size()); | |
328 WebVector<WebNotificationAction> clearedAndResized(count); | |
329 webActions->swap(clearedAndResized); | |
330 for (size_t i = 0; i < count; ++i) | |
331 (*webActions)[i].title = actions[i].title(); | |
332 } | |
333 | |
334 void Notification::webActionsToActions(const WebVector<WebNotificationAction>& w ebActions, HeapVector<NotificationAction>* actions) | |
335 { | |
336 actions->clear(); | |
337 actions->grow(webActions.size()); | |
338 for (size_t i = 0; i < webActions.size(); ++i) | |
339 (*actions)[i].setTitle(webActions[i].title); | |
340 } | |
341 | |
342 bool Notification::dispatchEventInternal(PassRefPtrWillBeRawPtr<Event> event) | 330 bool Notification::dispatchEventInternal(PassRefPtrWillBeRawPtr<Event> event) |
343 { | 331 { |
344 ASSERT(executionContext()->isContextThread()); | 332 ASSERT(executionContext()->isContextThread()); |
345 return EventTarget::dispatchEventInternal(event); | 333 return EventTarget::dispatchEventInternal(event); |
346 } | 334 } |
347 | 335 |
348 const AtomicString& Notification::interfaceName() const | 336 const AtomicString& Notification::interfaceName() const |
349 { | 337 { |
350 return EventTargetNames::Notification; | 338 return EventTargetNames::Notification; |
351 } | 339 } |
352 | 340 |
353 void Notification::stop() | 341 void Notification::stop() |
354 { | 342 { |
355 notificationManager()->notifyDelegateDestroyed(this); | 343 notificationManager()->notifyDelegateDestroyed(this); |
356 | 344 |
357 m_state = NotificationStateClosed; | 345 m_state = NotificationStateClosed; |
358 | 346 |
359 m_asyncRunner.stop(); | 347 m_asyncRunner.stop(); |
360 } | 348 } |
361 | 349 |
362 bool Notification::hasPendingActivity() const | 350 bool Notification::hasPendingActivity() const |
363 { | 351 { |
364 return m_state == NotificationStateShowing || m_asyncRunner.isActive(); | 352 return m_state == NotificationStateShowing || m_asyncRunner.isActive(); |
365 } | 353 } |
366 | 354 |
367 ScriptValue Notification::data(ScriptState* scriptState) const | |
368 { | |
369 if (!m_serializedData) | |
370 return ScriptValue::createNull(scriptState); | |
371 | |
372 return ScriptValue(scriptState, m_serializedData->deserialize(scriptState->i solate())); | |
373 } | |
374 | |
375 DEFINE_TRACE(Notification) | 355 DEFINE_TRACE(Notification) |
376 { | 356 { |
377 visitor->trace(m_actions); | |
378 RefCountedGarbageCollectedEventTargetWithInlineData<Notification>::trace(vis itor); | 357 RefCountedGarbageCollectedEventTargetWithInlineData<Notification>::trace(vis itor); |
379 ActiveDOMObject::trace(visitor); | 358 ActiveDOMObject::trace(visitor); |
380 } | 359 } |
381 | 360 |
382 } // namespace blink | 361 } // namespace blink |
OLD | NEW |