Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
WKWebView とめんどくさいお友達
Search
Megabits_mzq
February 22, 2022
Programming
810
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
WKWebView とめんどくさいお友達
Swift 愛好会 2022/2/22 登壇資料
Megabits_mzq
February 22, 2022
More Decks by Megabits_mzq
See All by Megabits_mzq
OTP を自動で入力する裏技
megabitsenmzq
0
210
SwiftUI と Shader を活用した楽しいオンボーディング起動画面の作成
megabitsenmzq
0
150
Liquid Glass, どこが変わったのか
megabitsenmzq
0
210
iPhone 16 Camera Control
megabitsenmzq
0
170
240fps で画像処理したい
megabitsenmzq
0
250
Swift 開発が楽になる道具たち
megabitsenmzq
1
790
Animoji を作ってみた
megabitsenmzq
0
220
MainMenu.xib を翻訳してみた
megabitsenmzq
0
320
先週解決した SwiftUI 問題
megabitsenmzq
0
160
Other Decks in Programming
See All in Programming
最近”作らせてる” ローカルAIアプリの話
akit37
0
460
Turning Architecture into Unit Tests in the AI Era (NSSpain XIV)
steliosf
PRO
1
140
HTML-in-Canvasで作る未来のWeb UI表現
clockmaker
4
4.8k
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
730
技術的負債の返済は、AI時代の複利で効く投資 — 経営としての意思決定とその遂行
curekoshimizu
1
2.3k
Domain-Driven Transformation
hschwentner
2
2.3k
contenteditable と日本語入力に向き合う
colorful12
0
190
Ghostty + Neovimで作る 透明でカッコ良い開発環境
j341nono
0
170
When benchmarks go bad - what I learned from measuring performance wrong
hollycummins
0
210
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
15
8.3k
モデルのリファクタリングが難しいと思ったら、そもそも複雑だったのはビジネス仕様だった ? / is-the-business-domain-the-real-complexity
hatsu38
0
470
dotNET_vulnclicker_intro.pdf
deka_morita
0
140
Featured
See All Featured
Paper Plane
katiecoart
PRO
4
54k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
780
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.7k
My Coaching Mixtape
mlcsv
0
350
How to build a perfect <img>
jonoalderson
1
6.1k
Bash Introduction
62gerente
615
220k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
480
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.7k
Deep Space Network (abreviated)
tonyrice
0
370
Scaling GitHub
holman
464
140k
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.4k
Side Projects
sachag
456
43k
Transcript
Jinyu Meng (Megabits Studio) WKWebView とめんどくさいお友達
個⼈紹介 独 ⽴ 開 発 者 で す ! Megabits
⾦⿂
個⼈紹介 Twitter @Megabits_mzq @Megabits_Studio
None
None
“WebKit を触ると、不幸になる” ──マハトマ・ガンディー が⾔ったことがない
None
self.view.window?.level = .floating
None
None
let width = view.frame.width let height = view.frame.height -
toolbarView.frame.maxY let script1 = """ Object.defineProperty(window, "screenTop", { get: function(){ return 0; }}); Object.defineProperty(window, "outerWidth", { get: function(){ return \(width); }}); Object.defineProperty(window, "outerHeight", { get: function(){ return \(height); }}); """ let userScript1 = WKUserScript(source: script1, injectionTime: .atDocumentStart, forMainFrameOnly: true) webView.configuration.userContentController.addUserScript(userScript1)
#import <AppKit/AppKit.h> #import <objc/runtime.h> static void HookMessage(Class cls, SEL selName,
IMP replaced, IMP *orig) { Method origMethod = class_getInstanceMethod(cls, selName); if (!origMethod) {return;} *orig = method_setImplementation(origMethod, replaced); } static NSRect (*original_NSScreen_frame)(NSScreen *self, SEL _cmd); static NSRect replaced_NSScreen_frame(NSScreen *self, SEL _cmd) { //https://stackoverflow.com/questions/1451342/objective-c-find-caller-of-method NSString *sourceString = [[NSThread callStackSymbols] objectAtIndex:1]; NSCharacterSet *separatorSet = [NSCharacterSet characterSetWithCharactersInString:@" -[]+?.,"]; NSMutableArray *array = [NSMutableArray arrayWithArray:[sourceString componentsSeparatedByCharactersInSet:separatorSet]]; [array removeObject:@""]; NSString *framework = [array objectAtIndex:1]; NSRect origFrame = original_NSScreen_frame(self, _cmd); if (![framework isEqualTo:@"WebCore"] { return origFrame; } return CGRectMake(0, 0, 0, 0); } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" __attribute__((constructor)) static void makeMyMagicWork() { HookMessage( objc_getClass("NSScreen"), NSSelectorFromString(@"frame"), (IMP)&replaced_NSScreen_frame, (IMP *)&original_NSScreen_frame ); } #pragma clang diagnostic pop
None
None
None
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy)
-> Void) { if navigationResponse.canShowMIMEType, let mimeType = navigationResponse.response.mimeType { let mainType = mimeType.components(separatedBy: "/")[0] // print(mainType, mimeType, navigationResponse.isForMainFrame) if !["image", "audio", "video"].contains(mainType) { if !(["text/plain", "text/xml"].contains(mimeType) && navigationResponse.isForMainFrame){ decisionHandler(.allow) return } } else if !navigationResponse.isForMainFrame { decisionHandler(.allow) return } } if let fileName = navigationResponse.response.suggestedFilename, let url = navigationResponse.response.url { webView.configuration.websiteDataStore.httpCookieStore.getAllCookies() { cookies in print(fileName) self.createNewDownload(url: url, fileName: fileName, cookies: cookies) } } decisionHandler(.cancel) }
MIME Type • text/plain • text/xml • Image/png • Audio/mp3
• Video/mp4 • … navigationResponse.canShowMIMEType, navigationResponse.response.mimeType
navigationResponse.isForMainFrame
Blob 👉 WKDownloadDelegate
None
override func willOpenMenu(_ menu: NSMenu, with event: NSEvent) { super.willOpenMenu(menu,
with: event) for (index, menuItem) in menu.items.enumerated() { switch menuItem.identifier?.rawValue { case "WKMenuItemIdentifierCopyImage": menu.removeItem(menuItem) default: break // print(menuItem.identifier?.rawValue ?? "") } } }
window.oncontextmenu = (event) => { var target = event.target var
href = target.href var parentElement = target while (href == null && parentElement.parentElement != null) { parentElement = parentElement.parentElement href = parentElement.href } if (href == null) { parentElement = null; } window.webkit.messageHandlers.oncontextmenu.postMessage({ nodeName: target.nodeName, id: target.id, src: target.src, hrefNodeName: parentElement?.nodeName, hrefId: parentElement?.id, href }); } """ https://stackover fl ow.com/a/66836354/1711103
None
@Megabits_mzq @Megabits_Studio https://menubarx.app