Friday, October 19, 2018
  • Index
  •  » Code Exchange
  •  » Safari CookieCleaner - full featured white/junk list based cleaner

#1 2018-06-03 09:26:48 pm

Ron_Pinkas
Member
Registered: 2018-06-04
Posts: 2

Safari CookieCleaner - full featured white/junk list based cleaner

Safari-CookieCleaner is a full featured applet allowing automated quick and safe cleanup of Safari's Cookie/Cache/Local Storage/Databases.

The optional Whitelist allows intuitive definition of domains to be protected, supporting patterns:
   starts with
   ends with
   contains
   exact

The optional Junk list (intersected against the Whitelist) allows quick and safe definition of common/frequent privacy threats. Whitelisted domains will be protected even if matched. If the first item of the Junk list is "*" then all EXCEPT the Whitelisted domains will be deleted, but ONLY if a Whitelist is indeed defined.

The applet will make sure to first find, and move to Trash, respective Databases (of matched non whitelisted junk), so as to circumvent the common difficulty of removal of cookies from domains with database.

The applet is a small single script which can be run through Script Editor or saved as an application and run like any other app.

The only required setup is to enable Assistive Accessibility:
(System Preferences->Security & Privacy->Privacy->Accessibility)
to Script Editor (if not enabled yet) and/or to the generated application file, if desired to be run as a stand alone.

The script is maintained and available at: https://github.com/ronpinkas/Safari-CookieCleaner

Your input is very welcome.

Applescript:


(*
Author: Ron Pinkas

You are free to use this code in any way permitable by the GNU General Public License V3.0
*)


