1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #import "chrome/browser/ui/cocoa/applescript/tab_applescript.h" 6 7 #include "base/bind.h" 8 #include "base/files/file_path.h" 9 #include "base/logging.h" 10 #import "base/mac/scoped_nsobject.h" 11 #include "base/strings/sys_string_conversions.h" 12 #include "chrome/browser/printing/print_view_manager.h" 13 #include "chrome/browser/sessions/session_tab_helper.h" 14 #include "chrome/browser/ui/cocoa/applescript/apple_event_util.h" 15 #include "chrome/browser/ui/cocoa/applescript/error_applescript.h" 16 #include "chrome/common/url_constants.h" 17 #include "components/sessions/session_id.h" 18 #include "content/public/browser/navigation_controller.h" 19 #include "content/public/browser/navigation_entry.h" 20 #include "content/public/browser/render_frame_host.h" 21 #include "content/public/browser/render_view_host.h" 22 #include "content/public/browser/save_page_type.h" 23 #include "content/public/browser/web_contents.h" 24 #include "content/public/browser/web_contents_delegate.h" 25 #include "url/gurl.h" 26 27 using content::NavigationController; 28 using content::NavigationEntry; 29 using content::OpenURLParams; 30 using content::RenderFrameHost; 31 using content::RenderViewHost; 32 using content::Referrer; 33 using content::WebContents; 34 35 namespace { 36 37 void ResumeAppleEventAndSendReply(NSAppleEventManagerSuspensionID suspension_id, 38 const base::Value* result_value) { 39 NSAppleEventDescriptor* result_descriptor = 40 chrome::mac::ValueToAppleEventDescriptor(result_value); 41 42 NSAppleEventManager* manager = [NSAppleEventManager sharedAppleEventManager]; 43 NSAppleEventDescriptor* reply_event = 44 [manager replyAppleEventForSuspensionID:suspension_id]; 45 [reply_event setParamDescriptor:result_descriptor 46 forKeyword:keyDirectObject]; 47 [manager resumeWithSuspensionID:suspension_id]; 48 } 49 50 } // namespace 51 52 @interface TabAppleScript() 53 @property (nonatomic, copy) NSString* tempURL; 54 @end 55 56 @implementation TabAppleScript 57 58 @synthesize tempURL = tempURL_; 59 60 - (id)init { 61 if ((self = [super init])) { 62 SessionID session; 63 SessionID::id_type futureSessionIDOfTab = session.id() + 1; 64 // Holds the SessionID that the new tab is going to get. 65 base::scoped_nsobject<NSNumber> numID( 66 [[NSNumber alloc] initWithInt:futureSessionIDOfTab]); 67 [self setUniqueID:numID]; 68 } 69 return self; 70 } 71 72 - (void)dealloc { 73 [tempURL_ release]; 74 [super dealloc]; 75 } 76 77 - (id)initWithWebContents:(content::WebContents*)webContents { 78 if (!webContents) { 79 [self release]; 80 return nil; 81 } 82 83 if ((self = [super init])) { 84 // It is safe to be weak; if a tab goes away (e.g. the user closes a tab) 85 // the AppleScript runtime calls tabs in AppleScriptWindow and this 86 // particular tab is never returned. 87 webContents_ = webContents; 88 SessionTabHelper* session_tab_helper = 89 SessionTabHelper::FromWebContents(webContents); 90 base::scoped_nsobject<NSNumber> numID( 91 [[NSNumber alloc] initWithInt:session_tab_helper->session_id().id()]); 92 [self setUniqueID:numID]; 93 } 94 return self; 95 } 96 97 - (void)setWebContents:(content::WebContents*)webContents { 98 DCHECK(webContents); 99 // It is safe to be weak; if a tab goes away (e.g. the user closes a tab) 100 // the AppleScript runtime calls tabs in AppleScriptWindow and this 101 // particular tab is never returned. 102 webContents_ = webContents; 103 SessionTabHelper* session_tab_helper = 104 SessionTabHelper::FromWebContents(webContents); 105 base::scoped_nsobject<NSNumber> numID( 106 [[NSNumber alloc] initWithInt:session_tab_helper->session_id().id()]); 107 [self setUniqueID:numID]; 108 109 if ([self tempURL]) 110 [self setURL:[self tempURL]]; 111 } 112 113 - (NSString*)URL { 114 if (!webContents_) { 115 return nil; 116 } 117 118 NavigationEntry* entry = webContents_->GetController().GetActiveEntry(); 119 if (!entry) { 120 return nil; 121 } 122 const GURL& url = entry->GetVirtualURL(); 123 return base::SysUTF8ToNSString(url.spec()); 124 } 125 126 - (void)setURL:(NSString*)aURL { 127 // If a scripter sets a URL before the node is added save it at a temporary 128 // location. 129 if (!webContents_) { 130 [self setTempURL:aURL]; 131 return; 132 } 133 134 GURL url(base::SysNSStringToUTF8(aURL)); 135 // check for valid url. 136 if (!url.is_empty() && !url.is_valid()) { 137 AppleScript::SetError(AppleScript::errInvalidURL); 138 return; 139 } 140 141 NavigationEntry* entry = webContents_->GetController().GetActiveEntry(); 142 if (!entry) 143 return; 144 145 const GURL& previousURL = entry->GetVirtualURL(); 146 webContents_->OpenURL(OpenURLParams( 147 url, 148 content::Referrer(previousURL, blink::WebReferrerPolicyDefault), 149 CURRENT_TAB, 150 ui::PAGE_TRANSITION_TYPED, 151 false)); 152 } 153 154 - (NSString*)title { 155 NavigationEntry* entry = webContents_->GetController().GetActiveEntry(); 156 if (!entry) 157 return nil; 158 159 base::string16 title = entry ? entry->GetTitle() : base::string16(); 160 return base::SysUTF16ToNSString(title); 161 } 162 163 - (NSNumber*)loading { 164 BOOL loadingValue = webContents_->IsLoading() ? YES : NO; 165 return [NSNumber numberWithBool:loadingValue]; 166 } 167 168 - (void)handlesUndoScriptCommand:(NSScriptCommand*)command { 169 webContents_->Undo(); 170 } 171 172 - (void)handlesRedoScriptCommand:(NSScriptCommand*)command { 173 webContents_->Redo(); 174 } 175 176 - (void)handlesCutScriptCommand:(NSScriptCommand*)command { 177 webContents_->Cut(); 178 } 179 180 - (void)handlesCopyScriptCommand:(NSScriptCommand*)command { 181 webContents_->Copy(); 182 } 183 184 - (void)handlesPasteScriptCommand:(NSScriptCommand*)command { 185 webContents_->Paste(); 186 } 187 188 - (void)handlesSelectAllScriptCommand:(NSScriptCommand*)command { 189 webContents_->SelectAll(); 190 } 191 192 - (void)handlesGoBackScriptCommand:(NSScriptCommand*)command { 193 NavigationController& navigationController = webContents_->GetController(); 194 if (navigationController.CanGoBack()) 195 navigationController.GoBack(); 196 } 197 198 - (void)handlesGoForwardScriptCommand:(NSScriptCommand*)command { 199 NavigationController& navigationController = webContents_->GetController(); 200 if (navigationController.CanGoForward()) 201 navigationController.GoForward(); 202 } 203 204 - (void)handlesReloadScriptCommand:(NSScriptCommand*)command { 205 NavigationController& navigationController = webContents_->GetController(); 206 const bool checkForRepost = true; 207 navigationController.Reload(checkForRepost); 208 } 209 210 - (void)handlesStopScriptCommand:(NSScriptCommand*)command { 211 webContents_->Stop(); 212 } 213 214 - (void)handlesPrintScriptCommand:(NSScriptCommand*)command { 215 bool initiated = 216 printing::PrintViewManager::FromWebContents(webContents_)->PrintNow(); 217 if (!initiated) { 218 AppleScript::SetError(AppleScript::errInitiatePrinting); 219 } 220 } 221 222 - (void)handlesSaveScriptCommand:(NSScriptCommand*)command { 223 NSDictionary* dictionary = [command evaluatedArguments]; 224 225 NSURL* fileURL = [dictionary objectForKey:@"File"]; 226 // Scripter has not specifed the location at which to save, so we prompt for 227 // it. 228 if (!fileURL) { 229 webContents_->OnSavePage(); 230 return; 231 } 232 233 base::FilePath mainFile(base::SysNSStringToUTF8([fileURL path])); 234 // We create a directory path at the folder within which the file exists. 235 // Eg. if main_file = '/Users/Foo/Documents/Google.html' 236 // then directory_path = '/Users/Foo/Documents/Google_files/'. 237 base::FilePath directoryPath = mainFile.RemoveExtension(); 238 directoryPath = directoryPath.InsertBeforeExtension(std::string("_files/")); 239 240 NSString* saveType = [dictionary objectForKey:@"FileType"]; 241 242 content::SavePageType savePageType = content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML; 243 if (saveType) { 244 if ([saveType isEqualToString:@"only html"]) { 245 savePageType = content::SAVE_PAGE_TYPE_AS_ONLY_HTML; 246 } else if ([saveType isEqualToString:@"complete html"]) { 247 savePageType = content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML; 248 } else { 249 AppleScript::SetError(AppleScript::errInvalidSaveType); 250 return; 251 } 252 } 253 254 webContents_->SavePage(mainFile, directoryPath, savePageType); 255 } 256 257 - (void)handlesCloseScriptCommand:(NSScriptCommand*)command { 258 webContents_->GetDelegate()->CloseContents(webContents_); 259 } 260 261 - (void)handlesViewSourceScriptCommand:(NSScriptCommand*)command { 262 NavigationEntry* entry = 263 webContents_->GetController().GetLastCommittedEntry(); 264 if (entry) { 265 webContents_->OpenURL( 266 OpenURLParams(GURL(content::kViewSourceScheme + std::string(":") + 267 entry->GetURL().spec()), 268 Referrer(), 269 NEW_FOREGROUND_TAB, 270 ui::PAGE_TRANSITION_LINK, 271 false)); 272 } 273 } 274 275 - (id)handlesExecuteJavascriptScriptCommand:(NSScriptCommand*)command { 276 content::RenderFrameHost* frame = webContents_->GetMainFrame(); 277 if (!frame) { 278 NOTREACHED(); 279 return nil; 280 } 281 282 NSAppleEventManager* manager = [NSAppleEventManager sharedAppleEventManager]; 283 NSAppleEventManagerSuspensionID suspensionID = 284 [manager suspendCurrentAppleEvent]; 285 content::RenderFrameHost::JavaScriptResultCallback callback = 286 base::Bind(&ResumeAppleEventAndSendReply, suspensionID); 287 288 base::string16 script = base::SysNSStringToUTF16( 289 [[command evaluatedArguments] objectForKey:@"javascript"]); 290 frame->ExecuteJavaScript(script, callback); 291 292 return nil; 293 } 294 295 @end 296