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

Side by Side Diff: content/browser/in_process_webkit/indexed_db_key_utility_client.cc

Issue 10272018: Move IndexedDBKey, IndexedDBKeyRange into content namespace (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Use "using content::*" aliasing for IDB/SSV types Created 8 years, 7 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 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/in_process_webkit/indexed_db_key_utility_client.h" 5 #include "content/browser/in_process_webkit/indexed_db_key_utility_client.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/lazy_instance.h" 8 #include "base/lazy_instance.h"
9 #include "base/synchronization/waitable_event.h" 9 #include "base/synchronization/waitable_event.h"
10 #include "content/browser/utility_process_host_impl.h" 10 #include "content/browser/utility_process_host_impl.h"
11 #include "content/common/indexed_db/indexed_db_key.h" 11 #include "content/common/indexed_db/indexed_db_key.h"
12 #include "content/common/indexed_db/indexed_db_key_path.h" 12 #include "content/common/indexed_db/indexed_db_key_path.h"
13 #include "content/common/indexed_db/indexed_db_messages.h" 13 #include "content/common/indexed_db/indexed_db_messages.h"
14 #include "content/common/utility_messages.h" 14 #include "content/common/utility_messages.h"
15 #include "content/public/browser/utility_process_host_client.h" 15 #include "content/public/browser/utility_process_host_client.h"
16 #include "content/public/common/serialized_script_value.h" 16 #include "content/public/common/serialized_script_value.h"
17 17
18 using content::BrowserThread; 18 using content::BrowserThread;
19 using content::IndexedDBKey;
19 using content::IndexedDBKeyPath; 20 using content::IndexedDBKeyPath;
20 using content::UtilityProcessHostClient; 21 using content::UtilityProcessHostClient;
22 using content::SerializedScriptValue;
21 23
22 // This class is used to obtain IndexedDBKeys from SerializedScriptValues 24 // This class is used to obtain IndexedDBKeys from SerializedScriptValues
23 // given an IDBKeyPath. It uses UtilityProcess to do this inside a sandbox 25 // given an IDBKeyPath. It uses UtilityProcess to do this inside a sandbox
24 // (a V8 lock is required there). At this level, all methods are synchronous 26 // (a V8 lock is required there). At this level, all methods are synchronous
25 // as required by the caller. The public API is used on WEBKIT thread, 27 // as required by the caller. The public API is used on WEBKIT thread,
26 // but internally it moves around to UI and IO as needed. 28 // but internally it moves around to UI and IO as needed.
27 class KeyUtilityClientImpl 29 class KeyUtilityClientImpl
28 : public base::RefCountedThreadSafe<KeyUtilityClientImpl> { 30 : public base::RefCountedThreadSafe<KeyUtilityClientImpl> {
29 public: 31 public:
30 KeyUtilityClientImpl(); 32 KeyUtilityClientImpl();
31 33
32 // Starts the UtilityProcess. Must be called before any other method. 34 // Starts the UtilityProcess. Must be called before any other method.
33 void StartUtilityProcess(); 35 void StartUtilityProcess();
34 36
35 // Stops the UtilityProcess. No further keys can be created after this. 37 // Stops the UtilityProcess. No further keys can be created after this.
36 void Shutdown(); 38 void Shutdown();
37 39
38 // Synchronously obtain the |keys| from |values| for the given |key_path|. 40 // Synchronously obtain the |keys| from |values| for the given |key_path|.
39 void CreateIDBKeysFromSerializedValuesAndKeyPath( 41 void CreateIDBKeysFromSerializedValuesAndKeyPath(
40 const std::vector<content::SerializedScriptValue>& values, 42 const std::vector<SerializedScriptValue>& values,
41 const IndexedDBKeyPath& key_path, 43 const IndexedDBKeyPath& key_path,
42 std::vector<IndexedDBKey>* keys); 44 std::vector<IndexedDBKey>* keys);
43 45
44 // Synchronously inject |key| into |value| using the given |key_path|, 46 // Synchronously inject |key| into |value| using the given |key_path|,
45 // returning the new value. 47 // returning the new value.
46 content::SerializedScriptValue InjectIDBKeyIntoSerializedValue( 48 SerializedScriptValue InjectIDBKeyIntoSerializedValue(
47 const IndexedDBKey& key, 49 const IndexedDBKey& key,
48 const content::SerializedScriptValue& value, 50 const SerializedScriptValue& value,
49 const IndexedDBKeyPath& key_path); 51 const IndexedDBKeyPath& key_path);
50 52
51 private: 53 private:
52 class Client : public UtilityProcessHostClient { 54 class Client : public UtilityProcessHostClient {
53 public: 55 public:
54 explicit Client(KeyUtilityClientImpl* parent); 56 explicit Client(KeyUtilityClientImpl* parent);
55 57
56 // UtilityProcessHostClient 58 // UtilityProcessHostClient
57 virtual void OnProcessCrashed(int exit_code); 59 virtual void OnProcessCrashed(int exit_code);
58 virtual bool OnMessageReceived(const IPC::Message& message); 60 virtual bool OnMessageReceived(const IPC::Message& message);
59 61
60 // IPC message handlers 62 // IPC message handlers
61 void OnIDBKeysFromValuesAndKeyPathSucceeded( 63 void OnIDBKeysFromValuesAndKeyPathSucceeded(
62 int id, const std::vector<IndexedDBKey>& keys); 64 int id, const std::vector<IndexedDBKey>& keys);
63 void OnIDBKeysFromValuesAndKeyPathFailed(int id); 65 void OnIDBKeysFromValuesAndKeyPathFailed(int id);
64 void OnInjectIDBKeyFinished(const content::SerializedScriptValue& value); 66 void OnInjectIDBKeyFinished(const SerializedScriptValue& value);
65 67
66 private: 68 private:
67 virtual ~Client() {} 69 virtual ~Client() {}
68 70
69 KeyUtilityClientImpl* parent_; 71 KeyUtilityClientImpl* parent_;
70 72
71 DISALLOW_COPY_AND_ASSIGN(Client); 73 DISALLOW_COPY_AND_ASSIGN(Client);
72 }; 74 };
73 75
74 friend class base::RefCountedThreadSafe<KeyUtilityClientImpl>; 76 friend class base::RefCountedThreadSafe<KeyUtilityClientImpl>;
75 ~KeyUtilityClientImpl(); 77 ~KeyUtilityClientImpl();
76 78
77 void GetRDHAndStartUtilityProcess(); 79 void GetRDHAndStartUtilityProcess();
78 void StartUtilityProcessInternal(); 80 void StartUtilityProcessInternal();
79 void EndUtilityProcessInternal(); 81 void EndUtilityProcessInternal();
80 void CallStartIDBKeyFromValueAndKeyPathFromIOThread( 82 void CallStartIDBKeyFromValueAndKeyPathFromIOThread(
81 const std::vector<content::SerializedScriptValue>& values, 83 const std::vector<SerializedScriptValue>& values,
82 const IndexedDBKeyPath& key_path); 84 const IndexedDBKeyPath& key_path);
83 void CallStartInjectIDBKeyFromIOThread( 85 void CallStartInjectIDBKeyFromIOThread(
84 const IndexedDBKey& key, 86 const IndexedDBKey& key,
85 const content::SerializedScriptValue& value, 87 const SerializedScriptValue& value,
86 const IndexedDBKeyPath& key_path); 88 const IndexedDBKeyPath& key_path);
87 89
88 void SetKeys(const std::vector<IndexedDBKey>& keys); 90 void SetKeys(const std::vector<IndexedDBKey>& keys);
89 void FinishCreatingKeys(); 91 void FinishCreatingKeys();
90 void SetValueAfterInjection(const content::SerializedScriptValue& value); 92 void SetValueAfterInjection(const SerializedScriptValue& value);
91 void FinishInjectingKey(); 93 void FinishInjectingKey();
92 94
93 base::WaitableEvent waitable_event_; 95 base::WaitableEvent waitable_event_;
94 96
95 // Used in both IO and WEBKIT threads, but guarded by WaitableEvent, i.e., 97 // Used in both IO and WEBKIT threads, but guarded by WaitableEvent, i.e.,
96 // these members are only set / read when the other thread is blocked. 98 // these members are only set / read when the other thread is blocked.
97 enum State { 99 enum State {
98 STATE_UNINITIALIZED, 100 STATE_UNINITIALIZED,
99 STATE_INITIALIZED, 101 STATE_INITIALIZED,
100 STATE_CREATING_KEYS, 102 STATE_CREATING_KEYS,
101 STATE_INJECTING_KEY, 103 STATE_INJECTING_KEY,
102 STATE_SHUTDOWN, 104 STATE_SHUTDOWN,
103 }; 105 };
104 State state_; 106 State state_;
105 std::vector<IndexedDBKey> keys_; 107 std::vector<IndexedDBKey> keys_;
106 content::SerializedScriptValue value_after_injection_; 108 SerializedScriptValue value_after_injection_;
107 109
108 // Used in the IO thread. 110 // Used in the IO thread.
109 base::WeakPtr<content::UtilityProcessHost> utility_process_host_; 111 base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
110 scoped_refptr<Client> client_; 112 scoped_refptr<Client> client_;
111 113
112 DISALLOW_COPY_AND_ASSIGN(KeyUtilityClientImpl); 114 DISALLOW_COPY_AND_ASSIGN(KeyUtilityClientImpl);
113 }; 115 };
114 116
115 // IndexedDBKeyUtilityClient definitions. 117 // IndexedDBKeyUtilityClient definitions.
116 118
(...skipping 15 matching lines...) Expand all
132 IndexedDBKeyUtilityClient* instance = client_instance.Pointer(); 134 IndexedDBKeyUtilityClient* instance = client_instance.Pointer();
133 if (!instance->impl_) 135 if (!instance->impl_)
134 return; 136 return;
135 137
136 instance->is_shutdown_ = true; 138 instance->is_shutdown_ = true;
137 instance->impl_->Shutdown(); 139 instance->impl_->Shutdown();
138 } 140 }
139 141
140 // static 142 // static
141 void IndexedDBKeyUtilityClient::CreateIDBKeysFromSerializedValuesAndKeyPath( 143 void IndexedDBKeyUtilityClient::CreateIDBKeysFromSerializedValuesAndKeyPath(
142 const std::vector<content::SerializedScriptValue>& values, 144 const std::vector<SerializedScriptValue>& values,
143 const IndexedDBKeyPath& key_path, 145 const IndexedDBKeyPath& key_path,
144 std::vector<IndexedDBKey>* keys) { 146 std::vector<IndexedDBKey>* keys) {
145 IndexedDBKeyUtilityClient* instance = client_instance.Pointer(); 147 IndexedDBKeyUtilityClient* instance = client_instance.Pointer();
146 148
147 if (instance->is_shutdown_) { 149 if (instance->is_shutdown_) {
148 keys->clear(); 150 keys->clear();
149 return; 151 return;
150 } 152 }
151 153
152 if (!instance->impl_) { 154 if (!instance->impl_) {
153 instance->impl_ = new KeyUtilityClientImpl(); 155 instance->impl_ = new KeyUtilityClientImpl();
154 instance->impl_->StartUtilityProcess(); 156 instance->impl_->StartUtilityProcess();
155 } 157 }
156 158
157 instance->impl_->CreateIDBKeysFromSerializedValuesAndKeyPath(values, key_path, 159 instance->impl_->CreateIDBKeysFromSerializedValuesAndKeyPath(values, key_path,
158 keys); 160 keys);
159 } 161 }
160 162
161 // static 163 // static
162 content::SerializedScriptValue 164 SerializedScriptValue
163 IndexedDBKeyUtilityClient::InjectIDBKeyIntoSerializedValue( 165 IndexedDBKeyUtilityClient::InjectIDBKeyIntoSerializedValue(
164 const IndexedDBKey& key, const content::SerializedScriptValue& value, 166 const IndexedDBKey& key, const SerializedScriptValue& value,
165 const IndexedDBKeyPath& key_path) { 167 const IndexedDBKeyPath& key_path) {
166 IndexedDBKeyUtilityClient* instance = client_instance.Pointer(); 168 IndexedDBKeyUtilityClient* instance = client_instance.Pointer();
167 169
168 if (instance->is_shutdown_) 170 if (instance->is_shutdown_)
169 return content::SerializedScriptValue(); 171 return SerializedScriptValue();
170 172
171 if (!instance->impl_) { 173 if (!instance->impl_) {
172 instance->impl_ = new KeyUtilityClientImpl(); 174 instance->impl_ = new KeyUtilityClientImpl();
173 instance->impl_->StartUtilityProcess(); 175 instance->impl_->StartUtilityProcess();
174 } 176 }
175 177
176 return instance->impl_->InjectIDBKeyIntoSerializedValue(key, value, key_path); 178 return instance->impl_->InjectIDBKeyIntoSerializedValue(key, value, key_path);
177 } 179 }
178 180
179 181
(...skipping 26 matching lines...) Expand all
206 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); 208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
207 DCHECK(state_ == STATE_UNINITIALIZED); 209 DCHECK(state_ == STATE_UNINITIALIZED);
208 210
209 GetRDHAndStartUtilityProcess(); 211 GetRDHAndStartUtilityProcess();
210 waitable_event_.Wait(); 212 waitable_event_.Wait();
211 213
212 DCHECK(state_ == STATE_INITIALIZED); 214 DCHECK(state_ == STATE_INITIALIZED);
213 } 215 }
214 216
215 void KeyUtilityClientImpl::CreateIDBKeysFromSerializedValuesAndKeyPath( 217 void KeyUtilityClientImpl::CreateIDBKeysFromSerializedValuesAndKeyPath(
216 const std::vector<content::SerializedScriptValue>& values, 218 const std::vector<SerializedScriptValue>& values,
217 const IndexedDBKeyPath& key_path, 219 const IndexedDBKeyPath& key_path,
218 std::vector<IndexedDBKey>* keys) { 220 std::vector<IndexedDBKey>* keys) {
219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); 221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
220 if (state_ == STATE_SHUTDOWN) { 222 if (state_ == STATE_SHUTDOWN) {
221 keys->clear(); 223 keys->clear();
222 return; 224 return;
223 } 225 }
224 226
225 DCHECK(state_ == STATE_INITIALIZED); 227 DCHECK(state_ == STATE_INITIALIZED);
226 228
227 state_ = STATE_CREATING_KEYS; 229 state_ = STATE_CREATING_KEYS;
228 CallStartIDBKeyFromValueAndKeyPathFromIOThread(values, key_path); 230 CallStartIDBKeyFromValueAndKeyPathFromIOThread(values, key_path);
229 waitable_event_.Wait(); 231 waitable_event_.Wait();
230 DCHECK(state_ == STATE_INITIALIZED); 232 DCHECK(state_ == STATE_INITIALIZED);
231 233
232 *keys = keys_; 234 *keys = keys_;
233 } 235 }
234 236
235 content::SerializedScriptValue 237 SerializedScriptValue KeyUtilityClientImpl::InjectIDBKeyIntoSerializedValue(
236 KeyUtilityClientImpl::InjectIDBKeyIntoSerializedValue(
237 const IndexedDBKey& key, 238 const IndexedDBKey& key,
238 const content::SerializedScriptValue& value, 239 const SerializedScriptValue& value,
239 const IndexedDBKeyPath& key_path) { 240 const IndexedDBKeyPath& key_path) {
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); 241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
241 if (state_ == STATE_SHUTDOWN) 242 if (state_ == STATE_SHUTDOWN)
242 return content::SerializedScriptValue(); 243 return SerializedScriptValue();
243 244
244 DCHECK(state_ == STATE_INITIALIZED); 245 DCHECK(state_ == STATE_INITIALIZED);
245 246
246 state_ = STATE_INJECTING_KEY; 247 state_ = STATE_INJECTING_KEY;
247 CallStartInjectIDBKeyFromIOThread(key, value, key_path); 248 CallStartInjectIDBKeyFromIOThread(key, value, key_path);
248 249
249 waitable_event_.Wait(); 250 waitable_event_.Wait();
250 DCHECK(state_ == STATE_INITIALIZED); 251 DCHECK(state_ == STATE_INITIALIZED);
251 252
252 return value_after_injection_; 253 return value_after_injection_;
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
300 if (utility_process_host_) { 301 if (utility_process_host_) {
301 utility_process_host_->EndBatchMode(); 302 utility_process_host_->EndBatchMode();
302 utility_process_host_.reset(); 303 utility_process_host_.reset();
303 } 304 }
304 client_ = NULL; 305 client_ = NULL;
305 state_ = STATE_SHUTDOWN; 306 state_ = STATE_SHUTDOWN;
306 waitable_event_.Signal(); 307 waitable_event_.Signal();
307 } 308 }
308 309
309 void KeyUtilityClientImpl::CallStartIDBKeyFromValueAndKeyPathFromIOThread( 310 void KeyUtilityClientImpl::CallStartIDBKeyFromValueAndKeyPathFromIOThread(
310 const std::vector<content::SerializedScriptValue>& values, 311 const std::vector<SerializedScriptValue>& values,
311 const IndexedDBKeyPath& key_path) { 312 const IndexedDBKeyPath& key_path) {
312 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { 313 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
313 BrowserThread::PostTask( 314 BrowserThread::PostTask(
314 BrowserThread::IO, FROM_HERE, 315 BrowserThread::IO, FROM_HERE,
315 base::Bind(&KeyUtilityClientImpl:: 316 base::Bind(&KeyUtilityClientImpl::
316 CallStartIDBKeyFromValueAndKeyPathFromIOThread, 317 CallStartIDBKeyFromValueAndKeyPathFromIOThread,
317 this, values, key_path)); 318 this, values, key_path));
318 return; 319 return;
319 } 320 }
320 321
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
322 if (utility_process_host_) { 323 if (utility_process_host_) {
323 utility_process_host_->Send(new UtilityMsg_IDBKeysFromValuesAndKeyPath( 324 utility_process_host_->Send(new UtilityMsg_IDBKeysFromValuesAndKeyPath(
324 0, values, key_path)); 325 0, values, key_path));
325 } 326 }
326 } 327 }
327 328
328 void KeyUtilityClientImpl::CallStartInjectIDBKeyFromIOThread( 329 void KeyUtilityClientImpl::CallStartInjectIDBKeyFromIOThread(
329 const IndexedDBKey& key, 330 const IndexedDBKey& key,
330 const content::SerializedScriptValue& value, 331 const SerializedScriptValue& value,
331 const IndexedDBKeyPath& key_path) { 332 const IndexedDBKeyPath& key_path) {
332 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { 333 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
333 BrowserThread::PostTask( 334 BrowserThread::PostTask(
334 BrowserThread::IO, FROM_HERE, 335 BrowserThread::IO, FROM_HERE,
335 base::Bind(&KeyUtilityClientImpl::CallStartInjectIDBKeyFromIOThread, 336 base::Bind(&KeyUtilityClientImpl::CallStartInjectIDBKeyFromIOThread,
336 this, key, value, key_path)); 337 this, key, value, key_path));
337 return; 338 return;
338 } 339 }
339 340
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
341 if (utility_process_host_) 342 if (utility_process_host_)
342 utility_process_host_->Send(new UtilityMsg_InjectIDBKey( 343 utility_process_host_->Send(new UtilityMsg_InjectIDBKey(
343 key, value, key_path)); 344 key, value, key_path));
344 } 345 }
345 346
346 void KeyUtilityClientImpl::SetKeys(const std::vector<IndexedDBKey>& keys) { 347 void KeyUtilityClientImpl::SetKeys(const std::vector<IndexedDBKey>& keys) {
347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
348 keys_ = keys; 349 keys_ = keys;
349 } 350 }
350 351
351 void KeyUtilityClientImpl::FinishCreatingKeys() { 352 void KeyUtilityClientImpl::FinishCreatingKeys() {
352 DCHECK(state_ == STATE_CREATING_KEYS); 353 DCHECK(state_ == STATE_CREATING_KEYS);
353 state_ = STATE_INITIALIZED; 354 state_ = STATE_INITIALIZED;
354 waitable_event_.Signal(); 355 waitable_event_.Signal();
355 } 356 }
356 357
357 void KeyUtilityClientImpl::SetValueAfterInjection( 358 void KeyUtilityClientImpl::SetValueAfterInjection(
358 const content::SerializedScriptValue& value) { 359 const SerializedScriptValue& value) {
359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
360 value_after_injection_ = value; 361 value_after_injection_ = value;
361 } 362 }
362 363
363 void KeyUtilityClientImpl::FinishInjectingKey() { 364 void KeyUtilityClientImpl::FinishInjectingKey() {
364 DCHECK(state_ == STATE_INJECTING_KEY); 365 DCHECK(state_ == STATE_INJECTING_KEY);
365 state_ = STATE_INITIALIZED; 366 state_ = STATE_INITIALIZED;
366 waitable_event_.Signal(); 367 waitable_event_.Signal();
367 } 368 }
368 369
(...skipping 20 matching lines...) Expand all
389 return handled; 390 return handled;
390 } 391 }
391 392
392 void KeyUtilityClientImpl::Client::OnIDBKeysFromValuesAndKeyPathSucceeded( 393 void KeyUtilityClientImpl::Client::OnIDBKeysFromValuesAndKeyPathSucceeded(
393 int id, const std::vector<IndexedDBKey>& keys) { 394 int id, const std::vector<IndexedDBKey>& keys) {
394 parent_->SetKeys(keys); 395 parent_->SetKeys(keys);
395 parent_->FinishCreatingKeys(); 396 parent_->FinishCreatingKeys();
396 } 397 }
397 398
398 void KeyUtilityClientImpl::Client::OnInjectIDBKeyFinished( 399 void KeyUtilityClientImpl::Client::OnInjectIDBKeyFinished(
399 const content::SerializedScriptValue& value) { 400 const SerializedScriptValue& value) {
400 parent_->SetValueAfterInjection(value); 401 parent_->SetValueAfterInjection(value);
401 parent_->FinishInjectingKey(); 402 parent_->FinishInjectingKey();
402 } 403 }
403 404
404 void KeyUtilityClientImpl::Client::OnIDBKeysFromValuesAndKeyPathFailed( 405 void KeyUtilityClientImpl::Client::OnIDBKeysFromValuesAndKeyPathFailed(
405 int id) { 406 int id) {
406 parent_->FinishCreatingKeys(); 407 parent_->FinishCreatingKeys();
407 } 408 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698