| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "platform/v8_inspector/V8Console.h" | 5 #include "platform/v8_inspector/V8Console.h" |
| 6 | 6 |
| 7 #include "platform/inspector_protocol/Platform.h" | 7 #include "platform/inspector_protocol/Platform.h" |
| 8 #include "platform/inspector_protocol/String16.h" | 8 #include "platform/inspector_protocol/String16.h" |
| 9 #include "platform/v8_inspector/InjectedScript.h" | 9 #include "platform/v8_inspector/InjectedScript.h" |
| 10 #include "platform/v8_inspector/InspectedContext.h" | 10 #include "platform/v8_inspector/InspectedContext.h" |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 { | 69 { |
| 70 if (m_debuggerClient) | 70 if (m_debuggerClient) |
| 71 return m_debuggerClient; | 71 return m_debuggerClient; |
| 72 InspectedContext* inspectedContext = ensureInspectedContext(); | 72 InspectedContext* inspectedContext = ensureInspectedContext(); |
| 73 if (!inspectedContext) | 73 if (!inspectedContext) |
| 74 return nullptr; | 74 return nullptr; |
| 75 m_debuggerClient = inspectedContext->debugger()->client(); | 75 m_debuggerClient = inspectedContext->debugger()->client(); |
| 76 return m_debuggerClient; | 76 return m_debuggerClient; |
| 77 } | 77 } |
| 78 | 78 |
| 79 void reportMessageToConsole(v8::Local<v8::Context> context, ConsoleAPIType t
ype, MessageLevel level, const String16& message, const v8::FunctionCallbackInfo
<v8::Value>* arguments, unsigned skipArgumentCount) | 79 void reportCall(ConsoleAPIType type) |
| 80 { |
| 81 if (!m_info.Length()) |
| 82 return; |
| 83 std::vector<v8::Local<v8::Value>> arguments; |
| 84 for (int i = 0; i < m_info.Length(); ++i) |
| 85 arguments.push_back(m_info[i]); |
| 86 reportCall(type, arguments); |
| 87 } |
| 88 |
| 89 void reportCallWithDefaultArgument(ConsoleAPIType type, const String16& mess
age) |
| 90 { |
| 91 std::vector<v8::Local<v8::Value>> arguments; |
| 92 for (int i = 0; i < m_info.Length(); ++i) |
| 93 arguments.push_back(m_info[i]); |
| 94 if (!m_info.Length()) |
| 95 arguments.push_back(toV8String(m_isolate, message)); |
| 96 reportCall(type, arguments); |
| 97 } |
| 98 |
| 99 void reportCallWithArgument(ConsoleAPIType type, const String16& message) |
| 100 { |
| 101 std::vector<v8::Local<v8::Value>> arguments = { toV8String(m_isolate, me
ssage) }; |
| 102 reportCall(type, arguments); |
| 103 } |
| 104 |
| 105 void reportCall(ConsoleAPIType type, const std::vector<v8::Local<v8::Value>>
& arguments) |
| 80 { | 106 { |
| 81 InspectedContext* inspectedContext = ensureInspectedContext(); | 107 InspectedContext* inspectedContext = ensureInspectedContext(); |
| 82 if (!inspectedContext) | 108 if (!inspectedContext) |
| 83 return; | 109 return; |
| 84 V8DebuggerImpl* debugger = inspectedContext->debugger(); | 110 V8DebuggerImpl* debugger = inspectedContext->debugger(); |
| 85 | 111 std::unique_ptr<V8ConsoleMessage> message = V8ConsoleMessage::createForC
onsoleAPI(debugger->client()->currentTimeMS(), type, arguments, debugger->captur
eStackTrace(false), inspectedContext); |
| 86 std::unique_ptr<V8ConsoleMessage> consoleMessage = nullptr; | 112 debugger->ensureConsoleMessageStorage(inspectedContext->contextGroupId()
)->addMessage(std::move(message)); |
| 87 if (arguments) { | |
| 88 std::vector<v8::Local<v8::Value>> messageArguments; | |
| 89 for (int i = skipArgumentCount; i < arguments->Length(); ++i) | |
| 90 messageArguments.push_back((*arguments)[i]); | |
| 91 consoleMessage = V8ConsoleMessage::createForConsoleAPI(debugger->cli
ent()->currentTimeMS(), type, level, message, &messageArguments, debugger->captu
reStackTrace(false), inspectedContext); | |
| 92 } else { | |
| 93 consoleMessage = V8ConsoleMessage::createForConsoleAPI(debugger->cli
ent()->currentTimeMS(), type, level, message, nullptr, debugger->captureStackTra
ce(false), inspectedContext); | |
| 94 } | |
| 95 debugger->ensureConsoleMessageStorage(inspectedContext->contextGroupId()
)->addMessage(std::move(consoleMessage)); | |
| 96 } | 113 } |
| 97 | 114 |
| 98 void addMessage(ConsoleAPIType type, MessageLevel level, String16 emptyText,
int skipArgumentCount) | 115 void reportDeprecatedCall(const char* id, const String16& message) |
| 99 { | |
| 100 if (emptyText.isEmpty() && !m_info.Length()) | |
| 101 return; | |
| 102 reportMessageToConsole(m_context, type, level, m_info.Length() <= skipAr
gumentCount ? emptyText : String16(), &m_info, skipArgumentCount); | |
| 103 } | |
| 104 | |
| 105 void addMessage(ConsoleAPIType type, MessageLevel level, const String16& mes
sage) | |
| 106 { | |
| 107 reportMessageToConsole(m_context, type, level, message, nullptr, 0 /* sk
ipArgumentsCount */); | |
| 108 } | |
| 109 | |
| 110 void addDeprecationMessage(const char* id, const String16& message) | |
| 111 { | 116 { |
| 112 if (checkAndSetPrivateFlagOnConsole(id, false)) | 117 if (checkAndSetPrivateFlagOnConsole(id, false)) |
| 113 return; | 118 return; |
| 114 reportMessageToConsole(m_context, ConsoleAPIType::kLog, WarningMessageLe
vel, message, nullptr, 0 /* skipArgumentsCount */); | 119 std::vector<v8::Local<v8::Value>> arguments = { toV8String(m_isolate, me
ssage) }; |
| 120 reportCall(ConsoleAPIType::kWarning, arguments); |
| 115 } | 121 } |
| 116 | 122 |
| 117 bool firstArgToBoolean(bool defaultValue) | 123 bool firstArgToBoolean(bool defaultValue) |
| 118 { | 124 { |
| 119 if (m_info.Length() < 1) | 125 if (m_info.Length() < 1) |
| 120 return defaultValue; | 126 return defaultValue; |
| 121 if (m_info[0]->IsBoolean()) | 127 if (m_info[0]->IsBoolean()) |
| 122 return m_info[0].As<v8::Boolean>()->Value(); | 128 return m_info[0].As<v8::Boolean>()->Value(); |
| 123 return m_info[0]->BooleanValue(m_context).FromMaybe(defaultValue); | 129 return m_info[0]->BooleanValue(m_context).FromMaybe(defaultValue); |
| 124 } | 130 } |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 275 func->Set(toV8StringInternalized(context->GetIsolate(), "toString"),
toStringFunction); | 281 func->Set(toV8StringInternalized(context->GetIsolate(), "toString"),
toStringFunction); |
| 276 } | 282 } |
| 277 if (!console->Set(context, funcName, func).FromMaybe(false)) | 283 if (!console->Set(context, funcName, func).FromMaybe(false)) |
| 278 return; | 284 return; |
| 279 } | 285 } |
| 280 | 286 |
| 281 } // namespace | 287 } // namespace |
| 282 | 288 |
| 283 void V8Console::debugCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 289 void V8Console::debugCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 284 { | 290 { |
| 285 ConsoleHelper(info).addMessage(ConsoleAPIType::kLog, DebugMessageLevel, Stri
ng16(), 0); | 291 ConsoleHelper(info).reportCall(ConsoleAPIType::kDebug); |
| 286 } | 292 } |
| 287 | 293 |
| 288 void V8Console::errorCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 294 void V8Console::errorCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 289 { | 295 { |
| 290 ConsoleHelper(info).addMessage(ConsoleAPIType::kLog, ErrorMessageLevel, Stri
ng16(), 0); | 296 ConsoleHelper(info).reportCall(ConsoleAPIType::kError); |
| 291 } | 297 } |
| 292 | 298 |
| 293 void V8Console::infoCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 299 void V8Console::infoCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 294 { | 300 { |
| 295 ConsoleHelper(info).addMessage(ConsoleAPIType::kLog, InfoMessageLevel, Strin
g16(), 0); | 301 ConsoleHelper(info).reportCall(ConsoleAPIType::kInfo); |
| 296 } | 302 } |
| 297 | 303 |
| 298 void V8Console::logCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 304 void V8Console::logCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 299 { | 305 { |
| 300 ConsoleHelper(info).addMessage(ConsoleAPIType::kLog, LogMessageLevel, String
16(), 0); | 306 ConsoleHelper(info).reportCall(ConsoleAPIType::kLog); |
| 301 } | 307 } |
| 302 | 308 |
| 303 void V8Console::warnCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 309 void V8Console::warnCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 304 { | 310 { |
| 305 ConsoleHelper(info).addMessage(ConsoleAPIType::kLog, WarningMessageLevel, St
ring16(), 0); | 311 ConsoleHelper(info).reportCall(ConsoleAPIType::kWarning); |
| 306 } | 312 } |
| 307 | 313 |
| 308 void V8Console::dirCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 314 void V8Console::dirCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 309 { | 315 { |
| 310 ConsoleHelper(info).addMessage(ConsoleAPIType::kDir, LogMessageLevel, String
16(), 0); | 316 ConsoleHelper(info).reportCall(ConsoleAPIType::kDir); |
| 311 } | 317 } |
| 312 | 318 |
| 313 void V8Console::dirxmlCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 319 void V8Console::dirxmlCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 314 { | 320 { |
| 315 ConsoleHelper(info).addMessage(ConsoleAPIType::kDirXML, LogMessageLevel, Str
ing16(), 0); | 321 ConsoleHelper(info).reportCall(ConsoleAPIType::kDirXML); |
| 316 } | 322 } |
| 317 | 323 |
| 318 void V8Console::tableCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 324 void V8Console::tableCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 319 { | 325 { |
| 320 ConsoleHelper(info).addMessage(ConsoleAPIType::kTable, LogMessageLevel, Stri
ng16(), 0); | 326 ConsoleHelper(info).reportCall(ConsoleAPIType::kTable); |
| 321 } | 327 } |
| 322 | 328 |
| 323 void V8Console::traceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 329 void V8Console::traceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 324 { | 330 { |
| 325 ConsoleHelper(info).addMessage(ConsoleAPIType::kTrace, LogMessageLevel, Stri
ng16("console.trace"), 0); | 331 ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kTrace, St
ring16("console.trace")); |
| 326 } | 332 } |
| 327 | 333 |
| 328 void V8Console::groupCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 334 void V8Console::groupCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 329 { | 335 { |
| 330 ConsoleHelper(info).addMessage(ConsoleAPIType::kStartGroup, LogMessageLevel,
String16("console.group"), 0); | 336 ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kStartGrou
p, String16("console.group")); |
| 331 } | 337 } |
| 332 | 338 |
| 333 void V8Console::groupCollapsedCallback(const v8::FunctionCallbackInfo<v8::Value>
& info) | 339 void V8Console::groupCollapsedCallback(const v8::FunctionCallbackInfo<v8::Value>
& info) |
| 334 { | 340 { |
| 335 ConsoleHelper(info).addMessage(ConsoleAPIType::kStartGroupCollapsed, LogMess
ageLevel, String16("console.groupCollapsed"), 0); | 341 ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kStartGrou
pCollapsed, String16("console.groupCollapsed")); |
| 336 } | 342 } |
| 337 | 343 |
| 338 void V8Console::groupEndCallback(const v8::FunctionCallbackInfo<v8::Value>& info
) | 344 void V8Console::groupEndCallback(const v8::FunctionCallbackInfo<v8::Value>& info
) |
| 339 { | 345 { |
| 340 ConsoleHelper(info).addMessage(ConsoleAPIType::kEndGroup, LogMessageLevel, S
tring16("console.groupEnd"), 0); | 346 ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kEndGroup,
String16("console.groupEnd")); |
| 341 } | 347 } |
| 342 | 348 |
| 343 void V8Console::clearCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 349 void V8Console::clearCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 344 { | 350 { |
| 345 ConsoleHelper(info).addMessage(ConsoleAPIType::kClear, LogMessageLevel, Stri
ng16("console.clear"), 0); | 351 ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kClear, St
ring16("console.clear")); |
| 346 } | 352 } |
| 347 | 353 |
| 348 void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 354 void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 349 { | 355 { |
| 350 ConsoleHelper helper(info); | 356 ConsoleHelper helper(info); |
| 351 | 357 |
| 352 String16 title = helper.firstArgToString(String16()); | 358 String16 title = helper.firstArgToString(String16()); |
| 353 String16 identifier; | 359 String16 identifier; |
| 354 if (title.isEmpty()) { | 360 if (title.isEmpty()) { |
| 355 std::unique_ptr<V8StackTraceImpl> stackTrace = V8StackTraceImpl::capture
(nullptr, 0, 1); | 361 std::unique_ptr<V8StackTraceImpl> stackTrace = V8StackTraceImpl::capture
(nullptr, 0, 1); |
| 356 if (stackTrace) | 362 if (stackTrace) |
| 357 identifier = stackTrace->topSourceURL() + ":" + String16::number(sta
ckTrace->topLineNumber()); | 363 identifier = stackTrace->topSourceURL() + ":" + String16::number(sta
ckTrace->topLineNumber()); |
| 358 } else { | 364 } else { |
| 359 identifier = title + "@"; | 365 identifier = title + "@"; |
| 360 } | 366 } |
| 361 | 367 |
| 362 v8::Local<v8::Map> countMap; | 368 v8::Local<v8::Map> countMap; |
| 363 if (!helper.privateMap("V8Console#countMap").ToLocal(&countMap)) | 369 if (!helper.privateMap("V8Console#countMap").ToLocal(&countMap)) |
| 364 return; | 370 return; |
| 365 int64_t count = helper.getIntFromMap(countMap, identifier, 0) + 1; | 371 int64_t count = helper.getIntFromMap(countMap, identifier, 0) + 1; |
| 366 helper.setIntOnMap(countMap, identifier, count); | 372 helper.setIntOnMap(countMap, identifier, count); |
| 367 helper.addMessage(ConsoleAPIType::kCount, DebugMessageLevel, title + ": " +
String16::number(count)); | 373 helper.reportCallWithArgument(ConsoleAPIType::kCount, title + ": " + String1
6::number(count)); |
| 368 } | 374 } |
| 369 | 375 |
| 370 void V8Console::assertCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 376 void V8Console::assertCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 371 { | 377 { |
| 372 ConsoleHelper helper(info); | 378 ConsoleHelper helper(info); |
| 373 if (helper.firstArgToBoolean(false)) | 379 if (helper.firstArgToBoolean(false)) |
| 374 return; | 380 return; |
| 375 helper.addMessage(ConsoleAPIType::kAssert, ErrorMessageLevel, String16("cons
ole.assert"), 1); | 381 |
| 382 std::vector<v8::Local<v8::Value>> arguments; |
| 383 for (int i = 1; i < info.Length(); ++i) |
| 384 arguments.push_back(info[i]); |
| 385 if (info.Length() < 2) |
| 386 arguments.push_back(toV8String(info.GetIsolate(), String16("console.asse
rt"))); |
| 387 helper.reportCall(ConsoleAPIType::kAssert, arguments); |
| 388 |
| 376 if (V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent()) | 389 if (V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent()) |
| 377 debuggerAgent->breakProgramOnException(protocol::Debugger::Paused::Reaso
nEnum::Assert, nullptr); | 390 debuggerAgent->breakProgramOnException(protocol::Debugger::Paused::Reaso
nEnum::Assert, nullptr); |
| 378 } | 391 } |
| 379 | 392 |
| 380 void V8Console::markTimelineCallback(const v8::FunctionCallbackInfo<v8::Value>&
info) | 393 void V8Console::markTimelineCallback(const v8::FunctionCallbackInfo<v8::Value>&
info) |
| 381 { | 394 { |
| 382 ConsoleHelper(info).addDeprecationMessage("V8Console#markTimelineDeprecated"
, "'console.markTimeline' is deprecated. Please use 'console.timeStamp' instead.
"); | 395 ConsoleHelper(info).reportDeprecatedCall("V8Console#markTimelineDeprecated",
"'console.markTimeline' is deprecated. Please use 'console.timeStamp' instead."
); |
| 383 timeStampCallback(info); | 396 timeStampCallback(info); |
| 384 } | 397 } |
| 385 | 398 |
| 386 void V8Console::profileCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 399 void V8Console::profileCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 387 { | 400 { |
| 388 ConsoleHelper helper(info); | 401 ConsoleHelper helper(info); |
| 389 if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent()) | 402 if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent()) |
| 390 profilerAgent->consoleProfile(helper.firstArgToString(String16())); | 403 profilerAgent->consoleProfile(helper.firstArgToString(String16())); |
| 391 } | 404 } |
| 392 | 405 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 420 String16 protocolTitle = helper.firstArgToString("default"); | 433 String16 protocolTitle = helper.firstArgToString("default"); |
| 421 if (timelinePrefix) | 434 if (timelinePrefix) |
| 422 protocolTitle = "Timeline '" + protocolTitle + "'"; | 435 protocolTitle = "Timeline '" + protocolTitle + "'"; |
| 423 client->consoleTimeEnd(protocolTitle); | 436 client->consoleTimeEnd(protocolTitle); |
| 424 | 437 |
| 425 v8::Local<v8::Map> timeMap; | 438 v8::Local<v8::Map> timeMap; |
| 426 if (!helper.privateMap("V8Console#timeMap").ToLocal(&timeMap)) | 439 if (!helper.privateMap("V8Console#timeMap").ToLocal(&timeMap)) |
| 427 return; | 440 return; |
| 428 double elapsed = client->currentTimeMS() - helper.getDoubleFromMap(timeM
ap, protocolTitle, 0.0); | 441 double elapsed = client->currentTimeMS() - helper.getDoubleFromMap(timeM
ap, protocolTitle, 0.0); |
| 429 String16 message = protocolTitle + ": " + String16::fromDoubleFixedPreci
sion(elapsed, 3) + "ms"; | 442 String16 message = protocolTitle + ": " + String16::fromDoubleFixedPreci
sion(elapsed, 3) + "ms"; |
| 430 helper.addMessage(ConsoleAPIType::kTimeEnd, DebugMessageLevel, message); | 443 helper.reportCallWithArgument(ConsoleAPIType::kTimeEnd, message); |
| 431 } | 444 } |
| 432 } | 445 } |
| 433 | 446 |
| 434 void V8Console::timelineCallback(const v8::FunctionCallbackInfo<v8::Value>& info
) | 447 void V8Console::timelineCallback(const v8::FunctionCallbackInfo<v8::Value>& info
) |
| 435 { | 448 { |
| 436 ConsoleHelper(info).addDeprecationMessage("V8Console#timeline", "'console.ti
meline' is deprecated. Please use 'console.time' instead."); | 449 ConsoleHelper(info).reportDeprecatedCall("V8Console#timeline", "'console.tim
eline' is deprecated. Please use 'console.time' instead."); |
| 437 timeFunction(info, true); | 450 timeFunction(info, true); |
| 438 } | 451 } |
| 439 | 452 |
| 440 void V8Console::timelineEndCallback(const v8::FunctionCallbackInfo<v8::Value>& i
nfo) | 453 void V8Console::timelineEndCallback(const v8::FunctionCallbackInfo<v8::Value>& i
nfo) |
| 441 { | 454 { |
| 442 ConsoleHelper(info).addDeprecationMessage("V8Console#timelineEnd", "'console
.timelineEnd' is deprecated. Please use 'console.timeEnd' instead."); | 455 ConsoleHelper(info).reportDeprecatedCall("V8Console#timelineEnd", "'console.
timelineEnd' is deprecated. Please use 'console.timeEnd' instead."); |
| 443 timeEndFunction(info, true); | 456 timeEndFunction(info, true); |
| 444 } | 457 } |
| 445 | 458 |
| 446 void V8Console::timeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 459 void V8Console::timeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 447 { | 460 { |
| 448 timeFunction(info, false); | 461 timeFunction(info, false); |
| 449 } | 462 } |
| 450 | 463 |
| 451 void V8Console::timeEndCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | 464 void V8Console::timeEndCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 452 { | 465 { |
| (...skipping 356 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 809 continue; | 822 continue; |
| 810 if (name->IsString()) { | 823 if (name->IsString()) { |
| 811 v8::Local<v8::Value> descriptor; | 824 v8::Local<v8::Value> descriptor; |
| 812 bool success = m_global->GetOwnPropertyDescriptor(m_context, v8::Loc
al<v8::String>::Cast(name)).ToLocal(&descriptor); | 825 bool success = m_global->GetOwnPropertyDescriptor(m_context, v8::Loc
al<v8::String>::Cast(name)).ToLocal(&descriptor); |
| 813 DCHECK(success); | 826 DCHECK(success); |
| 814 } | 827 } |
| 815 } | 828 } |
| 816 } | 829 } |
| 817 | 830 |
| 818 } // namespace blink | 831 } // namespace blink |
| OLD | NEW |