1// Copyright (c) 2011 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/window_applescript.h"
6
7#include "base/logging.h"
8#import "base/memory/scoped_nsobject.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/time.h"
11#import "chrome/browser/app_controller_mac.h"
12#import "chrome/browser/chrome_browser_application_mac.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/tabs/tab_strip_model.h"
15#include "chrome/browser/ui/browser.h"
16#include "chrome/browser/ui/browser_list.h"
17#include "chrome/browser/ui/browser_navigator.h"
18#include "chrome/browser/ui/browser_window.h"
19#include "chrome/browser/ui/cocoa/applescript/constants_applescript.h"
20#include "chrome/browser/ui/cocoa/applescript/error_applescript.h"
21#import "chrome/browser/ui/cocoa/applescript/tab_applescript.h"
22#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
23#include "chrome/common/url_constants.h"
24#include "content/browser/tab_contents/tab_contents.h"
25
26@interface WindowAppleScript(WindowAppleScriptPrivateMethods)
27// The NSWindow that corresponds to this window.
28- (NSWindow*)nativeHandle;
29@end
30
31@implementation WindowAppleScript
32
33- (id)init {
34  // Check which mode to open a new window.
35  NSScriptCommand* command = [NSScriptCommand currentCommand];
36  NSString* mode = [[[command evaluatedArguments]
37      objectForKey:@"KeyDictionary"] objectForKey:@"mode"];
38  AppController* appDelegate = [NSApp delegate];
39
40  Profile* defaultProfile = [appDelegate defaultProfile];
41
42  if (!defaultProfile) {
43    AppleScript::SetError(AppleScript::errGetProfile);
44    return nil;
45  }
46
47  Profile* profile;
48  if ([mode isEqualToString:AppleScript::kIncognitoWindowMode]) {
49    profile = defaultProfile->GetOffTheRecordProfile();
50  }
51  else if ([mode isEqualToString:AppleScript::kNormalWindowMode] || !mode) {
52    profile = defaultProfile;
53  } else {
54    // Mode cannot be anything else
55    AppleScript::SetError(AppleScript::errInvalidMode);
56    return nil;
57  }
58  // Set the mode to nil, to ensure that it is not set once more.
59  [[[command evaluatedArguments] objectForKey:@"KeyDictionary"]
60      setValue:nil forKey:@"mode"];
61  return [self initWithProfile:profile];
62}
63
64- (id)initWithProfile:(Profile*)aProfile {
65  if (!aProfile) {
66    [self release];
67    return nil;
68  }
69
70  if ((self = [super init])) {
71    browser_ = Browser::Create(aProfile);
72    browser_->NewTab();
73    browser_->window()->Show();
74    scoped_nsobject<NSNumber> numID(
75        [[NSNumber alloc] initWithInt:browser_->session_id().id()]);
76    [self setUniqueID:numID];
77  }
78  return self;
79}
80
81- (id)initWithBrowser:(Browser*)aBrowser {
82  if (!aBrowser) {
83    [self release];
84    return nil;
85  }
86
87  if ((self = [super init])) {
88    // It is safe to be weak, if a window goes away (eg user closing a window)
89    // the applescript runtime calls appleScriptWindows in
90    // BrowserCrApplication and this particular window is never returned.
91    browser_ = aBrowser;
92    scoped_nsobject<NSNumber> numID(
93        [[NSNumber alloc] initWithInt:browser_->session_id().id()]);
94    [self setUniqueID:numID];
95  }
96  return self;
97}
98
99- (NSWindow*)nativeHandle {
100  // window() can be NULL during startup.
101  if (browser_->window())
102    return browser_->window()->GetNativeHandle();
103  return nil;
104}
105
106- (NSNumber*)activeTabIndex {
107  // Note: applescript is 1-based, that is lists begin with index 1.
108  int activeTabIndex = browser_->active_index() + 1;
109  if (!activeTabIndex) {
110    return nil;
111  }
112  return [NSNumber numberWithInt:activeTabIndex];
113}
114
115- (void)setActiveTabIndex:(NSNumber*)anActiveTabIndex {
116  // Note: applescript is 1-based, that is lists begin with index 1.
117  int atIndex = [anActiveTabIndex intValue] - 1;
118  if (atIndex >= 0 && atIndex < browser_->tab_count())
119    browser_->ActivateTabAt(atIndex, true);
120  else
121    AppleScript::SetError(AppleScript::errInvalidTabIndex);
122}
123
124- (NSString*)mode {
125  Profile* profile = browser_->profile();
126  if (profile->IsOffTheRecord())
127    return AppleScript::kIncognitoWindowMode;
128  return AppleScript::kNormalWindowMode;
129}
130
131- (void)setMode:(NSString*)theMode {
132  // cannot set mode after window is created.
133  if (theMode) {
134    AppleScript::SetError(AppleScript::errSetMode);
135  }
136}
137
138- (TabAppleScript*)activeTab {
139  TabAppleScript* currentTab =
140      [[[TabAppleScript alloc]
141          initWithTabContent:browser_->GetSelectedTabContentsWrapper()]
142              autorelease];
143  [currentTab setContainer:self
144                  property:AppleScript::kTabsProperty];
145  return currentTab;
146}
147
148- (NSArray*)tabs {
149  NSMutableArray* tabs = [NSMutableArray
150      arrayWithCapacity:browser_->tab_count()];
151
152  for (int i = 0; i < browser_->tab_count(); ++i) {
153    // Check to see if tab is closing.
154    if (browser_->GetTabContentsAt(i)->is_being_destroyed()) {
155      continue;
156    }
157
158    scoped_nsobject<TabAppleScript> tab(
159        [[TabAppleScript alloc]
160            initWithTabContent:(browser_->GetTabContentsWrapperAt(i))]);
161    [tab setContainer:self
162             property:AppleScript::kTabsProperty];
163    [tabs addObject:tab];
164  }
165  return tabs;
166}
167
168- (void)insertInTabs:(TabAppleScript*)aTab {
169  // This method gets called when a new tab is created so
170  // the container and property are set here.
171  [aTab setContainer:self
172            property:AppleScript::kTabsProperty];
173
174  // Set how long it takes a tab to be created.
175  base::TimeTicks newTabStartTime = base::TimeTicks::Now();
176  TabContentsWrapper* contents =
177      browser_->AddSelectedTabWithURL(GURL(chrome::kChromeUINewTabURL),
178                                      PageTransition::TYPED);
179  contents->tab_contents()->set_new_tab_start_time(newTabStartTime);
180  [aTab setTabContent:contents];
181}
182
183- (void)insertInTabs:(TabAppleScript*)aTab atIndex:(int)index {
184  // This method gets called when a new tab is created so
185  // the container and property are set here.
186  [aTab setContainer:self
187            property:AppleScript::kTabsProperty];
188
189  // Set how long it takes a tab to be created.
190  base::TimeTicks newTabStartTime = base::TimeTicks::Now();
191  browser::NavigateParams params(browser_,
192                                 GURL(chrome::kChromeUINewTabURL),
193                                 PageTransition::TYPED);
194  params.disposition = NEW_FOREGROUND_TAB;
195  params.tabstrip_index = index;
196  browser::Navigate(&params);
197  params.target_contents->tab_contents()->set_new_tab_start_time(
198      newTabStartTime);
199
200  [aTab setTabContent:params.target_contents];
201}
202
203- (void)removeFromTabsAtIndex:(int)index {
204  browser_->tabstrip_model()->DetachTabContentsAt(index);
205}
206
207- (NSNumber*)orderedIndex{
208  return [NSNumber numberWithInt:[[self nativeHandle] orderedIndex]];
209}
210
211- (void)setOrderedIndex:(NSNumber*)anIndex {
212  int index = [anIndex intValue] - 1;
213  if (index < 0 || index >= (int)BrowserList::size()) {
214    AppleScript::SetError(AppleScript::errWrongIndex);
215    return;
216  }
217  [[self nativeHandle] setOrderedIndex:index];
218}
219
220- (NSComparisonResult)windowComparator:(WindowAppleScript*)otherWindow {
221  int thisIndex = [[self orderedIndex] intValue];
222  int otherIndex = [[otherWindow orderedIndex] intValue];
223  if (thisIndex < otherIndex)
224    return NSOrderedAscending;
225  else if (thisIndex > otherIndex)
226    return NSOrderedDescending;
227  // Indexes can never be same.
228  NOTREACHED();
229  return NSOrderedSame;
230}
231
232// Get and set values from the associated NSWindow.
233- (id)valueForUndefinedKey:(NSString*)key {
234  return [[self nativeHandle] valueForKey:key];
235}
236
237- (void)setValue:(id)value forUndefinedKey:(NSString*)key {
238  [[self nativeHandle] setValue:(id)value forKey:key];
239}
240
241- (void)handlesCloseScriptCommand:(NSCloseCommand*)command {
242  // window() can be NULL during startup.
243  if (browser_->window())
244    browser_->window()->Close();
245}
246
247@end
248