Archive for the 'WebView' Category

2015/11/09 Safariと同じUser Agent文字列を組み立てる

Cocoaの機能を用いて、SafariのUser Agent名が返す文字列と同じものを生成するAppleScriptです。

さまざまな用途のために、AppleScriptでカスタムWebブラウザを作ることがあります。その際に、Safariと同じUser Agent名をWebサーバー側に返すことができるとメリットがあります。正確にいえば、Safariのフリをしてブラウズを行うことで、さまざまなトラブルを回避できます。

取得にあたっては、Safariを起動してSafariからUser Agent名を取るのではなく、WebKitが返すUser Agent情報に(未起動状態の)SafariのバージョンやビルドNo.をつないで生成しています。ビルドNo.についてはメジャーNo.の下3桁だけが出力されており、少々不思議なところですが、実物のSafariと合うように調整しています。

一応本ルーチンが正常に文字列を生成できているかのテストのために、SafariからUser Agent名を取得して比較を行い、同一の文字列が生成できていることを確認しています(実際に使うときには削除)。

AppleScript名:SafariのUser Agent文字を組み立てる
– Created 2015-11-09 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “Webkit”

set aRes to getSafariUserAgentString() of me
–>  ”Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.6 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.6″

–ほんまもんのSafariからUser Agentを取得してテスト
tell application “Safari”
  tell document 1
    set dRes to do JavaScript “navigator.userAgent”
    
–>  ”Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.6 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.6″
  end tell
end tell

return (aRes = dRes)
–>  true

–Safariと同じUser Agentの文字列を取得する
on getSafariUserAgentString()
  set aBundle to current application’s NSBundle’s bundleWithIdentifier:“com.apple.Safari”
  
set aVer to (aBundle’s infoDictionary()’s objectForKey:“CFBundleShortVersionString”) as text
  
–>  ”9.0.2″
  
  
set aBuildNo to (aBundle’s infoDictionary()’s objectForKey:“CFBundleVersion”) as text
  
–>  ”11601.3.6″
  
set {v1, v2, v3} to parseVersionNumber(aBuildNo) of me
  
set aV1 to current application’s NSString’s stringWithString:v1
  
set bV1 to aV1’s substringFromIndex:2 –3 characters from tail –?????
  
–> “601″
  
  
set bStr to retStrFromArrayWithDelimiter({bV1, v2, v3}, “.”) of me
  
  
set aWebView to current application’s WebView’s alloc()’s init()
  
set aRes to (aWebView’s stringByEvaluatingJavaScriptFromString:“navigator.userAgent”) as text
  
–>  ”Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.6 (KHTML, like Gecko)”
  
  
return (aRes & ” Version/” & aVer & ” Safari/” & bStr)
  
end getSafariUserAgentString

–バージョン番号文字列からメジャーバージョンを取り出し数値として返す
on parseVersionNumber(a)
  set aStr to current application’s NSString’s stringWithString:a
  
set aRes to (aStr’s componentsSeparatedByString:“.”)
  
set bRes to aRes’s allObjects()
  
return bRes as list
end parseVersionNumber

–リストを指定デリミタをはさんでテキスト化
on retStrFromArrayWithDelimiter(aList, aDelim)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set aRes to anArray’s componentsJoinedByString:aDelim
  
return aRes as text
end retStrFromArrayWithDelimiter

★Click Here to Open This Script 

2015/09/16 指定URLをロード WKWebView版

Cocoaの機能を用いて指定URLのページの内容を読み込んで、ページのタイトルを取得するAppleScriptです。

先のオフラインレンダラーのAppleScriptをMLに投稿してみたところ、ShaneよりYosemiteならWkWebViewが新たにWebViewの後継として用意されているので試してはどうか? という指摘があり、(知らなかったので)慌てて調べた次第です。

通常のAppleScriptでCocoaの機能が使えるといっても、さすがにKey Value Observation(キー値監視)はサポートしていないので、 そこはloading()プロパティをループで確認しています。ただ、このシンプルさゆえにフォアグランド実行(Control-Command-R)させる必要がありません。

一応、これで指定URLの内容は取得できているようなのですが、いまだその内容を画像化はできていません。いまのところ、綺麗な800×600ドットのサイズのグレーの画像が取得できています(つまり、明らかに失敗している)。

# El Capitanで動かしてみたら、実行が終了しませんでした。

AppleScript名:ASOCで指定URLをロード_WKWebView版
– Created 2015-09-16 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “WebKit”

property theWebView : missing value

