Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions 9 chromium_src/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,15 @@ static_library("chrome") {
]
}
}

sources += [ "//chrome/browser/hang_monitor/hang_crash_dump.h" ]
if (is_mac) {
sources += [ "//chrome/browser/hang_monitor/hang_crash_dump_mac.cc" ]
} else if (is_win) {
sources += [ "//chrome/browser/hang_monitor/hang_crash_dump_win.cc" ]
} else {
sources += [ "//chrome/browser/hang_monitor/hang_crash_dump.cc" ]
}
}

source_set("plugins") {
Expand Down
28 changes: 28 additions & 0 deletions 28 docs/api/web-contents.md
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,34 @@ Navigates to the specified offset from the "current entry".

Returns `Boolean` - Whether the renderer process has crashed.

#### `contents.forcefullyCrashRenderer()`

Forcefully terminates the renderer process that is currently hosting this
`webContents`. This will cause the `render-process-gone` event to be emitted
with the `reason=killed || reason=crashed`. Please note that some webContents share renderer
processes and therefore calling this method may also crash the host process
for other webContents as well.

Calling `reload()` immediately after calling this
method will force the reload to occur in a new process. This should be used
when this process is unstable or unusable, for instance in order to recover
from the `unresponsive` event.

```js
contents.on('unresponsive', async () => {
const { response } = await dialog.showMessageBox({
message: 'App X has become unresponsive',
title: 'Do you want to try forcefully reloading the app?',
buttons: ['OK', 'Cancel'],
cancelId: 1
})
if (response === 0) {
contents.forcefullyCrashRenderer()
contents.reload()
}
})
```

#### `contents.setUserAgent(userAgent)`

* `userAgent` String
Expand Down
27 changes: 27 additions & 0 deletions 27 shell/browser/api/electron_api_web_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/hang_monitor/hang_crash_dump.h"
#include "chrome/browser/ssl/security_state_tab_helper.h"
#include "content/browser/renderer_host/frame_tree_node.h" // nogncheck
#include "content/browser/renderer_host/render_frame_host_manager.h" // nogncheck
Expand Down Expand Up @@ -1763,6 +1764,30 @@ bool WebContents::IsCrashed() const {
return web_contents()->IsCrashed();
}

void WebContents::ForcefullyCrashRenderer() {
content::RenderWidgetHostView* view =
web_contents()->GetRenderWidgetHostView();
if (!view)
return;

content::RenderWidgetHost* rwh = view->GetRenderWidgetHost();
if (!rwh)
return;

content::RenderProcessHost* rph = rwh->GetProcess();
if (rph) {
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// A generic |CrashDumpHungChildProcess()| is not implemented for Linux.
// Instead we send an explicit IPC to crash on the renderer's IO thread.
rph->ForceCrash();
#else
// Try to generate a crash report for the hung process.
CrashDumpHungChildProcess(rph->GetProcess().Handle());
rph->Shutdown(content::RESULT_CODE_HUNG);
#endif
ckerr marked this conversation as resolved.
Show resolved Hide resolved
}
}

void WebContents::SetUserAgent(const std::string& user_agent) {
web_contents()->SetUserAgentOverride(
blink::UserAgentOverride::UserAgentOnly(user_agent), false);
Expand Down Expand Up @@ -2921,6 +2946,8 @@ v8::Local<v8::ObjectTemplate> WebContents::FillObjectTemplate(
.SetMethod("_goForward", &WebContents::GoForward)
.SetMethod("_goToOffset", &WebContents::GoToOffset)
.SetMethod("isCrashed", &WebContents::IsCrashed)
.SetMethod("forcefullyCrashRenderer",
&WebContents::ForcefullyCrashRenderer)
.SetMethod("setUserAgent", &WebContents::SetUserAgent)
.SetMethod("getUserAgent", &WebContents::GetUserAgent)
.SetMethod("savePage", &WebContents::SavePage)
Expand Down
1 change: 1 addition & 0 deletions 1 shell/browser/api/electron_api_web_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ class WebContents : public gin::Wrappable<WebContents>,
const std::string GetWebRTCIPHandlingPolicy() const;
void SetWebRTCIPHandlingPolicy(const std::string& webrtc_ip_handling_policy);
bool IsCrashed() const;
void ForcefullyCrashRenderer();
void SetUserAgent(const std::string& user_agent);
std::string GetUserAgent();
void InsertCSS(const std::string& css);
Expand Down
48 changes: 48 additions & 0 deletions 48 spec-main/api-web-contents-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,54 @@ describe('webContents module', () => {
});
});

const crashPrefs = [
{
nodeIntegration: true
},
{
sandbox: true
}
];

const nicePrefs = (o: any) => {
let s = '';
for (const key of Object.keys(o)) {
s += `${key}=${o[key]}, `;
}
return `(${s.slice(0, s.length - 2)})`;
};

for (const prefs of crashPrefs) {
describe(`crash with webPreferences ${nicePrefs(prefs)}`, () => {
let w: BrowserWindow;
beforeEach(async () => {
w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
await w.loadURL('about:blank');
});
afterEach(closeAllWindows);

it('isCrashed() is false by default', () => {
expect(w.webContents.isCrashed()).to.equal(false);
});

it('forcefullyCrashRenderer() crashes the process with reason=killed||crashed', async () => {
expect(w.webContents.isCrashed()).to.equal(false);
const crashEvent = emittedOnce(w.webContents, 'render-process-gone');
w.webContents.forcefullyCrashRenderer();
const [, details] = await crashEvent;
expect(details.reason === 'killed' || details.reason === 'crashed').to.equal(true, 'reason should be killed || crashed');
expect(w.webContents.isCrashed()).to.equal(true);
});

it('a crashed process is recoverable with reload()', async () => {
expect(w.webContents.isCrashed()).to.equal(false);
w.webContents.forcefullyCrashRenderer();
w.webContents.reload();
expect(w.webContents.isCrashed()).to.equal(false);
});
});
}

// Destroying webContents in its event listener is going to crash when
// Electron is built in Debug mode.
describe('destroy()', () => {
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.