Chromium 中<input type=“file“>选择文件实现分析c++

一、<input type="file">

带有 type="file" 的 <input> 元素允许用户可以从他们的设备中选择一个或多个文件。选择后,这些文件可以使用提交表单的方式上传到服务器上,或者通过 Javascript 代码和文件 API 对文件进行操作。

<input type="file"> - HTML(超文本标记语言) | MDN

例子:

<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob.com)</title> 
</head>
<body>
<p>

<label for="avatar">Choose a profile picture:</label>
<input type="file" id="avatar" name="avatar" accept="image/png, image/jpeg" />

</body>
</html>

二、file_chooser.mojom定义:

        third_party\blink\public\mojom\choosers\file_chooser.mojom

module blink.mojom;

import "mojo/public/mojom/base/file_path.mojom";
import "mojo/public/mojom/base/string16.mojom";
import "mojo/public/mojom/base/time.mojom";
import "url/mojom/url.mojom";

// FileChooserParams stores arguments for an operation to open a file
// chooser dialog.
struct FileChooserParams {
  enum Mode {
    // Requires that the file exists before allowing the user to pick it.
    kOpen,

    // Like Open, but allows picking multiple files to open.
    kOpenMultiple,

    // Like Open, but selects a folder for upload.  This mode returns multiple
    // FileChooserFileInfo objects for descendant files, rather than a single
    // FileChooserFileInfo for the selected directory.
    kUploadFolder,

    // Allows picking a nonexistent file, and prompts to overwrite if the file
    // already exists. This is not for Blink but for PPAPI.
    kSave,
  };
  Mode mode = kOpen;

  // Title to be used for the dialog. This may be empty for the default title,
  // which will be either "Open" or "Save" depending on the mode.
  mojo_base.mojom.String16 title;

  // Default file name to select in the dialog with kSave mode.
  mojo_base.mojom.FilePath default_file_name;

  // |selected_files| has filenames which a file upload control already
  // selected. A FileChooser implementation may ask a user to select
  //  - removing a file from the selected files,
  //  - appending other files, or
  //  - replacing with other files
  // before opening a file chooser dialog.
  array<mojo_base.mojom.FilePath> selected_files;

  // A list of valid lower-cased MIME types or file extensions specified
  // in an |accept| attribute of an input element, such as "audio/*",
  // "text/plain", ".mp3" ".txt". It is used to restrict selectable
  // files to such types.
  array<mojo_base.mojom.String16> accept_types;

  // Whether the caller needs native file path or not.
  // This may be false if the callsite can handle |file_system_url| field of
  // resultant FileChooserFileInfo objects.
  bool need_local_path = true;

  // See http://www.w3.org/TR/html-media-capture for more information.
  // If true, the data should be obtained using the device's camera/mic/etc.
  bool use_media_capture = false;

  // If non-empty, represents the URL of the requestor if the request was
  // initiated by a document. Note that this value should be considered
  // untrustworthy since it is specified by the sandbox and not validated.
  url.mojom.Url requestor;
};

struct NativeFileInfo {
  mojo_base.mojom.FilePath file_path;

  // The display name of the file that is to be exposed as File.name in the
  // DOM layer.  If it is empty, the base part of the |file_path| is used.
  mojo_base.mojom.String16 display_name;
};

struct FileSystemFileInfo {
  url.mojom.Url url;
  mojo_base.mojom.Time modification_time;
  int64 length = 0;
};

// Represents a file selected by a user.
union FileChooserFileInfo {
  NativeFileInfo native_file;
  FileSystemFileInfo file_system;
};

// Represents the result of FileChooser::OpenFileChooser() or
// EnumerateChosenDirectory().
struct FileChooserResult {
  // Selected files.
  array<FileChooserFileInfo> files;

  // Requested base directory.
  // This is a user-chosen path in a case of OpenFileChooser() with
  // mode=kUploadFolder.
  // This is |directory_path| in a case of EnumerateChosenDirectory().
  // Otherwise, it's an empty FilePath.
  mojo_base.mojom.FilePath base_directory;
};