set aURL to “http://www.apple.com/us/shop/browse/home/specialdeals/mac”
set aRes to getPage(aURL)
set aTitle to (theWebView)’s title()
return aTitle as text
–>  ”Refurbished Mac - Apple Certified - Apple”

–Download the URL page to WkWebView
on getPage(aURL)
  set thisURL to current application’s |NSURL|’s URLWithString:aURL
  
set theRequest to current application’s NSURLRequest’s requestWithURL:thisURL
  
  
set aConf to current application’s WKWebViewConfiguration’s alloc()’s init()
  
  
set (my theWebView) to current application’s WKWebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, 800, 600)) configuration:aConf –フレームの大きさは根拠レス
  (
my theWebView)’s setNavigationDelegate:me
  (
my theWebView)’s loadRequest:theRequest
  
  
set waitLoop to 1000 * 60 –60 seconds
  
  
set hitF to false
  
repeat waitLoop times
    set aLoadF to ((my theWebView)’s estimatedProgress()) as number
    
if aLoadF = 1.0 then
      set hitF to true
      
exit repeat
    end if
    
current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
  end repeat
  
  
return hitF
end getPage

★Click Here to Open This Script 

2015/09/15 指定URLをロードして1枚ものの大きなPDFにレンダリング

Cocoaの機能を用いて、指定URLのページを読み込み、1枚ものの大きなPDFにレンダリングするAppleScriptです。

デスクトップ上に「outPdf_2.pdf」の名前で書き出します。実行には、Script Editor上でControl-Command-Rによるフォアグラウンド実行する必要があります。

まだまだ、完成にはほど遠い感じがしますが、とりあえずPDF出力できたので。

render2.png
▲本AppleScriptでレンダリングしたPDF

render1.png
▲同じページをSafariで表示させたところ

ページの上と下が「ものすごく残念な状態」になっています。一応、User AgentをSafariに合わせてみたのですが、あまり状況はよくなっていないようです。

まだまだ、本Blogを表示させると、右側のメニュー部分しか表示されなかったり、PDFの縦の長さをページ内容に合わせて自動調整できていなかったり、ページ上のグラフィックがロードされていない状態になることがあったり、、、と、前途多難な印象を受けます。

ただ、これまでこの手のオフラインレンダラーは有用な割にAppleScriptへの対応度が低く、「ならAppleScriptで自力でやっちゃえばいいじゃん」というアプローチは間違っていない(たぶん)はず。また、オフラインレンダラー自体のアップデートが割とおざなりで、OSのアップデートについてこられないことがままあります(便利なのに)。

AppleScript名:ASOCで指定URLをロードして1枚ものの大きなPDFにレンダリング
– Created 2015-09-15 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “WebKit”
use framework “Quartz”
use framework “AppKit”

property loadDone : false
property theWebView : missing value
property userAgentName : “Version/9.0 Safari/601.1.56″

set aOutPath to “~/Desktop/outPdf_2.pdf”
set aURL to “http://www.apple.com/jp/shop/browse/home/specialdeals/mac”
–set aURL to “http://piyocast.com/as”–このBlogをレンダリングすると悲惨な状態に、、、
set targX to 1024
set targY to 2000 –動的に変更できないか?

–Check If this script runs in foreground
if not (current application’s NSThread’s isMainThread()) as boolean then
  display alert “This script must be run from the main thread (Command-Control-R in Script Editor).” buttons {“Cancel”} as critical
  
error number -128
end if

set aPath to current application’s NSString’s stringWithString:aOutPath
set bPath to aPath’s stringByExpandingTildeInPath()

–URL Validation check
set aRes to validateURL(aURL)
if aRes = false then return
set aTitle to getURLandRender(aURL)
–>  ”AS Hole(AppleScriptの穴) By Piyomaru Software”
set aTargetView to (my theWebView)’s mainFrame()’s frameView()’s documentView()

set aScreen to current application’s NSScreen’s mainScreen()
set {scrX, scrY} to aScreen’s frame()’s |size|() as list

(*
set aWin to current application’s NSWindow’s alloc()’s init()
aWin’s setContentSize:{scrX, scrY}
aWin’s setContentView:(my theWebView)
aWin’s |center|()
*)

set aPrintInfo to current application’s NSPrintInfo’s sharedPrintInfo()
set newPrintOp to current application’s NSPrintOperation’s PDFOperationWithView:(aTargetView) insideRect:(current application’s NSMakeRect(0, 0, targX, targY)) toPath:bPath printInfo:aPrintInfo

