Scripting Safari and Google Chrome simultaneously

Hi All,

I’ve got two scripts that work with Safari or Google Chrome: one is for quickly copying page titles and URLs to the clipboard for pasting in emails etc…, and the other is for moving a web browsing session from one to the other.

The dictionaries for both programs have nearly identical vocabulary, so I’d like to make my code less ugly somehow by combining calls to both programs. I know that OS X 10.9 introduced the “use” statement to allow an entire script to always have access to an application’s (or library’s) dictionary without “using terminology from” or “tell” statements. Can I use that with the below scripts, or would that not work due to the overlap between Safari’s and Chrome’s dictionaries?

Thanks,

Tim

Copying URLs to clipboard (see how ugly it is having all these different tell statements?):
Copies title and URL for a tab, a window’s tabs, or all windows’ tabs to the clipboard. Control how much gets copied using the two arguments to main(). For my use I call this from another script with the appropriate options from Safari’s or Chrome’s application scripts.

property ProgramNames : {"Safari", "Google Chrome"}

main(false, false)

on main(SingleWindow, SingleTab)
	local ProgramName, WindowList, URLList, WindowCount, FullTabList
	
	--set SingleWindow to false
	--set SingleTab to false
	
	tell application "System Events"
		set ProgramName to name of first process whose frontmost is true
	end tell
	
	if ProgramNames contains ProgramName then
		if SingleWindow then
			if ProgramName is "Safari" then tell application "Safari" to set WindowList to window 1
			if ProgramName is "Google Chrome" then tell application "Google Chrome" to set WindowList to window 1
		else
			if ProgramName is "Safari" then tell application "Safari" to set WindowList to every window whose id is greater than -1 and name is not "Extensions" and name is not "Favorites"
			if ProgramName is "Google Chrome" then tell application "Google Chrome" to set WindowList to every window whose id is greater than -1
		end if
		set URLList to ""
		set WindowCount to (count of WindowList)
		if WindowCount > 1 then set i to 1
		if SingleTab then
			if ProgramName is "Safari" then tell application "Safari" to if name of current tab of window 1 is not "Favorites" then set URLList to URLList & name of current tab of window 1 & " ” " & URL of current tab of window 1 & return
			if ProgramName is "Google Chrome" then tell application "Google Chrome" to if title of active tab of window 1 is not "New Tab" then set URLList to URLList & title of active tab of window 1 & " ” " & URL of active tab of window 1 & return
		else
			repeat with aWindow in WindowList
				if ProgramName is "Safari" then tell application "Safari" to set FullTabList to (every tab of aWindow whose name is not "Favorites")
				if ProgramName is "Google Chrome" then tell application "Google Chrome" to set FullTabList to (every tab of aWindow whose title is not "New Tab")
				if WindowCount > 1 then
					if i > 1 then set URLList to URLList & return
					set URLList to URLList & "Window " & i & " (" & (count of FullTabList)
					if (count of FullTabList) > 1 then
						set URLList to URLList & " tabs):" & return
					else
						set URLList to URLList & " tab):" & return
					end if
				end if
				repeat with aTab in FullTabList
					if ProgramName is "Safari" then tell application "Safari" to set URLList to URLList & name of aTab & " ” " & URL of aTab & return
					if ProgramName is "Google Chrome" then tell application "Google Chrome" to set URLList to URLList & title of aTab & " ” " & URL of aTab & return
				end repeat
				if WindowCount > 1 then set i to i + 1
			end repeat
		end if
		set the clipboard to URLList
		
		display notification "" with title "URLs copied to clipboard..."
	end if
	
end main

Switching between Chrome and Safari.
Moves the current tab, the frontmost window, or all windows from Safari to Chrome or from Chrome to Safari, maintaining the active tab on each window moved. If a single tab is moved then it is opened in the top window of the other browser. Sorry, this one’s a little incoherent, but it works.

