Skip to content
Open
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
6 changes: 6 additions & 0 deletions docs/References/App.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ Mark the Application cache group specified by manifest_url obsolete. This method

Send the `close` event to all windows of current app, if no window is blocking the `close` event, then the app will quit after all windows have done shutdown. Use this method to quit an app will give windows a chance to save data.

## App.restart()

Restart current app. The app is relaunched during shutdown and keeps the current NW.js app path, command line switches, and arguments.

This method will **not** send `close` event to windows. Use `App.closeAllWindows()` first if windows need a chance to save data.

## App.crashBrowser()
## App.crashRenderer()

Expand Down
6 changes: 3 additions & 3 deletions src/api/app/app.cc
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,10 @@ void App::Call(Shell* shell,
CommandLine::StringVector args = command_line->GetArgs();
CommandLine::StringVector argv = command_line->original_argv();

// Ignore first non-switch arg if it's not a standalone package.
bool ignore_arg = !package->self_extract();
// Ignore the package path only when it was passed as a positional argument.
bool ignore_arg = !package->self_extract() && !command_line->HasSwitch("nwapp");
for (unsigned i = 1; i < argv.size(); ++i) {
if (ignore_arg && argv[i] == args[0]) {
if (ignore_arg && args.size() && argv[i] == args[0]) {
ignore_arg = false;
continue;
}
Expand Down
4 changes: 4 additions & 0 deletions src/api/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ App.prototype.quit = function() {
nw.callStaticMethod('App', 'Quit', [ ]);
}

App.prototype.restart = function() {
nw.App.restart();
}

App.prototype.closeAllWindows = function() {
nw.callStaticMethod('App', 'CloseAllWindows', [ ]);
}
Expand Down
1 change: 1 addition & 0 deletions src/api/nw_app.idl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace nw.App {
static void enableComponent(ComponentExtensions extension_id, ComponentCallback callback);
static void updateComponent(ComponentExtensions extension_id, ErrorCallback callback);
static void quit();
static void restart();
static void closeAllWindows();
static void clearCache();
static void clearAppCache(DOMString manifest_url);
Expand Down
166 changes: 160 additions & 6 deletions src/api/nw_app_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
#include "content/public/common/content_features.h"

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/current_thread.h"
#include "content/public/browser/browser_task_traits.h"
Expand All @@ -26,6 +32,7 @@
#include "components/keep_alive_registry/keep_alive_registry.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "content/nw/src/api/nw_app.h"
#include "content/nw/src/browser/nw_content_browser_hooks.h"
#include "content/nw/src/nw_base.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
Expand All @@ -44,18 +51,133 @@
#include "net/url_request/url_request_context_getter.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"

#if defined(OS_MAC)
#include "chrome/browser/mac/relauncher.h"
#endif

using namespace extensions::nwapi::nw__app;

namespace extensions {
NwAppQuitFunction::NwAppQuitFunction() {
namespace {

base::FilePath GetAbsolutePackagePath() {
nw::Package* package = nw::package();
if (!package)
return base::FilePath();

base::FilePath path = package->path().NormalizePathSeparators();
if (path.empty() || path.IsAbsolute())
return path;

base::FilePath current_directory;
if (!base::GetCurrentDirectory(&current_directory))
return path;

return current_directory.Append(path).NormalizePathSeparators();
}

NwAppQuitFunction::~NwAppQuitFunction() {
bool ReplaceNwappSwitch(base::CommandLine::StringVector* argv,
const base::FilePath& package_path) {
#if defined(OS_WIN)
const base::CommandLine::StringType nwapp_switch = L"--nwapp=";
const base::CommandLine::StringType alt_nwapp_switch = L"-nwapp=";
const base::CommandLine::StringType win_nwapp_switch = L"/nwapp=";
#else
const base::CommandLine::StringType nwapp_switch = "--nwapp=";
const base::CommandLine::StringType alt_nwapp_switch = "-nwapp=";
#endif

for (auto& arg : *argv) {
if (arg.compare(0, nwapp_switch.length(), nwapp_switch) == 0) {
arg = nwapp_switch + package_path.value();
return true;
}
if (arg.compare(0, alt_nwapp_switch.length(), alt_nwapp_switch) == 0) {
arg = alt_nwapp_switch + package_path.value();
return true;
}
#if defined(OS_WIN)
if (arg.compare(0, win_nwapp_switch.length(), win_nwapp_switch) == 0) {
arg = win_nwapp_switch + package_path.value();
return true;
}
#endif
}

return false;
}

void NwAppQuitFunction::DoJob(extensions::ExtensionRegistrar* registrar,
std::string extension_id) {
bool PathReferencesPackage(const base::CommandLine::StringType& arg,
const base::FilePath& package_path) {
base::FilePath arg_path(arg);
if (!arg_path.IsAbsolute()) {
base::FilePath current_directory;
if (!base::GetCurrentDirectory(&current_directory))
return false;
arg_path = current_directory.Append(arg_path);
}

return arg_path.NormalizePathSeparators() == package_path;
}

void ReplacePackagePathArg(base::CommandLine::StringVector* argv,
const base::FilePath& package_path) {
for (auto it = argv->begin() + 1; it != argv->end(); ++it) {
if (PathReferencesPackage(*it, package_path)) {
*it = package_path.value();
return;
}
}
}

base::CommandLine BuildRelaunchCommandLine() {
const base::CommandLine& current_command_line =
*base::CommandLine::ForCurrentProcess();
base::CommandLine::StringVector argv = current_command_line.original_argv();

base::FilePath exe;
if (base::PathService::Get(base::FILE_EXE, &exe) && !argv.empty())
argv[0] = exe.value();

base::FilePath package_path = GetAbsolutePackagePath();
if (!package_path.empty()) {
bool has_nwapp_switch = ReplaceNwappSwitch(&argv, package_path);
if (!has_nwapp_switch && argv.size() > 1)
ReplacePackagePathArg(&argv, package_path);
}

return base::CommandLine(argv);
}

void RelaunchCurrentApp() {
base::CommandLine command_line = BuildRelaunchCommandLine();
base::FilePath exe;
if (base::PathService::Get(base::FILE_EXE, &exe))
command_line.SetProgram(exe);

#if defined(OS_MAC)
if (!mac_relauncher::RelaunchApp(command_line.argv()))
LOG(ERROR) << "Failed to relaunch NW app";
#else
base::LaunchOptions options;
#if defined(OS_WIN)
if (!exe.empty()) {
options.current_directory = exe.DirName();
options.grant_foreground_privilege = true;
}
#endif

#if defined(OS_LINUX)
options.allow_new_privs = true;
#endif

if (!base::LaunchProcess(command_line, options).IsValid())
LOG(ERROR) << "Failed to relaunch NW app";
#endif
}

void QuitApp(extensions::ExtensionRegistrar* registrar,
const std::string& extension_id) {
if (base::FeatureList::IsEnabled(::features::kNWNewWin)) {
chrome::CloseAllBrowsersAndQuit(true);
// trigger BrowserProcessImpl::Unpin()
Expand All @@ -70,6 +192,20 @@ void NwAppQuitFunction::DoJob(extensions::ExtensionRegistrar* registrar,
registrar->GetWeakPtr(), extension_id));
}

} // namespace

NwAppQuitFunction::NwAppQuitFunction() {

}

NwAppQuitFunction::~NwAppQuitFunction() {
}

void NwAppQuitFunction::DoJob(extensions::ExtensionRegistrar* registrar,
std::string extension_id) {
QuitApp(registrar, extension_id);
}

ExtensionFunction::ResponseAction
NwAppQuitFunction::Run() {
extensions::ExtensionRegistrar* registrar =
Expand All @@ -81,6 +217,24 @@ NwAppQuitFunction::Run() {
return RespondNow(NoArguments());
}

void NwAppRestartFunction::DoJob(extensions::ExtensionRegistrar* registrar,
std::string extension_id) {
nw::ScheduleRelaunchOnShutdown(&RelaunchCurrentApp);
QuitApp(registrar, extension_id);
}

ExtensionFunction::ResponseAction
NwAppRestartFunction::Run() {
extensions::ExtensionRegistrar* registrar =
extensions::ExtensionRegistrar::Get(browser_context());
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&NwAppRestartFunction::DoJob,
registrar, extension_id()));

return RespondNow(NoArguments());
}

void NwAppCloseAllWindowsFunction::DoJob(AppWindowRegistry* registry, std::string id) {
if (base::FeatureList::IsEnabled(::features::kNWNewWin)) {
chrome::CloseAllBrowsers();
Expand Down Expand Up @@ -165,8 +319,8 @@ bool NwAppGetArgvSyncFunction::RunNWSync(base::ListValue* response, std::string*
base::CommandLine::StringVector args = command_line->GetArgs();
base::CommandLine::StringVector argv = command_line->original_argv();

// Ignore first non-switch arg if it's not a standalone package.
bool ignore_arg = !package->self_extract();
// Ignore the package path only when it was passed as a positional argument.
bool ignore_arg = !package->self_extract() && !command_line->HasSwitch("nwapp");
for (unsigned i = 1; i < argv.size(); ++i) {
if (ignore_arg && args.size() && argv[i] == args[0]) {
ignore_arg = false;
Expand Down
14 changes: 14 additions & 0 deletions src/api/nw_app_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ class NwAppQuitFunction : public ExtensionFunction {
void Callback();
};

class NwAppRestartFunction : public ExtensionFunction {
public:
NwAppRestartFunction() {}

static void DoJob(extensions::ExtensionRegistrar* registrar,
std::string extension_id);
protected:
~NwAppRestartFunction() override {}

// ExtensionFunction:
ResponseAction Run() override;
DECLARE_EXTENSION_FUNCTION("nw.App.restart", UNKNOWN)
};

class NwAppCloseAllWindowsFunction : public ExtensionFunction {
public:
NwAppCloseAllWindowsFunction() {}
Expand Down
8 changes: 8 additions & 0 deletions src/browser/nw_content_browser_hooks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ bool g_pinning_renderer = true;
bool g_mixed_context = false;
bool g_in_webview_apply_attr = false;
bool g_in_webview_apply_attr_allow_nw = false;
RelaunchCallback g_relaunch_on_shutdown = nullptr;

} //namespace

#if defined(OS_MAC)
Expand Down Expand Up @@ -98,9 +100,15 @@ bool GetPackageImage(nw::Package* package,
}

void MainPartsPostDestroyThreadsHook() {
if (g_relaunch_on_shutdown)
g_relaunch_on_shutdown();
ReleaseNWPackage();
}

void ScheduleRelaunchOnShutdown(RelaunchCallback relaunch) {
g_relaunch_on_shutdown = relaunch;
}

void RendererProcessTerminatedHook(content::RenderProcessHost* process,
const content::ChildProcessTerminationInfo& info) {
int exit_code = info.exit_code;
Expand Down
2 changes: 2 additions & 0 deletions src/browser/nw_content_browser_hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace content {
namespace nw {

class Package;
using RelaunchCallback = void (*)();

//
// implemented in nw_content_browser_hooks.cc
Expand All @@ -38,6 +39,7 @@ int MainPartsPreCreateThreadsHook();
void MainPartsPreMainMessageLoopRunHook();
// ref in chrome/browser/chrome_browser_main.cc
CONTENT_EXPORT void MainPartsPostDestroyThreadsHook();
CONTENT_EXPORT void ScheduleRelaunchOnShutdown(RelaunchCallback relaunch);
// ref in chrome/browser/extensions/extension_service.cc
CONTENT_EXPORT void RendererProcessTerminatedHook(content::RenderProcessHost* process,
const content::ChildProcessTerminationInfo& info);
Expand Down
Loading