set runPrint to newPrintOp’s runOperation()
return (runPrint as boolean)

–Download the URL page to WebView and get page title
on getURLandRender(aURL)
  
  
set my loadDone to false
  
set my theWebView to missing value
  
openURL(aURL)
  
  
set waitLoop to 1000 * 60 –60 seconds
  
  
set hitF to false
  
repeat waitLoop times
    if my loadDone = true then
      set hitF to true
      
exit repeat
    end if
    
current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
  end repeat
  
if hitF = false then return
  
  
set jsText to “document.title”
  
set x to ((my theWebView)’s stringByEvaluatingJavaScriptFromString:jsText) as text
  
  
return x
end getURLandRender

–Down Load URL contents to WebView
on openURL(aURL)
  set noter1 to current application’s NSNotificationCenter’s defaultCenter()
  
set (my theWebView) to current application’s WebView’s alloc()’s init()
  (
my theWebView)’s setApplicationNameForUserAgent:userAgentName
  
noter1’s addObserver:me selector:“webLoaded:” |name|:(current application’s WebViewProgressFinishedNotification) object:(my theWebView)
  (
my theWebView)’s setMainFrameURL:aURL
end openURL

–Web View’s Event Handler:load finished
on webLoaded:aNotification
  set my loadDone to true
end webLoaded:

