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

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: 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.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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698