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.
5 #import "chrome/browser/ui/cocoa/applescript/tab_applescript.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_id.h"
14 #include "chrome/browser/sessions/session_tab_helper.h"
15 #include "chrome/browser/ui/cocoa/applescript/apple_event_util.h"
16 #include "chrome/browser/ui/cocoa/applescript/error_applescript.h"
17 #include "chrome/common/url_constants.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"
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;
37 void ResumeAppleEventAndSendReply(NSAppleEventManagerSuspensionID suspension_id,
38 const base::Value* result_value) {
39 NSAppleEventDescriptor* result_descriptor =
40 chrome::mac::ValueToAppleEventDescriptor(result_value);
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];
52 @interface TabAppleScript()
53 @property (nonatomic, copy) NSString* tempURL;
56 @implementation TabAppleScript
58 @synthesize tempURL = tempURL_;
61 if ((self = [super init])) {
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];
77 - (id)initWithWebContents:(content::WebContents*)webContents {
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];
97 - (void)setWebContents:(content::WebContents*)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];
110 [self setURL:[self tempURL]];
118 NavigationEntry* entry = webContents_->GetController().GetActiveEntry();
122 const GURL& url = entry->GetVirtualURL();
123 return base::SysUTF8ToNSString(url.spec());
126 - (void)setURL:(NSString*)aURL {
127 // If a scripter sets a URL before the node is added save it at a temporary
130 [self setTempURL:aURL];
134 GURL url(base::SysNSStringToUTF8(aURL));
135 // check for valid url.
136 if (!url.is_empty() && !url.is_valid()) {
137 AppleScript::SetError(AppleScript::errInvalidURL);
141 NavigationEntry* entry = webContents_->GetController().GetActiveEntry();
145 const GURL& previousURL = entry->GetVirtualURL();
146 webContents_->OpenURL(OpenURLParams(
148 content::Referrer(previousURL, blink::WebReferrerPolicyDefault),
150 content::PAGE_TRANSITION_TYPED,
155 NavigationEntry* entry = webContents_->GetController().GetActiveEntry();
159 base::string16 title = entry ? entry->GetTitle() : base::string16();
160 return base::SysUTF16ToNSString(title);
163 - (NSNumber*)loading {
164 BOOL loadingValue = webContents_->IsLoading() ? YES : NO;
165 return [NSNumber numberWithBool:loadingValue];
168 - (void)handlesUndoScriptCommand:(NSScriptCommand*)command {
169 webContents_->Undo();
172 - (void)handlesRedoScriptCommand:(NSScriptCommand*)command {
173 webContents_->Redo();
176 - (void)handlesCutScriptCommand:(NSScriptCommand*)command {
180 - (void)handlesCopyScriptCommand:(NSScriptCommand*)command {
181 webContents_->Copy();
184 - (void)handlesPasteScriptCommand:(NSScriptCommand*)command {
185 webContents_->Paste();
188 - (void)handlesSelectAllScriptCommand:(NSScriptCommand*)command {
189 webContents_->SelectAll();
192 - (void)handlesGoBackScriptCommand:(NSScriptCommand*)command {
193 NavigationController& navigationController = webContents_->GetController();
194 if (navigationController.CanGoBack())
195 navigationController.GoBack();
198 - (void)handlesGoForwardScriptCommand:(NSScriptCommand*)command {
199 NavigationController& navigationController = webContents_->GetController();
200 if (navigationController.CanGoForward())
201 navigationController.GoForward();
204 - (void)handlesReloadScriptCommand:(NSScriptCommand*)command {
205 NavigationController& navigationController = webContents_->GetController();
206 const bool checkForRepost = true;
207 navigationController.Reload(checkForRepost);
210 - (void)handlesStopScriptCommand:(NSScriptCommand*)command {
211 RenderViewHost* view = webContents_->GetRenderViewHost();
213 // We tolerate Stop being called even before a view has been created.
214 // So just log a warning instead of a NOTREACHED().
215 DLOG(WARNING) << "Stop: no view for handle ";
222 - (void)handlesPrintScriptCommand:(NSScriptCommand*)command {
224 printing::PrintViewManager::FromWebContents(webContents_)->PrintNow();
226 AppleScript::SetError(AppleScript::errInitiatePrinting);
230 - (void)handlesSaveScriptCommand:(NSScriptCommand*)command {
231 NSDictionary* dictionary = [command evaluatedArguments];
233 NSURL* fileURL = [dictionary objectForKey:@"File"];
234 // Scripter has not specifed the location at which to save, so we prompt for
237 webContents_->OnSavePage();
241 base::FilePath mainFile(base::SysNSStringToUTF8([fileURL path]));
242 // We create a directory path at the folder within which the file exists.
243 // Eg. if main_file = '/Users/Foo/Documents/Google.html'
244 // then directory_path = '/Users/Foo/Documents/Google_files/'.
245 base::FilePath directoryPath = mainFile.RemoveExtension();
246 directoryPath = directoryPath.InsertBeforeExtension(std::string("_files/"));
248 NSString* saveType = [dictionary objectForKey:@"FileType"];
250 content::SavePageType savePageType = content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML;
252 if ([saveType isEqualToString:@"only html"]) {
253 savePageType = content::SAVE_PAGE_TYPE_AS_ONLY_HTML;
254 } else if ([saveType isEqualToString:@"complete html"]) {
255 savePageType = content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML;
257 AppleScript::SetError(AppleScript::errInvalidSaveType);
262 webContents_->SavePage(mainFile, directoryPath, savePageType);
265 - (void)handlesCloseScriptCommand:(NSScriptCommand*)command {
266 webContents_->GetDelegate()->CloseContents(webContents_);
269 - (void)handlesViewSourceScriptCommand:(NSScriptCommand*)command {
270 NavigationEntry* entry =
271 webContents_->GetController().GetLastCommittedEntry();
273 webContents_->OpenURL(
274 OpenURLParams(GURL(content::kViewSourceScheme + std::string(":") +
275 entry->GetURL().spec()),
278 content::PAGE_TRANSITION_LINK,
283 - (id)handlesExecuteJavascriptScriptCommand:(NSScriptCommand*)command {
284 content::RenderFrameHost* frame = webContents_->GetMainFrame();
290 NSAppleEventManager* manager = [NSAppleEventManager sharedAppleEventManager];
291 NSAppleEventManagerSuspensionID suspensionID =
292 [manager suspendCurrentAppleEvent];
293 content::RenderFrameHost::JavaScriptResultCallback callback =
294 base::Bind(&ResumeAppleEventAndSendReply, suspensionID);
296 base::string16 script = base::SysNSStringToUTF16(
297 [[command evaluatedArguments] objectForKey:@"javascript"]);
298 frame->ExecuteJavaScript(script, callback);