// An interface to receive file chooser requests.
interface FileChooser {
  // Ask a user to choose files interactively.  Null |result| is returned if the
  // user canceled a dialog, or the request is not accepted.
  //
  // TODO(crbug.com/1142011): Consider chunking the reply into multiple messages
  // on a separate interface to avoid one very large IPC in some corner cases.
  [UnlimitedSize]
  OpenFileChooser(FileChooserParams params) => (FileChooserResult? result);

  // Ask to enumerate files which are descendants of the specified directory.
  // Null |result| is returned if the enumeration request is not accepted.
  EnumerateChosenDirectory(mojo_base.mojom.FilePath directory_path)
       => (FileChooserResult? result);
};

三、file_chooser.mojom定义实现:

out\Debug\gen\third_party\blink\public\mojom\choosers\file_chooser.mojom.h

out\Debug\gen\third_party\blink\public\mojom\choosers\file_chooser.mojom.cc

1、OpenFileChooser发送:

void FileChooserProxy::OpenFileChooser(
    FileChooserParamsPtr in_params, OpenFileChooserCallback callback) {
#if BUILDFLAG(MOJO_TRACE_ENABLED)
  TRACE_EVENT1(
    "mojom", "Send blink::mojom::FileChooser::OpenFileChooser", "input_parameters",
    [&](perfetto::TracedValue context){
      auto dict = std::move(context).WriteDictionary();
      perfetto::WriteIntoTracedValueWithFallback(
           dict.AddItem("params"), in_params,
                        "<value of type FileChooserParamsPtr>");
   });
#endif

  const bool kExpectsResponse = true;
  const bool kIsSync = false;
  const bool kAllowInterrupt = true;
  const bool is_urgent = false;
  
  const uint32_t kFlags =
      ((kExpectsResponse) ? mojo::Message::kFlagExpectsResponse : 0) |
      ((kIsSync) ? mojo::Message::kFlagIsSync : 0) |
      ((kAllowInterrupt) ? 0 : mojo::Message::kFlagNoInterrupt) |
      ((is_urgent) ? mojo::Message::kFlagIsUrgent : 0);
  
  mojo::Message message(
      internal::kFileChooser_OpenFileChooser_Name, kFlags, 0, 0,
      MOJO_CREATE_MESSAGE_FLAG_UNLIMITED_SIZE, nullptr);
  mojo::internal::MessageFragment<
      ::blink::mojom::internal::FileChooser_OpenFileChooser_Params_Data> params(
          message);
  params.Allocate();
  mojo::internal::MessageFragment<
      typename decltype(params->params)::BaseType> params_fragment(
          params.message());
  mojo::internal::Serialize<::blink::mojom::FileChooserParamsDataView>(
      in_params, params_fragment);
  params->params.Set(
      params_fragment.is_null() ? nullptr : params_fragment.data());
  MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
      params->params.is_null(),
      mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
      "null params in FileChooser.OpenFileChooser request");

#if defined(ENABLE_IPC_FUZZER)
  message.set_interface_name(FileChooser::Name_);
  message.set_method_name("OpenFileChooser");
#endif
  std::unique_ptr<mojo::MessageReceiver> responder(
      new FileChooser_OpenFileChooser_ForwardToCallback(
          std::move(callback)));
  ::mojo::internal::SendMojoMessage(*receiver_, message, std::move(responder));
}

2、OpenFileChooser实现

    case internal::kFileChooser_OpenFileChooser_Name: {

      internal::FileChooser_OpenFileChooser_Params_Data* params =
          reinterpret_cast<
              internal::FileChooser_OpenFileChooser_Params_Data*>(
                  message->mutable_payload());
      
      bool success = true;
      FileChooserParamsPtr p_params{};
      FileChooser_OpenFileChooser_ParamsDataView input_data_view(params, message);
      
      if (success && !input_data_view.ReadParams(&p_params))
        success = false;
      if (!success) {
        ReportValidationErrorForMessage(
            message,
            mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,
            FileChooser::Name_, 0, false);
        return false;
      }
      FileChooser::OpenFileChooserCallback callback =
          FileChooser_OpenFileChooser_ProxyToResponder::CreateCallback(
              *message, std::move(responder));
      // A null |impl| means no implementation was bound.
      DCHECK(impl);
      impl->OpenFileChooser(
std::move(p_params), std::move(callback));
      return true;
    }

