1 /* 2 * Copyright (C) 2007 Ryan Leavengood <leavengood@gmail.com> 3 * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de> 4 * 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "BrowserApp.h" 30 31 #include <AboutWindow.h> 32 #include <Alert.h> 33 #include <Autolock.h> 34 #include <Catalog.h> 35 #include <Directory.h> 36 #include <Entry.h> 37 #include <FindDirectory.h> 38 #include <Locale.h> 39 #include <Path.h> 40 #include <Screen.h> 41 #include <UrlContext.h> 42 #include <debugger.h> 43 44 #include <stdio.h> 45 46 #include "BrowserWindow.h" 47 #include "BrowsingHistory.h" 48 #include "DownloadWindow.h" 49 #include "SettingsMessage.h" 50 #include "SettingsWindow.h" 51 #include "ConsoleWindow.h" 52 #include "CookieWindow.h" 53 #include "NetworkCookieJar.h" 54 #include "WebKitInfo.h" 55 #include "WebPage.h" 56 #include "WebSettings.h" 57 #include "WebView.h" 58 #include "WebViewConstants.h" 59 60 61 #undef B_TRANSLATION_CONTEXT 62 #define B_TRANSLATION_CONTEXT "WebPositive" 63 64 const char* kApplicationSignature = "application/x-vnd.Haiku-WebPositive"; 65 const char* kApplicationName = B_TRANSLATE_SYSTEM_NAME("WebPositive"); 66 static const uint32 PRELOAD_BROWSING_HISTORY = 'plbh'; 67 68 #define ENABLE_NATIVE_COOKIES 1 69 70 71 BrowserApp::BrowserApp() 72 : 73 BApplication(kApplicationSignature), 74 fWindowCount(0), 75 fLastWindowFrame(50, 50, 950, 750), 76 fLaunchRefsMessage(0), 77 fInitialized(false), 78 fSettings(NULL), 79 fCookies(NULL), 80 fSession(NULL), 81 fContext(NULL), 82 fDownloadWindow(NULL), 83 fSettingsWindow(NULL), 84 fConsoleWindow(NULL), 85 fCookieWindow(NULL) 86 { 87 #ifdef __i386__ 88 // First let's check SSE2 is available 89 cpuid_info info; 90 get_cpuid(&info, 1, 0); 91 92 if ((info.eax_1.features & (1 << 26)) == 0) { 93 BAlert alert(B_TRANSLATE("No SSE2 support"), B_TRANSLATE("Your CPU is " 94 "too old and does not support the SSE2 extensions, without which " 95 "WebPositive cannot run. We recommend installing NetSurf instead."), 96 B_TRANSLATE("Darn!")); 97 alert.Go(); 98 exit(-1); 99 } 100 #endif 101 102 #if ENABLE_NATIVE_COOKIES 103 BString cookieStorePath = kApplicationName; 104 cookieStorePath << "/Cookies"; 105 fCookies = new SettingsMessage(B_USER_SETTINGS_DIRECTORY, 106 cookieStorePath.String()); 107 fContext = new BPrivate::Network::BUrlContext(); 108 if (fCookies->InitCheck() == B_OK) { 109 BMessage cookieArchive = fCookies->GetValue("cookies", cookieArchive); 110 fContext->SetCookieJar( 111 BPrivate::Network::BNetworkCookieJar(&cookieArchive)); 112 } 113 #endif 114 115 BString sessionStorePath = kApplicationName; 116 sessionStorePath << "/Session"; 117 fSession = new SettingsMessage(B_USER_SETTINGS_DIRECTORY, 118 sessionStorePath.String()); 119 } 120 121 122 BrowserApp::~BrowserApp() 123 { 124 delete fLaunchRefsMessage; 125 delete fSettings; 126 delete fCookies; 127 delete fSession; 128 } 129 130 131 void 132 BrowserApp::AboutRequested() 133 { 134 BAboutWindow* window = new BAboutWindow(kApplicationName, 135 kApplicationSignature); 136 137 // create the about window 138 139 const char* authors[] = { 140 "Andrea Anzani", 141 "Stephan Aßmus", 142 "Alexandre Deckner", 143 "Adrien Destugues", 144 "Rajagopalan Gangadharan", 145 "Rene Gollent", 146 "Ryan Leavengood", 147 "Michael Lotz", 148 "Maxime Simon", 149 NULL 150 }; 151 152 BString aboutText(""); 153 aboutText << "HaikuWebKit " << WebKitInfo::HaikuWebKitVersion(); 154 aboutText << "\nWebKit " << WebKitInfo::WebKitVersion(); 155 156 window->AddCopyright(2007, "Haiku, Inc."); 157 window->AddAuthors(authors); 158 window->AddExtraInfo(aboutText.String()); 159 160 window->Show(); 161 } 162 163 164 void 165 BrowserApp::ArgvReceived(int32 argc, char** argv) 166 { 167 BMessage message(B_REFS_RECEIVED); 168 for (int i = 1; i < argc; i++) { 169 if (strcmp("-f", argv[i]) == 0 170 || strcmp("--fullscreen", argv[i]) == 0) { 171 message.AddBool("fullscreen", true); 172 continue; 173 } 174 const char* url = argv[i]; 175 BEntry entry(argv[i], true); 176 BPath path; 177 if (entry.Exists() && entry.GetPath(&path) == B_OK) 178 url = path.Path(); 179 message.AddString("url", url); 180 } 181 // Upon program launch, it will buffer a copy of the message, since 182 // ArgReceived() is called before ReadyToRun(). 183 RefsReceived(&message); 184 } 185 186 187 void 188 BrowserApp::ReadyToRun() 189 { 190 // Since we will essentially run the GUI... 191 set_thread_priority(Thread(), B_DISPLAY_PRIORITY); 192 193 BWebPage::InitializeOnce(); 194 BWebPage::SetCacheModel(B_WEBKIT_CACHE_MODEL_WEB_BROWSER); 195 196 BPath path; 197 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK 198 && path.Append(kApplicationName) == B_OK 199 && create_directory(path.Path(), 0777) == B_OK) { 200 201 BWebSettings::SetPersistentStoragePath(path.Path()); 202 } 203 204 BString mainSettingsPath(kApplicationName); 205 mainSettingsPath << "/Application"; 206 fSettings = new SettingsMessage(B_USER_SETTINGS_DIRECTORY, 207 mainSettingsPath.String()); 208 209 fLastWindowFrame = fSettings->GetValue("window frame", fLastWindowFrame); 210 BRect defaultDownloadWindowFrame(-10, -10, 365, 265); 211 BRect downloadWindowFrame = fSettings->GetValue("downloads window frame", 212 defaultDownloadWindowFrame); 213 BRect settingsWindowFrame = fSettings->GetValue("settings window frame", 214 BRect()); 215 BRect consoleWindowFrame = fSettings->GetValue("console window frame", 216 BRect(50, 50, 400, 300)); 217 BRect cookieWindowFrame = fSettings->GetValue("cookie window frame", 218 BRect(50, 50, 400, 300)); 219 bool showDownloads = fSettings->GetValue("show downloads", false); 220 221 fDownloadWindow = new DownloadWindow(downloadWindowFrame, showDownloads, 222 fSettings); 223 if (downloadWindowFrame == defaultDownloadWindowFrame) { 224 // Initially put download window in lower right of screen. 225 BRect screenFrame = BScreen().Frame(); 226 BMessage decoratorSettings; 227 fDownloadWindow->GetDecoratorSettings(&decoratorSettings); 228 float borderWidth = 0; 229 if (decoratorSettings.FindFloat("border width", &borderWidth) != B_OK) 230 borderWidth = 5; 231 fDownloadWindow->MoveTo(screenFrame.Width() 232 - fDownloadWindow->Frame().Width() - borderWidth, 233 screenFrame.Height() - fDownloadWindow->Frame().Height() 234 - borderWidth); 235 } 236 fSettingsWindow = new SettingsWindow(settingsWindowFrame, fSettings); 237 238 BWebPage::SetDownloadListener(BMessenger(fDownloadWindow)); 239 240 fConsoleWindow = new ConsoleWindow(consoleWindowFrame); 241 fCookieWindow = new CookieWindow(cookieWindowFrame, fContext->GetCookieJar()); 242 243 fInitialized = true; 244 245 int32 pagesCreated = 0; 246 bool fullscreen = false; 247 if (fLaunchRefsMessage) { 248 _RefsReceived(fLaunchRefsMessage, &pagesCreated, &fullscreen); 249 delete fLaunchRefsMessage; 250 fLaunchRefsMessage = NULL; 251 } 252 253 // If no refs led to a new open page, open new session if set 254 if (fSession->InitCheck() == B_OK && pagesCreated == 0) { 255 const char* kSettingsKeyStartUpPolicy = "start up policy"; 256 uint32 fStartUpPolicy = fSettings->GetValue(kSettingsKeyStartUpPolicy, 257 (uint32)ResumePriorSession); 258 if (fStartUpPolicy == StartNewSession) { 259 PostMessage(NEW_WINDOW); 260 } else { 261 // otherwise, restore previous session 262 BMessage archivedWindow; 263 for (int i = 0; fSession->FindMessage("window", i, &archivedWindow) 264 == B_OK; i++) { 265 BRect frame = archivedWindow.FindRect("window frame"); 266 BString url; 267 archivedWindow.FindString("tab", 0, &url); 268 BrowserWindow* window = new(std::nothrow) BrowserWindow(frame, 269 fSettings, url, fContext); 270 271 if (window != NULL) { 272 window->Show(); 273 pagesCreated++; 274 275 for (int j = 1; archivedWindow.FindString("tab", j, &url) 276 == B_OK; j++) { 277 printf("Create %d:%d\n", i, j); 278 _CreateNewTab(window, url, false); 279 pagesCreated++; 280 } 281 } 282 } 283 } 284 } 285 286 // If previous session did not contain any window, create a new empty one. 287 if (pagesCreated == 0) 288 _CreateNewWindow("", fullscreen); 289 290 PostMessage(PRELOAD_BROWSING_HISTORY); 291 } 292 293 294 void 295 BrowserApp::MessageReceived(BMessage* message) 296 { 297 switch (message->what) { 298 case PRELOAD_BROWSING_HISTORY: 299 // Accessing the default instance will load the history from disk. 300 BrowsingHistory::DefaultInstance(); 301 break; 302 case B_SILENT_RELAUNCH: 303 _CreateNewPage(""); 304 break; 305 case NEW_WINDOW: { 306 BString url; 307 if (message->FindString("url", &url) != B_OK) 308 break; 309 _CreateNewWindow(url); 310 break; 311 } 312 case NEW_TAB: { 313 BrowserWindow* window; 314 if (message->FindPointer("window", 315 reinterpret_cast<void**>(&window)) != B_OK) 316 break; 317 BString url; 318 message->FindString("url", &url); 319 bool select = false; 320 message->FindBool("select", &select); 321 _CreateNewTab(window, url, select); 322 break; 323 } 324 case WINDOW_OPENED: 325 fWindowCount++; 326 fDownloadWindow->SetMinimizeOnClose(false); 327 break; 328 case WINDOW_CLOSED: 329 fWindowCount--; 330 message->FindRect("window frame", &fLastWindowFrame); 331 if (fWindowCount <= 0) { 332 BMessage* message = new BMessage(B_QUIT_REQUESTED); 333 message->AddMessage("window", DetachCurrentMessage()); 334 PostMessage(message); 335 } 336 break; 337 338 case SHOW_DOWNLOAD_WINDOW: 339 _ShowWindow(message, fDownloadWindow); 340 break; 341 case SHOW_SETTINGS_WINDOW: 342 _ShowWindow(message, fSettingsWindow); 343 break; 344 case SHOW_CONSOLE_WINDOW: 345 _ShowWindow(message, fConsoleWindow); 346 break; 347 case SHOW_COOKIE_WINDOW: 348 _ShowWindow(message, fCookieWindow); 349 break; 350 case ADD_CONSOLE_MESSAGE: 351 fConsoleWindow->PostMessage(message); 352 break; 353 354 default: 355 BApplication::MessageReceived(message); 356 break; 357 } 358 } 359 360 361 void 362 BrowserApp::RefsReceived(BMessage* message) 363 { 364 if (!fInitialized) { 365 delete fLaunchRefsMessage; 366 fLaunchRefsMessage = new BMessage(*message); 367 return; 368 } 369 370 _RefsReceived(message); 371 } 372 373 374 bool 375 BrowserApp::QuitRequested() 376 { 377 if (fDownloadWindow->DownloadsInProgress()) { 378 BAlert* alert = new BAlert(B_TRANSLATE("Downloads in progress"), 379 B_TRANSLATE("There are still downloads in progress, do you really " 380 "want to quit WebPositive now?"), B_TRANSLATE("Quit"), 381 B_TRANSLATE("Continue downloads")); 382 int32 choice = alert->Go(); 383 if (choice == 1) { 384 if (fWindowCount == 0) { 385 if (fDownloadWindow->Lock()) { 386 fDownloadWindow->SetWorkspaces(1 << current_workspace()); 387 if (fDownloadWindow->IsHidden()) 388 fDownloadWindow->Show(); 389 else 390 fDownloadWindow->Activate(); 391 fDownloadWindow->SetMinimizeOnClose(true); 392 fDownloadWindow->Unlock(); 393 return false; 394 } 395 } else 396 return false; 397 } 398 } 399 400 fSession->MakeEmpty(); 401 402 /* See if we got here because the last window is already closed. 403 * In that case we only need to save that one, which is already archived */ 404 BMessage* message = CurrentMessage(); 405 BMessage windowMessage; 406 407 status_t ret = message->FindMessage("window", &windowMessage); 408 if (ret == B_OK) { 409 fSession->AddMessage("window", &windowMessage); 410 } else { 411 for (int i = 0; BWindow* window = WindowAt(i); i++) { 412 BrowserWindow* webWindow = dynamic_cast<BrowserWindow*>(window); 413 if (!webWindow) 414 continue; 415 if (!webWindow->Lock()) 416 continue; 417 418 BMessage windowArchive; 419 webWindow->Archive(&windowArchive, true); 420 fSession->AddMessage("window", &windowArchive); 421 422 if (webWindow->QuitRequested()) { 423 fLastWindowFrame = webWindow->WindowFrame(); 424 webWindow->Quit(); 425 i--; 426 } else { 427 webWindow->Unlock(); 428 return false; 429 } 430 } 431 } 432 433 BWebPage::ShutdownOnce(); 434 435 fSettings->SetValue("window frame", fLastWindowFrame); 436 if (fDownloadWindow->Lock()) { 437 fSettings->SetValue("downloads window frame", fDownloadWindow->Frame()); 438 fSettings->SetValue("show downloads", !fDownloadWindow->IsHidden()); 439 fDownloadWindow->Unlock(); 440 } 441 if (fSettingsWindow->Lock()) { 442 fSettings->SetValue("settings window frame", fSettingsWindow->Frame()); 443 fSettingsWindow->Unlock(); 444 } 445 if (fConsoleWindow->Lock()) { 446 fSettings->SetValue("console window frame", fConsoleWindow->Frame()); 447 fConsoleWindow->Unlock(); 448 } 449 if (fCookieWindow->Lock()) { 450 fSettings->SetValue("cookie window frame", fCookieWindow->Frame()); 451 fCookieWindow->Unlock(); 452 } 453 454 BMessage cookieArchive; 455 BPrivate::Network::BNetworkCookieJar& cookieJar = fContext->GetCookieJar(); 456 cookieJar.PurgeForExit(); 457 if (cookieJar.Archive(&cookieArchive) == B_OK) 458 fCookies->SetValue("cookies", cookieArchive); 459 460 return true; 461 } 462 463 464 void 465 BrowserApp::_RefsReceived(BMessage* message, int32* _pagesCreated, 466 bool* _fullscreen) 467 { 468 int32 pagesCreated = 0; 469 470 BrowserWindow* window = NULL; 471 if (message->FindPointer("window", (void**)&window) != B_OK) 472 window = NULL; 473 474 bool fullscreen; 475 if (message->FindBool("fullscreen", &fullscreen) != B_OK) 476 fullscreen = false; 477 478 entry_ref ref; 479 for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { 480 BEntry entry(&ref, true); 481 if (!entry.Exists()) 482 continue; 483 BPath path; 484 if (entry.GetPath(&path) != B_OK) 485 continue; 486 BUrl url(path); 487 window = _CreateNewPage(url.UrlString(), window, fullscreen, 488 pagesCreated == 0); 489 pagesCreated++; 490 } 491 492 BString url; 493 for (int32 i = 0; message->FindString("url", i, &url) == B_OK; i++) { 494 window = _CreateNewPage(url, window, fullscreen, pagesCreated == 0); 495 pagesCreated++; 496 } 497 498 if (_pagesCreated != NULL) 499 *_pagesCreated = pagesCreated; 500 if (_fullscreen != NULL) 501 *_fullscreen = fullscreen; 502 } 503 504 505 BrowserWindow* 506 BrowserApp::_CreateNewPage(const BString& url, BrowserWindow* webWindow, 507 bool fullscreen, bool useBlankTab) 508 { 509 // Let's first see if we must target a specific window... 510 if (webWindow && webWindow->Lock()) { 511 if (useBlankTab && webWindow->IsBlankTab()) { 512 if (url.Length() != 0) 513 webWindow->CurrentWebView()->LoadURL(url); 514 } else 515 webWindow->CreateNewTab(url, true); 516 webWindow->Activate(); 517 webWindow->CurrentWebView()->MakeFocus(true); 518 webWindow->Unlock(); 519 return webWindow; 520 } 521 522 // Otherwise, try to find one in the current workspace 523 uint32 workspace = 1 << current_workspace(); 524 525 bool loadedInWindowOnCurrentWorkspace = false; 526 for (int i = 0; BWindow* window = WindowAt(i); i++) { 527 webWindow = dynamic_cast<BrowserWindow*>(window); 528 if (!webWindow) 529 continue; 530 531 if (webWindow->Lock()) { 532 if (webWindow->Workspaces() & workspace) { 533 if (useBlankTab && webWindow->IsBlankTab()) { 534 if (url.Length() != 0) 535 webWindow->CurrentWebView()->LoadURL(url); 536 } else 537 webWindow->CreateNewTab(url, true); 538 webWindow->Activate(); 539 webWindow->CurrentWebView()->MakeFocus(true); 540 loadedInWindowOnCurrentWorkspace = true; 541 } 542 webWindow->Unlock(); 543 } 544 if (loadedInWindowOnCurrentWorkspace) 545 return webWindow; 546 } 547 548 // Finally, if no window is available, let's create one. 549 return _CreateNewWindow(url, fullscreen); 550 } 551 552 553 BrowserWindow* 554 BrowserApp::_CreateNewWindow(const BString& url, bool fullscreen) 555 { 556 // Offset the window frame unless this is the first window created in the 557 // session. 558 if (fWindowCount > 0) 559 fLastWindowFrame.OffsetBy(20, 20); 560 if (!BScreen().Frame().Contains(fLastWindowFrame)) 561 fLastWindowFrame.OffsetTo(50, 50); 562 563 BrowserWindow* window = new BrowserWindow(fLastWindowFrame, fSettings, 564 url, fContext); 565 if (fullscreen) 566 window->ToggleFullscreen(); 567 window->Show(); 568 return window; 569 } 570 571 572 void 573 BrowserApp::_CreateNewTab(BrowserWindow* window, const BString& url, 574 bool select) 575 { 576 if (!window->Lock()) 577 return; 578 window->CreateNewTab(url, select); 579 window->Unlock(); 580 } 581 582 583 void 584 BrowserApp::_ShowWindow(const BMessage* message, BWindow* window) 585 { 586 BAutolock _(window); 587 uint32 workspaces; 588 if (message->FindUInt32("workspaces", &workspaces) == B_OK) 589 window->SetWorkspaces(workspaces); 590 if (window->IsHidden()) 591 window->Show(); 592 else 593 window->Activate(); 594 } 595 596 597 // #pragma mark - 598 599 600 int 601 main(int, char**) 602 { 603 try { 604 new BrowserApp(); 605 be_app->Run(); 606 delete be_app; 607 } catch (...) { 608 debugger("Exception caught."); 609 } 610 611 return 0; 612 } 613 614