property BrowserNames : {¬
	"Safari", ¬
	"Google Chrome", ¬
	"Firefox"}

property PreferredBrowsers : {1, 2}

main(true, false)

on main(DupAllTabs, DupAllWindows)
	local CurrentBrowser, TabList, ChooseBrowser, PB, TabWinIDs, CurTabURL, TabCount
	
	tell application "System Events"
		set CurrentBrowser to name of first process whose frontmost is true
	end tell
	
	--set CurrentBrowser to "Safari"
	--set CurrentBrowser to "Google Chrome"
	
	--display alert CurrentBrowser
	
	if (BrowserNames contains CurrentBrowser) then
		set TabList to {}
		set OtherBrowsers to {}
		repeat with i from 1 to count of BrowserNames
			if (item i of BrowserNames is not CurrentBrowser) then set end of OtherBrowsers to i
		end repeat
		if (count of OtherBrowsers) > 1 then
			set ChooseBrowser to {}
			set PB to {}
			repeat with i in OtherBrowsers
				set end of ChooseBrowser to item i of BrowserNames
				if PreferredBrowsers contains i then set end of PB to item i of BrowserNames
			end repeat
			if (count of PB) > 1 then
				set ChooseBrowser to beginning of (choose from list ChooseBrowser with prompt "Open page in which browser?" without empty selection allowed)
				if ChooseBrowser is false then return
			else if (count of PB) = 1 then
				set ChooseBrowser to beginning of PB
			end if
		else if (count of OtherBrowsers) = 1 then
			set ChooseBrowser to item (beginning of OtherBrowsers) of BrowserNames
		end if
		
		set CurTabURL to {}
		set FrontWinID to -1
		if CurrentBrowser is "Safari" then
			if (DupAllWindows or DupAllTabs) then
				tell application "Safari"
					repeat with i in windows
						if (closeable of i and (count of tabs of i) > 0) then
							set end of TabList to URL of (tabs of i whose URL does not contain "topsites://")
							set end of CurTabURL to URL of current tab of i
							if not DupAllWindows then
								close i
								exit repeat
							end if
						end if
					end repeat
				end tell
			else
				tell application "Safari"
					try
						set end of TabList to {URL of current tab of window 1}
						set end of CurTabURL to URL of current tab of window 1
						close current tab of window 1
					on error
						return
					end try
				end tell
			end if
		else if CurrentBrowser is "Google Chrome" then
			if (DupAllWindows or DupAllTabs) then
				tell application "Google Chrome"
					repeat with i in windows
						if (closeable of i) then
							set end of TabList to (URL of (tabs of i) whose title does not contain "New Tab" and URL does not contain "chrome://newtab/")
							set end of CurTabURL to URL of active tab of i
							if not DupAllWindows then
								close i
								exit repeat
							end if
						end if
					end repeat
				end tell
			else
				tell application "Google Chrome"
					try
						set end of TabList to {URL of active tab of window 1}
						set end of CurTabURL to URL of active tab of window 1
						close active tab of window 1
					on error
						return
					end try
				end tell
			end if
		end if
		
		set TabCount to 0
		repeat with i in TabList
			set TabCount to TabCount + (count of i)
		end repeat
		
		if TabCount > 0 then
			set TabWinIDs to {}
			if ChooseBrowser is "Safari" then
				tell application "Safari"
					activate
					repeat with W from 1 to count of TabList
						if (DupAllTabs or DupAllWindows) then make new document at end of documents
						repeat with T from 1 to count of item W of TabList
							try
								tell window 1 to make new tab with properties {URL:item T of item W of TabList}
							on error
								make new document at end of documents
								tell window 1 to make new tab with properties {URL:item T of item W of TabList}
							end try
							tell window 1 to close (tabs whose name is "Favorites")
							if (item T of item W of TabList is item W of CurTabURL) then
								set end of TabWinIDs to {count of tabs of window 1, id of window 1}
							end if
						end repeat
						if W = 1 then set FrontWinID to id of front window
					end repeat
					set W to windows
					repeat with i in W
						try
							close (tabs of i whose name is "Favorites")
						end try
					end repeat
					if (count of TabWinIDs) > 0 then
						repeat with W in TabWinIDs
							tell window id (end of W)
								set current tab to tab ((beginning of W))
							end tell
						end repeat
					end if
					try
						set index of window id FrontWinID to 1
					end try
				end tell
			else if ChooseBrowser is "Google Chrome" then
				tell application "Google Chrome"
					activate
					repeat with W from 1 to count of TabList
						if (DupAllTabs or DupAllWindows) then make new window with properties {index:1}
						repeat with T from 1 to count of item W of TabList
							try
								tell window 1 to make new tab with properties {URL:item T of item W of TabList}
							on error
								make new window with properties {index:1}
								tell window 1 to make new tab with properties {URL:item T of item W of TabList}
							end try
							tell window 1 to close (tabs whose title is "New Tab")
							if (item T of item W of TabList is item W of CurTabURL) then
								set end of TabWinIDs to {count of tabs of window 1, id of window 1}
							end if
						end repeat
						if W = 1 then set FrontWinID to id of front window
					end repeat
					set W to windows
					repeat with i in W
						try
							close (tabs of i whose title is "New Tab")
						end try
					end repeat
					if (count of TabWinIDs) > 0 then
						repeat with W in TabWinIDs
							tell window id (end of W)
								set active tab index to ((beginning of W))
							end tell
						end repeat
					end if
					try
						set index of window id FrontWinID to 1
					end try
				end tell
			end if
		end if
	end if