3、FileChooser_OpenFileChooser_ForwardToCallback回调:

bool FileChooser_OpenFileChooser_ForwardToCallback::Accept(
    mojo::Message* message) {

  DCHECK(message->is_serialized());
  internal::FileChooser_OpenFileChooser_ResponseParams_Data* params =
      reinterpret_cast<
          internal::FileChooser_OpenFileChooser_ResponseParams_Data*>(
              message->mutable_payload());
  
  bool success = true;
  FileChooserResultPtr p_result{};
  FileChooser_OpenFileChooser_ResponseParamsDataView input_data_view(params, message);
  
  if (success && !input_data_view.ReadResult(&p_result))
    success = false;
  if (!success) {
    ReportValidationErrorForMessage(
        message,
        mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,
        FileChooser::Name_, 0, true);
    return false;
  }
  if (!callback_.is_null())
    std::move(callback_).Run(
std::move(p_result));
  return true;
}

四、file_chooser.mojom定义blink实现:

与主进程通信类:

third_party\blink\renderer\core\html\forms\file_chooser.cc

third_party\blink\renderer\core\html\forms\file_chooser.h

FileChooser::OpenFileChooser选择文件函数。

bool FileChooser::OpenFileChooser(ChromeClientImpl& chrome_client_impl) {
  LocalFrame* frame = FrameOrNull();
  if (!frame)
    return false;
  chrome_client_impl_ = chrome_client_impl;
  frame->GetBrowserInterfaceBroker().GetInterface(
      file_chooser_.BindNewPipeAndPassReceiver());
  file_chooser_.set_disconnect_handler(
      WTF::BindOnce(&FileChooser::DidCloseChooser, WTF::Unretained(this)));
  file_chooser_->OpenFileChooser(
      params_.Clone(),
      WTF::BindOnce(&FileChooser::DidChooseFiles, WTF::Unretained(this)));

  // Should be released on file choosing or connection error.
  AddRef();
  chrome_client_impl.RegisterPopupOpeningObserver(client_);
  return true;
}

<input type="file">对应c++代码实现类如下:

third_party\blink\renderer\core\html\forms\file_input_type.cc

third_party\blink\renderer\core\html\forms\file_input_type.h

FileInputType::HandleDOMActivateEvent(Event& event) //鼠标点击事件

FileInputType::OpenPopupView() 

void FileInputType::HandleDOMActivateEvent(Event& event) {
  if (GetElement().IsDisabledFormControl())
    return;

  HTMLInputElement& input = GetElement();
  Document& document = input.GetDocument();

  if (!LocalFrame::HasTransientUserActivation(document.GetFrame())) {
    String message =
        "File chooser dialog can only be shown with a user activation.";
    document.AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
        mojom::ConsoleMessageSource::kJavaScript,
        mojom::ConsoleMessageLevel::kWarning, message));
    return;
  }

  OpenPopupView();
  event.SetDefaultHandled();
}

void FileInputType::OpenPopupView() {
  HTMLInputElement& input = GetElement();
  Document& document = input.GetDocument();

  bool intercepted = false;
  probe::FileChooserOpened(document.GetFrame(), &input, input.Multiple(),
                           &intercepted);
  if (intercepted) {
    return;
  }

  if (ChromeClient* chrome_client = GetChromeClient()) {
    FileChooserParams params;
    bool is_directory =
        input.FastHasAttribute(html_names::kWebkitdirectoryAttr);
    if (is_directory)
      params.mode = FileChooserParams::Mode::kUploadFolder;
    else if (input.FastHasAttribute(html_names::kMultipleAttr))
      params.mode = FileChooserParams::Mode::kOpenMultiple;
    else
      params.mode = FileChooserParams::Mode::kOpen;
    params.title = g_empty_string;
    params.need_local_path = is_directory;
    params.accept_types = CollectAcceptTypes(input);
    params.selected_files = file_list_->PathsForUserVisibleFiles();
    params.use_media_capture = RuntimeEnabledFeatures::MediaCaptureEnabled() &&
                               input.FastHasAttribute(html_names::kCaptureAttr);
    params.requestor = document.Url();

    UseCounter::Count(
        document, GetElement().GetExecutionContext()->IsSecureContext()
                      ? WebFeature::kInputTypeFileSecureOriginOpenChooser
                      : WebFeature::kInputTypeFileInsecureOriginOpenChooser);
    chrome_client->OpenFileChooser(document.GetFrame(), NewFileChooser(params));
  }
}

