Skip to content

Commit 209ad61

Browse files
campersauamaitland
authored andcommitted
Core - Cancel pending tasks when browser crashed or v8 context gets released
1 parent c579ddf commit 209ad61

File tree

7 files changed

+197
-48
lines changed

7 files changed

+197
-48
lines changed

CefSharp.Core.Runtime/Internals/CefFrameWrapper.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,15 +237,15 @@ Task<JavascriptResponse^>^ CefFrameWrapper::EvaluateScriptAsync(String^ script,
237237
//If we're unable to get the underlying browser/browserhost then return null
238238
if (!browser.get() || !host.get())
239239
{
240-
return nullptr;
240+
return Task::FromException<JavascriptResponse^>(gcnew InvalidOperationException("Browser host not available"));
241241
}
242242

243243
auto client = static_cast<ClientAdapter*>(host->GetClient().get());
244244

245245
auto pendingTaskRepository = client->GetPendingTaskRepository();
246246

247247
//create a new taskcompletionsource
248-
auto idAndComplectionSource = pendingTaskRepository->CreatePendingTask(timeout);
248+
auto idAndComplectionSource = pendingTaskRepository->CreatePendingTask(Identifier, timeout);
249249

250250
if (useImmediatelyInvokedFuncExpression)
251251
{

CefSharp.Core.Runtime/Internals/ClientAdapter.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,8 @@ namespace CefSharp
701701

702702
void ClientAdapter::OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser, TerminationStatus status, int errorCode, const CefString& errorString)
703703
{
704+
_pendingTaskRepository->CancelPendingTasks();
705+
704706
auto handler = _browserControl->RequestHandler;
705707

706708
if (handler != nullptr)
@@ -1382,9 +1384,13 @@ namespace CefSharp
13821384
//we get here, only continue if we have a valid frame reference
13831385
if (frame.get() && frame->IsValid())
13841386
{
1387+
auto frameId = StringUtils::ToClr(frame->GetIdentifier());
1388+
1389+
_pendingTaskRepository->CancelPendingTasks(frameId);
1390+
13851391
if (frame->IsMain())
13861392
{
1387-
_browserControl->SetCanExecuteJavascriptOnMainFrame(StringUtils::ToClr(frame->GetIdentifier()), false);
1393+
_browserControl->SetCanExecuteJavascriptOnMainFrame(frameId, false);
13881394
}
13891395

13901396
auto handler = _browserControl->RenderProcessMessageHandler;
@@ -1475,14 +1481,16 @@ namespace CefSharp
14751481
return true;
14761482
}
14771483

1484+
auto frameId = StringUtils::ToClr(frame->GetIdentifier());
1485+
14781486
auto callbackFactory = browserAdapter->JavascriptCallbackFactory;
14791487

14801488
auto success = argList->GetBool(0);
14811489
auto callbackId = GetInt64(argList, 1);
14821490

14831491
auto pendingTask = name == kEvaluateJavascriptResponse ?
1484-
_pendingTaskRepository->RemovePendingTask(callbackId) :
1485-
_pendingTaskRepository->RemoveJavascriptCallbackPendingTask(callbackId);
1492+
_pendingTaskRepository->RemovePendingTask(frameId, callbackId) :
1493+
_pendingTaskRepository->RemoveJavascriptCallbackPendingTask(frameId, callbackId);
14861494

14871495
if (pendingTask != nullptr)
14881496
{

CefSharp.Core.Runtime/Internals/ClientAdapter.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@ namespace CefSharp
7070

7171
CloseAllPopups(true);
7272

73-
//this will dispose the repository and cancel all pending tasks
74-
delete _pendingTaskRepository;
73+
_pendingTaskRepository->CancelPendingTasks();
7574

7675
_browser = nullptr;
7776
_browserControl = nullptr;
@@ -80,6 +79,7 @@ namespace CefSharp
8079
_tooltip = nullptr;
8180
_browserAdapter = nullptr;
8281
_popupBrowsers = nullptr;
82+
_pendingTaskRepository = nullptr;
8383
}
8484

8585
HWND GetBrowserHwnd() { return _browserHwnd; }

CefSharp.Core.Runtime/Internals/JavascriptCallbackProxy.cpp

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,30 +27,31 @@ namespace CefSharp
2727
auto browser = GetBrowser();
2828
if (browser == nullptr)
2929
{
30-
throw gcnew InvalidOperationException("Browser instance is null. Check CanExecute before calling this method.");
30+
return Task::FromException<JavascriptResponse^>(gcnew InvalidOperationException("Browser instance is null. Check CanExecute before calling this method."));
3131
}
3232

3333
auto browserWrapper = static_cast<CefBrowserWrapper^>(browser);
34-
auto javascriptNameConverter = GetJavascriptNameConverter();
35-
36-
auto doneCallback = _pendingTasks->CreateJavascriptCallbackPendingTask(_callback->Id, timeout);
37-
38-
auto callbackMessage = CefProcessMessage::Create(kJavascriptCallbackRequest);
39-
auto argList = callbackMessage->GetArgumentList();
40-
SetInt64(argList, 0, doneCallback.Key);
41-
SetInt64(argList, 1, _callback->Id);
42-
auto paramList = CefListValue::Create();
43-
for (int i = 0; i < parameters->Length; i++)
44-
{
45-
auto param = parameters[i];
46-
SerializeV8Object(paramList, i, param, javascriptNameConverter);
47-
}
48-
argList->SetList(2, paramList);
4934

5035
auto frame = browserWrapper->Browser->GetFrameByIdentifier(StringUtils::ToNative(_callback->FrameId));
5136

5237
if (frame.get() && frame->IsValid())
5338
{
39+
auto javascriptNameConverter = GetJavascriptNameConverter();
40+
41+
auto doneCallback = _pendingTasks->CreateJavascriptCallbackPendingTask(_callback->FrameId, _callback->Id, timeout);
42+
43+
auto callbackMessage = CefProcessMessage::Create(kJavascriptCallbackRequest);
44+
auto argList = callbackMessage->GetArgumentList();
45+
SetInt64(argList, 0, doneCallback.Key);
46+
SetInt64(argList, 1, _callback->Id);
47+
auto paramList = CefListValue::Create();
48+
for (int i = 0; i < parameters->Length; i++)
49+
{
50+
auto param = parameters[i];
51+
SerializeV8Object(paramList, i, param, javascriptNameConverter);
52+
}
53+
argList->SetList(2, paramList);
54+
5455
frame->SendProcessMessage(CefProcessId::PID_RENDERER, callbackMessage);
5556

5657
return doneCallback.Value->Task;

CefSharp.Test/Javascript/EvaluateScriptAsyncTests.cs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Text;
99
using System.Threading.Tasks;
1010
using Bogus;
11+
using CefSharp.Example;
1112
using Xunit;
1213
using Xunit.Abstractions;
1314
using Xunit.Repeat;
@@ -26,6 +27,61 @@ public EvaluateScriptAsyncTests(ITestOutputHelper output, CefSharpFixture collec
2627
this.collectionFixture = collectionFixture;
2728
}
2829

30+
[Fact]
31+
public async Task V8Context()
32+
{
33+
Task evaluateCancelAfterDisposeTask;
34+
using (var browser = new CefSharp.OffScreen.ChromiumWebBrowser(automaticallyCreateBrowser: false))
35+
{
36+
await browser.CreateBrowserAsync();
37+
38+
// no V8 context
39+
await Assert.ThrowsAsync<Exception>(() => browser.EvaluateScriptAsync("1+1"));
40+
41+
Task evaluateWithoutV8ContextCancelTask;
42+
Task<int> evaluateWithoutV8ContextTask;
43+
using (var frame = browser.GetMainFrame())
44+
{
45+
evaluateWithoutV8ContextTask = frame.EvaluateScriptAsync<int>("1+2");
46+
evaluateWithoutV8ContextCancelTask = frame.EvaluateScriptAsync("new Promise(resolve => setTimeout(resolve, 1000))");
47+
}
48+
49+
// V8 context
50+
await browser.LoadUrlAsync(CefExample.HelloWorldUrl);
51+
var evaluateCancelAfterV8ContextChangeTask = browser.EvaluateScriptAsync("new Promise(resolve => setTimeout(resolve, 1000))");
52+
53+
Assert.Equal(3, await evaluateWithoutV8ContextTask);
54+
Assert.Equal(4, await browser.EvaluateScriptAsync<int>("1+3"));
55+
56+
// change V8 context
57+
await browser.LoadUrlAsync(CefExample.HelloWorldUrl);
58+
evaluateCancelAfterDisposeTask = browser.EvaluateScriptAsync("new Promise(resolve => setTimeout(resolve, 1000))");
59+
60+
Assert.Equal(5, await browser.EvaluateScriptAsync<int>("1+4"));
61+
62+
await Assert.ThrowsAsync<TaskCanceledException>(() => evaluateCancelAfterV8ContextChangeTask);
63+
await Assert.ThrowsAsync<TaskCanceledException>(() => evaluateWithoutV8ContextCancelTask);
64+
}
65+
await Assert.ThrowsAsync<TaskCanceledException>(() => evaluateCancelAfterDisposeTask);
66+
}
67+
68+
[Fact]
69+
public async Task CancelEvaluateOnOOM()
70+
{
71+
await Assert.ThrowsAsync<TaskCanceledException>(() => Browser.EvaluateScriptAsync(
72+
@"
73+
let array1 = [];
74+
for (let i = 0; i < 10000000; i++) {
75+
let array2 = [];
76+
for (let j = 0; j < 10000000; j++) {
77+
array2.push('a'.repeat(100000000));
78+
}
79+
array1.push(array2);
80+
}
81+
"
82+
));
83+
}
84+
2985
[Theory]
3086
[InlineData(double.MaxValue, "Number.MAX_VALUE")]
3187
[InlineData(double.MaxValue / 2, "Number.MAX_VALUE / 2")]
@@ -264,7 +320,7 @@ public async Task CanEvaluateScriptAsyncReturnArrayBuffer(int iteration)
264320

265321
var randomizer = new Randomizer();
266322

267-
var expected = randomizer.Utf16String(minLength: iteration, maxLength:iteration);
323+
var expected = randomizer.Utf16String(minLength: iteration, maxLength: iteration);
268324
var expectedBytes = Encoding.UTF8.GetBytes(expected);
269325

270326
var javascriptResponse = await Browser.EvaluateScriptAsync($"new TextEncoder().encode('{expected}').buffer");

0 commit comments

Comments
 (0)