【CEF3学习】– 在同一窗口打开链接

本次着手实现将 target="_blank“的行为修改为 target="_self"。 CEF3提供了UI窗口生命周期接口 CefLifeSpanHandler,借助这个接口来监听新窗口创建事件,并在创建前取消窗口创建,使用已有窗口来加载新链接。 class CefLifeSpanHandler : public virtual CefBaseRefCounted { public: ///由UI线程调用,调用时机是新浏览窗口创建前。 // frame 弹出新窗口链接的源frame(即从哪个frame里的链接弹出新窗口) // target_url 目的链接 // target_frame_name 目的frame名字 // 返回true,则表示要取消窗口创建 virtual bool OnBeforePopup(CefRefPtr browser, CefRefPtr frame, const CefString& target_url, const CefString& target_frame_name, WindowOpenDisposition target_disposition, bool user_gesture, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, CefRefPtr& client, CefBrowserSettings& settings, CefRefPtr& extra_info, bool* no_javascript_access) { frame->LoadURL(target_url); //在当前frame中加载链接 return true; //返回true,取消窗口创建 }

March 23, 2022 · 1 min · holdsky

【CEF3学习】-- 文件下载管理

CEF3关于文件下载的开放接口是 CefDownloadHandler,这个类的公开函数也挺简单的,就两个:一个是下载前 OnBeforeDownload,一个是下载状态更新 OnDownloadUpdated class CefDownloadHandler : public virtual CefBaseRefCounted { public: /// 文件下载前调用 // suggested_name (建议的)文件保存名 // callback 回调,用来通知下载动作继续进行。默认情况下,如果不主动调用这个回调,则会取消下载动作。 virtual void OnBeforeDownload( CefRefPtr browser, CefRefPtr download_item, const CefString& suggested_name, CefRefPtr callback) = 0; /// 用来通知下载状态信息,包括下载进度、总大小、已完成大小、是否完成等;这个方法可能会被调用多次,以用来持续更新下载状态 // download_item 下载项,下载的状态信息保存在这里。 // callback 回调,用来取消/暂停/恢复/下载。默认情况下,如果不主动调用这个回调,则会继续下载。 virtual void OnDownloadUpdated(CefRefPtr browser, CefRefPtr download_item, CefRefPtr callback) {} }; 需要注意的是,上面两个方法都有一个 download_item 参数,这个参数是不能在上面两个方法的作用域外使用的。

March 23, 2022 · 1 min · holdsky

【CEF3学习】-- 修改User-Agent

本文要实现的是在 右键菜单 中添加一个修改User-Agent功能 定义一个变量,用来控制是使用默认UA还是Android UA static bool s_UerAgentAndroid = false; 添加 右键菜单 // 添加菜单枚举 enum client_menu_ids { CLIENT_ID_SHOW_DEVTOOLS = MENU_ID_USER_FIRST, ... .... CLIENT_ID_UserAnget, }; // 修改菜单项和菜单事件处理 void ClientHandler::OnBeforeContextMenu(CefRefPtr browser, CefRefPtr frame, CefRefPtr params, CefRefPtr model) { ...... ...... //添加菜单项 model->AddItem(CLIENT_ID_UserAnget, s_UerAgentAndroid ? "默认UA":"Android UA"); if (delegate_) delegate_->OnBeforeContextMenu(model); } bool ClientHandler::OnContextMenuCommand(CefRefPtr browser, CefRefPtr frame, CefRefPtr params, int command_id, EventFlags event_flags) { CEF_REQUIRE_UI_THREAD(); switch (command_id) { ...... ...... //菜单事件处理 case CLIENT_ID_UserAnget: s_UerAgentAndroid = !s_UerAgentAndroid; browser->Reload(); // 修改UA后重新加载网页 return true; default: // Allow default handling, if any. return ExecuteTestMenu(command_id); } } 下面就是对每一个请求修改HTTP报文的User-Agent。CEF3会将网页所有资源请求事件通知给 CefRequestHandler 或其子类处理,其 OnBeforeResourceLoad 是在http请求发出前的处理点,修改相应内容,代码如下 ...

March 9, 2022 · 1 min · holdsky

【CEF3学习】-- 添加右键菜单

cefclient这个项目启动后,默认情况下会有下图所示的右键菜单 我们要做的就是仿照这个菜单,添加新的菜单项。 根据关键字搜索,可以找到client_handler.cpp文件,有两函数 OnBeforeContextMenu、 OnContextMenuCommand,用来处理菜单项。修改这两个函数,增加新的菜单项,例如重新加载当前页面。 /// enum client_menu_ids { CLIENT_ID_SHOW_DEVTOOLS = MENU_ID_USER_FIRST, .... CLIENT_ID_TESTMENU_RADIOITEM3, CLIENT_ID_RELOAD_PAGE /// 添加一个菜单项枚举 }; void ClientHandler::OnBeforeContextMenu(CefRefPtr browser, CefRefPtr frame, CefRefPtr params, CefRefPtr model) { ///在菜单显示前,处理菜单项 .... model->AddItem(CLIENT_ID_RELOAD_PAGE, "重新加载页面"); .... } bool ClientHandler::OnContextMenuCommand(CefRefPtr browser, CefRefPtr frame, CefRefPtr params, int command_id, EventFlags event_flags){ ///对应菜单项的响应处理 switch (command_id) { case CLIENT_ID_RELOAD_PAGE: browser->Reload(); return true; case CLIENT_ID_SHOW_DEVTOOLS: ..... ... } } ...

March 7, 2022 · 1 min · holdsky

【CEF3学习】-- 生成IDE的工程文件

下载下来的CEF压缩包解压后,里面包含CEF接口源码、示例、静态库等资源,并提供cmake脚本文件以生成IDE工程文件;使用CMake UI来操作这些脚本文件。 首先,填写好CEF解压后的路径(包含CMakeLists.txt的目录),IDE工程文件的输出路径(可以写和CMakeLists.txt相同的目录) 然后,点击 Configure 按钮,配置IDE参数 正常情况下,下图到参数是自动生成的 点击 Generate 按钮,生成IDE工程文件 打开IDE工程文件,直接以Debug模式运行 cefclient 这个项目,可以看到一个十分简单的浏览器页面出现了

March 7, 2022 · 1 min · holdsky

【CEF3学习】-- 下载构建好的版本

CEF全称Chromium Embedded Framework,是一个基于Google Chromium 的开源项目。相关资料在https://bitbucket.org/chromiumembedded/cef/wiki/Home 中基本都能找到。 我不选择手动构建,一是因为没有合适的硬件资源,二是构建好的二进制产品可以很方便的从这里下载。 https://cef-builds.spotifycdn.com/index.html 上面这个网站提供包括32/64位以及RAM架构的CPU下的Linux/MacOS/Windows平台的构建产品; 提供的版本有两种,一种是稳定版本(Current Stable Build (Preferred)),一种是Beta版本(Current Beta Build (For Testing))。 本文选择的是MacOS-64-Standard Distribution版本, https://cef-builds.spotifycdn.com/cef_binary_97.1.7%2Bg45ce543%2Bchromium-97.0.4692.99_macosx64.tar.bz2

March 7, 2022 · 1 min · holdsky

Framework not found Pods_xxxxx

把老的xcode工程升级为cocoapod工程,先创建一个空的podfile use_frameworks!:linkage => :static inhibit_all_warnings! # 忽略引入库的所有警告 workspace 'all.xcworkspace' target 'testXcode8' do platform :ios, '8.0' project 'testXcode8/testXcode8' end pod install 后,执行构建在链接阶段报错 Framework not found Pods_testXcode8 尝试的解决过程就不赘述了,最后修改了一个八杆子打不着的BuidSetting解决的,将 Build Active Architecture Only 由非默认值NO修改为默认值 未修改前 修改后

February 10, 2022 · 1 min · holdsky

iOS 动态库运行时符号冲突

本文demo 构造了这样的场景: 有两个动态库framework,DA和DB,DA定义了函数func,DB定义了函数func,两个func的名字一样,但是内部实现不一样,然后使用App加载这两个动态库,结果运行时func的调用和预期的不一致。 动态库DA // func.h const char * func(void); const char * func_a(void); // func.c #include "func.h" const char * func(void) { return "i m da func\n"; } const char * func_a(void) { return "i m da func_a\n"; } 动态库DB // func.h const char * func(void); const char * func_b(void); // func.c #include "func.h" const char * func(void) { return "i m db func\n"; } const char * func_a(void) { return "i m db func_b\n"; } App 集成 //Appdelegate.m #import "AppDelegate.h" #import @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { printf("%s",func()); //预期打印 i m da func printf("%s",func_a()); return YES; } //ViewController.m #import "ViewController.h" #import @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; printf("%s",func());//预期打印 i m db func printf("%s",func_b()); } @end 实测 ...

December 15, 2021 · 1 min · holdsky

iOS App页面置灰实现

App页面置灰,本质是将彩色图像转换为灰度图像,本文提供两种方法实现,一种是App整体置灰,一种是单个页面置灰,可结合具体的业务场景使用。 方法一:分别将图片和文字置灰 一般情况下,App页面的颜色深度是24bit,也就是RGB各8bit;如果算上Alpha通道的话就是32bit,RGBA(或者ARGB)各8bit。灰度图像的颜色深度是8bit,这8bit表示的颜色不是彩色,而是256种不同亮度的黑色或白色。 说到灰度图像,在YUV颜色空间上—其中Y代表亮度,调整Y值就可以得到不同的灰度图像。 理论上,颜色空间RGB和YUV是等价的,同一种颜色用RGB或YUV都可以表示。从RGB数值对应到亮度Y,一般采用公式Y = 0.299R+0.587G+0.114B,得到的结果再填充到RGB上就得到了对应的灰度RGB颜色。 Y = 0.299R+0.587G+0.114B Gray = RGB(Y,Y,Y) 以上是方法一App页面置灰的原理基础。 UIImage转成灰度图 核心是创建一个灰度空间,然后将图像绘制到这个空间上 -(UIImage*)getGrayImage:(UIImage*)sourceImage { int width = sourceImage.size.width; int height = sourceImage.size.height; // 创建灰度空间 CGColorSpaceRef colorSpace =CGColorSpaceCreateDeviceGray(); // 创建绘制上下文 CGContextRef context =CGBitmapContextCreate(nil,width,height,8,0,colorSpace,kCGImageAlphaNone); CGColorSpaceRelease(colorSpace); if(context== NULL){ return nil; } // 绘制原始图像到新的上下文(灰度) CGContextDrawImage(context,CGRectMake(0,0, width, height), sourceImage.CGImage); // 获取灰度图像 CGImageRef grayImageRef =CGBitmapContextCreateImage(context); // CGImage -> UIImage UIImage*grayImage=[UIImage imageWithCGImage:grayImageRef]; //回收资源 CGContextRelease(context); CGImageRelease(grayImageRef); return grayImage; } UIColor转成灰度颜色 比较简单了,使用公式就可以了 Y = 0.299R+0.587G+0.114B ...

December 14, 2021 · 1 min · holdsky

GoogleWebRTC for iOS 设置SDP和ICE顺序错误导致不互通

环境: GoogleWebRTC 1.1.31999 Xcode 13 iOS 15 问题描述: 1、 学习WebRTC时,先用simple-peer实现了浏览器和浏览器之间的音视频互通。 2、然后结合浏览器的经验,基于GoogleWebRTC for iOS实现了iOS真机和浏览器直接的互通 3、 最后在测试iOS真机之间的互通时,基本不互通,测试失败。排查了很长时间,最后定位为API调用顺序错误 应先设置远端SDP,再设置远端ICE,具体到API就是,先设置 调用 setRemoteDescription,然后再调用 addIceCandidate // 收到远端SDP和ICE信息后 [peerConnection setRemoteDescription:remoteSDP completionHandler:^(NSError * err){ [peerConnection addIceCandidate:remoteICE]; } ];

November 18, 2021 · 1 min · holdsky