end main

Why write such a complicated code ?

It seems that it would be neater with two dedicated handlers.

tell application "System Events"
	set ProgramName to name of first process whose frontmost is true
end tell

# Define two parameters ruling the handlers behavior
set SingleWindowPref to false
set SingleTabPref to false

# Decide which handler must do the job
if ProgramName is "Safari" then
	set URLList to my workWithSafari(SingleWindowPref, SingleTabPref)
else if ProgramName is "Google Chrome" then
	set URLList to my workWithChrome(SingleWindowPref, SingleTabPref)
else
	return # Neither Safari nor Chrome
end if

set the clipboard to URLList

display notification "" with title "URLs copied to clipboard..."

#=====

on workWithSafari(SingleWindow, SingleTab)
	tell application "Safari"
		if SingleWindow then
			set WindowList to window 1
		else
			set WindowList to every window whose id is greater than -1 and name is not "Extensions" and name is not "Favorites"
		end if
	end tell
	set URLList to ""
	set WindowCount to (count of WindowList)
	if WindowCount > 1 then set i to 1
	if SingleTab then
		tell application "Safari" to if name of current tab of window 1 is not "Favorites" then set URLList to URLList & name of current tab of window 1 & " ” " & URL of current tab of window 1 & return
	else
		repeat with aWindow in WindowList
			tell application "Safari" to set FullTabList to (every tab of aWindow whose name is not "Favorites")
			if WindowCount > 1 then
				if i > 1 then set URLList to URLList & return
				set URLList to URLList & "Window " & i & " (" & (count of FullTabList)
				if (count of FullTabList) > 1 then
					set URLList to URLList & " tabs):" & return
				else
					set URLList to URLList & " tab):" & return
				end if
			end if
			repeat with aTab in FullTabList
				tell application "Safari" to set URLList to URLList & name of aTab & " ” " & URL of aTab & return
			end repeat
			if WindowCount > 1 then set i to i + 1
		end repeat
	end if
	return URLList
end workWithSafari

#=====

