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

Side by Side Diff: Source/modules/indexeddb/IDBRequest.cpp

Issue 235933013: Add the backchannel for Blobs to be received into Blink from the database backend. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Remove accidentally-included files Created 6 years, 8 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) 2010 Google Inc. All rights reserved. 2 * Copyright (C) 2010 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 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 7 *
8 * 1. Redistributions of source code must retain the above copyright 8 * 1. 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 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
(...skipping 21 matching lines...) Expand all
32 #include "bindings/v8/ExceptionState.h" 32 #include "bindings/v8/ExceptionState.h"
33 #include "bindings/v8/ExceptionStatePlaceholder.h" 33 #include "bindings/v8/ExceptionStatePlaceholder.h"
34 #include "bindings/v8/IDBBindingUtilities.h" 34 #include "bindings/v8/IDBBindingUtilities.h"
35 #include "core/dom/ExecutionContext.h" 35 #include "core/dom/ExecutionContext.h"
36 #include "core/events/EventQueue.h" 36 #include "core/events/EventQueue.h"
37 #include "modules/indexeddb/IDBCursorWithValue.h" 37 #include "modules/indexeddb/IDBCursorWithValue.h"
38 #include "modules/indexeddb/IDBDatabase.h" 38 #include "modules/indexeddb/IDBDatabase.h"
39 #include "modules/indexeddb/IDBEventDispatcher.h" 39 #include "modules/indexeddb/IDBEventDispatcher.h"
40 #include "modules/indexeddb/IDBTracing.h" 40 #include "modules/indexeddb/IDBTracing.h"
41 #include "platform/SharedBuffer.h" 41 #include "platform/SharedBuffer.h"
42 #include "public/platform/WebBlobInfo.h"
42 43
43 using blink::WebIDBCursor; 44 using blink::WebIDBCursor;
44 45
45 namespace WebCore { 46 namespace WebCore {
46 47
47 PassRefPtr<IDBRequest> IDBRequest::create(ExecutionContext* context, PassRefPtr< IDBAny> source, IDBTransaction* transaction) 48 PassRefPtr<IDBRequest> IDBRequest::create(ExecutionContext* context, PassRefPtr< IDBAny> source, IDBTransaction* transaction)
48 { 49 {
49 RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, transact ion))); 50 RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, transact ion)));
50 request->suspendIfNeeded(); 51 request->suspendIfNeeded();
51 // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames ) are not associated with transactions. 52 // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames ) are not associated with transactions.
(...skipping 17 matching lines...) Expand all
69 , m_didFireUpgradeNeededEvent(false) 70 , m_didFireUpgradeNeededEvent(false)
70 , m_preventPropagation(false) 71 , m_preventPropagation(false)
71 , m_resultDirty(true) 72 , m_resultDirty(true)
72 { 73 {
73 ScriptWrappable::init(this); 74 ScriptWrappable::init(this);
74 } 75 }
75 76
76 IDBRequest::~IDBRequest() 77 IDBRequest::~IDBRequest()
77 { 78 {
78 ASSERT(m_readyState == DONE || m_readyState == EarlyDeath || !executionConte xt()); 79 ASSERT(m_readyState == DONE || m_readyState == EarlyDeath || !executionConte xt());
80 handleBlobAcks();
79 } 81 }
80 82
81 ScriptValue IDBRequest::result(ExceptionState& exceptionState) 83 ScriptValue IDBRequest::result(ExceptionState& exceptionState)
82 { 84 {
83 if (m_readyState != DONE) { 85 if (m_readyState != DONE) {
84 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::request NotFinishedErrorMessage); 86 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::request NotFinishedErrorMessage);
85 return ScriptValue(); 87 return ScriptValue();
86 } 88 }
87 if (m_contextStopped || !executionContext()) 89 if (m_contextStopped || !executionContext())
88 return ScriptValue(); 90 return ScriptValue();
89 m_resultDirty = false; 91 m_resultDirty = false;
90 return idbAnyToScriptValue(m_scriptState.get(), m_result); 92 ScriptValue value = idbAnyToScriptValue(m_scriptState.get(), m_result);
93 handleBlobAcks();
94 return value;
91 } 95 }
92 96
93 PassRefPtrWillBeRawPtr<DOMError> IDBRequest::error(ExceptionState& exceptionStat e) const 97 PassRefPtrWillBeRawPtr<DOMError> IDBRequest::error(ExceptionState& exceptionStat e) const
94 { 98 {
95 if (m_readyState != DONE) { 99 if (m_readyState != DONE) {
96 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::request NotFinishedErrorMessage); 100 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::request NotFinishedErrorMessage);
97 return nullptr; 101 return nullptr;
98 } 102 }
99 return m_error; 103 return m_error;
100 } 104 }
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 { 176 {
173 if (!m_result) 177 if (!m_result)
174 return 0; 178 return 0;
175 if (m_result->type() == IDBAny::IDBCursorType) 179 if (m_result->type() == IDBAny::IDBCursorType)
176 return m_result->idbCursor(); 180 return m_result->idbCursor();
177 if (m_result->type() == IDBAny::IDBCursorWithValueType) 181 if (m_result->type() == IDBAny::IDBCursorWithValueType)
178 return m_result->idbCursorWithValue(); 182 return m_result->idbCursorWithValue();
179 return 0; 183 return 0;
180 } 184 }
181 185
182 void IDBRequest::setResultCursor(PassRefPtr<IDBCursor> cursor, PassRefPtr<IDBKey > key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value) 186 void IDBRequest::ackReceivedBlobs(const Vector<blink::WebBlobInfo>* blobInfo)
187 {
188 ASSERT(blobInfo);
189 if (!blobInfo->size())
190 return;
191 Vector<blink::WebBlobInfo>::const_iterator iter;
192 Vector<String> uuids;
193 uuids.reserveCapacity(blobInfo->size());
194 for (iter = blobInfo->begin(); iter != blobInfo->end(); ++iter)
195 uuids.append(iter->uuid());
196 m_transaction->db()->backend()->ackReceivedBlobs(uuids);
jsbell 2014/04/15 18:29:07 Could just be m_transaction->backendDB() That sai
ericu 2014/04/16 23:04:14 Done.
197 }
198
199 void IDBRequest::setResultCursor(PassRefPtr<IDBCursor> cursor, PassRefPtr<IDBKey > key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value, PassOwnPtr <Vector<blink::WebBlobInfo> > blobInfo)
183 { 200 {
184 ASSERT(m_readyState == PENDING); 201 ASSERT(m_readyState == PENDING);
185 m_cursorKey = key; 202 m_cursorKey = key;
186 m_cursorPrimaryKey = primaryKey; 203 m_cursorPrimaryKey = primaryKey;
187 m_cursorValue = value; 204 m_cursorValue = value;
205 ASSERT(!m_blobInfo.get());
206 m_blobInfo = blobInfo;
188 207
189 onSuccessInternal(IDBAny::create(cursor)); 208 onSuccessInternal(IDBAny::create(cursor));
190 } 209 }
191 210
192 void IDBRequest::checkForReferenceCycle() 211 void IDBRequest::checkForReferenceCycle()
193 { 212 {
194 // If this request and its cursor have the only references 213 // If this request and its cursor have the only references
195 // to each other, then explicitly break the cycle. 214 // to each other, then explicitly break the cycle.
196 IDBCursor* cursor = getResultCursor(); 215 IDBCursor* cursor = getResultCursor();
197 if (!cursor || cursor->request() != this) 216 if (!cursor || cursor->request() != this)
198 return; 217 return;
199 218
200 if (!hasOneRef() || !cursor->hasOneRef()) 219 if (!hasOneRef() || !cursor->hasOneRef())
201 return; 220 return;
202 221
203 m_result.clear(); 222 m_result.clear();
204 } 223 }
205 224
225 void IDBRequest::handleBlobAcks()
cmumford 2014/04/15 16:05:02 Why do we check for non-zero size in IDBCursor::ha
ericu 2014/04/16 23:04:14 It seems a reasonable optimization to check that t
226 {
227 if (m_blobInfo.get()) {
228 ackReceivedBlobs(m_blobInfo.get());
229 m_blobInfo.clear();
230 }
231 }
232
206 bool IDBRequest::shouldEnqueueEvent() const 233 bool IDBRequest::shouldEnqueueEvent() const
207 { 234 {
208 if (m_contextStopped || !executionContext()) 235 if (m_contextStopped || !executionContext())
209 return false; 236 return false;
210 ASSERT(m_readyState == PENDING || m_readyState == DONE); 237 ASSERT(m_readyState == PENDING || m_readyState == DONE);
211 if (m_requestAborted) 238 if (m_requestAborted)
212 return false; 239 return false;
213 ASSERT(m_readyState == PENDING); 240 ASSERT(m_readyState == PENDING);
214 ASSERT(!m_error && !m_result); 241 ASSERT(!m_error && !m_result);
215 return true; 242 return true;
(...skipping 15 matching lines...) Expand all
231 IDB_TRACE("IDBRequest::onSuccess(StringList)"); 258 IDB_TRACE("IDBRequest::onSuccess(StringList)");
232 if (!shouldEnqueueEvent()) 259 if (!shouldEnqueueEvent())
233 return; 260 return;
234 261
235 RefPtr<DOMStringList> domStringList = DOMStringList::create(); 262 RefPtr<DOMStringList> domStringList = DOMStringList::create();
236 for (size_t i = 0; i < stringList.size(); ++i) 263 for (size_t i = 0; i < stringList.size(); ++i)
237 domStringList->append(stringList[i]); 264 domStringList->append(stringList[i]);
238 onSuccessInternal(IDBAny::create(domStringList.release())); 265 onSuccessInternal(IDBAny::create(domStringList.release()));
239 } 266 }
240 267
241 void IDBRequest::onSuccess(PassOwnPtr<blink::WebIDBCursor> backend, PassRefPtr<I DBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value) 268 void IDBRequest::onSuccess(PassOwnPtr<blink::WebIDBCursor> backend, PassRefPtr<I DBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value, PassO wnPtr<Vector<blink::WebBlobInfo> > blobInfo)
242 { 269 {
243 IDB_TRACE("IDBRequest::onSuccess(IDBCursor)"); 270 IDB_TRACE("IDBRequest::onSuccess(IDBCursor)");
244 if (!shouldEnqueueEvent()) 271 if (!shouldEnqueueEvent())
245 return; 272 return;
246 273
247 ASSERT(!m_pendingCursor); 274 ASSERT(!m_pendingCursor);
248 RefPtr<IDBCursor> cursor; 275 RefPtr<IDBCursor> cursor;
249 switch (m_cursorType) { 276 switch (m_cursorType) {
250 case IndexedDB::CursorKeyOnly: 277 case IndexedDB::CursorKeyOnly:
251 cursor = IDBCursor::create(backend, m_cursorDirection, this, m_source.ge t(), m_transaction.get()); 278 cursor = IDBCursor::create(backend, m_cursorDirection, this, m_source.ge t(), m_transaction.get());
252 break; 279 break;
253 case IndexedDB::CursorKeyAndValue: 280 case IndexedDB::CursorKeyAndValue:
254 cursor = IDBCursorWithValue::create(backend, m_cursorDirection, this, m_ source.get(), m_transaction.get()); 281 cursor = IDBCursorWithValue::create(backend, m_cursorDirection, this, m_ source.get(), m_transaction.get());
255 break; 282 break;
256 default: 283 default:
257 ASSERT_NOT_REACHED(); 284 ASSERT_NOT_REACHED();
258 } 285 }
259 setResultCursor(cursor, key, primaryKey, value); 286 setResultCursor(cursor, key, primaryKey, value, blobInfo);
260 } 287 }
261 288
262 void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey) 289 void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
263 { 290 {
264 IDB_TRACE("IDBRequest::onSuccess(IDBKey)"); 291 IDB_TRACE("IDBRequest::onSuccess(IDBKey)");
265 if (!shouldEnqueueEvent()) 292 if (!shouldEnqueueEvent())
266 return; 293 return;
267 294
268 if (idbKey && idbKey->isValid()) 295 if (idbKey && idbKey->isValid())
269 onSuccessInternal(IDBAny::create(idbKey)); 296 onSuccessInternal(IDBAny::create(idbKey));
270 else 297 else
271 onSuccessInternal(IDBAny::createUndefined()); 298 onSuccessInternal(IDBAny::createUndefined());
272 } 299 }
273 300
274 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer) 301 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer, PassOwnPtr<Vect or<blink::WebBlobInfo> > blobInfo)
275 { 302 {
276 IDB_TRACE("IDBRequest::onSuccess(SharedBuffer)"); 303 IDB_TRACE("IDBRequest::onSuccess(SharedBuffer)");
277 if (!shouldEnqueueEvent()) 304 if (!shouldEnqueueEvent())
278 return; 305 return;
279 306
280 if (m_pendingCursor) { 307 if (m_pendingCursor) {
281 // Value should be null, signifying the end of the cursor's range. 308 // Value should be null, signifying the end of the cursor's range.
282 ASSERT(!valueBuffer.get()); 309 ASSERT(!valueBuffer.get());
310 ASSERT(!blobInfo->size());
283 m_pendingCursor->close(); 311 m_pendingCursor->close();
284 m_pendingCursor.clear(); 312 m_pendingCursor.clear();
285 } 313 }
286 314
287 onSuccessInternal(IDBAny::create(valueBuffer)); 315 ASSERT(!m_blobInfo.get());
316 m_blobInfo = blobInfo;
317 onSuccessInternal(IDBAny::create(valueBuffer, m_blobInfo.get()));
288 } 318 }
289 319
290 #ifndef NDEBUG 320 #ifndef NDEBUG
291 static PassRefPtr<IDBObjectStore> effectiveObjectStore(PassRefPtr<IDBAny> source ) 321 static PassRefPtr<IDBObjectStore> effectiveObjectStore(PassRefPtr<IDBAny> source )
292 { 322 {
293 if (source->type() == IDBAny::IDBObjectStoreType) 323 if (source->type() == IDBAny::IDBObjectStoreType)
294 return source->idbObjectStore(); 324 return source->idbObjectStore();
295 if (source->type() == IDBAny::IDBIndexType) 325 if (source->type() == IDBAny::IDBIndexType)
296 return source->idbIndex()->objectStore(); 326 return source->idbIndex()->objectStore();
297 327
298 ASSERT_NOT_REACHED(); 328 ASSERT_NOT_REACHED();
299 return nullptr; 329 return nullptr;
300 } 330 }
301 #endif 331 #endif
302 332
303 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> prpValueBuffer, PassRefPtr<I DBKey> prpPrimaryKey, const IDBKeyPath& keyPath) 333 void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> prpValueBuffer, PassOwnPtr<V ector<blink::WebBlobInfo> > blobInfo, PassRefPtr<IDBKey> prpPrimaryKey, const ID BKeyPath& keyPath)
304 { 334 {
305 IDB_TRACE("IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)"); 335 IDB_TRACE("IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)");
306 if (!shouldEnqueueEvent()) 336 if (!shouldEnqueueEvent())
307 return; 337 return;
308 338
309 #ifndef NDEBUG 339 #ifndef NDEBUG
310 ASSERT(keyPath == effectiveObjectStore(m_source)->metadata().keyPath); 340 ASSERT(keyPath == effectiveObjectStore(m_source)->metadata().keyPath);
311 #endif 341 #endif
312 342
313 RefPtr<SharedBuffer> valueBuffer = prpValueBuffer; 343 RefPtr<SharedBuffer> valueBuffer = prpValueBuffer;
314 RefPtr<IDBKey> primaryKey = prpPrimaryKey; 344 RefPtr<IDBKey> primaryKey = prpPrimaryKey;
345 ASSERT(!m_blobInfo.get());
346 m_blobInfo = blobInfo;
315 347
316 #ifndef NDEBUG 348 #ifndef NDEBUG
317 assertPrimaryKeyValidOrInjectable(m_scriptState.get(), valueBuffer, primaryK ey, keyPath); 349 assertPrimaryKeyValidOrInjectable(m_scriptState.get(), valueBuffer, m_blobIn fo.get(), primaryKey, keyPath);
318 #endif 350 #endif
319 351
320 onSuccessInternal(IDBAny::create(valueBuffer, primaryKey, keyPath)); 352 onSuccessInternal(IDBAny::create(valueBuffer, m_blobInfo.get(), primaryKey, keyPath));
321 } 353 }
322 354
323 void IDBRequest::onSuccess(int64_t value) 355 void IDBRequest::onSuccess(int64_t value)
324 { 356 {
325 IDB_TRACE("IDBRequest::onSuccess(int64_t)"); 357 IDB_TRACE("IDBRequest::onSuccess(int64_t)");
326 if (!shouldEnqueueEvent()) 358 if (!shouldEnqueueEvent())
327 return; 359 return;
328 onSuccessInternal(IDBAny::create(value)); 360 onSuccessInternal(IDBAny::create(value));
329 } 361 }
330 362
(...skipping 12 matching lines...) Expand all
343 setResult(result); 375 setResult(result);
344 enqueueEvent(Event::create(EventTypeNames::success)); 376 enqueueEvent(Event::create(EventTypeNames::success));
345 } 377 }
346 378
347 void IDBRequest::setResult(PassRefPtr<IDBAny> result) 379 void IDBRequest::setResult(PassRefPtr<IDBAny> result)
348 { 380 {
349 m_result = result; 381 m_result = result;
350 m_resultDirty = true; 382 m_resultDirty = true;
351 } 383 }
352 384
353 void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey , PassRefPtr<SharedBuffer> value) 385 void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey , PassRefPtr<SharedBuffer> value, PassOwnPtr<Vector<blink::WebBlobInfo> > blobIn fo)
354 { 386 {
355 IDB_TRACE("IDBRequest::onSuccess(key, primaryKey, value)"); 387 IDB_TRACE("IDBRequest::onSuccess(key, primaryKey, value)");
356 if (!shouldEnqueueEvent()) 388 if (!shouldEnqueueEvent())
357 return; 389 return;
358 390
359 ASSERT(m_pendingCursor); 391 ASSERT(m_pendingCursor);
360 setResultCursor(m_pendingCursor.release(), key, primaryKey, value); 392 setResultCursor(m_pendingCursor.release(), key, primaryKey, value, blobInfo) ;
361 } 393 }
362 394
363 bool IDBRequest::hasPendingActivity() const 395 bool IDBRequest::hasPendingActivity() const
364 { 396 {
365 // FIXME: In an ideal world, we should return true as long as anyone has a o r can 397 // FIXME: In an ideal world, we should return true as long as anyone has a o r can
366 // get a handle to us and we have event listeners. This is order to h andle 398 // get a handle to us and we have event listeners. This is order to h andle
367 // user generated events properly. 399 // user generated events properly.
368 return m_hasPendingActivity && !m_contextStopped; 400 return m_hasPendingActivity && !m_contextStopped;
369 } 401 }
370 402
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
423 // this object to actually hold a reference to the database (to ensure 455 // this object to actually hold a reference to the database (to ensure
424 // it stays alive). 456 // it stays alive).
425 targets.append(m_transaction->db()); 457 targets.append(m_transaction->db());
426 } 458 }
427 459
428 // Cursor properties should not be updated until the success event is being dispatched. 460 // Cursor properties should not be updated until the success event is being dispatched.
429 RefPtr<IDBCursor> cursorToNotify; 461 RefPtr<IDBCursor> cursorToNotify;
430 if (event->type() == EventTypeNames::success) { 462 if (event->type() == EventTypeNames::success) {
431 cursorToNotify = getResultCursor(); 463 cursorToNotify = getResultCursor();
432 if (cursorToNotify) 464 if (cursorToNotify)
433 cursorToNotify->setValueReady(m_cursorKey.release(), m_cursorPrimary Key.release(), m_cursorValue.release()); 465 cursorToNotify->setValueReady(m_cursorKey.release(), m_cursorPrimary Key.release(), m_cursorValue.release(), m_blobInfo.release());
434 } 466 }
435 467
436 if (event->type() == EventTypeNames::upgradeneeded) { 468 if (event->type() == EventTypeNames::upgradeneeded) {
437 ASSERT(!m_didFireUpgradeNeededEvent); 469 ASSERT(!m_didFireUpgradeNeededEvent);
438 m_didFireUpgradeNeededEvent = true; 470 m_didFireUpgradeNeededEvent = true;
439 } 471 }
440 472
441 // FIXME: When we allow custom event dispatching, this will probably need to change. 473 // FIXME: When we allow custom event dispatching, this will probably need to change.
442 ASSERT_WITH_MESSAGE(event->type() == EventTypeNames::success || event->type( ) == EventTypeNames::error || event->type() == EventTypeNames::blocked || event- >type() == EventTypeNames::upgradeneeded, "event type was %s", event->type().utf 8().data()); 474 ASSERT_WITH_MESSAGE(event->type() == EventTypeNames::success || event->type( ) == EventTypeNames::error || event->type() == EventTypeNames::blocked || event- >type() == EventTypeNames::upgradeneeded, "event type was %s", event->type().utf 8().data());
443 const bool setTransactionActive = m_transaction && (event->type() == EventTy peNames::success || event->type() == EventTypeNames::upgradeneeded || (event->ty pe() == EventTypeNames::error && !m_requestAborted)); 475 const bool setTransactionActive = m_transaction && (event->type() == EventTy peNames::success || event->type() == EventTypeNames::upgradeneeded || (event->ty pe() == EventTypeNames::error && !m_requestAborted));
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
518 550
519 void IDBRequest::dequeueEvent(Event* event) 551 void IDBRequest::dequeueEvent(Event* event)
520 { 552 {
521 for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { 553 for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
522 if (m_enqueuedEvents[i].get() == event) 554 if (m_enqueuedEvents[i].get() == event)
523 m_enqueuedEvents.remove(i); 555 m_enqueuedEvents.remove(i);
524 } 556 }
525 } 557 }
526 558
527 } // namespace WebCore 559 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698