五、file_chooser.mojom定义browser实现:

content\browser\web_contents\file_chooser_impl.cc

content\browser\web_contents\file_chooser_impl.h

void FileChooserImpl::OpenFileChooser(blink::mojom::FileChooserParamsPtr params,
                                      OpenFileChooserCallback callback) {
  if (listener_impl_ || !render_frame_host()) {
    std::move(callback).Run(nullptr);
    return;
  }
  callback_ = std::move(callback);
  auto listener = base::MakeRefCounted<FileSelectListenerImpl>(this);
  listener_impl_ = listener.get();
  // Do not allow messages with absolute paths in them as this can permit a
  // renderer to coerce the browser to perform I/O on a renderer controlled
  // path.
  if (params->default_file_name != params->default_file_name.BaseName()) {
    mojo::ReportBadMessage(
        "FileChooser: The default file name must not be an absolute path.");
    listener->FileSelectionCanceled();
    return;
  }

  // Don't allow page with open FileChooser to enter BackForwardCache to avoid
  // any unexpected behaviour from BackForwardCache.
  BackForwardCache::DisableForRenderFrameHost(
      render_frame_host(),
      BackForwardCacheDisable::DisabledReason(
          BackForwardCacheDisable::DisabledReasonId::kFileChooser));

  WebContentsImpl::FromRenderFrameHostImpl(render_frame_host())
      ->RunFileChooser(GetWeakPtr(), render_frame_host(), std::move(listener),
                       *params);
}

六、调用堆栈:

1、在页面点击<input type="file">控件:

2、FileInputType::HandleDOMActivateEvent(Event& event) //鼠标点击事件

3、FileInputType::OpenPopupView()  //发送选择文件框命令

4、ChromeClientImpl::OpenFileChooser

src\third_party\blink\renderer\core\page\chrome_client_impl.cc

src\third_party\blink\renderer\core\page\chrome_client_impl.h

5、FileChooser::OpenFileChooser:

src\third_party\blink\renderer\core\html\forms\file_chooser.cc

src\third_party\blink\renderer\core\html\forms\file_chooser.h

6、FileChooserProxy::OpenFileChooser

//mojom消息主进程通信file_chooser_impl.cc

7、主进程收到OpenFileChooser命令:

      impl->OpenFileChooser(

std::move(p_params), std::move(callback));

8、主进程响应mojom FileChooserImpl::OpenFileChooser:

9、主进程将选择结果返回给render进程:

    FileChooserImpl::FileSelected执行OpenFileChooserCallback回调

std::move(callback_).Run(FileChooserResult::New(std::move(files), base_dir));

10、render进程收到OpenFileChooserCallback回调:

FileChooser_OpenFileChooser_ForwardToCallback::Accept

11、render进程FileChooser::DidChooseFiles收到结果:

  此段代码是解析主进程发送过来的数据的

  FileChooserResultPtr p_result{};
  FileChooser_OpenFileChooser_ResponseParamsDataView input_data_view(params, message);
  if (success && !input_data_view.ReadResult(&p_result))

选择的2.png存储在p_result中:

12、最终前端展示选择结果:

总结:至此input type="file"选择文件流程分析完毕。