on main()
   set debugLog to false
   
   # Item starting with "*" will match domain names ENDING with its subssequent text
   # Item endiding with "*" will match domain names BEGINING with its preceding text
   # Items starting and ending with "*" will match domain names CONTAINING its enclosed text    
   # Otherwise NON GRIDY so use full name INCLUDING .com etc.!
   set whiteList to {"amazon.com", ¬
       "ebay.com", ¬
       "facebook.com", "facebook.net", ¬
       "github.com", "*.githubusercontent.com", ¬
       "google.com", "google-analytics.com", "*.googleapis.com", "googleusercontent.com", "gstatic.com", ¬
       "instagram.com", "cdninstagram.com", ¬
       "netflix.com", ¬
       "paypal.com", ¬
       "twitter.com", "twimg.com", ¬
       "visualstudio.com", ¬
       "whatsapp.*", ¬
       "youtube.com"}
   
   # Items in this list will be fed AS-IS to Safari's cookie search field, which is GRIDY, i.e
   # it uses CONTAINS logic to search for matches - so be careful or better yet
   # protect valuable domains using the above whiteList!
   # IF FIRST Element is "*" then the junkList will be ignored and ALL EXCEPT whiteList will be deleted!
   set junkList to {"*", ".co.", "addthis", "adobe", "affirm", "akc", "allure", "akamai", "azureedge", ¬
       "barron", "beaver", "bam-x", "bing", "bkrtx", "blue", "bold", "boot", "bounce", "btt", "b-cdn", ¬
       "cdn", "chartbeat", "chimp", "click", "cloudflare", "cloudfront", "cnbc", "cohesion", "cookie", "count", "coust", "crazy", "createjs", "criteo", "crsspxl", "crwd", ¬
       "dazz", "demdex", "dental", "desk", "digitech", "disqus", "docashop", ¬
       "exelator", "expose", "ggpht", "facebook", "fbcdn", "google-analytics", "googleadservices", "googletag", "googleusercontent", "gravatar", "gstatic", ¬
       "home", "imgur", "jeeng", "link", "local", "mac", "networks", "newrelic", "omny", "opentok", "optimizer", "parsely", "petametric", "pir.fm", "porn", "quantserve", ¬
       "reddit", "resources", "score", "sekindo", "sstatic", "stack", "taboola", "track", "truspilot", "twimg", "user", "visistat", "ytimg"}
   
   set homePath to (path to home folder)
   set databasesPath to alias ((homePath as text) & "Library:Safari:Databases")
   set indexedDBPath to alias ((databasesPath as text) & "___IndexedDB")
   
   tell application "Safari" to activate
   
   tell application "System Events"
       tell process "Safari"
           set frontmost to true
           
           set myMenu to menu "Safari" of menu bar item "Safari" of menu bar 1
           click menu item "Preferences…" of myMenu
           # WAIT till opened.
           repeat until (exists button "Privacy" of toolbar 1) of window 1
               delay 0.01
           end repeat
           
           set windowPreferences to window 1
           
           tell windowPreferences
               click button "Privacy" of toolbar 1
               # WAIT till opened.
               repeat until (exists button "Manage Website Data…" of group 1 of group 1)
                   delay 0.01
               end repeat
               
               click button "Manage Website Data…" of group 1 of group 1
               # WAIT till opened.
               repeat until (exists table 1 of scroll area 1 of sheet 1)
                   delay 0.01
               end repeat
               
               set myTable to table 1 of scroll area 1 of sheet 1
               set searchField to text field 1 of sheet 1
               
               -- Allow ALL except WHITE list to be considered JUNK by setting FIRST Specifier to "*"!
               try
                   if (count of whiteList) > 0 and first item of junkList is equal to "*" then
                       # will never get here if whiteList is undefined or empty!
                       # Simulate an ALL is JUNK mode - ignoring the rest of junkList.
                       set junkList to {""}
                   end if
               end try
               
               # SEARCH MATCHES for ALL Specifiers of junkList
               repeat with theJunkSpecifier in junkList
                   if theJunkSpecifier is equal to "" and theJunkSpecifier is not equal to first item of junkList then
                       display notification "Invalid junk specifier \"\""
                       set theJunkSpecifier to "*empty specifier ignored*"
                   end if
                   
                   select searchField
                   set value of searchField to theJunkSpecifier
                   
                   -- WAIT UNTIL Search finalizes, with either some rows OR "No Saved Website Data" is displayed!
                   set prospectiveMatches to true
                   repeat while prospectiveMatches
                       delay 0.01
                       
                       if (count of (rows of myTable)) > 0 then
                           --display notification "Found prospective junk for: '" & theJunkSpecifier & "'"
                           exit repeat
                       else
                           try
                               set uiChildren to entire contents of sheet 1
                               
                               repeat with theUIChild in uiChildren
                                   if class of theUIChild is static text and value of theUIChild contains "No Saved Website Data" then
                                       set prospectiveMatches to false
                                       exit repeat
                                   end if
                               end repeat
                           end try
                       end if
                   end repeat
                   -- END WAIT (repeat while prospectiveMatches                    
                   
                   set prospectiveJunkRows to count of (rows of myTable)
                   set whiteListed to false
                   set rowSite to ""
                   set rowIndex to 1
                   
                   #Reset Scroll bar to TOP, incase Windos was alreasdy opened, and scrolled by User
                   set value of scroll bar 1 of scroll area 1 of sheet 1 to 0
                   
                   # PROCESS ALL Lines MATCHING the junk specifier
                   repeat while prospectiveJunkRows ≥ rowIndex
                       try
                           set selectedRow to row rowIndex of myTable
                           select selectedRow
                           set rowSite to description of first UI element of selectedRow
                           set domainName to first item of my textTokens(rowSite, space)
                           
                           if debugLog then
                               log "-------- TOP"
                               log "Cookie: '" & rowSite & "'"
                               log "Domain: '" & domainName & "'"
                               log "rowIndex: " & rowIndex as text
                               log "prospectiveJunkRows: " & prospectiveJunkRows as text
                               log "count of rows: " & (count of (rows of myTable)) as text
                           end if
                           
                           -- AppleScript indexing is 1 based BUT item 0 maps to 1 so it can be confusing!
                           -- SCROLL so selected row (as well as at least the prior and next rows) are in the VISIBLE Scroll port.
                           repeat while (count of value of attribute "AXVisibleChildren" of row (my min(rowIndex + 1, prospectiveJunkRows)) of myTable) = 0
                               --tell scroll area 1 of sheet 1 to perform action "AXScrollUpByPage"
                               click button 1 of scroll bar 1 of scroll area 1 of sheet 1
                               delay 0.01
                           end repeat
                           repeat while (count of value of attribute "AXVisibleChildren" of row (my max(rowIndex - 1, 1)) of myTable) = 0
                               --tell scroll area 1 of sheet 1 to perform action "AXScrollDownByPage"
                               click button 2 of scroll bar 1 of scroll area 1 of sheet 1
                               delay 0.01
                           end repeat
                           -- END SCROLL
                           
                           -- SEARCH whiteList
                           set whiteListed to false
                           repeat with whiteSite in whiteList
                               --log "White: '" & whiteSite & "'"
                               
                               # whiteSite is a REFERENCE which must be DEreferenced to be correctly compared!
                               --log "Equals: " & (domainName is equal to whiteSite) as text
                               --log "Equals: " & (domainName is equal to whiteSite as text) as text
                               
                               # INTENTIONALLY not comparing equality when "*" found!
                               if first character of whiteSite = "*" and last character of whiteSite = "*" then
                                   if domainName contains (text 2 through -2 of whiteSite) then
                                       log "Contains: " & (text 2 through -2 of whiteSite)
                                       set whiteListed to true
                                       exit repeat
                                   end if
                               else if first character of whiteSite = "*" then
                                   if domainName ends with text 2 through -1 of whiteSite then
                                       log "Ends with: " & (text 2 through -1 of whiteSite)
                                       set whiteListed to true
                                       exit repeat
                                   end if
                               else if last character of whiteSite = "*" then
                                   if domainName begins with text 1 through -2 of whiteSite then
                                       log "Begins with: " & (text 1 through -2 of whiteSite)
                                       set whiteListed to true
                                       exit repeat
                                   end if
                               else if domainName is equal to whiteSite as text then
                                   log "Equals to: " & whiteSite
                                   set whiteListed to true
                                   exit repeat
                               end if
                           end repeat
                           -- END SEARCH whiteList
                           
                           if whiteListed then
                               log "White: " & rowSite
                               set rowIndex to rowIndex + 1
                           else
                               log "Delete: " & rowSite
                               
                               # DELETE DATABASE first if present.
                               if rowSite contains "Databases" then
                                   -- FIND & DELETE DATABASE file
                                   log domainName & ": Has Databases!"
                                   
                                   # Limiting GRIDINESS of conatins by prefixing a "." and "_"                                    
                                   set myFiles to (files of application "System Events"'s folder (databasesPath as text) where (name contains ("." & domainName)) or name contains ("_" & domainName))
                                   set myFiles to myFiles & (folders of application "System Events"'s folder (indexedDBPath as text) where (name contains ("." & domainName)) or name contains ("_" & domainName))
                                   
                                   repeat with theFile in myFiles
                                       log "Trash: " & name of theFile
                                       move theFile to application "System Events"'s trash
                                   end repeat
                                   -- END FIND & DELETE
                               end if
                               -- END DELETE DATABASE
                               
                               # DELETE COOCKIE!
                               set repeatCount to 0
                               repeat until (repeatCount > 10) or (prospectiveJunkRows > (count of (rows of myTable)))
                                   set repeatCount to repeatCount + 1
                                   
                                   # Because row may be last row and might have been deleted just after above <until...> was evaluated.
                                   try
                                       # Row must be SELECTED (even if was) to enable the Remove button!
                                       repeat until selected of selectedRow
                                           select selectedRow
                                           delay 0.01
                                       end repeat
                                       click button "Remove" of sheet 1
                                       delay 0.1
                                   end try
                               end repeat
                               
                               if prospectiveJunkRows = (count of (rows of myTable)) then
                                   log "Failed deleting: " & rowSite
                               end if
                               -- END DELETE COOCKIE
                           end if
                       on error errorMessage number errorNumber from f to t partial result p
                           --log "*** ERROR! " & errorMessage
                           error errorMessage number errorNumber from f to t partial result p
                       end try
                       
                       # Here instead of just after DELETE because it might also change outside of our control!
                       set prospectiveJunkRows to count of (rows of myTable)
                       
                       if debugLog then
                           log "-------- BOTTOM"
                           log "Cookie: '" & rowSite & "'"
                           log "Domain: '" & domainName & "'"
                           log "rowIndex: " & rowIndex as text
                           log "prospectiveJunkRows: " & prospectiveJunkRows as text
                           log "count of rows: " & (count of (rows of myTable)) as text
                       end if
                   end repeat #while prospectiveJunkRows > rowIndex    
                   -- END PROCESS ALL Lines MATCHING
               end repeat #with theJunkSpecifier in junkList
               -- ENND SEARCH
               
               click button "Done" of sheet 1
               
               # Don't like this style.
               --keystroke "w" using command down
               
               # This is hard coded but works
               --click button 1
               
               # WARNING: this line will FAIL upon removal of the () around <butons where...>
               # because the <where ...> clause will be wrongly applied the implied
               # <of windowPreferences> target instead of its <buttons> collection!    
               click button 1 of (buttons where role description = "close button")
           end tell #windowPreferences
       end tell
   end tell
end main

on textTokens(textToParse, parseDelimiters)
   set listTokens to missing value
   set asTID to AppleScript's text item delimiters
   
   try
       set AppleScript's text item delimiters to parseDelimiters
       set listTokens to text items of textToParse
   end try
   
   set AppleScript's text item delimiters to asTID
   return listTokens
end textTokens

on min(n1, n2)
   if n1 ≤ n2 then
       return n1
   end if
   
   return n2
end min

on max(n1, n2)
   if n1 ≥ n2 then
       return n1
   end if
   
   return n2
end max

on run {}
   main()
end run

* Revised to reflect 2 suggestions by Nigel.
* Revised 2 <repeat while not ...> to <repeat until ...>

Last edited by Ron_Pinkas (2018-06-05 08:42:35 am)


Filed under: GUI, safari, Cleaner, cookie

Offline

 

#2 2018-06-04 03:01:55 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4693

Re: Safari CookieCleaner - full featured white/junk list based cleaner

Hi Ron.

Welcome to MacScripter and thanks for posting your code. I haven't got round to trying it yet, but just looking at the beginning of the System Events block:

Applescript:

tell application "System Events"
   tell process "Safari"
       activate
       set frontmost to true
       
       set myMenu to menu "Safari" of menu bar item "Safari" of menu bar 1
       click menu item "Preferences…" of myMenu
       
       repeat while not (exists window 1)
           delay 0.01
       end repeat
       set windowPreferences to window 1
       
       -- etc. …
   end tell
end tell



'activate' is an application command, so here it's being applied to the application "System Events", not to the process "Safari". If you want to activate Safari, the command must go to the Safari application.

Repeating while window 1 doesn't exist assumes that Safari has no windows open at all when the "Preferences…" menu item's clicked. It would be safer to count the windows first, then click the menu item and wait until the number of windows is more than it was:

Applescript:

tell application "Safari" to activate

tell application "System Events"
   tell process "Safari"
       set frontmost to true
       
       set windowCount to (count windows)
       
       set myMenu to menu "Safari" of menu bar item "Safari" of menu bar 1
       click menu item "Preferences…" of myMenu
       
       repeat until ((count windows) > windowCount)
           delay 0.01
       end repeat
       set windowPreferences to window 1
   end tell
   
   -- etc. …
end tell


NG

Offline

 

#3 2018-06-04 08:15:42 am

Ron_Pinkas
Member
Registered: 2018-06-04
Posts: 2

Re: Safari CookieCleaner - full featured white/junk list based cleaner

Hi Nigel,

Many thanks indeed.

Strangely somehow in all the samples I saw, Safari was listed only as a process rather than an application, and I was myself curious about the distinction. I somehow missed the fact that process and application are not mutually exclusive.

Right after posting I did notice the weakness of <while not exists window 1> but decided to postpone the revision to today. I was wondering if someone will notice before I got to it. smile

But, your suggested correction also assumes that the preferncesWindow is not already open smile

The correct fix is:

Applescript:


           set myMenu to menu "Safari" of menu bar item "Safari" of menu bar 1
           click menu item "Preferences…" of myMenu
           # WAIT till opened.
           repeat until (exists button "Privacy" of toolbar 1) of window 1
               delay 0.01
           end repeat

And while at it, I of course corrected the equally important omission of "macsripter.net" from whiteList. smile

Thanks again.

Ron

Last edited by Ron_Pinkas (2018-06-04 01:16:45 pm)

Offline

 
  • Index
  •  » Code Exchange
  •  » Safari CookieCleaner - full featured white/junk list based cleaner

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)