–URL Validation Check
on validateURL(anURL as text)
  set regEx1 to current application’s NSString’s stringWithString:“((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+”
  
set predicate1 to current application’s NSPredicate’s predicateWithFormat_(“SELF MATCHES %@”, regEx1)
  
set aPredRes1 to (predicate1’s evaluateWithObject:anURL) as boolean
  
return aPredRes1
end validateURL

★Click Here to Open This Script 

2015/09/07 指定URLをロードしてtitleを取得

Cocoaの機能を用いて、指定URLのページのtitleを取得するAppleScriptです。

Script Editor上でControl-Command-Rの操作でforeground実行する必要があります。

動的にWebViewを作成し、そこに指定URLを読み込み。JavaScript経由でタイトルを取得するという、ひねりも工夫もないものです。ログインなどが必要なSNS内などのページではなく、普通にただオープンすれば見られるタイプのページを前提としています。

この手の処理を行う場合には、Safariをコントロールするのが定番でしたが、GUIベースのアプリケーションであるSafariの全機能を必要としない場合には、動的にWebViewを生成して情報取得するほうが便利なケースもあります。

AppleScript名:指定URLをロードしてtitleを取得
– Created 2015-09-07 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “WebKit”

property loadDone : false
property theWebView : missing value

set aURL to “https://www.youtube.com/watch?v=WuziqYptTyE”
set aTitle to getPageTitle(aURL)
–>  ”戦場の絆ポータブル【HD】鉱山都市 オンライン対戦 2015.09.04 - YouTube”

on getPageTitle(aURL)
  –Check If this script runs in foreground
  
if not (current application’s NSThread’s isMainThread()) as boolean then
    display alert “This script must be run from the main thread (Command-Control-R in Script Editor).” buttons {“Cancel”} as critical
    
error number -128
  end if
  
  
set my loadDone to false
  
set my theWebView to missing value
  
openURL(aURL)
  
  
set waitLoop to 1000 * 60 –60 seconds
  
  
set hitF to false
  
repeat waitLoop times
    if my loadDone = true then
      set hitF to true
      
exit repeat
    end if
    
current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
  end repeat
  
if hitF = false then return
  
  
set jsText to “document.title”
  
set x to ((my theWebView)’s stringByEvaluatingJavaScriptFromString:jsText) as text
  
set my theWebView to missing value
  
  
return x
end getPageTitle

–WebViewにURLを読み込む
on openURL(aURL)
  set noter1 to current application’s NSNotificationCenter’s defaultCenter()
  
set my theWebView to current application’s WebView’s alloc()’s init()
  
noter1’s addObserver:me selector:“webLoaded:” |name|:(current application’s WebViewProgressFinishedNotification) object:(my theWebView)
  
my (theWebView’s setMainFrameURL:aURL)
end openURL

–Web Viewのローディング完了時に実行
on webLoaded:aNotification
  set my loadDone to true
end webLoaded:

★Click Here to Open This Script 

2014/12/19 AppleScriptでJavaScriptを実行する

AppleScriptからJavaScriptを実行する方法がUS AppleのAppleScript Users MLで議論されていたので、いろいろとテストしたうえでまとめてみました。

なお、本AppleScriptは明示的にメインスレッドで実行させるために、Script Editor上で通常の「Command-R」ではなく、「Control-Command-R」で実行する必要があります(この操作を行わないと、サブスレッド上で実行されている旨のエラーが出ます)。ASObjCExplorer 4上で実行する場合には、ウィンドウ左下にある「Run in foreground」のチェックボックスにチェックを入れてから実行します。

(1)run script xxxxx in “OSA-Language Name”

こんな方法で実行できたとは、、、。正直言って驚きました。これまで、OSに複数のOSA言語がインストールされてリリースされたことが(ほとんど)なかったので、他のOSA言語を呼び出すという場面がなかったわけですが、こういう呼び出し方で呼び出せるんですね。リリースノートに書かれているんだかいないんだか。この呼び出し方でJXA(JavaScript for Automation)のScriptをAppleScript側から実行できます。同様に、JXA側からAppleScriptを実行することも可能と思われます。

(2)WebViewを動的に生成してJavaScriptを実行

WebViewを動的に生成して操作する場合には、JavaScriptのコマンドを実行することになります。つまり、Webブラウザ上と同様にJavaScriptを実行できるわけですが、これはJXAではありません。このため、Webブラウザ上で実行できるJavaScriptはこの方法で呼び出せますが、JXAで書かれたScriptを実行することはできません。

(3)JavaScript CoreでJavaScriptを実行

WebKitでJS実行するのと速度差はありません。同様に、ここで実行できるJavaScriptはJXAではありません。

js_table1.png
▲OS X上で実行可能な(主な)JavaScript処理系

AppleScript名:ASでJavaScriptを実行する
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “JavaScriptCore”
use framework “WebKit”

–実行するJavaScriptのテキスト
set jsText to
var app = Application.currentApplication()
app.includeStandardAdditions = true
app.displayDialog(’How are you?’)

–run scriptでAppleScriptからJavaScriptを実行する(JXA:アプリケーション操作可能)
set k to run script jsText in “Javascript” –AppleScriptから一番手軽なJXA呼び出し
log {“1″}

–WebKitでJavaScriptを実行する(高速だが、アプリケーション操作不可)
set theWebView to current application’s WebView’s alloc()’s init()
set x to (theWebView’s stringByEvaluatingJavaScriptFromString:jsText) as text
log {“2″}

–JavaScript CoreでJavaScriptを実行する(高速だが、アプリケーション操作不可)
set theContext to current application’s JSContext’s alloc()’s init()
set theJSValue to theContext’s evaluateScript:jsText
log {“3″}

★Click Here to Open This Script 

2013/04/17 WebViewでローディング完了通知

WebViewで任意のURLのWebを表示し、Webコンテンツのローディングが完了したら通知するAppleScriptObjCのサンプルです。

web11.png

web2.png

自前のWebブラウザを作って、ローディング完了通知をもらうのは簡単ですが……Safari内部のローディング完了通知なんかを取れないものかと考えてしまうところ。

→ Xcodeプロジェクトのダウンロード

AppleScriptObjCファイル名:wbTestAppDelegate.applescript

– wbTestAppDelegate.applescript
– wbTest

– Created by 長野谷 隆昌 on 13/04/17.
– Copyright 2012 Piyomaru Software. All rights reserved.


script wbTestAppDelegate
  property parent : class “NSObject”
  
  property webview : missing value
  
property internetlocation : missing value
  
  
  on applicationWillFinishLaunching_(aNotification)
    
    –Web Viewのローディング完了通知をNotification Centerに登録
    
set noter1 to current application’s NSNotificationCenter’s defaultCenter()
    
noter1’s addObserver_selector_name_object_(me, “webLoaded:”, current application’s WebViewProgressFinishedNotification, webview)
    
  end applicationWillFinishLaunching_
  
  
  on applicationShouldTerminate_(sender)
    return current application’s NSTerminateNow
  end applicationShouldTerminate_
  
  
  on clicked_(aSender)
    
    set aurlobj to internetlocation’s stringValue()
    
set aurlobj to aurlobj as string
    
    –URLのプロトコルヘッダーが欠けている場合には補完
    
if (aurlobj does not start with “http://”) and (aurlobj does not start with “https://”) then
      set aurlobj to “http://” & aurlobj
    end if
    
    –指定URLをオープンする
    
webview’s setMainFrameURL_(aurlobj)
    
  end clicked_
  
  
  –Web Viewのローディング完了時に実行
  
on webLoaded_(aNotification)
    display dialog “Loaded”
  end webLoaded_
  
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に