Electron 是一个基于 Chromium 和 Node.js 的框架,可以用于开发跨平台的桌面应用程序。它提供了一组 API,可以让开发者使用 Web 技术构建桌面应用程序。如果你想在 Electron 中播放视频,可以使用 HTML5 的 video 标签。 如果你需要在 C++ 中播放视频,可以使用一些第三方库,例如 FFmpeg 或者 GStreamer。这些库可以让你在 C++ 中解码和渲染视频。 在 Electron 中使用 C++ 库可以使用 Node.js 的 C++ 插件。你可以编写一个 Node.js 插件,使用 C++ 调用第三方库来播放视频。然后在 Electron 中使用这个插件来播放视频。 这里提供一个简单的示例代码,使用 FFmpeg 在 C++ 中播放视频: ```c++ #include <iostream> #include <string> #include <thread> #include <chrono> extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> } int main(int argc, char** argv) { AVFormatContext* pFormatCtx = nullptr; AVCodecContext* pCodecCtx = nullptr; AVCodec* pCodec = nullptr; AVFrame* pFrame = nullptr; AVFrame* pFrameRGB = nullptr; uint8_t* buffer = nullptr; int videoStream = -1; int numBytes = 0; struct SwsContext* swsCtx = nullptr; AVPacket packet; // Open video file if (avformat_open_input(&pFormatCtx, "video.mp4", nullptr, nullptr) != 0) { std::cerr << "Failed to open video file" << std::endl; return -1; } // Retrieve stream information if (avformat_find_stream_info(pFormatCtx, nullptr) < 0) { std::cerr << "Failed to retrieve stream information" << std::endl; return -1; } // Find the first video stream for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStream = i; break; } } if (videoStream == -1) { std::cerr << "Failed to find video stream" << std::endl; return -1; } // Get a pointer to the codec context for the video stream pCodecCtx = avcodec_alloc_context3(nullptr); avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar); pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == nullptr) { std::cerr << "Failed to find codec" << std::endl; return -1; } // Open codec if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) { std::cerr << "Failed to open codec" << std::endl; return -1; } // Allocate video frame pFrame = av_frame_alloc(); pFrameRGB = av_frame_alloc(); if (pFrameRGB == nullptr || pFrame == nullptr) { std::cerr << "Failed to allocate video frame" << std::endl; return -1; } // Determine required buffer size and allocate buffer numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1); buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t)); if (buffer == nullptr) { std::cerr << "Failed to allocate buffer" << std::endl; return -1; } // Assign appropriate parts of buffer to image planes in pFrameRGB av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1); // Initialize SWS context for software scaling swsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr); if (swsCtx == nullptr) { std::cerr << "Failed to initialize SWS context" << std::endl; return -1; } // Read frames and save first five frames to disk int frameCount = 0; while (av_read_frame(pFormatCtx, &packet) >= 0 && frameCount < 5) { // Is this a packet from the video stream? if (packet.stream_index == videoStream) { // Decode video frame int ret = avcodec_send_packet(pCodecCtx, &packet); if (ret < 0) { std::cerr << "Failed to send packet for decoding" << std::endl; return -1; } while (ret >= 0) { ret = avcodec_receive_frame(pCodecCtx, pFrame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { std::cerr << "Failed to decode video frame" << std::endl; return -1; } // Convert the image from its native format to RGB sws_scale(swsCtx, (uint8_t const* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); // Save the frame to disk std::string fileName = "frame" + std::to_string(frameCount) + ".ppm"; FILE* file = fopen(fileName.c_str(), "wb"); if (file == nullptr) { std::cerr << "Failed to open file for saving frame" << std::endl; return -1; } fprintf(file, "P6\n%d %d\n255\n", pCodecCtx->width, pCodecCtx->height); fwrite(pFrameRGB->data[0], 1, numBytes, file); fclose(file); std::cout << "Saved frame " << frameCount << std::endl; frameCount++; } } // Free the packet that was allocated by av_read_frame av_packet_unref(&packet); } // Clean up avformat_close_input(&pFormatCtx); avcodec_free_context(&pCodecCtx); av_frame_free(&pFrame); av_frame_free(&pFrameRGB); sws_freeContext(swsCtx); av_free(buffer); return 0; } ``` 这里的代码使用 FFmpeg 解码视频,并将每一帧转换为 RGB 格式,然后保存到磁盘上。你可以根据需要修改代码,将视频渲染到屏幕上,或者在 Electron 中使用 Node.js 插件调用这个 C++ 程序来播放视频。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值