on workWithChrome(SingleWindow, SingleTab)
	tell application "Google Chrome"
		if SingleWindow then
			set WindowList to window 1
		else
			set WindowList to every window whose id is greater than -1
		end if
	end tell
	set URLList to ""
	set WindowCount to (count of WindowList)
	if WindowCount > 1 then set i to 1
	if SingleTab then
		tell application "Google Chrome" to if title of active tab of window 1 is not "New Tab" then set URLList to URLList & title of active tab of window 1 & " ” " & URL of active tab of window 1 & return
	else
		repeat with aWindow in WindowList
			tell application "Google Chrome" to set FullTabList to (every tab of aWindow whose title is not "New Tab")
			if WindowCount > 1 then
				if i > 1 then set URLList to URLList & return
				set URLList to URLList & "Window " & i & " (" & (count of FullTabList)
				if (count of FullTabList) > 1 then
					set URLList to URLList & " tabs):" & return
				else
					set URLList to URLList & " tab):" & return
				end if
			end if
			repeat with aTab in FullTabList
				tell application "Google Chrome" to set URLList to URLList & title of aTab & " ” " & URL of aTab & return
			end repeat
			if WindowCount > 1 then set i to i + 1
		end repeat
	end if
	return URLList
end workWithChrome

Yvan KOENIG (VALLAURIS, France) mercredi 11 mars 2015 21:08:43

Thanks Yvan,

I can certainly make the main script nicer looking by making the separate handlers, but I’m interested in consolidating, somehow, the code for Safari and Chrome that differ only by certain vocabulary like “current” vs “active” and “name” vs “title”.

Is there a way to accomplish that?

As far as I know, the answer is : no.
It’s why I split the code into two handlers which are quite identical so it would be easy to make changes if some are needed.

Yvan KOENIG (VALLAURIS, France) jeudi 12 mars 2015 10:03:08

Well, I’ll try it anyway. And with less code. Also, with the addition of the possibility of execution in the form of a script as is:


-- script lists all windows and tabs opened in the Safari or in the Google Chrome

tell application "System Events" to set visible of process (name of current application) to false
tell application "System Events" to set ProgramName to name of 1st process whose frontmost is true
if not (ProgramName is in {"Google Chrome", "Safari"}) then return
# Define two parameters ruling the handlers behavior
set {SingleWindowPref, SingleTabPref} to {false, false}
set URLList to my workWithSafariOrWithChrome(SingleWindowPref, SingleTabPref, ProgramName)


on workWithSafariOrWithChrome(SingleWindow, SingleTab, ProgramName)
	if SingleWindow then
		set WindowList to window 1 of application ProgramName
	else
		if ProgramName is "Safari" then
			tell application "Safari" to set WindowList to windows whose (id > -1) and (name is not "Extensions") and (name is not "Favorites")
		else
			tell application "Google Chrome" to set WindowList to windows whose id > -1
		end if
	end if
	
	set {URLList, i} to {{}, 1}
	if SingleTab then
		if ProgramName is "Safari" then
			tell application "Safari" to if name of current tab of window 1 is not "Favorites" then set end of URLList to name of current tab of window 1 & " — " & URL of current tab of window 1
		else
			tell application "Google Chrome" to if title of active tab of window 1 is not "New Tab" then set end of URLList to title of active tab of window 1 & " — " & URL of active tab of window 1
		end if
		return URLList
	end if
	
	repeat with aWindow in WindowList
		if ProgramName is "Safari" then
			tell application "Safari" to set FullTabList to (tabs of aWindow whose name is not "Favorites")
		else
			tell application "Google Chrome" to set FullTabList to (tabs of aWindow whose title is not "New Tab")
		end if
		set end of URLList to "Window " & i & "   (" & (count of FullTabList) & " tabs):"
		
		repeat with aTab in FullTabList
			if ProgramName is "Safari" then
				tell application "Safari" to set end of URLList to name of aTab & " — " & URL of aTab
			else
				tell application "Google Chrome" to set end of URLList to title of aTab & " — " & URL of aTab
			end if
		end repeat
		if (count of WindowList) > 1 then set i to i + 1
	end repeat
	return URLList
end workWithSafariOrWithChrome