Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(877)

Side by Side Diff: Source/modules/notifications/Notification.cpp

Issue 1260793007: Generalize validation of developer input for Web Notifications (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: fix actions Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « Source/modules/notifications/Notification.h ('k') | Source/modules/notifications/NotificationData.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698