mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
318 lines
11 KiB
C++
318 lines
11 KiB
C++
// Copyright 2016 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "flutter/shell/common/platform_view_service_protocol.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "flutter/common/threads.h"
|
|
#include "flutter/shell/common/picture_serializer.h"
|
|
#include "flutter/shell/common/rasterizer.h"
|
|
#include "flutter/shell/common/shell.h"
|
|
#include "lib/ftl/memory/weak_ptr.h"
|
|
#include "third_party/skia/include/core/SkSurface.h"
|
|
#include "third_party/skia/src/utils/SkBase64.h"
|
|
|
|
namespace shell {
|
|
namespace {
|
|
|
|
constexpr char kViewIdPrefx[] = "_flutterView/";
|
|
constexpr size_t kViewIdPrefxLength = sizeof(kViewIdPrefx) - 1;
|
|
|
|
static intptr_t KeyIndex(const char** param_keys,
|
|
intptr_t num_params,
|
|
const char* key) {
|
|
if (param_keys == NULL) {
|
|
return -1;
|
|
}
|
|
for (intptr_t i = 0; i < num_params; i++) {
|
|
if (strcmp(param_keys[i], key) == 0) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static const char* ValueForKey(const char** param_keys,
|
|
const char** param_values,
|
|
intptr_t num_params,
|
|
const char* key) {
|
|
intptr_t index = KeyIndex(param_keys, num_params, key);
|
|
if (index < 0) {
|
|
return NULL;
|
|
}
|
|
return param_values[index];
|
|
}
|
|
|
|
static bool ErrorMissingParameter(const char** json_object, const char* name) {
|
|
const intptr_t kInvalidParams = -32602;
|
|
std::stringstream response;
|
|
response << "{\"code\":" << std::to_string(kInvalidParams) << ",";
|
|
response << "\"message\":\"Invalid params\",";
|
|
response << "\"data\": {\"details\": \"" << name << "\"}}";
|
|
*json_object = strdup(response.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
static bool ErrorBadParameter(const char** json_object,
|
|
const char* name,
|
|
const char* value) {
|
|
const intptr_t kInvalidParams = -32602;
|
|
std::stringstream response;
|
|
response << "{\"code\":" << std::to_string(kInvalidParams) << ",";
|
|
response << "\"message\":\"Invalid params\",";
|
|
response << "\"data\": {\"details\": \"parameter: " << name << " has a bad ";
|
|
response << "value: " << value << "\"}}";
|
|
*json_object = strdup(response.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
static bool ErrorUnknownView(const char** json_object, const char* view_id) {
|
|
const intptr_t kInvalidParams = -32602;
|
|
std::stringstream response;
|
|
response << "{\"code\":" << std::to_string(kInvalidParams) << ",";
|
|
response << "\"message\":\"Invalid params\",";
|
|
response << "\"data\": {\"details\": \"view not found: " << view_id << "\"}}";
|
|
*json_object = strdup(response.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
static bool ErrorServer(const char** json_object, const char* message) {
|
|
const intptr_t kServerError = -32000;
|
|
std::stringstream response;
|
|
response << "{\"code\":" << std::to_string(kServerError) << ",";
|
|
response << "\"message\":\"" << message << "\"}";
|
|
*json_object = strdup(response.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
static void AppendIsolateRef(std::stringstream* stream,
|
|
int64_t main_port,
|
|
const std::string name) {
|
|
*stream << "{\"type\":\"@Isolate\",\"fixedId\":true,\"id\":\"isolates/";
|
|
*stream << main_port << "\",\"name\":\"" << name << "\",";
|
|
*stream << "\"number\":\"" << main_port << "\"}";
|
|
}
|
|
|
|
static void AppendFlutterView(std::stringstream* stream,
|
|
uintptr_t view_id,
|
|
int64_t isolate_id,
|
|
const std::string isolate_name) {
|
|
*stream << "{\"type\":\"FlutterView\", \"id\": \"" << kViewIdPrefx << "0x"
|
|
<< std::hex << view_id << std::dec << "\"";
|
|
if (isolate_id != ILLEGAL_PORT) {
|
|
// Append the isolate (if it exists).
|
|
*stream << ","
|
|
<< "\"isolate\":";
|
|
AppendIsolateRef(stream, isolate_id, isolate_name);
|
|
}
|
|
*stream << "}";
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void PlatformViewServiceProtocol::RegisterHook(bool running_precompiled_code) {
|
|
// Listing of FlutterViews.
|
|
Dart_RegisterRootServiceRequestCallback(kListViewsExtensionName, &ListViews,
|
|
nullptr);
|
|
// Screenshot.
|
|
Dart_RegisterRootServiceRequestCallback(kScreenshotExtensionName, &Screenshot,
|
|
nullptr);
|
|
// The following set of service protocol extensions require debug build
|
|
if (running_precompiled_code) {
|
|
return;
|
|
}
|
|
Dart_RegisterRootServiceRequestCallback(kRunInViewExtensionName, &RunInView,
|
|
nullptr);
|
|
}
|
|
|
|
const char* PlatformViewServiceProtocol::kRunInViewExtensionName =
|
|
"_flutter.runInView";
|
|
|
|
bool PlatformViewServiceProtocol::RunInView(const char* method,
|
|
const char** param_keys,
|
|
const char** param_values,
|
|
intptr_t num_params,
|
|
void* user_data,
|
|
const char** json_object) {
|
|
const char* view_id =
|
|
ValueForKey(param_keys, param_values, num_params, "viewId");
|
|
const char* asset_directory =
|
|
ValueForKey(param_keys, param_values, num_params, "assetDirectory");
|
|
const char* main_script =
|
|
ValueForKey(param_keys, param_values, num_params, "mainScript");
|
|
const char* packages_file =
|
|
ValueForKey(param_keys, param_values, num_params, "packagesFile");
|
|
if (view_id == NULL) {
|
|
return ErrorMissingParameter(json_object, "viewId");
|
|
}
|
|
if (strncmp(view_id, kViewIdPrefx, kViewIdPrefxLength) != 0) {
|
|
return ErrorBadParameter(json_object, "viewId", view_id);
|
|
}
|
|
if (asset_directory == NULL) {
|
|
return ErrorMissingParameter(json_object, "assetDirectory");
|
|
}
|
|
if (main_script == NULL) {
|
|
return ErrorMissingParameter(json_object, "mainScript");
|
|
}
|
|
if (packages_file == NULL) {
|
|
return ErrorMissingParameter(json_object, "packagesFile");
|
|
}
|
|
|
|
// Convert the actual flutter view hex id into a number.
|
|
uintptr_t view_id_as_num =
|
|
std::stoull((view_id + kViewIdPrefxLength), nullptr, 16);
|
|
|
|
// Ask the Shell to run this script in the specified view. This will run a
|
|
// task on the UI thread before returning.
|
|
Shell& shell = Shell::Shared();
|
|
bool view_existed = false;
|
|
Dart_Port main_port = ILLEGAL_PORT;
|
|
std::string isolate_name;
|
|
shell.RunInPlatformView(view_id_as_num, main_script, packages_file,
|
|
asset_directory, &view_existed, &main_port,
|
|
&isolate_name);
|
|
|
|
if (!view_existed) {
|
|
// If the view did not exist this request has definitely failed.
|
|
return ErrorUnknownView(json_object, view_id);
|
|
} else {
|
|
// The view existed and the isolate was created. Success.
|
|
std::stringstream response;
|
|
response << "{\"type\":\"Success\","
|
|
<< "\"view\":";
|
|
AppendFlutterView(&response, view_id_as_num, main_port, isolate_name);
|
|
response << "}";
|
|
*json_object = strdup(response.str().c_str());
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const char* PlatformViewServiceProtocol::kListViewsExtensionName =
|
|
"_flutter.listViews";
|
|
|
|
bool PlatformViewServiceProtocol::ListViews(const char* method,
|
|
const char** param_keys,
|
|
const char** param_values,
|
|
intptr_t num_params,
|
|
void* user_data,
|
|
const char** json_object) {
|
|
// Ask the Shell for the list of platform views. This will run a task on
|
|
// the UI thread before returning.
|
|
Shell& shell = Shell::Shared();
|
|
std::vector<Shell::PlatformViewInfo> platform_views;
|
|
shell.WaitForPlatformViewIds(&platform_views);
|
|
|
|
std::stringstream response;
|
|
|
|
response << "{\"type\":\"FlutterViewList\",\"views\":[";
|
|
bool prefix_comma = false;
|
|
for (auto it = platform_views.begin(); it != platform_views.end(); it++) {
|
|
uintptr_t view_id = it->view_id;
|
|
int64_t isolate_id = it->isolate_id;
|
|
const std::string& isolate_name = it->isolate_name;
|
|
if (!view_id) {
|
|
continue;
|
|
}
|
|
if (prefix_comma) {
|
|
response << ',';
|
|
} else {
|
|
prefix_comma = true;
|
|
}
|
|
AppendFlutterView(&response, view_id, isolate_id, isolate_name);
|
|
}
|
|
response << "]}";
|
|
// Copy the response.
|
|
*json_object = strdup(response.str().c_str());
|
|
return true;
|
|
}
|
|
|
|
const char* PlatformViewServiceProtocol::kScreenshotExtensionName =
|
|
"_flutter.screenshot";
|
|
|
|
static sk_sp<SkData> EncodeBitmapAsPNG(const SkBitmap& bitmap) {
|
|
if (bitmap.empty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
SkPixmap pixmap;
|
|
if (!bitmap.peekPixels(&pixmap)) {
|
|
return nullptr;
|
|
}
|
|
|
|
PngPixelSerializer serializer;
|
|
sk_sp<SkData> data(serializer.encode(pixmap));
|
|
|
|
return data;
|
|
}
|
|
|
|
bool PlatformViewServiceProtocol::Screenshot(const char* method,
|
|
const char** param_keys,
|
|
const char** param_values,
|
|
intptr_t num_params,
|
|
void* user_data,
|
|
const char** json_object) {
|
|
ftl::AutoResetWaitableEvent latch;
|
|
SkBitmap bitmap;
|
|
blink::Threads::Gpu()->PostTask([&latch, &bitmap]() {
|
|
ScreenshotGpuTask(&bitmap);
|
|
latch.Signal();
|
|
});
|
|
|
|
latch.Wait();
|
|
|
|
sk_sp<SkData> png(EncodeBitmapAsPNG(bitmap));
|
|
|
|
if (!png)
|
|
return ErrorServer(json_object, "can not encode screenshot");
|
|
|
|
size_t b64_size = SkBase64::Encode(png->data(), png->size(), nullptr);
|
|
SkAutoTMalloc<char> b64_data(b64_size);
|
|
SkBase64::Encode(png->data(), png->size(), b64_data.get());
|
|
|
|
std::stringstream response;
|
|
response << "{\"type\":\"Screenshot\","
|
|
<< "\"screenshot\":\"" << std::string{b64_data.get(), b64_size}
|
|
<< "\"}";
|
|
*json_object = strdup(response.str().c_str());
|
|
return true;
|
|
}
|
|
|
|
void PlatformViewServiceProtocol::ScreenshotGpuTask(SkBitmap* bitmap) {
|
|
std::vector<ftl::WeakPtr<Rasterizer>> rasterizers;
|
|
Shell::Shared().GetRasterizers(&rasterizers);
|
|
if (rasterizers.size() != 1)
|
|
return;
|
|
|
|
Rasterizer* rasterizer = rasterizers[0].get();
|
|
if (rasterizer == nullptr)
|
|
return;
|
|
|
|
flow::LayerTree* layer_tree = rasterizer->GetLastLayerTree();
|
|
if (layer_tree == nullptr)
|
|
return;
|
|
|
|
const SkISize& frame_size = layer_tree->frame_size();
|
|
if (!bitmap->tryAllocN32Pixels(frame_size.width(), frame_size.height()))
|
|
return;
|
|
|
|
sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect(
|
|
bitmap->info(), bitmap->getPixels(), bitmap->rowBytes());
|
|
|
|
flow::CompositorContext compositor_context(nullptr);
|
|
SkCanvas* canvas = surface->getCanvas();
|
|
flow::CompositorContext::ScopedFrame frame =
|
|
compositor_context.AcquireFrame(nullptr, canvas, false);
|
|
|
|
canvas->clear(SK_ColorBLACK);
|
|
layer_tree->Raster(frame);
|
|
canvas->flush();
|
|
}
|
|
|
|
} // namespace shell
|